LJSUP-7813: New thread expander in S2, minimalism
[livejournal.git] / bin / upgrading / s2layers / core1.s2
blob4b8bf336c67c67e98db5a48a186200f944041abd
1 # -*-s2-*-
3 ##[ layerinfo ]
5 layerinfo "type" = "core";
6 layerinfo "name" = "LiveJournal S2 Core, v1";
7 layerinfo "redist_uniq" = "core1";
8 layerinfo "majorversion" = "1";
9 layerinfo "author_name" = "LiveJournal Webmaster";
10 layerinfo "author_email" = "webmaster@livejournal.com";
12 ##[ S2 core classes ]
14 class int
15 "An integer number.  This isn't really a class, as suggested by its lower-case name.  Parameters of type int pass by value, unlike all variables of real object types, which pass by reference.  Instead, this is just a pseudo-class which provides convenience methods on instances of integers.  The other pseudo-class is [class[string]]."
17     function builtin zeropad(int digits) : string
18     "Return the integer as a string formatted at least \$digits characters long, left-padded with zeroes.";
20     function builtin compare(int n) : int
21     "Compare one integer with another. Returns a negative number if n is less than the subject, positive if greater or zero if the two are numerically equal.";
24 class string
25 "A series of characters.  This isn't really a class, as suggested by its lower-case name.  Parameters of type string pass by value, unlike all variables of real object types, which pass by reference.  Instead, this is just a pseudo-class which provides convenience methods on instances of strings.  The other pseudo-class is [class[int]]."
27     function builtin substr(int start, int length) : string
28     "Returns up to \$length characters from string, skipping \$start characters from the beginning.";
30     function builtin ends_with (string sub) : bool
31     "Returns true if string ends in \$sub";
33     function builtin starts_with (string sub) : bool
34     "Returns true if string begins with \$sub";
36     function builtin contains (string sub) : bool
37     "Return true if string contains \$sub";
39     function builtin lower : string
40     "Returns string in lower case.";
42     function builtin upper : string
43     "Returns string in upper case";
45     function builtin upperfirst : string
46     "Return string with the first character capitalized.";
48     function builtin length() : int
49     "Return the number of characters in the string.";
51     function builtin repeat(int n) : string
52     "Returns the string repeated n times";
54     function builtin compare(string s) : int
55     "Compare one string with another. Returns a negative number if n is alphabetically before the subject, positive if greater or zero if the two are equal. Note that this function currently does a simple ASCII compare, not a proper unicode-aware sort.";
57     function builtin css_string() : string
58     "Returns the string escaped and quoted as a CSS string literal, safe for insertion into a stylesheet.";
60     function builtin css_keyword() : string
61     "If the string is syntactically valid as a CSS keyword (only letters and spaces) returns it, else returns an empty string.";
63     function builtin css_keyword_list() : string
64     "Analyses the string as a space-separated list of CSS keywords and returns a string containing the items that are syntactically acceptable.";
66     function builtin css_keyword(string[] allowed) : string
67     "Same as [method[string.css_keyword()]] except also imposes a whitelist of valid keywords given in \$allowed.";
69     function builtin css_keyword_list(string[] allowed) : string
70     "Same as [method[string.css_keyword_list()]] except also imposes a whitelist of valid keywords given in \$allowed.";
72     function builtin css_length_value() : string
73     "If the string contains a valid CSS length value, returns a canonical version. Else returns an empty string.";
75     function builtin css_url_value : string
76     "If the string contains a valid HTTP or HTTPS URL it is returned. Otherwise, an empty string is returned.";
79 class Color
80 "Represents a color."
82   var readonly int r "Red value, 0-255.";
83   var readonly int g "Green value, 0-255.";
84   var readonly int b "Blue value, 0-255.";
85   var  string as_string "HTML hex encoded: #rrggbb";
86   function builtin Color(string s) : Color "Constructor for color class.  Lets you make a Color object from a string of form #rrggbb";
87   function builtin set_hsl (int h, int s, int v) "Set the HSL value for a color class.";
89   function builtin red(int r) "Set the red value. (0-255)";
90   function builtin green(int g) "Set the green value. (0-255)";
91   function builtin blue(int b) "Set the blue value. (0-255)";
92   function builtin red() : int "Get the red value.";
93   function builtin green() : int "Get the green value.";
94   function builtin blue() : int "Get the blue value.";
96   function builtin hue(int h) "Set the hue value. (0-255)";
97   function builtin saturation(int s) "Set the saturation value. (0-255)";
98   function builtin lightness(int v) "Set the lightness value. (0-255)";
99   function builtin hue() : int "Get the hue value. (0-255)";
100   function builtin saturation() : int "Get the saturation value. (0-255)";
101   function builtin lightness() : int "Get the lightness value. (0-255)";
103   function builtin clone() : Color "Returns identical color.";
104   function builtin lighter() : Color "Returns a new color with lightness increased by 30.";
105   function builtin lighter(int amt) : Color "Returns a new color with lightness increased by amount given.";
106   function builtin darker() : Color "Returns a new color with lightness decreased by 30.";
107   function builtin darker(int amt) : Color "Returns a new color with lightness decreased by amount given.";
108   function builtin inverse() : Color "Returns inverse of color.";
109   function builtin average(Color other) : Color "Returns color averaged with \$other color.";
110   function builtin blend(Color other, int value) : Color "Returns color blended with \$other color by percentage value (int between 0 and 100).";
114 ##[ site-core classes ]
116 class Date
117 "Represents a date."
119     var int year "Year; 4 digits.";
120     var int month "Month; 1-12.";
121     var int day "Day; 1-31.";
123     function builtin day_of_week() : int
124     "Returns the day of the week this date falls on, from Sunday=1 to Saturday=7";
126     function builtin date_format () : string
127     "Returns date formatted as normal.  /// SeeAlso: siteapi.core1.dateformats";
129     function builtin date_format (string fmt) : string
130     "Returns date formatted as indicated by \$fmt.  One of: short, med, long, med_day, long_day.  Or a custom format.  Default is 'short'. /// SeeAlso: siteapi.core1.dateformats";
132     function builtin compare(Date d) : int
133     "Compare two dates. Returns a negative number if d is before the subject in time, positive if it is after, or zero if the two dates are equal. When comparing a Date with a DateTime, the time on the bare Date value is assumed to be midnight.";
135     function builtin compare(DateTime d) : int
136     "Compare two dates. Returns a negative number if d is before the subject in time, positive if it is after, or zero if the two dates are equal. When comparing a Date with a DateTime, the time on the bare Date value is assumed to be midnight.";
139 class DateTime extends Date
140 "Represents both a date and time."
142     var int hour "Hour; 0-23.";
143     var int min "Minute; 0-59.";
144     var int sec "Second; 0-59.";
146     function builtin time_format () : string
147     "Returns time formatted as normal.  /// SeeAlso: siteapi.core1.dateformats";
149     function builtin time_format (string fmt) : string
150     "Returns time formatted as indicated by \$fmt, or normal if blank.  /// SeeAlso: siteapi.core1.dateformats";
153 class Image
154 "Represents an image."
156     var readonly string url "URL of the image";
157     var int width "Width in pixels";
158     var int height "Height in pixels";
159     var string alttext "Default alternative text for image";
160     var readonly string{} extra "Extra params for img tag";
162     function builtin set_url (string url)
163     "Sets the URL, doing any necessary escaping.";
165     function print ()
166     "Print an HTML tag for this Image";
168     function print (string alttext)
169     "Print an HTML tag for this Image with given alttext";
171     function print (string{} opts)
172     "Print the HTML for an image, Supported keys are 'href' to create a link to the image source
173      and 'a_attr' which adds attributes to the anchor tag if a link is to be printed.";
175     function as_string () : string
176     "Return the HTML tag for this image";
178     function as_string (string alttext) : string
179     "Return an HTML tag for this Image with given alttext";
181     function as_string_ex (string alttext, string attributes) : string
182     "Return an HTML tag for this Image with given alttext and given additional HTML tag attributes";
184     function as_string (string{} opts) : string
185     "Return the HTML for an image, Supported keys are 'href' to create a link to the image source
186      and 'a_attr' which adds attributes to the anchor tag if a link is to be printed.";
189 class Link
190 "A link or button"
192     var readonly string url "URL which the link points to";
193     var readonly string caption "The caption for the link";
194     var Image icon
195     "A suggestion from the server as to which icon to use. layouts/users can override this of course.
196     alt text works similarly to [member[Link.caption]].";
198     function print_button
199     "Output this Link as a clickable button using [member[Link.icon]]";
201     function as_string() : string
202     "Return the button HTML link.";
204     function builtin print_raw() 
205     "Output additional link HTML code.";   
208 class ItemRange
209 "Represents a range of items which optionally contain items."
211   var bool all_subitems_displayed "True if the subitems in this range represent the entire set. In this case, all of the URL members are blank.";
212   var int num_subitems_displayed "The number of subitems in this range.";
213   var int total "The total number of items that are navigable to.";
214   var int current "The currently-active item.";
215   var int from_subitem "The index of the first subitem in this range.";
216   var int to_subitem "The index of the last subitem in this range.";
217   var int total_subitems "The number of subitems.";
218   var readonly string url_next "URL for the 'next' link.  Blank if there isn't a next URL.";
219   var readonly string url_prev "URL for the 'previous' link.  Blank if there isn't a previous URL.";
220   var readonly string url_first "URL for the 'first' link.  Blank if already on the first page.";
221   var readonly string url_last "URL for the 'last' link.  Blank if already on the last page.";
222   function builtin url_of(int n) : string "Returns the URL to use to link to the nth item";
224   function print () "Prints the item range links";
225   function print(string labeltext) "Prints the item range links with the given \$labeltext";
228 ### LJ Specific Classes
230 class CommentInfo
231 "Information about comments attached to something."
233     var readonly string read_url "URL pointer to the 'Read Comments' view.";
234     var readonly string read_spam_url "URL pointer to the 'Show/Hide Spam' view.";
235     var readonly string post_url "URL pointer to the 'Post Comments' view.";
236     var int count "Current number of comments available to be read by the viewer.";
237     var int spam_counter "Current number of spam comments.";
238     var bool screened "Set to true if there are screened comments and remote user can unscreen them.";
239     var bool enabled "Set to false if comments disabled journal-wide or just on this item.";
240     var bool locked "Set to true if no further comments can be left on this entry.";
241     var bool maxcomments "Set to true if entry has reached a comment maximum.";
242     var bool show_postlink "Indicates whether the Post Comment link for this entry should be shown.";
243     var bool show_readlink "Indicates whether the Read Comments link for this entry should be shown.";
245     function print
246     "Print all comment related links";
247     function print_readlink
248     "Print the formatted link to the 'Read Comments' view";
249     function print_postlink
250     "Print the formatted link to the 'Post Comments' view";
251     function print_spamlink
252     "Print the formatted link to the 'Show spam' view";
255 class UserLink
256 "A user-defined link to an outside resource."
258     var readonly bool is_heading "Is this link a heading or category name?  If so, it has no url and a list of children.";
259     var readonly string title "The title or label for the link";
260     var readonly string url "The url to which the link points";
261     var readonly UserLink[] children "Not Implemented: An array of child UserLink objects.";
264 class UserLite
265 "A 'lite' version of a [class[User]] which the system often has more readily-available than a full version."
267     var readonly string username "Canonical Username, ex: johnqpub.  Note that if journal_type is an external identity, there will be no username, so this field will be a display version of their URL, longer than 15 characters, and with characters other than a-z, 0-9 and underscore.";
268     var readonly string name "User's formatted name, ex: John Q. Public";
269     var readonly string journal_type "Type of account: P (personal), C (community), Y (syndicated), S (shared), I (external identity) etc";
271     var Link{} data_link "Links to various machine-readable data sources relating to this user";
272     var string[] data_links_order "An array of data views which can be used to order the data_link hash";
274     var string[] link_keyseq "Array of keys which can be passed into the get_link";
276     function builtin equals(UserLite u) : bool "Returns true if the two user objects refer to the same user. Use this rather than comparing usernames, since usernames aren't globally unique.";
277     function builtin ljuser() : string "Returns an LJ user tag for the user.";
278     function builtin ljuser(Color link_color) : string "Returns an LJ user tag for the user.  The color of the link will be link_color.";
279     function builtin get_link(string key) : Link "Returns a link based on the given key, or null if the link is unavailable";
280     function base_url () : string "Returns URL of user's journal.";
281     function tag_manage_url () : string "Returns URL to user's tag management page.";
282     function print_linkbar() "Print the link bar for this user/journal.";
283     function as_string() : string;
284     function print ();
287 class Tag
288 "Represents a tag in its most basic form."
290     var readonly string name "Textual representation of this tag.";
291     var readonly string url "URL to view entries with this tag.";
294 class TagDetail extends Tag
295 "A rich structure with lots of information about a Tag."
297     var readonly int use_count "Count of how many times this tag has been used.";
298     var readonly string visibility "The visibility level for this tag.  Based on the entries it's used on.  Can be one of: public, private, friends, group.";
299     var readonly int{} security_counts "How many times this tag has used this security.  The keys are which security, one of: public, private, friends, group.  The value is the count of times the tag is used on entries with that security level.";
302 class EntryLite
303 "Base class for both journal entries and comments."
305     var readonly string subject "Subject.  May contain HTML.  Don't do substring chops on this.";
307     var readonly string text
308         "Entry Text; Use [method[EntryLite.print_text()]] to print this so that the entry's trust level is not affected by your layer's trust level.";
311     var readonly bool text_must_print_trusted
312         "Indicates that this entry's text contains some content that must be printed trusted, with [method[EntryLite.print_text()]], rather than printed directly from an untrusted context. Use this to fall back to a plain trusted print if you are doing something unusual with [member[EntryLite.text]]. Most layers can just ignore this and always use [method[EntryLite.print_text()]].";
314     var DateTime time "The user-specified time of the post, or the GMT time if it's a comment.";
315     var DateTime system_time "The system time (in GMT) this entry or comment was posted.";
317     var UserLite poster "Author of the entry, or null if an anonymous comment";
318     var UserLite journal "Journal the entry has been posted to";
320     var readonly Tag[] tags "Array of tags applied to this entry.";
322     var Image userpic "The userpic selected to relate to this entry.";
324     var readonly string{} metadata "Post metadata. Keys: 'music', 'mood'";
326     var readonly string dom_id "The DOM 'id' attribute you should put on your outer-most element";
328     var readonly string permalink_url "A URL at which this specific entry can be viewed, for linking purposes.";
329     var int depth "Visual depth of entry.  Top-level journal entries are always depth zero.  Comments have a depth greater than or equal to one, depending on where the thread is rooted at.";
331     var string[] link_keyseq "An array of keys which you should pass to [method[EntryLite.get_link(string key)]] to produce an entry 'toolbar'. Does not contain nav_next and nav_prev for entries; you should retrieve those separately and put them somewhere appropriate for your layout.";
333     function print_text() [fixed]
334         "Print the entry text. Doesn't print anything in some contexts, such as on a month view or in a collapsed comment.";
335     function builtin get_link (string key) : Link "Get a link to some action related to this entry or comment. You can iterate over [member[EntryLite.link_keyseq]] to get keys to pass in here to produce a 'toolbar' of links.";
336     function builtin get_plain_subject () : string "For Entries that can contain HTML subjects, this returns the subject without HTML.  Comments can't have HTML in subjects, so this is equivalent to just using \$.subject.  The returned 'plain' subject may still contain HTML entities, so don't do substring chops on it either.";
337     function builtin get_tags_text () : string "Returns a string containing a div of class 'ljtags' with the tags for the entry.  If there are no tags on the entry, returns a blank string.  The string is formatted according to the 'text_tags' property.";
338     function print_linkbar() "Print the link bar for this entry or comment.";
339     function time_display () : string "Show the time of this post, with most useful information for user, and with tooltip for more.";
340     function time_display (string datefmt, string timefmt) : string "time_display, with customized date/time formats.";
341     function builtin formatted_subject (string{} opts) : string "formats subject - outputs subject as html-link, gets hash of attributes - class and(or) style ";
344 class Entry extends EntryLite
345 "A journal entry"
347     var readonly string security "The security level of the entry ('private', 'protected'), or blank if public.";
348     var Image security_icon "A little icon which should be displayed somewhere on an entry to represent the security setting";
350     var Image mood_icon "Mood icon, or null.";
351     var CommentInfo comments "Comment information on this entry";
353     var bool new_day "Is this entry on a different day to the previous one?";
354     var bool end_day "Is this the last entry of a day?";
356     var int itemid "Server stored ID number for this entry";
358     function print_metadata ();
359     function builtin plain_subject () : string
360         "Return entry's subject as plain text, with all HTML removed.";
362     function print_link_next() "Print the link to the next entry in this journal.";
363     function print_link_prev() "Print the link to the previous entry in this journal.";
364     function builtin viewer_sees_ebox() [fixed] : bool
365     "True if opaque horizontal site-specific content boxes between entries should be displayed to the user.";
366     function builtin print_ebox() [fixed]
367     "Prints a small horizontal bar of site-specific content between entries in a journal.";
369     function builtin is_eventrate_enable() : bool
370     "True if users can rate entry.";
371     function builtin get_eventratescounters() : int
372     "How many users rates this entry.";
373     function builtin get_eventrates() : UserLite[]
374     "Id list of users who rates this entry.";
375     function builtin is_myvoice() : bool
376     "Is my voice already counted by the system.";
379 class Comment extends EntryLite
380 "A comment to a journal entry, or to another comment."
382     var Image subject_icon "Subject icon, or null.";
383     var int talkid "Server stored ID number for this comment.";
384     var Comment[] replies "Comments replying to this comment.";
385     var bool full "True if all information is available for this comment.  False if only the subject, poster, and date are available.  (collapsed threads)";
386     var readonly string parent_url "URL to parent comment, or blank if a top-level comment.";
387     var readonly string reply_url "URL to reply to this comment.";
388     var readonly string thread_url "URL to view threaded rooted at this comment, or blank if comment has no children.";
389     var readonly bool screened "True if comment is in screened state.";
390     var readonly bool frozen "True if comment is in frozen state.";
391     var readonly bool deleted "True if comment has been deleted. Deleted comments still show up if they are the parent of a thread.";
392     var readonly string anchor "Direct link to comment, via HTML name anchors";
393     var readonly bool comment_posted "True if comment was just posted by the current user.";
394     var readonly bool edited "True if the comment has been edited.";
395     var readonly bool spam "This is spam message.";
397     var readonly DateTime time_remote "The local time the comment appeared, in the remote user's (reader's) timezone.  Or undef if no remote user, or remote user hasn't set their timezone.";
398     var readonly DateTime time_poster "The local time the comment appeared, in the commenter's timezone.  Or undef if anonymous comment, or commenter's timezone is unknown.";
399     var readonly int seconds_since_entry "The number of elapsed seconds from the time of the journal entry until the comment was initially posted.";
401     var readonly DateTime edittime "The GMT time the comment was edited.  Or undef if the comment hasn't been edited.";
402     var readonly DateTime edittime_remote "The local time the comment was edited, in the remote user's (reader's) timezone.  Or undef if no remote user, or remote user hasn't set their timezone, or the comment hasn't been edited.";
403     var readonly DateTime edittime_poster "The local time the comment was edited, in the commenter's timezone.  Or undef if anonymous comment, or commenter's timezone is unknown, or the comment hasn't been edited.";
405     function builtin print_multiform_check "Prints the select checkbox in CSS class 'ljcomsel' with DOM id 'ljcomsel_\$talkid' for a multi-action form started with [method[EntryPage.print_multiform_start()]].";
407     function builtin print_reply_link(string{} opts) "Prints a link to reply to the comment. You may specify the link text in the 'linktext' option, and the link CSS class in 'class'. You may also specify the url of an image to use as a button in 'img_url'.";
409     function builtin print_reply_container() "Prints the area in which the quickreply box will go. If no container is available, quickreply will not work.";
411     function builtin print_reply_container(string{} opts) "Prints the area in which the quickreply box will go. You may 'class' which will be the CSS class used by the container. If no container is available, quickreply will not work.";
413     function builtin expand_link () : string "Returns a link to expand a collapsed comment. Uses the value of the 'text_comment_expand' property as the text. Will not work in untrusted layers.";
414     function builtin expand_link (string{} opts) : string "Returns a link to expand a collapsed comment. Can pass options 'text', 'title', 'class', and 'img_url' (and other 'img_*' options). Will not work in untrusted layers.";
416     function builtin print_expand_link () : string "Prints a link to expand a collapsed comment. Uses the value of the 'text_comment_expand' property as the text.";
417     function builtin print_expand_link (string{} opts) : string "Prints a link to expand a collapsed comment. Can pass options 'text', 'title', 'class', and 'img_url' (and other 'img_*' options).";
419     function builtin print_expand_collapse_links () "Prints links to expand and to collapse this comment.";
421     function time_display (string datefmt, string timefmt, bool edittime) : string "Same as EntryLite::time_display, except can pass in if we want the edit time or not.";
422     function edittime_display () : string "Show the time that this comment was edited, with most useful information for user.  Empty string if the comment hasn't been edited.";
423     function edittime_display (string datefmt, string timefmt) : string "edittime_display, with customized date/time formats.";
425     function print_edit_text () "Print the text that says when this comment was edited.";
428 ### Userinfo
430 class Friend extends UserLite
431 "Represents a friends or friendof list"
433     var Color bgcolor "Background color selected for friend";
434     var Color fgcolor "Foreground color selected for friend";
437 class User extends UserLite
438 "A more information-rich userinfo structure"
440     var Image default_pic "Information about default userpic";
441     var readonly string userpic_listing_url "URL of a page listing this user's userpics";
442     var readonly string website_url "URL pointer to user's website";
443     var readonly string website_name "'pretty' name of user's website";
446 ### Other
448 class Redirector
449 "A redirector makes either a GET URL which redirects to a pretty URL or an HTML form which posts to a URL that redirects to a pretty URL.  This class exists because it's often desirable to use a form to end up at a URL, instead of doing a GET request.  It's also used in cases where finding the previous or next URL would incur database overhead which would be wasteful, considering most people don't click previous/next links.  Instead, the system will give you a Redirector object which has a URL that'll do the lookup for you later, followed by a redirect."
452     var readonly string user;
453     var readonly string vhost;
454     var readonly string type;
455     var readonly string url;
457     function start_form() "Starts an inline HTML form, then calls [method[Redirector.print_hiddens()]].  You can also make it yourself, using [member[Redirector.url]], if you need special form attributes.";
458     function print_hiddens() "Prints the necessary hidden elements for a form.  Called automatically by [method[Redirector.start_form()]].";
459     function end_form() "Prints a form close tag.";
461     function builtin get_url(string redir_key) "Returns a GET URL, given a redir_key";
464 ### Pages
466 class Page
467 "Base template for all views"
469     var readonly string view "The view type (recent, friends, archive, month, day, entry)";
470     var readonly string{} args
471     "Arguments from the URL's query string (after the question mark). S2 code can only access arguments starting with a period, and this period is not included in the hash key.";
473     var User journal "User whose journal is being viewed";
474     var readonly string journal_type "Journal type, ex: 'P' (personal), 'C' (community), etc.";
475     var readonly string base_url "The base URL of the journal being viewed.";
476     var readonly string{} view_url
477     "Links to top-level views where id equals the name of the view being linked to.
478     (if one of views == \$.view, already looking at that view)";
480     var readonly string[] views_order "An array of view identifiers which can be used to order the views hash.";
481     var readonly string head_content
482     "Extra tags supplied by the server to go in the <head> section of the output HTML document. Layouts
483     should include this in the head section if they are writing HTML.";
485     var readonly string stylesheet_url
486     "The URL to use in a link element for the server-supported external stylesheet to put stuff in it)";
488     var readonly string global_title
489     "A title selected by the user for their whole journal.";
491     var readonly string global_subtitle
492     "A sub-title selected by the user for their whole journal.";
494     var readonly UserLink[] linklist
495     "An array of UserLink objects defined by the user to be displayed on their journal.";
497     var readonly DateTime time
498     "A DateTime object filled with the time (GMT) when the page was created.";
500     var Link{} data_link "Links to various machine-readable data sources relating to this page";
501     var string[] data_links_order "An array of data views which can be used to order the data_link hash";
503     var bool showspam
504     "Set to true if need show only spam messages.";
505     
506     var bool view_my_games
507     "View my games on profilepage.";
509     function print
510     "The main entry point that LiveJournal calls. Layouts should override this to create HTML that's the
511     same for all view types, and use \$this->title, \$this->head and \$this->body to include view-specific
512     content into the template.";
514     function view_title : string
515     "Return a title for this particular page, such as \"Friends' Recent Entries\" for the friends view,
516     or a date for the day view. Should be overridden in i18n layers. Ideally, layout layers should never override
517     this.  See [method[Page.title()]].";
519     function title : string
520     "Return a relevant combination of [member[Page.global_title]] and [method[Page.view_title()]].  May be
521     overridden in layout layers or left untouched for the core layer to handle.";
523     function print_body
524     "Call from [method[Page.print()]] to render parts of the view that are specific to the view, eg print
525     the recent set of journal entries, recent friends entries, or rows of user information";
527     function print_head [fixed]
528     "Print server side supplied head content. This is a fixed function, so you can't override it. See
529     [method[Page.print_custom_head()]] if you want to supply custom head content.";
531     function builtin print_hbox_top [fixed]
532     "Prints an horizontal bar of site-specific content at the top of a journal.";
534     function builtin print_hbox_bottom [fixed]
535     "Prints an horizontal bar of site-specific content at the bottom of a journal.";
537     function builtin print_vbox [fixed]
538     "Prints an vertical bar of site-specific content on a journal.";
540     function builtin print_ad_box(string type) [fixed]
541     "Prints an side bar of site-specific content on a journal.";
543     function builtin print_ad(string type) [fixed]
544     "Deprecated function.  Use print_hbox_(top|bottom) or print_vbox from now on.";
546     function builtin print_control_strip [fixed]
547     "Prints a control strip for the user's convenience";
549     function print_custom_head
550     "Layers can override this to add extra HTML to the head section of the HTML document.
551     Note that layouts are not intended to override this method.";
553     function print_linklist
554     "Print the list of UserLink objects specified by the user.";
556     function print_entry(Entry e)
557     "Output a journal entry. Layouts should override this and the inherited versions in RecentPage, FriendsPage
558     and DayPage to change how entries display.";
560     function print_entry_poster(Entry e)
561     "Output a line of text which says who posted an entry (just \"user\", or \"user posting in somejournal\")";
563     function builtin get_latest_month() : YearMonth
564     "Returns information about the latest month the user posted (or the current month, if no posts), so that the page may include a mini-calendar or similar features.";
566     function builtin visible_tag_list() : TagDetail[]
567     "Returns an array of tags that the logged in user can see for the journal being viewed.";
569     function builtin print_reply_link(string{} opts) "Prints a link to reply to the comment. You may specify the link text in the 'linktext' option, the link CSS class in 'class', and the target container in the 'target' option. You may also specify the url of an image to use as a button in 'img_url'.";
571     function builtin print_reply_container(string{} opts) "Prints the area in which the quickreply box will go. Options you may specify are 'target' which will be the target id, and 'class' which will be the CSS class used by the container. If no container is available, quickreply will not work.";
573     function builtin print_show_spam_button()
574     "Show Spam Button.";
576     function print_stylesheets
577     "Prints all defined stylesheets, including default and user-defined ones.";
579     function builtin print_trusted(string key)
580     "Prints a trusted string by key.";
582     function builtin widget(string{} opts) : string
583     "Returns rendered widget body.";
585     function builtin render_widget(string{} opts)
586     "Prints rendered widget body.";
589 class TagsPage extends Page
590 "A detail page listing a user's tags."
592     var TagDetail[] tags "List of tags visible to the user viewing the page.";
595 class MessagePage extends Page
596 "A page showing an error or confirmation message."
598     var readonly string title "The title of the message.";
599     var readonly string message "The body of the message. Do not print this directly; use [method[MessagePage.print_body()]] instead.";
600     var Link{} links "An associative array of links to be displayed alongside this message. Iterate over [member[MessagePage.link_keyseq]] to find the keys.";
601     var string[] link_keyseq "A list of links, indicated by key, that should be displayed alongside this error. They should ideally be displayed in a similar way to the entry links displayed on the entry page.";
603     function print_message() [fixed] "Print the message. Call this rather than printing [member[MessagePage.message]] directly.";
604     function print_links() "Print the links from the [member[MessagePage.links]] and [member[MessagePage.link_keyseq]] members. Layouts will probably want to override this.";
607 class RecentNav
608 "Navigation position within a [class[RecentPage]] or [class[FriendsPage]] and URLs to move about."
610     var int version        "Currently version 1.  A new method of navigation has been frequently discussed, so this is planning for the future";
612     # version 1 attributes:
613     var int skip            "Indicates how many entries are being skipped back.";
614     var int count           "Indicates how many entries we're currently seeing";
615     var readonly string forward_url  "URL to go forward in time, or blank if furthest forward.";
616     var int forward_skip    "Number of items we'd be skipping going forward.";
617     var int forward_count   "Number of items we'd be potentially seeing going forward.";
618     var readonly string backward_url "URL to go backward in time, or blank if furthest back server will allow.";
619     var int backward_skip   "Number of items we'd be skipping going back more.";
620     var int backward_count  "Number of items we'd be potentially seeing going backward.";
623 class RecentPage extends Page
624 "Most recent entries page, formerly known as the LASTN view in the previous style system"
626     var Entry[] entries
627     "Array of entries available to be seen by the viewer of the page.";
629     var RecentNav nav;
632 class FriendsPage extends RecentPage
633 "Friends most recent entries"
635     var Friend{} friends
636     "A mapping from friend username to color association information.  There will only be keys for friends whose entries are in the entries array.";
638     var readonly string friends_title
639     "A user-selected title for their friends page.";
641     var string friends_mode
642     "The 'mode' of this view. An empty string indicates a normal friends view, while 'friendsfriends' indicates the Friends-of-friends view.";
644     var bool filter_active
645     "If true, some kind of filter is in effect. If this filter has a name, it will be included in [member[FriendsPage.filter_name]]";
647     var string filter_name
648     "The name of the filter in effect, if it has a name. This is only used when 'custom' [member[FriendsPage.filter_active]] is true.";
651 class DayPage extends Page
652 "View entries by specific day"
654     var Date prev_date "Previous day";
655     var Date next_date "Next day";
656     var readonly string prev_url "URL to previous day";
657     var readonly string next_url "URL to next day";
658     var bool has_entries "True if there are entries on the specified day";
659     var Entry[] entries "Array of entries available to be seen by the viewer of the page";
660     var Date date "Date of the current day";
663 ### Archive classes
665 class YearYear
666 "Information on how to link to a year in the year archive"
668     var int year "Number of the year, eg 2001.";
669     var readonly string url "URL to link to for this year.";
670     var bool displayed "If this is the year currently being displayed, this will be true.";
673 class YearDay
674 "Information on how to link to a day in the year archive"
676     var int day "Day of month number";
677     var Date date "Date of day";
679     # http://zilla.livejournal.org/show_bug.cgi?id=504
680     # var bool is_today "True if the day represents the current day.";
682     var int num_entries "Number of entries made on this day";
683     var readonly string url "A URL to view the day, if there are entries, else blank.";
686 class YearWeek
687 "Represents a week on the [class[YearMonth]] on the [class[YearPage]]."
689     var int pre_empty "How many days at the start of the week are blank? (From previous month)";
690     var int post_empty "How many days at the end of the week are blank? (From next month)";
691     var YearDay[] days "An array of the days of the week (0=sunday)";
693     function print()
694     "Print formatted week";
697 class YearMonth
698 "A month on the [class[YearPage]]."
700     var bool has_entries "If this is false, you probably don't want to display this month.";
701     var int month "The number of the month";
702     var int year "The number of the year";
703     var YearWeek[] weeks "An array of the weeks of the month (for ease of building a row-per-week calendar)";
704     var readonly string url "A url to link to in order to view this month.";
705     var readonly string prev_url "A url to link to in order to view the previous month.";
706     var readonly string next_url "A url to link to in order to view the next month.";
707     var Date prev_date "Date of previous month, with day of zero, or null if none.";
708     var Date next_date "Date of next month, with day of zero, or null if none.";
710     function builtin month_format () : string
711     "Returns month formatted long (February 1980)  /// SeeAlso: siteapi.core1.dateformats";
712     function builtin month_format (string fmt) : string
713     "Returns time formatted as indicated by \$fmt, or 'long' if blank.  /// SeeAlso: siteapi.core1.dateformats";
716 class YearPage extends Page
717 "Entire calendar page for a single year."
719     var int year "The year being viewed";
720     var YearYear[] years "Information for linking to other years";
721     var YearMonth[] months "12 months objects, even if no entries are in that month.";
723     function print_month(YearMonth m)
724     "Print the calendar cell for the given month";
726     function print_year_links()
727     "Print the navigation links to move between years";
730 class MonthDay extends YearDay
731 "Summaries of posts on a given day on the [class[MonthPage]]."
733     var bool has_entries "True if there are entries on this day.";
734     var Entry[] entries "Only populated on the month view.  Entry text not present.";
736     function print_subjectlist
737     "Print a list of entry summaries including subjects";
740 class MonthEntryInfo
741 "A month the user has journal entries, along with information to link to it."
743     var Date date "Date of month, with day of zero.";
744     var readonly string url "URL for the [class[MonthPage]] month view.";
745     var readonly string redir_key "The 'redir_key' parameter for a [class[Redirector]] instance.";
748 class MonthPage extends Page
749 "A page which contains a list of posts made in that month"
751     var Date date "Date of this month, with day of zero.";
752     var MonthDay[] days "One entry for each day of the month.";
753     var MonthEntryInfo[] months "Other months this journal has entries.";
754     var Date prev_date "Date of previous month, with day of zero, or null if none.";
755     var Date next_date "Date of next month, with day of zero, or null if none.";
756     var readonly string prev_url "URL of previous month, or empty string if none.";
757     var readonly string next_url "URL of next month, or empty string if none.";
758     var Redirector redir "Necessary to make a form which POSTs to a redirector";
761 class EntryPage extends Page
762 "A page with a single journal entry and associated comments."
764     var Entry entry "Journal entry being viewed";
765     var ItemRange comment_pages "Represents what comment page is being displayed.";
766     var Comment[] comments "Comments to journal entry, or at least some of them.";
767     var bool viewing_thread "True if viewing a specific sub-thread of the comments.  Style may which to hide the journal entry at this point, since the focus is the comments.";
769     function print_comments(Comment[] comments) "Prints comments";
770     function print_comment(Comment comment) "Prints a full comment";
771     function print_comment_full(Comment comment) "Prints a expanded comment";
772     function print_comment_partial(Comment comment) "Prints a collapsed comment";
774     var bool multiform_on "Set to true if the multi-action is to be printed, which requires both comments and applicable permissions for the remote user.";
775     function builtin print_multiform_start "Prints start of form tag and hidden elements to do a multi-comment action (multiple delete, screen, unscreen, etc...)";
776     function builtin print_multiform_end "Prints end of form tag to do a multi-comment action.";
777     function builtin print_multiform_actionline "Prints the line of the multiform giving instructions, options, and the submit button, using the text of the different \$*text_multiform_ properties.";
780 class ReplyForm
781 "This class will be used more in the future to set options on the reply form before
782 it's printed out by the system.  The system has to print it since it contains
783 sensitive information which can't be made available to S2."
785     var readonly bool subj_icons "Whether user has enabled subject icons or not.  Currently read-only until policy is decided on whether layers should be able to change it (rather than changing it in the user preferences)";
786     function builtin print() "Prints the reply form";
789 class ReplyPage extends Page
790 "A page to reply to a journal entry or comment"
792     var Entry entry "The journal entry for this talk page";
793     var EntryLite replyto "The object which is being replied to, either the entry or a comment";
794     var ReplyForm form "The reply form.";
797 class PalItem
798 "A specification for a numbered palette index in a GIF or PNG to be changed to a certain color"
800     var int index "Integer palette index.";
801     var Color color "Color to put at specified index.";
804 ##[ Built-in Functions ]
806 function builtin eurl (string s) : string
807 "URL escape";
809 function builtin ehtml (string s) : string
810 "Escapes all HTML tags and entities from the text";
812 function builtin etags (string s) : string
813 "Escapes all HTML tags (but not entities) from text";
815 function builtin clean_url (string s) : string
816 "Returns the given URL back if it's a valid URL.";
818 function builtin rand (int high) : int
819 "Returns a random integer between 1 and \$high, inclusive.";
821 function builtin rand (int low, int high) : int
822 "Returns a random integer between \$low and \$high, inclusive.";
824 function builtin pageview_unique_string () : string
825 "Returns a unique string for the remote user.";
827 function builtin alternate (string a, string b) : string
828 "With each call, this function will alternate between the two values and return one of them.
829 Useful for making tables whose rows alternate in background color.";
831 function builtin zeropad (int n, int digits) : string
832 "Returns the number padded with zeroes so it has the amount of digits indicated.";
834 function builtin zeropad (string n, int digits) : string
835 "Returns the number padded with zeroes so it has the amount of digits indicated.";
837 function builtin striphtml (string s) : string
838 "Similar to ehtml, but the HTML tags are stripped rather than escaped.";
840 function builtin viewer_logged_in() : bool
841 "Returns true if the user viewing the page is logged in. It's recommended that your page links to the site
842 login page if the user isn't logged in.";
844 function builtin viewer_is_owner() : bool
845 "Returns true if the user viewing the page is both logged in, and is the owner of the content in question.
846 Useful for returning links to manage information, or edit entries.";
848 function builtin viewer_is_friend() : bool
849 "Returns true if the user viewing the page is both logged in, and is a friend of the journal being viewed. Always returns false for communities, since they cannot have friends.";
851 function builtin viewer_is_member() : bool
852 "Returns true if the user viewing the page is both logged in, and is a member of the community being viewed. Always returns false for personal journals, since they cannot have members.";
854 function builtin viewer_sees_ads() [fixed] : bool
855 "Deprecated function.  Use viewer_sees_hbox_(top|bottom) or viewer_sees_vbox from now on.";
857 function builtin viewer_sees_vbox() [fixed] : bool
858 "True if opaque vertical site-specific content box should be displayed to the user.";
860 function builtin viewer_sees_hbox_top() [fixed] : bool
861 "True if opaque horizontal site-specific content box should be displayed to the user at the top of the page.";
863 function builtin viewer_sees_hbox_bottom() [fixed] : bool
864 "True if opaque horizontal site-specific content box should be displayed to the user at the bottom of the page.";
866 function builtin viewer_sees_ad_box(string type) [fixed] : bool
867 "True if opaque sidebar site-specific content box should be displayed to the user.";
869 function builtin viewer_sees_ebox() [fixed] : bool
870 "True if the user has selected to see opaque content boxes between entries.";
872 function builtin viewer_sees_control_strip [fixed] : bool
873 "Returns true if reader will see the built in control strip.";
875 function builtin control_strip_logged_out_userpic_css [fixed] : string
876 "Returns CSS for the userpic div in the logged out version of the control strip.";
878 function builtin control_strip_logged_out_full_userpic_css [fixed] : string
879 "Returns CSS for the loggedout_userpic div in the logged out version of the control strip.";
881 function builtin get_page () : Page
882 "Gets the top-level [class[Page]] instance that LiveJournal ran the [method[Page.print()]] method on.";
884 function builtin get_url(string user, string view) : string
885 "Returns a URL to the specified view for the specified user. Views use the same names as elsewhere. (recent, friends, archive, month, userinfo, usergames)";
887 function builtin get_url(UserLite user, string view) : string
888 "Returns a URL to the specified view for the specified user. Views use the same names as elsewhere. (recent, friends, archive, month, userinfo, usergames)";
890 function builtin string(int i) : string
891 "Return the given integer as a string";
893 function builtin int(string s) : int
894 "Convert the string to an integer and return";
896 function builtin set_content_type(string text)
897 "Set the HTTP Content-type response header (for example, if outputting XML). Must be called before printing any data.";
899 function builtin get_plural_phrase(int n, string prop) : string
900 "Picks the phrase with the proper plural form from those in the property \$prop, passing \$n to [function[lang_map_plural(int)]] to get the proper form for the current language, and then substituting the # character with \$n.  Also, returned string is HTML-escaped.";
902 function builtin weekdays() : int[]
903 "Integers representing the days of the week. This will start on Monday (2) or Sunday (1) depending on the property setting for start-of-week and go to Sunday (1) or Saturday (7)";
905 function builtin PalItem(int index, Color c) : PalItem
906 "Convenience constructor to make populating an array of PalItems (like in [function[palimg_modify(string,PalItem[])]]) easy.";
909 function builtin UserLite(string username) : UserLite
910 "Constructor for making a UserLite object from a username";
912 function builtin palimg_modify(string filename, PalItem[] items) : string
913 "Return a URL to the specified filename (relative to the palimg root) with its palette table altered, once for each provided [class[PalItem]].  Restrictions:  only 7 palette entries may be modified, and the PalItem indexes must be 0-15.";
915 function builtin palimg_tint(string filename, Color bright) : string
916     "Return a URL to the specified filename (relative to the palimg root) with its palette table tinted.  The given 'bright' color will be the new white, and darkest color remains black.";
918 function builtin palimg_tint(string filename, Color bright, Color dark) : string
919     "Return a URL to the specified filename (relative to the palimg root) with its palette table tinted.  The given 'bright' color will be the new white, and the given 'dark' color is the new black.";
921 function builtin palimg_gradient(string filename, PalItem start, PalItem end) : string
922     "Return a URL to the specified filename (relative to the palimg root) with its palette table made into a gradient.  All palette entries between the inclusive indexes of \$start and \$end will fade from the colors in \$start and \$end.  The palette indexes for the start and end can be between 0 and 255.";
924 function builtin set_handler(string eventname, string[][] commands);
926 function builtin userlite_base_url(UserLite ul) : string;
927 function builtin userlite_as_string(UserLite ul) : string "Deprecated function.  Use ljuser() from now on.";
929 function builtin start_css () "Declare that you're about to start printing out CSS that should be buffered, then later cleaned when you call end_css().  WARNING: this is not re-entrant.  You can't call start_css recursively.";
930 function builtin end_css () "Declare that you're done printing CSS and the output thus buffered should be cleaned and printed.";
932 function builtin journal_current_datetime() : DateTime
933 "Returns the current datetime in the timezone of the journal being viewed.";
935 function builtin style_is_active() : bool
936 "Returns if the style (layout and theme) calling it is active based on a hook.  If hook isn't defined, returns true always.";
938 ##[ properties ]
940 propgroup colors = "Colors";
941 propgroup fonts = "Fonts";
942 propgroup presentation = "Presentation";
943 propgroup other = "Other";
944 propgroup text = "Text";
945 propgroup background = "Background";
946 propgroup sidebar = "Sidebar";
947 propgroup appearance = "Appearance";
948 propgroup options = "Options";
949 propgroup images = "Images";
950 propgroup customcss = "Custom CSS";
952 property string lang_current {
953         noui = 1;
954         des = "Current language code.  So layouts can change date/time formats more safely if they want.";
956 set lang_current = "en";  # core is English.
958 property string lang_fmt_date_short {
959     noui = 1;
960     des = "Short date format.  All numeric.";
962 set lang_fmt_date_short = "%%m%%/%%d%%/%%yy%%";
964 property string lang_fmt_date_med {
965     noui = 1;
966     des = "Medium date format.  Abbreviated month name, no day of the week.";
968 set lang_fmt_date_med = "%%mon%%. %%dayord%%, %%yyyy%%";
970 property string lang_fmt_date_med_day {
971     noui = 1;
972     des = "Medium date format with day of week.  Abbreviated month name and abbreviated day of the week.";
974 set lang_fmt_date_med_day = "%%da%%, %%mon%%. %%dayord%%, %%yyyy%%";
976 property string lang_fmt_date_long {
977     noui = 1;
978     des = "Long date format.  With full month name, but no day of the week.";
980 set lang_fmt_date_long = "%%month%% %%dayord%%, %%yyyy%%";
982 property string lang_fmt_date_long_day {
983     noui = 1;
984     des = "Long date format.  With full month name and full day of the week.";
986 set lang_fmt_date_long_day = "%%day%%, %%month%% %%dayord%%, %%yyyy%%";
988 property string lang_fmt_time_short {
989     noui = 1;
990     des = "Time format.";
992 set lang_fmt_time_short = "%%hh%%:%%min%% %%a%%m";
994 property string lang_fmt_month_short {
995     noui = 1;
996     des = "Short month format.";
998 set lang_fmt_month_short = "%%m%%/%%yy%%";
1000 property string lang_fmt_month_med {
1001     noui = 1;
1002     des = "Medium month format.";
1004 set lang_fmt_month_med = "%%mon%% %%yyyy%%";
1006 property string lang_fmt_month_long {
1007     noui = 1;
1008     des = "Long month format.";
1010 set lang_fmt_month_long = "%%month%% %%yyyy%%";
1012 property string[] lang_monthname_long {
1013     noui = 1;
1014     des = "Months of the year.  Indexed from 1 (January) to 12 (December).";
1016 set lang_monthname_long = [ "", "January",  "February", "March",
1017                             "April", "May", "June",
1018                             "July", "August", "September",
1019                             "October", "November", "December" ];
1021 property string[] lang_monthname_short {
1022     noui = 1;
1023     des = "Months of the year, in their short forms.  Indexed from 1 (Jan) to 12 (Dec).";
1025 set lang_monthname_short = [ "", "Jan",  "Feb", "Mar",
1026                              "Apr", "May", "Jun",
1027                              "Jul", "Aug", "Sep",
1028                              "Oct", "Nov", "Dec" ];
1030 property string[] lang_dayname_long {
1031     noui = 1;
1032     des = "Days of the week.  Indexed from 1 (Sunday) to 7 (Saturday).";
1034 set lang_dayname_long = [ "", "Sunday", "Monday",  "Tuesday", "Wednesday",
1035                           "Thursday", "Friday", "Saturday" ];
1037 property string[] lang_dayname_short {
1038     noui = 1;
1039     des = "Days of the week, in their short forms.  Indexed from 1 (Sun) to 7 (Sat).";
1041 set lang_dayname_short = [ "", "Sun", "Mon",  "Tue", "Wed",
1042                            "Thu", "Fri", "Sat" ];
1044 property string[] lang_dayname_shorter {
1045     noui = 1;
1046     des = "Days of the week, in their one letter forms.  Indexed from 1 (S) to 7 (S).";
1048 set lang_dayname_shorter = [ "", "S", "M",  "T", "W",
1049                              "T", "F", "S" ];
1051 property builtin string IMGDIR {
1052     noui = 1;
1053     doc_flags = "[sys]";
1054     des = "The base URL of the current LiveJournal site's image directory, without a trailing slash.  Example: \"http://www.livejournal.com/img\".";
1056 property builtin string STATDIR {
1057     noui = 1;
1058     doc_flags = "[sys]";
1059     des = "The base URL of the current LiveJournal site's static files directory, without a trailing slash.  Example: \"http://www.livejournal.com/stc\".";
1061 property builtin string SITENAME {
1062     noui = 1;
1063     doc_flags = "[sys]";
1064     des = "Name of the current LiveJournal site.  Example: \"LiveJournal.com\".";
1067 property builtin string SITENAMESHORT {
1068     noui = 1;
1069     doc_flags = "[sys]";
1070     des = "Shorter name of the current LiveJournal site.  Example: \"LiveJournal\".";
1073 property builtin string SITENAMEABBREV {
1074     noui = 1;
1075     doc_flags = "[sys]";
1076     des = "Abbreviation of the current LiveJournal site.  Example: \"LJ\".";
1079 property builtin string SITEROOT {
1080     noui = 1;
1081     doc_flags = "[sys]";
1082     des = "The base URL of the current LiveJournal site, without a trailing slash.  Example: \"http://www.livejournal.com\".";
1085 property builtin string PALIMGROOT {
1086     noui = 1;
1087     doc_flags = "[sys]";
1088     des = "The base URL of palimg files, without a trailing slash.  Example: \"http://www.livejournal.com/palimg\".";
1091 property int page_recent_items {
1092     des = "Number of journal entries to show on recent entries page";
1093     doc_flags = "[construct]";
1094     min = 1;
1095     max = 50;
1097 property int page_friends_items {
1098     des = "Number of journal entries to show on friends page";
1099     doc_flags = "[construct]";
1100     min = 5;
1101     max = 50;
1103 set page_recent_items = 10;
1104 set page_friends_items = 10;
1106 property string page_day_sortorder {
1107     des = "Order of entries shown on a day page";
1108     values = "forward|Least recent first|reverse|Most recent first";
1110 property string page_year_sortorder {
1111     des = "Order of months shown on the year archive page";
1112     values = "forward|Least recent first|reverse|Most recent first";
1114 set page_day_sortorder = "forward";
1115 set page_year_sortorder = "forward";
1117 property bool page_month_textsubjects {
1118     des = "If set, subjects will be provided for the month view in plain text only, with HTML removed.";
1119     doc = "If set, subjects will be provided for the [class[MonthPage]] in plain text only, with HTML removed.";
1121 set page_month_textsubjects = true;
1123 property string text_meta_music {
1124     des = "Text for 'Current Music'";
1126 set text_meta_music = "Current Music";
1128 property string text_meta_mood {
1129     des = "Text for 'Current Mood'";
1131 set text_meta_mood = "Current Mood";
1133 property string text_meta_location {
1134     des = "Text for 'Current Location'";
1136 set text_meta_location = "Current Location";
1138 property string text_meta_groups {
1139     des = "Text for 'Custom Friends Groups'";
1141 set text_meta_groups = "Custom Friends Groups";
1143 property string text_copyr_agree {
1144     des = "Text for copyright post agree"; }
1145 set text_copyr_agree= "Hereinafter I give my permission to reproduce in any form";
1147 property string text_copyr_disagree {
1148     des = "Text for copyright post disagree"; }
1149 set text_copyr_disagree= "The reproduction is prohibited in any form";
1152 property string text_post_comment {
1153     des = "Link text to leave a comment";
1154     example = "Leave a comment";
1156 property string text_max_comments {
1157     des = "Text when entry has reached a comment maximum";
1158     example = "Maximum comments reached";
1160 property string text_read_comments {
1161     des = "Link text to read comments";
1162     format = "plurals";
1163     example = "1 comment // # comments";
1165 property string text_read_all_comments {
1166     des = "Link text to read all comments";
1167     format = "plurals";
1168     example = "Read 1 comment // Read # comments";
1170 set text_post_comment = "Leave a comment";
1171 set text_max_comments = "Maximum comments reached";
1172 set text_read_comments = "1 comment // # comments";
1173 set text_read_all_comments = "Read 1 comment // Read # comments";
1175 property string text_read_spam_comments {
1176     des = "Link text to read spam comments";
1177     format = "plurals";
1178     example = "1 spam comment // # spam comments";
1180 property string text_read_all_spam_comments {
1181     des = "Link text to read all spam comments";
1182     format = "plurals";
1183     example = "Read 1 spam comment // Read # spam comments";
1185 set text_read_spam_comments = "1 spam comment // # spam comments";
1186 set text_read_all_spam_comments = "Read 1 spam comment // Read # spam comments";
1188 property string text_post_comment_friends {
1189     des = "Link text to leave a comment on friends view entry";
1190     example = "Leave a comment";
1192 property string text_read_comments_friends {
1193     des = "Link text to read comments on a friends view entry";
1194     format = "plurals";
1195     example = "1 comment // # comments";
1197 set text_post_comment_friends = "Leave a comment";
1198 set text_read_comments_friends = "1 comment // # comments";
1200 property string text_showspam {
1201     des = "Show spam messages";
1202     example = "Show spam";
1204 property string text_hidespam {
1205     des = "Hide spam messages";
1206     example = "Hide  spam";
1208 set text_showspam = "Show spam";
1209 set text_hidespam = "Hide spam";
1211 property string text_spam_comment {
1212     des = "Spam comment messages";
1213     example = "Spam comment";
1215 set text_spam_comment = "(Spam comment)";
1217 property string text_view_games {
1218     des = "Text for view games on profile page";
1219     example = "My Games";
1221 set text_view_games = "My Games";
1223 property string text_tags_page_header {
1224     des = "Text for the header of the Tags page";
1225     example = "Visible Tags";
1227 set text_tags_page_header = "Visible Tags";
1229 property string text_tag_uses {
1230     des = "Text to show how many times a tag has been used";
1231     format = "plurals";
1232     example = "1 use // # uses";
1234 set text_tag_uses = "1 use // # uses";
1236 property string text_skiplinks_back {
1237     des = "Text to show in a link to skip back through entries";
1238     maxlength = 20;
1239     "size" = 15;
1240     example = "Go back #";
1241     note = "Include a # character to insert the number of entries that will be viewable when skipping back.";
1243 property string text_skiplinks_forward {
1244     des = "Text to show in a link to skip forward through entries";
1245     maxlength = 20;
1246     "size" = 15;
1247     example = "Go forward #";
1248     note = "Include a # character to insert the number of entries that will be viewable when skipping forward.";
1250 property string text_skiplinks_forward_words {
1251     des = "Text to show in a link to skip forward through entries";
1252     maxlength = 20;
1253     "size" = 15;
1254     example = "Go forward";
1256 set text_skiplinks_back="Previous #";
1257 set text_skiplinks_forward="Next #";
1259 property string text_view_recent {
1260     des = "Text used to link to the 'Recent Entries' view";
1261     maxlength = 20;
1262     "size" = 15;
1263     example = "Recent Posts";
1265 property string text_view_friends {
1266     des = "Text used to link to the 'Friends' view";
1267     maxlength = 20;
1268     "size" = 15;
1269     example = "My Friends' Entries";
1271 property string text_view_friends_comm {
1272     des = "Text used to link to the 'Friends' view for a community";
1273     maxlength = 20;
1274     "size" = 15;
1275     example = "Members' Journals";
1277 property string text_view_friendsfriends {
1278     des = "Title of the 'Friends of Friends' view";
1279     maxlength = 20;
1280     "size" = 15;
1281     example = "Friends of Friends";
1283 property string text_view_friends_filter {
1284     des = "Title of a Friends page with an unnamed filter in effect";
1285     maxlength = 20;
1286     "size" = 15;
1287     example = "Friends (Custom filter)";
1289 property string text_view_friendsfriends_filter {
1290     des = "Title of a Friends of Friends page with an unnamed filter in effect";
1291     maxlength = 20;
1292     "size" = 15;
1293     example = "Friends of Friends (Custom filter)";
1295 property string text_view_archive {
1296     des = "Text used to link to the 'Archive' view";
1297     maxlength = 20;
1298     "size" = 15;
1299     example = "Journal Archive";
1301 property string text_view_userinfo {
1302     des = "Text used to link to the 'User Information' view";
1303     maxlength = 20;
1304     "size" = 15;
1305     example = "My Profile";
1308 property string text_view_usergames {
1309     des = "Text used to link to the 'My Games' view";
1310     maxlength = 20;
1311     "size" = 15;
1312     example = "My Games";
1315 property string text_view_memories {
1316     des = "Text used to link to the 'Memories' view";
1317     maxlength = 20;
1318     "size" = 15;
1319     example = "My Memories";
1322 property string text_view_month {
1323     des = "Text used to link to a list of subjects for a month";
1324     maxlength = 20;
1325     "size" = 15;
1326     example = "View Subjects";
1328 set text_view_recent = "Recent Entries";
1329 set text_view_friends = "Friends";
1330 set text_view_friends_comm = "Members";
1331 set text_view_friends_filter = "Friends (Custom filter)";
1332 set text_view_friendsfriends = "Friends of Friends";
1333 set text_view_friendsfriends_filter = "Friends of Friends (Custom filter)";
1334 set text_view_archive = "Archive";
1335 set text_view_userinfo = "User Info";
1336 set text_view_usergames = "My Games";
1337 set text_view_memories = "Memories";
1338 set text_view_month = "View Subjects";
1340 property string text_links {
1341     des = "Text used  for the heading of the links list";
1342     maxlength = 50;
1343     size = 20;
1344     example = "Links";
1346 set text_links = "Links";
1348 property string text_page_summary {
1349     des = "Text used for the heading for the Page Summary navigation section";
1350     maxlength = 100;
1351     size = 30;
1352     example = "Entries on This Page";
1354 set text_page_summary = "Page Summary";
1356 property string text_syndicate {
1357     des = "Text used for the heading for the Syndication navigation section";
1358     maxlength = 50;
1359     size = 20;
1360     example = "Subscribe to my feeds";
1362 set text_syndicate = "Syndicate";
1364 property string text_nosubject {
1365     des = "Text to replace a subject line when no subject is specified";
1366     maxlength = 20;
1367     size = 10;
1368     example = "No Subject";
1369     note = "This only appears in places where a subject line is required, such as on a month view.";
1371 set text_nosubject = "(no subject)";
1373 property string text_noentries_recent {
1374     des = "Text to display when there are no entries on the recent or friends views";
1375     maxlength = 255;
1376     size = 50;
1378 set text_noentries_recent = "There are no entries to display.";
1380 property string text_noentries_day {
1381     des = "Text to display when there are no entries on the day view";
1382     maxlength = 255;
1383     size = 50;
1385 set text_noentries_day = "There were no entries on this day.";
1387 property string text_entry_prev {
1388     des = "Text to link to the previous entry";
1390 set text_entry_prev = "Previous Entry";
1392 property string text_entry_next {
1393     des = "Text to link to the next entry";
1395 set text_entry_next = "Next Entry";
1397 property string text_permalink {
1398     des = "Text for an entry's permanent link";
1399     maxlength = 50;
1400     size = 20;
1402 set text_permalink = "Link";
1404 property string text_edit_entry {
1405     des = "Text to edit an entry";
1407 set text_edit_entry = "Edit Entry";
1409 property string text_edit_tags {
1410     des = "Text to edit tags for an entry";
1412 set text_edit_tags = "Edit Tags";
1414 property string text_tell_friend {
1415     des = "Text to tell a friend about an entry";
1417 set text_tell_friend = "Tell a Friend";
1419 property string text_share_this {
1420     des = "Text to share this with a friend";
1422 set text_share_this = "Share this!";
1424 property string text_share {
1425     des = "Text to share this with a friend";
1427 set text_share = "Share";
1429 property string text_share_facebook {
1430     des = "Text to share this with a friend on Facebook";
1432 set text_share_facebook = "Post to Facebook";
1434 property string text_share_twitter {
1435     des = "Text to share this with a friend on Twitter";
1437 set text_share_twitter = "Tweet this";
1439 property string text_share_email {
1440     des = "Text to share this with a friend by mail";
1442 set text_share_email = "Send by e-mail";
1444 property string text_mem_add {
1445     des = "Text to add an entry into memories";
1447 set text_mem_add = "Add to Memories";
1449 property string text_watch_comments {
1450     des = "Text to track events on an entry";
1452 set text_watch_comments = "Track This";
1454 property string text_unwatch_comments {
1455     des = "Text to stop tracking events on an entry";
1457 set text_unwatch_comments = "Untrack This";
1459 property string text_flag {
1460     des = "Text to flag an entry";
1462 set text_flag = "Flag";
1464 property string text_month_screened_comments {
1465     des = "Text to indicate there are screened comments";
1467 set text_month_screened_comments = "w/ Screened";
1469 property string text_month_form_btn {
1470     des = "Text used for Submit button in MonthPage selector";
1472 set text_month_form_btn = "View";
1474 property string text_replyform_header {
1475     des = "Text for the heading above the form on the reply page";
1477 set text_replyform_header = "Comment Form";
1479 property string text_multiform_check {
1480     des = "Text beside a comment multi-action checkbox";
1482 set text_multiform_check = "Select:";
1484 property string text_multiform_des {
1485     des = "Text on the multiform action line.";
1487 set text_multiform_des = "Mass action on comments:";
1489 property string text_multiform_btn {
1490     des = "Text on the multiform action button.";
1492 set text_multiform_btn = "Perform Action";
1494 property string text_multiform_opt_spam {
1495     des = "Text for the comment mark as spam action";
1497 set text_multiform_opt_spam = "Spam";
1499 property string text_multiform_opt_unspam {
1500     des = "Text for the comment mark as unspam action";
1502 set text_multiform_opt_unspam = "Unspam";
1504 property string text_multiform_opt_unscreen {
1505     des = "Text for the comment unscreening action";
1507 set text_multiform_opt_unscreen = "Unscreen";
1509 property string text_multiform_opt_unscreen_to_reply {
1510     des = "Text for the comment unscreening action it's showed instead of reply-link for screened comments";
1512 set text_multiform_opt_unscreen_to_reply = "Unscreen to reply";
1513     
1514 property string text_multiform_opt_screen {
1515     des = "Text for the comment screening action";
1517 set text_multiform_opt_screen = "Screen";
1519 property string text_multiform_opt_unfreeze {
1520     des = "Text for the comment unfreezing action";
1522 set text_multiform_opt_unfreeze = "Unfreeze";
1524 property string text_multiform_opt_freeze {
1525     des = "Text for the comment freezing action";
1527 set text_multiform_opt_freeze = "Freeze";
1529 property string text_multiform_opt_delete {
1530     des = "Text for the comment delete action";
1532 set text_multiform_opt_delete = "Delete";
1534 property string text_multiform_opt_deletespam {
1535     des = "Text for the comment delete and mark as spam action";
1537 set text_multiform_opt_deletespam = "Delete as Spam";
1539 property string text_multiform_conf_delete {
1540     des = "Text for the confirming mass-delete action";
1542 set text_multiform_conf_delete = "Delete selected comments?";
1544 property string text_multiform_no_comments {
1545     des = "Text if no comments for mass-action are selected";
1547 set text_multiform_no_comments = "You have not selected any comments";
1549 property string text_multiform_no_action {
1550     des = "Text if no mass-action selected";
1552 set text_multiform_no_action = "You have not selected one of the actions";
1554 property string text_multiform_opt_track {
1555     des = "Text for the comment tracking action";
1557 set text_multiform_opt_track = "Track This";
1559 property string text_multiform_opt_untrack {
1560     des = "Text for the comment untracking action";
1562 set text_multiform_opt_untrack = "Untrack This";
1564 property string text_multiform_opt_edit {
1565     des = "Text for the comment editing action";
1567 set text_multiform_opt_edit = "Edit";
1569 property string text_multiform_opt_selected {
1570     des = "Text for drop-down: Sub-header for action on selected comments";
1572 set text_multiform_opt_selected = "Selected";
1574 property string text_multiform_opt_all {
1575     des = "Text for drop-down: Sub-header for action on all comments";
1577 set text_multiform_opt_all = "All";
1579 property string text_multiform_opt_please {
1580     des = "Text for drop-down: Invitation to choosing mass action on comments";
1582 set text_multiform_opt_please = "(Select action)";
1584 property string text_comment_posted {
1585     des = "Text to display when a comment has just been posted by the user";
1587 set text_comment_posted = "Comment successfully posted.";
1589 property string text_tags {
1590     des = "Text for 'Tags' header for an entry";
1591     note = "Use a # where you want the tag list inserted.";
1592     example = "Tags: #";
1594 set text_tags = "Tags: #";
1596 property string text_tags_section_header {
1597     des = "Text for the 'Tags' heading in the navigation area";
1598     maxlength = 50;
1599     size = 20;
1600     example = "My tags";
1602 set text_tags_section_header = "Tags";
1604 property string font_base {
1605     des = "Preferred Font";
1606     maxlength = 25;
1607     "size" = 10;
1608     example = "Arial";
1609     note = "Leave blank if you don't care.";
1611 set font_base = "";   # In core, default is not to care. Layouts will probably specify fonts the author likes instead.
1613 property string font_fallback {
1614     des = "Alternative font style";
1615     values = "sans-serif|Sans-serif|serif|Serif|cursive|Cursive|monospace|Monospaced|none|Use browser's default";
1616     note = "This general style will serve as a fallback if your preferred font is unavailable.";
1618 set font_fallback = "none"; # Default in core is to let the browser handle it.
1620 property string reg_firstdayofweek {
1621     des = "The day of the week the calendar weeks starts on.";
1622     doc = "The day of the week the calendar weeks starts on.  Either 'sunday' or 'monday'.";
1623     doc_flags = "[construct]";
1624     values = "sunday|Sunday|monday|Monday";
1626 set reg_firstdayofweek = "sunday";
1628 property string text_day_prev {
1629     des = "Text to link to the previous day";
1630     example = "Previous Day";
1631     maxlength = 20;
1633 property string text_day_next {
1634     des = "Text to link to the next day";
1635     example = "Next Day";
1636     maxlength = 20;
1638 set text_day_prev = "Previous Day";
1639 set text_day_next = "Next Day";
1641 property string text_comment_from {
1642     des = "Text of the 'from' header in comments";
1643     example = "From:";
1644     maxlength = "20";
1646 property string text_comment_date {
1647     des = "Text of the 'date' header in comments";
1648     example = "Date:";
1649     maxlength = "20";
1651 property string text_comment_ipaddr {
1652     des = "Text of the 'IP Address' header in comments";
1653     example = "IP Address:";
1654     maxlength = "20";
1656 property string text_comment_edittime {
1657     des = "Text of the 'Edited at' string at the bottom of edited comments";
1658     example = "Edited at:";
1659     maxlength = "20";
1661 set text_comment_from = "From:";
1662 set text_comment_date = "Date:";
1663 set text_comment_ipaddr = "IP Address:";
1664 set text_comment_edittime = "Edited at";
1666 property string text_comment_reply {
1667     des = "Text to link to reply for comment";
1668     example = "Reply to this";
1669     maxlength = "50";
1671 property string text_comment_frozen {
1672     des = "Text to replace reply link with if comment is frozen";
1673     example = "Replies frozen";
1674     maxlength = "50";
1676 property string text_comment_deleted {
1677     des = "Text to replace comment body, if it is deleted";
1678     example = "(Deleted)";
1679     maxlength = "50";
1681 property string text_comment_screened {
1682     des = "Text to replace comment body, if it is screened";
1683     example = "(Screened)";
1684     maxlength = "50";
1686 property string text_comment_parent {
1687     des = "Text to link to parent comment of current comment";
1688     example = "Parent";
1689     maxlength = "50";
1691 property string text_comment_thread {
1692     des = "Text to link to the thread stemming from the comment";
1693     example = "Thread";
1694     maxlength = "50";
1696 property string text_comment_expand {
1697     des = "Text to expand a collapsed comment thread";
1698     example = "Expand";
1699     maxlength = "50";
1701 property string text_comment_collapse {
1702     des = "Text to collapse a expanded comment thread";
1703     example = "Collapse";
1704     maxlength = "50";
1706 set text_comment_reply = "Reply";
1707 set text_comment_frozen = "Frozen";
1708 set text_comment_deleted = "(Deleted comment)";
1709 set text_comment_screened = "(Screened comment)";
1710 set text_comment_parent = "Parent";
1711 set text_comment_thread = "Thread";
1712 set text_comment_expand = "Expand";
1713 set text_comment_collapse = "Collapse";
1715 property string text_poster_anonymous {
1716     des = "The placeholder used when something is posted by an anonymous user";
1717     example = "(Anonymous)";
1719 set text_poster_anonymous = "(Anonymous)";
1721 property string text_reply_back {
1722     des = "Text to link back to the single entry view from the read comments page";
1723     example = "Read Comments";
1724     maxlength = "50";
1726 set text_reply_back = "Read Comments";
1728 property string text_reply_nocomments_header {
1729     des = "Heading text that explains that comments are disabled";
1730     example = "Comments Disabled:";
1731     maxlength = "50";
1733 set text_reply_nocomments_header = "Comments Disabled:";
1735 property string text_reply_nocomments {
1736     des = "Text that explains that comments are not allowed for this post.";
1737     example = "Comments have been disabled for this post.";
1738     maxlength = "100";
1740 set text_reply_nocomments = "Comments have been disabled for this post.";
1742 property string text_website_default_name {
1743     des = "If an account's website is specified, but there's no website name, use this text instead";
1744     noui = 1;
1746 set text_website_default_name = "My Website";
1748 property string text_icon_alt_protected {
1749     des = "Alternative text for icons of friends-only entries";
1750     noui = 1;
1752 property string text_icon_alt_private {
1753     des = "Alternative text for icons of private entries";
1754     noui = 1;
1756 property string text_icon_alt_groups {
1757     des = "Alternative text for icons of custom friends group entries";
1758     noui = 1;
1760 set text_icon_alt_protected = "[protected post]";
1761 set text_icon_alt_private = "[private post]";
1762 set text_icon_alt_groups = "[custom friends groups post]";
1764 property string comment_form_text_hint {
1765     des = "Text hint for comment message";
1768 property bool use_shared_pic {
1769     des = "Use community userpics instead of poster's userpic.";
1771 set use_shared_pic = false;
1773 property bool view_entry_disabled {
1774     des = "Disable customized comment pages for your journal";
1776 set view_entry_disabled = false;
1778 property bool tags_aware {
1779     des = "When enabled, style is responsible for handling tags.  When disabled, tags are automatically inserted into entry bodies.";
1780     noui = 1;
1782 set tags_aware = false;
1784 property Color color_comment_bar {
1785    des = "Color of comment bar header";
1787 set color_comment_bar = "#d0d0ff";
1789 property string comment_userpic_style {
1790    des = "Userpic display style for comments";
1791    doc = "Userpic display style for comments.  Either '' for full, 'small' for small, or 'off' for none.";
1792    doc_flags = "[construct]";
1793    values = "|Full|small|Small|off|Off";
1795 set comment_userpic_style = "";
1797 property bool linklist_support {
1798     des = "Display link list";
1800 set linklist_support = true;
1802 property bool external_stylesheet {
1803     des = "Use linked stylesheet";
1804     note = "If true, a stylesheet link element will point to a file containing the layout's CSS data.";
1805     noui = 1;
1807 set external_stylesheet = false;
1809 property bool include_default_stylesheet {
1810     des = "Use layout's stylesheet(s)";
1811     note = "Disable this only if you want to re-style this layout completely from scratch using a custom stylesheet.";
1813 set include_default_stylesheet = true;
1815 property string linked_stylesheet {
1816     des = "Custom external stylesheet URL";
1817     note = "If you have a custom external stylesheet that you'd like to use, enter its URL here.";
1819 set linked_stylesheet = "";
1821 property string custom_css {
1822     des = "Custom stylesheet";
1823     note = "If you'd like to add custom CSS to this style, enter it here.";
1824     cols = 50;
1825     rows = 20;
1826     string_mode = "css";
1828 set custom_css = "";
1830 property string custom_control_strip_colors {
1831     des = "Navigation strip colors";
1832     values = "off|Do not use custom colors|on_gradient|Use custom colors with a background gradient|on_no_gradient|Use custom colors without a background gradient";
1833     note = "Custom colors can be set in the \"Colors\" section.";
1835 set custom_control_strip_colors = "off";
1837 property Color control_strip_bgcolor {
1838     des = "Background color of navigation strip";
1839     note = "If you don't enable custom navigation strip colors in the \"Presentation\" section, this won't have any effect.";
1841 set control_strip_bgcolor = "";
1843 property Color control_strip_fgcolor {
1844     des = "Text color of navigation strip";
1845     note = "If you don't enable custom navigation strip colors in the \"Presentation\" section, this won't have any effect.";
1847 set control_strip_fgcolor = "";
1849 property Color control_strip_bordercolor {
1850     des = "Border color of navigation strip";
1851     note = "If you don't enable custom navigation strip colors in the \"Presentation\" section, this won't have any effect.";
1853 set control_strip_bordercolor = "";
1855 property Color control_strip_linkcolor {
1856     des = "Link color of navigation strip";
1857     note = "If you don't enable custom navigation strip colors in the \"Presentation\" section, this won't have any effect.";
1859 set control_strip_linkcolor = "";
1861 property Color theme_bgcolor {
1862     des = "Background color set by theme";
1863     noui = 1;
1865 set theme_bgcolor = "";
1867 property Color theme_fgcolor {
1868     des = "Text color set by theme";
1869     noui = 1;
1871 set theme_bgcolor = "";
1873 property Color theme_bordercolor {
1874     des = "Border color set by theme";
1875     noui = 1;
1877 set theme_bordercolor = "";
1879 property Color theme_linkcolor {
1880     des = "Link color set by theme";
1881     noui = 1;
1883 set theme_linkcolor = "";
1885 ### I like it
1886 property string text_we_like_this {
1887     des="Hint for “View all” link";
1888     noui = 1;
1890 set text_we_like_this = "View people who like it";
1892 property string text_i_like_this {
1893     des="Hint for “+1” link";
1894     noui = 1;
1896 set text_i_like_this = "Please click Track this if you want to receive all comments to this entry";
1898 ###[ global function implementations ]
1900 function prop_init ()
1901   "This function is the first thing called and is the place to set properties based on the values of other properties.  It's called before the style system looks at its builtin properties, so if you need to conditionally setup something based on your own custom properties, do it here.  You can't print from this function."
1903   # do nothing, just exist.
1906 function print_stylesheet ()
1907   "Prints a stylesheet, the URL of which can be referenced by [member[Page.stylesheet_url]].  This is another S2 entry point, in addition to [method[Page.print()]]." {
1910 function print_custom_control_strip_css ()
1911   "Prints the CSS for custom control strip colors, if the option is enabled. This should be called by print_stylesheet()."
1913     if ($*custom_control_strip_colors != "off") {
1914         var bool bgcolor = $*control_strip_bgcolor.as_string != "";
1915         var bool fgcolor = $*control_strip_fgcolor.as_string != "";
1916         var bool bordercolor = $*control_strip_bordercolor.as_string != "";
1917         var bool linkcolor = $*control_strip_linkcolor.as_string != "";
1918         var string bgimage = "";
1920         if ($*custom_control_strip_colors == "on_gradient" and $bgcolor and $fgcolor) {
1921             var Color blended_color = $*control_strip_bgcolor->blend($*control_strip_fgcolor, 25); # Make the background color 25% like the text color
1922             $bgimage = palimg_tint("controlstrip/bg.gif", $*control_strip_bgcolor, $blended_color);
1923         }
1925         if ($bgcolor) {
1928 #lj_controlstrip {
1929     background-color: $*control_strip_bgcolor;
1930 """;
1931 if ($bgimage == "") {
1932     """    background-image: none;""";
1933 } else {
1934     """    background-image: url($bgimage);""";
1937 }""";
1938         }
1940         if ($fgcolor or $bordercolor) {
1943 #lj_controlstrip td {
1944 """;
1945 if ($fgcolor) {
1946     """    color: $*control_strip_fgcolor;""";
1948 if ($fgcolor and $bordercolor) {
1949     "\n";
1951 if ($bordercolor) {
1952     """    border-bottom: 1px solid $*control_strip_bordercolor;""";
1955 }""";
1956         }
1958         if ($fgcolor) {
1961 #lj_controlstrip_statustext {
1962     color: $*control_strip_fgcolor;
1963 }""";
1964         }
1966         if ($linkcolor) {
1969 #lj_controlstrip a {
1970     color: $*control_strip_linkcolor;
1971 }""";
1972         }
1974         if ($bordercolor) {
1977 #lj_controlstrip_user,
1978 #lj_controlstrip_actionlinks,
1979 #lj_controlstrip_search,
1980 #lj_controlstrip_login,
1981 #lj_controlstrip_loggedout_userpic {
1982     border-right: 1px solid $*control_strip_bordercolor;
1985 #lj_controlstrip_login td {
1986     border-bottom: 0;
1989 #lj_controlstrip td td {
1990     border-bottom: 0;
1991 }""";
1992         }
1994         if ($bgcolor and $fgcolor) {
1995             print control_strip_logged_out_userpic_css();
1996             print control_strip_logged_out_full_userpic_css();
1997         }
1998     }
2001 function builtin htmlattr(string name, string value) : string
2002 "If the value isn't blank, return in HTML attribute format with a leading space.  HTML of name is not escaped.";
2004 function builtin htmlattr(string name, int value) : string
2005 "If the value isn't blank, return in HTML attribute format with a leading space.  HTML of name is not escaped.";
2007 ### Language
2009 function lang_map_plural (int n) : int {
2010     if ($n == 1) { return 0; } # singular
2011     return 1;             # plural
2014 function lang_page_of_pages (int pg, int pgs) [notags] : string {
2015     return "Page $pg of $pgs";
2018 function lang_user_wrote(UserLite u) : string "Returns text describing that the user wrote something. i18nc layers should override this." {
2019     if (defined $u) {
2020         return $u->as_string()+" wrote";
2021     }
2022     else {
2023         return "An anonymous user wrote";
2024     }
2027 function lang_at_datetime(DateTime d) : string "Returns a string saying \"at {the data and time given}\". Used in the core implementation of EntryPage and ReplyPage. i18nc layers should override this." {
2028     return "on " + $d->date_format("long") + " at " + $d->time_format();
2031 function lang_ordinal(int num) [notags] : string
2032 "Make an ordinal number from a cardinal number"
2034     if ($num % 100 >= 4 and $num % 100 <= 20) { return $num+"th"; }
2035     if ($num % 10 == 1) { return $num+"st"; }
2036     if ($num % 10 == 2) { return $num+"nd"; }
2037     if ($num % 10 == 3) { return $num+"rd"; }
2038     return $num+"th";
2041 function lang_ordinal(string num) [notags, fixed] : string
2042 "Make an ordinal number from a cardinal number.  Don't override this, since the core layer implementation just calls [function[lang_ordinal(int)]], which i18nc layers should override."
2044     return lang_ordinal(int($num));
2048 function lang_viewname(string viewid) [notags] : string
2049 "Get some words representing a view"
2051     if ($viewid == "recent") { return $*text_view_recent; }
2052     if ($viewid == "archive") { return $*text_view_archive; }
2053     if ($viewid == "friends") { return $*text_view_friends; }
2054     if ($viewid == "day") { return "Day"; }
2055     if ($viewid == "month") { return "Month"; }
2056     if ($viewid == "userinfo") { return $*text_view_userinfo; }
2057     if ($viewid == "usergames") { return $*text_view_usergames; }
2058     if ($viewid == "entry") { return "Read Comments"; }
2059     if ($viewid == "reply") { return "Post Comment"; }
2060     if ($viewid == "tags") { return "Tags"; }
2061     return "Unknown View";
2064 function builtin get_remote_lang() : string
2065 "Returns remote's language";
2067 function lang_metadata_title(string which) [fixed] : string
2068 "Get a human-readable caption for a metadata key. Layers shouldn't override this, but should instead set the relevant string properties."
2070     if ($which == "music") {
2071         return $*text_meta_music;
2072     }
2073     elseif ($which == "mood") {
2074         return $*text_meta_mood;
2075     }
2076     elseif ($which == "location") {
2077         return $*text_meta_location;
2078     }
2079     elseif ($which == "groups") {
2080         return $*text_meta_groups;
2081     }
2082     else {
2083         return $which;
2084     }
2087 ### Navigation
2090 ### Image Manipulation
2092 function Image::as_string(string{} opts) [fixed] : string {
2093     var string img = "";
2094     if ($opts{"href"} != "") { $img = $img + "<a href=\"" + eurl($opts{"href"}) + "\" $opts{"a_attr"}>"; }
2095     $img = $img + $this->as_string($opts{"alt"});
2096     if ($opts{"href"} != "") { $img = $img + "</a>"; }
2098     return $img;
2101 function Image::as_string(string alttext) [fixed] : string {
2102     return $this->as_string_ex($alttext, "");
2105 function Image::as_string_ex(string alttext, string attributes) [fixed] : string {
2106     var string extraParams = "";
2108     foreach var string extraKey ($.extra) {
2109         $extraParams = $extraParams + """$extraKey="$.extra{$extraKey}" """;
2110     }
2112     if ($.height) { $extraParams = $extraParams + """height="$.height" """; }
2113     if ($.width)  { $extraParams = $extraParams +  """width="$.width" """; }
2115     $extraParams = $extraParams + $attributes;
2117     return "<img src=\"$.url\" title=\"\" alt=\"" + ehtml($alttext) + "\"" +
2118         $extraParams + " />";
2121 function Image::as_string() [fixed] : string {
2122     return $this->as_string($this.alttext);
2125 function Image::print (string{} opts)
2127     # must do safe here because opts could have the 'a_attr' key set
2128     print safe $this->as_string($opts);
2131 function Image::print (string alttext)
2133     print $this->as_string($alttext);
2136 function Image::print
2138     print $this->as_string($this.alttext);
2141 function userinfoicon(UserLite user) : Image {
2142     var Image uimage;
2143     $uimage.width = 16;
2144     $uimage.height = 16;
2146     if ($user.journal_type == "C") {
2147         $uimage->set_url("$*IMGDIR/community.gif");
2148     } elseif ($user.journal_type == "Y") {
2149         $uimage->set_url("$*IMGDIR/syndicated.gif");
2150     } elseif ($user.journal_type == "N") {
2151         $uimage->set_url("$*IMGDIR/newsinfo.gif");
2152     } elseif ($user.journal_type == "I") {
2153         $uimage->set_url("$*IMGDIR/openid-profile.gif");
2154     } else {
2155         $uimage->set_url("$*IMGDIR/userinfo.gif");
2156         $uimage.width = 17;
2157         $uimage.height = 17;
2158     }
2159     return $uimage;
2162 ### UserLite functions
2164 function UserLite::base_url() [fixed] : string {
2165     return userlite_base_url($this);
2168 function UserLite::tag_manage_url() [fixed] : string {
2169     return "$*SITEROOT/manage/tags.bml?authas=$.username";
2172 function UserLite::as_string() [fixed] : string {
2173     return $this->ljuser();
2176 function UserLite::print() {
2177     print $this->as_string();
2180 function UserLite::print_linkbar() {
2181     var Link link;
2182     foreach var string k ($.link_keyseq) {
2183         $link = $this->get_link($k);
2184         " $link ";
2185     }
2188 function Friend::print() {
2189     print $this->as_string();
2192 ### Generic Page Functions
2194 function Page::print_stylesheets() {
2195     if ($*include_default_stylesheet) {
2196         if ($*external_stylesheet) {
2197             println safe """<link rel="stylesheet" href="$.stylesheet_url" type="text/css" />""";
2198         }
2199         else {
2200             println """<style type="text/css">""";
2201             start_css();
2202             print_stylesheet();
2203             end_css();
2204             println """</style>""";
2205         }
2206     }
2208     if ($*linked_stylesheet != "") {
2209         println safe """<link rel="stylesheet" href="$*linked_stylesheet" type="text/css" />""";
2210     }
2212     if ($*custom_css != "") {
2213         println """<style type="text/css">""";
2214         start_css();
2215         println safe $*custom_css;
2216         end_css();
2217         println """</style>""";
2218     }
2221 function Page::view_title() [notags] : string {
2222     return lang_viewname($.view);
2224 function FriendsPage::view_title() : string {
2225     if ($.friends_mode == "") {
2226         if ($.filter_active) {
2227             if ($.filter_name != "") {
2228                 if ($.friends_title != "") {
2229                     return $.friends_title+" ("+$.filter_name+")";
2230                 } else {
2231                     return $*text_view_friends+" ("+$.filter_name+")";
2232                 }
2233             } else {
2234                 return $*text_view_friends_filter;
2235             }
2236         } else {
2237             if ($.friends_title != "") {
2238                 return $.friends_title;
2239             } elseif ($.journal.journal_type == "C") {
2240                 return $*text_view_friends_comm;
2241             } else {
2242                 return $*text_view_friends;
2243             }
2244         }
2245     }
2246     elseif ($.friends_mode == "friendsfriends") {
2247         if ($.filter_active) {
2248             if ($.filter_name != "") {
2249                 return $*text_view_friendsfriends+" ("+$.filter_name+")";
2250             }
2251             else {
2252                 return $*text_view_friendsfriends_filter;
2253             }
2254         }
2255         else {
2256             return $*text_view_friendsfriends;
2257         }
2258     }
2259     else {
2260         return "Unknown Friends View";
2261     }
2263 function DayPage::view_title : string {
2264     return $.date->date_format("long");
2266 function YearPage::view_title() : string {
2267     return string($.year);
2269 function EntryPage::view_title() : string {
2270     return $.entry.subject ? $.entry->get_plain_subject() : "";
2272 function ReplyPage::view_title() : string {
2273     return "Post a comment";
2275 function Page::title() [notags] : string {
2276     var string title = $.global_title;
2277     if ($title == "") {
2278         $title = $.journal.name;
2279     }
2280     if ($.view != "recent") {
2281         $title = $title + " - " + $this->view_title();
2282     }
2283     if ($.view == "friends") {
2284         $title = $this->view_title();
2285     }
2286     return $title;
2289 function server_sig() {
2290     """Powered by <a href="$*SITEROOT/">$*SITENAME</a>""";
2293 function Page::print() {
2294     "<html>\n<head>\n";
2295     """<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7; IE=EmulateIE9" /> """;
2296     $this->print_head();
2297     $this->print_stylesheets();
2298     "<title>"+$this->title()+"</title>\n";
2300     if ($*font_base != "" or $*font_fallback != "none") {
2301         """<style type="text/css">""";
2302         start_css();
2303         """
2304 <!--
2305 body, td, th, table, p, div {
2306     font-family: """;
2307         if ($*font_base != "") {
2308             "\"$*font_base\"";
2309             if ($*font_fallback != "none") {
2310                 ", ";
2311             }
2312         }
2313         if ($*font_fallback != "none") {
2314             print $*font_fallback;
2315         }
2316         ";\n}\n-->";
2317         end_css();
2318         "</style>\n";
2319     }
2321     "</head>\n<body>\n";
2322     $this->print_control_strip();
2323     if (defined $.journal.default_pic and
2324         $.view != "entry" and
2325         $.view != "reply") {
2326             $.journal.default_pic->print();
2327     }
2328     print "<h1>" + $this->title() + "</h1>\n";
2330     $this->print_body();
2331     """\n<hr><address>""";
2332     server_sig();
2333     """</address>\n</body></html>""";
2336 function Page::print_body() {
2337     """<h2>No Default Renderer</h2><p>There is no body renderer for viewtype <tt>$.view</tt> defined.</p>""";
2340 function Page::print_head() {
2341     print $.head_content;
2342     $this->print_custom_head();
2345 function Page::print_custom_head() {
2346     # blank
2349 function Page::print_linklist() {
2350     if (size $.linklist <= 0) {
2351         return;
2352     } elseif (not $*linklist_support) {
2353         return;
2354     }
2356     foreach var UserLink l ($.linklist) {
2357         if ($l.title) {
2358             if ($l.is_heading) {
2359                 "<b>$l.title</b>";
2360             } else {
2361                 "<a href='$l.url'>$l.title</a>";
2362             }
2363         }
2364         "<br />";
2365     }
2368 function Page::print_entry_poster(Entry e) {
2369     $e.poster->print();
2370     if ($.view == "friends" and not $e.poster->equals($e.journal)) {
2371         " posting in ";
2372         $e.journal->print();
2373     }
2375 function Page::print_entry(Entry e) {
2376     ## For most styles, this will be overridden by FriendsPage::print_entry and such.
2377     """<div class="entry" id="$e.dom_id">\n""";
2378     "<h3>$e.security_icon $e.subject</h3>\n";
2379     if ($.view == "friends" or $e.poster.username != $e.journal.username) {
2380         "<div>"; $this->print_entry_poster($e); "</div>";
2381     }
2382     """<div class="entrytext">""";
2383     $e->print_text();
2384     """</div>\n""";
2385     $e->print_metadata();
2386     if ($e.comments.enabled) {
2387         $e.comments->print();
2388     }
2389     "</div>\n\n";
2391 function FriendsPage::print_entry(Entry e) {
2392     ## For most styles, this will be overridden by FriendsPage::print_entry and such.
2393     """<div class="entry" id="$e.dom_id">\n""";
2394     var Color bg;
2395     var Color fg;
2396     $bg = $.friends{$e.journal.username}.bgcolor;
2397     $fg = $.friends{$e.journal.username}.fgcolor;
2398     "<h3>";
2399     $this->print_entry_poster($e);
2400     "$e.security_icon $e.subject";
2401     "</h3>\n";
2402     """<div style="color: """ + $fg + "; background: " + $bg + """ none;">"""; $this->print_entry_poster($e); "</div>";
2403     """<div class="entrytext">"""; $e->print_text(); """</div>""";
2404     $e->print_metadata();
2405     if ($e.comments.enabled) {
2406         $e.comments->print();
2407     }
2408     "</div>\n\n";
2411 function Comment::print_edit_text() {
2412     if ($this.edited) {
2413         print "<br /><br /><span class='ljedittime'><em>$*text_comment_edittime " + $this->edittime_display() + "</em></span>";
2414     }
2417 function EntryLite::print_text() [fixed] {
2418     print $.text;
2420     if ($this isa Comment) {
2421         var Comment c = $this as Comment;
2422         $c->print_edit_text();
2423     }
2426 function Entry::print_metadata() {
2427     if (size $.metadata) {
2428         """<div class="metadata">\n""";
2429         foreach var string m ($.metadata) {
2430             "<div><strong>$m</strong>: ";
2431             if ($m == "mood") {
2432                 " $.mood_icon ";
2433             }
2434             print $.metadata{$m}; "</div>\n";
2435         }
2436         "</div>\n";
2437     }
2440 function EntryLite::print_linkbar() {}
2441 function Comment::print_linkbar() {
2442     var Link link;
2443     foreach var string k ($.link_keyseq) {
2444         $link = $this->get_link($k);
2445         " $link ";
2446     }
2448 function Entry::print_link_next() {
2449     var Link link = $this->get_link("nav_next");
2450     " $link";
2452 function Entry::print_link_prev() {
2453     var Link link = $this->get_link("nav_prev");
2454     "$link ";
2456 function Entry::print_linkbar() {
2457     ## There's no point in showing previous/next links on pages which show
2458     ## multiple entries anyway, so we only print them on EntryPage and ReplyPage.
2460     var Page p = get_page();
2461     var bool show_interentry = ($p.view == "entry" or $p.view == "reply");
2462     if ($show_interentry) {
2463         $this->print_link_prev();
2464     }
2465     var Link link;
2466     foreach var string k ($.link_keyseq) {
2467         $link = $this->get_link($k);
2468         " $link ";
2469         $link->print_raw();
2470     }
2471     if ($show_interentry) {
2472         $this->print_link_next();
2473     }
2476 ### RecentPage and related functions
2478 function RecentPage::print_body {
2479     # Creator for both the Recent and Friends views, since they are similar
2480     # If someone wants to do the two views differently, they can create
2481     # FriendsPage::print_body since FriendsPage extends RecentPage.
2483     foreach var Entry e ($.entries) {
2484         if ($e.end_day) {
2485             "</div>";
2486         }
2487         if ($e.new_day) {
2488             """<div class="day" id="day"""; print $e.time->date_format("%%yyyy%%%%mm%%%%dd%%"); """">\n<h2>""";
2489             print $e.time->date_format("long_day");
2490             "</h2>\n";
2491         }
2492         # Print the entry
2493         $this->print_entry($e);
2494     }
2496     if ($.nav.backward_url != "" or $.nav.forward_url != "") {
2497         if ($.nav.backward_url != "") {
2498             print safe "<a href=\"" + $.nav.backward_url + "\">" + get_plural_phrase($.nav.backward_count, "text_skiplinks_back") + "</a>";
2499         }
2500         if ($.nav.backward_url != "" and $.nav.forward_url != "") {
2501             " | ";
2502         }
2503         if ($.nav.forward_url != "") {
2504             print safe "<a href=\"" + $.nav.forward_url + "\">" + get_plural_phrase($.nav.forward_count, "text_skiplinks_forward") + "</a>";
2505         }
2506     }
2510 function secs_to_string (int sec) : string {
2511    if ($sec < 60) {
2512         return string($sec) + " seconds";
2513     }
2514    if ($sec < 3600) {
2515         return string($sec / 60) + " minutes";
2516     }
2517    if ($sec < 86400) {
2518         return string($sec / 3600) + " hours";
2519     }
2520    return string($sec / 86400) + " days";
2523 function EntryLite::time_display(string datefmt, string timefmt) : string {
2524     if ($datefmt == "") {
2525         $datefmt = "med";
2526     }
2527     if ($timefmt == "") {
2528         $timefmt = "short";
2529     }
2531     var string ret;
2532     if ($datefmt != "none") { $ret = $ret + $this.time->date_format($datefmt); }
2533     if ($datefmt != "none" and $timefmt != "none") { $ret = $ret + " "; }
2534     if ($timefmt != "none") { $ret = $ret + $this.time->time_format($timefmt); }
2536     return ehtml($ret);
2539 # edittime argument is true if we want the get the edit time of the comment
2540 function Comment::time_display (string datefmt, string timefmt, bool edittime) : string {
2541     var DateTime time = ($edittime ? $this.edittime : $this.time);
2542     var DateTime time_remote = ($edittime ? $this.edittime_remote : $this.time_remote);
2543     var DateTime time_poster = ($edittime ? $this.edittime_poster : $this.time_poster);
2545     if ($datefmt == "") {
2546         $datefmt = "iso";
2547     }
2548     if ($timefmt == "") {
2549         $timefmt = "short";
2550     }
2552     var string tooltip = "";
2553     if ($edittime == false) {
2554         var string etime = secs_to_string($this.seconds_since_entry);
2555         $tooltip = $etime + " after journal entry";
2556     }
2558     var string main;
2560     var string display_date;
2561     var string display_time;
2563     if ($time_remote) {
2564         $display_date = $time_remote->date_format($datefmt);
2565         if ($timefmt == "none") { $display_date = $display_date + " (local)"; }
2566         $display_time = $time_remote->time_format($timefmt) + " (local)";
2567     } else {
2568         $display_date = $time->date_format($datefmt);
2569         if ($timefmt == "none") { $display_date = $display_date + " (UTC)"; }
2570         $display_time = $time->time_format($timefmt) + " (UTC)";
2571     }
2573     if (defined $time_poster and defined $this.poster)
2574     {
2575         var string poster_date = $time_poster->date_format($datefmt);
2576         if ($edittime == false) {
2577             $tooltip = $tooltip + ", ";
2578         }
2580         if ($poster_date == $display_date and $timefmt != "none") { $poster_date = ""; }
2581         else { $poster_date = $poster_date + " "; }
2583         if ($timefmt != "none") {
2584             $tooltip = $tooltip + $poster_date + $time_poster->time_format($timefmt) + " (" + $this.poster.username + "'s time)";
2585         } else {
2586             $tooltip = $tooltip + $poster_date + "(" + $this.poster.username + "'s time)";
2587         }
2588     }
2590     if ($datefmt != "none") { $main = $main + $display_date; }
2591     if ($datefmt != "none" and $timefmt != "none") { $main = $main + " "; }
2592     if ($timefmt != "none") { $main = $main + $display_time; }
2594     return "<span title=\"" + ehtml($tooltip) + "\">" + ehtml($main) + "</span>";
2597 function Comment::time_display (string datefmt, string timefmt) : string {
2598     return $this->time_display($datefmt, $timefmt, false);
2601 function EntryLite::time_display() : string {
2602     # Let the real function decide on some nice defaults
2603     return $this->time_display("", "");
2606 function Comment::edittime_display() : string {
2607     return $this->time_display("", "", true);
2610 function Comment::edittime_display (string datefmt, string timefmt) : string {
2611     return $this->time_display($datefmt, $timefmt, true);
2614 ### Year view
2616 function YearPage::print_body {
2617     $this->print_year_links();
2618     foreach var YearMonth m ($.months) {
2619         $this->print_month($m);
2620     }
2622 function YearPage::print_year_links() {
2623     """<ul>\n""";
2624     foreach var YearYear y ($.years) {
2625         if ($y.displayed) {
2626             """<li class="active">$y.year</li>\n""";
2627         } else {
2628             """<li><a href="$y.url">$y.year</a></li>\n""";
2629         }
2630     }
2631     """</ul>\n""";
2633 function YearPage::print_month(YearMonth m) {
2634     if (not $m.has_entries) { return; }
2635     """<table style="margin-left: 25%; margin-right: 25%; margin-top: 1em; margin-bottom: 1em;
2636        border-collapse: collapse; border: 1px solid;" border="1">\n
2637        <tr><th colspan="7" style="text-align: center; border: 1px solid;">""";
2638     print $m->month_format();
2639     """</th></tr>\n<tr>\n""";
2640     foreach var int d (weekdays()) {
2641         "<th>"+$*lang_dayname_short[$d]+"</th>\n";
2642     }
2643     "</tr>\n";
2644     foreach var YearWeek w ($m.weeks) {
2645         $w->print();
2646     }
2647     print safe """<tr><td colspan="7" style="text-align: center; border: 1px solid;">
2648         <a href="$m.url">$*text_view_month</a></td></tr>\n""";
2649     "</table>";
2652 function YearWeek::print() {
2653    """<tr valign="top" style="height: 2em;">\n""";
2654    if ($.pre_empty > 0) {
2655       """<td class="emptyday" colspan="$.pre_empty">&nbsp;</td>\n""";
2656    }
2657    foreach var YearDay d ($.days) {
2658        """<td style="border: 1px solid;">\n""";
2659        """<div style="text-align: right;">$d.day</div>\n""";
2660        if ($d.num_entries > 0) {
2661            """<div style="text-align: center;"><a href="$d.url">$d.num_entries</a></div>\n""";
2662        }
2663        """</td>\n""";
2664    }
2665    if ($.post_empty > 0) {
2666       """<td colspan="$.post_empty">&nbsp;</td>\n""";
2667    }
2668    "</tr>";
2671 function MonthPage::view_title : string {
2672     return $.date->date_format($*lang_fmt_month_long);
2675 function MonthPage::print_body {
2676     "<form method='post' action='$.redir.url'><center>";
2677     $.redir->print_hiddens();
2678     if ($.prev_url != "") { "[<a href='$.prev_url'>&lt;&lt;&lt;</a>]\n"; }
2679     if (size $.months > 1) {
2680         "<select name='redir_key'>\n";
2681         foreach var MonthEntryInfo mei ($.months) {
2682             var string sel;
2683             if ($mei.date.year == $.date.year and $mei.date.month == $.date.month) {
2684                 $sel = " selected='selected'";
2685             }
2686             "<option value='$mei.redir_key'$sel>" + $mei.date->date_format($*lang_fmt_month_long) + "</option>";
2687         }
2688         "</select>\n<input type='submit' value='View' />";
2689     }
2690     if ($.next_url != "") { "\n[<a href='$.next_url'>&gt;&gt;&gt;</a>]\n"; }
2691     "</center></form>\n<dl>";
2692     foreach var MonthDay d ($.days) {
2693         if ($d.has_entries) {
2694             "<dt><a href=\"$d.url\"><b>";
2695             print lang_ordinal($d.day);
2696             "</b></a></dt>\n<dd>";
2697             $d->print_subjectlist();
2698             "</dd>\n";
2699         }
2700     }
2701     "</dl>\n";
2704 function MonthDay::print_subjectlist() {
2705     # Too many tables...
2706     foreach var Entry e ($.entries) {
2707         print $e.time->time_format("short") + ": ";
2708         if ($e.poster.username != $e.journal.username) {
2709             $e.poster->print(); " ";
2710         }
2711         "$e.security_icon";
2712         if ($e.subject != "") {
2713             " <a href=\"$e.permalink_url\">$e.subject</a>";
2714         } else {
2715             print safe " <a href=\"$e.permalink_url\" style=\"font-style: italic;\">$*text_nosubject</a>";
2716         }
2717         if ($e.comments.count > 0) {
2718             print safe " - " + get_plural_phrase($e.comments.count, "text_read_comments");
2719         }
2720         if ($e.comments.screened) {
2721             print safe " <b>$*text_month_screened_comments</b>";
2722         }
2723         "<br />\n";
2724     }
2727 ### Day view
2729 function DayPage::print_body() {
2730     if ($.has_entries) {
2731         "<div class=\"day\" id=\"day"; print $.date->date_format("%%yyyy%%%%mm%%%%dd%%"); "\">\n<h2>";
2732         print $.date->date_format("long");
2733         "</h2>\n";
2735         foreach var Entry e ($.entries) {
2736             $this->print_entry($e);
2737         }
2739         "</div>";
2740     } else {
2741         print safe "<p>$*text_noentries_day</p>";
2742     }
2744     "<div class=\"skiplinks\">\n";
2745     print safe "<a href=\"$.prev_url\">$*text_day_prev</a> | ";
2746     print safe "<a href=\"$.next_url\">$*text_day_next</a>\n</div>";
2750 ### CommentInfo functions
2752 function CommentInfo::print_readlink {
2753     var Page p = get_page();
2754     print safe "<a href=\"$.read_url\">"+
2755         get_plural_phrase($.count, $p.view == "friends" ?
2756                           "text_read_comments_friends" : "text_read_comments")+
2757     "</a>";
2759 function CommentInfo::print_postlink() {
2760     var Page p = get_page();
2761     if ($.maxcomments) {
2762         print safe "$*text_max_comments";
2763     } else {
2764         print safe "<a href=\"$.post_url\">"+($p.view == "friends" ? $*text_post_comment_friends : $*text_post_comment)+"</a>";
2765     }
2767 function CommentInfo::print() {
2768     if ($.show_readlink or $.show_postlink) {
2769         """<div style="text-align: right;">\n(""";
2770         if ($.show_readlink) {
2771             $this->print_readlink();
2772         }
2773         if ($.show_postlink and $.show_readlink) {
2774             " | ";
2775         }
2776         if ($.show_postlink) {
2777             $this->print_postlink();
2778         }
2779         ")</div>";
2780     }
2784 ### Link object functions
2786 function Link::print_button() [fixed] {
2787     print $this->as_string();
2790 function Link::as_string() [fixed] : string {
2792     if ($.url == "") { return ""; }
2793     var string ealt = ehtml($.caption);
2794     var string{} extra = $.icon.extra;
2795     var string extraParams = "";
2797     foreach var string extraKey ($extra) {
2798         $extraParams = $extraParams + """$extraKey="$extra{$extraKey}" """;
2799     }
2801     return """<a href="$.url"><img border='0' width="$.icon.width" height="$.icon.height" alt="$ealt" title="$ealt" src="$.icon.url" $extraParams /></a>""";
2804 # Redirector
2806 function Redirector::start_form ()
2808     "<form method='post' action='$.url' style='display: inline'>";
2809     $this->print_hiddens();
2811 function Redirector::print_hiddens ()
2813     "<input type='hidden' name='redir_user' value='$.user' />\n";
2814     "<input type='hidden' name='redir_vhost' value='$.vhost' />\n";
2815     "<input type='hidden' name='redir_type' value='$.type' />\n";
2817 function Redirector::end_form ()
2819     "</form>";
2822 ### EntryPage functions
2824 function EntryPage::print_comments (Comment[] cs) {
2825     if (size $cs == 0) { return; }
2826     var Page p = get_page();
2827     foreach var Comment c ($cs) {
2828         if ($p.showspam) {
2829             if ($c.spam) {
2830                 "<div id='$c.dom_id' style='margin-left: 0px; margin-top: 5px'>\n";
2831                 $this->print_comment($c);
2832                 "</div>";
2833             }
2834         } else {
2835             var int indent = ($c.depth - 1) * 25;
2836             "<div id='$c.dom_id' style='margin-left: ${indent}px; margin-top: 5px'>\n";
2837             if ($c.spam) {
2838                 if (size $c.replies > 0) {
2839                     "(SPAM)";
2840                 }
2841             } elseif ($c.full) {
2842                 $this->print_comment($c);
2843             } else {
2844                 $this->print_comment_partial($c);
2845             }
2846             "</div>";
2847             $this->print_comments($c.replies);
2848         }
2849     }
2852 function EntryPage::print_comment (Comment c) {
2853     var Color barlight = $*color_comment_bar->clone();
2854     $barlight->lightness(($barlight->lightness() + 255) / 2);
2855     var Color barc = $c.depth % 2 ? $*color_comment_bar : $barlight;
2856     var string poster = defined $c.poster ? $c.poster->as_string() : "<i>$*text_poster_anonymous</i>";
2858     "<a name='$c.anchor'></a><div style='background-color: $barc; margin-top: 10px; width: 100%'>";
2859     "<table cellpadding='2' cellspacing='0' summary='0' style='width: 100%'><tr valign='top'>";
2860     if (defined $c.userpic and $*comment_userpic_style != "off") {
2861         var int w = $c.userpic.width;
2862         var int h = $c.userpic.height;
2863         # WARNING: this will later be done by the system (it'll be a
2864         # constructional property), so don't copy this hack into your
2865         # layout layers or you'll be messed up later.
2866         if ($*comment_userpic_style == "small") {
2867             $w = $w / 2;
2868             $h = $h / 2;
2869         }
2870         print "<td style='width: 102px'><img src='$c.userpic.url' width='$w' height='$h' alt='' /></td>";
2871     }
2872     "<td><table style='width: 100%'><tr>";
2874     "<td align='left' style='width: 50%'>";
2875       print "<table>";
2876       print safe "<tr><th align='right'>$*text_comment_from</th><td>$poster</td></tr>\n";
2877       print safe "<tr><th align='right'>$*text_comment_date</th><td style='white-space: nowrap'>";
2878         print $c->time_display() + "</td></tr>";
2879       if ($c.metadata{"poster_ip"}) { print safe "<tr><th align='right'>$*text_comment_ipaddr</th><td>(" + $c.metadata{"poster_ip"} + ")</td></tr>"; }
2880     "</table></td>";
2882     print "<td align='right' style='width: 50%'>";
2883     if ($this.multiform_on) {
2884         print safe " <label for='ljcomsel_$c.talkid'>$*text_multiform_check</label> ";
2885         $c->print_multiform_check();
2886     }
2887     $c->print_linkbar();
2888     "</td></tr>";
2890     print "<tr valign='top'><td style='width: 50%'>";
2891     if (defined $c.subject_icon or $c.subject != "") { "<h3>$c.subject_icon $c.subject</h3>\n"; }
2892     print "</td>";
2894     print safe "<td style='width:50%;' align='right'><strong>(<a href='$c.permalink_url'>$*text_permalink</a>)</strong></td></tr>\n";
2895     print "</table></td></tr></table></div>";
2897     print "<div style='margin-left: 5px'>"; $c->print_text(); "</div>\n";
2898     print "<div style='margin-top: 3px; font-size: smaller'>";
2899     if ($c.frozen) {
2900         print safe "($*text_comment_frozen) ";
2901     } else {
2902         "("; $c->print_reply_link({"linktext" => $*text_comment_reply}); ") ";
2903     }
2904     if ($c.parent_url != "") { print safe "(<a href='$c.parent_url'>$*text_comment_parent</a>) "; }
2905     if ($c.thread_url != "") {
2906         print safe "(<a href='$c.thread_url'>$*text_comment_thread</a>) ";
2907         var Link expand_link = $c->get_link("expand_comments");
2908         if (defined $expand_link) {
2909             "("; $c->print_expand_link(); ") ";
2910         }
2911     }
2912     "</div>\n";
2913     $c->print_reply_container();
2916 function EntryPage::print_comment_full (Comment c) {
2919 function EntryPage::print_comment_partial (Comment c) {
2920     var string poster = defined $c.poster ? $c.poster->as_string() : "<i>$*text_poster_anonymous</i>";
2921     var string subj = $c.subject != "" ? $c.subject : $*text_nosubject;
2922     print safe "<a href='$c.permalink_url'>$subj</a> - $poster";
2923     var Link expand_link = $c->get_link("expand_comments");
2924     if ($c.thread_url != "" and defined $expand_link) {
2925         " "; $c->print_expand_link();
2926     }
2929 function ItemRange::print() {
2930     if ($.all_subitems_displayed) { return; }
2931     print "<table align='center' border='0' cellpadding='3'>";
2932     print "<tr><td align='center' colspan='3'><b>" +
2933         lang_page_of_pages($.current, $.total) + "</b>";
2934     print "</td></tr>";
2935     var string url_prev = $this->url_of($.current - 1);
2936     if ($.current != 1) {
2937         print "<tr><td align='center'><a href='$url_prev#comments'>&lt;&lt;</a></td>";
2938     } else {
2939         print "<tr><td align='center'>&lt;&lt;</td>";
2940     }
2941     print "<td align='center'>";
2942     foreach var int i (1..$.total) {
2943         if ($i == $.current) { "<b>[$i]</b> "; }
2944         else {
2945             var string url_of = $this->url_of($i);
2946             "<a href='$url_of#comments'><b>[$i]</b></a> ";
2947         }
2948     }
2949     print "</td>";
2950     var string url_next = $this->url_of($.current + 1);
2951     if ($.current != $.total) {
2952         print "<td align='center'><a href='$url_next#comments'>&gt;&gt;</a></td>";
2953     } else {
2954         print "<td align='center'>&gt;&gt;</td>";
2955     }
2956     print "</tr></table>";
2959 function EntryPage::print_body
2961     var Entry e = $.entry;
2962     """<div class="entry" id="$e.dom_id">\n""";
2964     "<table><tr valign='middle'>";
2965     if (defined $e.userpic) {
2966         print "<td>$e.userpic</td>";
2967     }
2968     print "<td>"+lang_user_wrote($e.poster);
2969     print "<br />"+lang_at_datetime($e.time);
2970     "</td></tr></table>\n";
2972     print "<div style='text-align: center'>";
2973     $e->print_linkbar();
2974     print "</div>";
2976     "<h2>$e.security_icon $e.subject</h2>\n";
2978     if (not $.viewing_thread) {
2979         "<div class='entrytext'>";
2980         $e->print_text();
2981         "</div>\n";
2982         $e->print_metadata();
2983         "</div>\n\n";
2984     }
2986     if ($.entry.comments.enabled) {
2987         "<hr /><a name='comments'></a>";
2988         "("; $this->print_reply_link({ "linktext" => $*text_post_comment, "target" => "topcomment" }); ")";
2989         $this->print_reply_container({ "target" => "topcomment" });
2990         if ($.comment_pages.total_subitems > 0) {
2991             "<hr />";
2992             $.comment_pages->print();
2993             $this->print_multiform_start();
2994             $this->print_comments($.comments);
2995             print "<hr />";
2996             "("; $this->print_reply_link({ "linktext" => $*text_post_comment, "target" => "bottomcomment" }); ")";
2997             $this->print_reply_container({"target" => "bottomcomment"});
2998             if ($.comment_pages.all_subitems_displayed) { print "<hr />"; }
2999             $this->print_multiform_actionline();
3000             $this->print_multiform_end();
3001             $.comment_pages->print();
3002         }
3003     }
3006 function ReplyPage::print_body
3008     if (not $.entry.comments.enabled) {
3009         print safe "<h2>$*text_reply_nocomments_header</h2><p>$*text_reply_nocomments</p>";
3010         return;
3011     }
3013     var Image security;
3014     if ($.replyto isa Entry) {
3015         var Entry en = $.replyto as Entry;
3017         $security = $en.security_icon;
3018     }
3020     "<table><tr valign='middle'>";
3021         if (defined $.replyto.userpic) {
3022             print "<td>$.replyto.userpic</td>";
3023         }
3024     print "<td>"+lang_user_wrote($.replyto.poster);
3025     print "<br />"+lang_at_datetime($.replyto.time);
3026     "</td></tr></table>\n";
3028     print "<h2>$security $.replyto.subject</h2>";
3029     print "<div>"; $.replyto->print_text(); "</div>";
3031     print "\n<hr />\n";
3032     print safe """(<a href="$.entry.permalink_url">$*text_reply_back</a>)""";
3033     $.form->print();
3036 ### TagsPage functions
3038 function TagsPage::print_body
3040     print safe "<h2>$*text_tags_page_header</h2><ul class='ljtaglist'>";
3042     foreach var TagDetail td ($.tags) {
3043         var string uses = get_plural_phrase($td.use_count, "text_tag_uses");
3044         print safe """<li><a href="$td.url">$td.name</a> - $uses</li>""";
3045     }
3047     print "</ul>";
3050 ### MessagePage functions
3052 function MessagePage::view_title() : string {
3053     return $.title;
3056 function MessagePage::print_body() {
3057     $this->print_links();
3058     $this->print_message();
3061 function MessagePage::print_message() {
3062     print $.message;
3065 function MessagePage::print_links() {
3066     println """<div style='text-align: center;'>""";
3067     foreach var string k ($.link_keyseq) {
3068         println """ $.links{$k} """;
3069     }
3070     println """</div>""";