1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 | const path = require('path');
|
17 | const {JSHandle} = require('./ExecutionContext');
|
18 | const {helper, assert, debugError} = require('./helper');
|
19 |
|
20 | class ElementHandle extends JSHandle {
|
21 | |
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 | constructor(context, client, remoteObject, page, frameManager) {
|
29 | super(context, client, remoteObject);
|
30 | this._client = client;
|
31 | this._remoteObject = remoteObject;
|
32 | this._page = page;
|
33 | this._frameManager = frameManager;
|
34 | this._disposed = false;
|
35 | }
|
36 |
|
37 | |
38 |
|
39 |
|
40 |
|
41 | asElement() {
|
42 | return this;
|
43 | }
|
44 |
|
45 | |
46 |
|
47 |
|
48 | contentFrame() {return (fn => {
|
49 | const gen = fn.call(this);
|
50 | return new Promise((resolve, reject) => {
|
51 | function step(key, arg) {
|
52 | let info, value;
|
53 | try {
|
54 | info = gen[key](arg);
|
55 | value = info.value;
|
56 | } catch (error) {
|
57 | reject(error);
|
58 | return;
|
59 | }
|
60 | if (info.done) {
|
61 | resolve(value);
|
62 | } else {
|
63 | return Promise.resolve(value).then(
|
64 | value => {
|
65 | step('next', value);
|
66 | },
|
67 | err => {
|
68 | step('throw', err);
|
69 | });
|
70 | }
|
71 | }
|
72 | return step('next');
|
73 | });
|
74 | })(function*(){
|
75 | const nodeInfo = (yield this._client.send('DOM.describeNode', {
|
76 | objectId: this._remoteObject.objectId
|
77 | }));
|
78 | if (typeof nodeInfo.node.frameId !== 'string')
|
79 | return null;
|
80 | return this._frameManager.frame(nodeInfo.node.frameId);
|
81 | });}
|
82 |
|
83 | _scrollIntoViewIfNeeded() {return (fn => {
|
84 | const gen = fn.call(this);
|
85 | return new Promise((resolve, reject) => {
|
86 | function step(key, arg) {
|
87 | let info, value;
|
88 | try {
|
89 | info = gen[key](arg);
|
90 | value = info.value;
|
91 | } catch (error) {
|
92 | reject(error);
|
93 | return;
|
94 | }
|
95 | if (info.done) {
|
96 | resolve(value);
|
97 | } else {
|
98 | return Promise.resolve(value).then(
|
99 | value => {
|
100 | step('next', value);
|
101 | },
|
102 | err => {
|
103 | step('throw', err);
|
104 | });
|
105 | }
|
106 | }
|
107 | return step('next');
|
108 | });
|
109 | })(function*(){
|
110 | const error = (yield this.executionContext().evaluate(/* async */(element, pageJavascriptEnabled) => {return (fn => {
|
111 | const gen = fn.call(this);
|
112 | return new Promise((resolve, reject) => {
|
113 | function step(key, arg) {
|
114 | let info, value;
|
115 | try {
|
116 | info = gen[key](arg);
|
117 | value = info.value;
|
118 | } catch (error) {
|
119 | reject(error);
|
120 | return;
|
121 | }
|
122 | if (info.done) {
|
123 | resolve(value);
|
124 | } else {
|
125 | return Promise.resolve(value).then(
|
126 | value => {
|
127 | step('next', value);
|
128 | },
|
129 | err => {
|
130 | step('throw', err);
|
131 | });
|
132 | }
|
133 | }
|
134 | return step('next');
|
135 | });
|
136 | })(function*(){
|
137 | if (!element.isConnected)
|
138 | return 'Node is detached from document';
|
139 | if (element.nodeType !== Node.ELEMENT_NODE)
|
140 | return 'Node is not of type HTMLElement';
|
141 |
|
142 | if (!pageJavascriptEnabled) {
|
143 | element.scrollIntoView({block: 'center', inline: 'center', behavior: 'instant'});
|
144 | return false;
|
145 | }
|
146 | const visibleRatio = (yield new Promise(resolve => {
|
147 | const observer = new IntersectionObserver(entries => {
|
148 | resolve(entries[0].intersectionRatio);
|
149 | observer.disconnect();
|
150 | });
|
151 | observer.observe(element);
|
152 | }));
|
153 | if (visibleRatio !== 1.0)
|
154 | element.scrollIntoView({block: 'center', inline: 'center', behavior: 'instant'});
|
155 | return false;
|
156 | });}, this, this._page._javascriptEnabled));
|
157 | if (error)
|
158 | throw new Error(error);
|
159 | });}
|
160 |
|
161 | |
162 |
|
163 |
|
164 | _clickablePoint() {return (fn => {
|
165 | const gen = fn.call(this);
|
166 | return new Promise((resolve, reject) => {
|
167 | function step(key, arg) {
|
168 | let info, value;
|
169 | try {
|
170 | info = gen[key](arg);
|
171 | value = info.value;
|
172 | } catch (error) {
|
173 | reject(error);
|
174 | return;
|
175 | }
|
176 | if (info.done) {
|
177 | resolve(value);
|
178 | } else {
|
179 | return Promise.resolve(value).then(
|
180 | value => {
|
181 | step('next', value);
|
182 | },
|
183 | err => {
|
184 | step('throw', err);
|
185 | });
|
186 | }
|
187 | }
|
188 | return step('next');
|
189 | });
|
190 | })(function*(){
|
191 | const result = (yield this._client.send('DOM.getContentQuads', {
|
192 | objectId: this._remoteObject.objectId
|
193 | }).catch(debugError));
|
194 | if (!result || !result.quads.length)
|
195 | throw new Error('Node is either not visible or not an HTMLElement');
|
196 |
|
197 | const quads = result.quads.map(quad => this._fromProtocolQuad(quad)).filter(quad => computeQuadArea(quad) > 1);
|
198 | if (!quads.length)
|
199 | throw new Error('Node is either not visible or not an HTMLElement');
|
200 |
|
201 | const quad = quads[0];
|
202 | let x = 0;
|
203 | let y = 0;
|
204 | for (const point of quad) {
|
205 | x += point.x;
|
206 | y += point.y;
|
207 | }
|
208 | return {
|
209 | x: x / 4,
|
210 | y: y / 4
|
211 | };
|
212 | });}
|
213 |
|
214 | |
215 |
|
216 |
|
217 | _getBoxModel() {
|
218 | return this._client.send('DOM.getBoxModel', {
|
219 | objectId: this._remoteObject.objectId
|
220 | }).catch(error => debugError(error));
|
221 | }
|
222 |
|
223 | |
224 |
|
225 |
|
226 |
|
227 | _fromProtocolQuad(quad) {
|
228 | return [
|
229 | {x: quad[0], y: quad[1]},
|
230 | {x: quad[2], y: quad[3]},
|
231 | {x: quad[4], y: quad[5]},
|
232 | {x: quad[6], y: quad[7]}
|
233 | ];
|
234 | }
|
235 |
|
236 | hover() {return (fn => {
|
237 | const gen = fn.call(this);
|
238 | return new Promise((resolve, reject) => {
|
239 | function step(key, arg) {
|
240 | let info, value;
|
241 | try {
|
242 | info = gen[key](arg);
|
243 | value = info.value;
|
244 | } catch (error) {
|
245 | reject(error);
|
246 | return;
|
247 | }
|
248 | if (info.done) {
|
249 | resolve(value);
|
250 | } else {
|
251 | return Promise.resolve(value).then(
|
252 | value => {
|
253 | step('next', value);
|
254 | },
|
255 | err => {
|
256 | step('throw', err);
|
257 | });
|
258 | }
|
259 | }
|
260 | return step('next');
|
261 | });
|
262 | })(function*(){
|
263 | (yield this._scrollIntoViewIfNeeded());
|
264 | const {x, y} = (yield this._clickablePoint());
|
265 | (yield this._page.mouse.move(x, y));
|
266 | });}
|
267 |
|
268 | |
269 |
|
270 |
|
271 | click(options = {}) {return (fn => {
|
272 | const gen = fn.call(this);
|
273 | return new Promise((resolve, reject) => {
|
274 | function step(key, arg) {
|
275 | let info, value;
|
276 | try {
|
277 | info = gen[key](arg);
|
278 | value = info.value;
|
279 | } catch (error) {
|
280 | reject(error);
|
281 | return;
|
282 | }
|
283 | if (info.done) {
|
284 | resolve(value);
|
285 | } else {
|
286 | return Promise.resolve(value).then(
|
287 | value => {
|
288 | step('next', value);
|
289 | },
|
290 | err => {
|
291 | step('throw', err);
|
292 | });
|
293 | }
|
294 | }
|
295 | return step('next');
|
296 | });
|
297 | })(function*(){
|
298 | (yield this._scrollIntoViewIfNeeded());
|
299 | const {x, y} = (yield this._clickablePoint());
|
300 | (yield this._page.mouse.click(x, y, options));
|
301 | });}
|
302 |
|
303 | |
304 |
|
305 |
|
306 |
|
307 | uploadFile(...filePaths) {return (fn => {
|
308 | const gen = fn.call(this);
|
309 | return new Promise((resolve, reject) => {
|
310 | function step(key, arg) {
|
311 | let info, value;
|
312 | try {
|
313 | info = gen[key](arg);
|
314 | value = info.value;
|
315 | } catch (error) {
|
316 | reject(error);
|
317 | return;
|
318 | }
|
319 | if (info.done) {
|
320 | resolve(value);
|
321 | } else {
|
322 | return Promise.resolve(value).then(
|
323 | value => {
|
324 | step('next', value);
|
325 | },
|
326 | err => {
|
327 | step('throw', err);
|
328 | });
|
329 | }
|
330 | }
|
331 | return step('next');
|
332 | });
|
333 | })(function*(){
|
334 | const files = filePaths.map(filePath => path.resolve(filePath));
|
335 | const objectId = this._remoteObject.objectId;
|
336 | return this._client.send('DOM.setFileInputFiles', { objectId, files });
|
337 | });}
|
338 |
|
339 | tap() {return (fn => {
|
340 | const gen = fn.call(this);
|
341 | return new Promise((resolve, reject) => {
|
342 | function step(key, arg) {
|
343 | let info, value;
|
344 | try {
|
345 | info = gen[key](arg);
|
346 | value = info.value;
|
347 | } catch (error) {
|
348 | reject(error);
|
349 | return;
|
350 | }
|
351 | if (info.done) {
|
352 | resolve(value);
|
353 | } else {
|
354 | return Promise.resolve(value).then(
|
355 | value => {
|
356 | step('next', value);
|
357 | },
|
358 | err => {
|
359 | step('throw', err);
|
360 | });
|
361 | }
|
362 | }
|
363 | return step('next');
|
364 | });
|
365 | })(function*(){
|
366 | (yield this._scrollIntoViewIfNeeded());
|
367 | const {x, y} = (yield this._clickablePoint());
|
368 | (yield this._page.touchscreen.tap(x, y));
|
369 | });}
|
370 |
|
371 | focus() {return (fn => {
|
372 | const gen = fn.call(this);
|
373 | return new Promise((resolve, reject) => {
|
374 | function step(key, arg) {
|
375 | let info, value;
|
376 | try {
|
377 | info = gen[key](arg);
|
378 | value = info.value;
|
379 | } catch (error) {
|
380 | reject(error);
|
381 | return;
|
382 | }
|
383 | if (info.done) {
|
384 | resolve(value);
|
385 | } else {
|
386 | return Promise.resolve(value).then(
|
387 | value => {
|
388 | step('next', value);
|
389 | },
|
390 | err => {
|
391 | step('throw', err);
|
392 | });
|
393 | }
|
394 | }
|
395 | return step('next');
|
396 | });
|
397 | })(function*(){
|
398 | (yield this.executionContext().evaluate(element => element.focus(), this));
|
399 | });}
|
400 |
|
401 | |
402 |
|
403 |
|
404 |
|
405 | type(text, options) {return (fn => {
|
406 | const gen = fn.call(this);
|
407 | return new Promise((resolve, reject) => {
|
408 | function step(key, arg) {
|
409 | let info, value;
|
410 | try {
|
411 | info = gen[key](arg);
|
412 | value = info.value;
|
413 | } catch (error) {
|
414 | reject(error);
|
415 | return;
|
416 | }
|
417 | if (info.done) {
|
418 | resolve(value);
|
419 | } else {
|
420 | return Promise.resolve(value).then(
|
421 | value => {
|
422 | step('next', value);
|
423 | },
|
424 | err => {
|
425 | step('throw', err);
|
426 | });
|
427 | }
|
428 | }
|
429 | return step('next');
|
430 | });
|
431 | })(function*(){
|
432 | (yield this.focus());
|
433 | (yield this._page.keyboard.type(text, options));
|
434 | });}
|
435 |
|
436 | |
437 |
|
438 |
|
439 |
|
440 | press(key, options) {return (fn => {
|
441 | const gen = fn.call(this);
|
442 | return new Promise((resolve, reject) => {
|
443 | function step(key, arg) {
|
444 | let info, value;
|
445 | try {
|
446 | info = gen[key](arg);
|
447 | value = info.value;
|
448 | } catch (error) {
|
449 | reject(error);
|
450 | return;
|
451 | }
|
452 | if (info.done) {
|
453 | resolve(value);
|
454 | } else {
|
455 | return Promise.resolve(value).then(
|
456 | value => {
|
457 | step('next', value);
|
458 | },
|
459 | err => {
|
460 | step('throw', err);
|
461 | });
|
462 | }
|
463 | }
|
464 | return step('next');
|
465 | });
|
466 | })(function*(){
|
467 | (yield this.focus());
|
468 | (yield this._page.keyboard.press(key, options));
|
469 | });}
|
470 |
|
471 | |
472 |
|
473 |
|
474 | boundingBox() {return (fn => {
|
475 | const gen = fn.call(this);
|
476 | return new Promise((resolve, reject) => {
|
477 | function step(key, arg) {
|
478 | let info, value;
|
479 | try {
|
480 | info = gen[key](arg);
|
481 | value = info.value;
|
482 | } catch (error) {
|
483 | reject(error);
|
484 | return;
|
485 | }
|
486 | if (info.done) {
|
487 | resolve(value);
|
488 | } else {
|
489 | return Promise.resolve(value).then(
|
490 | value => {
|
491 | step('next', value);
|
492 | },
|
493 | err => {
|
494 | step('throw', err);
|
495 | });
|
496 | }
|
497 | }
|
498 | return step('next');
|
499 | });
|
500 | })(function*(){
|
501 | const result = (yield this._getBoxModel());
|
502 |
|
503 | if (!result)
|
504 | return null;
|
505 |
|
506 | const quad = result.model.border;
|
507 | const x = Math.min(quad[0], quad[2], quad[4], quad[6]);
|
508 | const y = Math.min(quad[1], quad[3], quad[5], quad[7]);
|
509 | const width = Math.max(quad[0], quad[2], quad[4], quad[6]) - x;
|
510 | const height = Math.max(quad[1], quad[3], quad[5], quad[7]) - y;
|
511 |
|
512 | return {x, y, width, height};
|
513 | });}
|
514 |
|
515 | |
516 |
|
517 |
|
518 | boxModel() {return (fn => {
|
519 | const gen = fn.call(this);
|
520 | return new Promise((resolve, reject) => {
|
521 | function step(key, arg) {
|
522 | let info, value;
|
523 | try {
|
524 | info = gen[key](arg);
|
525 | value = info.value;
|
526 | } catch (error) {
|
527 | reject(error);
|
528 | return;
|
529 | }
|
530 | if (info.done) {
|
531 | resolve(value);
|
532 | } else {
|
533 | return Promise.resolve(value).then(
|
534 | value => {
|
535 | step('next', value);
|
536 | },
|
537 | err => {
|
538 | step('throw', err);
|
539 | });
|
540 | }
|
541 | }
|
542 | return step('next');
|
543 | });
|
544 | })(function*(){
|
545 | const result = (yield this._getBoxModel());
|
546 |
|
547 | if (!result)
|
548 | return null;
|
549 |
|
550 | const {content, padding, border, margin, width, height} = result.model;
|
551 | return {
|
552 | content: this._fromProtocolQuad(content),
|
553 | padding: this._fromProtocolQuad(padding),
|
554 | border: this._fromProtocolQuad(border),
|
555 | margin: this._fromProtocolQuad(margin),
|
556 | width,
|
557 | height
|
558 | };
|
559 | });}
|
560 |
|
561 | |
562 |
|
563 |
|
564 |
|
565 |
|
566 | screenshot(options = {}) {return (fn => {
|
567 | const gen = fn.call(this);
|
568 | return new Promise((resolve, reject) => {
|
569 | function step(key, arg) {
|
570 | let info, value;
|
571 | try {
|
572 | info = gen[key](arg);
|
573 | value = info.value;
|
574 | } catch (error) {
|
575 | reject(error);
|
576 | return;
|
577 | }
|
578 | if (info.done) {
|
579 | resolve(value);
|
580 | } else {
|
581 | return Promise.resolve(value).then(
|
582 | value => {
|
583 | step('next', value);
|
584 | },
|
585 | err => {
|
586 | step('throw', err);
|
587 | });
|
588 | }
|
589 | }
|
590 | return step('next');
|
591 | });
|
592 | })(function*(){
|
593 | let needsViewportReset = false;
|
594 |
|
595 | let boundingBox = (yield this.boundingBox());
|
596 | assert(boundingBox, 'Node is either not visible or not an HTMLElement');
|
597 |
|
598 | const viewport = this._page.viewport();
|
599 |
|
600 | if (boundingBox.width > viewport.width || boundingBox.height > viewport.height) {
|
601 | const newViewport = {
|
602 | width: Math.max(viewport.width, Math.ceil(boundingBox.width)),
|
603 | height: Math.max(viewport.height, Math.ceil(boundingBox.height)),
|
604 | };
|
605 | (yield this._page.setViewport(Object.assign({}, viewport, newViewport)));
|
606 |
|
607 | needsViewportReset = true;
|
608 | }
|
609 |
|
610 | (yield this._scrollIntoViewIfNeeded());
|
611 |
|
612 | boundingBox = (yield this.boundingBox());
|
613 | assert(boundingBox, 'Node is either not visible or not an HTMLElement');
|
614 |
|
615 | const { layoutViewport: { pageX, pageY } } = (yield this._client.send('Page.getLayoutMetrics'));
|
616 |
|
617 | const clip = Object.assign({}, boundingBox);
|
618 | clip.x += pageX;
|
619 | clip.y += pageY;
|
620 |
|
621 | const imageData = (yield this._page.screenshot(Object.assign({}, {
|
622 | clip
|
623 | }, options)));
|
624 |
|
625 | if (needsViewportReset)
|
626 | (yield this._page.setViewport(viewport));
|
627 |
|
628 | return imageData;
|
629 | });}
|
630 |
|
631 | |
632 |
|
633 |
|
634 |
|
635 | $(selector) {return (fn => {
|
636 | const gen = fn.call(this);
|
637 | return new Promise((resolve, reject) => {
|
638 | function step(key, arg) {
|
639 | let info, value;
|
640 | try {
|
641 | info = gen[key](arg);
|
642 | value = info.value;
|
643 | } catch (error) {
|
644 | reject(error);
|
645 | return;
|
646 | }
|
647 | if (info.done) {
|
648 | resolve(value);
|
649 | } else {
|
650 | return Promise.resolve(value).then(
|
651 | value => {
|
652 | step('next', value);
|
653 | },
|
654 | err => {
|
655 | step('throw', err);
|
656 | });
|
657 | }
|
658 | }
|
659 | return step('next');
|
660 | });
|
661 | })(function*(){
|
662 | const handle = (yield this.executionContext().evaluateHandle(
|
663 | (element, selector) => element.querySelector(selector),
|
664 | this, selector
|
665 | ));
|
666 | const element = handle.asElement();
|
667 | if (element)
|
668 | return element;
|
669 | (yield handle.dispose());
|
670 | return null;
|
671 | });}
|
672 |
|
673 | |
674 |
|
675 |
|
676 |
|
677 | $$(selector) {return (fn => {
|
678 | const gen = fn.call(this);
|
679 | return new Promise((resolve, reject) => {
|
680 | function step(key, arg) {
|
681 | let info, value;
|
682 | try {
|
683 | info = gen[key](arg);
|
684 | value = info.value;
|
685 | } catch (error) {
|
686 | reject(error);
|
687 | return;
|
688 | }
|
689 | if (info.done) {
|
690 | resolve(value);
|
691 | } else {
|
692 | return Promise.resolve(value).then(
|
693 | value => {
|
694 | step('next', value);
|
695 | },
|
696 | err => {
|
697 | step('throw', err);
|
698 | });
|
699 | }
|
700 | }
|
701 | return step('next');
|
702 | });
|
703 | })(function*(){
|
704 | const arrayHandle = (yield this.executionContext().evaluateHandle(
|
705 | (element, selector) => element.querySelectorAll(selector),
|
706 | this, selector
|
707 | ));
|
708 | const properties = (yield arrayHandle.getProperties());
|
709 | (yield arrayHandle.dispose());
|
710 | const result = [];
|
711 | for (const property of properties.values()) {
|
712 | const elementHandle = property.asElement();
|
713 | if (elementHandle)
|
714 | result.push(elementHandle);
|
715 | }
|
716 | return result;
|
717 | });}
|
718 |
|
719 | |
720 |
|
721 |
|
722 |
|
723 |
|
724 |
|
725 | $eval(selector, pageFunction, ...args) {return (fn => {
|
726 | const gen = fn.call(this);
|
727 | return new Promise((resolve, reject) => {
|
728 | function step(key, arg) {
|
729 | let info, value;
|
730 | try {
|
731 | info = gen[key](arg);
|
732 | value = info.value;
|
733 | } catch (error) {
|
734 | reject(error);
|
735 | return;
|
736 | }
|
737 | if (info.done) {
|
738 | resolve(value);
|
739 | } else {
|
740 | return Promise.resolve(value).then(
|
741 | value => {
|
742 | step('next', value);
|
743 | },
|
744 | err => {
|
745 | step('throw', err);
|
746 | });
|
747 | }
|
748 | }
|
749 | return step('next');
|
750 | });
|
751 | })(function*(){
|
752 | const elementHandle = (yield this.$(selector));
|
753 | if (!elementHandle)
|
754 | throw new Error(`Error: failed to find element matching selector "${selector}"`);
|
755 | const result = (yield this.executionContext().evaluate(pageFunction, elementHandle, ...args));
|
756 | (yield elementHandle.dispose());
|
757 | return result;
|
758 | });}
|
759 |
|
760 | |
761 |
|
762 |
|
763 |
|
764 |
|
765 |
|
766 | $$eval(selector, pageFunction, ...args) {return (fn => {
|
767 | const gen = fn.call(this);
|
768 | return new Promise((resolve, reject) => {
|
769 | function step(key, arg) {
|
770 | let info, value;
|
771 | try {
|
772 | info = gen[key](arg);
|
773 | value = info.value;
|
774 | } catch (error) {
|
775 | reject(error);
|
776 | return;
|
777 | }
|
778 | if (info.done) {
|
779 | resolve(value);
|
780 | } else {
|
781 | return Promise.resolve(value).then(
|
782 | value => {
|
783 | step('next', value);
|
784 | },
|
785 | err => {
|
786 | step('throw', err);
|
787 | });
|
788 | }
|
789 | }
|
790 | return step('next');
|
791 | });
|
792 | })(function*(){
|
793 | const arrayHandle = (yield this.executionContext().evaluateHandle(
|
794 | (element, selector) => Array.from(element.querySelectorAll(selector)),
|
795 | this, selector
|
796 | ));
|
797 |
|
798 | const result = (yield this.executionContext().evaluate(pageFunction, arrayHandle, ...args));
|
799 | (yield arrayHandle.dispose());
|
800 | return result;
|
801 | });}
|
802 |
|
803 | |
804 |
|
805 |
|
806 |
|
807 | $x(expression) {return (fn => {
|
808 | const gen = fn.call(this);
|
809 | return new Promise((resolve, reject) => {
|
810 | function step(key, arg) {
|
811 | let info, value;
|
812 | try {
|
813 | info = gen[key](arg);
|
814 | value = info.value;
|
815 | } catch (error) {
|
816 | reject(error);
|
817 | return;
|
818 | }
|
819 | if (info.done) {
|
820 | resolve(value);
|
821 | } else {
|
822 | return Promise.resolve(value).then(
|
823 | value => {
|
824 | step('next', value);
|
825 | },
|
826 | err => {
|
827 | step('throw', err);
|
828 | });
|
829 | }
|
830 | }
|
831 | return step('next');
|
832 | });
|
833 | })(function*(){
|
834 | const arrayHandle = (yield this.executionContext().evaluateHandle(
|
835 | (element, expression) => {
|
836 | const document = element.ownerDocument || element;
|
837 | const iterator = document.evaluate(expression, element, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);
|
838 | const array = [];
|
839 | let item;
|
840 | while ((item = iterator.iterateNext()))
|
841 | array.push(item);
|
842 | return array;
|
843 | },
|
844 | this, expression
|
845 | ));
|
846 | const properties = (yield arrayHandle.getProperties());
|
847 | (yield arrayHandle.dispose());
|
848 | const result = [];
|
849 | for (const property of properties.values()) {
|
850 | const elementHandle = property.asElement();
|
851 | if (elementHandle)
|
852 | result.push(elementHandle);
|
853 | }
|
854 | return result;
|
855 | });}
|
856 |
|
857 | |
858 |
|
859 |
|
860 | isIntersectingViewport() {
|
861 | return this.executionContext().evaluate( element => {return (fn => {
|
862 | const gen = fn.call(this);
|
863 | return new Promise((resolve, reject) => {
|
864 | function step(key, arg) {
|
865 | let info, value;
|
866 | try {
|
867 | info = gen[key](arg);
|
868 | value = info.value;
|
869 | } catch (error) {
|
870 | reject(error);
|
871 | return;
|
872 | }
|
873 | if (info.done) {
|
874 | resolve(value);
|
875 | } else {
|
876 | return Promise.resolve(value).then(
|
877 | value => {
|
878 | step('next', value);
|
879 | },
|
880 | err => {
|
881 | step('throw', err);
|
882 | });
|
883 | }
|
884 | }
|
885 | return step('next');
|
886 | });
|
887 | })(function*(){
|
888 | const visibleRatio = (yield new Promise(resolve => {
|
889 | const observer = new IntersectionObserver(entries => {
|
890 | resolve(entries[0].intersectionRatio);
|
891 | observer.disconnect();
|
892 | });
|
893 | observer.observe(element);
|
894 | }));
|
895 | return visibleRatio > 0;
|
896 | });}, this);
|
897 | }
|
898 | }
|
899 |
|
900 | function computeQuadArea(quad) {
|
901 |
|
902 |
|
903 | let area = 0;
|
904 | for (let i = 0; i < quad.length; ++i) {
|
905 | const p1 = quad[i];
|
906 | const p2 = quad[(i + 1) % quad.length];
|
907 | area += (p1.x * p2.y - p2.x * p1.y) / 2;
|
908 | }
|
909 | return area;
|
910 | }
|
911 |
|
912 | module.exports = {ElementHandle};
|
913 | helper.tracePublicAPI(ElementHandle);
|