jl165 merging heads
[LibreOffice.git] / testautomation / framework / tools / includes / fileoperations.inc
blob1ba484df58afffa2dd08199487bb7a76b4a36578
1 'encoding UTF-8  Do not remove or change this line!
2 '**************************************************************************
3 ' DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 ' Copyright 2000, 2010 Oracle and/or its affiliates.
7 ' OpenOffice.org - a multi-platform office productivity suite
9 ' This file is part of OpenOffice.org.
11 ' OpenOffice.org is free software: you can redistribute it and/or modify
12 ' it under the terms of the GNU Lesser General Public License version 3
13 ' only, as published by the Free Software Foundation.
15 ' OpenOffice.org is distributed in the hope that it will be useful,
16 ' but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ' GNU Lesser General Public License version 3 for more details
19 ' (a copy is included in the LICENSE file that accompanied this code).
21 ' You should have received a copy of the GNU Lesser General Public License
22 ' version 3 along with OpenOffice.org.  If not, see
23 ' <http://www.openoffice.org/license.html>
24 ' for a copy of the LGPLv3 License.
26 '/************************************************************************
28 '* owner : gregor.hartmann@oracle.com
30 '* short description : check the internal file dialog ( extended tests )
32 '\******************************************************************************
34 function hSaveLoadDelSuccess( cFile as string ) as integer
36     '///<h3>Successfully save, close, load, close and delete a file</h3>
37     '///<i>Uses</i>: framework\tools\t_stringtools.inc<br><br>
38     '///<u>Input</u>:
39     '///<ol>
40     '///+<li>Filename incl. extension (string)</li>
41     '///</ol>
42     '///<u>Returns</u>:
43     '///<ol>
44     '///+<li>Errorcode (integer)</li>
45     '///<ul>
46     '///+<li>0  = all ok</li>
47     '///+<li>1  = Saving failed</li>
48     '///+<li>2  = Closing the file failed</li> 
49     '///+<li>3  = Reloading failed</li>
50     '///+<li>4  = Closing the file failed</li>
51     '///+<li>5  = Deleting failed</li>
52     '///+<li>-1 = Post operation error</li>
53     '///</ul>
54     '///</ol>
55     '///<u>Description</u>:
56     '///<ul>
57     
58     dim brc as boolean
59     dim cFileExt as string : cFileExt = cFile & hGetSuffix( "current" )
60     
61     const CFN = "hSaveLoadDelSuccess::"
62     
63     printlog( "" )
64     printlog( CFN & "Enter with option: " & cFile & "/" & cFileExt )
65     
66     '///+<li>Close the navigator if it exists</li>
67     kontext "Navigator"
68     hCloseDialog( Navigator, "close,optional" )
70     '///+<li>Save the current file, overwriting existing</li>
71     brc = hSaveFileExpectSuccess( cFile , TRUE ) ' save and overwrite
72     if ( brc ) then
74         '///+<li>Close the file</li>
75         brc = hDestroyDocument()
76         if ( brc ) then
77         
78             '///+<li>Reload the file</li>
79             brc = hLoadFileExpectSuccess( cFileExt )
80             if ( brc ) then
81             
82                 '///+<li>Close the document</li>
83                 brc = hDestroyDocument()
84                 if ( brc ) then
85                 
86                     '///+<li>Delete the file via FileOpen</li>
87                     brc = hDeleteFileViaFileOpen( cFileExt )
88                     if ( brc ) then
89                         printlog( CFN & "Save, close, load, close, delete ok" )
90                         hSaveLoadDelSuccess() = 0
91                     else
92                         warnlog( CFN & "Failed to delete file" )
93                         hSaveLoadDelSuccess() = 5
94                     endif
95                 
96                 else
97                     warnlog( CFN & "Failed to close file" ) 
98                     hSaveLoadDelSuccess() = 4
99                 endif
100             
101             else
102                 warnlog( CFN & "Failed to load file" )
103                 hSaveLoadDelSuccess() = 3
104             endif
105         
106         
107         else
108             warnlog( CFN & "Closing file failed" )
109             hSaveLoadDelSuccess() = 2
110         endif
111     
112     else
113         warnlog( CFN & "Saving failed" )
114         hSaveLoadDelSuccess() = 1
115     endif
116     
117     '///+<li>Close possible Messagebox (#i33946#)</li>
118     kontext "active"
119     if ( active.exists( 1 ) ) then
120         printlog( CFN & "Unexpected message: " & active.getText() )
121         qaerrorlog( "#i33946# - message when deleting last document in folder" )
122         active.ok()
123         hSaveLoadDelSuccess() = 6
124     endif
125     
126     '///+<li>Close document</li>
127     brc = hDestroyDocument()
129     '///</ul>
131 end function
133 '*******************************************************************************
135 function hLoadFileExpectSuccess( fpath as string ) as boolean
137     '///<h3>Load a file where failure is expected</h3>
138     '///<i>Uses</i>: framework\tools\t_stringtools.inc<br><br>
139     '///<i>This function is quite similar to hLoadFile but does much less
140     '///+ errorhandling so the information of the type of failure is a little
141     '///+ more exact</i><br><br>
142     '///<u>Input</u>:
143     '///<ol>
144     '///+<li>Filename incl. extension (string)</li>
145     '///</ol>
146     '///<u>Returns</u>:
147     '///<ol>
148     '///+<li>Errorcondition (boolean)</li>
149     '///<ul>
150     '///+<li>TRUE = File was loaded without problems</li>
151     '///+<li>FALSE = Any error</li>
152     '///</ul>
153     '///</ol>
154     '///<u>Description</u>:
155     '///<ul>
156     
157     dim brc as boolean : brc = true
158         
159     const CFN = "hLoadFileExpectSuccess::"
160     printlog( CFN & "Enter with option: " & fpath )
162     '///+<li>Click FileOpen (or use the menu)</li>
163     hUseAsyncSlot( "FileOpen" )
165     '///+<li>Enter the filename (with extension)</li>
166     'printlog( " - Type the filepath/name into the entryfield" )
167     Kontext "OeffnenDLG"
168     if ( OeffnenDlg.exists( 1 ) ) then
169         DateiName.setText( fpath )
171         '///+<li>Click &quot;Open&quot;</li>
172         'printlog( " - Click 'Open'" )
173         oeffnen.click()    
175         '///+<li>Watch out for an unexpected messagebox<br>
176         Kontext "Active"
177         if ( Active.Exists( 1 ) ) then
178             printlog( "Unexpected active: " & active.getText() )
179             Active.OK()
181             Kontext "OeffnenDLG"
182             OeffnenDLG.cancel()
183             brc = false
184         endif
186         '///+recover in case of error so the test has a chance to continue</li>
187         ' try to recover in case of failure so the test can continue.
188         Kontext "OeffnenDLG"
189         if ( Oeffnen.exists( 1 ) ) then
190             warnlog( "The file was not opened, it doesn't appear to exist" )
191             OeffnenDLG.cancel()
192             brc = false
193         endif
194     else
195         warnlog( CFN & "File Open dialog did not open" )
196     endif
198     '///+<li>Return the errorcode</li>
199     printlog( CFN & "Exit with result: " & brc )
200     hLoadFileExpectSuccess() = brc
201     '///</ul>
202     
203 end function
205 '*******************************************************************************
207 function hSaveFileExpectSuccess( fpath as string , bReplace as boolean ) as boolean
209     '///<h3>Save a file with optional replace where success is expected</h3>
210     '///<i>Uses</i>: framework\tools\t_stringtools.inc<br><br>
211     '///<u>Input</u>:
212     '///<ol>
213     '///+<li>Filename incl. extension (string)</li>
214     '///+<li>Replace file (boolean)</li>
215     '///<ul>
216     '///+<li>TRUE = Replace the file</li>
217     '///+<li>FALSE = Do not replace the file</li>
218     '///</ul>
219     '///</ol>
220     '///<u>Returns</u>:
221     '///<ol>
222     '///+<li>Errorcondition (boolean)</li>
223     '///<ul>
224     '///+<li>TRUE = There was an error loading the file (as expected)</li>
225     '///+<li>FALSE = The file was loaded without problems/any other error</li>
226     '///</ul>
227     '///</ol>
228     '///<u>Description</u>:
229     '///<ul>
231     const CFN = "hSaveFileExpectSuccess::" 
232     dim brc as boolean : brc = true
233         
234     dim iDocumentCount as integer
235     
236     printlog( CFN & "Enter with options: " & fpath & ", " & bReplace )
238     '///+<li>Make sure we start from the backing window</li>
239     hFileCloseAll()
242     '///+<li>Open a new document</li>
243     hCreateDocument()
245     '///+<li>Click &quot;Save As...&quot;</li>
246     hUseAsyncSlot( "FileSaveAs" )
248     Kontext "SpeichernDlg"
249     if ( SpeichernDlg.exists( 1 ) ) then
251         '///+<li>Enter a filename (with extension)</li>
252         Dateiname.setText( hGetWorkPath() & fpath )
254         '///+<li>Click &quot;Save&quot;</li>
255         speichern.click()
257         '///+<li>If the file exists, say &quot;yes&quot; to replace it</li>
258         if ( bReplace ) then
259             Kontext "Active"
260             if ( active.exists( 2 ) ) then
261                 printlog( CFN & "Overwriting file" )
262                 printlog( "Message: " & active.getText() )
263                 try
264                     active.yes()
265                 catch
266                     warnlog( CFN & "Unexpected active - no YES button available." )
267                     printlog( CFN & "if any this should have been overwrite warning." )
268                 endcatch
269             endif
270         endif
272         '///+<li>Handle any unexpected errormessage with &quot;OK&quot;</li>
273         Kontext "Active"
274         if ( active.exists( 2 ) ) then
275             printlog( CFN & "Unexpected active: " & active.getText() )
276             brc = false
277             active.ok()
278         endif
279         
280         '///+<li>The FileSave dialog should be closed at this point</li>
281         kontext "SpeichernDlg"
282         if ( SpeichernDlg.exists( 1 ) ) then
283             warnlog( CFN & "File Save dialog is still open, it should be closed" )
284             SpeichernDlg.cancel()
285         endif
286     else
287         warnlog( CFN & "Failed to open File Open dialog" )
288         brc = false
289     endif
291     '///+<li>Verify that exactly one document is open</li>
292     if( getDocumentCount <> 1  ) then        
293         warnlog( CFN & "Incorrect number of open documents" )
294         brc = false
295     endif
296     
297     printlog( CFN & "Exit with result: " & brc )
298     hSaveFileExpectSuccess() = brc
299     '///</ul>
301 end function
303 '*******************************************************************************
305 function hSaveFileExpectFailure( fpath as string , errortype as integer ) as boolean
307     '///<h3>Save a file where failure is expected</h3>
308     '///<i>Uses</i>: framework\tools\t_stringtools.inc<br><br>
309     '///<u>Input</u>:
310     '///<ol>
311     '///+<li>Filename incl. extension (string)</li>
312     '///+<li>Errortype (Integer). Valid options are:</li>
313     '///<ul>
314     '///+<li>0 = Invalid characters in string</li>
315     '///+<li>1 = Filename is interpreted as device</li>
316     '///</ul>
317     '///</ol>
318     '///<u>Returns</u>:
319     '///<ol>
320     '///+<li>Errorcondition (boolean)</li>
321     '///<ul>
322     '///+<li>TRUE = <b><i>There was an error saving the file (as expected)</i></b></li>
323     '///+<li>FALSE = The file was saved without problems/any other error</li>
324     '///</ul>
325     '///</ol>
326     '///<u>Description</u>:
327     '///<ul>
328     
329     ' currently we have two different kinds of failure
330     ' 1. File cannot be saved due to invalid character(s)
331     '    This is errortype = 0
332     ' 2. File cannot be saved because the given name is interpreted as device
333     '    This is errortype = 1
334     ' This sequence tries to save a document with an invalid name. The errormsg
335     ' is closed, the filedialog cancelled and the file closed. 
336     
337     dim brc as boolean : brc = false
338         
339     dim cMsg as string
340         
341     const CFN = "hSaveFileExpectFailure::"
343     '///+<li>Open an new document</li>
344     hCreateDocument()
345     
346     '///+<li>Click &quot;Save As;&quot;</li>
347     printlog( "" )
348     printlog( CFN & "Enter with options: " & fpath & ", " & errortype )
349     hUseAsyncSlot( "FileSaveAs" )
350     
351     '///+<li>Enter the filename</li>
352     kontext "SpeichernDlg"
353     if ( SpeichernDlg.exists( 1 ) ) then
354     
355         Kontext "SpeichernDlg"
356         Dateiname.setText( fpath )
358         '///+<li>Click &quot;Save&quot;</li>
359         speichern.click()
361         '///<ul>
362         '///+<li>Handle invalid characters (Errormessages)</li>
363         select case( errortype )
364         case 0: 
365         
366                 brc = false
367         
368                 Kontext "Active"
369                 if ( Active.exists( 1 ) ) then
370                 
371                         printlog( CFN & "Check for possible overwrite warning..." )
372                         try
373                                 Active.yes()
374                                 printlog( CFN & "Closed Messagebox with <YES>" )
375                                 printlog( CFN & "This was the overwrite warning" )
376                     catch
377                     endcatch
378                 endif
380                 Kontext "Active"
381                 if ( Active.exists( 1 ) ) then
382                     cMsg = active.getText()
383                         try
384                                 Active.ok()
385                                 printlog( CFN & "Closed Save-Failure warning with <OK>" )
386                                 brc = true
387                     catch
388                         qaerrorlog( CFN & "Unknown dialog encountered, <OK> failed:" )
389                         printlog( cMSG )
390                     endcatch
391                 endif
392                 
393                 
394         case 1: 
396             Kontext "Active"
397             if ( Active.exists( 1 ) ) then
398                 printlog( CFN & "Expected active: " & active.getText() )
399                 Active.OK()
401                 Kontext "SpeichernDLG"
402                 if ( SpeichernDlg.exists( 1 ) ) then
403                     SpeichernDLG.cancel()
404                     brc = true
405                 else
406                     warnlog( CFN & "File Save dialog is missing. Bad!" )
407                     brc = false
408                 endif
409             else
410                 warnlog( CFN & "Errormessage for '/', '\' or ':' is missing" )
411                 brc = false
412             endif
413             
414         end select
416     else
417         warnlog( CFN & "Failed to open File Save dialog" )
418     endif
419     '///</ul>
421     '///+<li>Close the document</li>
422     hDestroyDocument()
423     
424     '///+<li>Return errorcondition</li>
425     printlog( CFN & "Exit with status: " & brc )
426     hSaveFileExpectFailure() = brc
427     
428     '///</ul>
429     
430 end function
432 '*******************************************************************************
434 function hDeleteFileViaFileOpen( cFile as String ) as boolean
436     '///<h3>Delete a file using the File Open dialog</h3>
437     '///<i>Uses</i>: framework\tools\t_stringtools.inc<br><br>
438     '///<u>Input</u>:
439     '///<ol>
440     '///+<li>Filename incl. extension (string)</li>
441     '///</ol>
442     '///<u>Returns</u>:
443     '///<ol>
444     '///+<li>Errorcondition (boolean)</li>
445     '///<ul>
446     '///+<li>TRUE = File was deleted</li>
447     '///+<li>FALSE = Any other condition</li>
448     '///</ul>
449     '///</ol>
450     '///<u>Description</u>:
451     '///<ul>
452     
453     ' This sub deletes a file by name. No errors or warnings are expected.
454     ' if everything goes well,  we're back to the originating document after
455     ' completion.
457     dim iSelectedFilePosition as integer
458     dim brc as boolean
459     
460     const CFN = "hDeleteFileViaFileOpen::"
461     printlog( CFN & "Enter with option: " & cFile )
462     
463     ' Test for wildcards - the dialog cannot handle those
464     if ( instr( cFile , "?" ) <> 0 ) then
465         warnlog( CFN & "Incorrect call to function, wildcards are not allowed" )
466         hDeleteFileViaFileOpen() = false
467         exit function
468     endif
469     
470     ' Test for wildcards - the dialog cannot handle those
471     if ( instr( cFile , "*" ) <> 0 ) then
472         warnlog( CFN & "Incorrect call to function, wildcards are not allowed" )
473         hDeleteFileViaFileOpen() = false
474         exit function
475     endif    
477     '///+<li>Click &quot;File Open&quot;</li>
478     hUseAsyncSlot( "FileOpen" )
479     
480     '///+<li>Look for the requested file, get the position from the filelist</li>
481     Kontext "OeffnenDLG"
482     iSelectedFilePosition = hFindFileObjectViaFileOpen( cFile )
483     
484     '///+<li>If the file exists, delete it + verify, if not: Warn and exit</li>
485     if ( iSelectedFilePosition > 0 ) then
486     
487         ' Enable for debug
488         'printlog( CFN & "Requested file: " & cFile )
489         'printlog( CFN & "Object is at..: " & iSelectedFilePosition )
490         'printlog( CFN & "Object name is: " & DateiAuswahl.getSelText()  )
491         
492         DateiAuswahl.TypeKeys( "<DELETE>" )
493     
494         ' Confirm deletion. This dialog should always pop up when deleting
495         Kontext "ConfirmDelete"
496         if ( ConfirmDelete.exists( 1 ) ) then
497             Delete.click()
498             printlog( CFN & "Deleted file: " & cFile ) 
499         else
500             warnlog( CFN & "Messagebox to confirm deletion of file is missing" )
501         endif
502     
503         ' Handle possible Warnings/Errormessages. If no errors happen, 
504         ' verify that the file has been deleted. Note that this dialog will not
505         ' change the returnvalue. 
506         Kontext "Active"
507         if( Active.exists( 1 ) ) then
508             warnlog( CFN & "Unexpected active: " & active.getText() )
509             Active.OK()
510         endif 
511         
512         ' Verify that the file does no longer exist in the filelist.
513         iSelectedFilePosition = hFindFileObjectViaFileOpen( cFile )
514         if ( iSelectedFilePosition = 0 ) then
515             brc = true
516         else
517             brc = false
518         endif
519         
520     else
521     
522         brc = false
523         warnlog( CFN & "File not found in workdir: " & cFile )
524         
525     endif
526     
527     '///+<li>Cancel the FileOpen dialog</li>
528     kontext "OeffnenDlg"
529     OeffnenDlg.cancel()
530     
531     '///+<li>Return errorcondition</li>
532     printlog( CFN & "Exit with result: " & brc )
533     hDeleteFileViaFileOpen() = brc
534     
535     '///</ul>
537 end function
539 '*******************************************************************************
541 function hNameGen_append( iDecChar as long ) as string
543     '///<h3>Create a filename with specified character at the end</h3>
544     '///<u>Input</u>:
545     '///<ol>
546     '///+<li>Character as number (integer)</li>
547     '///<ul>
548     '///+<li>Only positive numbers are allowed</li>
549     '///+<li>Respect integer boundaries</li>
550     '///</ul>
551     '///</ol>
552     '///<u>Returns</u>:
553     '///<ol>
554     '///+<li>A filename containing a special character at the end (before suffix)</li>
555     '///</ol>
556     '///<u>Description</u>:
557     '///<ul>
559     dim cFile as string
560     const CFN = "hNameGen_append::"
562     '///+<li>Create a string &quot;test&quot;, append ascii char</li>
563     cFile = "test" & CHR$( iDecChar )
564     printlog( CFN & "ASCII " & iDecChar & " appended, len = " & len( cFile ) )
565     if ( len( cFile ) <> 5 ) then
566         warnlog( CFN & "Character not appended" )
567     endif
569     '///+<li>Return the new filename</li>
570     hNameGen_append() = cFile
571     
572     '///</ul>
574 end function
576 '*******************************************************************************
578 function hNameGen_lead( iDecChar as long ) as string
580     '///<h3>Create a filename with specified character at the beginning</h3>
581     '///<u>Input</u>:
582     '///<ol>
583     '///+<li>Character as number (integer)</li>
584     '///<ul>
585     '///+<li>Only positive numbers are allowed</li>
586     '///+<li>Respect integer boundaries</li>
587     '///</ul>
588     '///</ol>
589     '///<u>Returns</u>:
590     '///<ol>
591     '///+<li>A filename containing a special character at the beginning</li>
592     '///</ol>
593     '///<u>Description</u>:
594     '///<ul>
595    
596     dim cFile as string
597     const CFN = "hNameGen_lead::"
599     '///+<li>Create a string beginning with ascii char and append &quot;test&quot</li>
600     cFile = CHR$( iDecChar ) & "test"
601     printlog( CFN & "ASCII " & iDecChar & " prepended, len = " & len( cFile ) )
602     if ( len( cFile ) <> 5 ) then
603         warnlog( CFN & "Character not prepended" )
604     endif
606     '///+<li>Return the new filename</li>
607     hNamegen_lead() = cFile
608     
609     '///</ul>
611 end function
613 '*******************************************************************************
615 function hFindFileObjectViaFileOpen( cName as string ) as integer
617     '///<h3>Get the position of a filesystem object in filelist of fileopen dialog</h3>
618     '///<i>Note that the filepicker must be open in order to use this function.<br>
619     '///The object with the given name is selected when leaving the function.</i><br>
620     '///<u>Input</u>:
621     '///<ol>
622     '///+<li>Filename incl. extension (string) but without any pathseparator</li>
623     '///</ol>
624     '///<u>Returns</u>:
625     '///<ol>
626     '///+<li>Position of the object in the filepicker list</li>
627     '///<ul>
628     '///+<li>0: Object not found</li>
629     '///+<li>&gt; 0: Position</li>
630     '///</ul>
631     '///</ol>
632     '///<u>Description</u>:
633     '///<ul>
634     
635     const CFN = "hFindFileObjectViaFileOpen::"
637     dim iCurrentObject as integer
638     dim cCurrentName as string
639     dim iFileOpenItemCount as integer
640     
641     dim iPos as integer : iPos = 0
642     dim cPath as string : cPath = cName
644     dim iPathItems as integer
645     dim asPathList( 100 ) as string
646     
647     dim iWait as integer ' some increment variable
648     
649     printlog( CFN & "Enter with option (File): " & cName )
651     '///+<li>If a fully qualified path has been provided we have to split it up</li>
652     iPathItems = DirNameList( cPath, asPathList() )
653     'printlog( CFN & "iPathItems (new): " & iPathItems )
655     if ( iPathItems > 3 ) then
656         cName = asPathList( iPathItems )
657         printlog( CFN & "CNAME from plist: " & cName )
659         '///+<li>And walk to the directory</li>
660         cPath = left( cPath , ( len ( cPath ) - len( cName ) ) - 1 )
661         printlog( CFN & "CPATH from plist: " & cPath )
663         kontext "OeffnenDlg"
664         if ( OeffnenDlg.exists( 1 ) ) then
665             Dateiname.setText( cPath )
666             Oeffnen.click()
667         else
668             warnlog( CFN & "File Open dialog is not open" )
669             hFindFileObjectViaFileOpen() = 0
670             exit function
671         endif
672     endif
673         
674     '///+<li>get the number of items in the filepicker</li>
675     kontext "OeffnenDlg"
676     iWait = 0
677     while ( DateiAuswahl.getItemCount() = 0 )
678         wait( 100 )
679         iWait = iWait + 1
680         if ( iWait = 10 ) then
681             warnlog( "Filepicker is not populated within reasonable timeframe, aborting" )
682             kontext "OeffnenDlg"
683             OeffnenDlg.Close()
684             hFindFileObjectViaFileOpen() = 0
685             exit function
686         else
687             printlog( "Waiting for filepicker list to populate..." )
688         endif
689     wend
690     
691     iFileOpenItemCount = DateiAuswahl.getItemCount()
692     
693     '///+<li>Jump to the first item in the filelist and select it with SPACE</li>
694     DateiAuswahl.typeKeys( "<HOME>" )
695     DateiAuswahl.typeKeys( "<SPACE>" )
696     
697     '///+<li>Navigate through the list until we find the requested object</li>
698     for iCurrentObject = 1 to iFileOpenItemCount
699     
700         cCurrentName = DateiAuswahl.getSelText()
701     
702         if ( cCurrentName = cName ) then
703             iPos = iCurrentObject
704             exit for
705         endif
706         
707         DateiAuswahl.typeKeys( "<DOWN>" )
708         
709     next iCurrentObject
710     
711     '///+<li>Print some info to the log</li>
712     if ( iPos > 0 ) then
713         printlog( CFN & "Exit: Found item [" & cName & "] at pos: " & iPos )
714     else
715         printlog( CFN & "Exit: Requested item [" & cName & "] was not found." )
716     endif
717     
718     '///+<li>return the position or 0 on failure</li>
719     hFindFileObjectViaFileOpen() = iPos
720     '///</ul>
721     
722 end function