Subversion-Projekte lars-tiefland.prado

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
    /**
3
     *	Base include file for SimpleTest
4
     *	@package	SimpleTest
5
     *	@subpackage	WebTester
6
     *	@version	$Id: page.php 1398 2006-09-08 19:31:03Z xue $
7
     */
8
 
9
    /**#@+
10
     *	include other SimpleTest class files
11
     */
12
    require_once(dirname(__FILE__) . '/http.php');
13
    require_once(dirname(__FILE__) . '/parser.php');
14
    require_once(dirname(__FILE__) . '/tag.php');
15
    require_once(dirname(__FILE__) . '/form.php');
16
    require_once(dirname(__FILE__) . '/selector.php');
17
    /**#@-*/
18
 
19
    /**
20
     *    Creates tags and widgets given HTML tag
21
     *    attributes.
22
	 *    @package SimpleTest
23
	 *    @subpackage WebTester
24
     */
25
    class SimpleTagBuilder {
26
 
27
        /**
28
         *    Factory for the tag objects. Creates the
29
         *    appropriate tag object for the incoming tag name
30
         *    and attributes.
31
         *    @param string $name        HTML tag name.
32
         *    @param hash $attributes    Element attributes.
33
         *    @return SimpleTag          Tag object.
34
         *    @access public
35
         */
36
        function createTag($name, $attributes) {
37
            static $map = array(
38
                    'a' => 'SimpleAnchorTag',
39
                    'title' => 'SimpleTitleTag',
40
                    'button' => 'SimpleButtonTag',
41
                    'textarea' => 'SimpleTextAreaTag',
42
                    'option' => 'SimpleOptionTag',
43
                    'label' => 'SimpleLabelTag',
44
                    'form' => 'SimpleFormTag',
45
                    'frame' => 'SimpleFrameTag');
46
            $attributes = $this->_keysToLowerCase($attributes);
47
            if (array_key_exists($name, $map)) {
48
                $tag_class = $map[$name];
49
                return new $tag_class($attributes);
50
            } elseif ($name == 'select') {
51
                return $this->_createSelectionTag($attributes);
52
            } elseif ($name == 'input') {
53
                return $this->_createInputTag($attributes);
54
            }
55
            return new SimpleTag($name, $attributes);
56
        }
57
 
58
        /**
59
         *    Factory for selection fields.
60
         *    @param hash $attributes    Element attributes.
61
         *    @return SimpleTag          Tag object.
62
         *    @access protected
63
         */
64
        function _createSelectionTag($attributes) {
65
            if (isset($attributes['multiple'])) {
66
                return new MultipleSelectionTag($attributes);
67
            }
68
            return new SimpleSelectionTag($attributes);
69
        }
70
 
71
        /**
72
         *    Factory for input tags.
73
         *    @param hash $attributes    Element attributes.
74
         *    @return SimpleTag          Tag object.
75
         *    @access protected
76
         */
77
        function _createInputTag($attributes) {
78
            if (! isset($attributes['type'])) {
79
                return new SimpleTextTag($attributes);
80
            }
81
            $type = strtolower(trim($attributes['type']));
82
            $map = array(
83
                    'submit' => 'SimpleSubmitTag',
84
                    'image' => 'SimpleImageSubmitTag',
85
                    'checkbox' => 'SimpleCheckboxTag',
86
                    'radio' => 'SimpleRadioButtonTag',
87
                    'text' => 'SimpleTextTag',
88
                    'hidden' => 'SimpleTextTag',
89
                    'password' => 'SimpleTextTag',
90
                    'file' => 'SimpleUploadTag');
91
            if (array_key_exists($type, $map)) {
92
                $tag_class = $map[$type];
93
                return new $tag_class($attributes);
94
            }
95
            return false;
96
        }
97
 
98
        /**
99
         *    Make the keys lower case for case insensitive look-ups.
100
         *    @param hash $map   Hash to convert.
101
         *    @return hash       Unchanged values, but keys lower case.
102
         *    @access private
103
         */
104
        function _keysToLowerCase($map) {
105
            $lower = array();
106
            foreach ($map as $key => $value) {
107
                $lower[strtolower($key)] = $value;
108
            }
109
            return $lower;
110
        }
111
    }
112
 
113
    /**
114
     *    SAX event handler. Maintains a list of
115
     *    open tags and dispatches them as they close.
116
	 *    @package SimpleTest
117
	 *    @subpackage WebTester
118
     */
119
    class SimplePageBuilder extends SimpleSaxListener {
120
        protected $_tags;
121
        protected $_page;
122
        protected $_private_content_tag;
123
 
124
        /**
125
         *    Sets the builder up empty.
126
         *    @access public
127
         */
128
        function SimplePageBuilder() {
129
            $this->SimpleSaxListener();
130
        }
131
 
132
        /**
133
         *    Frees up any references so as to allow the PHP garbage
134
         *    collection from unset() to work.
135
         *    @access public
136
         */
137
        function free() {
138
            unset($this->_tags);
139
            unset($this->_page);
140
            unset($this->_private_content_tags);
141
        }
142
 
143
        /**
144
         *    Reads the raw content and send events
145
         *    into the page to be built.
146
         *    @param $response SimpleHttpResponse  Fetched response.
147
         *    @return SimplePage                   Newly parsed page.
148
         *    @access public
149
         */
150
        function &parse($response) {
151
            $this->_tags = array();
152
            $this->_page = $this->_createPage($response);
153
            $parser = $this->_createParser($this);
154
            $parser->parse($response->getContent());
155
            $this->_page->acceptPageEnd();
156
            return $this->_page;
157
        }
158
 
159
        /**
160
         *    Creates an empty page.
161
         *    @return SimplePage        New unparsed page.
162
         *    @access protected
163
         */
164
        function &_createPage($response) {
165
            $page = new SimplePage($response);
166
            return $page;
167
        }
168
 
169
        /**
170
         *    Creates the parser used with the builder.
171
         *    @param $listener SimpleSaxListener   Target of parser.
172
         *    @return SimpleSaxParser              Parser to generate
173
         *                                         events for the builder.
174
         *    @access protected
175
         */
176
        function &_createParser($listener) {
177
            $parser = new SimpleHtmlSaxParser($listener);
178
            return $parser;
179
        }
180
 
181
        /**
182
         *    Start of element event. Opens a new tag.
183
         *    @param string $name         Element name.
184
         *    @param hash $attributes     Attributes without content
185
         *                                are marked as true.
186
         *    @return boolean             False on parse error.
187
         *    @access public
188
         */
189
        function startElement($name, $attributes) {
190
            $factory = new SimpleTagBuilder();
191
            $tag = $factory->createTag($name, $attributes);
192
            if (! $tag) {
193
                return true;
194
            }
195
            if ($tag->getTagName() == 'label') {
196
                $this->_page->acceptLabelStart($tag);
197
                $this->_openTag($tag);
198
                return true;
199
            }
200
            if ($tag->getTagName() == 'form') {
201
                $this->_page->acceptFormStart($tag);
202
                return true;
203
            }
204
            if ($tag->getTagName() == 'frameset') {
205
                $this->_page->acceptFramesetStart($tag);
206
                return true;
207
            }
208
            if ($tag->getTagName() == 'frame') {
209
                $this->_page->acceptFrame($tag);
210
                return true;
211
            }
212
            if ($tag->isPrivateContent() && ! isset($this->_private_content_tag)) {
213
                $this->_private_content_tag = $tag;
214
            }
215
            if ($tag->expectEndTag()) {
216
                $this->_openTag($tag);
217
                return true;
218
            }
219
            $this->_page->acceptTag($tag);
220
            return true;
221
        }
222
 
223
        /**
224
         *    End of element event.
225
         *    @param string $name        Element name.
226
         *    @return boolean            False on parse error.
227
         *    @access public
228
         */
229
        function endElement($name) {
230
            if ($name == 'label') {
231
                $this->_page->acceptLabelEnd();
232
                return true;
233
            }
234
            if ($name == 'form') {
235
                $this->_page->acceptFormEnd();
236
                return true;
237
            }
238
            if ($name == 'frameset') {
239
                $this->_page->acceptFramesetEnd();
240
                return true;
241
            }
242
            if ($this->_hasNamedTagOnOpenTagStack($name)) {
243
                $tag = array_pop($this->_tags[$name]);
244
                if ($tag->isPrivateContent() && $this->_private_content_tag->getTagName() == $name) {
245
                    unset($this->_private_content_tag);
246
                }
247
                $this->_addContentTagToOpenTags($tag);
248
                $this->_page->acceptTag($tag);
249
                return true;
250
            }
251
            return true;
252
        }
253
 
254
        /**
255
         *    Test to see if there are any open tags awaiting
256
         *    closure that match the tag name.
257
         *    @param string $name        Element name.
258
         *    @return boolean            True if any are still open.
259
         *    @access private
260
         */
261
        function _hasNamedTagOnOpenTagStack($name) {
262
            return isset($this->_tags[$name]) && (count($this->_tags[$name]) > 0);
263
        }
264
 
265
        /**
266
         *    Unparsed, but relevant data. The data is added
267
         *    to every open tag.
268
         *    @param string $text        May include unparsed tags.
269
         *    @return boolean            False on parse error.
270
         *    @access public
271
         */
272
        function addContent($text) {
273
            if (isset($this->_private_content_tag)) {
274
                $this->_private_content_tag->addContent($text);
275
            } else {
276
                $this->_addContentToAllOpenTags($text);
277
            }
278
            return true;
279
        }
280
 
281
        /**
282
         *    Any content fills all currently open tags unless it
283
         *    is part of an option tag.
284
         *    @param string $text        May include unparsed tags.
285
         *    @access private
286
         */
287
        function _addContentToAllOpenTags($text) {
288
            foreach (array_keys($this->_tags) as $name) {
289
                for ($i = 0, $count = count($this->_tags[$name]); $i < $count; $i++) {
290
                    $this->_tags[$name][$i]->addContent($text);
291
                }
292
            }
293
        }
294
 
295
        /**
296
         *    Parsed data in tag form. The parsed tag is added
297
         *    to every open tag. Used for adding options to select
298
         *    fields only.
299
         *    @param SimpleTag $tag        Option tags only.
300
         *    @access private
301
         */
302
        function _addContentTagToOpenTags($tag) {
303
            if ($tag->getTagName() != 'option') {
304
                return;
305
            }
306
            foreach (array_keys($this->_tags) as $name) {
307
                for ($i = 0, $count = count($this->_tags[$name]); $i < $count; $i++) {
308
                    $this->_tags[$name][$i]->addTag($tag);
309
                }
310
            }
311
        }
312
 
313
        /**
314
         *    Opens a tag for receiving content. Multiple tags
315
         *    will be receiving input at the same time.
316
         *    @param SimpleTag $tag        New content tag.
317
         *    @access private
318
         */
319
        function _openTag($tag) {
320
            $name = $tag->getTagName();
321
            if (! in_array($name, array_keys($this->_tags))) {
322
                $this->_tags[$name] = array();
323
            }
324
            $this->_tags[$name][] = $tag;
325
        }
326
    }
327
 
328
    /**
329
     *    A wrapper for a web page.
330
	 *    @package SimpleTest
331
	 *    @subpackage WebTester
332
     */
333
    class SimplePage {
334
        protected $_links;
335
        protected $_title;
336
        protected $_last_widget;
337
        protected $_label;
338
        protected $_left_over_labels;
339
        protected $_open_forms;
340
        protected $_complete_forms;
341
        protected $_frameset;
342
        protected $_frames;
343
        protected $_frameset_nesting_level;
344
        protected $_transport_error;
345
        protected $_raw;
346
        protected $_text;
347
        protected $_sent;
348
        protected $_headers;
349
        protected $_method;
350
        protected $_url;
351
        protected $_request_data;
352
 
353
        /**
354
         *    Parses a page ready to access it's contents.
355
         *    @param SimpleHttpResponse $response     Result of HTTP fetch.
356
         *    @access public
357
         */
358
        function SimplePage($response = false) {
359
            $this->_links = array();
360
            $this->_title = false;
361
            $this->_left_over_labels = array();
362
            $this->_open_forms = array();
363
            $this->_complete_forms = array();
364
            $this->_frameset = false;
365
            $this->_frames = array();
366
            $this->_frameset_nesting_level = 0;
367
            $this->_text = false;
368
            if ($response) {
369
                $this->_extractResponse($response);
370
            } else {
371
                $this->_noResponse();
372
            }
373
        }
374
 
375
        /**
376
         *    Extracts all of the response information.
377
         *    @param SimpleHttpResponse $response    Response being parsed.
378
         *    @access private
379
         */
380
        function _extractResponse($response) {
381
            $this->_transport_error = $response->getError();
382
            $this->_raw = $response->getContent();
383
            $this->_sent = $response->getSent();
384
            $this->_headers = $response->getHeaders();
385
            $this->_method = $response->getMethod();
386
            $this->_url = $response->getUrl();
387
            $this->_request_data = $response->getRequestData();
388
        }
389
 
390
        /**
391
         *    Sets up a missing response.
392
         *    @access private
393
         */
394
        function _noResponse() {
395
            $this->_transport_error = 'No page fetched yet';
396
            $this->_raw = false;
397
            $this->_sent = false;
398
            $this->_headers = false;
399
            $this->_method = 'GET';
400
            $this->_url = false;
401
            $this->_request_data = false;
402
        }
403
 
404
        /**
405
         *    Original request as bytes sent down the wire.
406
         *    @return mixed              Sent content.
407
         *    @access public
408
         */
409
        function getRequest() {
410
            return $this->_sent;
411
        }
412
 
413
        /**
414
         *    Accessor for raw text of page.
415
         *    @return string        Raw unparsed content.
416
         *    @access public
417
         */
418
        function getRaw() {
419
            return $this->_raw;
420
        }
421
 
422
        /**
423
         *    Accessor for plain text of page as a text browser
424
         *    would see it.
425
         *    @return string        Plain text of page.
426
         *    @access public
427
         */
428
        function getText() {
429
            if (! $this->_text) {
430
                $this->_text = SimpleHtmlSaxParser::normalise($this->_raw);
431
            }
432
            return $this->_text;
433
        }
434
 
435
        /**
436
         *    Accessor for raw headers of page.
437
         *    @return string       Header block as text.
438
         *    @access public
439
         */
440
        function getHeaders() {
441
            if ($this->_headers) {
442
                return $this->_headers->getRaw();
443
            }
444
            return false;
445
        }
446
 
447
        /**
448
         *    Original request method.
449
         *    @return string        GET, POST or HEAD.
450
         *    @access public
451
         */
452
        function getMethod() {
453
            return $this->_method;
454
        }
455
 
456
        /**
457
         *    Original resource name.
458
         *    @return SimpleUrl        Current url.
459
         *    @access public
460
         */
461
        function getUrl() {
462
            return $this->_url;
463
        }
464
 
465
        /**
466
         *    Original request data.
467
         *    @return mixed              Sent content.
468
         *    @access public
469
         */
470
        function getRequestData() {
471
            return $this->_request_data;
472
        }
473
 
474
        /**
475
         *    Accessor for last error.
476
         *    @return string        Error from last response.
477
         *    @access public
478
         */
479
        function getTransportError() {
480
            return $this->_transport_error;
481
        }
482
 
483
        /**
484
         *    Accessor for current MIME type.
485
         *    @return string    MIME type as string; e.g. 'text/html'
486
         *    @access public
487
         */
488
        function getMimeType() {
489
            if ($this->_headers) {
490
                return $this->_headers->getMimeType();
491
            }
492
            return false;
493
        }
494
 
495
        /**
496
         *    Accessor for HTTP response code.
497
         *    @return integer    HTTP response code received.
498
         *    @access public
499
         */
500
        function getResponseCode() {
501
            if ($this->_headers) {
502
                return $this->_headers->getResponseCode();
503
            }
504
            return false;
505
        }
506
 
507
        /**
508
         *    Accessor for last Authentication type. Only valid
509
         *    straight after a challenge (401).
510
         *    @return string    Description of challenge type.
511
         *    @access public
512
         */
513
        function getAuthentication() {
514
            if ($this->_headers) {
515
                return $this->_headers->getAuthentication();
516
            }
517
            return false;
518
        }
519
 
520
        /**
521
         *    Accessor for last Authentication realm. Only valid
522
         *    straight after a challenge (401).
523
         *    @return string    Name of security realm.
524
         *    @access public
525
         */
526
        function getRealm() {
527
            if ($this->_headers) {
528
                return $this->_headers->getRealm();
529
            }
530
            return false;
531
        }
532
 
533
        /**
534
         *    Accessor for current frame focus. Will be
535
         *    false as no frames.
536
         *    @return array    Always empty.
537
         *    @access public
538
         */
539
        function getFrameFocus() {
540
            return array();
541
        }
542
 
543
        /**
544
         *    Sets the focus by index. The integer index starts from 1.
545
         *    @param integer $choice    Chosen frame.
546
         *    @return boolean           Always false.
547
         *    @access public
548
         */
549
        function setFrameFocusByIndex($choice) {
550
            return false;
551
        }
552
 
553
        /**
554
         *    Sets the focus by name. Always fails for a leaf page.
555
         *    @param string $name    Chosen frame.
556
         *    @return boolean        False as no frames.
557
         *    @access public
558
         */
559
        function setFrameFocus($name) {
560
            return false;
561
        }
562
 
563
        /**
564
         *    Clears the frame focus. Does nothing for a leaf page.
565
         *    @access public
566
         */
567
        function clearFrameFocus() {
568
        }
569
 
570
        /**
571
         *    Adds a tag to the page.
572
         *    @param SimpleTag $tag        Tag to accept.
573
         *    @access public
574
         */
575
        function acceptTag($tag) {
576
            if ($tag->getTagName() == "a") {
577
                $this->_addLink($tag);
578
            } elseif ($tag->getTagName() == "title") {
579
                $this->_setTitle($tag);
580
            } elseif ($this->_isFormElement($tag->getTagName())) {
581
                for ($i = 0; $i < count($this->_open_forms); $i++) {
582
                    $this->_open_forms[$i]->addWidget($tag);
583
                }
584
                $this->_last_widget = $tag;
585
            }
586
        }
587
 
588
        /**
589
         *    Opens a label for a described widget.
590
         *    @param SimpleFormTag $tag      Tag to accept.
591
         *    @access public
592
         */
593
        function acceptLabelStart($tag) {
594
            $this->_label = $tag;
595
            unset($this->_last_widget);
596
        }
597
 
598
        /**
599
         *    Closes the most recently opened label.
600
         *    @access public
601
         */
602
        function acceptLabelEnd() {
603
            if (isset($this->_label)) {
604
                if (isset($this->_last_widget)) {
605
                    $this->_last_widget->setLabel($this->_label->getText());
606
                    unset($this->_last_widget);
607
                } else {
608
                    $this->_left_over_labels[] = SimpleTestCompatibility::copy($this->_label);
609
                }
610
                unset($this->_label);
611
            }
612
        }
613
 
614
        /**
615
         *    Tests to see if a tag is a possible form
616
         *    element.
617
         *    @param string $name     HTML element name.
618
         *    @return boolean         True if form element.
619
         *    @access private
620
         */
621
        function _isFormElement($name) {
622
            return in_array($name, array('input', 'button', 'textarea', 'select'));
623
        }
624
 
625
        /**
626
         *    Opens a form. New widgets go here.
627
         *    @param SimpleFormTag $tag      Tag to accept.
628
         *    @access public
629
         */
630
        function acceptFormStart($tag) {
631
            $this->_open_forms[] = new SimpleForm($tag, $this->getUrl());
632
        }
633
 
634
        /**
635
         *    Closes the most recently opened form.
636
         *    @access public
637
         */
638
        function acceptFormEnd() {
639
            if (count($this->_open_forms)) {
640
                $this->_complete_forms[] = array_pop($this->_open_forms);
641
            }
642
        }
643
 
644
        /**
645
         *    Opens a frameset. A frameset may contain nested
646
         *    frameset tags.
647
         *    @param SimpleFramesetTag $tag      Tag to accept.
648
         *    @access public
649
         */
650
        function acceptFramesetStart($tag) {
651
            if (! $this->_isLoadingFrames()) {
652
                $this->_frameset = $tag;
653
            }
654
            $this->_frameset_nesting_level++;
655
        }
656
 
657
        /**
658
         *    Closes the most recently opened frameset.
659
         *    @access public
660
         */
661
        function acceptFramesetEnd() {
662
            if ($this->_isLoadingFrames()) {
663
                $this->_frameset_nesting_level--;
664
            }
665
        }
666
 
667
        /**
668
         *    Takes a single frame tag and stashes it in
669
         *    the current frame set.
670
         *    @param SimpleFrameTag $tag      Tag to accept.
671
         *    @access public
672
         */
673
        function acceptFrame($tag) {
674
            if ($this->_isLoadingFrames()) {
675
                if ($tag->getAttribute('src')) {
676
                    $this->_frames[] = $tag;
677
                }
678
            }
679
        }
680
 
681
        /**
682
         *    Test to see if in the middle of reading
683
         *    a frameset.
684
         *    @return boolean        True if inframeset.
685
         *    @access private
686
         */
687
        function _isLoadingFrames() {
688
            if (! $this->_frameset) {
689
                return false;
690
            }
691
            return ($this->_frameset_nesting_level > 0);
692
        }
693
 
694
        /**
695
         *    Test to see if link is an absolute one.
696
         *    @param string $url     Url to test.
697
         *    @return boolean        True if absolute.
698
         *    @access protected
699
         */
700
        function _linkIsAbsolute($url) {
701
            $parsed = new SimpleUrl($url);
702
            return (boolean)($parsed->getScheme() && $parsed->getHost());
703
        }
704
 
705
        /**
706
         *    Adds a link to the page.
707
         *    @param SimpleAnchorTag $tag      Link to accept.
708
         *    @access protected
709
         */
710
        function _addLink($tag) {
711
            $this->_links[] = $tag;
712
        }
713
 
714
        /**
715
         *    Marker for end of complete page. Any work in
716
         *    progress can now be closed.
717
         *    @access public
718
         */
719
        function acceptPageEnd() {
720
            while (count($this->_open_forms)) {
721
                $this->_complete_forms[] = array_pop($this->_open_forms);
722
            }
723
            foreach ($this->_left_over_labels as $label) {
724
                for ($i = 0, $count = count($this->_complete_forms); $i < $count; $i++) {
725
                    $this->_complete_forms[$i]->attachLabelBySelector(
726
                            new SimpleById($label->getFor()),
727
                            $label->getText());
728
                }
729
            }
730
        }
731
 
732
        /**
733
         *    Test for the presence of a frameset.
734
         *    @return boolean        True if frameset.
735
         *    @access public
736
         */
737
        function hasFrames() {
738
            return (boolean)$this->_frameset;
739
        }
740
 
741
        /**
742
         *    Accessor for frame name and source URL for every frame that
743
         *    will need to be loaded. Immediate children only.
744
         *    @return boolean/array     False if no frameset or
745
         *                              otherwise a hash of frame URLs.
746
         *                              The key is either a numerical
747
         *                              base one index or the name attribute.
748
         *    @access public
749
         */
750
        function getFrameset() {
751
            if (! $this->_frameset) {
752
                return false;
753
            }
754
            $urls = array();
755
            for ($i = 0; $i < count($this->_frames); $i++) {
756
                $name = $this->_frames[$i]->getAttribute('name');
757
                $url = new SimpleUrl($this->_frames[$i]->getAttribute('src'));
758
                $urls[$name ? $name : $i + 1] = $url->makeAbsolute($this->getUrl());
759
            }
760
            return $urls;
761
        }
762
 
763
        /**
764
         *    Fetches a list of loaded frames.
765
         *    @return array/string    Just the URL for a single page.
766
         *    @access public
767
         */
768
        function getFrames() {
769
            $url = $this->getUrl();
770
            return $url->asString();
771
        }
772
 
773
        /**
774
         *    Accessor for a list of all fixed links.
775
         *    @return array   List of urls with scheme of
776
         *                    http or https and hostname.
777
         *    @access public
778
         */
779
        function getAbsoluteUrls() {
780
            $all = array();
781
            foreach ($this->_links as $link) {
782
                if ($this->_linkIsAbsolute($link->getHref())) {
783
                    $all[] = $link->getHref();
784
                }
785
            }
786
            return $all;
787
        }
788
 
789
        /**
790
         *    Accessor for a list of all relative links.
791
         *    @return array      List of urls without hostname.
792
         *    @access public
793
         */
794
        function getRelativeUrls() {
795
            $all = array();
796
            foreach ($this->_links as $link) {
797
                if (! $this->_linkIsAbsolute($link->getHref())) {
798
                    $all[] = $link->getHref();
799
                }
800
            }
801
            return $all;
802
        }
803
 
804
        /**
805
         *    Accessor for URLs by the link label. Label will match
806
         *    regardess of whitespace issues and case.
807
         *    @param string $label    Text of link.
808
         *    @return array           List of links with that label.
809
         *    @access public
810
         */
811
        function getUrlsByLabel($label) {
812
            $matches = array();
813
            foreach ($this->_links as $link) {
814
                if ($link->getText() == $label) {
815
                    $matches[] = $this->_getUrlFromLink($link);
816
                }
817
            }
818
            return $matches;
819
        }
820
 
821
        /**
822
         *    Accessor for a URL by the id attribute.
823
         *    @param string $id       Id attribute of link.
824
         *    @return SimpleUrl       URL with that id of false if none.
825
         *    @access public
826
         */
827
        function getUrlById($id) {
828
            foreach ($this->_links as $link) {
829
                if ($link->getAttribute('id') === (string)$id) {
830
                    return $this->_getUrlFromLink($link);
831
                }
832
            }
833
            return false;
834
        }
835
 
836
        /**
837
         *    Converts a link into a target URL.
838
         *    @param SimpleAnchor $link    Parsed link.
839
         *    @return SimpleUrl            URL with frame target if any.
840
         *    @access private
841
         */
842
        function _getUrlFromLink($link) {
843
            $url = $this->_makeAbsolute($link->getHref());
844
            if ($link->getAttribute('target')) {
845
                $url->setTarget($link->getAttribute('target'));
846
            }
847
            return $url;
848
        }
849
 
850
        /**
851
         *    Expands expandomatic URLs into fully qualified
852
         *    URLs.
853
         *    @param SimpleUrl $url        Relative URL.
854
         *    @return SimpleUrl            Absolute URL.
855
         *    @access protected
856
         */
857
        function _makeAbsolute($url) {
858
            if (! is_object($url)) {
859
                $url = new SimpleUrl($url);
860
            }
861
            return $url->makeAbsolute($this->getUrl());
862
        }
863
 
864
        /**
865
         *    Sets the title tag contents.
866
         *    @param SimpleTitleTag $tag    Title of page.
867
         *    @access protected
868
         */
869
        function _setTitle($tag) {
870
            $this->_title = $tag;
871
        }
872
 
873
        /**
874
         *    Accessor for parsed title.
875
         *    @return string     Title or false if no title is present.
876
         *    @access public
877
         */
878
        function getTitle() {
879
            if ($this->_title) {
880
                return $this->_title->getText();
881
            }
882
            return false;
883
        }
884
 
885
        /**
886
         *    Finds a held form by button label. Will only
887
         *    search correctly built forms.
888
         *    @param SimpleSelector $selector       Button finder.
889
         *    @return SimpleForm                    Form object containing
890
         *                                          the button.
891
         *    @access public
892
         */
893
        function &getFormBySubmit($selector) {
894
            for ($i = 0; $i < count($this->_complete_forms); $i++) {
895
                if ($this->_complete_forms[$i]->hasSubmit($selector)) {
896
                    return $this->_complete_forms[$i];
897
                }
898
            }
899
            $null = null;
900
            return $null;
901
        }
902
 
903
        /**
904
         *    Finds a held form by image using a selector.
905
         *    Will only search correctly built forms.
906
         *    @param SimpleSelector $selector  Image finder.
907
         *    @return SimpleForm               Form object containing
908
         *                                     the image.
909
         *    @access public
910
         */
911
        function &getFormByImage($selector) {
912
            for ($i = 0; $i < count($this->_complete_forms); $i++) {
913
                if ($this->_complete_forms[$i]->hasImage($selector)) {
914
                    return $this->_complete_forms[$i];
915
                }
916
            }
917
            $null = null;
918
            return $null;
919
        }
920
 
921
        /**
922
         *    Finds a held form by the form ID. A way of
923
         *    identifying a specific form when we have control
924
         *    of the HTML code.
925
         *    @param string $id     Form label.
926
         *    @return SimpleForm    Form object containing the matching ID.
927
         *    @access public
928
         */
929
        function &getFormById($id) {
930
            for ($i = 0; $i < count($this->_complete_forms); $i++) {
931
                if ($this->_complete_forms[$i]->getId() == $id) {
932
                    return $this->_complete_forms[$i];
933
                }
934
            }
935
            $null = null;
936
            return $null;
937
        }
938
 
939
        /**
940
         *    Sets a field on each form in which the field is
941
         *    available.
942
         *    @param SimpleSelector $selector    Field finder.
943
         *    @param string $value               Value to set field to.
944
         *    @return boolean                    True if value is valid.
945
         *    @access public
946
         */
947
        function setField($selector, $value) {
948
            $is_set = false;
949
            for ($i = 0; $i < count($this->_complete_forms); $i++) {
950
                if ($this->_complete_forms[$i]->setField($selector, $value)) {
951
                    $is_set = true;
952
                }
953
            }
954
            return $is_set;
955
        }
956
 
957
        /**
958
         *    Accessor for a form element value within a page.
959
         *    @param SimpleSelector $selector    Field finder.
960
         *    @return string/boolean             A string if the field is
961
         *                                       present, false if unchecked
962
         *                                       and null if missing.
963
         *    @access public
964
         */
965
        function getField($selector) {
966
            for ($i = 0; $i < count($this->_complete_forms); $i++) {
967
                $value = $this->_complete_forms[$i]->getValue($selector);
968
                if (isset($value)) {
969
                    return $value;
970
                }
971
            }
972
            return null;
973
        }
974
    }
975
?>