https://wiki.secondlife.com/w/api.php?action=feedcontributions&user=Natty+Linden&feedformat=atomSecond Life Wiki - User contributions [en]2024-03-29T09:54:33ZUser contributionsMediaWiki 1.36.1https://wiki.secondlife.com/w/index.php?title=Linden_Lab_Official:Registration_API&diff=1214709Linden Lab Official:Registration API2023-10-03T22:10:28Z<p>Natty Linden: </p>
<hr />
<div>{{multi-lang}}<br />
{{Supported API}}<br />
{{:API Portal/navigation|reg}}<br />
__TOC__<br />
<br clear=all/><br />
== Overview == <br />
The Registration API (Reg API) enables you to register new Second Life users on your website when you require more than just the ability to send people to a location after they complete registration. If you want to have someone register an account and land at a specific location at login, then use [[SLURL#If_the_person_doesn.27t_have_Second_Life|DirectSLURL]].<br />
<br />
Use the Reg API to create Second Life accounts during the user registration process on your website. You can customize the Second Life registration process to capture additional information or fit your registration process. When a user registers, they choose a unique Second Life name and password. Registration provides Linden Lab with the same information provided through standard registrations on the [http://secondlife.com Second Life] website. At the end of registration, your customer must download the Second Life client, install, and run it. At that time, they will be registered with the account they created on your website and will start Second Life in a location you specify.<br />
<br />
The Reg API is a simple [http://en.wikipedia.org/wiki/REST#RESTful_Web_services REST-style web service]. It sends and returns data in [[LLSD]] XML format. Linden Lab provides libraries to parse and encode LLSD in Perl, PHP, Python, and Ruby; see [[Reg API Examples]].<br />
<br />
In return for some programming effort, the Reg API enables you to:<br />
* Limit accounts to your estate<br />
* Create accounts with a custom name (with special permission from Linden Lab - see [[Custom_Name_Program]])<br />
* Register multiple employees or students from your company or education institution from a single location or as part of your organization's website.<br />
<br />
===Important Limitations and Conditions===<br />
To qualify for the RegAPI Program, you must meet the following criteria:<br />
* You must provide Linden Lab's Terms of Service as part of registration, in addition to any terms you require.<br />
* All participants in Linden Lab's API program must abide by the [[Linden Lab Official:API Terms of Use|API Terms Of Use]].<br />
* You must make clear that users are establishing a relationship with Linden Lab, separate from their relationship with you. <br />
* If your page is public, you must manage registration to comply with Linden Lab's policies about alternate accounts and registration verification. In general, Linden Lab will not approve use of the Reg API for public registration, since [[SLURL#If_the_person_doesn.27t_have_Second_Life|DirectSLURL]] is a preferable option.<br />
<br />
For more information on the RegAPI see the Working Inworld blog post [https://blogs.secondlife.com/community/workinginworld/blog/2009/09/17/pocket-faq-creating-your-private-branded-workspace-with-reg-api Pocket FAQ: Creating Your Private, Branded Workspace with Reg API] <br />
<br />
=== What the Reg API Can Do ===<br />
With the Reg API, you can:<br />
* Register Second Life Residents from your website and track them.<br />
* Provide customized and branded registration.<br />
* Add newly registered Residents to a Second Life group.<br />
* Send them to a location of your choice when they first log in to Second Life.<br />
* Limit accounts to your estate.<br />
* If Linden Lab has granted permission, limit name selection, or create accounts with a custom name. You must request this permission separately; see [[Custom Name Program]] for more information.<br />
* Provide registrations for your organization with internal access only, for example using an internal web site.<br />
<br />
=== What the Reg API Cannot Do ===<br />
The Reg API '''does not''' provide the ability to:<br />
* Provide inventory, create attachments, or control the Resident's outfit<br />
* Alter the appearance of the SL Viewer.<br />
* Automatically balance load on a sim. Instead, use the Reg API to selectively set the starting location based on data sent from an LSL script in each location.<br />
<br />
The Reg API does not prohibit you from setting a starting location to land that you don't own. You must make sure that the owner of the landing location agrees to have the new Residents arrive there if you don't own the land.<br />
<br />
=== How to Apply for Reg API ===<br />
Before applying, please read the section "Important limitations and conditions apply" above. In general, we only approve requests that involve: limit to estate, custom name, or registration of an organization's members behind their login or firewall. Public registration should use [[SLURL#If_the_person_doesn.27t_have_Second_Life|DirectSLURL]].<br />
<br />
To request participation in the beta Registration API program, submit the [[Linden Lab Official:Registration API Request Form|Registration API Request Form]]. Linden Lab will contact you by email after reviewing your application. If you have had a RegAPI account that was recently closed, reapply via the [https://spreadsheets.google.com/viewform?formkey=dDVoWm5vOUVqeDdaelEzc0twZFJpX3c6MA RegAPI Renewal Form].<br />
<br />
=== Important Notices ===<br />
{{KBcaution|The RegAPI is a beta program that requires some technical proficiency to use successfully. Linden Lab does not currently provide support or technical advice on its use.}}<br />
* Accounts with Reg API capabilities that have not signed up at least one account within 90 days of receipt of the capabilities will have their Reg API access removed.<br />
* All participants in the Reg API program must abide by Linden Lab's [[Linden Lab Official:API Terms of Use|API Terms Of Use]].<br />
<br />
=== Discussion and Support ===<br />
* Use the [[Talk:Registration API|talk page]] to post questions about the RegAPI.<br />
* Although Linden Lab does not provide technical support for the Reg API, you can purchase support from third-party providers; see [[Registration API Third Party Support]].<br />
<br />
== How to set up your Reg API ==<br />
===Setting up your Reg API===<br />
# Fill out and submit the [[Linden Lab Official:Sign up to use the Registration API|Registration API Request Form]]. Linden Lab will respond by email.<br />
# Verify your capabilities using the [[Linden Lab Official:Registration API Request Form#Check_your_capabilities| capabilities form]].<br />
# Make sure you have permission from the owner of the estate where your users will appear inworld. You ''must'' have permission of the landowner (except for Linden Lab Orientation Islands). If you have multiple registrations and need to send people to different locations, contact us to obtain additional assignments.<br />
# Create your registration web page<br />
# If using PHP, make sure the necessary libraries are installed and working (see http://uk.php.net/phpinfo)<br />
# Test your registration application:<br />
#* Register several new users.<br />
#* If you aren't getting results you expect, check the [[Registration API Error Codes|error code]].<br />
#* Create registrations to test the most common [[Registration API Error Codes|error codes]] and verify that you receive and properly handle them appropriately.<br />
<br />
===Testing your Reg API===<br />
If you are using the Reg API to sign up registrants on the main grid, users under 13 will not be allowed to register, since they are not permitted on the Main Grid. Therefore, when testing, use a birth date that makes the registrant older than 18 years for best results.<br />
<br />
Use an email address that does not currently exist in Second Life. If a user has already registered with an email address, the Reg API will return error 95, email exists. If you trap errors, you will see this error code, but if not, registration will simply fail.<br />
<br />
===Using capability URLs===<br />
<br />
Reg API [[Capabilities|capabilities]] represent permissions to perform certain actions. The Reg API grants capabilities with ''capability URLs'' of the form:<br />
<br />
{{Command|<nowiki>https://cap.secondlife.com/cap/0/</nowiki>''UUID''}}<br />
<br />
Where ''UUID'' is the UUID (universally unique identifier) granted for the specific capability.<br />
<br />
For example:<br />
<br />
{{Command|<nowiki>https://cap.secondlife.com/cap/0/2897456b-6959-473e-9b94-36d8cec1b9c3</nowiki>}}<br />
<br />
The Reg API provides capability URLs for the following operations:<br />
* '''add_to_group''' - add a new user to the specified Second Life group.<br />
* '''create_user''' - create a new user.<br />
* '''check_name''' - indicates whether a user can be registered with a given Second Life first name and last name.<br />
* '''get_last_names''' - return last names and corresponding IDs with which you are able to register new users. <br />
* '''get_error_codes''' - return the set of error codes.<br />
<br />
For more information on these operations, see the [[Registration API Reference]].<br />
<br />
For example, consider the [[Linden_Lab_Official:Sign_up_to_use_the_Registration_API#Check_your_capabilities|check capabilities form]]. It requests a description of the capabilities currently granted to you. <br />
<br />
The form POSTs your "first_name", "last_name" and "password" to https://cap.secondlife.com/get_reg_capabilities to get your capabilities. The actual HTML looks like this:<br />
<br />
<syntaxhighlight lang="html5"><br />
<form action="https://cap.secondlife.com/get_reg_capabilities" method="POST"><br />
<p><label>First Name <input name="first_name"></label></p><br />
<p><label>Last Name <input name="last_name"></label></p><br />
<p><label>Password <input name="password" type="password"></label></p><br />
<p><input type="submit" value="Get Capabilities"></p><br />
</form><br />
</syntaxhighlight><br />
<br />
When you submit a form such as this, the Reg API returns an XML document that looks like this:<br />
<br />
<syntaxhighlight lang="xml"><br />
<llsd><br />
<map><br />
<key>get_error_codes</key><br />
<string>https://cap.secondlife.com/cap/0/e2fd05e0-d4f0-4f04-8805-fdc1bd4e9ea2</string><br />
</map><br />
</llsd><br />
</syntaxhighlight><br />
<br />
This example shows that you have the "get_error_codes" capability. If instead you had been granted more capabilities, the document would look something like this:<br />
<br />
<syntaxhighlight lang="xml"><br />
<llsd><br />
<map><br />
<key>create_user</key><br />
<string>https://cap.secondlife.com/cap/0/35ff3b8c-a30d-4d18-b29a-e3f7f6c79cb6</string><br />
<br />
<key>check_name</key><br />
<string>https://cap.secondlife.com/cap/0/6e528ba1-a8b0-4f6b-8b56-362ee6f5cef8</string><br />
<br />
<key>get_last_names</key><br />
<string>https://cap.secondlife.com/cap/0/be4e4d2e-c00a-46cd-bb8d-d17cb8e92c9b</string><br />
<br />
<key>get_error_codes</key><br />
<string>https://cap.secondlife.com/cap/0/e75f81a5-b7da-4480-8f95-b1cf9d2d680f</string><br />
</map><br />
</llsd><br />
</syntaxhighlight><br />
<br />
Of course, these example capabilities URLs don't actually work.<br />
<br />
When using capability URLs, follow these guidelines:<br />
* Add code to capture and display returned error codes; this will assist you in troubleshooting problems.<br />
* Do not hard-code your capabilities URLs. Either code your capabilities URLs as constants, or better yet, obtain them at run-time. Capability URLs will expire eventually, so fresh ones are always better.<br />
* Keep your capability URLs secret! The capabilities granted to you are only meant for you. A capability URL is sensitive much like a password. Moreover, Linden Lab tracks the use of each capability.<br />
<br />
''' Using LLSD with the Reg API '''<br />
Use [[LLSD]] XML data format to send and get information to and from capability URLs. Here is an example of a typical LLSD XML message:<br />
<br />
<syntaxhighlight lang="xml"><br />
<llsd><br />
<map><br />
<key>dob</key><string>1987-07-06</string><br />
<key>start_region_name</key><string>da boom</string><br />
<key>username</key><string>mistaht</string><br />
<key>last_name_id</key><integer>1872</integer><br />
<key>password</key><string>123456</string><br />
<key>email</key><string>ben@ben.com</string><br />
</map><br />
</llsd><br />
</syntaxhighlight><br />
<br />
This document represents a map (hash) containing keys for:<br />
* dob<br />
* start_region_name<br />
* username<br />
* last_name_id<br />
* password<br />
* email<br />
<br />
This document is a typical LLSD XML message that you might POST to the "create_user" capability URL to create a new user.<br />
<br />
The LLSD specification specifies many different data types: Map, Array, String, Integer, Float, Date, and so on. Each data type corresponds to a native object type in most languages.<br />
<br />
The LLSD XML libraries handle most of the work of encoding native objects into LLSD XML and vice-versa. For example, you can use the Ruby library as follows:<br />
<br />
<syntaxhighlight lang="ruby"><br />
require 'LLSD'<br />
<br />
native_obj = 123<br />
llsd_xml = LLSD.to_xml(native_obj) # returns <llsd><integer>123</integer></llsd><br />
returned_native_obj = LLSD.parse(llsd_xml) # returns native ruby integer, 123</ruby><br />
</syntaxhighlight><br />
<br />
If you have problems encoding and decoding LLSD, look at the actual LLSD XML being sent and received to start debugging.<br />
<br />
== Troubleshooting ==<br />
''' Limiting Accounts to an Estate '''<br />
You can use the Reg API to limit registrants to the estate of the registrar (the account that controls the registration). You must use the estate ID for the limited_to_estate parameter when creating the account. See [[Registration_API_Reference#create_user|create_user]] for details. <br />
<br />
To find your estate Code (as of March 16th 09):<br />
# {{KeyCombo|ctrl=*|alt=*|shift=*|D}} to enable the extra pull-down [[advanced menu]].<br />
# Then use Advanced > View Admin Options (near the bottom).<br />
# From new pull-down menu Admin, select God Tools.<br />
# Look at the Region tab. The Estate ID is listed there. <br />
<br />
''' Some Accounts Can't Access an Estate '''<br />
If you're setting registration to 'restrict to estate' and people can't enter your estate, realize that if you've set access controls to require group membership to enter, you must first add the the new users to the requisite group for them to be able to enter your estate.<br />
<br />
Alternatively, you can subdivide your estate and create a parcel that has no restrictions, set your landing area there, then add new users to the group(s) to enable them to access the rest of the Estate.<br />
<br />
'''Other Troubleshooting Tips'''<br />
* Make sure you're using the account that was enabled for the Registration API to create accounts.<br />
* You must use the Registration API to create additional accounts, not the Viewer or the secondlife.com registration page.<br />
* Verify you'rve including your capabilities URL and have properly set up your account and the location in the script<br />
* Check for [[Registration API Error Codes | error codes]]<br />
<br />
'''If using PHP'''<br />
* The LLSD library has not been tested with PHP5, and is known NOT to work with many common configurations of PHP5. We expect to have and improved version of it available soon, which will be compatible with PHP4 and PHP5. In the meantime, if you have to use PHP5, some folks report using this adapter successfully: http://alexandre.alapetite.net/doc-alex/domxml-php4-php5/<br />
* Or if you like living dangerously, try [[RegAPI_for_PHP5]].<br />
* Make sure the Curl library is installed and running.<br />
<br />
[[Category:RegAPI]]</div>Natty Lindenhttps://wiki.secondlife.com/w/index.php?title=User:Natty_Linden&diff=1212734User:Natty Linden2022-11-02T18:30:49Z<p>Natty Linden: Created blank page</p>
<hr />
<div></div>Natty Lindenhttps://wiki.secondlife.com/w/index.php?title=Linden_Lab_Official:Live_Data_Feeds&diff=1212733Linden Lab Official:Live Data Feeds2022-11-02T18:30:25Z<p>Natty Linden: reflect api.secondlife.com updates</p>
<hr />
<div>{{Supported API}}<br />
{{:API Portal/navigation}}<br />
__TOC__<br />
<br clear="all"/><br />
== Statistics == <br />
<br />
=== XML feed ===<br />
: https://api.secondlife.com/datafeeds/secondlife.xml<br />
<br />
Example Response:<br />
<nowiki><stats> <br />
<status>ONLINE</status> <br />
<signups>18,072,648</signups> <br />
<inworld>38,836</inworld> <br />
</stats></nowiki><br />
<br />
{| style="border:1px solid #A3B1BF; text-align:left; background:#f5faff"<br />
|-<br />
|style="background: #cee0f2; padding: 0.3em; text-align:center;"|'''Parameter'''<br />
|style="background: #cee0f2; padding: 0.3em; text-align:center;"|'''Data type'''<br />
|style="background: #cee0f2; padding: 0.3em; text-align:center;"|'''Description'''<br />
|-<br />
|status<br />
|string<br />
|<code>ONLINE</code> when the grid is online. If the grid is offline, <code>OFFLINE</code>.<br />
|-<br />
|signups<br />
|comma delimited integer<br />
|Number of Resident accounts that are open and in good standing. Updated daily.<br />
|-<br />
|inworld<br />
|comma delimited integer<br />
|Number of Resident accounts currently logged in. Updated every 3 minutes.<br />
|}<br />
<br />
=== LLSD feed ===<br />
: https://api.secondlife.com/datafeeds/homepage.xml<br />
<br />
Each key pair has additional dates associated indicating when the data was updated in UNIX time and SL time (Pacific Time).<br />
<br />
Example:<br />
<nowiki><llsd> <br />
<map> <br />
<key>stats</key> <br />
<map> <br />
<key>signups</key> <br />
<integer>18072841</integer> <br />
<key>signups_updated_unix</key> <br />
<integer>1265100902</integer> <br />
<key>signups_updated_slt</key> <br />
<string>2010-02-02 00:55:02</string> <br />
<key>inworld</key> <br />
<integer>38574</integer> <br />
<key>inworld_updated_unix</key> <br />
<integer>1265100605</integer> <br />
<key>inworld_updated_slt</key> <br />
<string>2010-02-02 00:50:05</string> <br />
</map> <br />
</map> <br />
</llsd></nowiki><br />
<br />
{| style="border:1px solid #A3B1BF; text-align:left; background:#f5faff"<br />
|-<br />
|style="background: #cee0f2; padding: 0.3em; text-align:center;"|'''Parameter'''<br />
|style="background: #cee0f2; padding: 0.3em; text-align:center;"|'''Data type'''<br />
|style="background: #cee0f2; padding: 0.3em; text-align:center;"|'''Description'''<br />
|-<br />
|signups<br />
|integer<br />
|Number of Resident accounts that are open and in good standing. Updated daily<br />
|-<br />
|signups_updated_unix<br />
|integer<br />
|UNIX time stamp when signups value was last updated.<br />
|-<br />
|signups_updated_slt<br />
|date<br />
|Time stamp when signups value was last updated in Pacific Time.<br />
|-<br />
|inworld<br />
|integer<br />
|Number of Resident accounts currently logged in. Updated every 3 minutes.<br />
|-<br />
|inworld_updated_unix<br />
|integer<br />
|UNIX time stamp when inworld value was last updated.<br />
|-<br />
|inworld_updated_slt<br />
|date<br />
|Time stamp when inworld value was last updated in Pacific Time.<br />
|}<br />
<br />
=== Text Feed ===<br />
Newline-seperated key-value pairs, with timestamps (for [[llHTTPRequest]]())<br />
<br />
: https://api.secondlife.com/datafeeds/homepage.txt<br />
<br />
Key Pairs are delimited by new lines. Each key pair has additional dates associated indicating when the data was updated in UNIX time and SL time (Pacific Time).<br />
<br />
Example:<br />
<br />
signups_updated_slt<br />
2010-02-02 00:55:02<br />
signups_updated_unix<br />
1265100902<br />
signups<br />
18072841<br />
exchange_rate_updated_slt<br />
2010-02-02 01:03:28<br />
exchange_rate_updated_unix<br />
1265101408<br />
exchange_rate<br />
262.2055<br />
inworld_updated_unix<br />
1265101209<br />
inworld_updated_slt<br />
2010-02-02 01:00:09<br />
inworld<br />
38489<br />
<br />
<br />
{| style="border:1px solid #A3B1BF; text-align:left; background:#f5faff"<br />
|-<br />
|style="background: #cee0f2; padding: 0.3em; text-align:center;"|'''Parameter'''<br />
|style="background: #cee0f2; padding: 0.3em; text-align:center;"|'''Data type'''<br />
|style="background: #cee0f2; padding: 0.3em; text-align:center;"|'''Description'''<br />
|-<br />
|signups_updated_slt<br />
|date<br />
|Time stamp when signups value was last updated in Pacific Time.<br />
|-<br />
|signups_updated_unix<br />
|integer<br />
|UNIX time stamp when signups value was last updated.<br />
|-<br />
|signups<br />
|integer<br />
|Number of Resident accounts that are open and in good standing. Updated daily.<br />
|-<br />
|exchange_rate_updated_slt<br />
|date<br />
|Time stamp when exchange_rate value was last updated in Pacific Time.<br />
|-<br />
|exchange_rate_updated_unix<br />
|integer<br />
|UNIX time stamp when exchange_rate value was last updated.<br />
|-<br />
|exchange_rate<br />
|float<br />
|Average number of L$ that 1 US$ can purchase.<br />
|-<br />
|inworld_updated_unix<br />
|integer<br />
|UNIX time stamp when inworld value was last updated.<br />
|-<br />
|inworld_updated_slt<br />
|date<br />
|Time stamp when inworld value was last updated in Pacific Time.<br />
|-<br />
|inworld<br />
|integer<br />
|Number of Resident accounts currently logged in. Updated every 3 minutes.<br />
|}<br />
<br />
== LindeX == <br />
<br />
=== [[LLSD]] feed ===<br />
<br />
: https://api.secondlife.com/datafeeds/lindex.xml<br />
<br />
Example:<br />
<nowiki><llsd> <br />
<map> <br />
<key>stats</key> <br />
<map> <br />
<key>last_updated_unix</key> <br />
<integer>1265102153</integer> <br />
<key>last_updated_slt</key> <br />
<string>2010-02-02 01:15:53</string> <br />
<br />
<br />
<key>limit_buy_to_limit_sell</key> <br />
<map> <br />
<key>1_hour</key> <br />
<map> <br />
<key>min_rate</key> <br />
<integer>0</integer> <br />
<key>max_rate</key> <br />
<integer>0</integer> <br />
<key>l$_volume</key> <br />
<integer>0</integer> <br />
<key>us$_volume</key> <br />
<float>0.00</float> <br />
</map> <br />
<key>1_day</key> <br />
<map> <br />
<key>min_rate</key> <br />
<integer>259</integer> <br />
<key>max_rate</key> <br />
<integer>269</integer> <br />
<key>l$_volume</key> <br />
<integer>107679</integer> <br />
<key>us$_volume</key> <br />
<float>409.06</float> <br />
</map> <br />
<key>today</key> <br />
<map> <br />
<key>min_rate</key> <br />
<integer>0</integer> <br />
<key>max_rate</key> <br />
<integer>0</integer> <br />
<key>l$_volume</key> <br />
<integer>0</integer> <br />
<key>us$_volume</key> <br />
<float>0.00</float> <br />
</map> <br />
</map> <br />
<key>market_buy</key> <br />
<map> <br />
<key>1_hour</key> <br />
<map> <br />
<key>min_rate</key> <br />
<integer>259</integer> <br />
<key>max_rate</key> <br />
<integer>260</integer> <br />
<key>l$_volume</key> <br />
<integer>1681958</integer> <br />
<key>us$_volume</key> <br />
<float>6476.66</float> <br />
</map> <br />
<key>1_day</key> <br />
<map> <br />
<key>min_rate</key> <br />
<integer>259</integer> <br />
<key>max_rate</key> <br />
<integer>268</integer> <br />
<key>l$_volume</key> <br />
<integer>75386206</integer> <br />
<key>us$_volume</key> <br />
<float>290519.80</float> <br />
</map> <br />
<key>today</key> <br />
<map> <br />
<key>min_rate</key> <br />
<integer>259</integer> <br />
<key>max_rate</key> <br />
<integer>260</integer> <br />
<key>l$_volume</key> <br />
<integer>2252570</integer> <br />
<key>us$_volume</key> <br />
<float>8674.93</float> <br />
</map> <br />
</map> <br />
<key>limit_buy</key> <br />
<map> <br />
<key>best_10_percent</key> <br />
<map> <br />
<key>l$_offered</key> <br />
<integer>45217317</integer> <br />
<key>min_rate</key> <br />
<integer>269</integer> <br />
<key>max_rate</key> <br />
<integer>295</integer> <br />
</map> <br />
</map> <br />
<key>market_sell</key> <br />
<map> <br />
<key>1_hour</key> <br />
<map> <br />
<key>min_rate</key> <br />
<integer>268</integer> <br />
<key>max_rate</key> <br />
<integer>269</integer> <br />
<key>l$_volume</key> <br />
<integer>566660</integer> <br />
<key>us$_volume</key> <br />
<float>2111.89</float> <br />
</map> <br />
<key>1_day</key> <br />
<map> <br />
<key>min_rate</key> <br />
<integer>263</integer> <br />
<key>max_rate</key> <br />
<integer>269</integer> <br />
<key>l$_volume</key> <br />
<integer>31952607</integer> <br />
<key>us$_volume</key> <br />
<float>118893.52</float> <br />
</map> <br />
<key>today</key> <br />
<map> <br />
<key>min_rate</key> <br />
<integer>268</integer> <br />
<key>max_rate</key> <br />
<integer>269</integer> <br />
<key>l$_volume</key> <br />
<integer>711410</integer> <br />
<key>us$_volume</key> <br />
<float>2650.43</float> <br />
</map> <br />
</map> <br />
<key>limit_sell</key> <br />
<map> <br />
<key>best_10_percent</key> <br />
<map> <br />
<key>l$_offered</key> <br />
<integer>128164597</integer> <br />
<key>min_rate</key> <br />
<integer>235</integer> <br />
<key>max_rate</key> <br />
<integer>260</integer> <br />
</map> <br />
</map> <br />
</map> <br />
</map> <br />
</llsd></nowiki><br />
<br />
=== Text feed ===<br />
<br />
Newline-separated key-value pairs for [[llHTTPRequest]]()<br />
<br />
: https://api.secondlife.com/datafeeds/lindex.txt<br />
<br />
updated_unix<br />
1265102153<br />
updated_slt<br />
2010-02-02 01:15:53<br />
ll_1h_l$<br />
0<br />
ll_1h_us$<br />
0.00<br />
ll_1h_max_rate<br />
0<br />
ll_1h_min_rate<br />
0<br />
ll_1d_us$<br />
409.06<br />
ll_1d_max_rate<br />
269<br />
ll_1d_min_rate<br />
259<br />
ll_1d_l$<br />
107679<br />
ll_t_max_rate<br />
0<br />
ll_t_min_rate<br />
0<br />
ll_t_us$<br />
0.00<br />
ll_t_l$<br />
0<br />
mb_1h_min_rate<br />
259<br />
mb_1h_max_rate<br />
260<br />
mb_1h_l$<br />
1681958<br />
mb_1h_us$<br />
6476.66<br />
mb_1d_us$<br />
290519.80<br />
mb_1d_l$<br />
75386206<br />
mb_1d_min_rate<br />
259<br />
mb_1d_max_rate<br />
268<br />
mb_t_max_rate<br />
260<br />
mb_t_us$<br />
8674.93<br />
mb_t_l$<br />
2252570<br />
mb_t_min_rate<br />
259<br />
lb_10%_max_rate<br />
295<br />
lb_10%_l$_offer<br />
45217317<br />
lb_10%_min_rate<br />
269<br />
ms_1h_max_rate<br />
269<br />
ms_1h_min_rate<br />
268<br />
ms_1h_us$<br />
2111.89<br />
ms_1h_l$<br />
566660<br />
ms_1d_max_rate<br />
269<br />
ms_1d_us$<br />
118893.52<br />
ms_1d_l$<br />
31952607<br />
ms_1d_min_rate<br />
263<br />
ms_t_max_rate<br />
269<br />
ms_t_l$<br />
711410<br />
ms_t_min_rate<br />
268<br />
ms_t_us$<br />
2650.43<br />
ls_10%_max_rate<br />
260<br />
ls_10%_min_rate<br />
235<br />
ls_10%_l$_offer<br />
128164597<br />
<br />
The LindeX feeds are updated every 15 minutes and include one set of timestamps.<br />
<br />
<br />
== Reference ==<br />
* [http://community.secondlife.com/t5/Features/New-Data-Feeds-1/ba-p/525446 New Data Feeds #1], SL blog, 2006-10-03<br />
* [http://forums-archive.secondlife.com/54/3c/234168/1.html "Concurrency stats" thread], archived SL forums, 2008-08-01</div>Natty Lindenhttps://wiki.secondlife.com/w/index.php?title=Linden_Lab_Official:Viewer_Web_Widgets&diff=1210226Linden Lab Official:Viewer Web Widgets2021-08-09T18:32:02Z<p>Natty Linden: update widget loader URLs to HTTPS versions</p>
<hr />
<div>{{Supported API}}<br />
<br />
{{KBcaution|Work-in-progress! Expect these widgets to change over time. Watch this page for updates.}}<br />
<br />
These viewer web widgets are a first attempt at making the current Second Life Viewer [https://viewer-splash.secondlife.com/ splash screen] features available for easy use in third-party viewers. Linden Lab provides a base Javascript library that searches the DOM of a given page, populating specifically-named divs with widget content. <br />
<br />
==Basic implementation==<br />
<br />
The example below shows the simplest way to implement these widgets. Simply include the sl.widgets.js library, then include a div with both id="[the widget you want]" and class="second-life-widget".<br />
<br />
&lt;!DOCTYPE html><br />
&lt;html><br />
&lt;head><br />
&lt;script type="text/javascript" src="<nowiki>https:/</nowiki>/lecs-viewer-login-agni.s3.amazonaws.com/pubwidgets/v2/modules/sl.widgets.js"><br />
&lt;/script><br />
&lt;/head><br />
&lt;body><br />
&lt;div id="whats-hot-now-widget" class="second-life-widget">&lt;/div><br />
&lt;/body><br />
&lt;/html><br />
<br />
Version 2 of the main sl.widgets.js library currently lives here:<br />
<br />
https://lecs-viewer-login-agni.s3.amazonaws.com/pubwidgets/v2/modules/sl.widgets.js<br />
<br />
This library will pull in all the supporting content it needs -- other Javascript files, CSS, and the content for each widget. It uses its own version of jQuery, and will create a separate namespace to avoid conflicts if you already have jQuery installed.<br />
<br />
==Widgets==<br />
<br />
====Widget Options====<br />
<br />
Each widget has 2 configuration options that can be passed to it.<br />
<br />
# The number of items a particular widget can have.<br />
# The callback javascript function that needs to be called when someone clicks the particular event/item inside the widget.<br />
<br />
All configuration is passed to widgets through a global javascript variable called - '''slconfig'''. It's a javascript object which further contains individual configuration object for each widget. Below is an example of setting some options to the What's Hot Now widget.<br />
<br />
<script type="text/javascript"><br />
var slconfig = {};<br />
slconfig['whatshotnow'] = {<br />
itemsPerPage : 4,<br />
onclick: localClick<br />
};<br />
<br />
function localClick(stuff){<br />
alert(stuff);<br />
return false;<br />
}<br />
</script><br />
<br />
In the above code we do couple of things.<br />
<br />
# Define a global javascript object called slconfig.<br />
# Set the config object for what's how not widget. This needs to be called 'whatshotnow'. For other widgets please refer below sections.<br />
# We also set onclick functionality. We are setting it to a localClick function which is defined later in the code. Please make sure you don't add () next to the function name as it would result in calling the function which we don't want. We just need to set the function name.<br />
# The local function gets passed slurl of the event or destination.<br />
<br />
====Widget Size Precedence====<br />
<br />
# Items per page<br />
# Width defined in css<br />
<br />
When items per page is passed, the widget resizes to fit those number of items. It won't take into consideration whether the widget fits the container or not. When the items per page isn't passed - the size of the widgets is taken from the css file. This again has an order. By default all widgets use Second Life widgets css, and can be over-ridden by your local css file or inline css.<br />
<br />
===What's hot now===<br />
<br />
This widget displays a thumbnail list of destinations with people (a population of 25 or less, in the last ~10 minutes). The cutoff number may change over time; 25 was chosen as a reasonable performance threshold.<br />
<br />
With the main js library loaded, include this div in your html markup:<br />
<br />
&lt;div id="whats-hot-now-widget" class="second-life-widget">&lt;/div><br />
<br />
This widget expects the above div to be a fixed height of 148px. It will adapt its content to 100% of the div's width, so you can make it as wide as you like. To control positioning, size, and other parameters, just style the #whats-hot-now-widget in your own page's CSS.<br />
<br />
This widget takes 2 options. It needs to be set in '''slconfig['whatshotnow']''' object. Below are the options.<br />
<br />
# itemsPerPage - the number of items in the widget per page.<br />
# onclick - a javascript function which needs to be called when an item in this widget gets clicked. The javascript function is called back with the clicked item slurl. When this option is set, the overlay which usually shows up is cancelled. <br />
<br />
===Destinations===<br />
<br />
This widget displays a thumbnail list of destinations from a category. The default category is 'Featured Events'. It can be changed from the select box in the widget. The maximum number of destinations shown is 24.<br />
<br />
With the main js library loaded, include this div in your html markup:<br />
<br />
&lt;div id="destinations-widget" class="second-life-widget">&lt;/div><br />
<br />
This widget expects the above div to be a fixed height of 148px. It will adapt its content to 100% of the div's width, so you can make it as wide as you like. To control positioning, size, and other parameters, just style the #destinations-widget in your own page's CSS.<br />
<br />
This widget takes 2 options. It needs to be set in '''slconfig['destinations']''' object. Below are the options.<br />
<br />
# itemsPerPage - the number of items in the widget per page.<br />
# onclick - a javascript function which needs to be called when an item in this widget gets clicked. The javascript function is called back with the clicked item slurl. When this option is set, the overlay which usually shows up is cancelled. <br />
<br />
===Blogs===<br />
<br />
This widget displays a list of all recent blog posts from SL. <br />
<br />
With the main js library loaded, include this div in your html markup:<br />
<br />
&lt;div id="blogs-widget" class="second-life-widget">&lt;/div><br />
<br />
This widget expects the above div to be a fixed width of 263px. The height will change based on the css/itemsPerPage options. To control positioning, size, and other parameters, just style the #blogs-widget in your own page's CSS.<br />
<br />
This widget takes 2 options. It needs to be set in '''slconfig['blog']''' object. Below are the options.<br />
<br />
# itemsPerPage - the number of blog listings in the widget per page.<br />
# onclick - a javascript function which needs to be called when an item in this widget gets clicked. The javascript function is called back with an object. When this option is set, the overlay which usually shows up is cancelled. Here is what is passed back to the call back - a object with these properties - creator, date, description, link, pubDate, title<br />
<br />
===Destination Images===<br />
<br />
This widget displays a scrollable list of featured destinations with larger pictures. The number of destinations shown is 8.<br />
<br />
With the main js library loaded, include this div in your html markup:<br />
<br />
&lt;div id="destination-images-widget" class="second-life-widget">&lt;/div><br />
<br />
This widget expects the above div to be a fixed height of 267px and fixed width of 660px. To control positioning, and other parameters, just style the #destination-widget in your own page's CSS.<br />
<br />
This widget takes 1 options. It needs to be set in '''slconfig['destination-images']''' object. Below are the options.<br />
<br />
# onclick - a javascript function which needs to be called when an item in this widget gets clicked. The javascript function is called back with the clicked item slurl. When this option is set, the overlay which usually shows up is cancelled. <br />
<br />
===Upcoming Events (List Version)===<br />
<br />
This widget displays a list of upcoming events sorted with the most recent one at top<br />
<br />
With the main js library loaded, include this div in your html markup:<br />
<br />
&lt;div id="upcoming-events-widget" class="second-life-widget">&lt;/div><br />
<br />
This widget expects the above div to be a fixed width of 263px. It will adapt its content to 100% of the div's width, so you can make it as wide as you like. To control positioning, size, and other parameters, just style the #upcoming-events-widget in your own page's CSS.<br />
<br />
This widget takes 2 options. It needs to be set in '''slconfig['events']''' object. Below are the options.<br />
<br />
# itemsPerPage - the number of items in the widget per page.<br />
# onclick - a javascript function which needs to be called when an item in this widget gets clicked. The javascript function is called back with the clicked item slurl. When this option is set, the overlay which usually shows up is cancelled. <br />
<br />
<br />
===Upcoming Events (Filmstrip Version)===<br />
<br />
This widget displays thumbnail list of upcoming events sorted with the most recent one at the beginning<br />
<br />
With the main js library loaded, include this div in your html markup:<br />
<br />
&lt;div id="events-fs-widget" class="second-life-widget">&lt;/div><br />
<br />
This widget expects the above div to be a fixed height of 143px. It will adapt its content to 100% of the div's width, so you can make it as wide as you like. To control positioning, size, and other parameters, just style the #upcoming-events-widget in your own page's CSS.<br />
<br />
This widget takes 2 options. It needs to be set in '''slconfig['eventsfs']''' object. Below are the options.<br />
<br />
# itemsPerPage - the number of items in the widget per page.<br />
# onclick - a javascript function which needs to be called when an item in this widget gets clicked. The javascript function is called back with the clicked item slurl. When this option is set, the overlay which usually shows up is cancelled. <br />
<br />
<br />
==Localization==<br />
<br />
Widget strings can be localized by adding ?lang=[de|es|fr|ja|pt] to your page's URL.<br />
<br />
== Teleport ==<br />
<br />
The teleport widget provides a more intuitive user experience for links to locations within Second Life (both <tt><nowiki>secondlife://</nowiki></tt> links and <tt><nowiki>maps.secondlife.com</nowiki></tt> links). If a user has the Second Life viewer installed, the link should launch the viewer and teleport to that location. If the user does not have the viewer installed, the link should take the user to the corresponding page in <tt><nowiki>maps.secondlife.com</nowiki></tt>. The widget provides the following behavior:<br />
* If the viewer is installed, links to both <tt><nowiki>secondlife://</nowiki></tt> URLs and <tt><nowiki>maps.secondlife.com</nowiki></tt> URLs should launch the viewer and go to the specified location in-world.<br />
* If the viewer is not installed, links to both <tt><nowiki>secondlife://</nowiki></tt>URLs and <tt><nowiki>maps.secondlife.com</nowiki></tt> URLs should go to the specified location in <tt><nowiki>maps.secondlife.com</nowiki></tt>.<br />
* If the link is to something other than a <tt><nowiki>secondlife://</nowiki></tt> URL or a <tt><nowiki>maps.secondlife.com</nowiki></tt> URL, the link should behave normally and go to the specified href.<br />
To employ the widget requires only two steps:<br />
* Add the following script tag to your webpage:<br />
<nowiki><script type="text/javascript" src="https://lecs-viewer-login-agni.s3.amazonaws.com/pubwidgets/v2/modules/sl.teleport.js"></script></nowiki><br />
* Add the class "sl-teleport" to any links you wish to be controlled by the widget, for example:<br />
<nowiki><a class="sl-teleport" href="https://maps.secondlife.com/secondlife/MyCoolPlace/000/000/000/">My cool place</a></nowiki><br />
:''or''<br />
<nowiki><a class="sl-teleport" href="secondlife://MyCoolPlace/000/000/000/">My cool place</a></nowiki><br />
<br />
==Future roadmap==<br />
<br />
For other features or requests, please file a Jira in the 'Web' project for component 'Viewer Login Page' and/or discuss in the talk page here.</div>Natty Lindenhttps://wiki.secondlife.com/w/index.php?title=Linden_Lab_Official:Viewer_Web_Widgets&diff=1210137Linden Lab Official:Viewer Web Widgets2021-07-30T21:09:41Z<p>Natty Linden: fix code blocks</p>
<hr />
<div>{{Supported API}}<br />
<br />
{{KBcaution|Work-in-progress! Expect these widgets to change over time. Watch this page for updates.}}<br />
<br />
These viewer web widgets are a first attempt at making the current Second Life Viewer [http://viewer-login.agni.lindenlab.com/ login screen] features available for easy use in third-party viewers. Linden Lab provides a base Javascript library that searches the DOM of a given page, populating specifically-named divs with widget content. <br />
<br />
==Basic implementation==<br />
<br />
The example below shows the simplest way to implement these widgets. Simply include the sl.widgets.js library, then include a div with both id="[the widget you want]" and class="second-life-widget".<br />
<br />
&lt;!DOCTYPE html><br />
&lt;html><br />
&lt;head><br />
&lt;script type="text/javascript" src="<nowiki>http:/</nowiki>/lecs-viewer-login-agni.s3.amazonaws.com/pubwidgets/v2/modules/sl.widgets.js"><br />
&lt;/script><br />
&lt;/head><br />
&lt;body><br />
&lt;div id="whats-hot-now-widget" class="second-life-widget">&lt;/div><br />
&lt;/body><br />
&lt;/html><br />
<br />
Version 2 of the main sl.widgets.js library currently lives here:<br />
<br />
http://lecs-viewer-login-agni.s3.amazonaws.com/pubwidgets/v2/modules/sl.widgets.js<br />
<br />
This library will pull in all the supporting content it needs -- other Javascript files, CSS, and the content for each widget. It uses its own version of jQuery, and will create a separate namespace to avoid conflicts if you already have jQuery installed.<br />
<br />
==Widgets==<br />
<br />
====Widget Options====<br />
<br />
Each widget has 2 configuration options that can be passed to it.<br />
<br />
# The number of items a particular widget can have.<br />
# The callback javascript function that needs to be called when someone clicks the particular event/item inside the widget.<br />
<br />
All configuration is passed to widgets through a global javascript variable called - '''slconfig'''. It's a javascript object which further contains individual configuration object for each widget. Below is an example of setting some options to the What's Hot Now widget.<br />
<br />
<script type="text/javascript"><br />
var slconfig = {};<br />
slconfig['whatshotnow'] = {<br />
itemsPerPage : 4,<br />
onclick: localClick<br />
};<br />
<br />
function localClick(stuff){<br />
alert(stuff);<br />
return false;<br />
}<br />
</script><br />
<br />
In the above code we do couple of things.<br />
<br />
# Define a global javascript object called slconfig.<br />
# Set the config object for what's how not widget. This needs to be called 'whatshotnow'. For other widgets please refer below sections.<br />
# We also set onclick functionality. We are setting it to a localClick function which is defined later in the code. Please make sure you don't add () next to the function name as it would result in calling the function which we don't want. We just need to set the function name.<br />
# The local function gets passed slurl of the event or destination.<br />
<br />
====Widget Size Precedence====<br />
<br />
# Items per page<br />
# Width defined in css<br />
<br />
When items per page is passed, the widget resizes to fit those number of items. It won't take into consideration whether the widget fits the container or not. When the items per page isn't passed - the size of the widgets is taken from the css file. This again has an order. By default all widgets use Second Life widgets css, and can be over-ridden by your local css file or inline css.<br />
<br />
===What's hot now===<br />
<br />
This widget displays a thumbnail list of destinations with people (a population of 25 or less, in the last ~10 minutes). The cutoff number may change over time; 25 was chosen as a reasonable performance threshold.<br />
<br />
With the main js library loaded, include this div in your html markup:<br />
<br />
&lt;div id="whats-hot-now-widget" class="second-life-widget">&lt;/div><br />
<br />
This widget expects the above div to be a fixed height of 148px. It will adapt its content to 100% of the div's width, so you can make it as wide as you like. To control positioning, size, and other parameters, just style the #whats-hot-now-widget in your own page's CSS.<br />
<br />
This widget takes 2 options. It needs to be set in '''slconfig['whatshotnow']''' object. Below are the options.<br />
<br />
# itemsPerPage - the number of items in the widget per page.<br />
# onclick - a javascript function which needs to be called when an item in this widget gets clicked. The javascript function is called back with the clicked item slurl. When this option is set, the overlay which usually shows up is cancelled. <br />
<br />
===Destinations===<br />
<br />
This widget displays a thumbnail list of destinations from a category. The default category is 'Featured Events'. It can be changed from the select box in the widget. The maximum number of destinations shown is 24.<br />
<br />
With the main js library loaded, include this div in your html markup:<br />
<br />
&lt;div id="destinations-widget" class="second-life-widget">&lt;/div><br />
<br />
This widget expects the above div to be a fixed height of 148px. It will adapt its content to 100% of the div's width, so you can make it as wide as you like. To control positioning, size, and other parameters, just style the #destinations-widget in your own page's CSS.<br />
<br />
This widget takes 2 options. It needs to be set in '''slconfig['destinations']''' object. Below are the options.<br />
<br />
# itemsPerPage - the number of items in the widget per page.<br />
# onclick - a javascript function which needs to be called when an item in this widget gets clicked. The javascript function is called back with the clicked item slurl. When this option is set, the overlay which usually shows up is cancelled. <br />
<br />
===Blogs===<br />
<br />
This widget displays a list of all recent blog posts from SL. <br />
<br />
With the main js library loaded, include this div in your html markup:<br />
<br />
&lt;div id="blogs-widget" class="second-life-widget">&lt;/div><br />
<br />
This widget expects the above div to be a fixed width of 263px. The height will change based on the css/itemsPerPage options. To control positioning, size, and other parameters, just style the #blogs-widget in your own page's CSS.<br />
<br />
This widget takes 2 options. It needs to be set in '''slconfig['blog']''' object. Below are the options.<br />
<br />
# itemsPerPage - the number of blog listings in the widget per page.<br />
# onclick - a javascript function which needs to be called when an item in this widget gets clicked. The javascript function is called back with an object. When this option is set, the overlay which usually shows up is cancelled. Here is what is passed back to the call back - a object with these properties - creator, date, description, link, pubDate, title<br />
<br />
===Destination Images===<br />
<br />
This widget displays a scrollable list of featured destinations with larger pictures. The number of destinations shown is 8.<br />
<br />
With the main js library loaded, include this div in your html markup:<br />
<br />
&lt;div id="destination-images-widget" class="second-life-widget">&lt;/div><br />
<br />
This widget expects the above div to be a fixed height of 267px and fixed width of 660px. To control positioning, and other parameters, just style the #destination-widget in your own page's CSS.<br />
<br />
This widget takes 1 options. It needs to be set in '''slconfig['destination-images']''' object. Below are the options.<br />
<br />
# onclick - a javascript function which needs to be called when an item in this widget gets clicked. The javascript function is called back with the clicked item slurl. When this option is set, the overlay which usually shows up is cancelled. <br />
<br />
===Upcoming Events (List Version)===<br />
<br />
This widget displays a list of upcoming events sorted with the most recent one at top<br />
<br />
With the main js library loaded, include this div in your html markup:<br />
<br />
&lt;div id="upcoming-events-widget" class="second-life-widget">&lt;/div><br />
<br />
This widget expects the above div to be a fixed width of 263px. It will adapt its content to 100% of the div's width, so you can make it as wide as you like. To control positioning, size, and other parameters, just style the #upcoming-events-widget in your own page's CSS.<br />
<br />
This widget takes 2 options. It needs to be set in '''slconfig['events']''' object. Below are the options.<br />
<br />
# itemsPerPage - the number of items in the widget per page.<br />
# onclick - a javascript function which needs to be called when an item in this widget gets clicked. The javascript function is called back with the clicked item slurl. When this option is set, the overlay which usually shows up is cancelled. <br />
<br />
<br />
===Upcoming Events (Filmstrip Version)===<br />
<br />
This widget displays thumbnail list of upcoming events sorted with the most recent one at the beginning<br />
<br />
With the main js library loaded, include this div in your html markup:<br />
<br />
&lt;div id="events-fs-widget" class="second-life-widget">&lt;/div><br />
<br />
This widget expects the above div to be a fixed height of 143px. It will adapt its content to 100% of the div's width, so you can make it as wide as you like. To control positioning, size, and other parameters, just style the #upcoming-events-widget in your own page's CSS.<br />
<br />
This widget takes 2 options. It needs to be set in '''slconfig['eventsfs']''' object. Below are the options.<br />
<br />
# itemsPerPage - the number of items in the widget per page.<br />
# onclick - a javascript function which needs to be called when an item in this widget gets clicked. The javascript function is called back with the clicked item slurl. When this option is set, the overlay which usually shows up is cancelled. <br />
<br />
<br />
==Localization==<br />
<br />
Widget strings can be localized by adding ?lang=[de|es|fr|ja|pt] to your page's URL.<br />
<br />
== Teleport ==<br />
<br />
The teleport widget provides a more intuitive user experience for links to locations within Second Life (both <tt><nowiki>secondlife://</nowiki></tt> links and <tt><nowiki>maps.secondlife.com</nowiki></tt> links). If a user has the Second Life viewer installed, the link should launch the viewer and teleport to that location. If the user does not have the viewer installed, the link should take the user to the corresponding page in <tt><nowiki>maps.secondlife.com</nowiki></tt>. The widget provides the following behavior:<br />
* If the viewer is installed, links to both <tt><nowiki>secondlife://</nowiki></tt> URLs and <tt><nowiki>http://maps.secondlife.com</nowiki></tt> URLs should launch the viewer and go to the specified location in-world.<br />
* If the viewer is not installed, links to both <tt><nowiki>secondlife://</nowiki></tt>URLs and <tt><nowiki>http://maps.secondlife.com</nowiki></tt> URLs should go to the specified location in <tt><nowiki>maps.secondlife.com</nowiki></tt>.<br />
* If the link is to something other than a <tt><nowiki>secondlife://</nowiki></tt> URL or a <tt><nowiki>http://maps.secondlife.com</nowiki></tt> URL, the link should behave normally and go to the specified href.<br />
To employ the widget requires only two steps:<br />
* Add the following script tag to your webpage:<br />
<nowiki><script type="text/javascript" src="https://lecs-viewer-login-agni.s3.amazonaws.com/pubwidgets/v2/modules/sl.teleport.js"></script></nowiki><br />
* Add the class "sl-teleport" to any links you wish to be controlled by the widget, for example:<br />
<nowiki><a class="sl-teleport" href="http://maps.secondlife.com/secondlife/MyCoolPlace/000/000/000/">My cool place</a></nowiki><br />
:''or''<br />
<nowiki><a class="sl-teleport" href="secondlife://MyCoolPlace/000/000/000/">My cool place</a></nowiki><br />
<br />
==Future roadmap==<br />
<br />
For other features or requests, please file a Jira in the 'Web' project for component 'Viewer Login Page' and/or discuss in the talk page here.</div>Natty Lindenhttps://wiki.secondlife.com/w/index.php?title=Linden_Lab_Official:Map_API_Basic_Examples&diff=1205054Linden Lab Official:Map API Basic Examples2016-12-14T01:51:46Z<p>Natty Linden: Updated for version 2 of API #2</p>
<hr />
<div>{{Supported API}}<br />
{{:API Portal/navigation|map}}<br />
__TOC__<br />
<br />
See [http://maps.secondlife.com/examples/ Second Life Map API Examples] to see these examples in action.<br />
<br clear=all/><br />
== Required image files ==<br />
<br />
Some of the following examples use image files for markers. To run the examples with the code as shown, download the image files and save them in the same directory as the example HTML files. Alternatively, create the Img objects using the URLs listed in the table below that refer to the images stored on this wiki.<br />
<br />
The following table lists the images used in the examples:<br />
<br />
{| {{Prettytable}}<br />
|-{{Hl2}}<br />
! Image<br />
! File name<br />
! URL<br />
<br />
|-<br />
| [[Image:Yellow marker.gif]]<br />
| b_map_yellow.gif<br />
| http://maps.secondlife.com/examples/b_map_yellow.gif<br />
<br />
|-<br />
| [[Image:Fountain.gif]] <br />
| fountain.gif<br />
| http://maps.secondlife.com/examples/fountain.gif<br />
|}<br />
<br />
== Simple map ==<br />
<br />
The first example is a map with basic panning and zooming capabilities. When you are done, it will look as shown here.<br />
<br />
[[Image:MapAPI ex1.png|thumb|right|Simple map]]<br />
<br />
You can pan the map and zoom in and out.<br />
<br />
To create this example, follow these steps:<br />
<br />
# Add common HTML header code.<br />
# Add HTML body element.<br />
# Add '''loadmap()''' function. <br />
# Save and test.<br />
<br />
<br clear="all"/><br />
<br />
'''1. Add common HTML header'''<br />
<br />
First, include the [[Webmap_API#Common_HTML_header|common header code]], required by all Webmap applications. <br />
<br />
<source lang="html5"><br />
<!DOCTYPE html><br />
<html><head><br />
<meta charset="utf-8"><br />
<br />
<script src="http://maps.secondlife.com/_scripts/sl.mapapi2.js"></script><br />
<link rel="stylesheet" type="text/css" href="http://maps.secondlife.com/_styles/sl.mapapi2.css"><br />
<br />
<style><br />
div#map-container {<br />
width: 500px;<br />
height: 500px;<br />
}<br />
</style><br />
...<br />
</head><br />
</source><br />
<br />
'''2. Add HTML body element'''<br />
<br />
Next, add the HTML body element to contain your map. The body contains a Javascript onload event handler that calls the '''loadmap()''' function you will define in the next step.<br />
<br />
<source lang="html5"><br />
<body onload="loadmap()"><br />
<div id="map-container"></div><br />
</body><br />
</source><br />
<br />
The div element is passed to the SLMap object constructor, using the <code>document.getElementById('map-container')</code> DOM method call.<br />
<br />
'''3. Add loadmap() function'''<br />
<br />
The body element you just added has a Javascript event handler for the "onload" event, called when the browser initially loads the page. You deifined this to call a '''loadmap()''' function. Now, define that Javascript function in the head of your page (after the code that is already there), as follows: <br />
<br />
<source lang="html5"><br />
<script><br />
function loadmap() {<br />
var lmap = SLMap(document.getElementById('map-container'));<br />
lmap.setView([1002, 997], 7);<br />
}<br />
</script><br />
</source><br />
<br />
The first line defines a new Map object from the HTML div element. The second line centers the map at a specific point, and zooms it to zoom level three.<br />
<br />
'''4. Save and test''' <br />
<br />
When you are done, save the file, and then load it into your web browser. Try panning the map by clicking and dragging the mouse cursor. Also try zooming the view in and out by clicking the "+" and "-" icons.<br />
<br />
'''Complete source code'''<br />
<br />
<source lang="html5"><br />
<!DOCTYPE html><br />
<html><head><br />
<meta charset="utf-8"><br />
<br />
<script src="http://maps.secondlife.com/_scripts/sl.mapapi2.js"></script><br />
<link rel="stylesheet" type="text/css" href="http://maps.secondlife.com/_styles/sl.mapapi2.css"><br />
<br />
<style><br />
div#map-container {<br />
width: 500px;<br />
height: 500px;<br />
}<br />
</style><br />
<br />
<script><br />
function loadmap() {<br />
var lmap = new SLMap(document.getElementById('map-container'));<br />
lmap.setView([1002, 997], 7);<br />
}<br />
</script><br />
<br />
</head><body onload="loadmap()"><br />
<br />
<div id="map-container"></div><br />
<br />
</body></html><br />
</source><br />
<br />
==Map with a marker ==<br />
<br />
[[Image:Map with Marker.png|thumb|right|Map with a marker]]<br />
<br />
A ''marker'' is simply an image displayed at a specific location on the map. The Map API enables you to define an image for each zoom level, so the marker can appear differently at each zoom level if you wish.<br />
<br />
This example shows how to make a map with a marker. The code is the same as the first example, with with addition of some Javascript after the initialization of the map in the page's onload event handler function, '''loadmap()'''. <br />
<br />
To use the code below verbatim, you must download the marker image as described in [[#Required_image_files|Required image files]]; you can also use the URL of the wiki image if you prefer. <br />
<br />
The code first creates the icon for the marker, then places it on the map at (997, 1002) at the welcome area fountain. Because the same icon is specified for every zoom level, it appears the same size regardless of zoom level.<br />
<br />
<br clear="all"/><br />
<br />
<source lang="javascript"><br />
// creates the icon<br />
var icon = L.icon({<br />
iconUrl: 'b_map_yellow.gif',<br />
iconSize: [9, 9]<br />
});<br />
<br />
// creates the marker<br />
L.marker([1002, 997], {<br />
icon: icon<br />
}).addTo(lmap);<br />
</source><br />
<br />
In this example, clicking on the marker does nothing. Subsequent examples illustrate adding some onClick behavior. <br />
<br />
'''Source code'''<br />
<br />
Replace the first line comment below with the [[Webmap_API#Common_HTML_header|common header code]].<br />
<br />
<source lang="html5"><br />
<!DOCTYPE html><br />
<html><head><br />
<meta charset="utf-8"><br />
<br />
<script src="/_scripts/sl.mapapi2.js" type="text/javascript"></script><br />
<link href="/_styles/sl.mapapi2.css" rel="stylesheet" type="text/css"><br />
<br />
<script><br />
function loadmap() {<br />
// creates the map<br />
var lmap = SLMap(document.getElementById('map-container'));<br />
lmap.setView([1002, 997], 7);<br />
<br />
// creates the icon<br />
var icon = L.icon({<br />
iconUrl: 'b_map_yellow.gif',<br />
iconSize: [9, 9]<br />
});<br />
<br />
// creates the marker<br />
L.marker([1002, 997], {<br />
icon: icon<br />
}).addTo(lmap);<br />
}<br />
</script><br />
<br />
</head><body onload="loadmap()"><br />
<br />
<p>There should be a yellow marker in the center of this map:</p><br />
<div id="map-container"></div><br />
<br />
</body></html><br />
</source><br />
<br />
==Map with an initial open popup ==<br />
<br />
[[Image:Map_with_window.png|thumb|right|Map with a popup]]<br />
<br />
A ''popup'' is a caption pointing to a specific point on the map, as shown in the screenshot at right. It looks something like a comic book dialog bubble.<br />
<br />
Use the [http://leafletjs.com/reference-1.0.2.html#popup Popup] object to create a popup.<br />
For example, to create a popup that opens initially when the page loads:<br />
<br />
# Instantiate a popup object with the desired caption.<br />
# Call its '''openOn()''' method with the [[Webmap_API_Reference#Map|Map]] object to add the popup to the map. <br />
<br clear="all"/><br />
For example:<br />
<br />
<source lang="javascript"><br />
// creates a popup<br />
L.popup()<br />
.setLatLng([1002, 997])<br />
.setContent("<p>This is where the welcome area fountain is!!</p><img src='fountain.gif'>")<br />
.openOn(lmap);<br />
</source><br />
<br />
The code adds an open popup to the map positioned again at (997, 1002), right above the welcome area fountain. <br />
<br />
NOTE: after the user closes the popup there is no way to bring it back. A subsequent example will show how to enable the user to reopen the popup, by using markers with popups. <br />
<br clear="all"/><br />
<br />
To make the map initially centered on the fountain, change the call to '''setView()''' as follows:<br />
<br />
<source lang="javascript"><br />
lmap.setView([1002, 997], 6);<br />
</source><br />
<br />
'''Source code'''<br />
<br />
This example shows how to create a map with a popup. The code is the same as the first example, with the addition of some lines of Javascript after the initialization of the map, in the '''loadmap()''' function.<br />
<br />
<source lang="html5"><br />
<!DOCTYPE html><br />
<html><head><br />
<meta charset="utf-8"><br />
<br />
<script src="/_scripts/sl.mapapi2.js" type="text/javascript"></script><br />
<link href="/_styles/sl.mapapi2.css" rel="stylesheet" type="text/css"><br />
<br />
<link href="/_styles/MAIN.css" rel="stylesheet" type="text/css"><br />
<style><br />
div#map-container {<br />
width: 500px;<br />
height: 500px;<br />
}<br />
</style><br />
<script><br />
var lmap;<br />
function loadmap() {<br />
// creates the map<br />
lmap = SLMap(document.getElementById('map-container'));<br />
lmap.setView([1001.5, 997.5], 7);<br />
<br />
// creates a popup<br />
L.popup()<br />
.setLatLng([1002, 997])<br />
.setContent("<p>This is where the welcome area fountain is!!</p><img src='fountain.gif'>")<br />
.openOn(lmap);<br />
}<br />
</script><br />
<br />
<title>Map API Examples - Map with an initial open popup</title><br />
<br />
</head><body onload="loadmap()"><br />
<br />
<h1>Map with an initial open popup </h1><br />
<p><a href="javascript:gotoSLURL(997, 1002, lmap)"> Go to (997, 1002)</a> where the welcome fountain is.</p><br />
<br />
<div id="map-container"></div><br />
<br />
</body></html><br />
</source><br />
<br />
==Map with a marker that opens a popup ==<br />
<br />
[[Image:Map with marker and window.png|thumb|right|Map with marker that opens a popup]]<br />
<br />
This example shows how to create a map with a marker which opens a popup when clicked. It is a combination of the previous two examples, because the code first creates a marker, then a popup.<br />
<br />
The difference comes in adding the marker to the map: first you bind the popup to the marker, using '''marker.bindPopup()''':<br />
<br />
<source lang="javascript"><br />
marker.bindPopup(popup);<br />
marker.addTo(lmap);<br />
</source><br />
<br />
Doing this adds the marker and causes it to open the specified popup when the user clicks it. If the user subsequently closes the popup, they can open it again by clicking on the marker.<br />
<br />
<br clear="all" /><br />
'''Source code'''<br />
<br />
<source lang="html5"><br />
<!DOCTYPE html><br />
<html><head><br />
<meta charset="utf-8"><br />
<br />
<script src="/_scripts/sl.mapapi2.js" type="text/javascript"></script><br />
<link href="/_styles/sl.mapapi2.css" rel="stylesheet" type="text/css"><br />
<br />
<link href="/_styles/MAIN.css" rel="stylesheet" type="text/css"><br />
<style><br />
div#map-container {<br />
width: 500px;<br />
height: 500px;<br />
}<br />
</style><br />
<script><br />
function loadmap() {<br />
// creates the map<br />
var lmap = SLMap(document.getElementById('map-container'));<br />
lmap.setView([1001.5, 997.5], 7);<br />
<br />
// creates the icon<br />
var icon = L.icon({<br />
iconUrl: 'b_map_yellow.gif',<br />
iconSize: [9, 9]<br />
});<br />
<br />
// creates the marker<br />
var marker = L.marker([1002, 997], {<br />
icon: icon<br />
});<br />
<br />
// creates a popup<br />
var popup = L.popup().setContent("<p>This is where the welcome area fountain is!!</p><img src='fountain.gif'>");<br />
<br />
// adds the marker to the map<br />
marker.bindPopup(popup);<br />
marker.addTo(lmap);<br />
}<br />
</script><br />
<br />
<title>Map API Examples - Map with a marker that opens a popup</title><br />
<br />
</head><body onload="loadmap()" onlunload="GUnload()"><br />
<br />
<h1>Map with a marker that opens a popup</h1><br />
<br />
<div id="map-container"></div><br />
<p>Click on the marker to open a popup.</p><br />
<br />
</body></html><br />
</source></div>Natty Lindenhttps://wiki.secondlife.com/w/index.php?title=Linden_Lab_Official:Map_API_Basic_Examples&diff=1205053Linden Lab Official:Map API Basic Examples2016-12-14T01:49:52Z<p>Natty Linden: Updated for version 2 of API</p>
<hr />
<div>{{Supported API}}<br />
{{:API Portal/navigation|map}}<br />
__TOC__<br />
<br />
See [http://maps.secondlife.com/examples/ Second Life Map API Examples] to see these examples in action.<br />
<br clear=all/><br />
== Required image files ==<br />
<br />
Some of the following examples use image files for markers. To run the examples with the code as shown, download the image files and save them in the same directory as the example HTML files. Alternatively, create the Img objects using the URLs listed in the table below that refer to the images stored on this wiki.<br />
<br />
The following table lists the images used in the examples:<br />
<br />
{| {{Prettytable}}<br />
|-{{Hl2}}<br />
! Image<br />
! File name<br />
! URL<br />
<br />
|-<br />
| [[Image:Yellow marker.gif]]<br />
| b_map_yellow.gif<br />
| <br>http://slurl.com/examples/b_map_yellow.gif<br />
<br />
|-<br />
| [[Image:Fountain.gif]] <br />
| fountain.gif<br />
| http://slurl.com/examples/fountain.gif<br />
|}<br />
<br />
== Simple map ==<br />
<br />
The first example is a map with basic panning and zooming capabilities. When you are done, it will look as shown here.<br />
<br />
[[Image:MapAPI ex1.png|thumb|right|Simple map]]<br />
<br />
You can pan the map and zoom in and out.<br />
<br />
To create this example, follow these steps:<br />
<br />
# Add common HTML header code.<br />
# Add HTML body element.<br />
# Add '''loadmap()''' function. <br />
# Save and test.<br />
<br />
<br clear="all"/><br />
<br />
'''1. Add common HTML header'''<br />
<br />
First, include the [[Webmap_API#Common_HTML_header|common header code]], required by all Webmap applications. <br />
<br />
<source lang="html5"><br />
<!DOCTYPE html><br />
<html><head><br />
<meta charset="utf-8"><br />
<br />
<script src="http://maps.secondlife.com/_scripts/sl.mapapi2.js"></script><br />
<link rel="stylesheet" type="text/css" href="http://maps.secondlife.com/_styles/sl.mapapi2.css"><br />
<br />
<style><br />
div#map-container {<br />
width: 500px;<br />
height: 500px;<br />
}<br />
</style><br />
...<br />
</head><br />
</source><br />
<br />
'''2. Add HTML body element'''<br />
<br />
Next, add the HTML body element to contain your map. The body contains a Javascript onload event handler that calls the '''loadmap()''' function you will define in the next step.<br />
<br />
<source lang="html5"><br />
<body onload="loadmap()"><br />
<div id="map-container"></div><br />
</body><br />
</source><br />
<br />
The div element is passed to the SLMap object constructor, using the <code>document.getElementById('map-container')</code> DOM method call.<br />
<br />
'''3. Add loadmap() function'''<br />
<br />
The body element you just added has a Javascript event handler for the "onload" event, called when the browser initially loads the page. You deifined this to call a '''loadmap()''' function. Now, define that Javascript function in the head of your page (after the code that is already there), as follows: <br />
<br />
<source lang="html5"><br />
<script><br />
function loadmap() {<br />
var lmap = SLMap(document.getElementById('map-container'));<br />
lmap.setView([1002, 997], 7);<br />
}<br />
</script><br />
</source><br />
<br />
The first line defines a new Map object from the HTML div element. The second line centers the map at a specific point, and zooms it to zoom level three.<br />
<br />
'''4. Save and test''' <br />
<br />
When you are done, save the file, and then load it into your web browser. Try panning the map by clicking and dragging the mouse cursor. Also try zooming the view in and out by clicking the "+" and "-" icons.<br />
<br />
'''Complete source code'''<br />
<br />
<source lang="html5"><br />
<!DOCTYPE html><br />
<html><head><br />
<meta charset="utf-8"><br />
<br />
<script src="http://maps.secondlife.com/_scripts/sl.mapapi2.js"></script><br />
<link rel="stylesheet" type="text/css" href="http://maps.secondlife.com/_styles/sl.mapapi2.css"><br />
<br />
<style><br />
div#map-container {<br />
width: 500px;<br />
height: 500px;<br />
}<br />
</style><br />
<br />
<script><br />
function loadmap() {<br />
var lmap = new SLMap(document.getElementById('map-container'));<br />
lmap.setView([1002, 997], 7);<br />
}<br />
</script><br />
<br />
</head><body onload="loadmap()"><br />
<br />
<div id="map-container"></div><br />
<br />
</body></html><br />
</source><br />
<br />
==Map with a marker ==<br />
<br />
[[Image:Map with Marker.png|thumb|right|Map with a marker]]<br />
<br />
A ''marker'' is simply an image displayed at a specific location on the map. The Map API enables you to define an image for each zoom level, so the marker can appear differently at each zoom level if you wish.<br />
<br />
This example shows how to make a map with a marker. The code is the same as the first example, with with addition of some Javascript after the initialization of the map in the page's onload event handler function, '''loadmap()'''. <br />
<br />
To use the code below verbatim, you must download the marker image as described in [[#Required_image_files|Required image files]]; you can also use the URL of the wiki image if you prefer. <br />
<br />
The code first creates the icon for the marker, then places it on the map at (997, 1002) at the welcome area fountain. Because the same icon is specified for every zoom level, it appears the same size regardless of zoom level.<br />
<br />
<br clear="all"/><br />
<br />
<source lang="javascript"><br />
// creates the icon<br />
var icon = L.icon({<br />
iconUrl: 'b_map_yellow.gif',<br />
iconSize: [9, 9]<br />
});<br />
<br />
// creates the marker<br />
L.marker([1002, 997], {<br />
icon: icon<br />
}).addTo(lmap);<br />
</source><br />
<br />
In this example, clicking on the marker does nothing. Subsequent examples illustrate adding some onClick behavior. <br />
<br />
'''Source code'''<br />
<br />
Replace the first line comment below with the [[Webmap_API#Common_HTML_header|common header code]].<br />
<br />
<source lang="html5"><br />
<!DOCTYPE html><br />
<html><head><br />
<meta charset="utf-8"><br />
<br />
<script src="/_scripts/sl.mapapi2.js" type="text/javascript"></script><br />
<link href="/_styles/sl.mapapi2.css" rel="stylesheet" type="text/css"><br />
<br />
<script><br />
function loadmap() {<br />
// creates the map<br />
var lmap = SLMap(document.getElementById('map-container'));<br />
lmap.setView([1002, 997], 7);<br />
<br />
// creates the icon<br />
var icon = L.icon({<br />
iconUrl: 'b_map_yellow.gif',<br />
iconSize: [9, 9]<br />
});<br />
<br />
// creates the marker<br />
L.marker([1002, 997], {<br />
icon: icon<br />
}).addTo(lmap);<br />
}<br />
</script><br />
<br />
</head><body onload="loadmap()"><br />
<br />
<p>There should be a yellow marker in the center of this map:</p><br />
<div id="map-container"></div><br />
<br />
</body></html><br />
</source><br />
<br />
==Map with an initial open popup ==<br />
<br />
[[Image:Map_with_window.png|thumb|right|Map with a popup]]<br />
<br />
A ''popup'' is a caption pointing to a specific point on the map, as shown in the screenshot at right. It looks something like a comic book dialog bubble.<br />
<br />
Use the [http://leafletjs.com/reference-1.0.2.html#popup Popup] object to create a popup.<br />
For example, to create a popup that opens initially when the page loads:<br />
<br />
# Instantiate a popup object with the desired caption.<br />
# Call its '''openOn()''' method with the [[Webmap_API_Reference#Map|Map]] object to add the popup to the map. <br />
<br clear="all"/><br />
For example:<br />
<br />
<source lang="javascript"><br />
// creates a window<br />
L.popup()<br />
.setLatLng([1002, 997])<br />
.setContent("<p>This is where the welcome area fountain is!!</p><img src='fountain.gif'>")<br />
.openOn(lmap);<br />
</source><br />
<br />
The code adds an open popup to the map positioned again at (997, 1002), right above the welcome area fountain. <br />
<br />
NOTE: after the user closes the popup there is no way to bring it back. A subsequent example will show how to enable the user to reopen the popup, by using markers with popups. <br />
<br clear="all"/><br />
<br />
To make the map initially centered on the fountain, change the call to '''setView()''' as follows:<br />
<br />
<source lang="javascript"><br />
lmap.setView([1002, 997], 6);<br />
</source><br />
<br />
'''Source code'''<br />
<br />
This example shows how to create a map with a popup. The code is the same as the first example, with the addition of some lines of Javascript after the initialization of the map, in the '''loadmap()''' function.<br />
<br />
<source lang="html5"><br />
<!DOCTYPE html><br />
<html><head><br />
<meta charset="utf-8"><br />
<br />
<script src="/_scripts/sl.mapapi2.js" type="text/javascript"></script><br />
<link href="/_styles/sl.mapapi2.css" rel="stylesheet" type="text/css"><br />
<br />
<link href="/_styles/MAIN.css" rel="stylesheet" type="text/css"><br />
<style><br />
div#map-container {<br />
width: 500px;<br />
height: 500px;<br />
}<br />
</style><br />
<script><br />
var lmap;<br />
function loadmap() {<br />
// creates the map<br />
lmap = SLMap(document.getElementById('map-container'));<br />
lmap.setView([1001.5, 997.5], 7);<br />
<br />
// creates a window<br />
L.popup()<br />
.setLatLng([1002, 997])<br />
.setContent("<p>This is where the welcome area fountain is!!</p><img src='fountain.gif'>")<br />
.openOn(lmap);<br />
}<br />
</script><br />
<br />
<title>Map API Examples - Map with an initial open window</title><br />
<br />
</head><body onload="loadmap()"><br />
<br />
<h1>Map with an initial open window </h1><br />
<p><a href="javascript:gotoSLURL(997, 1002, lmap)"> Go to (997, 1002)</a> where the welcome fountain is.</p><br />
<br />
<div id="map-container"></div><br />
<br />
</body></html><br />
</source><br />
<br />
==Map with a marker that opens a popup ==<br />
<br />
[[Image:Map with marker and window.png|thumb|right|Map with marker that opens a popup]]<br />
<br />
This example shows how to create a map with a marker which opens a popup when clicked. It is a combination of the previous two examples, because the code first creates a marker, then a popup.<br />
<br />
The difference comes in adding the marker to the map: first you bind the popup to the marker, using '''marker.bindPopup()''':<br />
<br />
<source lang="javascript"><br />
marker.bindPopup(popup);<br />
marker.addTo(lmap);<br />
</source><br />
<br />
Doing this adds the marker and causes it to open the specified popup when the user clicks it. If the user subsequently closes the popup, they can open it again by clicking on the marker.<br />
<br />
<br clear="all" /><br />
'''Source code'''<br />
<br />
<source lang="html5"><br />
<!DOCTYPE html><br />
<html><head><br />
<meta charset="utf-8"><br />
<br />
<script src="/_scripts/sl.mapapi2.js" type="text/javascript"></script><br />
<link href="/_styles/sl.mapapi2.css" rel="stylesheet" type="text/css"><br />
<br />
<link href="/_styles/MAIN.css" rel="stylesheet" type="text/css"><br />
<style><br />
div#map-container {<br />
width: 500px;<br />
height: 500px;<br />
}<br />
</style><br />
<script><br />
function loadmap() {<br />
// creates the map<br />
var lmap = SLMap(document.getElementById('map-container'));<br />
lmap.setView([1001.5, 997.5], 7);<br />
<br />
// creates the icon<br />
var icon = L.icon({<br />
iconUrl: 'b_map_yellow.gif',<br />
iconSize: [9, 9]<br />
});<br />
<br />
// creates the marker<br />
var marker = L.marker([1002, 997], {<br />
icon: icon<br />
});<br />
<br />
// creates a window<br />
var popup = L.popup().setContent("<p>This is where the welcome area fountain is!!</p><img src='fountain.gif'>");<br />
<br />
// adds the marker to the map<br />
marker.bindPopup(popup);<br />
marker.addTo(lmap);<br />
}<br />
</script><br />
<br />
<title>Map API Examples - Map with a marker that opens a popup</title><br />
<br />
</head><body onload="loadmap()" onlunload="GUnload()"><br />
<br />
<h1>Map with a marker that opens a popup</h1><br />
<br />
<div id="map-container"></div><br />
<p>Click on the marker to open a popup.</p><br />
<br />
</body></html><br />
</source></div>Natty Lindenhttps://wiki.secondlife.com/w/index.php?title=API_Portal/navigation&diff=1205052API Portal/navigation2016-12-14T01:24:55Z<p>Natty Linden: Updated for version 2 of Map API</p>
<hr />
<div><div id="box" style="float:{{{float|right}}}; width:20em; margin-left: 0.5em"><br />
<div style="text-align: center; font-size: 11pt; background-color:#ddd; padding: 5px 5px 10px 5px; border-bottom: 1px solid #aaaaaa;">'''[[APIs and Web Services Portal|Second Life APIs]]'''</div><br />
<div style=" padding-left:1em; padding-bottom:1em;"><br />
* {{LLOlink|Registration API}}<br />
{{#ifeq:{{{1}}}|reg| <br />
** {{LLOlink|Registration API Request Form|Reg API Request Form}}<br />
** {{LLOlink|Reg API Examples|Examples}}<br />
** {{LLOlink|Registration API Reference|API Reference}}<br />
** {{LLOlink|Registration API Error Codes|Error Codes}}<br />
** [[Registration API Third Party Support|Third Party Support]]<br />
** {{LLOlink|Custom Name Program}}<br />
| }}<br />
* {{LLOlink|Map API}}<br />
{{#ifeq:{{{1}}}|map|<br />
** {{LLOlink|Map API Introduction|Introduction}}<br />
** {{LLOlink|Map API SLurl Technical Details|SLurl Technical Details}}<br />
** {{LLOlink|Map API Basic Examples|Basic Examples}}<br />
** {{LLOlink|Map API Reference|API Reference}}<br />
| }}<br />
* {{LLOlink|Inventory API}}<br />
* {{LLOlink|Live Data Feeds}}<br />
* {{LLOlink|Media Rendering Plugin System|Media Plugin API}}<br />
{{#ifeq:{{{1}}}|media|<br />
** {{LLOlink|Media Rendering Plugin System FAQ|Frequently Asked Questions}}<br />
** {{LLOlink|Media Rendering Plugin System Technical Overview|Technical Overview}}<br />
** {{LLOlink|Plugin Test App: LLMediaPluginTest|Using the Plugin Test App}}<br />
** {{LLOlink|Getting Started Developing Media Rendering Plugins|Getting Started}}<br />
*** {{LLOlink|Hello World plugin: basic plugin code|Hello World Plugin Code}}<br />
** {{LLOlink|Media Rendering Plugin Operation and Data Flow|Plugin Operation and Data Flow}}<br />
** {{LLOlink|Media Plugin System Messages | Messages}}<br />
*** {{LLOlink|Media Plugin System Internal Messages|Internal Messages}}<br />
| }}<br />
* {{LLOlink|Viewer_Managed_Marketplace/Marketplace_API_for_Viewers|Marketplace API for Viewers}}<br />
</div><br />
<div style="text-align: center; font-size: 11pt; background-color:#ddd; padding: 5px 5px 7px 5px; border-bottom: 1px solid #aaaaaa; border-top: 1px solid #aaaaaa;">'''Unofficial APIs'''</div><br />
<div style=" padding-left:1em; padding-bottom:1em;"><br />
* [[Search API]]<br />
* [[Snapshot API]]<br />
* [[World API]]<br />
</div><br />
<includeonly>[[Category:APIs]]<br />
[[Category:Developer Resources]]</includeonly><br />
</div></div>Natty Lindenhttps://wiki.secondlife.com/w/index.php?title=Linden_Lab_Official:Map_API_Reference&diff=1205051Linden Lab Official:Map API Reference2016-12-14T01:23:32Z<p>Natty Linden: Updated for version 2 of API</p>
<hr />
<div>{{Supported API}}<br />
{{:API Portal/navigation|map}}<br />
'''NOTE:''' See the [[Linden Lab Official:Map API Introduction#Release notes|Release Notes]] for information on new features and known issues.<br />
__TOC__<br />
<br clear="all"/><br />
== Global functions ==<br />
<br />
{| border="1" cellspacing="0" cellpadding="3" rules="all" class="apitable"<br />
|- bgcolor="#A7C1F2"<br />
!valign="top"| Function<br />
!valign="top"| Return Value<br />
!valign="top"| Description<br />
<br />
|-<br />
|valign="top" class="signature"|<br />
SLMap(<br />
[https://developer.mozilla.org/en/DOM/element HTMLElement] element)<br />
|[[#Map|Map]]<br />
|Creates a new map inside the specified DOM element.<br />
|-<br />
|valign="top" class="signature"|<br />
gotoSLURL(<br />
x Number,<br />
y Number,<br />
[[#Map|Map]] lmap)<br />
|None<br />
|Pans the map to display the specified inworld location.<br />
|}<br />
<br />
== Objects ==<br />
<br />
=== Map ===<br />
This class is the Leaflet class that represents the map. For more information, see [http://leafletjs.com/reference-1.0.2.html#map the Leaflet Map reference].<br />
<br />
{{API Constructor| <br />
[http://leafletjs.com/reference-1.0.2.html#map Map] SLMap([https://developer.mozilla.org/en/DOM/element HTMLElement] container)<br />
}}<br />
<br />
Creates a Leaflet map configured to display a map of Second Life.<br />
<br />
The map is generated with a click event handler that calls '''gotoSLURL()''' to display a popup with a “Visit this location” link.<br />
<br />
==== Useful Methods ====<br />
<br />
For other methods, see [http://leafletjs.com/reference-1.0.2.html#map-method the Leaflet Map methods reference].<br />
<br />
{| border="1" cellspacing="0" cellpadding="3" rules="all" class="apitable"<br />
|- bgcolor="#A7C1F2"<br />
!valign="top"| Method<br />
!valign="top"| Return Value<br />
!valign="top"| Description<br />
<br />
|-<br />
|valign="top" class="signature"|<br />
[http://leafletjs.com/reference-1.0.2.html#map-setview setView]( <br />
[http://leafletjs.com/reference-1.0.2.html#latlng LatLng] center,<br />
Number zoom)<br />
|valign="top"| [http://leafletjs.com/reference-1.0.2.html#map Map]<br />
|valign="top"| Centers and zooms the map to the specified location. Argument zoom must be an integer value. Note that [http://leafletjs.com/examples/crs-simple/crs-simple.html#this-is-not-the-latlng-youre-looking-for a LatLng is a (Y, X) coordinate], so coordinates passed as ''center'' should be in (Y, X) order.<br />
|}<br />
<br />
== Utility URLs ==<br />
These map helper functions are available from the caps server. The JSON-like results return text that Javascript could eval(), but scripts coming from outside should parse them the "hard" way for safety.<br />
<br />
=== Region name from global coordinates ===<br />
https<nowiki></nowiki>://cap.secondlife.com/cap/0/'''b713fe80-283b-4585-af4d-a3b7d9a32492'''?'''var'''=''varName''&'''grid_x'''=''xcoord''&'''grid_y'''=''ycoord''<br />
* '''var''' is arbitrary, for output formatting.<br />
* '''grid_x''' is the region's global x coordinate as an integer.<br />
* '''grid_y''' is the region's global y coordinate as an integer.<br />
<br />
'''Example'''<br />
https://cap.secondlife.com/cap/0/b713fe80-283b-4585-af4d-a3b7d9a32492?var=region&grid_x=1000&grid_y=1000<br />
:Returns: <pre>var region='Da Boom';</pre><br />
<br />
=== Global coordinates from region name ===<br />
https<nowiki></nowiki>://cap.secondlife.com/cap/0/'''d661249b-2b5a-4436-966a-3d3b8d7a574f'''?'''var'''=''varName''&'''sim_name'''=''RegionName''<br />
* '''var''' is arbitrary, for output formatting.<br />
* '''sim_name''' is a region on the Second Life map.<br />
<br />
'''Example'''<br />
https://cap.secondlife.com/cap/0/d661249b-2b5a-4436-966a-3d3b8d7a574f?var=coords&sim_name=Da%20Boom<br />
:Returns: <pre>var coords = {'x' : 1000, 'y' : 1000 };</pre></div>Natty Lindenhttps://wiki.secondlife.com/w/index.php?title=Linden_Lab_Official:Map_API_Introduction&diff=1205048Linden Lab Official:Map API Introduction2016-12-13T19:14:45Z<p>Natty Linden: Updated for version 2 of the API</p>
<hr />
<div>{{Supported API}}<br />
{{:API Portal/navigation|map}}<br />
__TOC__<br />
<br />
== Getting Started ==<br />
<br />
The Second Life Map API is a JavaScript library to embed Second Life maps onto your web pages. It's a purely client-side JavaScript API. If needed, you can also [[#Accessing_map_images_directly|directly access]] the map images.<br />
<br />
'''NOTE:'''<br />
This API is still in beta. URLs and API signatures may change. See the [[#Release notes|Release Notes]] for information on new features and known issues.<br />
<br />
=== Map UI features ===<br />
<br />
The Map API enables you to create web pages that have web map functionality, using maps of Second Life, rather than Earth. In general, the default map interface looks as shown here.<br />
<br />
[[Image:Map ui cropped.png|right]]<br />
<br />
The main map display area shows the current map area. By default, the map provides the following features:<br />
<br />
* Click and drag the mouse pointer to pan the map around.<br />
* Double-click to zoom in by one level (power of two).<br />
* Click anywhere on the map to open a "teleport" popup (see '''Teleport popup''' below).<br />
* Use the mouse wheel to zoom in and out. See [[#Zoom_levels|Zoom levels]] for more information about zoom levels.<br />
* Use the zoom controls at upper left to zoom the view in and out.<br />
<br />
<br clear="all" /><br />
<br />
'''Teleport popup'''<br />
[[Image:Teleport window.png|left]]<br />
Clicking anywhere on the map opens a popup (left) where you click. Click "Visit this location" to start the Second Life Viewer & teleport you where you clicked. This feature is implemented with the [[Map_API_Reference#Global_functions|gotoSLURL()]] global function.<br />
<br />
<br clear="all" /><br />
<br />
=== Working examples ===<br />
<br />
See [[Map API Basic Examples]] for a detailed explanation of the working examples at http://maps.secondlife.com/examples/.<br />
<br />
<span id="Using slurl.com and DirectSLurl"></span><br />
=== Using web links ===<br />
<br />
The [http://maps.secondlife.com/ web site] uses the Map API to provide a mechanism to teleport directly to locations in Second Life from web pages.<br />
<br />
For more information, see [[Using Second Life URLs (SLurls)]].<br />
<br />
=== Include the API files ===<br />
<br />
To use the Map API, put the following lines at the beginning of each HTML page. This includes the JavaScript & CSS so that you can create the map & display it correctly.<br />
<br />
<pre><br />
<!DOCTYPE html><br />
<html><head><br />
<meta charset="utf-8"><br />
<br />
<script src="http://maps.secondlife.com/_scripts/sl.mapapi2.js"></script><br />
<link rel="stylesheet" type="text/css" href="http://maps.secondlife.com/_styles/sl.mapapi2.css"><br />
<br />
...<br />
</pre><br />
<br />
All the examples also require additional script statements in the HTML header.<br />
<br />
'''Stylesheet'''<br />
<br />
You can also include your own stylesheet to style the other elements of your page. For example:<br />
<br />
<pre><br />
<link rel="stylesheet" type="text/css" href="http://maps.secondlife.com/_styles/sl.mapapi2.css"><br />
<link rel="stylesheet" type="text/css" href="myStyleSheet.css"><br />
</pre><br />
<br />
=== Browser compatibility ===<br />
The Map API has been tested to work with the following browsers:<br />
* Internet Explorer 6.0<br />
* Internet Explorer 7.0<br />
* Firefox 2.0 or greater <br />
* Safari 2.0<br />
<br />
Other browsers may work but little issues may appear. Unless otherwise noted, all features work in all supported browsers.<br />
<br />
=== Comments and feedback ===<br />
<br />
If you encounter problems or bugs or have feature requests, please email '''webmap@secondlife.com'''.<br />
<br />
Happy coding from the Second Life Web team!<br />
<br />
== Basic concepts ==<br />
<br />
The Map API is built around [http://leafletjs.com the Leaflet web map library]. Leaflet provides many options, methods & events for customizing the presentation of your Second Life map.<br />
<br />
===About coordinates===<br />
<br />
Map coordinates are based on the Second Life grid system for positioning regions.<br />
<br />
[[Image:Webmap coords.jpg]]<br />
<br />
As illustrated in the diagram above, the Map API maps the Second Life world to the upper right quadrant of an earth map. A large scaling value equal to 2<sup>20</sup> or 1,048,576 defines the largest region coordinate at the top and far right edge of the displayed area. This value creates a map area with room for one trillion sims.<br />
<br />
The little blue square illustrates where the populated sims are in Second Life.<br />
<br />
=== Zoom levels ===<br />
There are eight zoom levels: one through eight. Zoom level 8 is most zoomed in, and zoom level 1 is most zoomed out. Each level zooms by a power of two. In other words, at zoom level six, each region is twice the width it was at zoom level five.<br />
<br />
== Accessing map images directly ==<br />
<br />
The Second Life map images are stored on [http://aws.amazon.com/s3/ Amazon Simple Storage Service (Amazon S3)]. You can access the map images directly on the Amazon S3 servers using the following URL format:<br />
<br />
<nowiki>http://map.secondlife.com/map-{z}-{x}-{y}-objects.jpg</nowiki><br />
<br />
Where:<br />
* <tt>z</tt> - zoom level from 1 (most zoomed-in, where each tile is one region) to 8 (most zoomed out). Note this is ''backward'' from how are addressed when making Leaflet API calls in your JavaScript.<br />
* <tt>x</tt> & <tt>y</tt> - the X and Y coordinates on the Second Life Grid of the region. For example, region Ahern is (997, 1002).<br />
<br />
Even zoomed-in tiles are addressed by their region coordinates. That is, tiles with zoom greater than 1 are stored by the <tt>x</tt> & <tt>y</tt> of the regions at their lower left corners. You can find the zoom tile containing region (X,Y) for zoom level Z with the filename map-Z-X'-Y'-objects.jpg where:<br />
<br />
X' = X - (X % (2<sup>Z - 1</sup>) )<br />
<br />
and <br />
<br />
Y' = Y - (Y % (2<sup>Z - 1</sup>) )<br />
<br />
Note that 2<sup>Z - 1</sup> is the number of regions encompassed by the width of a tile at resolution Z. <br />
<br />
=== Examples ===<br />
<br />
For example, at Z = 1, four region tiles are:<br />
<br />
* http://map.secondlife.com/map-1-1000-1000-objects.jpg<br />
* http://map.secondlife.com/map-1-1001-1000-objects.jpg<br />
* http://map.secondlife.com/map-1-1000-1001-objects.jpg<br />
* http://map.secondlife.com/map-1-1001-1001-objects.jpg<br />
<br />
These four region tiles are compressed into the single tile:<br />
<br />
http://map.secondlife.com/map-2-1000-1000-objects.jpg<br />
<br />
The following shows the map zoomed all the way out, showing the once-entire Second Life world view:<br />
http://map.secondlife.com/map-8-1024-896-objects.jpg<br />
<br />
The following displays a single tile contained in the view above, zoomed all the way in:<br />
http://map.secondlife.com/map-1-1027-1019-objects.jpg<br />
<br />
=== Implementation notes ===<br />
<br />
The map generation process is more scalable than the previous release, and is able to image the entire Second Life grid of about 30,000 regions in less than two days. <br />
<br />
There is a "decay period" for offline regions. If a region is disabled, the tile will stay up for several days (in case it comes back or something else has gone wrong) before the tile disappears. That several-day period will be longer than the time it takes to update existing regions or put new regions on the map.<br />
<br />
=== Getting a map URL from LSL ===<br />
<br />
The following LSL script illustrates how to display a map of the current region to an avatar:<br />
<br />
<source lang="lsl2"><br />
ShowMap(key avatar) {<br />
string url = "http://map.secondlife.com/";<br />
vector sim_coord = llGetRegionCorner();<br />
string x = (string)((integer)(sim_coord.x / 256.0));<br />
string y = (string)((integer)(sim_coord.y / 256.0));<br />
url += "map-1-" + x + "-" + y + "-objects.jpg";<br />
llLoadURL(avatar, "View the sim map", url);<br />
}<br />
</source><br />
<br />
== Release notes ==<br />
<br />
This section describes new features and known issues with the current release of the Map API.<br />
<br />
Version 2 of the Map API was released on Dec 12, 2016.<br />
<br />
* Converted the Map API to Leaflet.<br />
<br />
Version 1 of the Map API was released on Jan 15, 2009.<br />
<br />
* See also [http://blog.secondlife.com/2009/01/22/improvements-to-mapping-and-upgrade-to-slurlcom/ Philip Linden's blog post announcement].</div>Natty Lindenhttps://wiki.secondlife.com/w/index.php?title=Coding_standard&diff=1197212Coding standard2015-08-12T16:36:23Z<p>Natty Linden: more <source> fixes</p>
<hr />
<div>{{OSWikiLearnBox}}<br />
<br />
This document defines and formalizes the style of code produced at Linden Lab. Adherence to these conventions is critical to ensuring readable and maintainable code.<br />
<br />
Some tools are available for checking some parts of these rules in the [[Mercurial Tools]] repository.<br />
<br />
__TOC__<br />
<br />
== Common Guidelines ==<br />
=== Line Endings ===<br />
<br />
<br />
All text files '''must''' use unix (linefeed only) line endings when submitted.<br />
<br />
Exceptions are allowed ''only'' if the file in question:<br />
* Is used or relevant ''only'' on Windows<br />
* Requires DOS style (carriage-return linefeed) line endings for correctness.<br />
<br />
Files that have DOS line endings must either:<br />
* have a suffix that is one of:<br />
** .bat<br />
** .sln<br />
** .vcxproj<br />
* '''or''' be located somewhere under a directory named <tt>windows</tt> or <tt>vstool</tt><br />
<br />
See [[How to avoid DOS line endings in Windows tools]]<br />
<br />
=== Comments ===<br />
When commenting, address the "why" and not the "what" unless the what is difficult to see. If what the code does requires commenting, also include why it has to be done that way so maintainers can follow your reasoning. Use complete sentences and correct spelling.<br />
<br />
Prefer the use of [http://www.doxygen.org/ doxygen] compatible comment markup. (See [[Using Doxygen]].)<br />
<br />
Sometimes you know something is broken or a bad idea but need to do it anyway &mdash; please follow the special comment token guidelines to not confuse the next engineer. Append your name or initials and the date of the comment when using these special comment tokens.<br />
<br />
{| border="1"<br />
|+ <b>Special Comment Tokens</b><br />
! Token !! Meaning !! Deprecated tokens<br />
|-<br />
| *FIX<br />
| The associated code is actually broken and has known failure cases. All instances of these should be fixed before shipping. Do not use this tag for hacks or suggested development. Do not simply tag the code with the token alone or simply provide instructions since then it is unclear if that is how it is broken. For example: "// *FIX: invalidate stored textures when number of faces change" &mdash; is really unclear if it is an instruction or a buggy side effect of the code segment.<br />
| FIXME BROKEN BUG <br />
|-<br />
| *HACK<br />
| The associated code relies on external information or processes outside the normal control flow for the function to work.<br />
| HACK<br />
|-<br />
| *TODO<br />
| The associated code should be optimized, clarified, or made more reliable. Provide instructions along with why it is probably a good idea to execute on the comment.<br />
| TODO <br />
|-<br />
| *NOTE<br />
| This denotes that there is something tricky or special about the associated code above and beyond answering the typical 'what' and 'why.'<br />
| NOTE "NOTA BENE" NB<br />
|}<br />
<br />
Examples:<br />
{| border="0"<br />
|-<br />
||<br />
<pre><br />
// *FIX: This will fail if the packets arrive out of order.<br />
<br />
// *HACK: Filter out some extra information from this <br />
// packet and put it into the status bar since it is more <br />
// up to date than the last balance update.<br />
<br />
// *TODO: This call does not always need to be made<br />
// and is computationally expensive. Write a test to see<br />
// if it must be made during this iteration.<br />
<br />
// *NOTE: The tile is actually 257 pixels wide, so<br />
// we have to fudge the texture coordinates a bit for this<br />
// to look right.<br />
</pre><br />
|}<br />
<br />
=== No New Serialization Formats ===<br />
After much effort and unit-testing, LLSD is now the sanctioned format for all new serialization tasks. Do not implement another serialization scheme unless you have a signed and notarized letter from Elvis explicitly stating that a new serialization format is a good idea. If speed is essential, LLSD has a fairly speedy binary parser and formatter available in C++ and python.<br />
<br />
=== Unicode ===<br />
Use UTF-8 for all string serialization and communication between processes. Do not use UTF-16 except when interfacing with Win32.<br />
<br />
=== File Names ===<br />
File names should be significant and of reasonable length, with no spaces or uppercase letters. Separate words in the name MUST be separated with underscore (<code>like_this.cpp</code>), because languages like Python cannot include module names with hyphens (<code>will-not-import.py</code>).<br />
<br />
=== Dead Code ===<br />
Do not leave commented-out code in any commits that you make. Delete that code instead.<br />
<br />
Block-commented out code, including code disabled through #if <something-false> is especially problematic because it is not distinguishable from live code in a grep. Our version control systems are good enough that any code that was checked in is retrievable even after deletion.<br />
<br />
== C++ ==<br />
<br />
=== Source Files ===<br />
All C++ source code files should begin with the header shown below:<br />
<syntaxhighlight lang="cpp"><br />
/** <br />
* @file file base name<br />
* @author optional<br />
* @brief brief description of the file<br />
*<br />
* $LicenseInfo:firstyear=2011&license=viewerlgpl$<br />
* Second Life Viewer Source Code<br />
* Copyright (C) 2011, Linden Research, Inc.<br />
* <br />
* This library is free software; you can redistribute it and/or<br />
* modify it under the terms of the GNU Lesser General Public<br />
* License as published by the Free Software Foundation;<br />
* version 2.1 of the License only.<br />
* <br />
* This library is distributed in the hope that it will be useful,<br />
* but WITHOUT ANY WARRANTY; without even the implied warranty of<br />
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU<br />
* Lesser General Public License for more details.<br />
* <br />
* You should have received a copy of the GNU Lesser General Public<br />
* License along with this library; if not, write to the Free Software<br />
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA<br />
* <br />
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA<br />
* $/LicenseInfo$<br />
*/<br />
</syntaxhighlight><br />
The <code>$LicenseInfo$</code> section is critically important so that we can process the source files based on license agreements. If you're unsure, use <code>viewerlgpl</code>. The licenses we support are:<br />
* <code>internal</code> - something probably not meant for distribution unless we have an agreement with the third party.<br />
* <code>viewergpl</code> - '''deprecated''' this was used for pre-[[Project Snowstorm|Snowstorm]] viewer distribution<br />
* <code>viewerlgpl</code> - the viewer and linden libraries distributed with it<br />
* <code>mit</code> - a basic MIT license for some libraries such as mulib, eventlet, and indra.ipc.<br />
Replace the <code>2011</code> with the year the file is created.<br />
<br />
All C++ source files should end in a newline. If not, GCC will fail with an error, as it enforces the C++ standard which requires one.<br />
<br />
Headers should be included in the following order:<br />
<syntaxhighlight lang="cpp"><br />
// All files MUST include one of the following first: <br />
// (includes linden_preprocessor.h, stdtypes.h, and some common headers)<br />
#include "llviewerprecompiledheaders.h" // (NEWVIEW)<br />
// OR<br />
#include "llsimprecompiledheaders.h" // NEWSIM<br />
// OR<br />
#include "linden_common.h" // the ll libraries<br />
<br />
#include "llfoo.h" // Associated header, should have no hidden dependencies<br />
<br />
#include <map> // stl headers<br />
#include <cmath> // std headers<br />
#include "vorbis/ogg.h" // External library headers<br />
<br />
#include "llbar.h" // Other linden headers<br />
</syntaxhighlight><br />
<br />
==== File Names ====<br />
All C++ source files should be prefixed with 'll'.<br />
<br />
File names must end with the appropriate suffix:<br />
* '''cpp''' for ANSI C++ code<br />
* '''h''' for included declarations<br />
* '''inl''' for large chunks of inlined C++ code. Avoid this.<br />
* '''asm''' for assembly files. <em>Do not do this without a note from some deity forgiving your transgressions.</em><br />
<br />
==== Include Files ====<br />
Include files should be specified using relative paths using the '<code>/</code>' character to help ensure portability. <br />
In the code relative paths should not be used. Instead, paths should be indicated in the relevant CMake file.<br />
<br />
Compiler directives used to prevent multiple header file inclusion should be based on the file name with a <code>LL_</code> prefix.<br />
Include dependent headers in the header file, but use forward declarations where possible.<br />
<br />
For example:<br />
<syntaxhighlight lang="cpp"><br />
#ifndef LL_LLFOO_H<br />
#define LL_LLFOO_H<br />
<br />
#include "llbar.h"<br />
<br />
class LLReferencedData; // forward declaration (no need to include "llrefrenceddata.h")<br />
<br />
class LLFoo : public LLBar<br />
{<br />
public:<br />
LLFoo();<br />
void setData(LLReferencedData& refdata);<br />
private:<br />
LLReferencedData* mRefData;<br />
};<br />
<br />
#endif //LL_LLFOO_H<br />
</syntaxhighlight><br />
<br />
=== Basic Facilities ===<br />
<br />
==== Linden Variable Types ====<br />
The following basic types are defined in "stdtypes.h" and should be used to ensure clarity and cross platform functionality:<br />
* Integral Types<br />
** The first letter specifies 'S' for signed integers and 'U' for unsigned integers<br />
** The type ends with the number of bits used to represent the integer<br />
** Examples: S8 (signed 8-bit integer), U32 (unsigned 32 bit integer)<br />
** The integer types are: '''U8''', '''S8''', '''U16''', '''S16''', '''U32''', '''S32''', '''U64''', '''S64'''<br />
** The '''char''' type may used when required by defualt C functions that need a char<br />
* Floating Point Types<br />
** '''F32''' denotes a 32-bit floating point value ("float")<br />
** '''F64''' denotes a 64-bit floating point value ("double")<br />
* Other Types<br />
** Prefer the use of the standard C++ '''bool'''. This is especially for comparators like operator< (using bool instead of BOOL will prevent lots of warnings when using STL). '''BOOL''' denotes a 32-bit integer value that is used to denote TRUE or FALSE. It is specifically not '''bool''' to prevent mistakes caused by "sizeof(bool)" being implementation specific.<br />
<br />
==== Project Defines ====<br />
* Use <code>LL_WINDOWS</code>, <code>LL_LINUX</code>, and <code>LL_DARWIN</code> for platform specific code. <br />
* Use <code>LL_RELEASE</code> and <code>LL_DEBUG</code> for release and debug specific code. <br />
* <code>LL_RELEASE_FOR_DOWNLOAD</code> and <code>LL_RELEASE</code> are both defined in versions destined to be in a resident's hands.<br />
* Prefer testing for the positive when using <code>#if</code>, and use <code>#if</code>/<code>#if !</code> instead of <code>#ifdef</code>/<code>#ifndef</code>. For example:<br />
<pre style="color:green"><br />
#if LL_DEBUG && !LL_DARWIN<br />
</pre><br />
instead of:<br />
<pre style="color: red"><br />
#ifndef LL_RELEASE && !defined(LL_DARWIN)<br />
</pre><br />
However, this construct is still useful:<br />
<pre><br />
#ifndef SOMETHING<br />
#define SOMETHING SOMETHING_NEW<br />
#endif<br />
</pre><br />
<br />
=== Usage ===<br />
==== String Handling ====<br />
See [[C++ Strings]] for more guidelines on strings in the C++ codebase.<br />
<br />
Use <code>std::string</code> as a string container and [https://bitbucket.org/lindenlab/viewer-release/src/tip/indra/llcommon/stringize.h the STRINGIZE() macro] for constructing strings from multiple sources.<br />
<br />
If you must use <code>printf</code> style formatting, use <code>snprintf()</code> or <code>llformat()</code>. Never use user supplied data (including configuration files) to specify formatting instructions to those functions.<br />
<br />
Avoid using c-string libraries. If you are writing something where speed is critical, prefer the use of <code>std::vector<char></code> since it's usage is less error prone, you can explicitly allocate or occupy memory, it is easy to null terminate with a single call to <code>container.push_back('\0')</code>, and it is <code>char*</code> compatible with a call to <code>&container[0]</code>.<br />
<br />
The following c functions are forbidden:<br />
* <code>sprintf()</code> and <code>vsprintf()</code> &mdash; most of the <code>printf</code> family of functions are like individual invitations to hackers to overflow a buffer. Don't use them.<br />
* <code>strcpy()</code> and <code>strncpy()</code> &mdash; both of these functions are dangerous and difficult to use correctly. For example, did you know that <code>strncpy()</code> does not null terminate if no null is found in the source?<br />
* <code>strcat()</code> and <code>strncat()</code> &mdash; these methods are inefficient in comparison to using <code>ostringstream()</code> and are easy to misuse. The <code>strncat()</code> function is almost forgivable, but please just avoid these.<br />
* <code>gets()</code> &mdash; Never, ever use this function.<br />
<br />
Our legacy being what it is, there are some unavoidable cases where we are forced to use c-string functions. Here are some tips:<br />
* <code>sscanf()</code>, <code>fscanf()</code>, etc... &mdash; For these methods, always supply a maximum width for strings. If you do not know how to do this off the top of your head, do not use these functions. Even if you think you know, read the man page again.<br />
* <code>snprintf()</code>, <code>fprintf()</code>, <code>vsnprintf()</code>, etc.. &mdash; As noted above, never let anything other than the program determine the formatting. Never trust configuration files, assets, message system data, etc.<br />
<br />
==== Unicode ====<br />
Useful functions for dealing with UTF-8 can be found in llstring.h. Remember that truncating a UTF-8 string must be done on glyph rather than byte boundaries or your string may no longer be valid UTF-8. Many methods in <code>std::string</code> and <code>LLString</code> are not UTF-8 safe since they work on <code>sizeof(char_t)</code> byte boundaries. Similarly, the functions in that header file which do not contain 'ut8f' in the prototype are probably not UTF-8 safe.<br />
<br />
Do not use UTF-16 except with interfacing with Win32.<br />
<br />
Avoid the use of wide strings except when iteration speed is essential, or you are interfacing with the host operating system.<br />
<br />
==== Trigraphs ====<br />
<br />
A trigraph is a set of three characters that represents one character in the C character set. The set of trigraph sequences was defined in the ANSI Standard to allow users to use the full range of C characters, even if their keyboards do not implement the full C character set. <br />
<br />
Each trigraph sequence is introduced by two question marks. The third character in the sequence indicates which character is being represented. The standard trigraphs and the characters they produce are:<br />
<br />
??( [<br />
??/ \<br />
??) ]<br />
??' ^<br />
??< {<br />
??! |<br />
??> }<br />
??- ~<br />
<br />
'''Using trigraphs anywhere in Second Life code is not allowed.'''<br />
<br />
==== Containers ====<br />
Prefer the use of the containers available in the C++ standard template library. They are well tested and in common usage across the industry &mdash; thus, nobody has to learn the special (and lame) quirks of containers like <code>LLMap<></code> and <code>LLLinkedList<></code>.<br />
<br />
This is one of the few places where specialized implementations can yield significant memory or speed improvements. Do not create those containers until you have profiling results that prove the container is the bottleneck.<br />
<br />
Never use <code>std::vector<bool></code>.<br />
<br />
==== Memory Management ====<br />
Object allocation and deallocation should always be done using new and delete (or delete[] where appropriate). <br />
<br />
Wherever possible, going forward, object lifetime should be managed using smart pointers, favoring the shared_pt, weak_ptr and unique_ptr types. When defining a pointer type declare a typedef to make it easily referenced elsewhere in the code, this typedef should be of the form:<br />
<br />
<syntaxhighlight lang="cpp"><br />
typedef boost::shared_ptr<Class> ClassPtr; <br />
typedef boost::intrusive_ptr<Class> ClassPtr;<br />
</syntaxhighlight><br />
and<br />
<syntaxhighlight lang="cpp"><br />
typedef boost::weak_ptr<Class> ClassWPtr;<br />
</syntaxhighlight><br />
<br />
Alternately, a class may define its own preferred pointer type as part of its definition.<br />
<br />
<syntaxhighlight lang="cpp"><br />
class MyClass <br />
{<br />
typedef boost::shared_ptr<MyClass> ptr_t;<br />
typedef boost::weak_ptr<MyClass> wptr_t;<br />
};<br />
<br />
/.../<br />
<br />
MyClass::ptr_t aPointerVariableToMyClass;<br />
<br />
</syntaxhighlight><br />
<br />
New uses of the internal LLPointer<T> class should be avoided.<br />
<br />
For legacy deallocation , not yet handled by managed pointers, remember that C++ defines delete NULL as being safe, therefore it is unnecessary to test for NULL before deleting.<br />
<syntaxhighlight lang="cpp"><br />
delete var;<br />
var = null;<br />
</syntaxhighlight><br />
<br />
==== Exception Handling ====<br />
Exceptions have been recently enabled for all platforms and targets in the main indra code base. If you are familiar with their use, you can use them judiciously in the code. Be sure to make sure the places where you are throwing exceptions will not cause resource leaks, and be sure to comment what linden exceptions might be thrown with the function declaration.<br />
<br />
* do not use exception specifications<br />
* do not throw in a destructor (ever)<br />
* read up on exceptions before using them<br />
<br />
===== Resources =====<br />
* [http://www.parashift.com/c++-faq-lite/exceptions.html C++ FAQ]<br />
* [http://www.amazon.com/Exceptional-C%2B%2B-Engineering-Programming-Depth/dp/0201615622/ref=sr_1_2/105-9891007-0374847?ie=UTF8&s=books&qid=1191554809&sr=8-2 exceptional C++]<br />
* [http://www.amazon.com/More-Exceptional-C%2B%2B-Engineering-Depth/dp/020170434X/ref=sr_1_1/105-9891007-0374847?ie=UTF8&s=books&qid=1191554809&sr=8-1 more exceptional C++]<br />
<br />
==== Error Messages ====<br />
* See [[Logging_System_Overview]] for detailed info on using the runtime logging system. (Test plan for logging system found at: [[Configurable Error Logging Test]])<br />
* Do not wrap your messages in <code>#ifdef _DEBUG</code>, unless it is inside a performance critical loop. The logging constructs compile to very small amounts of code, and are very efficiently skipped at run time. And the ability to turn on your debug messages at run-time, even in a deployed release build, will make you very happy one day.<br />
<br />
==== Obvious Things We All Know ====<br />
* No magic numbers<br />
* Use <code>TRUE</code> and <code>FALSE</code> when working with <code>BOOL</code> (but prefer <code>bool</code> in general)<br />
* Use <code>NULL</code> for pointer compares to <code>0</code><br />
* Use <code>'\0'</code> for character compares to <code>0</code><br />
* Use <code>const</code> and <code>inline</code> rather than <code>#define</code> whenever possible. Prefer optimizing '''after''' you have proven that the code you want to inline actually has a significant benefit from in-lineing.<br />
* Remember that C++ does not have garbage collection<br />
* Comment non-intuitive member variables<br />
* Fix all compiler warnings.<br />
* Take advantage of the fact that gcc generates different, sometimes better, warnings than Visual Studio.<br />
* Don't use assignments in <code>while</code> loops, e.g. <code>while (foo = nextData())</code> as this causes gcc to emit warnings. Use <code>while ((foo = nextData()) != NULL)</code> or restructure the code.<br />
<br />
=== Style ===<br />
==== Naming Convention ====<br />
The following name conventions apply to non-iterators:<br />
* Names should include one or more English words that are relevant to the entity being named. Try to use non-programming words, like <code>EmployeeList</code> rather than <code>LinkedListOfStrings</code><br />
* C functions are in lowercase, e.g. <code>main()</code>, <code>update_world_time()</code><br />
* Local variables and parameters are in lowercase, e.g. <code>frame_count</code><br />
** Local variables are '''not''' camel-case. <code>thisIsNotALocalVariable</code><br />
* Class methods start with a lowercase character but are otherwise CamelCase, e.g. <code>destroyWorld()</code><br />
* Non-static class member variables start with an '<code>m</code>' followed by an uppercase character, e.g. <code>mCameraPosition</code><br />
* Static class member variables start with an '<code>s</code>' followed by an uppercase character, e.g. <code>sInstance</code><br />
* Structures and Classes start with the uppercase '<code>LL</code>', followed by the name with an uppercase first letter, e.g. <code>LLMyClass</code><br />
* Enums start with the uppercase '<code>E</code>', followed by the name with an uppercase first letter, e.g. <code>EJerkyType</code>. The enum values themselves should use the initials of the type, e.g. <code>JT_TURKEY</code>, <code>JT_BEEF</code>, etc.<br />
* Variables are in MKS (meters, kilograms, seconds) unless otherwise specified, e.g. <code>frame_time</code> is seconds, <code>frame_time_ms</code> is milliseconds.<br />
* Data sizes are bytes unless otherwise specified, e.g. <code>size</code> vs. <code>size_bits</code>.<br />
* Global variables start with a lowercase '<code>g</code>', followed by the name with an uppercase first letter, e.g. <code>gGlobalTimeCounter</code><br />
* Compiler preprocessor directives should start with LL_ and consist of all uppercase letters separated with underscores, e.g. <code>LL_DEGREES_TO_RADIANS()</code><br />
* Externally available <code>const</code> global variables that replace <code>#define</code>s should start with <code>LL_</code> and consist of all uppercase letters separated with underscores, e.g. <code>LL_SECONDS_IN_DAY</code><br />
* File scoped <code>const</code> global variables that replace <code>#define</code>s need only consist of all uppercase letters separated with underscores, e.g. <code>SECONDS_BETWEEN_CLEANUP</code><br />
* You may optionally append a suffix character signifying the type, for example, <code>fontp</code> is a font pointer, <code>mTimeInSecondsf</code> is a float<br />
<br />
==== Enums ====<br />
<br />
Enums generally are declared using this form:<br />
<pre><br />
typedef enum e_new_enum<br />
{<br />
NE_ALPHA,<br />
NE_BETA,<br />
NE_GAMMA<br />
} ENewEnum;<br />
</pre><br />
so that the typedef name (ENewEnum in the example) can be used as a type name rather than (enum e_new_enum).<br />
<br />
==== Class Structure ====<br />
Member functions come first, followed by member variables at the end. Write it assuming a time-pressured programmer is going to read it from top to bottom looking for something they can use, so put the exported functions at the top, eg:<br />
<pre><br />
class LLMyClass <br />
{<br />
public:<br />
LLMyClass();<br />
~LLMyClass();<br />
<br />
void init(S32 area, F32 priority);<br />
<br />
void setVariable(BOOL variable) { mVariable = variable; }<br />
virtual void draw();<br />
<br />
private:<br />
BOOL mVariable;<br />
static U32 sCount;<br />
etc.<br />
};<br />
</pre> <br />
* Optionally, one line functions may be written in the class declaration header. Functions inlined for speed should go in the header. Other functions should go in the <code>.cpp</code> file. <br />
*:NOTE: Functions inlined this way do not require the <code>inline</code> compiler hint<br />
*:NOTE: virtual functions can not be inlined by the compiler so they should reside in the <code>.cpp</code> file because<br />
*:*otherwise the linker has to resolve each instance into a single function<br />
*:*it is misleading to imply that the function will be inlined when it will not be (e.g. don't use virtual functions inside performance critical inner loops)<br />
* Make sure that you initialize ALL member variables prior to use (often easiest to use an <code>init()</code> member function).<br />
<br />
==== Interface Naming ====<br />
C++ Interfaces follow the above conventions for normal classes. They must consist entirely pure virtual methods and have a pure virtual destructor. <br />
Interfaces must be named with <code>Interface</code> postfix.<br />
<pre><br />
class LLExampleInterface<br />
{<br />
public:<br />
virtual ~LLExampleInterface() = 0;<br />
<br />
virtual void example() = 0;<br />
};<br />
</pre><br />
<br />
==== Member Visibility ====<br />
Class declarations represent contracts and all non-private members are promises. It's important to promise as little as possible because it's much easier to expose more functionality later than it is to take it back. It also requires less precious time from each programmer attempting to use a given class when the non-private members are kept to a minimum. If you see something that's private, you know that you can safely ignore it, otherwise the designer is signaling that it is something that you might potentially want to use. Therefore please use these guidelines to keep visibility to a minimum, and remember that everything that applies to data members also applies to typedef, enum, class and method members.<br />
<br />
* Prefer <code>protected</code> members to <code>public</code>, and prefer <code>private</code> members to both.<br />
* Prefer <code>private</code> inner classes to <code>friend</code>s.<br />
* Document all non-private members but only document implementation details in the <code>.cpp</code> file.<br />
* Avoid creating a member in the first place when not absolutely needed. E.g. implement <code>getNumPixels() { "return mWidth * mHeight; }</code> rather than <code>getNumPixels() { return mArea; }</code> and having to always make sure that <code>mArea</code> is kept in sync with the current width and height. Remember: All class members are global to the class and all standard wisdom regarding globals apply.<br />
* Prefer accessor methods to accessible data.<br />
* Prefer local variables to member variables. Sometimes people create member variables as a way to share data between methods. When possible, it's better to create a local variable in one method and pass it as an argument to the ones that needed it.<br />
* Declare local variables as close to their point of use as possible. I.e. within nested blocks and as far down within its block as possible.<br />
<br />
==== Static Members ====<br />
Use <code>static</code> class members for global variables and singletons. Since these are globals, avoid using statics when practical.<br />
Avoid static class instances which will be globally constructed, use pointers and construct them in an initializer instead.<br />
<pre><br />
class LLFoo<br />
{<br />
static LLFoo* sInstance; // Don't do this -> static LLFoo sInstance;<br />
static void initClass();<br />
};<br />
...<br />
//static<br />
LLFoo* LLFoo::sInstance = NULL;<br />
<br />
//static<br />
void LLFoo::initClass()<br />
{<br />
sInstance = new LLFoo();<br />
}<br />
</pre><br />
<br />
==== Indentation ====<br />
#Configure your editor to treat a tab as having width of 4.<br />
#:Adding tabs that assume any other tab width is forbidden, and makes you look bad.<br />
#The use of either spaces or tabs for indentation is allowed, but '''spaces are preferred'''.<br />
<br />
{{KBnote|An earlier version of this standard required tabs, so much of the codebase uses them.<br />
'''Do not convert existing tabs to spaces unless you are either making significant changes to the code or are willing to do all merges on that file for anyone who needs them.'''}}<br />
<br />
When possible, use indentation to clarify variable lists and Boolean operations.<br />
<br />
==== Braces ====<br />
Source code should be written using braces on a line by themselves, similar to the gnu standard (not the more compact k&r standard), i.e.<br />
<tt><br />
while (foo < 10)<br />
<span style="font-weight: bold; color: green">{</span><br />
}<br />
</tt><br />
<br />
Instead of:<br />
<tt><br />
while (foo < 10) <span style="font-weight: bold; color: red">{</span><br />
}<br />
</tt><br />
<br />
And:<br />
<tt><br />
do <br />
<span style="font-weight: bold; color: green">{</span><br />
do_stuff();<br />
} while (foo < 10);<br />
</tt><br />
<br />
Instead of:<br />
<tt><br />
do <span style="font-weight: bold; color: red">{</span><br />
do_stuff();<br />
}<br />
while (foo < 10);<br />
</tt><br />
And:<br />
<tt><br />
if (foo < 10)<br />
<span style="font-weight: bold; color: green">{</span><br />
do_stuff();<br />
}<br />
else<br />
<span style="font-weight: bold; color: green">{</span><br />
do_stuff();<br />
}<br />
</tt><br />
<br />
Instead of:<br />
<tt><br />
if (foo < 10) <span style="font-weight: bold; color: red">{</span><br />
do_stuff();<br />
}<span style="font-weight: bold; color: red"> else {</span><br />
do_stuff();<br />
}<br />
</tt><br />
<br />
Use braces for clarity, even when not strictly required, e.g.<br />
<tt><br />
for (j = 0; j < NUM_TEXTURES; ++j)<br />
<span style="font-weight: bold; color: green">{</span><br />
do_stuff();<br />
<span style="font-weight: bold; color: green">}</span><br />
</tt><br />
Instead of:<br />
<tt><br />
for (j = 0; j < NUM_TEXTURES; ++j)<br />
<span style="font-weight: bold; color: red">do_stuff();</span><br />
</tt><br />
<br />
==== Comments ====<br />
Use C++ style comments for single line comments and member variable descriptions.<br />
Use classic C style comments <code>/* */</code> for temporarily commenting out large sections of code.<br />
<br />
<br />
We use the Doxygen system for generating documentation from header and source files. Doxygen supports a wide range of styles; this section provides recommendations for how it should be used in the Second Life Viewer project.<br />
<br />
<br />
====Doxygen Comment Style====<br />
<br />
Doxygen comments are distinguished from normal comments by an extra comment character at the beginning, and are associated with the adjacent code by their placement. Both C style comments and C++ single-line style comments are supported.<br />
<br />
=====C++ single-line style comments=====<br />
<br />
Doxygen interprets any C++ comment that has a '!' character at the beginning of a '//' comment, or an extra '*' character at the beginning of a '/* ... */' comment:<br />
<br />
//! A Doxygen single line comment<br />
<br />
/** A Doxygen single line comment */<br />
<br />
=====C multiple-line style comments=====<br />
<br />
/// A Doxygen comment<br />
/// that extends over more than one line<br />
/// this form should be used whenever there is more than one line<br />
<br />
'''note that triple slash does not work as a single line comment - it must have at least two lines'''<br />
<br />
=====Comment Placement=====<br />
<br />
Doxygen comments are, by default, associated with the code that immediately follows the comment:<br />
<br />
//! indicates whether or not the object currently allows any Foo actions.<br />
void isFooAllowed();<br />
<br />
For some constructs it is more readable to place the comment after the code element it documents, which is accomplished by adding a '<' character after the Doxygen comment indicator. This may be used with either single or multi-line comments:<br />
<br />
void doFooAt( int offset ///< the offset into the Foo<br />
,char* name ///< the name to look up for this Foo action<br />
/// in the FooMgr database.<br />
);<br />
<br />
Placing the Doxygen comment after the element it documents in this way is preferred whenever the element is a member of a list, as in parameter declarations or enum values.<br />
<br />
=====Class Documentation=====<br />
<br />
A class declaration must include a detailed description comment preceding the class:<br />
<br />
/// FooFactory is a factory class that constructs instances of the<br />
/// subclasses of Foo based on information obtained from the<br />
/// foo-config file.<br />
class FooFactory<br />
{<br />
<br />
<br />
The class comment is a good place to put general guidelines about how the methods in the class relate to one another.<br />
<br />
=====Member Grouping=====<br />
<br />
By default, Doxygen groups the members within a class based on heuristics that use the public/protected/private scope and the method signatures. For simple classes this usually works well, and may be used. When a class has many methods, it is usually better to explicitly control how they are grouped in the documentation, and to provide additional documentation at the group level. To explicitly control grouping, add the 'nosubgrouping' Doxygen command to the class comment:<br />
<br />
/// FooFactory is a factory class that constructs instances of the<br />
/// subclasses of Foo based on information obtained from the<br />
/// foo-config file.<br />
///<br />
/// @nosubgrouping<br />
///<br />
class FooFactory<br />
{<br />
<br />
Each group is then formed of the following elements:<br />
<br />
* An introducing Doxygen comment that supplies the group name using the 'name' Doxygen command,<br />
* The detailed comment for the group,<br />
* The Doxygen group start command '{',<br />
* The declarations of the members in the group and accompanying documentation, and finally<br />
* The Doxygen group end command '}'.<br />
<br />
For example (preceeded here by a non-Doxygen comment with a line of '=' characters so that it reads better in the source file):<br />
<br />
// ================================================================================<br />
/// @name Searching<br />
///<br />
/// The searching methods apply a compiled regular expression to a subject<br />
/// string. All searching methods return a boolean result indicating whether<br />
/// or not some match was found in the subject. To get information about<br />
/// the match, use the Results methods.<br />
///<br />
///@{<br />
''... the methods in the group ...''<br />
///@}<br />
<br />
=====Member Function Documentation=====<br />
<br />
Each member function should have:<br />
<br />
* A brief single line description preceeding the member declaration<br />
* Parameter descriptions following each parameter<br />
* A more detailed description following the declaration, which should include a Doxygen 'returns' command if the method returns a value.<br />
<br />
/// Search a string for matches to this regular expression.<br />
bool Search( const char * subject ///< the string to be searched for a match<br />
,int len = -1 ///< the length of the subject string<br />
,int options = 0 ///< sum of any PCRE options flags<br />
);<br />
///< Apply the regular expression to the subject string.<br />
/// Optional parameter len can be used to pass the subject's length to<br />
/// Search(). If not specified (or less than 0), strlen() is used<br />
/// internally to determine the length. Parameter options can contain<br />
/// any combination of options; for options documentation, see 'man pcre'<br />
/// @returns true if a match is found.<br />
<br />
<br />
<br />
=====Usage Examples=====<br />
<br />
Including examples in the documentation is strongly encouraged. To embed an example, use the Doxygen "@code ... @endcode" construct:<br />
<br />
///<<br />
/// May only be called after a successful search<br />
/// and applies to the results of that call.<br />
/// @returns true if there was an ith match, false if not<br />
///<br />
/// Example:@code<br />
/// RegEx matchBs("((B)B+)");<br />
/// UtlString getB;<br />
/// UtlString getBs;<br />
/// if (matchB.Search("xxaBBBBcyy"))<br />
/// {<br />
/// matchB.MatchString(&getBs,0);<br />
/// matchB.MatchString(&getB,2);<br />
/// }<br />
/// @endcode<br />
/// would set the UtlStrings<br />
/// - getBs to "BBBB"<br />
/// - getB to "B"<br />
<br />
=====Lists=====<br />
<br />
Numbered and bulleted lists are supported in Doxygen comments using simple indentation conventions:<br />
<br />
///< A numbered list is created using '#' characters:<br />
/// # first numbered item<br />
/// # second numbered item<br />
/// # first item in nested numbered list (2.1)<br />
///<br />
/// A bullet list uses '-' characters:<br />
/// - first bullet<br />
/// - second bullet<br />
/// - nested bullet<br />
<br />
==== Function Declaration ====<br />
The return value of the function should be on the same line as the function name, e.g.<br />
<br />
<pre><br />
void foo_func(S32 bar)<br />
{<br />
// do stuff<br />
}<br />
</pre><br />
<br />
== Python ==<br />
Our Python coding standards are generally based on http://www.python.org/dev/peps/pep-0008/, which should be considered authoritative for anything not covered here.<br />
<br />
<code>pylint</code> can be used for stylistic as well as error checking. E.g. <code>pylint --reports=n mydcode.py</code><br />
<br />
=== Source Files ===<br />
Unless it is an executable script, begin all Python source code files with this header:<br />
<br />
<source lang="python"><br />
"""\<br />
@file filename<br />
@author Optional author field<br />
@date Optional iso8601 creation date<br />
@brief brief description of the file<br />
<br />
$LicenseInfo:firstyear=2011&license=viewerlgpl$<br />
Second Life Viewer Source Code<br />
Copyright (C) 2011, Linden Research, Inc.<br />
<br />
This library is free software; you can redistribute it and/or<br />
modify it under the terms of the GNU Lesser General Public<br />
License as published by the Free Software Foundation;<br />
version 2.1 of the License only.<br />
<br />
This library is distributed in the hope that it will be useful,<br />
but WITHOUT ANY WARRANTY; without even the implied warranty of<br />
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU<br />
Lesser General Public License for more details.<br />
<br />
You should have received a copy of the GNU Lesser General Public<br />
License along with this library; if not, write to the Free Software<br />
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA<br />
<br />
Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA<br />
$/LicenseInfo$<br />
"""<br />
</source><br />
<br />
The <code>$LicenseInfo$</code> section is critically important. Choose <code>viewerlgpl</code> if you are not sure.<br />
<br />
==== Python File Names ====<br />
There is no need to prefix '<code>ll</code>' to every source file, but it may still be appropriate for the filename, for example, <code>llsd.py</code>.<br />
<br />
Write Python scripts intended to be executed from the command line in a way that makes them importable as a module. This means you should structure your code as follows:<br />
<br />
<source lang="python"><br />
#!/usr/bin/env python<br />
#<br />
# Doxygen header and license, see above<br />
...<br />
<br />
# Preamble, see below<br />
...<br />
<br />
# any class / utility functions here<br />
...<br />
<br />
# the main "api" function<br />
def do_stuff(arg1, arg2 ...):<br />
...<br />
<br />
# the main() function which does the command line parsing and validation<br />
# and invokes the "api" function.<br />
def main(argv):<br />
...<br />
return exit_code<br />
<br />
# execute main() only if invoked directly:<br />
if __name__ == "__main__":<br />
sys.exit(main(sys.argv))<br />
</source><br />
<br />
=== Style ===<br />
Library modules should never call functions or create objects which have side effects in the global process state prior to the first function call or object instantiation from an outside caller. This makes sure that different callers can configure the process state as necessary and not allow incorrect default values seep into the process state.<br />
<br />
<source lang="python"><br />
# replace this:<br />
CONSTANT = 'value'<br />
<br />
# with this:<br />
_g_constant = None<br />
def get_constant():<br />
global _g_constant<br />
if _g_constant is None:<br />
_g_constant = 'value'<br />
return _g_constant<br />
</source><br />
<br />
=== Whitespace ===<br />
Python can't parse files that mix using tabs and spaces for indentation. We settled on 4 spaces as the standard indentation increment.<br />
<br />
Put this in your <code>~/.emacs</code> to get the desired behavior (note that this also turns on colored fonts, which you probably wanted anyway):<br />
<pre><br />
(defun ll-python-mode-hook()<br />
(font-lock-mode 1)<br />
(font-lock-fontify-buffer)<br />
(set-variable 'indent-tabs-mode nil)<br />
(set-variable 'py-indent-offset 4)<br />
(message "ll coding style function executed"))<br />
(add-hook 'python-mode-hook 'll-python-mode-hook)<br />
</pre><br />
<br />
(insert instructions on using <code>python-mode</code> if it's not with your default emacs install)<br />
<br />
To convert tabs to spaces in Emacs:<br />
* Make sure your tab width is 4<br />
** In <code>.emacs</code>, <code>(set-variable 'tab-width 4)</code> (optionally inside your <code>ll-python-mode-hook()</code>)<br />
** Immediately: M-x set-variable[ret] tab-width[ret] 4[ret]<br />
* Set mark at start of file: M-< ^space <br />
* Jump to end of file: M-><br />
* Convert tabs to spaces within marked range: M-x untabify<br />
<br />
To use spaces instead of tabs in vi put this in ~/.vimrc:<br />
" You probably already have these<br />
set tabstop=4<br />
set shiftwidth=4<br />
" When reading a file or creating a new file, uses spaces not tabs<br />
autocmd BufRead,BufNewFile *.py set expandtab<br />
<br />
Another option for tabs/spaces:<br />
" automatically expand tab keypresses into the appropriate number of spaces<br />
set expandtab<br />
<br />
=== PYTHONPATH ===<br />
To write scripts that can be run without an explicit <code>PYTHONPATH</code>, see <code>linden/scripts/setup-path.py</code> and the scripts that reference it.<br />
<br />
We maintain a python library in <code>indra/lib/python</code>. In order to use the libraries in there, you have to set the <code>PYTHONPATH</code> environment variable to point to that directory, e.g.<br />
<code>PYTHONPATH=/var/tmp/rdw/release/indra/lib/python python test.py</code><br />
<br />
To avoid having to specify the <code>PYTHONPATH</code>, you can instead use this snippet of code:<br />
<source lang="python"><br />
import sys<br />
import os.path<br />
<br />
# Look for indra/lib/python in all possible parent directories ... <br />
# This is an improvement over the setup-path.py method used previously:<br />
# * the script may be relocated anywhere inside the source/runtime tree<br />
# * it doesn't depend on the current directory<br />
# * it doesn't depend on another file being present.<br />
<br />
def add_indra_lib_path():<br />
# Use a function to ensure that global scope isn't polluted<br />
root = os.path.realpath(__file__)<br />
# always insert the directory of the script in the search path<br />
libdir = os.path.dirname(root)<br />
if libdir not in sys.path:<br />
sys.path.insert(0, libdir)<br />
<br />
# Now go look for indra/lib/python in the parent dirs<br />
while root != os.path.sep:<br />
root = os.path.dirname(root)<br />
libdir = os.path.join(root, 'indra', 'lib', 'python')<br />
if os.path.isdir(libdir):<br />
if libdir not in sys.path:<br />
sys.path.insert(0, libdir)<br />
return root<br />
print >> sys.stderr, "This script is not inside a valid installation."<br />
sys.exit(1)<br />
<br />
# Use the return value to locate other files relative to the<br />
# install root...<br />
_root = add_indra_lib_path()<br />
</source><br />
<br />
Symlinking <code>/opt/linden</code> to your checkout should be deprecated. That's a bad dependency to have, and it doesn't play well with multiple devs on a station.<br />
<br />
On our machines, we now have a standard library in <code>/var/lib/python-support.python2.4/llindrapath.py</code> that does all this. As such, you should be able to accomplish the path by including this line:<br />
<br />
<source lang="python"><br />
import llindrapath<br />
</source><br />
<br />
=== imports ===<br />
* Include only one module per line<br />
* Order your imports alphabetically (this gets a little vague with <code>from x in y</code> imports TODO: invent a rule that makes sense)<br />
* Try not to pollute the module's namespace by importing individual functions and classes from other modules.<br />
<br />
bad:<br />
<br />
<source lang="python"><br />
from os.path import join, dirname, realpath<br />
import socket, urllib2<br />
</source><br />
<br />
good:<br />
<br />
<source lang="python"><br />
import os.path<br />
import socket<br />
import urllib2<br />
</source></div>Natty Lindenhttps://wiki.secondlife.com/w/index.php?title=Coding_standard&diff=1197211Coding standard2015-08-12T16:34:35Z<p>Natty Linden: more <source> fixes</p>
<hr />
<div>{{OSWikiLearnBox}}<br />
<br />
This document defines and formalizes the style of code produced at Linden Lab. Adherence to these conventions is critical to ensuring readable and maintainable code.<br />
<br />
Some tools are available for checking some parts of these rules in the [[Mercurial Tools]] repository.<br />
<br />
__TOC__<br />
<br />
== Common Guidelines ==<br />
=== Line Endings ===<br />
<br />
<br />
All text files '''must''' use unix (linefeed only) line endings when submitted.<br />
<br />
Exceptions are allowed ''only'' if the file in question:<br />
* Is used or relevant ''only'' on Windows<br />
* Requires DOS style (carriage-return linefeed) line endings for correctness.<br />
<br />
Files that have DOS line endings must either:<br />
* have a suffix that is one of:<br />
** .bat<br />
** .sln<br />
** .vcxproj<br />
* '''or''' be located somewhere under a directory named <tt>windows</tt> or <tt>vstool</tt><br />
<br />
See [[How to avoid DOS line endings in Windows tools]]<br />
<br />
=== Comments ===<br />
When commenting, address the "why" and not the "what" unless the what is difficult to see. If what the code does requires commenting, also include why it has to be done that way so maintainers can follow your reasoning. Use complete sentences and correct spelling.<br />
<br />
Prefer the use of [http://www.doxygen.org/ doxygen] compatible comment markup. (See [[Using Doxygen]].)<br />
<br />
Sometimes you know something is broken or a bad idea but need to do it anyway &mdash; please follow the special comment token guidelines to not confuse the next engineer. Append your name or initials and the date of the comment when using these special comment tokens.<br />
<br />
{| border="1"<br />
|+ <b>Special Comment Tokens</b><br />
! Token !! Meaning !! Deprecated tokens<br />
|-<br />
| *FIX<br />
| The associated code is actually broken and has known failure cases. All instances of these should be fixed before shipping. Do not use this tag for hacks or suggested development. Do not simply tag the code with the token alone or simply provide instructions since then it is unclear if that is how it is broken. For example: "// *FIX: invalidate stored textures when number of faces change" &mdash; is really unclear if it is an instruction or a buggy side effect of the code segment.<br />
| FIXME BROKEN BUG <br />
|-<br />
| *HACK<br />
| The associated code relies on external information or processes outside the normal control flow for the function to work.<br />
| HACK<br />
|-<br />
| *TODO<br />
| The associated code should be optimized, clarified, or made more reliable. Provide instructions along with why it is probably a good idea to execute on the comment.<br />
| TODO <br />
|-<br />
| *NOTE<br />
| This denotes that there is something tricky or special about the associated code above and beyond answering the typical 'what' and 'why.'<br />
| NOTE "NOTA BENE" NB<br />
|}<br />
<br />
Examples:<br />
{| border="0"<br />
|-<br />
||<br />
<pre><br />
// *FIX: This will fail if the packets arrive out of order.<br />
<br />
// *HACK: Filter out some extra information from this <br />
// packet and put it into the status bar since it is more <br />
// up to date than the last balance update.<br />
<br />
// *TODO: This call does not always need to be made<br />
// and is computationally expensive. Write a test to see<br />
// if it must be made during this iteration.<br />
<br />
// *NOTE: The tile is actually 257 pixels wide, so<br />
// we have to fudge the texture coordinates a bit for this<br />
// to look right.<br />
</pre><br />
|}<br />
<br />
=== No New Serialization Formats ===<br />
After much effort and unit-testing, LLSD is now the sanctioned format for all new serialization tasks. Do not implement another serialization scheme unless you have a signed and notarized letter from Elvis explicitly stating that a new serialization format is a good idea. If speed is essential, LLSD has a fairly speedy binary parser and formatter available in C++ and python.<br />
<br />
=== Unicode ===<br />
Use UTF-8 for all string serialization and communication between processes. Do not use UTF-16 except when interfacing with Win32.<br />
<br />
=== File Names ===<br />
File names should be significant and of reasonable length, with no spaces or uppercase letters. Separate words in the name MUST be separated with underscore (<code>like_this.cpp</code>), because languages like Python cannot include module names with hyphens (<code>will-not-import.py</code>).<br />
<br />
=== Dead Code ===<br />
Do not leave commented-out code in any commits that you make. Delete that code instead.<br />
<br />
Block-commented out code, including code disabled through #if <something-false> is especially problematic because it is not distinguishable from live code in a grep. Our version control systems are good enough that any code that was checked in is retrievable even after deletion.<br />
<br />
== C++ ==<br />
<br />
=== Source Files ===<br />
All C++ source code files should begin with the header shown below:<br />
<syntaxhighlight lang="cpp"><br />
/** <br />
* @file file base name<br />
* @author optional<br />
* @brief brief description of the file<br />
*<br />
* $LicenseInfo:firstyear=2011&license=viewerlgpl$<br />
* Second Life Viewer Source Code<br />
* Copyright (C) 2011, Linden Research, Inc.<br />
* <br />
* This library is free software; you can redistribute it and/or<br />
* modify it under the terms of the GNU Lesser General Public<br />
* License as published by the Free Software Foundation;<br />
* version 2.1 of the License only.<br />
* <br />
* This library is distributed in the hope that it will be useful,<br />
* but WITHOUT ANY WARRANTY; without even the implied warranty of<br />
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU<br />
* Lesser General Public License for more details.<br />
* <br />
* You should have received a copy of the GNU Lesser General Public<br />
* License along with this library; if not, write to the Free Software<br />
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA<br />
* <br />
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA<br />
* $/LicenseInfo$<br />
*/<br />
</syntaxhighlight><br />
The <code>$LicenseInfo$</code> section is critically important so that we can process the source files based on license agreements. If you're unsure, use <code>viewerlgpl</code>. The licenses we support are:<br />
* <code>internal</code> - something probably not meant for distribution unless we have an agreement with the third party.<br />
* <code>viewergpl</code> - '''deprecated''' this was used for pre-[[Project Snowstorm|Snowstorm]] viewer distribution<br />
* <code>viewerlgpl</code> - the viewer and linden libraries distributed with it<br />
* <code>mit</code> - a basic MIT license for some libraries such as mulib, eventlet, and indra.ipc.<br />
Replace the <code>2011</code> with the year the file is created.<br />
<br />
All C++ source files should end in a newline. If not, GCC will fail with an error, as it enforces the C++ standard which requires one.<br />
<br />
Headers should be included in the following order:<br />
<syntaxhighlight lang="cpp"><br />
// All files MUST include one of the following first: <br />
// (includes linden_preprocessor.h, stdtypes.h, and some common headers)<br />
#include "llviewerprecompiledheaders.h" // (NEWVIEW)<br />
// OR<br />
#include "llsimprecompiledheaders.h" // NEWSIM<br />
// OR<br />
#include "linden_common.h" // the ll libraries<br />
<br />
#include "llfoo.h" // Associated header, should have no hidden dependencies<br />
<br />
#include <map> // stl headers<br />
#include <cmath> // std headers<br />
#include "vorbis/ogg.h" // External library headers<br />
<br />
#include "llbar.h" // Other linden headers<br />
</syntaxhighlight><br />
<br />
==== File Names ====<br />
All C++ source files should be prefixed with 'll'.<br />
<br />
File names must end with the appropriate suffix:<br />
* '''cpp''' for ANSI C++ code<br />
* '''h''' for included declarations<br />
* '''inl''' for large chunks of inlined C++ code. Avoid this.<br />
* '''asm''' for assembly files. <em>Do not do this without a note from some deity forgiving your transgressions.</em><br />
<br />
==== Include Files ====<br />
Include files should be specified using relative paths using the '<code>/</code>' character to help ensure portability. <br />
In the code relative paths should not be used. Instead, paths should be indicated in the relevant CMake file.<br />
<br />
Compiler directives used to prevent multiple header file inclusion should be based on the file name with a <code>LL_</code> prefix.<br />
Include dependent headers in the header file, but use forward declarations where possible.<br />
<br />
For example:<br />
<syntaxhighlight lang="cpp"><br />
#ifndef LL_LLFOO_H<br />
#define LL_LLFOO_H<br />
<br />
#include "llbar.h"<br />
<br />
class LLReferencedData; // forward declaration (no need to include "llrefrenceddata.h")<br />
<br />
class LLFoo : public LLBar<br />
{<br />
public:<br />
LLFoo();<br />
void setData(LLReferencedData& refdata);<br />
private:<br />
LLReferencedData* mRefData;<br />
};<br />
<br />
#endif //LL_LLFOO_H<br />
</syntaxhighlight><br />
<br />
=== Basic Facilities ===<br />
<br />
==== Linden Variable Types ====<br />
The following basic types are defined in "stdtypes.h" and should be used to ensure clarity and cross platform functionality:<br />
* Integral Types<br />
** The first letter specifies 'S' for signed integers and 'U' for unsigned integers<br />
** The type ends with the number of bits used to represent the integer<br />
** Examples: S8 (signed 8-bit integer), U32 (unsigned 32 bit integer)<br />
** The integer types are: '''U8''', '''S8''', '''U16''', '''S16''', '''U32''', '''S32''', '''U64''', '''S64'''<br />
** The '''char''' type may used when required by defualt C functions that need a char<br />
* Floating Point Types<br />
** '''F32''' denotes a 32-bit floating point value ("float")<br />
** '''F64''' denotes a 64-bit floating point value ("double")<br />
* Other Types<br />
** Prefer the use of the standard C++ '''bool'''. This is especially for comparators like operator< (using bool instead of BOOL will prevent lots of warnings when using STL). '''BOOL''' denotes a 32-bit integer value that is used to denote TRUE or FALSE. It is specifically not '''bool''' to prevent mistakes caused by "sizeof(bool)" being implementation specific.<br />
<br />
==== Project Defines ====<br />
* Use <code>LL_WINDOWS</code>, <code>LL_LINUX</code>, and <code>LL_DARWIN</code> for platform specific code. <br />
* Use <code>LL_RELEASE</code> and <code>LL_DEBUG</code> for release and debug specific code. <br />
* <code>LL_RELEASE_FOR_DOWNLOAD</code> and <code>LL_RELEASE</code> are both defined in versions destined to be in a resident's hands.<br />
* Prefer testing for the positive when using <code>#if</code>, and use <code>#if</code>/<code>#if !</code> instead of <code>#ifdef</code>/<code>#ifndef</code>. For example:<br />
<pre style="color:green"><br />
#if LL_DEBUG && !LL_DARWIN<br />
</pre><br />
instead of:<br />
<pre style="color: red"><br />
#ifndef LL_RELEASE && !defined(LL_DARWIN)<br />
</pre><br />
However, this construct is still useful:<br />
<pre><br />
#ifndef SOMETHING<br />
#define SOMETHING SOMETHING_NEW<br />
#endif<br />
</pre><br />
<br />
=== Usage ===<br />
==== String Handling ====<br />
See [[C++ Strings]] for more guidelines on strings in the C++ codebase.<br />
<br />
Use <code>std::string</code> as a string container and [https://bitbucket.org/lindenlab/viewer-release/src/tip/indra/llcommon/stringize.h the STRINGIZE() macro] for constructing strings from multiple sources.<br />
<br />
If you must use <code>printf</code> style formatting, use <code>snprintf()</code> or <code>llformat()</code>. Never use user supplied data (including configuration files) to specify formatting instructions to those functions.<br />
<br />
Avoid using c-string libraries. If you are writing something where speed is critical, prefer the use of <code>std::vector<char></code> since it's usage is less error prone, you can explicitly allocate or occupy memory, it is easy to null terminate with a single call to <code>container.push_back('\0')</code>, and it is <code>char*</code> compatible with a call to <code>&container[0]</code>.<br />
<br />
The following c functions are forbidden:<br />
* <code>sprintf()</code> and <code>vsprintf()</code> &mdash; most of the <code>printf</code> family of functions are like individual invitations to hackers to overflow a buffer. Don't use them.<br />
* <code>strcpy()</code> and <code>strncpy()</code> &mdash; both of these functions are dangerous and difficult to use correctly. For example, did you know that <code>strncpy()</code> does not null terminate if no null is found in the source?<br />
* <code>strcat()</code> and <code>strncat()</code> &mdash; these methods are inefficient in comparison to using <code>ostringstream()</code> and are easy to misuse. The <code>strncat()</code> function is almost forgivable, but please just avoid these.<br />
* <code>gets()</code> &mdash; Never, ever use this function.<br />
<br />
Our legacy being what it is, there are some unavoidable cases where we are forced to use c-string functions. Here are some tips:<br />
* <code>sscanf()</code>, <code>fscanf()</code>, etc... &mdash; For these methods, always supply a maximum width for strings. If you do not know how to do this off the top of your head, do not use these functions. Even if you think you know, read the man page again.<br />
* <code>snprintf()</code>, <code>fprintf()</code>, <code>vsnprintf()</code>, etc.. &mdash; As noted above, never let anything other than the program determine the formatting. Never trust configuration files, assets, message system data, etc.<br />
<br />
==== Unicode ====<br />
Useful functions for dealing with UTF-8 can be found in llstring.h. Remember that truncating a UTF-8 string must be done on glyph rather than byte boundaries or your string may no longer be valid UTF-8. Many methods in <code>std::string</code> and <code>LLString</code> are not UTF-8 safe since they work on <code>sizeof(char_t)</code> byte boundaries. Similarly, the functions in that header file which do not contain 'ut8f' in the prototype are probably not UTF-8 safe.<br />
<br />
Do not use UTF-16 except with interfacing with Win32.<br />
<br />
Avoid the use of wide strings except when iteration speed is essential, or you are interfacing with the host operating system.<br />
<br />
==== Trigraphs ====<br />
<br />
A trigraph is a set of three characters that represents one character in the C character set. The set of trigraph sequences was defined in the ANSI Standard to allow users to use the full range of C characters, even if their keyboards do not implement the full C character set. <br />
<br />
Each trigraph sequence is introduced by two question marks. The third character in the sequence indicates which character is being represented. The standard trigraphs and the characters they produce are:<br />
<br />
??( [<br />
??/ \<br />
??) ]<br />
??' ^<br />
??< {<br />
??! |<br />
??> }<br />
??- ~<br />
<br />
'''Using trigraphs anywhere in Second Life code is not allowed.'''<br />
<br />
==== Containers ====<br />
Prefer the use of the containers available in the C++ standard template library. They are well tested and in common usage across the industry &mdash; thus, nobody has to learn the special (and lame) quirks of containers like <code>LLMap<></code> and <code>LLLinkedList<></code>.<br />
<br />
This is one of the few places where specialized implementations can yield significant memory or speed improvements. Do not create those containers until you have profiling results that prove the container is the bottleneck.<br />
<br />
Never use <code>std::vector<bool></code>.<br />
<br />
==== Memory Management ====<br />
Object allocation and deallocation should always be done using new and delete (or delete[] where appropriate). <br />
<br />
Wherever possible, going forward, object lifetime should be managed using smart pointers, favoring the shared_pt, weak_ptr and unique_ptr types. When defining a pointer type declare a typedef to make it easily referenced elsewhere in the code, this typedef should be of the form:<br />
<br />
<syntaxhighlight lang="cpp"><br />
typedef boost::shared_ptr<Class> ClassPtr; <br />
typedef boost::intrusive_ptr<Class> ClassPtr;<br />
</syntaxhighlight><br />
and<br />
<syntaxhighlight lang="cpp"><br />
typedef boost::weak_ptr<Class> ClassWPtr;<br />
</syntaxhighlight><br />
<br />
Alternately, a class may define its own preferred pointer type as part of its definition.<br />
<br />
<syntaxhighlight lang="cpp"><br />
class MyClass <br />
{<br />
typedef boost::shared_ptr<MyClass> ptr_t;<br />
typedef boost::weak_ptr<MyClass> wptr_t;<br />
};<br />
<br />
/.../<br />
<br />
MyClass::ptr_t aPointerVariableToMyClass;<br />
<br />
</syntaxhighlight><br />
<br />
New uses of the internal LLPointer<T> class should be avoided.<br />
<br />
For legacy deallocation , not yet handled by managed pointers, remember that C++ defines delete NULL as being safe, therefore it is unnecessary to test for NULL before deleting.<br />
<syntaxhighlight lang="cpp"><br />
delete var;<br />
var = null;<br />
</syntaxhighlight><br />
<br />
==== Exception Handling ====<br />
Exceptions have been recently enabled for all platforms and targets in the main indra code base. If you are familiar with their use, you can use them judiciously in the code. Be sure to make sure the places where you are throwing exceptions will not cause resource leaks, and be sure to comment what linden exceptions might be thrown with the function declaration.<br />
<br />
* do not use exception specifications<br />
* do not throw in a destructor (ever)<br />
* read up on exceptions before using them<br />
<br />
===== Resources =====<br />
* [http://www.parashift.com/c++-faq-lite/exceptions.html C++ FAQ]<br />
* [http://www.amazon.com/Exceptional-C%2B%2B-Engineering-Programming-Depth/dp/0201615622/ref=sr_1_2/105-9891007-0374847?ie=UTF8&s=books&qid=1191554809&sr=8-2 exceptional C++]<br />
* [http://www.amazon.com/More-Exceptional-C%2B%2B-Engineering-Depth/dp/020170434X/ref=sr_1_1/105-9891007-0374847?ie=UTF8&s=books&qid=1191554809&sr=8-1 more exceptional C++]<br />
<br />
==== Error Messages ====<br />
* See [[Logging_System_Overview]] for detailed info on using the runtime logging system. (Test plan for logging system found at: [[Configurable Error Logging Test]])<br />
* Do not wrap your messages in <code>#ifdef _DEBUG</code>, unless it is inside a performance critical loop. The logging constructs compile to very small amounts of code, and are very efficiently skipped at run time. And the ability to turn on your debug messages at run-time, even in a deployed release build, will make you very happy one day.<br />
<br />
==== Obvious Things We All Know ====<br />
* No magic numbers<br />
* Use <code>TRUE</code> and <code>FALSE</code> when working with <code>BOOL</code> (but prefer <code>bool</code> in general)<br />
* Use <code>NULL</code> for pointer compares to <code>0</code><br />
* Use <code>'\0'</code> for character compares to <code>0</code><br />
* Use <code>const</code> and <code>inline</code> rather than <code>#define</code> whenever possible. Prefer optimizing '''after''' you have proven that the code you want to inline actually has a significant benefit from in-lineing.<br />
* Remember that C++ does not have garbage collection<br />
* Comment non-intuitive member variables<br />
* Fix all compiler warnings.<br />
* Take advantage of the fact that gcc generates different, sometimes better, warnings than Visual Studio.<br />
* Don't use assignments in <code>while</code> loops, e.g. <code>while (foo = nextData())</code> as this causes gcc to emit warnings. Use <code>while ((foo = nextData()) != NULL)</code> or restructure the code.<br />
<br />
=== Style ===<br />
==== Naming Convention ====<br />
The following name conventions apply to non-iterators:<br />
* Names should include one or more English words that are relevant to the entity being named. Try to use non-programming words, like <code>EmployeeList</code> rather than <code>LinkedListOfStrings</code><br />
* C functions are in lowercase, e.g. <code>main()</code>, <code>update_world_time()</code><br />
* Local variables and parameters are in lowercase, e.g. <code>frame_count</code><br />
** Local variables are '''not''' camel-case. <code>thisIsNotALocalVariable</code><br />
* Class methods start with a lowercase character but are otherwise CamelCase, e.g. <code>destroyWorld()</code><br />
* Non-static class member variables start with an '<code>m</code>' followed by an uppercase character, e.g. <code>mCameraPosition</code><br />
* Static class member variables start with an '<code>s</code>' followed by an uppercase character, e.g. <code>sInstance</code><br />
* Structures and Classes start with the uppercase '<code>LL</code>', followed by the name with an uppercase first letter, e.g. <code>LLMyClass</code><br />
* Enums start with the uppercase '<code>E</code>', followed by the name with an uppercase first letter, e.g. <code>EJerkyType</code>. The enum values themselves should use the initials of the type, e.g. <code>JT_TURKEY</code>, <code>JT_BEEF</code>, etc.<br />
* Variables are in MKS (meters, kilograms, seconds) unless otherwise specified, e.g. <code>frame_time</code> is seconds, <code>frame_time_ms</code> is milliseconds.<br />
* Data sizes are bytes unless otherwise specified, e.g. <code>size</code> vs. <code>size_bits</code>.<br />
* Global variables start with a lowercase '<code>g</code>', followed by the name with an uppercase first letter, e.g. <code>gGlobalTimeCounter</code><br />
* Compiler preprocessor directives should start with LL_ and consist of all uppercase letters separated with underscores, e.g. <code>LL_DEGREES_TO_RADIANS()</code><br />
* Externally available <code>const</code> global variables that replace <code>#define</code>s should start with <code>LL_</code> and consist of all uppercase letters separated with underscores, e.g. <code>LL_SECONDS_IN_DAY</code><br />
* File scoped <code>const</code> global variables that replace <code>#define</code>s need only consist of all uppercase letters separated with underscores, e.g. <code>SECONDS_BETWEEN_CLEANUP</code><br />
* You may optionally append a suffix character signifying the type, for example, <code>fontp</code> is a font pointer, <code>mTimeInSecondsf</code> is a float<br />
<br />
==== Enums ====<br />
<br />
Enums generally are declared using this form:<br />
<pre><br />
typedef enum e_new_enum<br />
{<br />
NE_ALPHA,<br />
NE_BETA,<br />
NE_GAMMA<br />
} ENewEnum;<br />
</pre><br />
so that the typedef name (ENewEnum in the example) can be used as a type name rather than (enum e_new_enum).<br />
<br />
==== Class Structure ====<br />
Member functions come first, followed by member variables at the end. Write it assuming a time-pressured programmer is going to read it from top to bottom looking for something they can use, so put the exported functions at the top, eg:<br />
<pre><br />
class LLMyClass <br />
{<br />
public:<br />
LLMyClass();<br />
~LLMyClass();<br />
<br />
void init(S32 area, F32 priority);<br />
<br />
void setVariable(BOOL variable) { mVariable = variable; }<br />
virtual void draw();<br />
<br />
private:<br />
BOOL mVariable;<br />
static U32 sCount;<br />
etc.<br />
};<br />
</pre> <br />
* Optionally, one line functions may be written in the class declaration header. Functions inlined for speed should go in the header. Other functions should go in the <code>.cpp</code> file. <br />
*:NOTE: Functions inlined this way do not require the <code>inline</code> compiler hint<br />
*:NOTE: virtual functions can not be inlined by the compiler so they should reside in the <code>.cpp</code> file because<br />
*:*otherwise the linker has to resolve each instance into a single function<br />
*:*it is misleading to imply that the function will be inlined when it will not be (e.g. don't use virtual functions inside performance critical inner loops)<br />
* Make sure that you initialize ALL member variables prior to use (often easiest to use an <code>init()</code> member function).<br />
<br />
==== Interface Naming ====<br />
C++ Interfaces follow the above conventions for normal classes. They must consist entirely pure virtual methods and have a pure virtual destructor. <br />
Interfaces must be named with <code>Interface</code> postfix.<br />
<pre><br />
class LLExampleInterface<br />
{<br />
public:<br />
virtual ~LLExampleInterface() = 0;<br />
<br />
virtual void example() = 0;<br />
};<br />
</pre><br />
<br />
==== Member Visibility ====<br />
Class declarations represent contracts and all non-private members are promises. It's important to promise as little as possible because it's much easier to expose more functionality later than it is to take it back. It also requires less precious time from each programmer attempting to use a given class when the non-private members are kept to a minimum. If you see something that's private, you know that you can safely ignore it, otherwise the designer is signaling that it is something that you might potentially want to use. Therefore please use these guidelines to keep visibility to a minimum, and remember that everything that applies to data members also applies to typedef, enum, class and method members.<br />
<br />
* Prefer <code>protected</code> members to <code>public</code>, and prefer <code>private</code> members to both.<br />
* Prefer <code>private</code> inner classes to <code>friend</code>s.<br />
* Document all non-private members but only document implementation details in the <code>.cpp</code> file.<br />
* Avoid creating a member in the first place when not absolutely needed. E.g. implement <code>getNumPixels() { "return mWidth * mHeight; }</code> rather than <code>getNumPixels() { return mArea; }</code> and having to always make sure that <code>mArea</code> is kept in sync with the current width and height. Remember: All class members are global to the class and all standard wisdom regarding globals apply.<br />
* Prefer accessor methods to accessible data.<br />
* Prefer local variables to member variables. Sometimes people create member variables as a way to share data between methods. When possible, it's better to create a local variable in one method and pass it as an argument to the ones that needed it.<br />
* Declare local variables as close to their point of use as possible. I.e. within nested blocks and as far down within its block as possible.<br />
<br />
==== Static Members ====<br />
Use <code>static</code> class members for global variables and singletons. Since these are globals, avoid using statics when practical.<br />
Avoid static class instances which will be globally constructed, use pointers and construct them in an initializer instead.<br />
<pre><br />
class LLFoo<br />
{<br />
static LLFoo* sInstance; // Don't do this -> static LLFoo sInstance;<br />
static void initClass();<br />
};<br />
...<br />
//static<br />
LLFoo* LLFoo::sInstance = NULL;<br />
<br />
//static<br />
void LLFoo::initClass()<br />
{<br />
sInstance = new LLFoo();<br />
}<br />
</pre><br />
<br />
==== Indentation ====<br />
#Configure your editor to treat a tab as having width of 4.<br />
#:Adding tabs that assume any other tab width is forbidden, and makes you look bad.<br />
#The use of either spaces or tabs for indentation is allowed, but '''spaces are preferred'''.<br />
<br />
{{KBnote|An earlier version of this standard required tabs, so much of the codebase uses them.<br />
'''Do not convert existing tabs to spaces unless you are either making significant changes to the code or are willing to do all merges on that file for anyone who needs them.'''}}<br />
<br />
When possible, use indentation to clarify variable lists and Boolean operations.<br />
<br />
==== Braces ====<br />
Source code should be written using braces on a line by themselves, similar to the gnu standard (not the more compact k&r standard), i.e.<br />
<tt><br />
while (foo < 10)<br />
<span style="font-weight: bold; color: green">{</span><br />
}<br />
</tt><br />
<br />
Instead of:<br />
<tt><br />
while (foo < 10) <span style="font-weight: bold; color: red">{</span><br />
}<br />
</tt><br />
<br />
And:<br />
<tt><br />
do <br />
<span style="font-weight: bold; color: green">{</span><br />
do_stuff();<br />
} while (foo < 10);<br />
</tt><br />
<br />
Instead of:<br />
<tt><br />
do <span style="font-weight: bold; color: red">{</span><br />
do_stuff();<br />
}<br />
while (foo < 10);<br />
</tt><br />
And:<br />
<tt><br />
if (foo < 10)<br />
<span style="font-weight: bold; color: green">{</span><br />
do_stuff();<br />
}<br />
else<br />
<span style="font-weight: bold; color: green">{</span><br />
do_stuff();<br />
}<br />
</tt><br />
<br />
Instead of:<br />
<tt><br />
if (foo < 10) <span style="font-weight: bold; color: red">{</span><br />
do_stuff();<br />
}<span style="font-weight: bold; color: red"> else {</span><br />
do_stuff();<br />
}<br />
</tt><br />
<br />
Use braces for clarity, even when not strictly required, e.g.<br />
<tt><br />
for (j = 0; j < NUM_TEXTURES; ++j)<br />
<span style="font-weight: bold; color: green">{</span><br />
do_stuff();<br />
<span style="font-weight: bold; color: green">}</span><br />
</tt><br />
Instead of:<br />
<tt><br />
for (j = 0; j < NUM_TEXTURES; ++j)<br />
<span style="font-weight: bold; color: red">do_stuff();</span><br />
</tt><br />
<br />
==== Comments ====<br />
Use C++ style comments for single line comments and member variable descriptions.<br />
Use classic C style comments <code>/* */</code> for temporarily commenting out large sections of code.<br />
<br />
<br />
We use the Doxygen system for generating documentation from header and source files. Doxygen supports a wide range of styles; this section provides recommendations for how it should be used in the Second Life Viewer project.<br />
<br />
<br />
====Doxygen Comment Style====<br />
<br />
Doxygen comments are distinguished from normal comments by an extra comment character at the beginning, and are associated with the adjacent code by their placement. Both C style comments and C++ single-line style comments are supported.<br />
<br />
=====C++ single-line style comments=====<br />
<br />
Doxygen interprets any C++ comment that has a '!' character at the beginning of a '//' comment, or an extra '*' character at the beginning of a '/* ... */' comment:<br />
<br />
//! A Doxygen single line comment<br />
<br />
/** A Doxygen single line comment */<br />
<br />
=====C multiple-line style comments=====<br />
<br />
/// A Doxygen comment<br />
/// that extends over more than one line<br />
/// this form should be used whenever there is more than one line<br />
<br />
'''note that triple slash does not work as a single line comment - it must have at least two lines'''<br />
<br />
=====Comment Placement=====<br />
<br />
Doxygen comments are, by default, associated with the code that immediately follows the comment:<br />
<br />
//! indicates whether or not the object currently allows any Foo actions.<br />
void isFooAllowed();<br />
<br />
For some constructs it is more readable to place the comment after the code element it documents, which is accomplished by adding a '<' character after the Doxygen comment indicator. This may be used with either single or multi-line comments:<br />
<br />
void doFooAt( int offset ///< the offset into the Foo<br />
,char* name ///< the name to look up for this Foo action<br />
/// in the FooMgr database.<br />
);<br />
<br />
Placing the Doxygen comment after the element it documents in this way is preferred whenever the element is a member of a list, as in parameter declarations or enum values.<br />
<br />
=====Class Documentation=====<br />
<br />
A class declaration must include a detailed description comment preceding the class:<br />
<br />
/// FooFactory is a factory class that constructs instances of the<br />
/// subclasses of Foo based on information obtained from the<br />
/// foo-config file.<br />
class FooFactory<br />
{<br />
<br />
<br />
The class comment is a good place to put general guidelines about how the methods in the class relate to one another.<br />
<br />
=====Member Grouping=====<br />
<br />
By default, Doxygen groups the members within a class based on heuristics that use the public/protected/private scope and the method signatures. For simple classes this usually works well, and may be used. When a class has many methods, it is usually better to explicitly control how they are grouped in the documentation, and to provide additional documentation at the group level. To explicitly control grouping, add the 'nosubgrouping' Doxygen command to the class comment:<br />
<br />
/// FooFactory is a factory class that constructs instances of the<br />
/// subclasses of Foo based on information obtained from the<br />
/// foo-config file.<br />
///<br />
/// @nosubgrouping<br />
///<br />
class FooFactory<br />
{<br />
<br />
Each group is then formed of the following elements:<br />
<br />
* An introducing Doxygen comment that supplies the group name using the 'name' Doxygen command,<br />
* The detailed comment for the group,<br />
* The Doxygen group start command '{',<br />
* The declarations of the members in the group and accompanying documentation, and finally<br />
* The Doxygen group end command '}'.<br />
<br />
For example (preceeded here by a non-Doxygen comment with a line of '=' characters so that it reads better in the source file):<br />
<br />
// ================================================================================<br />
/// @name Searching<br />
///<br />
/// The searching methods apply a compiled regular expression to a subject<br />
/// string. All searching methods return a boolean result indicating whether<br />
/// or not some match was found in the subject. To get information about<br />
/// the match, use the Results methods.<br />
///<br />
///@{<br />
''... the methods in the group ...''<br />
///@}<br />
<br />
=====Member Function Documentation=====<br />
<br />
Each member function should have:<br />
<br />
* A brief single line description preceeding the member declaration<br />
* Parameter descriptions following each parameter<br />
* A more detailed description following the declaration, which should include a Doxygen 'returns' command if the method returns a value.<br />
<br />
/// Search a string for matches to this regular expression.<br />
bool Search( const char * subject ///< the string to be searched for a match<br />
,int len = -1 ///< the length of the subject string<br />
,int options = 0 ///< sum of any PCRE options flags<br />
);<br />
///< Apply the regular expression to the subject string.<br />
/// Optional parameter len can be used to pass the subject's length to<br />
/// Search(). If not specified (or less than 0), strlen() is used<br />
/// internally to determine the length. Parameter options can contain<br />
/// any combination of options; for options documentation, see 'man pcre'<br />
/// @returns true if a match is found.<br />
<br />
<br />
<br />
=====Usage Examples=====<br />
<br />
Including examples in the documentation is strongly encouraged. To embed an example, use the Doxygen "@code ... @endcode" construct:<br />
<br />
///<<br />
/// May only be called after a successful search<br />
/// and applies to the results of that call.<br />
/// @returns true if there was an ith match, false if not<br />
///<br />
/// Example:@code<br />
/// RegEx matchBs("((B)B+)");<br />
/// UtlString getB;<br />
/// UtlString getBs;<br />
/// if (matchB.Search("xxaBBBBcyy"))<br />
/// {<br />
/// matchB.MatchString(&getBs,0);<br />
/// matchB.MatchString(&getB,2);<br />
/// }<br />
/// @endcode<br />
/// would set the UtlStrings<br />
/// - getBs to "BBBB"<br />
/// - getB to "B"<br />
<br />
=====Lists=====<br />
<br />
Numbered and bulleted lists are supported in Doxygen comments using simple indentation conventions:<br />
<br />
///< A numbered list is created using '#' characters:<br />
/// # first numbered item<br />
/// # second numbered item<br />
/// # first item in nested numbered list (2.1)<br />
///<br />
/// A bullet list uses '-' characters:<br />
/// - first bullet<br />
/// - second bullet<br />
/// - nested bullet<br />
<br />
==== Function Declaration ====<br />
The return value of the function should be on the same line as the function name, e.g.<br />
<br />
<pre><br />
void foo_func(S32 bar)<br />
{<br />
// do stuff<br />
}<br />
</pre><br />
<br />
== Python ==<br />
Our Python coding standards are generally based on http://www.python.org/dev/peps/pep-0008/, which should be considered authoritative for anything not covered here.<br />
<br />
<code>pylint</code> can be used for stylistic as well as error checking. E.g. <code>pylint --reports=n mydcode.py</code><br />
<br />
=== Source Files ===<br />
Unless it is an executable script, begin all Python source code files with this header:<br />
<br />
<source lang="python"><br />
"""\<br />
@file filename<br />
@author Optional author field<br />
@date Optional iso8601 creation date<br />
@brief brief description of the file<br />
<br />
$LicenseInfo:firstyear=2011&license=viewerlgpl$<br />
Second Life Viewer Source Code<br />
Copyright (C) 2011, Linden Research, Inc.<br />
<br />
This library is free software; you can redistribute it and/or<br />
modify it under the terms of the GNU Lesser General Public<br />
License as published by the Free Software Foundation;<br />
version 2.1 of the License only.<br />
<br />
This library is distributed in the hope that it will be useful,<br />
but WITHOUT ANY WARRANTY; without even the implied warranty of<br />
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU<br />
Lesser General Public License for more details.<br />
<br />
You should have received a copy of the GNU Lesser General Public<br />
License along with this library; if not, write to the Free Software<br />
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA<br />
<br />
Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA<br />
$/LicenseInfo$<br />
"""<br />
</source><br />
<br />
The <code>$LicenseInfo$</code> section is critically important. Choose <code>viewerlgpl</code> if you are not sure.<br />
<br />
==== Python File Names ====<br />
There is no need to prefix '<code>ll</code>' to every source file, but it may still be appropriate for the filename, for example, <code>llsd.py</code>.<br />
<br />
Write Python scripts intended to be executed from the command line in a way that makes them importable as a module. This means you should structure your code as follows:<br />
<br />
<source lang="python"><br />
#!/usr/bin/env python<br />
#<br />
# Doxygen header and license, see above<br />
...<br />
<br />
# Preamble, see below<br />
...<br />
<br />
# any class / utility functions here<br />
...<br />
<br />
# the main "api" function<br />
def do_stuff(arg1, arg2 ...):<br />
...<br />
<br />
# the main() function which does the command line parsing and validation<br />
# and invokes the "api" function.<br />
def main(argv):<br />
...<br />
return exit_code<br />
<br />
# execute main() only if invoked directly:<br />
if __name__ == "__main__":<br />
sys.exit(main(sys.argv))<br />
</source><br />
<br />
=== Style ===<br />
Library modules should never call functions or create objects which have side effects in the global process state prior to the first function call or object instantiation from an outside caller. This makes sure that different callers can configure the process state as necessary and not allow incorrect default values seep into the process state.<br />
<br />
<source lang="python"><br />
# replace this:<br />
CONSTANT = 'value'<br />
<br />
# with this:<br />
_g_constant = None<br />
def get_constant():<br />
global _g_constant<br />
if _g_constant is None:<br />
_g_constant = 'value'<br />
return _g_constant<br />
</source><br />
<br />
=== Whitespace ===<br />
Python can't parse files that mix using tabs and spaces for indentation. We settled on 4 spaces as the standard indentation increment.<br />
<br />
Put this in your <code>~/.emacs</code> to get the desired behavior (note that this also turns on colored fonts, which you probably wanted anyway):<br />
<pre><br />
(defun ll-python-mode-hook()<br />
(font-lock-mode 1)<br />
(font-lock-fontify-buffer)<br />
(set-variable 'indent-tabs-mode nil)<br />
(set-variable 'py-indent-offset 4)<br />
(message "ll coding style function executed"))<br />
(add-hook 'python-mode-hook 'll-python-mode-hook)<br />
</pre><br />
<br />
(insert instructions on using <code>python-mode</code> if it's not with your default emacs install)<br />
<br />
To convert tabs to spaces in Emacs:<br />
* Make sure your tab width is 4<br />
** In <code>.emacs</code>, <code>(set-variable 'tab-width 4)</code> (optionally inside your <code>ll-python-mode-hook()</code>)<br />
** Immediately: M-x set-variable[ret] tab-width[ret] 4[ret]<br />
* Set mark at start of file: M-< ^space <br />
* Jump to end of file: M-><br />
* Convert tabs to spaces within marked range: M-x untabify<br />
<br />
To use spaces instead of tabs in vi put this in ~/.vimrc:<br />
" You probably already have these<br />
set tabstop=4<br />
set shiftwidth=4<br />
" When reading a file or creating a new file, uses spaces not tabs<br />
autocmd BufRead,BufNewFile *.py set expandtab<br />
<br />
Another option for tabs/spaces:<br />
" automatically expand tab keypresses into the appropriate number of spaces<br />
set expandtab<br />
<br />
=== PYTHONPATH ===<br />
To write scripts that can be run without an explicit <code>PYTHONPATH</code>, see <code>linden/scripts/setup-path.py</code> and the scripts that reference it.<br />
<br />
We maintain a python library in <code>indra/lib/python</code>. In order to use the libraries in there, you have to set the <code>PYTHONPATH</code> environment variable to point to that directory, e.g.<br />
<code>PYTHONPATH=/var/tmp/rdw/release/indra/lib/python python test.py</code><br />
<br />
To avoid having to specify the <code>PYTHONPATH</code>, you can instead use this snippet of code:<br />
<source lang="python"><br />
import sys<br />
import os.path<br />
<br />
# Look for indra/lib/python in all possible parent directories ... <br />
# This is an improvement over the setup-path.py method used previously:<br />
# * the script may be relocated anywhere inside the source/runtime tree<br />
# * it doesn't depend on the current directory<br />
# * it doesn't depend on another file being present.<br />
<br />
def add_indra_lib_path():<br />
# Use a function to ensure that global scope isn't polluted<br />
root = os.path.realpath(__file__)<br />
# always insert the directory of the script in the search path<br />
libdir = os.path.dirname(root)<br />
if libdir not in sys.path:<br />
sys.path.insert(0, libdir)<br />
<br />
# Now go look for indra/lib/python in the parent dirs<br />
while root != os.path.sep:<br />
root = os.path.dirname(root)<br />
libdir = os.path.join(root, 'indra', 'lib', 'python')<br />
if os.path.isdir(libdir):<br />
if libdir not in sys.path:<br />
sys.path.insert(0, libdir)<br />
return root<br />
print >> sys.stderr, "This script is not inside a valid installation."<br />
sys.exit(1)<br />
<br />
# Use the return value to locate other files relative to the<br />
# install root...<br />
_root = add_indra_lib_path()<br />
</source><br />
<br />
Symlinking <code>/opt/linden</code> to your checkout should be deprecated. That's a bad dependency to have, and it doesn't play well with multiple devs on a station.<br />
<br />
On our machines, we now have a standard library in <code>/var/lib/python-support.python2.4/llindrapath.py</code> that does all this. As such, you should be able to accomplish the path by including this line:<br />
<br />
<source lang="python"><br />
import llindrapath<br />
</source><br />
<br />
=== imports ===<br />
* Include only one module per line<br />
* Order your imports alphabetically (this gets a little vague with <code>from x in y</code> imports TODO: invent a rule that makes sense)<br />
* Try not to pollute the module's namespace by importing individual functions and classes from other modules.<br />
<br />
bad:<br />
<br />
<python><br />
from os.path import join, dirname, realpath<br />
import socket, urllib2<br />
</python><br />
<br />
good:<br />
<br />
<python><br />
import os.path<br />
import socket<br />
import urllib2<br />
</python></div>Natty Linden