Revert "Fix broken channel icon in chrome://help on CrOS" and try again
[chromium-blink-merge.git] / ui / accessibility / extensions / colorenhancer / src / popup.js
blobd8757062c2bd63de2cfeda34c8b302d59d9c9b96
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 /**
6  * Global exports, used locally to separate initialization from declaration.
7  */
8 (function(exports) {
9   var site;
11   /**
12    * Toggle between filters 0 and 1 in order to force a repaint.
13    * TODO(kevers): Consolidate with filter in CVD.
14    * @type {!number}
15    */
16   var activeFilterIndex = 0;
18   /**
19    * Save previous state of setup parameters for use in the event of a canceled
20    * setup.
21    * @type {{type: string, severity: number} | undefined}
22    */
23   var restoreSettings = undefined;
25   /**
26    * The strings for CVD Types.
27    * TODO(mustaq): Define an enum in cvd.js instead.
28    * @const {array{string}}
29    */
30   var CVD_TYPES = [
31     'PROTANOMALY',
32     'DEUTERANOMALY',
33     'TRITANOMALY'
34   ];
36   /**
37    * Vertical offset for displaying the row highlight.
38    * @const {number}
39    */
40   var HIGHLIGHT_OFFSET = 7;
42   // ======= Swatch generator =======
44   /**
45    * Set of colors for test swatches.
46    * Each row of swatches corresponds to a different type of color blindness.
47    * Tests for the 3 different types of dichromatic color vison.
48    * Colors selected based on color confusion lines for dichromats using our
49    * swatch generator tool. See:
50    * http://www.color-blindness.com/2007/01/23/confusion-lines-of-the-cie-1931-color-space/
51    */
52   var SWATCH_COLORS = [
53     {
54       BACKGROUND: [194,66,96],
55       PROTANOMALY: [123,73,103],
56       DEUTERANOMALY: [131,91,97],
57       TRITANOMALY: [182,57,199]
58     },
59     {
60       BACKGROUND: [156,90,94],
61       PROTANOMALY: [100,96,97],
62       DEUTERANOMALY: [106,110,95],
63       TRITANOMALY: [165,100,0]
64     },
65     {
66       BACKGROUND: [201,110,50],
67       PROTANOMALY: [125,120,52],
68       DEUTERANOMALY: [135,136,51],
69       TRITANOMALY: [189,99,163]
70     },
71     {
72       BACKGROUND: [90,180,60],
73       PROTANOMALY: [161,171,57],
74       DEUTERANOMALY: [156,154,59],
75       TRITANOMALY: [84,151,247]
76     },
77     {
78       BACKGROUND: [30,172,150],
79       PROTANOMALY: [114,163,144],
80       DEUTERANOMALY: [97,146,148],
81       TRITANOMALY: [31,154,246]
82     },
83     {
84       BACKGROUND: [50,99,144],
85       PROTANOMALY: [145,90,135],
86       DEUTERANOMALY: [97,81,142],
87       TRITANOMALY: [52,112,59]
88     },
89     {
90       BACKGROUND: [91,72,147],
91       PROTANOMALY: [62,74,151],
92       DEUTERANOMALY: [63,83,148],
93       TRITANOMALY: [102,88,12]
94     },
95   ];
97   /**
98    * Creates a radio button for selecting the given type of CVD and a series of
99    * color swatches for testing color vision.
100    * @param {string} cvdType Type of CVD, either "PROTANOMALY" or
101    *     "DEUTERANOMALY" or "TRITANOMALY".
102    *  @return {!Element} Row of color swatches with a leading radio button.
103    */
104   function createTestRow(type) {
105     var toCssColor = function(rgb) {
106       return 'rgb(' + rgb.join(',') + ')';
107     };
108     var row = document.createElement('label');
109     row.classList.add('row');
111     var button = document.createElement('input');
112     button.id = 'select-' + type;
113     button.name = 'cvdType';
114     button.setAttribute('type', 'radio');
115     button.value = type;
116     button.checked = false;
117     row.appendChild(button);
118     button.addEventListener('change', function() {
119       onTypeChange(this.value);
120     });
121     button.setAttribute('aria-label', type);
123     SWATCH_COLORS.forEach(function(data) {
124       var swatch = document.querySelector('.swatch.template').cloneNode(true);
125       swatch.style.background = toCssColor(data.BACKGROUND);
126       swatch.style.color = toCssColor(data[type]);
127       swatch.classList.remove('template');
128       row.appendChild(swatch);
129     });
130     return row;
131   }
134   // ======= UI hooks =======
136   /**
137    * Gets the CVD type selected through the radio buttons.
138    * @return {?string}
139    */
140   function getCvdTypeSelection() {
141     var active = undefined;
142     CVD_TYPES.forEach(function(str) {
143       if ($('select-' + str).checked) {
144         active = str;
145         return;
146       }
147     });
148     return active;
149   }
152   /**
153    * Sets the radio buttons selection to the given CVD type.
154    * @param {string} cvdType Type of CVD, either "PROTANOMALY" or
155    *     "DEUTERANOMALY" or "TRITANOMALY".
156    * @return {?string}
157    */
158   function setCvdTypeSelection(cvdType) {
159     var highlight = $('row-highlight');
160     highlight.hidden = true;
161     CVD_TYPES.forEach(function(str) {
162       var checkbox = $('select-' + str);
163       if (cvdType == str) {
164         checkbox.checked = true;
165         var top = checkbox.parentElement.offsetTop - HIGHLIGHT_OFFSET;
166         highlight.style.top = top + 'px';
167         highlight.hidden = false;
168       } else {
169         checkbox.checked = false;
170       }
171     });
172   }
174   /**
175    * Styles controls based on stage of setup.
176    */
177   function updateControls() {
178     if ($('setup-panel').classList.contains('collapsed')) {
179       // Not performing setup.  Ensure main controls are enabled.
180       $('enable').disabled = false;
181       $('delta').disabled = false;
182       $('setup').disabled = false;
183     } else {
184       // Disable main controls during setup phase.
185       $('enable').disabled = true;
186       $('delta').disabled = true;
187       $('setup').disabled = true;
189       if (!getCvdTypeSelection()) {
190         // Have not selected a CVD type. Mark Step 1 as active.
191         $('step-1').classList.add('active');
192         $('step-2').classList.remove('active');
193         // Disable "step 2" controls.
194         $('severity').disabled = true;
195         $('reset').disabled = true;
196       } else {
197         $('step-1').classList.remove('active');
198         $('step-2').classList.add('active');
199         // Enable "step 2" controls.
200         $('severity').disabled = false;
201         $('reset').disabled = false;
202         // Force filter update.
203         onSeverityChange(parseFloat($('severity').value));
204       }
205     }
206   }
208   /**
209    * Update the popup controls based on settings for this site or the default.
210    * @return {boolean} True if settings are valid and update performed.
211    */
212   function update() {
213     var type = getDefaultType();
214     var validType = false;
215     CVD_TYPES.forEach(function(cvdType) {
216       if (cvdType == type) {
217         validType = true;
218         return;
219       }
220     });
222     if (!validType)
223       return false;
225     if (site) {
226       $('delta').value = getSiteDelta(site);
227     } else {
228       $('delta').value = getDefaultDelta();
229     }
231     $('severity').value = getDefaultSeverity();
233     if (!$('setup-panel').classList.contains('collapsed'))
234       setCvdTypeSelection(getDefaultType());
235     $('enable').checked = getDefaultEnable();
237     debugPrint('update: ' +
238         ' del=' + $('delta').value +
239         ' sev=' + $('severity').value +
240         ' typ=' + getDefaultType() +
241         ' enb=' + $('enable').checked +
242         ' for ' + site
243     );
244     chrome.extension.getBackgroundPage().updateTabs();
245     return true;
246   }
248   /**
249    * Callback for color rotation slider.
250    *
251    * @param {number} value Parsed value of slider element.
252    */
253   function onDeltaChange(value) {
254     debugPrint('onDeltaChange: ' + value + ' for ' + site);
255     if (site) {
256       setSiteDelta(site, value);
257     }
258     setDefaultDelta(value);
259     update();
260   }
262   /**
263    * Callback for severity slider.
264    *
265    * @param {number} value Parsed value of slider element.
266    */
267   function onSeverityChange(value) {
268     debugPrint('onSeverityChange: ' + value + ' for ' + site);
269     setDefaultSeverity(value);
270     update();
271     // Apply filter to popup swatches.
272     var filter = window.getDefaultCvdCorrectionFilter(
273         getCvdTypeSelection(), value);
274     injectColorEnhancementFilter(filter);
275     // Force a refresh.
276     window.getComputedStyle(document.documentElement, null);
277   }
279   /**
280    * Callback for changing color deficiency type.
281    *
282    * @param {string} value Value of dropdown element.
283    */
284   function onTypeChange(value) {
285     debugPrint('onTypeChange: ' + value + ' for ' + site);
286     setDefaultType(value);
287     update();
288     // TODO(kevers): reset severity to effectively disable filter.
289     activeFilterType = value;
290     $('severity').value = 0;
291     updateControls();
292   }
294   /**
295    * Callback for enable/disable setting.
296    *
297    * @param {boolean} value Value of checkbox element.
298   */
299   function onEnableChange(value) {
300     debugPrint('onEnableChange: ' + value + ' for ' + site);
301     setDefaultEnable(value);
302     if (!update()) {
303       // Settings are not valid for a reconfiguration.
304       $('setup').onclick();
305     }
306   }
308   /**
309    * Callback for resetting stored per-site values.
310    */
311   function onReset() {
312     debugPrint('onReset');
313     resetSiteDeltas();
314     update();
315   }
317   /**
318    * Attach event handlers to controls and update the filter config values for
319    * the currently visible tab.
320    */
321   function initialize() {
322     var i18nElements = document.querySelectorAll('*[i18n-content]');
323     for (var i = 0; i < i18nElements.length; i++) {
324       var elem = i18nElements[i];
325       var msg = elem.getAttribute('i18n-content');
326       elem.textContent = chrome.i18n.getMessage(msg);
327     }
329     $('setup').onclick = function() {
330       $('setup-panel').classList.remove('collapsed');
331       // Store current settings in the event of a canceled setup.
332       restoreSettings = {
333         type: getDefaultType(),
334         severity: getDefaultSeverity()
335       };
336       // Initalize controls based on current settings.
337       setCvdTypeSelection(restoreSettings.type);
338       $('severity').value = restoreSettings.severity;
339       updateControls();
340     };
342     $('delta').addEventListener('input', function() {
343       onDeltaChange(parseFloat(this.value));
344     });
345     $('severity').addEventListener('input', function() {
346       onSeverityChange(parseFloat(this.value));
347     });
348     $('enable').addEventListener('change', function() {
349       onEnableChange(this.checked);
350     });
352     $('reset').onclick = function() {
353       setDefaultSeverity(0);
354       setDefaultType('');
355       setDefaultEnable(false);
356       $('severity').value = 0;
357       $('enable').checked = false;
358       setCvdTypeSelection('');
359       updateControls();
360       clearColorEnhancementFilter();
361     };
362     $('reset').hidden = !IS_DEV_MODE;
364     var closeSetup = function() {
365       $('setup-panel').classList.add('collapsed');
366       updateControls();
367     };
369     $('ok').onclick = function() {
370       closeSetup();
371     };
373     $('cancel').onclick = function() {
374       closeSetup();
375       if (restoreSettings) {
376         debugPrint(
377           'restore previous settings: ' +
378           'type = ' + restoreSettings.type +
379            ', severity = ' + restoreSettings.severity);
380         setDefaultType(restoreSettings.type);
381         setDefaultSeverity(restoreSettings.severity);
382       }
383     };
385     var swatches = $('swatches');
386     CVD_TYPES.forEach(function(cvdType) {
387       swatches.appendChild(createTestRow(cvdType));
388     });
390     chrome.windows.getLastFocused({'populate': true}, function(window) {
391       for (var i = 0; i < window.tabs.length; i++) {
392         var tab = window.tabs[i];
393         if (tab.active) {
394           site = siteFromUrl(tab.url);
395           debugPrint('init: active tab update for ' + site);
396           update();
397           return;
398         }
399       }
400       site = 'unknown site';
401       update();
402     });
403   }
405   /**
406    * Runs initialize once popup loading is complete.
407    */
408   exports.initializeOnLoad = function() {
409     var ready = new Promise(function readyPromise(resolve) {
410       if (document.readyState === 'complete') {
411         resolve();
412       }
413       document.addEventListener('DOMContentLoaded', resolve);
414     });
415     ready.then(initialize);
416   };
417 })(this);
419 this.initializeOnLoad();