Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(96)

Side by Side Diff: ios/chrome/browser/find_in_page/resources/find_in_page.js

Issue 1023813003: [iOS] Upstream ios/chrome/browser/find_in_page (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@upstreamAutofill
Patch Set: Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ios/chrome/browser/find_in_page/js_findinpage_manager.mm ('k') | ios/chrome/ios_chrome.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2012 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 /**
6 * This file is lifted from the GoogleMobile find tool.
7 *
8 * @fileoverview A find in page tool. It scans the DOM for elements with the
9 * text being search for, and wraps them with a span that highlights them.
10 *
11 * @author bmcmahan@google.com (Benjamin McMahan)
12 *
13 */
14
15 /**
16 * Namespace for this file. Depends on __gCrWeb having already been injected.
17 */
18 __gCrWeb['findInPage'] = {};
19
20 /**
21 * Index of the current highlighted choice. -1 means none.
22 * @type {number}
23 */
24 __gCrWeb['findInPage']['index'] = -1;
25
26 /**
27 * The list of found searches in span form.
28 * @type {Array.<Element>}
29 */
30 __gCrWeb['findInPage']['spans'] = [];
31
32 /**
33 * The list of frame documents.
34 * TODO(justincohen): x-domain frames won't work.
35 * @type {Array.<Document>}
36 */
37 __gCrWeb['findInPage'].frameDocs = [];
38
39 /**
40 * Associate array to stash element styles while the element is highlighted.
41 * @type {Object.<Element,Object<string,string>>}
42 */
43 __gCrWeb['findInPage'].savedElementStyles = {};
44
45 /**
46 * The style DOM element that we add.
47 * @type {Element}
48 */
49 __gCrWeb['findInPage'].style = null;
50
51 /**
52 * Width we expect the page to be. For example (320/480) for iphone,
53 * (1024/768) for ipad.
54 * @type {number}
55 */
56 __gCrWeb['findInPage'].pageWidth = 320;
57
58 /**
59 * Height we expect the page to be.
60 * @type {number}
61 */
62 __gCrWeb['findInPage'].pageHeight = 480;
63
64 /**
65 * Maximum number of visible elements to count
66 * @type {number}
67 */
68 __gCrWeb['findInPage'].maxVisibleElements = 100;
69
70 /**
71 * A search is in progress.
72 * @type {boolean}
73 */
74 __gCrWeb['findInPage'].searchInProgress = false;
75
76 /**
77 * Node names that are not going to be processed.
78 * @type {Object}
79 */
80 __gCrWeb['findInPage'].ignoreNodeNames = {
81 'SCRIPT': 1,
82 'STYLE': 1,
83 'EMBED': 1,
84 'OBJECT': 1
85 };
86
87 /**
88 * Class name of CSS element.
89 * @type {string}
90 */
91 __gCrWeb['findInPage'].CSS_CLASS_NAME = 'find_in_page';
92
93 /**
94 * ID of CSS style.
95 * @type {string}
96 */
97 __gCrWeb['findInPage'].CSS_STYLE_ID = '__gCrWeb.findInPageStyle';
98
99 /**
100 * Result passed back to app to indicate no results for the query.
101 * @type {string}
102 */
103 __gCrWeb['findInPage'].NO_RESULTS = '[0,[0,0,0]]';
104
105 /**
106 * Regex to escape regex special characters in a string.
107 * @type {RegExp}
108 */
109 __gCrWeb['findInPage'].REGEX_ESCAPER = /([.?*+^$[\]\\(){}|-])/g;
110
111 __gCrWeb['findInPage'].getCurrentSpan = function() {
112 return __gCrWeb['findInPage']['spans'][__gCrWeb['findInPage']['index']];
113 };
114
115 /**
116 * Creates the regex needed to find the text.
117 * @param {string} findText Phrase to look for.
118 * @param {boolean} opt_split True to split up the phrase.
119 * @return {RegExp} regex needed to find the text.
120 */
121 __gCrWeb['findInPage'].getRegex = function(findText, opt_split) {
122 var regexString = '';
123 if (opt_split) {
124 var words = [];
125 var split = findText.split(' ');
126 for (var i = 0; i < split.length; i++) {
127 words.push(__gCrWeb['findInPage'].escapeRegex(split[i]));
128 }
129 var joinedWords = words.join('|');
130 regexString = '(' +
131 // Match at least one word.
132 '\\b(?:' + joinedWords + ')' +
133 // Include zero or more additional words separated by whitespace.
134 '(?:\\s*\\b(?:' + joinedWords + '))*' +
135 ')';
136 } else {
137 regexString = '(' + __gCrWeb['findInPage'].escapeRegex(findText) + ')';
138 }
139 return new RegExp(regexString, 'ig');
140 };
141
142 /**
143 * Get current timestamp.
144 * @return {number} timestamp.
145 */
146 __gCrWeb['findInPage'].time = function() {
147 return (new Date).getTime();
148 };
149
150 /**
151 * After |timeCheck| iterations, return true if |now| - |start| is greater than
152 * |timeout|.
153 * @return {boolean} Find in page needs to return.
154 */
155 __gCrWeb['findInPage'].overTime = function() {
156 return (__gCrWeb['findInPage'].time() - __gCrWeb['findInPage'].startTime >
157 __gCrWeb['findInPage'].timeout);
158 };
159
160 /**
161 * Looks for a phrase in the DOM.
162 * @param {string} findText Phrase to look for like "ben franklin".
163 * @param {boolean} opt_split True to split up the words and look for any
164 * of them. False to require the full phrase to be there.
165 * Undefined will try the full phrase, and if nothing is found do the split.
166 * @param {number} timeout Maximum time to run.
167 * @return {number} How many results there are in the page.
168 */
169 __gCrWeb['findInPage']['highlightWord'] =
170 function(findText, opt_split, timeout) {
171 if (__gCrWeb['findInPage']['spans'] &&
172 __gCrWeb['findInPage']['spans'].length) {
173 // Clean up a previous run.
174 __gCrWeb['findInPage']['clearHighlight']();
175 }
176 if (!findText || !findText.replace(/\u00a0|\s/g, '')) {
177 // No searching for emptyness.
178 return __gCrWeb['findInPage'].NO_RESULTS;
179 }
180
181 // Store all DOM modifications to do them in a tight loop at once.
182 __gCrWeb['findInPage'].replacements = [];
183
184 // Node is what we are currently looking at.
185 __gCrWeb['findInPage'].node = document.body;
186
187 // Holds what nodes we have not processed yet.
188 __gCrWeb['findInPage'].stack = [];
189
190 // Push frames into stack too.
191 for (var i = __gCrWeb['findInPage'].frameDocs.length - 1; i >= 0; i--) {
192 var doc = __gCrWeb['findInPage'].frameDocs[i];
193 __gCrWeb['findInPage'].stack.push(doc);
194 }
195
196 // Number of visible elements found.
197 __gCrWeb['findInPage'].visibleFound = 0;
198
199 // Index tracking variables so search can be broken up into multiple calls.
200 __gCrWeb['findInPage'].visibleIndex = 0;
201 __gCrWeb['findInPage'].replacementsIndex = 0;
202 __gCrWeb['findInPage'].replacementNewNodesIndex = 0;
203
204 __gCrWeb['findInPage'].regex =
205 __gCrWeb['findInPage'].getRegex(findText, opt_split);
206
207 __gCrWeb['findInPage'].searchInProgress = true;
208
209 return __gCrWeb['findInPage']['pumpSearch'](timeout);
210 };
211
212 /**
213 * Break up find in page DOM regex, DOM manipulation and visibility check
214 * into sections that can be stopped and restarted later. Because the js runs
215 * in the main UI thread, anything over timeout will cause the UI to lock up.
216 * @param {number} timeout Only run find in page until timeout.
217 * @return {boolean} Whether find in page completed.
218 */
219 __gCrWeb['findInPage']['pumpSearch'] = function(timeout) {
220 var opt_split = false;
221 // TODO(justincohen): It would be better if this DCHECKed.
222 if (__gCrWeb['findInPage'].searchInProgress == false)
223 return __gCrWeb['findInPage'].NO_RESULTS;
224
225 __gCrWeb['findInPage'].timeout = timeout;
226 __gCrWeb['findInPage'].startTime = __gCrWeb['findInPage'].time();
227
228 var regex = __gCrWeb['findInPage'].regex;
229 // Go through every node in DFS fashion.
230 while (__gCrWeb['findInPage'].node) {
231 var node = __gCrWeb['findInPage'].node;
232 var children = node.childNodes;
233 if (children && children.length) {
234 // add all (reasonable) children
235 for (var i = children.length - 1; i >= 0; --i) {
236 var child = children[i];
237 if ((child.nodeType == 1 || child.nodeType == 3) &&
238 !__gCrWeb['findInPage'].ignoreNodeNames[child.nodeName]) {
239 __gCrWeb['findInPage'].stack.push(children[i]);
240 }
241 }
242 }
243 if (node.nodeType == 3 && node.parentNode) {
244 var strIndex = 0;
245 var nodes = [];
246 var match;
247 while (match = regex.exec(node.textContent)) {
248 try {
249 var matchText = match[0];
250
251 // If there is content before this match, add it to a new text node.
252 if (match.index > 0) {
253 var nodeSubstr = node.textContent.substring(strIndex,
254 match.index);
255 nodes.push(node.ownerDocument.createTextNode(nodeSubstr));
256 }
257
258 // Now create our matched element.
259 var element = node.ownerDocument.createElement('chrome_find');
260 element.setAttribute('class', __gCrWeb['findInPage'].CSS_CLASS_NAME);
261 element.innerHTML = __gCrWeb['findInPage'].escapeHTML(matchText);
262 nodes.push(element);
263
264 strIndex = match.index + matchText.length;
265 } catch (e) {
266 // Do nothing.
267 }
268 }
269 if (nodes.length) {
270 // Add any text after our matches to a new text node.
271 if (strIndex < node.textContent.length) {
272 var substr = node.textContent.substring(strIndex,
273 node.textContent.length);
274 nodes.push(node.ownerDocument.createTextNode(substr));
275 }
276 __gCrWeb['findInPage'].replacements.push(
277 {oldNode: node, newNodes: nodes});
278 regex.lastIndex = 0;
279 }
280
281 }
282
283 if (__gCrWeb['findInPage'].overTime())
284 return '[false]';
285
286 if (__gCrWeb['findInPage'].stack.length > 0) {
287 __gCrWeb['findInPage'].node = __gCrWeb['findInPage'].stack.pop();
288 } else {
289 __gCrWeb['findInPage'].node = null;
290 }
291 }
292
293 // Insert each of the replacement nodes into the old node's parent, then
294 // remove the old node.
295 var replacements = __gCrWeb['findInPage'].replacements;
296
297 // Last position in replacements array.
298 var rIndex = __gCrWeb['findInPage'].replacementsIndex;
299 var rMax = replacements.length;
300 for (; rIndex < rMax; rIndex++) {
301 var replacement = replacements[rIndex];
302 var parent = replacement.oldNode.parentNode;
303 if (parent == null)
304 continue;
305 var rNodesMax = replacement.newNodes.length;
306 for (var rNodesIndex = __gCrWeb['findInPage'].replacementNewNodesIndex;
307 rNodesIndex < rNodesMax; rNodesIndex++) {
308 if (__gCrWeb['findInPage'].overTime()) {
309 __gCrWeb['findInPage'].replacementsIndex = rIndex;
310 __gCrWeb['findInPage'].replacementNewNodesIndex = rNodesIndex;
311 return __gCrWeb.stringify([false]);
312 }
313 parent.insertBefore(replacement.newNodes[rNodesIndex],
314 replacement.oldNode);
315 }
316 parent.removeChild(replacement.oldNode);
317 __gCrWeb['findInPage'].replacementNewNodesIndex = 0;
318 }
319 // Save last position in replacements array.
320 __gCrWeb['findInPage'].replacementsIndex = rIndex;
321
322 __gCrWeb['findInPage']['spans'] =
323 __gCrWeb['findInPage'].getAllElementsByClassName(
324 __gCrWeb['findInPage'].CSS_CLASS_NAME);
325
326 // Count visible elements.
327 var max = __gCrWeb['findInPage']['spans'].length;
328 var maxVisible = __gCrWeb['findInPage'].maxVisibleElements;
329 for (var index = __gCrWeb['findInPage'].visibleIndex; index < max; index++) {
330 var elem = __gCrWeb['findInPage']['spans'][index];
331 if (__gCrWeb['findInPage'].overTime()) {
332 __gCrWeb['findInPage'].visibleIndex = index;
333 return __gCrWeb.stringify([false]);
334 }
335
336 // Stop after |maxVisible| elements.
337 if (__gCrWeb['findInPage'].visibleFound > maxVisible) {
338 __gCrWeb['findInPage']['spans'][index].visibleIndex = maxVisible;
339 continue;
340 }
341
342 if (__gCrWeb['findInPage'].isVisible(elem)) {
343 __gCrWeb['findInPage'].visibleFound++;
344 __gCrWeb['findInPage']['spans'][index].visibleIndex =
345 __gCrWeb['findInPage'].visibleFound;
346 }
347 }
348
349 __gCrWeb['findInPage'].searchInProgress = false;
350
351 var pos;
352 // Try again flow.
353 // If opt_split is true, we are done since we won't do any better.
354 // If opt_split is false, they were explicit about wanting the full thing
355 // so do not try with a split.
356 // If opt_split is undefined and we did not find an answer, go ahead and try
357 // splitting the terms.
358 if (__gCrWeb['findInPage']['spans'].length == 0 && opt_split === undefined) {
359 // Try to be more aggressive:
360 return __gCrWeb['findInPage']['highlightWord'](findText, true);
361 } else {
362 pos = __gCrWeb['findInPage']['goNext']();
363 if (pos) {
364 return '[' + __gCrWeb['findInPage'].visibleFound + ',' + pos + ']';
365 } else if (opt_split === undefined) {
366 // Nothing visible, go ahead and be more aggressive.
367 return __gCrWeb['findInPage']['highlightWord'](findText, true);
368 } else {
369 return __gCrWeb['findInPage'].NO_RESULTS;
370 }
371 }
372 };
373
374 /**
375 * Converts a node list to an array.
376 * @param {object} nodeList DOM node list.
377 * @return {object} array.
378 */
379 __gCrWeb['findInPage'].toArray = function(nodeList) {
380 var array = [];
381 for (var i = 0; i < nodeList.length; i++)
382 array[i] = nodeList[i];
383 return array;
384 };
385
386 /**
387 * Return all elements of class name, spread out over various frames.
388 * @param {string} name of class.
389 * @return {object} array of elements matching class name.
390 */
391 __gCrWeb['findInPage'].getAllElementsByClassName = function(name) {
392 var nodeList = document.getElementsByClassName(name);
393 var elements = __gCrWeb['findInPage'].toArray(nodeList);
394 for (var i = __gCrWeb['findInPage'].frameDocs.length - 1; i >= 0; i--) {
395 var doc = __gCrWeb['findInPage'].frameDocs[i];
396 nodeList = doc.getElementsByClassName(name);
397 elements = elements.concat(__gCrWeb['findInPage'].toArray(nodeList));
398 }
399 return elements;
400 };
401
402 /**
403 * Removes all currently highlighted spans.
404 * Note: It does not restore previous state, just removes the class name.
405 */
406 __gCrWeb['findInPage']['clearHighlight'] = function() {
407 if (__gCrWeb['findInPage']['index'] >= 0) {
408 __gCrWeb['findInPage'].removeSelectHighlight(
409 __gCrWeb['findInPage'].getCurrentSpan());
410 }
411 // Store all DOM modifications to do them in a tight loop.
412 var modifications = [];
413 var length = __gCrWeb['findInPage']['spans'].length;
414 var prevParent = null;
415 for (var i = length - 1; i >= 0; i--) {
416 var elem = __gCrWeb['findInPage']['spans'][i];
417 var parentNode = elem.parentNode;
418 // Safari has an occasional |elem.innerText| bug that drops the trailing
419 // space. |elem.innerText| would be more correct in this situation, but
420 // since we only allow text in this element, grabbing the HTML value should
421 // not matter.
422 var nodeText = elem.innerHTML;
423 // If this element has the same parent as the previous, check if we should
424 // add this node to the previous one.
425 if (prevParent && prevParent.isSameNode(parentNode) &&
426 elem.nextSibling.isSameNode(
427 __gCrWeb['findInPage']['spans'][i + 1].previousSibling)) {
428 var prevMod = modifications[modifications.length - 1];
429 prevMod.nodesToRemove.push(elem);
430 var elemText = elem.innerText;
431 if (elem.previousSibling) {
432 prevMod.nodesToRemove.push(elem.previousSibling);
433 elemText = elem.previousSibling.textContent + elemText;
434 }
435 prevMod.replacement.textContent =
436 elemText + prevMod.replacement.textContent;
437 }
438 else { // Element isn't attached to previous, so create a new modification.
439 var nodesToRemove = [elem];
440 if (elem.previousSibling && elem.previousSibling.nodeType == 3) {
441 nodesToRemove.push(elem.previousSibling);
442 nodeText = elem.previousSibling.textContent + nodeText;
443 }
444 if (elem.nextSibling && elem.nextSibling.nodeType == 3) {
445 nodesToRemove.push(elem.nextSibling);
446 nodeText = nodeText + elem.nextSibling.textContent;
447 }
448 var textNode = elem.ownerDocument.createTextNode(nodeText);
449 modifications.push({nodesToRemove: nodesToRemove, replacement: textNode});
450 }
451 prevParent = parentNode;
452 }
453 var numMods = modifications.length;
454 for (i = numMods - 1; i >= 0; i--) {
455 var mod = modifications[i];
456 for (var j = 0; j < mod.nodesToRemove.length; j++) {
457 var existing = mod.nodesToRemove[j];
458 if (j == 0) {
459 existing.parentNode.replaceChild(mod.replacement, existing);
460 } else {
461 existing.parentNode.removeChild(existing);
462 }
463 }
464 }
465
466 __gCrWeb['findInPage']['spans'] = [];
467 __gCrWeb['findInPage']['index'] = -1;
468 };
469
470 /**
471 * Increments the index of the current highlighted span or, if the index is
472 * already at the end, sets it to the index of the first span in the page.
473 */
474 __gCrWeb['findInPage']['incrementIndex'] = function() {
475 if (__gCrWeb['findInPage']['index'] >=
476 __gCrWeb['findInPage']['spans'].length - 1) {
477 __gCrWeb['findInPage']['index'] = 0;
478 } else {
479 __gCrWeb['findInPage']['index']++;
480 }
481 };
482
483 /**
484 * Switches to the next result, animating a little highlight in the process.
485 * @return {string} JSON encoded array of coordinates to scroll to, or blank if
486 * nothing happened.
487 */
488 __gCrWeb['findInPage']['goNext'] = function() {
489 if (!__gCrWeb['findInPage']['spans'] ||
490 __gCrWeb['findInPage']['spans'].length == 0) {
491 return '';
492 }
493 if (__gCrWeb['findInPage']['index'] >= 0) {
494 // Remove previous highlight.
495 __gCrWeb['findInPage'].removeSelectHighlight(
496 __gCrWeb['findInPage'].getCurrentSpan());
497 }
498 // Iterate through to the next index, but because they might not be visible,
499 // keep trying until you find one that is. Make sure we don't loop forever by
500 // stopping on what we are currently highlighting.
501 var oldIndex = __gCrWeb['findInPage']['index'];
502 __gCrWeb['findInPage']['incrementIndex']();
503 while (!__gCrWeb['findInPage'].isVisible(
504 __gCrWeb['findInPage'].getCurrentSpan())) {
505 if (oldIndex === __gCrWeb['findInPage']['index']) {
506 // Checked all spans but didn't find anything else visible.
507 return '';
508 }
509 __gCrWeb['findInPage']['incrementIndex']();
510 if (0 === __gCrWeb['findInPage']['index'] && oldIndex < 0) {
511 // Didn't find anything visible and haven't highlighted anything yet.
512 return '';
513 }
514 }
515 // Return scroll dimensions.
516 return __gCrWeb['findInPage'].findScrollDimensions();
517 };
518
519 /**
520 * Decrements the index of the current highlighted span or, if the index is
521 * already at the beginning, sets it to the index of the last span in the page.
522 */
523 __gCrWeb['findInPage']['decrementIndex'] = function() {
524 if (__gCrWeb['findInPage']['index'] <= 0) {
525 __gCrWeb['findInPage']['index'] =
526 __gCrWeb['findInPage']['spans'].length - 1;
527 } else {
528 __gCrWeb['findInPage']['index']--;
529 }
530 };
531
532 /**
533 * Switches to the previous result, animating a little highlight in the process.
534 * @return {string} JSON encoded array of coordinates to scroll to, or blank if
535 * nothing happened.
536 */
537 __gCrWeb['findInPage']['goPrev'] = function() {
538 if (!__gCrWeb['findInPage']['spans'] ||
539 __gCrWeb['findInPage']['spans'].length == 0) {
540 return '';
541 }
542 if (__gCrWeb['findInPage']['index'] >= 0) {
543 // Remove previous highlight.
544 __gCrWeb['findInPage'].removeSelectHighlight(
545 __gCrWeb['findInPage'].getCurrentSpan());
546 }
547 // Iterate through to the next index, but because they might not be visible,
548 // keep trying until you find one that is. Make sure we don't loop forever by
549 // stopping on what we are currently highlighting.
550 var old = __gCrWeb['findInPage']['index'];
551 __gCrWeb['findInPage']['decrementIndex']();
552 while (!__gCrWeb['findInPage'].isVisible(
553 __gCrWeb['findInPage'].getCurrentSpan())) {
554 __gCrWeb['findInPage']['decrementIndex']();
555 if (old == __gCrWeb['findInPage']['index']) {
556 // Checked all spans but didn't find anything.
557 return '';
558 }
559 }
560
561 // Return scroll dimensions.
562 return __gCrWeb['findInPage'].findScrollDimensions();
563 };
564
565 /**
566 * Adds the special highlighting to the result at the index.
567 * @param {number} opt_index Index to replace __gCrWeb['findInPage']['index']
568 * with.
569 */
570 __gCrWeb['findInPage'].addHighlightToIndex = function(opt_index) {
571 if (opt_index !== undefined) {
572 __gCrWeb['findInPage']['index'] = opt_index;
573 }
574 __gCrWeb['findInPage'].addSelectHighlight(
575 __gCrWeb['findInPage'].getCurrentSpan());
576 };
577
578 /**
579 * Updates the elements style, while saving the old style in
580 * __gCrWeb['findInPage'].savedElementStyles.
581 * @param {Element} element Element to update.
582 * @param {string} style Name of the style to update.
583 * @param {string} value New style value.
584 */
585 __gCrWeb['findInPage'].updateElementStyle = function(element, style, value) {
586 if (!__gCrWeb['findInPage'].savedElementStyles[element]) {
587 __gCrWeb['findInPage'].savedElementStyles[element] = {};
588 }
589 // We need to keep the original style setting for this element, so if we've
590 // already saved a value for this style don't update it.
591 if (!__gCrWeb['findInPage'].savedElementStyles[element][style]) {
592 __gCrWeb['findInPage'].savedElementStyles[element][style] =
593 element.style[style];
594 }
595 element.style[style] = value;
596 };
597
598 /**
599 * Adds selected highlight style to the specified element.
600 * @param {Element} element Element to highlight.
601 */
602 __gCrWeb['findInPage'].addSelectHighlight = function(element) {
603 element.className = (element.className || '') + ' findysel';
604 };
605
606 /**
607 * Removes selected highlight style from the specified element.
608 * @param {Element} element Element to remove highlighting from.
609 */
610 __gCrWeb['findInPage'].removeSelectHighlight = function(element) {
611 element.className = (element.className || '').replace(/\sfindysel/g, '');
612
613 // Restore any styles we may have saved when adding the select highlighting.
614 var savedStyles = __gCrWeb['findInPage'].savedElementStyles[element];
615 if (savedStyles) {
616 for (var style in savedStyles) {
617 element.style[style] = savedStyles[style];
618 }
619 delete __gCrWeb['findInPage'].savedElementStyles[element];
620 }
621 };
622
623 /**
624 * Normalize coordinates according to the current document dimensions. Don't go
625 * too far off the screen in either direction. Try to center if possible.
626 * @param {Element} elem Element to find normalized coordinates for.
627 * @return {Array.<number>} Normalized coordinates.
628 */
629 __gCrWeb['findInPage'].getNormalizedCoordinates = function(elem) {
630 var fip = __gCrWeb['findInPage'];
631 var pos = fip.findAbsolutePosition(elem);
632 var maxX =
633 Math.max(fip.getBodyWidth(), pos[0] + elem.offsetWidth);
634 var maxY =
635 Math.max(fip.getBodyHeight(), pos[1] + elem.offsetHeight);
636 // Don't go too far off the screen in either direction. Try to center if
637 // possible.
638 var xPos = Math.max(0,
639 Math.min(maxX - window.innerWidth,
640 pos[0] - (window.innerWidth / 2)));
641 var yPos = Math.max(0,
642 Math.min(maxY - window.innerHeight,
643 pos[1] - (window.innerHeight / 2)));
644 return [xPos, yPos];
645 };
646
647 /**
648 * Scale coordinates according to the width of the screen, in case the screen
649 * is zoomed out.
650 * @param {Array.<number>} coordinates Coordinates to scale.
651 * @return {Array.<number>} Scaled coordinates.
652 */
653 __gCrWeb['findInPage'].scaleCoordinates = function(coordinates) {
654 var scaleFactor = __gCrWeb['findInPage'].pageWidth / window.innerWidth;
655 return [coordinates[0] * scaleFactor, coordinates[1] * scaleFactor];
656 };
657
658 /**
659 * Finds the position of the result and scrolls to it.
660 * @param {number} opt_index Index to replace __gCrWeb['findInPage']['index']
661 * with.
662 * @return {string} JSON encoded array of the scroll coordinates "[x, y]".
663 */
664 __gCrWeb['findInPage'].findScrollDimensions = function(opt_index) {
665 if (opt_index !== undefined) {
666 __gCrWeb['findInPage']['index'] = opt_index;
667 }
668 var elem = __gCrWeb['findInPage'].getCurrentSpan();
669 if (!elem) {
670 return '';
671 }
672 var normalized = __gCrWeb['findInPage'].getNormalizedCoordinates(elem);
673 var xPos = normalized[0];
674 var yPos = normalized[1];
675
676 // Perform the scroll.
677 //window.scrollTo(xPos, yPos);
678
679 if (xPos < window.pageXOffset ||
680 xPos >= (window.pageXOffset + window.innerWidth) ||
681 yPos < window.pageYOffset ||
682 yPos >= (window.pageYOffset + window.innerHeight)) {
683 // If it's off the screen. Wait a bit to start the highlight animation so
684 // that scrolling can get there first.
685 window.setTimeout(__gCrWeb['findInPage'].addHighlightToIndex, 250);
686 } else {
687 __gCrWeb['findInPage'].addHighlightToIndex();
688 }
689 var scaled = __gCrWeb['findInPage'].scaleCoordinates(normalized);
690 var index = __gCrWeb['findInPage'].getCurrentSpan().visibleIndex;
691 scaled.unshift(index);
692 return __gCrWeb.stringify(scaled);
693 };
694
695 /**
696 * Initialize the __gCrWeb['findInPage'] module.
697 * @param {number} width Width of page.
698 * @param {number} height Height of page.
699
700 */
701 __gCrWeb['findInPage']['init'] = function(width, height) {
702 if (__gCrWeb['findInPage'].hasInitialized) {
703 return;
704 }
705 __gCrWeb['findInPage'].pageWidth = width;
706 __gCrWeb['findInPage'].pageHeight = height;
707 __gCrWeb['findInPage'].frameDocs = __gCrWeb['findInPage'].frameDocuments();
708 __gCrWeb['findInPage'].enable();
709 __gCrWeb['findInPage'].hasInitialized = true;
710 };
711
712 /**
713 * When the GSA app detects a zoom change, we need to update our css.
714 * @param {number} width Width of page.
715 * @param {number} height Height of page.
716 */
717 __gCrWeb['findInPage']['fixZoom'] = function(width, height) {
718 __gCrWeb['findInPage'].pageWidth = width;
719 __gCrWeb['findInPage'].pageHeight = height;
720 if (__gCrWeb['findInPage'].style) {
721 __gCrWeb['findInPage'].removeStyle();
722 __gCrWeb['findInPage'].addStyle();
723 }
724 };
725
726 /**
727 * Enable the __gCrWeb['findInPage'] module.
728 * Mainly just adds the style for the classes.
729 */
730 __gCrWeb['findInPage'].enable = function() {
731 if (__gCrWeb['findInPage'].style) {
732 // Already enabled.
733 return;
734 }
735 __gCrWeb['findInPage'].addStyle();
736 };
737
738 /**
739 * Gets the scale ratio between the application window and the web document.
740 * @return {number} Scale.
741 */
742 __gCrWeb['findInPage'].getPageScale = function() {
743 return (__gCrWeb['findInPage'].pageWidth /
744 __gCrWeb['findInPage'].getBodyWidth());
745 };
746
747 /**
748 * Maximum padding added to a highlighted item when selected.
749 * @type {number}
750 */
751 __gCrWeb['findInPage'].MAX_HIGHLIGHT_PADDING = 10;
752
753 /**
754 * Adds the appropriate style element to the page.
755 */
756 __gCrWeb['findInPage'].addStyle = function() {
757 __gCrWeb['findInPage'].addDocumentStyle(document);
758 for (var i = __gCrWeb['findInPage'].frameDocs.length - 1; i >= 0; i--) {
759 var doc = __gCrWeb['findInPage'].frameDocs[i];
760 __gCrWeb['findInPage'].addDocumentStyle(doc);
761 }
762 };
763
764 __gCrWeb['findInPage'].addDocumentStyle = function(thisDocument) {
765 var styleContent = [];
766 function addCSSRule(name, style) {
767 styleContent.push(name, '{', style, '}');
768 };
769 var scale = __gCrWeb['findInPage'].getPageScale();
770 var zoom = (1.0 / scale);
771 var left = ((1 - scale) / 2 * 100);
772 addCSSRule('.' + __gCrWeb['findInPage'].CSS_CLASS_NAME,
773 'background-color:#ffff00 !important;' +
774 'padding:0px;margin:0px;' +
775 'overflow:visible !important;');
776 addCSSRule('.findysel',
777 'background-color:#ff9632 !important;' +
778 'padding:0px;margin:0px;' +
779 'overflow:visible !important;');
780 __gCrWeb['findInPage'].style = thisDocument.createElement('style');
781 __gCrWeb['findInPage'].style.id = __gCrWeb['findInPage'].CSS_STYLE_ID;
782 __gCrWeb['findInPage'].style.setAttribute('type', 'text/css');
783 __gCrWeb['findInPage'].style.appendChild(
784 thisDocument.createTextNode(styleContent.join('')));
785 thisDocument.body.appendChild(__gCrWeb['findInPage'].style);
786 };
787
788 /**
789 * Removes the style element from the page.
790 */
791 __gCrWeb['findInPage'].removeStyle = function() {
792 if (__gCrWeb['findInPage'].style) {
793 __gCrWeb['findInPage'].removeDocumentStyle(document);
794 for (var i = __gCrWeb['findInPage'].frameDocs.length - 1; i >= 0; i--) {
795 var doc = __gCrWeb['findInPage'].frameDocs[i];
796 __gCrWeb['findInPage'].removeDocumentStyle(doc);
797 }
798 __gCrWeb['findInPage'].style = null;
799 }
800 };
801
802 __gCrWeb['findInPage'].removeDocumentStyle = function(thisDocument) {
803 var style = thisDocument.getElementById(__gCrWeb['findInPage'].CSS_STYLE_ID);
804 thisDocument.body.removeChild(style);
805 };
806
807 /**
808 * Disables the __gCrWeb['findInPage'] module.
809 * Basically just removes the style and class names.
810 */
811 __gCrWeb['findInPage']['disable'] = function() {
812 if (__gCrWeb['findInPage'].style) {
813 __gCrWeb['findInPage'].removeStyle();
814 window.setTimeout(__gCrWeb['findInPage']['clearHighlight'], 0);
815 }
816 __gCrWeb['findInPage'].hasInitialized = false;
817 };
818
819 /**
820 * Returns the width of the document.body. Sometimes though the body lies to
821 * try to make the page not break rails, so attempt to find those as well.
822 * An example: wikipedia pages for the ipad.
823 * @return {number} Width of the document body.
824 */
825 __gCrWeb['findInPage'].getBodyWidth = function() {
826 var body = document.body;
827 var documentElement = document.documentElement;
828 return Math.max(body.scrollWidth, documentElement.scrollWidth,
829 body.offsetWidth, documentElement.offsetWidth,
830 body.clientWidth, documentElement.clientWidth);
831 };
832
833 /**
834 * Returns the height of the document.body. Sometimes though the body lies to
835 * try to make the page not break rails, so attempt to find those as well.
836 * An example: wikipedia pages for the ipad.
837 * @return {number} Height of the document body.
838 */
839 __gCrWeb['findInPage'].getBodyHeight = function() {
840 var body = document.body;
841 var documentElement = document.documentElement;
842 return Math.max(body.scrollHeight, documentElement.scrollHeight,
843 body.offsetHeight, documentElement.offsetHeight,
844 body.clientHeight, documentElement.clientHeight);
845 };
846
847 /**
848 * Helper function that determines if an element is visible.
849 * @param {Element} elem Element to check.
850 * @return {boolean} Whether elem is visible or not.
851 */
852 __gCrWeb['findInPage'].isVisible = function(elem) {
853 if (!elem) {
854 return false;
855 }
856 var top = 0;
857 var left = 0;
858 var bottom = Infinity;
859 var right = Infinity;
860
861 var originalElement = elem;
862 var nextOffsetParent = originalElement.offsetParent;
863 var computedStyle =
864 elem.ownerDocument.defaultView.getComputedStyle(elem, null);
865
866 // We are currently handling all scrolling through the app, which means we can
867 // only scroll the window, not any scrollable containers in the DOM itself. So
868 // for now this function returns false if the element is scrolled outside the
869 // viewable area of its ancestors.
870 // TODO (jonwall): handle scrolling within the DOM.
871 var pageHeight = __gCrWeb['findInPage'].getBodyHeight();
872 var pageWidth = __gCrWeb['findInPage'].getBodyWidth();
873
874 while (elem && elem.nodeName != 'BODY') {
875 if (elem.style.display === 'none' ||
876 elem.style.visibility === 'hidden' ||
877 elem.style.opacity === 0 ||
878 computedStyle.display === 'none' ||
879 computedStyle.visibility === 'hidden' ||
880 computedStyle.opacity === 0) {
881 return false;
882 }
883
884 // For the original element and all ancestor offsetParents, trim down the
885 // visible area of the original element.
886 if (elem.isSameNode(originalElement) || elem.isSameNode(nextOffsetParent)) {
887 var visible = elem.getBoundingClientRect();
888 if (elem.style.overflow === 'hidden' &&
889 (visible.width === 0 || visible.height === 0))
890 return false;
891
892 top = Math.max(top, visible.top + window.pageYOffset);
893 bottom = Math.min(bottom, visible.bottom + window.pageYOffset);
894 left = Math.max(left, visible.left + window.pageXOffset);
895 right = Math.min(right, visible.right + window.pageXOffset);
896
897 // The element is not within the original viewport.
898 var notWithinViewport = top < 0 || left < 0;
899
900 // The element is flowing off the boundary of the page. Note this is
901 // not comparing to the size of the window, but the calculated offset
902 // size of the document body. This can happen if the element is within
903 // a scrollable container in the page.
904 var offPage = right > pageWidth || bottom > pageHeight;
905 if (notWithinViewport || offPage) {
906 return false;
907 }
908 nextOffsetParent = elem.offsetParent;
909 }
910
911 elem = elem.parentNode;
912 computedStyle = elem.ownerDocument.defaultView.getComputedStyle(elem, null);
913 }
914 return true;
915 };
916
917 /**
918 * Helper function to find the absolute position of an element on the page.
919 * @param {Element} elem Element to check.
920 * @return {Array.<number>} [x, y] positions.
921 */
922 __gCrWeb['findInPage'].findAbsolutePosition = function(elem) {
923 var boundingRect = elem.getBoundingClientRect();
924 return [boundingRect.left + window.pageXOffset,
925 boundingRect.top + window.pageYOffset];
926 };
927
928 /**
929 * @param {string} text Text to escape.
930 * @return {string} escaped text.
931 */
932 __gCrWeb['findInPage'].escapeHTML = function(text) {
933 var unusedDiv = document.createElement('div');
934 unusedDiv.innerText = text;
935 return unusedDiv.innerHTML;
936 };
937
938 /**
939 * Escapes regexp special characters.
940 * @param {string} text Text to escape.
941 * @return {string} escaped text.
942 */
943 __gCrWeb['findInPage'].escapeRegex = function(text) {
944 return text.replace(__gCrWeb['findInPage'].REGEX_ESCAPER, '\\$1');
945 };
946
947 /**
948 * Gather all iframes in the main window.
949 * @return {Array.<Document>} frames.
950 */
951 __gCrWeb['findInPage'].frameDocuments = function() {
952 var windowsToSearch = [window];
953 var documents = [];
954 while (windowsToSearch.length != 0) {
955 var win = windowsToSearch.pop();
956 for (var i = win.frames.length - 1; i >= 0; i--) {
957 if (win.frames[i].document) {
958 documents.push(win.frames[i].document);
959 windowsToSearch.push(win.frames[i]);
960 }
961 }
962 }
963 return documents;
964 };
OLDNEW
« no previous file with comments | « ios/chrome/browser/find_in_page/js_findinpage_manager.mm ('k') | ios/chrome/ios_chrome.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698