1// Copyright (c) 2013 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.
4
5cr.define('accessibility', function() {
6  'use strict';
7
8  // Keep in sync with view_message_enums.h
9  var AccessibilityModeFlag = {
10    Platform: 1 << 0,
11    FullTree: 1 << 1
12  }
13
14  var AccessibilityMode = {
15    Off: 0,
16    Complete:
17        AccessibilityModeFlag.Platform | AccessibilityModeFlag.FullTree,
18    EditableTextOnly: AccessibilityModeFlag.Platform,
19    TreeOnly: AccessibilityModeFlag.FullTree
20  }
21
22  function isAccessibilityComplete(mode) {
23    return ((mode & AccessibilityMode.Complete) == AccessibilityMode.Complete);
24  }
25
26  function requestData() {
27    var xhr = new XMLHttpRequest();
28    xhr.open('GET', 'targets-data.json', false);
29    xhr.send(null);
30    if (xhr.status === 200) {
31      console.log(xhr.responseText);
32      return JSON.parse(xhr.responseText);
33    }
34    return [];
35  }
36
37  // TODO(aboxhall): add a mechanism to request individual and global a11y
38  // mode, xhr them on toggle... or just re-requestData and be smarter about
39  // ID-ing rows?
40
41  function toggleAccessibility(data, element) {
42    chrome.send('toggleAccessibility',
43                [String(data.processId), String(data.routeId)]);
44    var a11y_was_on = (element.textContent.match(/on/) != null);
45    element.textContent = ' accessibility ' + (a11y_was_on ? ' off' : ' on');
46    var row = element.parentElement;
47    if (a11y_was_on) {
48      while (row.lastChild != element)
49        row.removeChild(row.lastChild);
50    } else {
51      row.appendChild(document.createTextNode(' | '));
52      row.appendChild(createShowAccessibilityTreeElement(data, row, false));
53    }
54  }
55
56  function requestAccessibilityTree(data, element) {
57    chrome.send('requestAccessibilityTree',
58                [String(data.processId), String(data.routeId)]);
59  }
60
61  function toggleGlobalAccessibility() {
62    chrome.send('toggleGlobalAccessibility');
63    document.location.reload(); // FIXME see TODO above
64  }
65
66  function initialize() {
67    console.log('initialize');
68    var data = requestData();
69
70    addGlobalAccessibilityModeToggle(data['global_a11y_mode']);
71
72    $('pages').textContent = '';
73
74    var list = data['list'];
75    for (var i = 0; i < list.length; i++) {
76      addToPagesList(list[i]);
77    }
78  }
79
80  function addGlobalAccessibilityModeToggle(global_a11y_mode) {
81    var full_a11y_on = isAccessibilityComplete(global_a11y_mode);
82    $('toggle_global').textContent = (full_a11y_on ? 'on' : 'off');
83    $('toggle_global').addEventListener('click',
84                                        toggleGlobalAccessibility);
85  }
86
87  function addToPagesList(data) {
88    // TODO: iterate through data and pages rows instead
89    var id = data['processId'] + '.' + data['routeId'];
90    var row = document.createElement('div');
91    row.className = 'row';
92    row.id = id;
93    formatRow(row, data);
94
95    row.processId = data.processId;
96    row.routeId = data.routeId;
97
98    var list = $('pages');
99    list.appendChild(row);
100  }
101
102  function formatRow(row, data) {
103    if (!('url' in data)) {
104      if ('error' in data) {
105        row.appendChild(createErrorMessageElement(data, row));
106        return;
107      }
108    }
109    var properties = ['favicon_url', 'name', 'url'];
110    for (var j = 0; j < properties.length; j++)
111      row.appendChild(formatValue(data, properties[j]));
112
113    row.appendChild(createToggleAccessibilityElement(data));
114    if (isAccessibilityComplete(data['a11y_mode'])) {
115      row.appendChild(document.createTextNode(' | '));
116      if ('tree' in data) {
117        row.appendChild(createShowAccessibilityTreeElement(data, row, true));
118        row.appendChild(document.createTextNode(' | '));
119        row.appendChild(createHideAccessibilityTreeElement(row.id));
120        row.appendChild(createAccessibilityTreeElement(data));
121      }
122      else {
123        row.appendChild(createShowAccessibilityTreeElement(data, row, false));
124        if ('error' in data)
125          row.appendChild(createErrorMessageElement(data, row));
126      }
127    }
128  }
129
130  function formatValue(data, property) {
131    var value = data[property];
132
133    if (property == 'favicon_url') {
134      var faviconElement = document.createElement('img');
135      if (value)
136        faviconElement.src = value;
137      faviconElement.alt = "";
138      return faviconElement;
139    }
140
141    var text = value ? String(value) : '';
142    if (text.length > 100)
143      text = text.substring(0, 100) + '\u2026';  // ellipsis
144
145    var span = document.createElement('span');
146    span.textContent = ' ' + text + ' ';
147    span.className = property;
148    return span;
149  }
150
151  function createToggleAccessibilityElement(data) {
152    var link = document.createElement('a');
153    link.setAttribute('href', '#');
154    var full_a11y_on = isAccessibilityComplete(data['a11y_mode']);
155    link.textContent = 'accessibility ' + (full_a11y_on ? 'on' : 'off');
156    link.addEventListener('click',
157                          toggleAccessibility.bind(this, data, link));
158    return link;
159  }
160
161  function createShowAccessibilityTreeElement(data, row, opt_refresh) {
162    var link = document.createElement('a');
163    link.setAttribute('href', '#');
164    if (opt_refresh)
165      link.textContent = 'refresh accessibility tree';
166    else
167      link.textContent = 'show accessibility tree';
168    link.id = row.id + ':showTree';
169    link.addEventListener('click',
170                          requestAccessibilityTree.bind(this, data, link));
171    return link;
172  }
173
174  function createHideAccessibilityTreeElement(id) {
175    var link = document.createElement('a');
176    link.setAttribute('href', '#');
177    link.textContent = 'hide accessibility tree';
178    link.addEventListener('click',
179                          function() {
180        $(id + ':showTree').textContent = 'show accessibility tree';
181        var existingTreeElements = $(id).getElementsByTagName('pre');
182        for (var i = 0; i < existingTreeElements.length; i++)
183          $(id).removeChild(existingTreeElements[i]);
184        var row = $(id);
185        while (row.lastChild != $(id + ':showTree'))
186          row.removeChild(row.lastChild);
187    });
188    return link;
189  }
190
191  function createErrorMessageElement(data) {
192    var errorMessageElement = document.createElement('div');
193    var errorMessage = data.error;
194    errorMessageElement.innerHTML = errorMessage + '&nbsp;';
195    var closeLink = document.createElement('a');
196    closeLink.href='#';
197    closeLink.textContent = '[close]';
198    closeLink.addEventListener('click', function() {
199        var parentElement = errorMessageElement.parentElement;
200        parentElement.removeChild(errorMessageElement);
201        if (parentElement.childElementCount == 0)
202          parentElement.parentElement.removeChild(parentElement);
203    });
204    errorMessageElement.appendChild(closeLink);
205    return errorMessageElement;
206  }
207
208  function showTree(data) {
209    var id = data.processId + '.' + data.routeId;
210    var row = $(id);
211    if (!row)
212      return;
213
214    row.textContent = '';
215    formatRow(row, data);
216  }
217
218  function createAccessibilityTreeElement(data) {
219    var treeElement = document.createElement('pre');
220    var tree = data.tree;
221    treeElement.textContent = tree;
222    return treeElement;
223  }
224
225  return {
226    initialize: initialize,
227    showTree: showTree
228  };
229});
230
231document.addEventListener('DOMContentLoaded', accessibility.initialize);
232