| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2011 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 |
| 5 goog.provide('cvox.BooksReaderApi'); |
| 6 |
| 7 goog.require('cvox.ChromeVox'); |
| 8 |
| 9 /** |
| 10 * @fileoverview API for accessing content and getting events from the Google |
| 11 * Books Reader. The API exposes an event driven API for accessibility. |
| 12 * Having this API keeps the accessibility scripts from being dependent on |
| 13 * the inner workings of the Google Books Reader. |
| 14 */ |
| 15 BooksReaderApi = { }; |
| 16 |
| 17 /** |
| 18 * Collection of classnames. Used to check on the status of threads and |
| 19 * messages. |
| 20 * @private |
| 21 * @type {Object.<String, String>} |
| 22 */ |
| 23 BooksReaderApi.CLASSES_ = { |
| 24 PAGE_CONTENT_WRAPPER: 'gb-content', |
| 25 PAGE_CONTENT: 'gb-two-page gb-content-justify', |
| 26 PAGE_NUMBER: 'gb-pagecontrol-input', |
| 27 DIALOG_CLOSE: 'SPRITE_dialog_close_box', |
| 28 INDEX_ITEM: 'gb-result', |
| 29 INDEX_ITEM_SELECTED: 'gb-result-hover', |
| 30 INDEX_ITEM_TEXT: 'gb-result-snippet', |
| 31 INDEX_ITEM_PAGE_NUMBER: 'gb-result-page', |
| 32 TABLE_OF_CONTENTS_BUTTON: 'SPRITE_icon_toc', |
| 33 TABLE_OF_CONTENTS_BUTTON_SELECTED: 'SPRITE_icon_toc_over', |
| 34 SEARCH_BUTTON: 'SPRITE_icon_search', |
| 35 SEARCH_BUTTON_SELECTED: 'SPRITE_icon_search_over', |
| 36 SEARCH_FIELD: 'gb-search-input' |
| 37 }; |
| 38 |
| 39 /** |
| 40 * Callback listener to invoke when an event happens. |
| 41 * @private |
| 42 * @type {Function} |
| 43 */ |
| 44 BooksReaderApi.listener_ = null; |
| 45 |
| 46 /** |
| 47 * Initializes the Books API script |
| 48 * @private |
| 49 */ |
| 50 BooksReaderApi.init_ = function() { |
| 51 window.addEventListener('DOMSubtreeModified', |
| 52 BooksReaderApi.domSubtreeModified, true); |
| 53 window.addEventListener('keydown', |
| 54 BooksReaderApi.keyDownHandler, true); |
| 55 }; |
| 56 |
| 57 /** |
| 58 * Sets the BooksListener object to be used for callbacks. |
| 59 * |
| 60 * public: |
| 61 * @param {function} booksListener The BooksListener to be used. |
| 62 */ |
| 63 BooksReaderApi.setBooksEventListener = function(booksListener) { |
| 64 BooksReaderApi.listener_ = booksListener; |
| 65 }; |
| 66 |
| 67 /** |
| 68 * Handles the DOM Subtree Modified event. This event is fired when the user |
| 69 * does some action (such as turning the page). |
| 70 * |
| 71 * @param {Object} evt The DOMSubtreeModified event used to determine when |
| 72 * there is a user action that we should respond to. |
| 73 */ |
| 74 BooksReaderApi.domSubtreeModified = function(evt) { |
| 75 var target = evt.target; |
| 76 if (target.className && |
| 77 (target.className == BooksReaderApi.CLASSES_.PAGE_CONTENT_WRAPPER)) { |
| 78 var pageContentNode = document.getElementsByClassName( |
| 79 BooksReaderApi.CLASSES_.PAGE_CONTENT)[0]; |
| 80 var pageNumber = document.getElementsByClassName( |
| 81 BooksReaderApi.CLASSES_.PAGE_NUMBER)[0].value; |
| 82 var currentPage = new Page(pageContentNode, pageNumber); |
| 83 BooksReaderApi.listener_.onPageTurned(currentPage); |
| 84 if (document.activeElement && (document.activeElement.tagName == 'INPUT')) { |
| 85 document.activeElement.blur(); |
| 86 } |
| 87 } |
| 88 if (target.className && (target.className.indexOf( |
| 89 BooksReaderApi.CLASSES_.INDEX_ITEM_SELECTED) != -1)) { |
| 90 var itemIndexNumber = target.getElementsByClassName( |
| 91 BooksReaderApi.CLASSES_.INDEX_ITEM_PAGE_NUMBER)[0].textContent; |
| 92 var text = target.getElementsByClassName( |
| 93 BooksReaderApi.CLASSES_.INDEX_ITEM_TEXT)[0].textContent; |
| 94 var indexItem = new IndexItem(target, text, itemIndexNumber); |
| 95 BooksReaderApi.listener_.onIndexItemSelected(indexItem); |
| 96 } |
| 97 var searchInputField = document.getElementsByClassName( |
| 98 BooksReaderApi.CLASSES_.SEARCH_FIELD)[0]; |
| 99 if (searchInputField) { |
| 100 searchInputField.focus(); |
| 101 } |
| 102 }; |
| 103 |
| 104 |
| 105 |
| 106 /** |
| 107 * Handles keydown events and adds a few more key commands to the reader for |
| 108 * quickly navigating to the various parts of the app. |
| 109 * |
| 110 * @param {Object} evt The keydown event. |
| 111 * @return {boolean} Whether or not the default action should be taken. |
| 112 */ |
| 113 BooksReaderApi.keyDownHandler = function(evt) { |
| 114 var keyCode = evt.keyCode; |
| 115 // Esc to dismiss any popups from table of contents/search |
| 116 if (evt.keyCode == 27) { |
| 117 var closeBox = document.getElementsByClassName( |
| 118 BooksReaderApi.CLASSES_.DIALOG_CLOSE)[0]; |
| 119 cvox.DomUtil.clickElem(closeBox, false); |
| 120 } |
| 121 // Down arrow to move through the Contents/Search results lists |
| 122 if (evt.keyCode == 40) { |
| 123 var items = document.getElementsByClassName( |
| 124 BooksReaderApi.CLASSES_.INDEX_ITEM); |
| 125 var length = items.length; |
| 126 if (length > 0) { |
| 127 for (var i = 0; item = items[i]; i++) { |
| 128 if (item.className.indexOf( |
| 129 BooksReaderApi.CLASSES_.INDEX_ITEM_SELECTED) != -1) { |
| 130 item.className = item.className.replace( |
| 131 BooksReaderApi.CLASSES_.INDEX_ITEM_SELECTED, ''); |
| 132 if (i < length - 1) { |
| 133 items[i + 1].className = items[i + 1].className + ' ' + |
| 134 BooksReaderApi.CLASSES_.INDEX_ITEM_SELECTED; |
| 135 return false; |
| 136 } |
| 137 } |
| 138 } |
| 139 items[0].className = items[0].className + ' ' + |
| 140 BooksReaderApi.CLASSES_.INDEX_ITEM_SELECTED; |
| 141 return false; |
| 142 } |
| 143 return true; |
| 144 } |
| 145 // Down arrow to move through the Contents/Search results lists |
| 146 if (evt.keyCode == 38) { |
| 147 var items = document.getElementsByClassName( |
| 148 BooksReaderApi.CLASSES_.INDEX_ITEM); |
| 149 var length = items.length; |
| 150 if (length > 0) { |
| 151 for (var i = length - 1; item = items[i]; i--) { |
| 152 if (item.className.indexOf( |
| 153 BooksReaderApi.CLASSES_.INDEX_ITEM_SELECTED) != -1) { |
| 154 item.className = item.className.replace( |
| 155 BooksReaderApi.CLASSES_.INDEX_ITEM_SELECTED, ''); |
| 156 if (i > 0) { |
| 157 items[i - 1].className = items[i - 1].className + ' ' + |
| 158 BooksReaderApi.CLASSES_.INDEX_ITEM_SELECTED; |
| 159 return false; |
| 160 } |
| 161 } |
| 162 } |
| 163 items[length - 1].className = items[length - 1].className + ' ' + |
| 164 BooksReaderApi.CLASSES_.INDEX_ITEM_SELECTED; |
| 165 return false; |
| 166 } |
| 167 return true; |
| 168 } |
| 169 // Enter to click on a result |
| 170 if (evt.keyCode == 13) { |
| 171 var currentItem = document.getElementsByClassName( |
| 172 BooksReaderApi.CLASSES_.INDEX_ITEM_SELECTED)[0]; |
| 173 if (currentItem) { |
| 174 cvox.DomUtil.clickElem(currentItem, false); |
| 175 var closeBox = document.getElementsByClassName( |
| 176 BooksReaderApi.CLASSES_.DIALOG_CLOSE)[0]; |
| 177 cvox.DomUtil.clickElem(closeBox, false); |
| 178 evt.stopPropagation(); |
| 179 return false; |
| 180 } |
| 181 return true; |
| 182 } |
| 183 // All keystrokes after this point should not be acted on if the user is |
| 184 // trying to type in a text field. |
| 185 if (document.activeElement && (document.activeElement.tagName == 'INPUT')) { |
| 186 return true; |
| 187 } |
| 188 // Numbers that are typed will go straight into the page number area |
| 189 if ((evt.keyCode >= 48) && (evt.keyCode <= 57)) { |
| 190 // Focus on the page input field. |
| 191 var jumpToPageInputField = document.getElementsByClassName( |
| 192 BooksReaderApi.CLASSES_.PAGE_NUMBER)[0]; |
| 193 jumpToPageInputField.focus(); |
| 194 jumpToPageInputField.select(); |
| 195 return false; |
| 196 } |
| 197 // c for Contents |
| 198 if (evt.keyCode == 67) { |
| 199 var contentsButton = document.getElementsByClassName( |
| 200 BooksReaderApi.CLASSES_.TABLE_OF_CONTENTS_BUTTON)[0]; |
| 201 if (!contentsButton) { |
| 202 contentsButton = document.getElementsByClassName( |
| 203 BooksReaderApi.CLASSES_.TABLE_OF_CONTENTS_BUTTON_SELECTED)[0]; |
| 204 } |
| 205 if (contentsButton) { |
| 206 cvox.DomUtil.clickElem(contentsButton, false); |
| 207 } |
| 208 return false; |
| 209 } |
| 210 // Slash (/) for Search |
| 211 if (evt.keyCode == 191) { |
| 212 var searchButton = document.getElementsByClassName( |
| 213 BooksReaderApi.CLASSES_.SEARCH_BUTTON)[0]; |
| 214 if (!searchButton) { |
| 215 searchButton = document.getElementsByClassName( |
| 216 BooksReaderApi.CLASSES_.SEARCH_BUTTON_SELECTED)[0]; |
| 217 } |
| 218 if (searchButton) { |
| 219 cvox.DomUtil.clickElem(searchButton, false); |
| 220 } |
| 221 var currentItem = document.getElementsByClassName( |
| 222 BooksReaderApi.CLASSES_.INDEX_ITEM_SELECTED)[0]; |
| 223 if (currentItem) { |
| 224 currentItem.className = currentItem.className.replace( |
| 225 BooksReaderApi.CLASSES_.INDEX_ITEM_SELECTED, ''); |
| 226 } |
| 227 return false; |
| 228 } |
| 229 }; |
| 230 |
| 231 /** |
| 232 * Listener interface for receiving Books events. |
| 233 * |
| 234 * public: |
| 235 * @constructor |
| 236 */ |
| 237 var BooksListener = function() { |
| 238 }; |
| 239 |
| 240 /** |
| 241 * Called when the user has turned the page. |
| 242 * |
| 243 * public: |
| 244 * @param {Object} pageObject The Page object. |
| 245 */ |
| 246 BooksListener.prototype.onPageTurned = function(pageObject) { |
| 247 }; |
| 248 |
| 249 /** |
| 250 * Called when the user has moved to a new index item. |
| 251 * |
| 252 * public: |
| 253 * @param {Object} indexItemObject The IndexItem object. |
| 254 */ |
| 255 BooksListener.prototype.onIndexItemSelected = function(indexItemObject) { |
| 256 }; |
| 257 |
| 258 /** |
| 259 * Page object that holds the DOM node of the page and additional |
| 260 * information about that page (such as the page number). |
| 261 * |
| 262 * public: |
| 263 * @constructor |
| 264 * @param {Element} node The node that contains the Page contents. |
| 265 * @param {String} number The page number as a string. The page number is a |
| 266 * string rather than an integer since the page numbers can sometimes be |
| 267 * Roman numberals (for example, the numbering in the table of contents). |
| 268 */ |
| 269 var Page = function(node, number) { |
| 270 this.node_ = node; |
| 271 this.number_ = number; |
| 272 }; |
| 273 |
| 274 /** |
| 275 * Returns the DOM node for the page. |
| 276 * |
| 277 * public: |
| 278 * @return {Element} The page node. |
| 279 */ |
| 280 Page.prototype.getNode = function() { |
| 281 return this.node_; |
| 282 }; |
| 283 |
| 284 /** |
| 285 * Returns the page number as a string. |
| 286 * |
| 287 * public: |
| 288 * @return {String} The page number as a string. |
| 289 */ |
| 290 Page.prototype.getPageNumber = function() { |
| 291 return this.number_; |
| 292 }; |
| 293 |
| 294 /** |
| 295 * An index item is an item such as the table of contents entry, |
| 296 * search result, etc. which consists of some text along with a page number. |
| 297 * IndexItem object that holds the DOM node of the index item, the text |
| 298 * of that item, and the page that the index is pointing at. |
| 299 * |
| 300 * public: |
| 301 * @constructor |
| 302 * @param {Element} node The node that contains the IndexItem contents. |
| 303 * @param {String} text The text. |
| 304 * @param {String} number The page number as a string. The page number is a |
| 305 * string rather than an integer since the page numbers can sometimes be |
| 306 * Roman numberals (for example, the numbering in the table of contents). |
| 307 */ |
| 308 var IndexItem = function(node, text, number) { |
| 309 this.node_ = node; |
| 310 this.text_ = text; |
| 311 this.number_ = number; |
| 312 }; |
| 313 |
| 314 /** |
| 315 * Returns the DOM node for the IndexItem. |
| 316 * |
| 317 * public: |
| 318 * @return {Element} The IndexItem node. |
| 319 */ |
| 320 IndexItem.prototype.getNode = function() { |
| 321 return this.node_; |
| 322 }; |
| 323 |
| 324 /** |
| 325 * Returns the text for the IndexItem. |
| 326 * |
| 327 * public: |
| 328 * @return {String} The text for the IndexItem. |
| 329 */ |
| 330 IndexItem.prototype.getText = function() { |
| 331 return this.text_; |
| 332 }; |
| 333 |
| 334 /** |
| 335 * Returns the page number as a string. |
| 336 * |
| 337 * public: |
| 338 * @return {String} The page number as a string. |
| 339 */ |
| 340 IndexItem.prototype.getPageNumber = function() { |
| 341 return this.number_; |
| 342 }; |
| 343 |
| 344 window.setTimeout(BooksReaderApi.init_, 100); |
| OLD | NEW |