upForm Library Documentation

by Unknown Person, Version 1.3 rev 3

Table of Contents

Download upForm Library

Getting Started

HTML has existed in BYOND for quite a while, but the idea of using HTML to enhance a program's interface has not been commonly heard of in BYOND games. This library will let you create and define the behaviour of HTML interfaces without having to re-invent the wheel every time you write some browser interface code.

Defining and Displaying Pages

To define a form, all you need to do is define a child type from the datum /upForm and define some variables and procs. Here is a simple definition of a form that would display "hello" to the user's default browser.

upForm/mypage GenerateBody() UpdatePage("Hello")

The proc GenerateBody() is where you should write your code to generate the body of the page, implied by the name. GenerateBody() should always call UpdatePage() with the body text of the page being sent through the parameter. This proc must be implemented in any form you wish to define.

The above code won't do anything, since it's just a declaration of a new form. We can do something with this datum by using the upForm() proc defined in the library. This proc essentially creates and handles a form for you. It is the hub of all form instantiation. To create an instance of a form, call upForm(), sending the required data through the args. The following will display the form we defined above when the player logs in.

mob/Login() ..() upForm(src, /upForm/mypage)

There are four to use upForm(), but the most common method is to specify the target of the form (a mob, or a client) as the first arg, and then the path of the form (such as /upForm/mypage) as the second arg. You may call upForm() as many times as you want with the limitation that another page will not be interrupted.

On another note, upForm() returns the reference of the newly instantiated form. Therefore, you can store this elsewhere to handle it, such as the case if you want to externally close the form. To close a form, the most simple way is to delete the instance. Deleting it handles all of the final closing code. More of this will be explained in the next sections.

mob/Login() ..() // store the instance of the form object returned by upForm() var/upForm/mypage/F = upForm(src, /upForm/mypage) spawn(50) // Close the form externally by deleting it after 5 seconds if(F) del(F)

Form Types

There are a total of four types of forms you may define with the /upForm datum. This is determined by what the value of the /upForm variable form_type is. The variable may be one of the four constants:

UPFORM_BROWSER
The default value. The page will be displayed to the user's default form. If no interface has been defined for your program, then it will display to the browser tab.
UPFORM_INTERFACE
The page will be displayed to a specified interface, which should be defined in the form's form_name variable.
UPFORM_WINDOW
The page will be displayed in a unique window. The window's settings are defined through various /upForm variables prefixed with window.
UPFORM_CLOSEWINDOW
The page will be displayed in a unique window with the purpose of being manually Xed out by the user. This setting is fundamentally the same as UPFORM_WINDOW with the exception that the form instance is deleted right after the page is displayed to the viewers.

The default value for form_type is UPFORM_BROWSER, so if it is not specified, the form will display the page to the default browser of the user's interface. The example in the previous section displays the page to the user's default interface. The only difference between UPFORM_BROWSER and UPFORM_INTERFACE is that you specify the interface control's name to output to using the form_name variable.

Windows are a fairly common way of displaying data, because you can have as many windows as you want on the screen, and they have the flexibility of being resizable, movable, and closable. To create a windowed form, set the form_type of the form to UPFORM_WINDOW.

upForm/mywindow form_type = UPFORM_WINDOW GenerateBody() UpdatePage("This is a window") mob/Login() ..() // display an unclosable window upForm(src, /upForm/mywindow)

Windows also have other important settings that you may want to configure. The three variables used to configure window settings are window_title, window_size, and window_params. The window_title variable is what will be displayed as the title of the window. window_size is the size of the window using a "[width]x[height]" format. window_params is a variable that takes flags as settings that determine the nature of the window. The variable may take the following flags: UPFORM_CANNOT_CLOSE, UPFORM_CANNOT_RESIZE, UPFORM_CANNOT_MINIMIZE, and UPFORM_NO_TITLEBAR.

Because there is no way to catch the event that a window has been closed by the client, two form types have been defined for you. If you want the user to have the ability to keep the window up for as long as he or she wants, then you should define the form as a UPFORM_CLOSEWINDOW. By defining a window as a regular UPFORM_WINDOW, then you will need to handle the closing yourself, either by external or internal means. Close Windows are deleted right after their pages are displayed, so upForm() will return null.

Link Handling

The upForm datum handles all of the link handling for you in Topic(). You will not and should not touch the Topic() proc. The library has defined a Link() proc for you to handle communication from the player to the form. Its usage is similar to how you would override Topic() if you were to handle your own communication. Here is a simple example showing how you could have a window have a close link.

upForm/mywindow form_type = UPFORM_WINDOW Link(list/href_list, client/C) // if a close link was sent if(href_list["action"] == "close") // close the form by deleting it del(src) GenerateBody() var/page = {" This is a window that can be closed. <br /> <br /> <a href="byond://?src=\ref[src]&action=close">Close Window</a> "} UpdatePage(page) mob/Login() ..() upForm(src, /upForm/mywindow)

Link() has two args, one that is actively demonstrated. The href_list var is an associative list holding a name=value list of the params sent through the link. The second arg is the client who sent the link, so it is possible to have distinct behaviour of your forms depending on who sent it.

The great thing about the library is that it handles all automated verification for you. The client sent through Link() is guaranteed to be valid, as it is verified by the form datum. Nothing will happen if an unauthorized user attempts to send a link to a form that the user is not viewing.

Targets, Owners, and Hosts

Because of the numerous ways that you can represent a form's purpose and functionality, this library has defined a few variables for you to utilize externally from the form. When you call upForm(), the first arg of the proc is always the target of the form. The target can either be a client, or a list of clients (if a mob or list of clients or mobs is sent in a list, they will be automatically converted if possible). The target of a form is essentially the client or clients that the form will be directed at. Therefore, you can send an instance of a form to the screen of many clients if you want them all to view and control the same thing. More information about multi-viewer forms is discussed in the next section.

If the target of an owner is an individual client or mob, then a form's owner is set to that client. By defining owner for form objects, we can identify internally who the form belongs to. There is no owner if the target is a list of clients.

Another concept of forms is the host. The host of a form is what the form is about, or what the form is modifying. The host variable holds any type of datum, and is completely optional. The variable should be specified in the arg after the owner when calling upForm(). The purpose of defining host is if you want convenient and reliable access to a certain object that the form will commonly use. For example, if you want to create a browser interface that reads or modifies a certain object, such as another character in your game, then you will want to send the character reference through the second arg of upForm(). The following is an example on how you would use the host variable to view the health of another player.

upForm/viewhealth form_type = UPFORM_CLOSEWINDOW window_size = "200x80" GenerateBody() // use getHost() to get the reference of a form's host var/mob/M = src.getHost() var/page = {" [M.name]'s health: <br /> [M.health] / [M.maxhealth] "} UpdatePage(page) mob var health = 100 maxhealth = 100 verb view_health(mob/M in world) // display a form to src to view M's health // src is the target, while M is the host upForm(src, M, /upForm/viewhealth)

Notice how the verb calls upForm() with three arguments instead of two. If you want a host to be specified, then the second arg should be the reference of a host. For easy reference, here are the two main ways to call upForm():

upForm(target, form_path)
upForm(target, host, form_path)

When you are trying to access the host or the owner of a form, you should use getOwner() and getHost() respectively. You should use these procs to ensure that you are not going to access something that does not exist. If you did not specify a host (or in the case of multi- viewer forms or global forms, an owner), then you should not be calling getHost(). However, in the case the host is deleted while the form is running, you will want to manually check the host variable before calling getHost(), as the proc will call an assertion if a host does not exist. You will have to handle the possibility of the host being deleted while the form is still running.

CSS and JavaScript

You can enhance your interface by including CSS and JavaScript. All forms have the variables page_css and page_js. Respectively, they represent the CSS code and JavaScript code that will be displayed in that form. The upForm Library defines a predefined set of commonly used JavaScript functions, all prefixed with upForm_. Refer to the Reference at the end of this documentation for information about the predefined JavaScript functions. JavaScript can also be written by sending text through the second arg of UpdatePage(). When you generate your page, you may want in a certain case to send JavaScript code.

If you want all of your pages to look similar, you can define a global CSS by defining the global_css var belonging to the /upForm datum. The below example shows the usage of global_css, page_css, page_js, and sending JavaScript code through UpdatePage()

upForm/global_css = "body { background-color: #000000; color: #ffffff }" // all pages have black background and white text upForm/mypage page_css = "span.big { font-size: 16pt }" page_js = {"function myFunction() { alert("This is a JavaScript function called from UpdatePage()"); } "} GenerateBody() var/page = {"<span class="big">Big Text</span> <br /> Regular Text" UpdatePage(page, "myFunction();")

Library Features

upForm not only easily lets us define and associate forms, but it has some unique features unheard of from other browser or html libraries on BYOND. These features include easy support for resource handling, multiple viewers, form handling, and timers.

Resource Handling

Another convenient feature in the library is the ability to easily handle and send resources such as images and sounds to the client. The resources associative list defined in the datum is a list of runtime resources you want the form to send to the user when it is created. The key of the list is the resource, while the association is an optional name for the file you want to send.

You are also able to dynamically send resources to the viewers of a form. If you have a dynamically generated resource such as a custom icon, then you can use the LoadResource() proc defined in the form to send a resource that you could not send through the resources list. The first mandatory arg for the proc is the resource itself, and the second optional arg is the name of the resource. You should specify the name of the resource if it is dynamically generated. The following is a form demonstrating both predefined and dynamic resource handling methods.

upForm/myresources resources = list(icon('graphics.dmi', "star")="star.png", 'banner.png') GenerateBody() var/icon/I = new('stickman.dmi') I.SwapColor(rgb(255,255,255), rgb(rand(0,255),rand(0,255),rand(0,255))) LoadResource(I, "dynamic.png") var/page = {" <img src="star.png" /> <img src="dynamic.png" /> <img src="star.png"> <br /> <img src="banner.png" /> "} UpdatePage(page)

The above example shows how you could use a combination of predefined and dynamic resources to display a dynamic image between two star icons on top of a banner png. LoadResource() can be called anywhere. However, it is up to you to decide when it is appropriate to call it and to think ahead of the necessity of sending the resource.

Viewer Control

The upForm Library gives you a fairly good amount of control over the creation and display of the form objects. For example, if a certain form could not be sent to a client, then upForm() will return null. Knowing this, we can handle the case where a form could not be sent to a user, such as the attempt to send a Battle Challenge page to a player who is already in a Battle. In this case, you could store the return value of upForm(), and check whether a form was returned by checking if a reference was sent. If it wasn't, then the form was not able to be sent to that client.

mob/verb/challenge(mob/M in world) // src challenges M to a battle. send form to M var/form/F = upForm(M, src, /upForm/challenge) if(!F) src << "Your battle challenge request could not be met."

But wait, how do we know whether a form can or can't be sent to a client? By default, an interface that displays to the same interface control in the client's form list, and a browser interface that is being sent when another browser interface is being displayed will not be allowed to display the form to that client. A form can't be sent if it interrupts another form object. This prevents the output coming from another form from being interrupted by a new form.

If you want to define your own conditions, such as the incomplete battle challenge request page, then you will need to override the existing canDisplayForm() proc. This proc belongs to the /upForm datum, and the arg is the client whom the form is being sent to. Here is how you could disallow a battle challenge to be sent if the player is currently in a battle:

upForm/challenge canDisplayForm(client/C) // can this form be displayed to C? . = ..() // still consider the predefined behaviour ... // ... yet add your own conditions! var/mob/M = C.mob if(M.inBattle()) // disallow the form to be displayed if the player is in a battle . = 0 Link(list/href_list) var/client/C = src.getOwner() var/mob/M = C.mob switch(href_list["action"]) if("accept") M.AcceptChallenge() del(src) if("decline") M.DeclineChallenge() del(src) GenerateBody() var/mob/M = src.getHost() var/page = {" [M.name] would like to challenge you to a battle to the death. Accept or Decline? <br /> <br /> <p align="center"> <a href="byond://?src=\ref[src]&action=accept"> \[Accept\] <a href="byond://?src=\ref[src]&action=decline"> \[Decline\] </p> "} UpdatePage(page)

The important part of the code is the implementation of canDisplayForm() in the above example. Notice how the proc calls and sets the default return value to . to further extend the behaviour of the original functionality. That way, we do not overwrite the original library functionality that prevents forms from interrupting other forms. Using this method, you can easily add more and more conditions to whether a form can be displayed.

Most commonly, you will want not want two windows of the same type to be displayed to the player. The library defines a client proc, upForm_isViewingForm() to easily check whether a certain player is viewing a certain form, or type of form. The arg for the proc accepts either an instance, a path, or a tag. It will return true a valid instance is found in the list. A window that should not be displayed more than once to the user at one given time would use this proc to check if any instances of the same type of the window we are trying to send is in the client's form list (to be explained in the next section). A definition of a window of such would look like this:

upForm/mygenericwindow form_type = UPFORM_WINDOW canDisplayForm(client/C) . = ..() if(C.upForm_isViewingForm(src.type)) // disallow the form to be displayed if the client is viewing // another instance of the same form type . = 0 GenerateBody() UpdatePage("You cannot have two of these forms up at once!")

Multi-Viewer Interfaces

There may be several cases when you will want to have many players be viewing the same page. With this in mind, the library has support for multiple viewers. To display a form to many players, you will want to specify the target when you call upForm() as a list of clients and/or mobs. When you send a list, the library will know whether to display the form to one or many players. Here's a small example on how to display "Hello" to everyone in the world.

upForm/helloworld form_type = UPFORM_CLOSEWINDOW GenerateBody() UpdatePage("Hello world!") mob/verb/hello() var/list/L = list() for(var/client/C) L += C upForm(L, /upForm/helloworld)

Fundamentally, the only difference between having a form that is only displayable to one player opposed to many players is that you will need to handle the case when a page is not successfully sent to a set of clients. The library makes it as easy as possible to catch these types of errors. When a form has failed to be sent to a client, then it will not be inside the upForms client variable. upForms is a list that contains all of the instances of forms that the client is currently viewing. To catch any cases where a form is incorrectly sent, you can loop through the list of clients you generated to check whether the form instance is located in the list by using the upForm_isViewingForm() proc, and sending the reference of the stored form through the arg.

mob/verb/testform() var/list/L = list() for(var/client/C) L += C var/form/F = upForm(L, /upForm/testform) if(!F) // no players could succesfully receive the form src << "Nobody could view the form" else for(var/client/C in L) if(!C.upForm_isViewingForm(F)) src << "[C.key] could not view the form"

The code above displays the clients who would not have that form displayed to their screens. You can use this to handle the case when a form is not possible to be displayed to a client's screen. By default, a client can't have an interface that interrupts an existing interface that is on the client's screen. For example, you cannot have two form objects that display to the same browser interfaces.

There are many ways to handle multiple viewers, and incorporate it with what the body displays. The /upForm datum stores the viewers in the viewers list variable. By looping through the list, you can create a form that will display all of the viewers currently viewing the form.

upForm/viewlist GenerateBody() var/page = "<b>People Viewing this Page</b> <br />" for(var/client/C in src.viewers) page += "[C.key] <br />" UpdatePage(page)

Global Interfaces

Global interfaces are a fancy way of saying a type of interface that would be shared by many viewers, yet will still exist when there are no viewers. These types of forms are created with the purpose that viewers will be freely added or removed from the form at a later time.

To create a global interface, you must: define the UPFORM_SET_SHARED flag in the settings var of the form definition, and call upForm() with a null target. Here is how you would define a global interface that displays "Hello world" by adding the player to the viewer list.

// store the global interface in a global variable for easy access var/upForm/globalhelloworld/helloworld world/New() ..() // create the form by calling upForm with a null target helloworld = upForm(null, /upForm/globalhelloworld) upForm/globalhelloworld form_type = UPFORM_WINDOW settings = UPFORM_SET_GLOBAL Link(list/href_list, client/C) if(href_list["action"] == "close") // remove the client as a viewer to "close" the window src.RemoveViewer(C) GenerateBody() var/page = {" Hello world! <br /> <br /> <a href="byond://?src=\ref[src]&action=close">\[Close\]</a> "} UpdatePage(page) mob/Login() ..() // display window by adding the player to the form's viewer list helloworld.AddViewer(src)

Note that the way that we are handling the form is by calling AddViewer() and RemoveViewer() respectively to display and close the form for the viewer. Although this is not a practical usage of a shared interface, you could use this model for something much more usable, such as a player list that updates when a player logs in or out. If you want the page to regenerate when a viewer is added or removed, then you will want to turn on the UPFORM_SET_RELOAD_ON_VIEWER_UPDATE flag. This example does not need it because it displays a constant message, but a dynamic player list interface would definitely want to be updated.

HTML Form Handling

Handling HTML forms is the main purpose of this library, hence its name. HTML forms are commonly used in websites that allow you to enter a set of information, and process it all at one time. The upForm datum gives you a plethora of functions and resources to make form handling as easy and intuitive as possible. There are three procs that you must implement when handling forms: FormInitTempVars(), FormSetTempVars(), and ProcessVariable().

To tell the datum that you want it to handle forms for you, you will have to turn on the UPFORM_SET_HANDLE_FORMS flag in the settings var. When the flag is on, the form object will know to separately handle any form data through its form functions. Next, you will have to implement the FormInitTempVars() proc. This proc uses the initFormVar() proc to set the initial values of the entries of a form control that you will eventually define in the body of your page. initFormVar() takes three args in this order: the form name, the variable name, and the value of the variable. For every single form, and form input, you will have to call initFormVar() to initialize and declare the value of the input of a certain form. Internally, this creates indexes for the values of a form to be recalled later. The form stores all of the values, whether they are valid or invalid, for future reference.

You also will have to implement the mirrored function, FormSetTempVars(). Opposed to the above proc that was explained, this proc gets called when a form is successfully submitted, and you want to set the variables that were submitted, or perform some other action. The argument that is sent through the proc is the name of the form that was submitted, so you can set the correct values of the variables. To get the values of the internally-stored variables, you must use the proc getFormVar(), which takes the form name and input name as the args, respectively. The proc returns the value of the input control, given the form name and input name that you have sent through the args. In FormSetTempVars(), you will set the variables that you intended to change using the HTML form. getFormVar() is also used when you write the HTML code for your form. You use this proc to get the value of the control in the page.

The other main proc that you will need to override is ProcessVariable(). This proc gets called when a form is submitted, and variables need to be processed. It is called for every single input that you have defined in your form. The proc's args hold three variables: the form name, the input name, and the value of the variable as a text string. You are expected to set the value of the input that was sent by using the setFormVar() proc, which also requires the same 3 args. If the variable is not valid, you must return a text string with the error, which you may choose to display on the screen.

When handling forms, GeneratePage() will have a list sent to it if there are errors in the page. This list is an associative list containing the values for the name of the input, and the error associated with the input which was returned by ProcessVariable(). This gives you the option of displaying an error on a screen for a particular variable such as the case of having too many characters in your name, or if you attempt to write a negative number for your age.

Writing an HTML form is not simple, so here is a relatively simple example for a page that has a form that will accept a name and a password.

upForm/register settings = UPFORM_SET_HANDLE_FORMS Link(list/href_list) if(href_list["action"] == "close") del(src) FormInitTempVars() var/mob/M = src.getHost() // initialize each form var to the default values initFormVar("reg", "uname", M.username) initFormVar("reg", "pass", M.password) FormSetTempVars(fname) var/mob/M = src.getHost() if(fname == "reg") // set each form var to the successfully submitted value M.username = getFormVar("reg", "uname") M.password = getFormVar("reg", "pass") ProcessVariable(fname, name, value) if(fname == "reg") switch(name) if("uname") setFormVar(fname, name, value) // verify and return an error message if something is wrong if(length(value) > 25 || length(value) < 3) return "Username must be between 3 and 25 characters long" if("pass") setFormVar(fname, name, value) if(length(value) <= 5) return "Password must be longer than 5 characters" return null FormSubmitSuccess(fname, client/C) var/mob/M = src.getHost() C << "Success! Your username is [M.username] and your password is [M.password]" del(src) // register success! delete form GenerateBody(list/errors=list()) var/page = {" <form name="reg" action="byond://" method="get"> <input type="hidden" name="src" value="\ref[src]" /> <input type="hidden" name="form" value="reg" /> Username: <input type="text" name="uname" value="[getFormVar("reg","uname")]" /> [errors["uname"]]<br /> Password: <input type="password" name="pass" value="[getFormVar("reg","pass")]" /> [errors["pass"]]<br /> <input type="submit" value="Register" /> <input type="reset" value="Reset" /> </form> "} UpdatePage(page) mob var username = "" password = "" verb/register() upForm(src, src, /upForm/register)

There are some things you may have noticed in the above example. When you are generating a page with an HTML form, you must have two hidden inputs sending information about the form reference, and the name of the form. This will allow the HTML form to be handled by the form functions rather than Link(). A generic form would look like this:

<form name="reg" action="byond://" method="get"> <input type="hidden" name="src" value="\ref[src]" /> <input type="hidden" name="form" value="reg" /> ... <input type="submit" value="Submit" /> </form>

Another thing you may have noticed is that we can override the FormSubmitSuccess() proc to do something on the event of a successful form submission. The name of the form is sent through the arg. By default, it reloads the page. In the above example, we close the form when the submission is successful. There is also the FormSubmitError() proc, which is called when a form with a validation error is sent. By default, it reloads the page with the errors. The form name and list of error is sent through the args.

HTML forms are a useful and powerful tool to quickly submit and review information all at one go. The library can support as many forms as you like, but you will have to specify the form values for each of them.

Timers

Timers are a simple yet useful feature for forms, and have been supported in this library due to the convenience of having an internal timer rather than having to manually implement timing or handling the time externally. Time is mainly handled with the time_left variable. This is the time (measured in ticks) left that until the page will be deleted. If you define an upForm datum with the time being a positive non-zero integer, then the form will be deleted in that duration.

upForm/timedpage time_left = 50 GenerateBody() UpdatePage("This page will close in 5 seconds")

This is the most common usage of a timed interface, which closes the page after a certain time. If you want finer control over the time, then you can modify the time_interval variable. By default, it is 10 ticks, which means that the timer will update every 10 ticks. This variable usually does not need to be changed, but if you want the form to be deleted in an interval of less than 10 ticks, then you will want to modify this number so time_left % time_interval = 0.

However, the main purpose of the time_interval variable is to control the refresh rate of a timed form that you want updated within the interval you defined. If the time_update variable is 1, then the page will be regenerated once per time_interval ticks. This is useful if you want the viewers to know the value of the counter. Below is an example showing a countdown from 10 to 0.

upForm/timebomb time_left = 100 time_update = 1 Link(list/href_list) if(href_list["action"] == "close") del(src) GenerateBody() var/page if(hasTimeStarted()) page = "You have [time_left / 10] seconds to live." else page = "Boom! You died. \ <a href=\"byond://?src=\ref[src]&action=close\">\[Close\]</a>" UpdatePage(page) TimeUp() // override to stop the default behaviour of form deletion // when the time runs out mob/Login() ..() upForm(src, /upForm/timebomb)

The above example uses two new procs: TimeUp() and hasTimeStarted(). As the names of the procs imply, TimeUp() is called when the timer reaches zero. By default, it will delete the form when the time runs out. The time bomb example overrides the functionality to prevent the timer from deleting the form so a message could instead be displayed. hasTimeStarted() returns true if the timer is running. This proc is useful if you want to display something different when the timer is running.

Two other procs exist for timers. TimeStarted(), similar to TimeUp() is called when the timer is started. If time_left is defined in the form definition, then it will be called when the form is created. If you don't want the timer to start when the form is created, then you can call StartTimer() manually, submitting the time in ticks through the arg. This will manually start the timer if it hasn't started already. StartTimer() will not sleep, so you do not need to call spawn().

Miscellaneous

The features explained in this section do not require as much depth as the above features, and are mainly used for convenience.

The interface_bgstyle variable is used for the specific case of an form with the form_type of UPFORM_INTERFACE or UPFORM_BROWSER. By default, when a form is closed, it will remove all of the text from the browser control. BYOND will display a blank white screen. In the case that you don't want a white background to be displayed, interface_bgstyle is used to display an empty page with a custom CSS definition. So if your game has a dark background, then you may want to define the variable as a dark grey background for the blank page.

upForm/myinterface form_type = UPFORM_INTERFACE form_name = "myinterface" page_css = "body { background-color: #222222; color: #ffffff }" interface_bgstyle = "body { background-color: #222222 }" GenerateBody() UpdatePage("When this page is deleted, it will display a dark grey \ background rather than an ugly white background.")

Reference

User-Defined Variables

/upForm/global_css
The CSS code that will be displayed on every single upForm instance. Defined when all forms should look similar. Default value is null.

Constants

form_type var settings:
UPFORM_BROWSER
The default value. The page will be displayed to the user's default form. If no interface has been defined for your program, then it will display to the browser tab.
UPFORM_INTERFACE
The page will be displayed to a specified interface, which should be defined in the form's form_name variable.
UPFORM_WINDOW
The page will be displayed in a unique window. The window's settings are defined through various /upForm variables prefixed with window.
UPFORM_CLOSEWINDOW
The page will be displayed in a unique window with the purpose of being manually Xed out by the user. This setting is fundamentally the same as UPFORM_WINDOW with the exception that the form instance is deleted right after the page is displayed to the viewers.
setting var flags:
UPFORM_SET_HANDLE_FORMS
The upForm object should handle the supported form functions when this flag is on. Form procs must be implemented.
UPFORM_SET_GLOBAL
The upForm object is shared and will not be deleted when the viewer list is empty. Global interfaces should be created via upForm() with a null target.
UPFORM_SET_RELOAD_ON_VIEWER_UPDATE
The upForm object's body is regenerated when a client is added or removed from the viewer list.
UPFORM_SET_SELF_REFERENCE
When this flag is on, the form will self reference itself to prevent the garbage collector from collecting the unreferenced form. Only turn on this flag when it is properly tagged, yet does not have an external reference. In almost all cases, this flag is not necessary.
window_params var flags:
UPFORM_CANNOT_CLOSE
The window will not be able to be closed from being Xed out when this flag is on. This flag is automatically turned on for UPFORM_WINDOW forms.
UPFORM_CANNOT_RESIZE
The window will not be resizable when this flag is on.
UPFORM_CANNOT_MINIMIZE
The window will not be minimizable when this flag is on.
UPFORM_NO_TITLEBAR
The window will have no titlebar, and will not be closable nor minimizable.

Global Procs

upForm(target, form_path[, tag])
upForm(target, host, form_path[, tag])
This function is the hub of all form creation. The target may be a client, mob, or a list. If a list is sent, then the form will have no owner. If null is sent, then the form will be created as a global interface. If the second arg is an instance of a datum, then the proc will set the host for the form to the datum. The arg after will be the path of the form that will be created. An optional last arg is a text string containing the form's tag, which can be later used to locate a form via locate() and other upForm functions that allow it.
upForm_UpdateForms(form)
Given a reference, string, or path, this proc regenerates the page for the given target. If a reference is sent, it will regenerate that reference. If a string is sent, it will attempt to locate the form and refresh the located instance. Otherwise if a path is sent, it will refresh all instances of the given path type. If the arg is invalid, it will throw an assertion.
upForm_isValidHost(datum/host)
Returns 1 if host is a valid host for a /upForm object. Otherwise, 0. A valid host is a datum. Used internally.
upForm_isValidFormPath(path)
Returns 1 if path is a valid form path, being a type of /upForm path. Otherwise, 0. Used internally.
upForm_getClientTarget(target)
Returns a client derived from the target (ie. the client of a mob). If a client could not be found, it returns null. Used internally.
upForm_formatViewerList(list/viewers)
Modifies an existing list of clients and/or mobs to list only clients derived from the mobs. Returns 1 if a successful format was made, otherwise 0. Used internally.

/upForm Vars

/upForm/form_name = ""
Name of the browser control on the client's interface where the page will be displayed to. This variable is only used when form_type is UPFORM_INTERFACE.
/upForm/form_type = UPFORM_BROWSER
The medium in which the page is displayed. Possible values are UPFORM_BROWSER, UPFORM_INTERFACE, UPFORM_WINDOW, and UPFORM_CLOSEWINDOW.
/upForm/settings = 0
A flag variable that determines general boolean settings for the form object. May contain UPFORM_SET_HANDLE_FORMS, UPFORM_SET_GLOBAL, UPFORM_SET_RELOAD_ON_VIEWER_UPDATE, and UPFORM_SET_SELF_REFERENCE.
/upForm/window_title = [world.name]
Determines the title of the window that the form displays its body to. Is not applicable if a form has no titlebar, or if it is not a window. Default to the world's name.
/upForm/window_size = "300x300"
Determines the initial size of a window. Is only applicable for windows that are resizable.
/upForm/window_params = 0
A flag variable that determines the setting for a window form. May contain UPFORM_CANNOT_CLOSE, UPFORM_CANNOT_RESIZE, UPFORM_CANNOT_MINIMIZE, and UPFORM_NO_TITLEBAR.
/upForm/page_css = ""
Determines the CSS code for the page.
/upForm/page_js = ""
Determines the JavaScript code for the page.
/upForm/time_left = -1
The amount of time left until the timer ends. When var reaches 0, calls TimeUp(). Read only, do not modify. To start timer after form initialization, call StartTimer().
/upForm/time_interval = 10
The interval between potential refreshes. Do not modify in runtime.
/upForm/time_update = 0
If 1, the page will be regenerated every time_interval ticks.
/upForm/list/resources = null
If instantiated as an associative list, it will send all the resources to the viewers.
/upForm/client/owner = null
The client who is considered the owner of the form object. The owner is set if a single target is submitted when upForm() is called to create the form object. Use getOwner() to read. May be null. Should not be touched.
/upForm/datum/host = null
An optional datum whose purpose is to access external information. Use getHost() to read. May be null. Should not be accessed directly.
/upForm/list/viewers
A list of clients who are currently viewing the form object. Read only.
/upForm/body
The HTML content of the page being sent. Use UpdatePage() to modify the body. Should not be modified.
/upForm/window_params_text = ""
Used internally to define the second arg for browse() when sending a window. Do not touch.
/upForm/self
Reference of self if self referencing. Do not touch.
/upForm/predef_js
Code for the predefined JavaScript functions placed in every form. Generated at runtime. Do not touch.
/upForm/global/refcount
Internal refcount. Do not touch.
/upForm/refnum
internal refnum. Do not touch.
/upForm/list/form_vars
Internal two-dimensional associative array used to store keys and values of form names, inputs, and values. Hands off.

/upForm Procs

/upForm/getOwner()
Returns the reference of the owner of the form, if it exists. If there is no owner, throws an upForm assertion. The programmer should know whether a form will be created without an owner, so using said proc instead of directly accessing the owner variable will verify that knowledge.
/upForm/getHost()
Returns the reference of the host. May return null.
/upForm/isViewer(client/C)
Returns 1 if C is a viewer of the form, otherwise 0.
/upForm/canBeViewer(client/C)
Returns 1 if C would be able to be added to the viewers list, otherwise 0.
/upForm/canDisplayForm(client/C)
Proc overridden to determine if the form should be able to display the page to C. Ensure you consider the default behaviour by setting . to ..().
/upForm/PreSettings()
Called right before the page is displayed for the first time. Overridable for the programmer to adjust any last-minute variables such as window_name.
/upForm/hasTimeStarted()
Returns 1 if the timer is running, otherwise 0.
/upForm/InitTimer()
Initializes the timer, and starts it if an initial time value has been defined. Do not touch.
/upForm/StartTimer(time)
Call when the time should be started. May be called at any time if the timer hasn't already been started.
/upForm/TimeStarted(time)
Called when the time has started. time is the starting amount of time for the form object.
/upForm/TimeUp()
Called when the time has reached zero. Deletes the form object by default.
/upForm/SendResources(target, rsc, rsc_name)
Sends the resource rsc to target with the name rsc_name. Used internally. Call LoadResource() if you want to dynamically load resources to the viewers.
/upForm/LoadResources(rsc, rsc_name)
Sends the resource rsc to all of the viewers of the page with the name rsc_name.
/upForm/Link(list/href_list, client/C)
Called by /upForm/Topic() on the event a link was sent to the form object. Data is guaranteed to be valid. Overridable for the programmer to write functionality when a link is sent.
/upForm/isValidForm(fname)
Returns 1 if fname is a valid form for the form object. Used internally.
/upForm/isValidFormVar(fname, fvar)
Returns 1 if fname and fvar are valid forms and input control names. Used internally.
/upForm/getFormVar(fname, fvar)
Returns the value of the input submitted or initialized by the form. Use this when you need to access any submitted form information.
/upForm/setFormVar(fname, fvar, fval)
Sets the value for an input control given the form name and form variable name. Use when processing a variable via ProcessVariable().
/upForm/initFormVar(fname, fvar, fval)
Initializes a form variable to a certain value. Use in FormInitTempVars() when initializing forms and form variables to their starting values.
/upForm/FormSubmitSuccess(fname, client/C)
Called on a successful submission of form fname by C. Reloads the page by default.
/upForm/FormSubmitError(fname, list/errors, client/C)
Called on an unsuccessful submission of form fname by C given errors errors. Reloads the page with the generated errors by default.
/upForm/FormInitTempVars()
Defined for the programmer to implement and initialize the initial values for the temorarily stored variables for the forms. Call initFormVar() for each form variable.
/upForm/FormSetTempVars(fname)
Defined for the programmer to implement the successful submission. Sets the values of the validated form variables. Use getFormVar() to get the verified form variable.
/upForm/ProcessVariable(fname, name, value, client/C)
Defined for the programmer to set and return an error if applicable. Use setFormVar() to set the formatted value value. value is either a text string, or list of name=value parameters to be processed.
/upForm/AddViewer(client/C)
Adds a new viewer to a multiple viewer form. Arg may accept a mob or a client.
/upForm/RemoveViewer(client/C)
Removes an existing mob or client from the viewer list.
/upForm/GenerateBody(list/errors)
Implemented for the programmer to write the code that generates the body of the HTML page to be displayed to the viewers. Must call UpdatePage() with the body (or optionally the javascript code) of the HTML. If a form has been submitted with errors, a list will be sent through errors containing an associative name=value list for each input control in the form with an error returned by ProcessVariable()
/upForm/RefreshPage(list/errors)
Reloads and displays the page to all viewers.
/upForm/DisplayPage()
Displays the already generated body to the viewers.
/upForm/UpdatePage(bodyText, jsText)
Generates the HTML for the page of the form object, given the contents of the body and the optional JavaScript code. Predefined CSS and JS elements are put into the HTML code. Should be called by GenerateBody(). Do not modify.
/upForm/DeleteForm()
Deletes the form, and closes the page for every viewer.
/upForm/InitPredefinedScript()
Internally initializes the predefined JavaScript code for the form. Do not touch.
/upForm/InitViewers(list/viewer_list)
Initializes the internally inputed viewer list. Do not touch.
/upForm/SetViewer(client/C)
Internally used to instantiate a viewer. Do not touch.
/upForm/InitResources(target)
Sends all predefined resources to target. Used internally, do not touch.
/upForm/HandleFormLinks(list/params, client/C)
Internally called when a form submission has been sent. Do not touch.
/upForm/ProcessForm(fname, list/params, client/C)
Internally called when a form needs to be processed. Do not touch.
/upForm/DisplayBrowserText(text, target)
Displays text to the browser output control to target. Internally used to handle output to the client. Do not touch.
/upForm/RemovePage(client/C)
Removes the page from C. Used internally, as a page should only be removed if the player is removed from the viewers list, or if the form is deleted.
/upForm/ClosePages()
Removes the page for all of the clients in the viewer list. Used internally.

Predefined JavaScript Functions

The following functions are JavaScript functions, and are not called by DM code.
function upF_sendData(data)
Sends data in a BYOND URL to the form: "byond://?src=[refcode]&[data]"
function upF_action(action)
Sends action as the value for the action param: "byond://?src=[refcode]&action=[action]"
function upF_sAction(action, value)
Sends action and value as values for the params: "byond://?src=[refcode]&action=[action]&value=[value]"
function upF_send(name, value)
Sends a name=value pair through the link to the form: "byond://?src=[refcode]&[name]=[value]"
function upF_set(input)
Sends the name and value in two pair for an input control: "byond://?src=[refcode]&name=[input.name]&value=[input.value]"
function upF_check(input)
Given a checkmark input control, returns the state of the checkmark as "true" or "false": "byond://?src=[refcode]&name=[input.name]&value=[input.checked]"