diff options
author | Tobias Diekershoff <tobias.diekershoff@gmx.net> | 2009-04-10 14:25:25 +0200 |
---|---|---|
committer | Tobias Diekershoff <tobias.diekershoff@gmx.net> | 2009-04-10 14:25:25 +0200 |
commit | 4277a6818c6ac66158dfba3b94bdc1b2eb06b594 (patch) | |
tree | 99149b95d0bd0bfaac4b2ad3d646e8cc1da7f51a | |
parent | d1bf8b2143f597d9d1b1e7ff472532c596200011 (diff) | |
parent | 6cdc2ff444b8c76a3cdc5b8c6e9e7e7539d9b6cc (diff) |
Merge branch '0.7.x' of git://gitorious.org/laconica/dev into 0.7.x
76 files changed, 2067 insertions, 1010 deletions
@@ -2,8 +2,8 @@ README ------ -Laconica 0.7.2.1 ("Talk about the Passion") -11 March 2009 +Laconica 0.7.3 ("You Are The Everything") +7 April 2009 This is the README file for Laconica, the Open Source microblogging platform. It includes installation instructions, descriptions of @@ -71,93 +71,29 @@ for additional terms. New this version ================ -This is a minor bug-fix and feature release since version 0.7.1, -released Feb 9 2009. Notable changes this version: - -- First version of a web-based installer -- Use Net_URL_Mapper instead of mod_rewrite to map "fancy URLs", - for a much simpler installation and use of PATH_INFO on sites - that don't have mod_rewrite. -- A plugin framework for system events, to make it easier to build - server-side plugins. -- A plugin for Google Analytics -- A plugin to use blogspam.net to check notices for spam -- A plugin to send linkbacks for notices about blog posts -- Configurable check for duplicate notices in a specific time - period -- Better Atom feeds -- First implementation of Twitter Search API -- Add streamlined mobile device-friendly styles when enabled in config. -- A queue server for sending notices to Twitter -- A queue server for sending notices to Facebook -- A queue server for sending notices to a ping server -- Fixed a bug in nonces for OAuth in OpenMicroBlogging -- Fixed bugs in transfer of avatars in OpenMicroBlogging -- @-links go to permalinks for local users -- Better handling of DB errors (instead of dreaded DB_DataObject blank - screen) -- Initial version of an RPM spec file -- More consistent display of notices in notice search -- A stylesheet for printed output -- "Social graph" methods for Twitter API -- Documentation for the JavaScript badge -- Debugged a ton of problems that happened with E_NOTICE on -- Better caching in RSS feeds -- Optionally send email when an @-message is received -- Automatically add tags for every group message -- Add framebusting JavaScript to help avoid clickjacking attacks. -- Optionally ignore some notice sources for public page. -- Add default SMS carriers and notice sources to distribution file. -- Change titles to use mixed case instead of all uppercase. -- Use exceptions for error handling. - -Changes in version 0.7.1: - -- Vast improvement in auto-linking to URLs. -- Link to group search from user's group page -- Improved interface in Facebook application -- Fix bad redirects in delete notice -- Updated PostgreSQL database creation script -- Show filesize in avatar/logo upload -- Vastly improved avatar/logo upload -- Allow re-authentication with OpenID -- Correctly link hashtabs inside parens and brackets -- Group and avatar image transparency works -- Better handling of commands through the Web and Ajax channels -- Fix links for profile page feeds -- Fixed destroy method in API -- Fix endpoint of Connect menu when XMPP disabled -- Show number of group members -- Enable configuration files in /etc/laconica/ - -Changes in version 0.7.0: - -- Support for groups. Users can join groups and send themed notices - to those groups. All other members of the group receive the notices. -- Laconica-specific extensions to the Twitter API. -- A Facebook application. -- A massive UI redesign. The HTML generated by Laconica has changed - significantly, to make theming easier and to give a more open look - by default. Also, sidebar. -- Massive code hygiene changes to move towards compliance with the PEAR - coding standards and to support the new UI redesign. -- Began the breakup of util.php -- moved about 30% of code to a views - hierarchy. -- UI elements for statistical information (like top posters or most - popular groups) added in a sidebar. -- include Javascript badge by Kent Brewster. -- Updated online documentation. -- Cropping of user avatars using Jcrop. -- fix for Twitter bridge to not send "Expect:" headers. -- add 'dm' as a synonym for 'd' in commands. -- Upgrade upstream version of jQuery to 1.3. -- Upgrade upstream version of PHP-OpenID to 2.1.2. -- Move OpenMicroBlogging specification to its own repository. -- Make tag-based RSS streams work. -- Additional locales: Bulgarian, Catalan, Greek, Hebrew, simplified - Chinese, Telugu, Taiwanese Chinese, Vietnamese, -- PostgreSQL updates. -- Nasty bug in Twitter bridge that wouldn't verify with Twitter +This is a minor bug-fix and feature release since version 0.7.2.1, +released Mar 11 2009. Notable changes this version: + +- A plugin to allow a templating language for customization +- A plugin for Piwik Analytics engine +- A bookmarklet for posting a notice about a Web page you're reading +- A welcome notice ('welcomebot') and default subscription for new users +- Support for SSL for some or all pages on the site +- Better handling of empty notice lists on many pages +- Major improvements to the Twitter friend-sync offline processing +- subscribers, subscriptions, groups are listed on the Personal page. +- "Invite" link restored to main menu +- Better memory handling in FOAF output +- Fix for SUP support (FriendFeed) +- Correct and intelligent redirect HTTP status codes +- Fix DB collations for search and sort +- Better H1s and Titles using user full names +- Fixes to make the linkback plugin operational +- Better indication that a notice is being published by Ajax (spinner) +- Better and unified Atom output +- Hiding "register" and "join now" messages when site is closed +- ping, twitter and facebook queuehandlers working better +- Updated RPM spec Prerequisites ============= @@ -257,9 +193,9 @@ especially if you've previously installed PHP/MySQL packages. 1. Unpack the tarball you downloaded on your Web server. Usually a command like this will work: - tar zxf laconica-0.7.2.1.tar.gz + tar zxf laconica-0.7.3.tar.gz - ...which will make a laconica-0.7.2.1 subdirectory in your current + ...which will make a laconica-0.7.3 subdirectory in your current directory. (If you don't have shell access on your Web server, you may have to unpack the tarball on your local computer and FTP the files to the server.) @@ -267,7 +203,7 @@ especially if you've previously installed PHP/MySQL packages. 2. Move the tarball to a directory of your choosing in your Web root directory. Usually something like this will work: - mv laconica-0.7.2.1 /var/www/mublog + mv laconica-0.7.3 /var/www/mublog This will make your Laconica instance available in the mublog path of your server, like "http://example.net/mublog". "microblog" or @@ -757,7 +693,7 @@ Upgrading If you've been using Laconica 0.6, 0.5 or lower, or if you've been tracking the "git" version of the software, you will probably want to upgrade and keep your existing data. There is no automated upgrade -procedure in Laconica 0.7.2.1. Try these step-by-step instructions; read +procedure in Laconica 0.7.3. Try these step-by-step instructions; read to the end first before trying them. 0. Download Laconica and set up all the prerequisites as if you were @@ -925,6 +861,16 @@ dupelimit: Time in which it's not OK for the same person to post the same notice; default = 60 seconds. logo: URL of an image file to use as the logo for the site. Overrides the logo in the theme, if any. +ssl: Whether to use SSL and https:// URLs for some or all pages. + Possible values are 'always' (use it for all pages), 'never' + (don't use it for any pages), or 'sometimes' (use it for + sensitive pages that include passwords like login and registration, + but not for regular pages). Default to 'never'. +sslserver: use an alternate server name for SSL URLs, like + 'secure.example.org'. You should be careful to set cookie + parameters correctly so that both the SSL server and the + "normal" server can access the session cookie and + preferably other cookies as well. db -- @@ -1169,6 +1115,20 @@ banned: an array of usernames and/or profile IDs of 'banned' profiles. not be accepted at all. (Compare with blacklisted users above, whose posts just won't show up in the public stream.) +newuser +------- + +Options with new users. + +default: nickname of a user account to automatically subscribe new + users to. Typically this would be system account for e.g. + service updates or announcements. Users are able to unsub + if they want. Default is null; no auto subscribe. +welcome: nickname of a user account that sends welcome messages to new + users. Can be the same as 'default' account, although on + busy servers it may be a good idea to keep that one just for + 'urgent' messages. Default is null; no message. + Troubleshooting =============== @@ -1181,7 +1141,7 @@ repository (see below), and you get a compilation error ("unexpected T_STRING") in the browser, check to see that you don't have any conflicts in your code. -If you upgraded to Laconica 0.7.2.1 without reading the "Notice inboxes" +If you upgraded to Laconica 0.7.3 without reading the "Notice inboxes" section above, and all your users' 'Personal' tabs are empty, read the "Notice inboxes" section above. @@ -1270,6 +1230,8 @@ if anyone's been overlooked in error. * Leslie Michael Orchard * Eric Helgeson * Ken Sedgwick +* Brian Hendrickson +* Tobias Diekershoff Thanks also to the developers of our upstream library code and to the thousands of people who have tried out Identi.ca, installed Laconi.ca, diff --git a/actions/all.php b/actions/all.php index 8e67ec0f3..f5bbfe2e3 100644 --- a/actions/all.php +++ b/actions/all.php @@ -23,31 +23,13 @@ require_once INSTALLDIR.'/lib/personalgroupnav.php'; require_once INSTALLDIR.'/lib/noticelist.php'; require_once INSTALLDIR.'/lib/feedlist.php'; -class AllAction extends Action +class AllAction extends ProfileAction { - var $user = null; - var $page = null; - function isReadOnly() { return true; } - function prepare($args) - { - parent::prepare($args); - $nickname = common_canonical_nickname($this->arg('nickname')); - $this->user = User::staticGet('nickname', $nickname); - $this->page = $this->trimmed('page'); - if (!$this->page) { - $this->page = 1; - } - - common_set_returnto($this->selfUrl()); - - return true; - } - function handle($args) { parent::handle($args); @@ -93,6 +75,27 @@ class AllAction extends Action $nav->show(); } + function showEmptyListMessage() + { + $message = sprintf(_('This is the timeline for %s and friends but no one has posted anything yet.'), $this->user->nickname) . ' '; + + if (common_logged_in()) { + $current_user = common_current_user(); + if ($this->user->id === $current_user->id) { + $message .= _('Try subscribing to more people, [join a group](%%action.groups) or post something yourself.'); + } else { + $message .= sprintf(_('You can try to [nudge %s](../%s) from his profile or [post something to his or her attention](%%%%action.newnotice%%%%?status_textarea=%s).'), $this->user->nickname, $this->user->nickname, '@' . $this->user->nickname); + } + } + else { + $message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to his or her attention.'), $this->user->nickname); + } + + $this->elementStart('div', 'guide'); + $this->raw(common_markup_to_html($message)); + $this->elementEnd('div'); + } + function showContent() { $notice = $this->user->noticesWithFriends(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1); @@ -101,6 +104,10 @@ class AllAction extends Action $cnt = $nl->show(); + if (0 == $cnt) { + $this->showEmptyListMessage(); + } + $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE, $this->page, 'all', array('nickname' => $this->user->nickname)); } diff --git a/actions/block.php b/actions/block.php index e77b634c8..34f991dc6 100644 --- a/actions/block.php +++ b/actions/block.php @@ -93,7 +93,8 @@ class BlockAction extends Action if ($this->arg('no')) { $cur = common_current_user(); $other = Profile::staticGet('id', $this->arg('blockto')); - common_redirect(common_local_url('showstream', array('nickname' => $other->nickname))); + common_redirect(common_local_url('showstream', array('nickname' => $other->nickname)), + 303); } elseif ($this->arg('yes')) { $this->blockProfile(); } elseif ($this->arg('blockto')) { @@ -102,7 +103,6 @@ class BlockAction extends Action } } - function showContent() { $this->areYouSureForm(); } @@ -110,7 +110,7 @@ class BlockAction extends Action function title() { return _('Block user'); } - + function showNoticeForm() { // nop } @@ -178,10 +178,11 @@ class BlockAction extends Action } if ($action) { - common_redirect(common_local_url($action, $args)); + common_redirect(common_local_url($action, $args), 303); } else { common_redirect(common_local_url('subscriptions', - array('nickname' => $cur->nickname))); + array('nickname' => $cur->nickname)), + 303); } } } diff --git a/actions/deletenotice.php b/actions/deletenotice.php index 16e2df889..6c350b33a 100644 --- a/actions/deletenotice.php +++ b/actions/deletenotice.php @@ -141,6 +141,6 @@ class DeletenoticeAction extends DeleteAction $url = common_local_url('public'); } - common_redirect($url); + common_redirect($url, 303); } } diff --git a/actions/disfavor.php b/actions/disfavor.php index 90bab3cca..bc13b09da 100644 --- a/actions/disfavor.php +++ b/actions/disfavor.php @@ -49,7 +49,7 @@ class DisfavorAction extends Action { /** * Class handler. - * + * * @param array $args query arguments * * @return void @@ -100,7 +100,8 @@ class DisfavorAction extends Action $this->elementEnd('html'); } else { common_redirect(common_local_url('showfavorites', - array('nickname' => $user->nickname))); + array('nickname' => $user->nickname)), + 303); } } } diff --git a/actions/editgroup.php b/actions/editgroup.php index e7e79040a..39dad0465 100644 --- a/actions/editgroup.php +++ b/actions/editgroup.php @@ -166,7 +166,6 @@ class EditgroupAction extends Action return; } - $nickname = common_canonical_nickname($this->trimmed('nickname')); $fullname = $this->trimmed('fullname'); $homepage = $this->trimmed('homepage'); @@ -221,7 +220,7 @@ class EditgroupAction extends Action if ($this->group->nickname != $orig->nickname) { common_redirect(common_local_url('editgroup', array('nickname' => $nickname)), - 307); + 303); } else { $this->showForm(_('Options saved.')); } diff --git a/actions/favor.php b/actions/favor.php index 3940df688..3b7d979eb 100644 --- a/actions/favor.php +++ b/actions/favor.php @@ -52,7 +52,7 @@ class FavorAction extends Action { /** * Class handler. - * + * * @param array $args query arguments * * @return void @@ -100,13 +100,14 @@ class FavorAction extends Action $this->elementEnd('html'); } else { common_redirect(common_local_url('showfavorites', - array('nickname' => $user->nickname))); + array('nickname' => $user->nickname)), + 303); } } /** * Notifies a user when his notice is favorited. - * + * * @param class $notice favorited notice * @param class $user user declaring a favorite * diff --git a/actions/favorited.php b/actions/favorited.php index 231b97897..09ab1216a 100644 --- a/actions/favorited.php +++ b/actions/favorited.php @@ -104,9 +104,9 @@ class FavoritedAction extends Action { parent::prepare($args); $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; - + common_set_returnto($this->selfUrl()); - + return true; } @@ -145,6 +145,22 @@ class FavoritedAction extends Action $this->elementEnd('div'); } + function showEmptyList() + { + $message = _('Favorite notices appear on this page but no one has favorited one yet.') . ' '; + + if (common_logged_in()) { + $message .= _('Be the first to add a notice to your favorites by clicking the fave button next to any notice you like.'); + } + else { + $message .= _('Why not [register an account](%%action.register%%) and be the first to add a notice to your favorites!'); + } + + $this->elementStart('div', 'guide'); + $this->raw(common_markup_to_html($message)); + $this->elementEnd('div'); + } + /** * Local navigation * @@ -198,6 +214,10 @@ class FavoritedAction extends Action $cnt = $nl->show(); + if ($cnt == 0) { + $this->showEmptyList(); + } + $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE, $this->page, 'favorited'); } diff --git a/actions/featured.php b/actions/featured.php index f3bade6a5..86fd3f374 100644 --- a/actions/featured.php +++ b/actions/featured.php @@ -107,6 +107,7 @@ class FeaturedAction extends Action $featured_nicks = common_config('nickname', 'featured'); + if (count($featured_nicks) > 0) { $quoted = array(); @@ -118,7 +119,7 @@ class FeaturedAction extends Action $user = new User; $user->whereAdd(sprintf('nickname IN (%s)', implode(',', $quoted))); $user->limit(($this->page - 1) * PROFILES_PER_PAGE, PROFILES_PER_PAGE + 1); - $user->orderBy('user.nickname ASC'); + $user->orderBy(common_database_tablename('user') .'.nickname ASC'); $user->find(); @@ -145,4 +146,4 @@ class FeaturedAction extends Action $this->page, 'featured'); } } -}
\ No newline at end of file +} diff --git a/actions/finishaddopenid.php b/actions/finishaddopenid.php index 8f10505cf..32bceecfd 100644 --- a/actions/finishaddopenid.php +++ b/actions/finishaddopenid.php @@ -139,7 +139,7 @@ class FinishaddopenidAction extends Action oid_set_last($display); - common_redirect(common_local_url('openidsettings')); + common_redirect(common_local_url('openidsettings'), 303); } } diff --git a/actions/finishopenidlogin.php b/actions/finishopenidlogin.php index 52d9be29c..952185742 100644 --- a/actions/finishopenidlogin.php +++ b/actions/finishopenidlogin.php @@ -271,7 +271,8 @@ class FinishopenidloginAction extends Action common_rememberme($user); } unset($_SESSION['openid_rememberme']); - common_redirect(common_local_url('showstream', array('nickname' => $user->nickname))); + common_redirect(common_local_url('showstream', array('nickname' => $user->nickname)), + 303); } function connectUser() @@ -324,7 +325,7 @@ class FinishopenidloginAction extends Action array('nickname' => $nickname)); } - common_redirect($url); + common_redirect($url, 303); } function bestNewNickname($display, $sreg) diff --git a/actions/finishremotesubscribe.php b/actions/finishremotesubscribe.php index eaf57c2d8..d54c29a60 100644 --- a/actions/finishremotesubscribe.php +++ b/actions/finishremotesubscribe.php @@ -230,7 +230,8 @@ class FinishremotesubscribeAction extends Action # show up close to the top of the page common_redirect(common_local_url('subscribers', array('nickname' => - $user->nickname))); + $user->nickname)), + 303); } function add_avatar($profile, $url) diff --git a/actions/foaf.php b/actions/foaf.php index 3a99835b4..416935b1b 100644 --- a/actions/foaf.php +++ b/actions/foaf.php @@ -33,7 +33,24 @@ class FoafAction extends Action function prepare($args) { parent::prepare($args); - $this->nickname = $this->trimmed('nickname'); + + $nickname_arg = $this->arg('nickname'); + + if (empty($nickname_arg)) { + $this->clientError(_('No such user.'), 404); + return false; + } + + $this->nickname = common_canonical_nickname($nickname_arg); + + // Permanent redirect on non-canonical nickname + + if ($nickname_arg != $this->nickname) { + common_redirect(common_local_url('foaf', + array('nickname' => $this->nickname)), + 301); + return false; + } $this->user = User::staticGet('nickname', $this->nickname); @@ -122,20 +139,30 @@ class FoafAction extends Action if ($sub->find()) { while ($sub->fetch()) { - if ($sub->token) { + if (!empty($sub->token)) { $other = Remote_profile::staticGet('id', $sub->subscribed); } else { $other = User::staticGet('id', $sub->subscribed); } - if (!$other) { + if (empty($other)) { common_debug('Got a bad subscription: '.print_r($sub,true)); continue; } $this->element('knows', array('rdf:resource' => $other->uri)); - $person[$other->uri] = array(LISTENEE, $other); + $person[$other->uri] = array(LISTENEE, + $other->id, + $other->nickname, + (empty($sub->token)) ? 'User' : 'Remote_profile'); + $other->free(); + $other = null; + unset($other); } } + $sub->free(); + $sub = null; + unset($sub); + // Get people who subscribe to user $sub = new Subscription(); @@ -156,25 +183,36 @@ class FoafAction extends Action if (array_key_exists($other->uri, $person)) { $person[$other->uri][0] = BOTH; } else { - $person[$other->uri] = array(LISTENER, $other); + $person[$other->uri] = array(LISTENER, + $other->id, + $other->nickname, + (empty($sub->token)) ? 'User' : 'Remote_profile'); } + $other->free(); + $other = null; + unset($other); } } + $sub->free(); + $sub = null; + unset($sub); + $this->elementEnd('Person'); foreach ($person as $uri => $p) { $foaf_url = null; - if ($p[1] instanceof User) { - $foaf_url = common_local_url('foaf', array('nickname' => $p[1]->nickname)); + list($type, $id, $nickname, $cls) = $p; + if ($cls == 'User') { + $foaf_url = common_local_url('foaf', array('nickname' => $nickname)); } - $this->profile = Profile::staticGet($p[1]->id); + $profile = Profile::staticGet($id); $this->elementStart('Person', array('rdf:about' => $uri)); - if ($p[0] == LISTENER || $p[0] == BOTH) { + if ($type == LISTENER || $type == BOTH) { $this->element('knows', array('rdf:resource' => $this->user->uri)); } - $this->showMicrobloggingAccount($this->profile, ($p[1] instanceof User) ? - common_root_url() : null); + $this->showMicrobloggingAccount($profile, ($cls == 'User') ? + common_root_url() : null); if ($foaf_url) { $this->element('rdfs:seeAlso', array('rdf:resource' => $foaf_url)); } @@ -182,6 +220,9 @@ class FoafAction extends Action if ($foaf_url) { $this->showPpd($foaf_url, $uri); } + $profile->free(); + $profile = null; + unset($profile); } $this->elementEnd('rdf:RDF'); diff --git a/actions/groupsearch.php b/actions/groupsearch.php index 109a53ce1..06b4a7755 100644 --- a/actions/groupsearch.php +++ b/actions/groupsearch.php @@ -72,12 +72,23 @@ class GroupsearchAction extends SearchAction $terms = preg_split('/[\s,]+/', $q); $results = new GroupSearchResults($user_group, $terms, $this); $results->show(); + $user_group->free(); + $this->pagination($page > 1, $cnt > GROUPS_PER_PAGE, + $page, 'groupsearch', array('q' => $q)); } else { - $this->element('p', 'error', _('No results')); + $this->element('p', 'error', _('No results.')); + $this->searchSuggestions($q); + if (common_logged_in()) { + $message = _('If you can\'t find the group you\'re looking for, you can [create it](%%action.newgroup%%) yourself.'); + } + else { + $message = _('Why not [register an account](%%action.register%%) and [create the group](%%action.newgroup%%) yourself!'); + } + $this->elementStart('div', 'guide'); + $this->raw(common_markup_to_html($message)); + $this->elementEnd('div'); + $user_group->free(); } - $user_group->free(); - $this->pagination($page > 1, $cnt > GROUPS_PER_PAGE, - $page, 'groupsearch', array('q' => $q)); } } @@ -98,10 +109,5 @@ class GroupSearchResults extends GroupList { return preg_replace($this->pattern, '<strong>\\1</strong>', htmlspecialchars($text)); } - - function isReadOnly() - { - return true; - } } diff --git a/actions/joingroup.php b/actions/joingroup.php index eeea4a37b..a5d82ddc7 100644 --- a/actions/joingroup.php +++ b/actions/joingroup.php @@ -143,7 +143,8 @@ class JoingroupAction extends Action $this->elementEnd('html'); } else { common_redirect(common_local_url('groupmembers', array('nickname' => - $this->group->nickname))); + $this->group->nickname)), + 303); } } }
\ No newline at end of file diff --git a/actions/leavegroup.php b/actions/leavegroup.php index eb30d0e50..215ccd901 100644 --- a/actions/leavegroup.php +++ b/actions/leavegroup.php @@ -147,7 +147,8 @@ class LeavegroupAction extends Action $this->elementEnd('html'); } else { common_redirect(common_local_url('groupmembers', array('nickname' => - $this->group->nickname))); + $this->group->nickname)), + 303); } } } diff --git a/actions/login.php b/actions/login.php index b049791fb..59c6b4874 100644 --- a/actions/login.php +++ b/actions/login.php @@ -138,7 +138,7 @@ class LoginAction extends Action $nickname)); } - common_redirect($url); + common_redirect($url, 303); } /** diff --git a/actions/logout.php b/actions/logout.php index 3977f90a0..b7681be38 100644 --- a/actions/logout.php +++ b/actions/logout.php @@ -46,10 +46,10 @@ require_once INSTALLDIR.'/lib/openid.php'; */ class LogoutAction extends Action { - + /** * This is read only. - * + * * @return boolean true */ function isReadOnly() @@ -59,7 +59,7 @@ class LogoutAction extends Action /** * Class handler. - * + * * @param array $args array of arguments * * @return nothing @@ -73,7 +73,7 @@ class LogoutAction extends Action common_set_user(null); common_real_login(false); // not logged in common_forgetme(); // don't log back in! - common_redirect(common_local_url('public')); + common_redirect(common_local_url('public'), 303); } } } diff --git a/actions/newgroup.php b/actions/newgroup.php index cbd8dfeec..67cd6b2f1 100644 --- a/actions/newgroup.php +++ b/actions/newgroup.php @@ -193,7 +193,7 @@ class NewgroupAction extends Action $group->query('COMMIT'); - common_redirect($group->homeUrl(), 307); + common_redirect($group->homeUrl(), 303); } function nicknameExists($nickname) diff --git a/actions/noticesearch.php b/actions/noticesearch.php index 9058cf53c..095d0a454 100644 --- a/actions/noticesearch.php +++ b/actions/noticesearch.php @@ -114,22 +114,27 @@ class NoticesearchAction extends SearchAction $cnt = $notice->find(); } if ($cnt === 0) { - $this->element('p', 'error', _('No results')); + $this->element('p', 'error', _('No results.')); + + $this->searchSuggestions($q); + if (common_logged_in()) { + $message = sprintf(_('Be the first to [post on this topic](%%%%action.newnotice%%%%?status_textarea=%s)!'), urlencode($q)); + } + else { + $message = sprintf(_('Why not [register an account](%%%%action.register%%%%) and be the first to [post on this topic](%%%%action.newnotice%%%%?status_textarea=%s)!'), urlencode($q)); + } + + $this->elementStart('div', 'guide'); + $this->raw(common_markup_to_html($message)); + $this->elementEnd('div'); return; } $terms = preg_split('/[\s,]+/', $q); $nl = new SearchNoticeList($notice, $this, $terms); - $cnt = $nl->show(); - $this->pagination($page > 1, $cnt > NOTICES_PER_PAGE, $page, 'noticesearch', array('q' => $q)); } - - function isReadOnly() - { - return true; - } } class SearchNoticeList extends NoticeList { diff --git a/actions/nudge.php b/actions/nudge.php index bc3d48478..b4e5e01dd 100644 --- a/actions/nudge.php +++ b/actions/nudge.php @@ -50,7 +50,7 @@ class NudgeAction extends Action { /** * Class handler. - * + * * @param array $args array of arguments * * @return nothing @@ -75,7 +75,7 @@ class NudgeAction extends Action // CSRF protection $token = $this->trimmed('token'); - + if (!$token || $token != common_session_token()) { $this->clientError(_('There was a problem with your session token. Try again, please.')); return; @@ -100,7 +100,8 @@ class NudgeAction extends Action } else { // display a confirmation to the user common_redirect(common_local_url('showstream', - array('nickname' => $other->nickname))); + array('nickname' => $other->nickname)), + 303); } } diff --git a/actions/peoplesearch.php b/actions/peoplesearch.php index 9e515ade1..65d970dd1 100644 --- a/actions/peoplesearch.php +++ b/actions/peoplesearch.php @@ -60,14 +60,8 @@ class PeoplesearchAction extends SearchAction function showResults($q, $page) { - $profile = new Profile(); - - // lcase it for comparison - // $q = strtolower($q); - $search_engine = $profile->getSearchEngine('identica_people'); - $search_engine->set_sort_mode('chron'); // Ask for an extra to see if there's more. $search_engine->limit((($page-1)*PROFILES_PER_PAGE), PROFILES_PER_PAGE + 1); @@ -81,14 +75,15 @@ class PeoplesearchAction extends SearchAction $terms = preg_split('/[\s,]+/', $q); $results = new PeopleSearchResults($profile, $terms, $this); $results->show(); + $profile->free(); + $this->pagination($page > 1, $cnt > PROFILES_PER_PAGE, + $page, 'peoplesearch', array('q' => $q)); + } else { - $this->element('p', 'error', _('No results')); + $this->element('p', 'error', _('No results.')); + $this->searchSuggestions($q); + $profile->free(); } - - $profile->free(); - - $this->pagination($page > 1, $cnt > PROFILES_PER_PAGE, - $page, 'peoplesearch', array('q' => $q)); } } diff --git a/actions/public.php b/actions/public.php index 9b22e0a2c..5a380de9a 100644 --- a/actions/public.php +++ b/actions/public.php @@ -166,6 +166,22 @@ class PublicAction extends Action $nav->show(); } + function showEmptyList() + { + $message = _('This is the public timeline for %%site.name%% but no one has posted anything yet.') . ' '; + + if (common_logged_in()) { + $message .= _('Be the first to post!'); + } + else { + $message .= _('Why not [register an account](%%action.register%%) and be the first to post!'); + } + + $this->elementStart('div', 'guide'); + $this->raw(common_markup_to_html($message)); + $this->elementEnd('div'); + } + /** * Fill the content area * @@ -189,6 +205,10 @@ class PublicAction extends Action $cnt = $nl->show(); + if ($cnt == 0) { + $this->showEmptyList(); + } + $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE, $this->page, 'public'); } diff --git a/actions/publictagcloud.php b/actions/publictagcloud.php index 6f5fc7541..855cfed9b 100644 --- a/actions/publictagcloud.php +++ b/actions/publictagcloud.php @@ -64,6 +64,22 @@ class PublictagcloudAction extends Action common_config('site', 'name'))); } + function showEmptyList() + { + $message = _('No one has posted a notice with a [hashtag](%%doc.tags%%) yet.') . ' '; + + if (common_logged_in()) { + $message .= _('Be the first to post one!'); + } + else { + $message .= _('Why not [register an account](%%action.register%%) and be the first to post one!'); + } + + $this->elementStart('div', 'guide'); + $this->raw(common_markup_to_html($message)); + $this->elementEnd('div'); + } + function showLocalNav() { $nav = new PublicGroupNav($this); @@ -126,6 +142,8 @@ class PublictagcloudAction extends Action $this->elementEnd('dd'); $this->elementEnd('dl'); $this->elementEnd('div'); + } else { + $this->showEmptyList(); } } diff --git a/actions/remotesubscribe.php b/actions/remotesubscribe.php index af130f425..0b1174896 100644 --- a/actions/remotesubscribe.php +++ b/actions/remotesubscribe.php @@ -97,9 +97,9 @@ class RemotesubscribeAction extends Action 'class' => 'form_settings', 'action' => common_local_url('remotesubscribe'))); $this->elementStart('fieldset'); - $this->element('legend', 'Subscribe to a remote user'); + $this->element('legend', _('Subscribe to a remote user')); $this->hidden('token', common_session_token()); - + $this->elementStart('ul', 'form_data'); $this->elementStart('li'); $this->input('nickname', _('User nickname'), $this->nickname, @@ -407,7 +407,7 @@ class RemotesubscribeAction extends Action # Redirect to authorization service - common_redirect($req->to_url()); + common_redirect($req->to_url(), 303); return; } } diff --git a/actions/replies.php b/actions/replies.php index 4ab9b14ed..2769cb422 100644 --- a/actions/replies.php +++ b/actions/replies.php @@ -166,12 +166,36 @@ class RepliesAction extends Action $nl = new NoticeList($notice, $this); $cnt = $nl->show(); + if (0 === $cnt) { + $this->showEmptyListMessage(); + } $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE, $this->page, 'replies', array('nickname' => $this->user->nickname)); } + function showEmptyListMessage() + { + $message = sprintf(_('This is the timeline showing replies to %s but %s hasn\'t received a notice to his attention yet.'), $this->user->nickname, $this->user->nickname) . ' '; + + if (common_logged_in()) { + $current_user = common_current_user(); + if ($this->user->id === $current_user->id) { + $message .= _('You can engage other users in a conversation, subscribe to more people or [join groups](%%action.groups%%).'); + } else { + $message .= sprintf(_('You can try to [nudge %s](../%s) or [post something to his or her attention](%%%%action.newnotice%%%%?status_textarea=%s).'), $this->user->nickname, $this->user->nickname, '@' . $this->user->nickname); + } + } + else { + $message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to his or her attention.'), $this->user->nickname); + } + + $this->elementStart('div', 'guide'); + $this->raw(common_markup_to_html($message)); + $this->elementEnd('div'); + } + function isReadOnly() { return true; diff --git a/actions/showfavorites.php b/actions/showfavorites.php index d1c9283f0..4d4349505 100644 --- a/actions/showfavorites.php +++ b/actions/showfavorites.php @@ -162,6 +162,25 @@ class ShowfavoritesAction extends Action $nav->show(); } + function showEmptyListMessage() + { + if (common_logged_in()) { + $current_user = common_current_user(); + if ($this->user->id === $current_user->id) { + $message = _('You haven\'t chosen any favorite notices yet. Click the fave button on notices you like to bookmark them for later or shed a spotlight on them.'); + } else { + $message = sprintf(_('%s hasn\'t added any notices to his favorites yet. Post something interesting they would add to their favorites :)'), $this->user->nickname); + } + } + else { + $message = sprintf(_('%s hasn\'t added any notices to his favorites yet. Why not [register an account](%%%%action.register%%%%) and then post something interesting they would add to thier favorites :)'), $this->user->nickname); + } + + $this->elementStart('div', 'guide'); + $this->raw(common_markup_to_html($message)); + $this->elementEnd('div'); + } + /** * Show the content * @@ -183,9 +202,17 @@ class ShowfavoritesAction extends Action $nl = new NoticeList($notice, $this); $cnt = $nl->show(); + if (0 == $cnt) { + $this->showEmptyListMessage(); + } $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE, $this->page, 'showfavorites', array('nickname' => $this->user->nickname)); } + + function showPageNotice() { + $this->element('p', 'instructions', _('This is a way to share what you like.')); + } } + diff --git a/actions/showgroup.php b/actions/showgroup.php index 58cc7a97c..79445851f 100644 --- a/actions/showgroup.php +++ b/actions/showgroup.php @@ -73,11 +73,17 @@ class ShowgroupAction extends Action function title() { + if (!empty($this->group->fullname)) { + $base = $this->group->fullname . ' (' . $this->group->nickname . ')'; + } else { + $base = $this->group->nickname; + } + if ($this->page == 1) { - return sprintf(_("%s group"), $this->group->nickname); + return sprintf(_("%s group"), $base); } else { return sprintf(_("%s group, page %d"), - $this->group->nickname, + $base, $this->page); } } diff --git a/actions/showstream.php b/actions/showstream.php index b83f45d53..ce237dae2 100644 --- a/actions/showstream.php +++ b/actions/showstream.php @@ -54,12 +54,8 @@ require_once INSTALLDIR.'/lib/feedlist.php'; * @link http://laconi.ca/ */ -class ShowstreamAction extends Action +class ShowstreamAction extends ProfileAction { - var $user = null; - var $page = null; - var $profile = null; - function isReadOnly() { return true; @@ -67,54 +63,21 @@ class ShowstreamAction extends Action function title() { + if (!empty($this->profile->fullname)) { + $base = $this->profile->fullname . ' (' . $this->user->nickname . ') '; + } else { + $base = $this->user->nickname; + } + if ($this->page == 1) { - return $this->user->nickname; + return $base; } else { return sprintf(_("%s, page %d"), - $this->user->nickname, + $base, $this->page); } } - function prepare($args) - { - parent::prepare($args); - - $nickname_arg = $this->arg('nickname'); - $nickname = common_canonical_nickname($nickname_arg); - - // Permanent redirect on non-canonical nickname - - if ($nickname_arg != $nickname) { - $args = array('nickname' => $nickname); - if ($this->arg('page') && $this->arg('page') != 1) { - $args['page'] = $this->arg['page']; - } - common_redirect(common_local_url('showstream', $args), 301); - return false; - } - - $this->user = User::staticGet('nickname', $nickname); - - if (!$this->user) { - $this->clientError(_('No such user.'), 404); - return false; - } - - $this->profile = $this->user->getProfile(); - - if (!$this->profile) { - $this->serverError(_('User has no profile.')); - return false; - } - - $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; - - common_set_returnto($this->selfUrl()); - - return true; - } - function handle($args) { @@ -140,16 +103,6 @@ class ShowstreamAction extends Action $nav->show(); } - function showPageTitle() - { - $user =& common_current_user(); - if ($user && ($user->id == $this->profile->id)) { - $this->element('h1', NULL, _("Your profile")); - } else { - $this->element('h1', NULL, sprintf(_('%s\'s profile'), $this->profile->nickname)); - } - } - function showPageNoticeBlock() { return; @@ -376,165 +329,39 @@ class ShowstreamAction extends Action _('Subscribe')); } - function showNotices() - { - $notice = $this->user->getNotices(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1); - - $pnl = new ProfileNoticeList($notice, $this); - $cnt = $pnl->show(); - - $this->pagination($this->page>1, $cnt>NOTICES_PER_PAGE, $this->page, - 'showstream', array('nickname' => $this->user->nickname)); - } - - function showSections() - { - $this->showSubscriptions(); - $this->showSubscribers(); - $this->showGroups(); - $this->showStatistics(); - $cloud = new PersonalTagCloudSection($this, $this->user); - $cloud->show(); - } - - function showSubscriptions() - { - $profile = $this->user->getSubscriptions(0, PROFILES_PER_MINILIST + 1); - - $this->elementStart('div', array('id' => 'entity_subscriptions', - 'class' => 'section')); - - $this->element('h2', null, _('Subscriptions')); - - if ($profile) { - $pml = new ProfileMiniList($profile, $this->user, $this); - $cnt = $pml->show(); - if ($cnt == 0) { - $this->element('p', null, _('(None)')); - } - } - - if ($cnt > PROFILES_PER_MINILIST) { - $this->elementStart('p'); - $this->element('a', array('href' => common_local_url('subscriptions', - array('nickname' => $this->profile->nickname)), - 'class' => 'more'), - _('All subscriptions')); - $this->elementEnd('p'); - } - - $this->elementEnd('div'); - } - - function showSubscribers() + function showEmptyListMessage() { - $profile = $this->user->getSubscribers(0, PROFILES_PER_MINILIST + 1); + $message = sprintf(_('This is the timeline for %s but %s hasn\'t posted anything yet.'), $this->user->nickname, $this->user->nickname) . ' '; - $this->elementStart('div', array('id' => 'entity_subscribers', - 'class' => 'section')); - - $this->element('h2', null, _('Subscribers')); - - if ($profile) { - $pml = new ProfileMiniList($profile, $this->user, $this); - $cnt = $pml->show(); - if ($cnt == 0) { - $this->element('p', null, _('(None)')); + if (common_logged_in()) { + $current_user = common_current_user(); + if ($this->user->id === $current_user->id) { + $message .= _('Seen anything interesting recently? You haven\'t posted any notices yet, now would be a good time to start :)'); + } else { + $message .= sprintf(_('You can try to nudge %s or [post something to his or her attention](%%%%action.newnotice%%%%?status_textarea=%s).'), $this->user->nickname, '@' . $this->user->nickname); } } - - if ($cnt > PROFILES_PER_MINILIST) { - $this->elementStart('p'); - $this->element('a', array('href' => common_local_url('subscribers', - array('nickname' => $this->profile->nickname)), - 'class' => 'more'), - _('All subscribers')); - $this->elementEnd('p'); + else { + $message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to his or her attention.'), $this->user->nickname); } + $this->elementStart('div', 'guide'); + $this->raw(common_markup_to_html($message)); $this->elementEnd('div'); } - function showStatistics() - { - // XXX: WORM cache this - $subs = new Subscription(); - $subs->subscriber = $this->profile->id; - $subs_count = (int) $subs->count() - 1; - - $subbed = new Subscription(); - $subbed->subscribed = $this->profile->id; - $subbed_count = (int) $subbed->count() - 1; - - $notices = new Notice(); - $notices->profile_id = $this->profile->id; - $notice_count = (int) $notices->count(); - - $this->elementStart('div', array('id' => 'entity_statistics', - 'class' => 'section')); - - $this->element('h2', null, _('Statistics')); - - // Other stats...? - $this->elementStart('dl', 'entity_member-since'); - $this->element('dt', null, _('Member since')); - $this->element('dd', null, date('j M Y', - strtotime($this->profile->created))); - $this->elementEnd('dl'); - - $this->elementStart('dl', 'entity_subscriptions'); - $this->elementStart('dt'); - $this->element('a', array('href' => common_local_url('subscriptions', - array('nickname' => $this->profile->nickname))), - _('Subscriptions')); - $this->elementEnd('dt'); - $this->element('dd', null, (is_int($subs_count)) ? $subs_count : '0'); - $this->elementEnd('dl'); - - $this->elementStart('dl', 'entity_subscribers'); - $this->elementStart('dt'); - $this->element('a', array('href' => common_local_url('subscribers', - array('nickname' => $this->profile->nickname))), - _('Subscribers')); - $this->elementEnd('dt'); - $this->element('dd', 'subscribers', (is_int($subbed_count)) ? $subbed_count : '0'); - $this->elementEnd('dl'); - - $this->elementStart('dl', 'entity_notices'); - $this->element('dt', null, _('Notices')); - $this->element('dd', null, (is_int($notice_count)) ? $notice_count : '0'); - $this->elementEnd('dl'); - - $this->elementEnd('div'); - } - - function showGroups() + function showNotices() { - $groups = $this->user->getGroups(0, GROUPS_PER_MINILIST + 1); - - $this->elementStart('div', array('id' => 'entity_groups', - 'class' => 'section')); - - $this->element('h2', null, _('Groups')); - - if ($groups) { - $gml = new GroupMiniList($groups, $this->user, $this); - $cnt = $gml->show(); - if ($cnt == 0) { - $this->element('p', null, _('(None)')); - } - } + $notice = $this->user->getNotices(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1); - if ($cnt > GROUPS_PER_MINILIST) { - $this->elementStart('p'); - $this->element('a', array('href' => common_local_url('usergroups', - array('nickname' => $this->profile->nickname)), - 'class' => 'more'), - _('All groups')); - $this->elementEnd('p'); + $pnl = new ProfileNoticeList($notice, $this); + $cnt = $pnl->show(); + if (0 == $cnt) { + $this->showEmptyListMessage(); } - $this->elementEnd('div'); + $this->pagination($this->page>1, $cnt>NOTICES_PER_PAGE, $this->page, + 'showstream', array('nickname' => $this->user->nickname)); } function showAnonymousMessage() @@ -554,6 +381,12 @@ class ShowstreamAction extends Action $this->elementEnd('div'); } + function showSections() + { + parent::showSections(); + $cloud = new PersonalTagCloudSection($this, $this->user); + $cloud->show(); + } } // We don't show the author for a profile, since we already know who it is! diff --git a/actions/smssettings.php b/actions/smssettings.php index a5f75d266..922bab9a4 100644 --- a/actions/smssettings.php +++ b/actions/smssettings.php @@ -488,7 +488,8 @@ class SmssettingsAction extends ConnectSettingsAction } common_redirect(common_local_url('confirmaddress', - array('code' => $code))); + array('code' => $code)), + 303); } /** diff --git a/actions/subedit.php b/actions/subedit.php index 89081ffc7..8ca2d7914 100644 --- a/actions/subedit.php +++ b/actions/subedit.php @@ -85,7 +85,8 @@ class SubeditAction extends Action } common_redirect(common_local_url('subscriptions', - array('nickname' => $cur->nickname))); + array('nickname' => $cur->nickname)), + 303); } } } diff --git a/actions/subscribe.php b/actions/subscribe.php index f761992de..0bc522867 100644 --- a/actions/subscribe.php +++ b/actions/subscribe.php @@ -75,7 +75,8 @@ class SubscribeAction extends Action $this->elementEnd('html'); } else { common_redirect(common_local_url('subscriptions', array('nickname' => - $user->nickname))); + $user->nickname)), + 303); } } } diff --git a/actions/subscribers.php b/actions/subscribers.php index 22faafaef..7ebb54d33 100644 --- a/actions/subscribers.php +++ b/actions/subscribers.php @@ -88,6 +88,9 @@ class SubscribersAction extends GalleryAction if ($subscribers) { $subscribers_list = new SubscribersList($subscribers, $this->user, $this); $cnt = $subscribers_list->show(); + if (0 == $cnt) { + $this->showEmptyListMessage(); + } } $subscribers->free(); @@ -96,6 +99,25 @@ class SubscribersAction extends GalleryAction $this->page, 'subscribers', array('nickname' => $this->user->nickname)); } + + function showEmptyListMessage() + { + if (common_logged_in()) { + $current_user = common_current_user(); + if ($this->user->id === $current_user->id) { + $message = _('You have no subscribers. Try subscribing to people you know and they might return the favor'); + } else { + $message = sprintf(_('%s has no subscribers. Want to be the first?'), $this->user->nickname); + } + } + else { + $message = sprintf(_('%s has no subscribers. Why not [register an account](%%%%action.register%%%%) and be the first?'), $this->user->nickname); + } + + $this->elementStart('div', 'guide'); + $this->raw(common_markup_to_html($message)); + $this->elementEnd('div'); + } } class SubscribersList extends ProfileList diff --git a/actions/subscriptions.php b/actions/subscriptions.php index 3fbea2039..e6f3c54db 100644 --- a/actions/subscriptions.php +++ b/actions/subscriptions.php @@ -95,6 +95,9 @@ class SubscriptionsAction extends GalleryAction if ($subscriptions) { $subscriptions_list = new SubscriptionsList($subscriptions, $this->user, $this); $cnt = $subscriptions_list->show(); + if (0 == $cnt) { + $this->showEmptyListMessage(); + } } $subscriptions->free(); @@ -103,6 +106,25 @@ class SubscriptionsAction extends GalleryAction $this->page, 'subscriptions', array('nickname' => $this->user->nickname)); } + + function showEmptyListMessage() + { + if (common_logged_in()) { + $current_user = common_current_user(); + if ($this->user->id === $current_user->id) { + $message = _('You\'re not listening to anyone\'s notices right now, try subscribing to people you know. Try [people search](%%action.peoplesearch%%), look for members in groups you\'re interested in and in our [featured users](%%action.featured%%). If you\'re a [Twitter user](%%action.twittersettings%%), you can automatically subscribe to people you already follow there.'); + } else { + $message = sprintf(_('%s is not listening to anyone.'), $this->user->nickname); + } + } + else { + $message = sprintf(_('%s is not listening to anyone.'), $this->user->nickname); + } + + $this->elementStart('div', 'guide'); + $this->raw(common_markup_to_html($message)); + $this->elementEnd('div'); + } } class SubscriptionsList extends ProfileList @@ -117,7 +139,7 @@ class SubscriptionsList extends ProfileList $this->out->elementStart('form', array('id' => 'subedit-' . $profile->id, 'method' => 'post', - 'class' => 'form_subcription_edit', + 'class' => 'form_subscription_edit', 'action' => common_local_url('subedit'))); $this->out->hidden('token', common_session_token()); $this->out->hidden('profile', $profile->id); diff --git a/actions/sup.php b/actions/sup.php index 8ef9207fa..246b3299d 100644 --- a/actions/sup.php +++ b/actions/sup.php @@ -45,7 +45,7 @@ class SupAction extends Action function availablePeriods() { static $periods = array(86400, 43200, 21600, 7200, - 3600, 1800, 600, 300, 120, + 3600, 1800, 600, 300, 120, 60, 30, 15); $available = array(); foreach ($periods as $period) { diff --git a/actions/tag.php b/actions/tag.php index 231f2c299..d15f64498 100644 --- a/actions/tag.php +++ b/actions/tag.php @@ -33,7 +33,9 @@ class TagAction extends Action } if ($this->tag != $taginput) { - common_redirect(common_local_url('tag', array('tag' => $this->tag))); + common_redirect(common_local_url('tag', array('tag' => $this->tag)), + 301); + return false; } $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; diff --git a/actions/tagother.php b/actions/tagother.php index 0d18945a0..0c5bb7cf3 100644 --- a/actions/tagother.php +++ b/actions/tagother.php @@ -221,7 +221,8 @@ class TagotherAction extends Action $this->elementEnd('html'); } else { common_redirect(common_local_url($action, array('nickname' => - $user->nickname))); + $user->nickname)), + 303); } } diff --git a/actions/unblock.php b/actions/unblock.php index bad496353..8573b2a87 100644 --- a/actions/unblock.php +++ b/actions/unblock.php @@ -116,10 +116,11 @@ class UnblockAction extends Action } } if ($action) { - common_redirect(common_local_url($action, $args)); + common_redirect(common_local_url($action, $args), 303); } else { common_redirect(common_local_url('subscriptions', - array('nickname' => $cur->nickname))); + array('nickname' => $cur->nickname)), + 303); } } } diff --git a/actions/unsubscribe.php b/actions/unsubscribe.php index b1e2b6425..7dcab04c0 100644 --- a/actions/unsubscribe.php +++ b/actions/unsubscribe.php @@ -77,7 +77,8 @@ class UnsubscribeAction extends Action $this->elementEnd('html'); } else { common_redirect(common_local_url('subscriptions', array('nickname' => - $user->nickname))); + $user->nickname)), + 303); } } } diff --git a/actions/userauthorization.php b/actions/userauthorization.php index 6a76e3a4c..8723848c7 100644 --- a/actions/userauthorization.php +++ b/actions/userauthorization.php @@ -25,7 +25,7 @@ define('TIMESTAMP_THRESHOLD', 300); class UserauthorizationAction extends Action { var $error; - var $req; + var $params; function handle($args) { @@ -35,8 +35,8 @@ class UserauthorizationAction extends Action # CSRF protection $token = $this->trimmed('token'); if (!$token || $token != common_session_token()) { - $req = $this->getStoredRequest(); - $this->showForm($req, _('There was a problem with your session token. '. + $params = $this->getStoredParams(); + $this->showForm($params, _('There was a problem with your session token. '. 'Try again, please.')); return; } @@ -50,18 +50,13 @@ class UserauthorizationAction extends Action common_redirect(common_local_url('login')); return; } + try { - # this must be a new request - $req = $this->getNewRequest(); - if (!$req) { - $this->clientError(_('No request found!')); - } - # XXX: only validate new requests, since nonce is one-time use - $this->validateRequest($req); - $this->storeRequest($req); - $this->showForm($req); + $this->validateRequest(); + $this->storeParams($_GET); + $this->showForm($_GET); } catch (OAuthException $e) { - $this->clearRequest(); + $this->clearParams(); $this->clientError($e->getMessage()); return; } @@ -69,9 +64,9 @@ class UserauthorizationAction extends Action } } - function showForm($req, $error=null) + function showForm($params, $error=null) { - $this->req = $req; + $this->params = $params; $this->error = $error; $this->showPage(); } @@ -91,16 +86,16 @@ class UserauthorizationAction extends Action function showContent() { - $req = $this->req; + $params = $this->params; - $nickname = $req->get_parameter('omb_listenee_nickname'); - $profile = $req->get_parameter('omb_listenee_profile'); - $license = $req->get_parameter('omb_listenee_license'); - $fullname = $req->get_parameter('omb_listenee_fullname'); - $homepage = $req->get_parameter('omb_listenee_homepage'); - $bio = $req->get_parameter('omb_listenee_bio'); - $location = $req->get_parameter('omb_listenee_location'); - $avatar = $req->get_parameter('omb_listenee_avatar'); + $nickname = $params['omb_listenee_nickname']; + $profile = $params['omb_listenee_profile']; + $license = $params['omb_listenee_license']; + $fullname = $params['omb_listenee_fullname']; + $homepage = $params['omb_listenee_homepage']; + $bio = $params['omb_listenee_bio']; + $location = $params['omb_listenee_location']; + $avatar = $params['omb_listenee_avatar']; $this->elementStart('div', 'profile'); if ($avatar) { @@ -147,56 +142,56 @@ class UserauthorizationAction extends Action function sendAuthorization() { - $req = $this->getStoredRequest(); + $params = $this->getStoredParams(); - if (!$req) { + if (!$params) { $this->clientError(_('No authorization request!')); return; } - $callback = $req->get_parameter('oauth_callback'); + $callback = $params['oauth_callback']; if ($this->arg('accept')) { - if (!$this->authorizeToken($req)) { + if (!$this->authorizeToken($params)) { $this->clientError(_('Error authorizing token')); } - if (!$this->saveRemoteProfile($req)) { + if (!$this->saveRemoteProfile($params)) { $this->clientError(_('Error saving remote profile')); } if (!$callback) { - $this->showAcceptMessage($req->get_parameter('oauth_token')); + $this->showAcceptMessage($params['oauth_token']); } else { - $params = array(); - $params['oauth_token'] = $req->get_parameter('oauth_token'); - $params['omb_version'] = OMB_VERSION_01; - $user = User::staticGet('uri', $req->get_parameter('omb_listener')); + $newparams = array(); + $newparams['oauth_token'] = $params['oauth_token']; + $newparams['omb_version'] = OMB_VERSION_01; + $user = User::staticGet('uri', $params['omb_listener']); $profile = $user->getProfile(); if (!$profile) { common_log_db_error($user, 'SELECT', __FILE__); $this->serverError(_('User without matching profile')); return; } - $params['omb_listener_nickname'] = $user->nickname; - $params['omb_listener_profile'] = common_local_url('showstream', + $newparams['omb_listener_nickname'] = $user->nickname; + $newparams['omb_listener_profile'] = common_local_url('showstream', array('nickname' => $user->nickname)); if (!is_null($profile->fullname)) { - $params['omb_listener_fullname'] = $profile->fullname; + $newparams['omb_listener_fullname'] = $profile->fullname; } if (!is_null($profile->homepage)) { - $params['omb_listener_homepage'] = $profile->homepage; + $newparams['omb_listener_homepage'] = $profile->homepage; } if (!is_null($profile->bio)) { - $params['omb_listener_bio'] = $profile->bio; + $newparams['omb_listener_bio'] = $profile->bio; } if (!is_null($profile->location)) { - $params['omb_listener_location'] = $profile->location; + $newparams['omb_listener_location'] = $profile->location; } $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); if ($avatar) { - $params['omb_listener_avatar'] = $avatar->url; + $newparams['omb_listener_avatar'] = $avatar->url; } $parts = array(); - foreach ($params as $k => $v) { + foreach ($newparams as $k => $v) { $parts[] = $k . '=' . OAuthUtil::urlencode_rfc3986($v); } $query_string = implode('&', $parts); @@ -214,12 +209,10 @@ class UserauthorizationAction extends Action } } - function authorizeToken(&$req) + function authorizeToken(&$params) { - $consumer_key = $req->get_parameter('oauth_consumer_key'); - $token_field = $req->get_parameter('oauth_token'); + $token_field = $params['oauth_token']; $rt = new Token(); - $rt->consumer_key = $consumer_key; $rt->tok = $token_field; $rt->type = 0; $rt->state = 0; @@ -235,21 +228,21 @@ class UserauthorizationAction extends Action # XXX: refactor with similar code in finishremotesubscribe.php - function saveRemoteProfile(&$req) + function saveRemoteProfile(&$params) { # FIXME: we should really do this when the consumer comes # back for an access token. If they never do, we've got stuff in a # weird state. - $nickname = $req->get_parameter('omb_listenee_nickname'); - $fullname = $req->get_parameter('omb_listenee_fullname'); - $profile_url = $req->get_parameter('omb_listenee_profile'); - $homepage = $req->get_parameter('omb_listenee_homepage'); - $bio = $req->get_parameter('omb_listenee_bio'); - $location = $req->get_parameter('omb_listenee_location'); - $avatar_url = $req->get_parameter('omb_listenee_avatar'); + $nickname = $params['omb_listenee_nickname']; + $fullname = $params['omb_listenee_fullname']; + $profile_url = $params['omb_listenee_profile']; + $homepage = $params['omb_listenee_homepage']; + $bio = $params['omb_listenee_bio']; + $location = $params['omb_listenee_location']; + $avatar_url = $params['omb_listenee_avatar']; - $listenee = $req->get_parameter('omb_listenee'); + $listenee = $params['omb_listenee']; $remote = Remote_profile::staticGet('uri', $listenee); if ($remote) { @@ -309,14 +302,11 @@ class UserauthorizationAction extends Action } $user = common_current_user(); - $datastore = omb_oauth_datastore(); - $consumer = $this->getConsumer($datastore, $req); - $token = $this->getToken($datastore, $req, $consumer); $sub = new Subscription(); $sub->subscriber = $user->id; $sub->subscribed = $remote->id; - $sub->token = $token->key; # NOTE: request token, not valid for use! + $sub->token = $params['oauth_token']; # NOTE: request token, not valid for use! $sub->created = DB_DataObject_Cast::dateTime(); # current time if (!$sub->insert()) { @@ -360,65 +350,59 @@ class UserauthorizationAction extends Action common_show_footer(); } - function storeRequest($req) + function storeParams($params) { common_ensure_session(); - $_SESSION['userauthorizationrequest'] = $req; + $_SESSION['userauthorizationparams'] = $params; } - function clearRequest() + function clearParams() { common_ensure_session(); - unset($_SESSION['userauthorizationrequest']); + unset($_SESSION['userauthorizationparams']); } - function getStoredRequest() + function getStoredParams() { common_ensure_session(); - $req = $_SESSION['userauthorizationrequest']; - return $req; - } - - function getNewRequest() - { - common_remove_magic_from_request(); - $req = OAuthRequest::from_request(); - return $req; + $params = $_SESSION['userauthorizationparams']; + return $params; } # Throws an OAuthException if anything goes wrong - function validateRequest(&$req) + function validateRequest() { - # OAuth stuff -- have to copy from OAuth.php since they're - # all private methods, and there's no user-authentication method - $this->checkVersion($req); - $datastore = omb_oauth_datastore(); - $consumer = $this->getConsumer($datastore, $req); - $token = $this->getToken($datastore, $req, $consumer); - $this->checkTimestamp($req); - $this->checkNonce($datastore, $req, $consumer, $token); - $this->checkSignature($req, $consumer, $token); - $this->validateOmb($req); + /* Find token. + TODO: If no token is passed the user should get a prompt to enter it + according to OAuth Core 1.0 */ + $t = new Token(); + $t->tok = $_GET['oauth_token']; + $t->type = 0; + if (!$t->find(true)) { + throw new OAuthException("Invalid request token: " . $_GET['oauth_token']); + } + + $this->validateOmb(); return true; } - function validateOmb(&$req) + function validateOmb() { foreach (array('omb_version', 'omb_listener', 'omb_listenee', 'omb_listenee_profile', 'omb_listenee_nickname', 'omb_listenee_license') as $param) { - if (is_null($req->get_parameter($param))) { + if (!isset($_GET[$param]) || is_null($_GET[$param])) { throw new OAuthException("Required parameter '$param' not found"); } } # Now, OMB stuff - $version = $req->get_parameter('omb_version'); + $version = $_GET['omb_version']; if ($version != OMB_VERSION_01) { throw new OAuthException("OpenMicroBlogging version '$version' not supported"); } - $listener = $req->get_parameter('omb_listener'); + $listener = $_GET['omb_listener']; $user = User::staticGet('uri', $listener); if (!$user) { throw new OAuthException("Listener URI '$listener' not found here"); @@ -427,7 +411,7 @@ class UserauthorizationAction extends Action if ($cur->id != $user->id) { throw new OAuthException("Can't add for another user!"); } - $listenee = $req->get_parameter('omb_listenee'); + $listenee = $_GET['omb_listenee']; if (!Validate::uri($listenee) && !common_valid_tag($listenee)) { throw new OAuthException("Listenee URI '$listenee' not a recognizable URI"); @@ -450,13 +434,13 @@ class UserauthorizationAction extends Action throw new OAuthException("Already subscribed to user!"); } } - $nickname = $req->get_parameter('omb_listenee_nickname'); + $nickname = $_GET['omb_listenee_nickname']; if (!Validate::string($nickname, array('min_length' => 1, 'max_length' => 64, 'format' => VALIDATE_NUM . VALIDATE_ALPHA_LOWER))) { throw new OAuthException('Nickname must have only letters and numbers and no spaces.'); } - $profile = $req->get_parameter('omb_listenee_profile'); + $profile = $_GET['omb_listenee_profile']; if (!common_valid_http_url($profile)) { throw new OAuthException("Invalid profile URL '$profile'."); } @@ -465,7 +449,7 @@ class UserauthorizationAction extends Action throw new OAuthException("Profile URL '$profile' is for a local user."); } - $license = $req->get_parameter('omb_listenee_license'); + $license = $_GET['omb_listenee_license']; if (!common_valid_http_url($license)) { throw new OAuthException("Invalid license URL '$license'."); } @@ -474,23 +458,23 @@ class UserauthorizationAction extends Action throw new OAuthException("Listenee stream license '$license' not compatible with site license '$site_license'."); } # optional stuff - $fullname = $req->get_parameter('omb_listenee_fullname'); + $fullname = $_GET['omb_listenee_fullname']; if ($fullname && mb_strlen($fullname) > 255) { throw new OAuthException("Full name '$fullname' too long."); } - $homepage = $req->get_parameter('omb_listenee_homepage'); + $homepage = $_GET['omb_listenee_homepage']; if ($homepage && (!common_valid_http_url($homepage) || mb_strlen($homepage) > 255)) { throw new OAuthException("Invalid homepage '$homepage'"); } - $bio = $req->get_parameter('omb_listenee_bio'); + $bio = $_GET['omb_listenee_bio']; if ($bio && mb_strlen($bio) > 140) { throw new OAuthException("Bio too long '$bio'"); } - $location = $req->get_parameter('omb_listenee_location'); + $location = $_GET['omb_listenee_location']; if ($location && mb_strlen($location) > 255) { throw new OAuthException("Location too long '$location'"); } - $avatar = $req->get_parameter('omb_listenee_avatar'); + $avatar = $_GET['omb_listenee_avatar']; if ($avatar) { if (!common_valid_http_url($avatar) || strlen($avatar) > 255) { throw new OAuthException("Invalid avatar URL '$avatar'"); @@ -507,7 +491,7 @@ class UserauthorizationAction extends Action throw new OAuthException("Wrong image type for '$avatar'"); } } - $callback = $req->get_parameter('oauth_callback'); + $callback = $_GET['oauth_callback']; if ($callback && !common_valid_http_url($callback)) { throw new OAuthException("Invalid callback URL '$callback'"); } @@ -515,92 +499,4 @@ class UserauthorizationAction extends Action throw new OAuthException("Callback URL '$callback' is for local site."); } } - - # Snagged from OAuthServer - - function checkVersion(&$req) - { - $version = $req->get_parameter("oauth_version"); - if (!$version) { - $version = 1.0; - } - if ($version != 1.0) { - throw new OAuthException("OAuth version '$version' not supported"); - } - return $version; - } - - # Snagged from OAuthServer - - function getConsumer($datastore, $req) - { - $consumer_key = @$req->get_parameter("oauth_consumer_key"); - if (!$consumer_key) { - throw new OAuthException("Invalid consumer key"); - } - - $consumer = $datastore->lookup_consumer($consumer_key); - if (!$consumer) { - throw new OAuthException("Invalid consumer"); - } - return $consumer; - } - - # Mostly cadged from OAuthServer - - function getToken($datastore, &$req, $consumer) - {/*{{{*/ - $token_field = @$req->get_parameter('oauth_token'); - $token = $datastore->lookup_token($consumer, 'request', $token_field); - if (!$token) { - throw new OAuthException("Invalid $token_type token: $token_field"); - } - return $token; - } - - function checkTimestamp(&$req) - { - $timestamp = @$req->get_parameter('oauth_timestamp'); - $now = time(); - if ($now - $timestamp > TIMESTAMP_THRESHOLD) { - throw new OAuthException("Expired timestamp, yours $timestamp, ours $now"); - } - } - - # NOTE: don't call twice on the same request; will fail! - function checkNonce(&$datastore, &$req, $consumer, $token) - { - $timestamp = @$req->get_parameter('oauth_timestamp'); - $nonce = @$req->get_parameter('oauth_nonce'); - $found = $datastore->lookup_nonce($consumer, $token, $nonce, $timestamp); - if ($found) { - throw new OAuthException("Nonce already used"); - } - return true; - } - - function checkSignature(&$req, $consumer, $token) - { - $signature_method = $this->getSignatureMethod($req); - $signature = $req->get_parameter('oauth_signature'); - $valid_sig = $signature_method->check_signature($req, - $consumer, - $token, - $signature); - if (!$valid_sig) { - throw new OAuthException("Invalid signature"); - } - } - - function getSignatureMethod(&$req) - { - $signature_method = @$req->get_parameter("oauth_signature_method"); - if (!$signature_method) { - $signature_method = "PLAINTEXT"; - } - if ($signature_method != 'HMAC-SHA1') { - throw new OAuthException("Signature method '$signature_method' not supported."); - } - return omb_hmac_sha1(); - } } diff --git a/actions/usergroups.php b/actions/usergroups.php index ded4ba76b..06b2334bf 100644 --- a/actions/usergroups.php +++ b/actions/usergroups.php @@ -139,10 +139,28 @@ class UsergroupsAction extends Action if ($groups) { $gl = new GroupList($groups, $this->user, $this); $cnt = $gl->show(); + if (0 == $cnt) { + $this->showEmptyListMessage(); + } } $this->pagination($this->page > 1, $cnt > GROUPS_PER_PAGE, $this->page, 'usergroups', array('nickname' => $this->user->nickname)); } + + function showEmptyListMessage() + { + $message = sprintf(_('%s is not a member of any group.'), $this->user->nickname) . ' '; + + if (common_logged_in()) { + $current_user = common_current_user(); + if ($this->user->id === $current_user->id) { + $message .= _('Try [searching for groups](%%action.groupsearch%%) and joining them.'); + } + } + $this->elementStart('div', 'guide'); + $this->raw(common_markup_to_html($message)); + $this->elementEnd('div'); + } } diff --git a/classes/Notice.php b/classes/Notice.php index 44a6aeb98..5fa0d79a1 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -67,6 +67,8 @@ class Notice extends Memcached_DataObject $this->blowSubsCache(true); $this->query('BEGIN'); + //Null any notices that are replies to this notice + $this->query(sprintf("UPDATE notice set reply_to = null WHERE reply_to = %d", $this->id)); $related = array('Reply', 'Fave', 'Notice_tag', diff --git a/classes/User.php b/classes/User.php index d9f30bec5..3b9b5cd83 100644 --- a/classes/User.php +++ b/classes/User.php @@ -1,7 +1,7 @@ <?php /* * Laconica - a distributed open-source microblogging tool - * Copyright (C) 2008, Controlez-Vous, Inc. + * Copyright (C) 2008, 2009, Control Yourself, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -17,11 +17,14 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -if (!defined('LACONICA')) { exit(1); } +if (!defined('LACONICA')) { + exit(1); +} /** * Table Definition for user */ + require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; require_once 'Validate.php'; @@ -79,13 +82,13 @@ class User extends Memcached_DataObject function isSubscribed($other) { assert(!is_null($other)); - # XXX: cache results of this query + // XXX: cache results of this query $sub = Subscription::pkeyGet(array('subscriber' => $this->id, 'subscribed' => $other->id)); return (is_null($sub)) ? false : true; } - # 'update' won't write key columns, so we have to do it ourselves. + // 'update' won't write key columns, so we have to do it ourselves. function updateKeys(&$orig) { @@ -96,7 +99,7 @@ class User extends Memcached_DataObject } } if (count($parts) == 0) { - # No changes + // No changes return true; } $toupdate = implode(', ', $parts); @@ -117,7 +120,7 @@ class User extends Memcached_DataObject function allowed_nickname($nickname) { - # XXX: should already be validated for size, content, etc. + // XXX: should already be validated for size, content, etc. static $blacklist = array('rss', 'xrds', 'doc', 'main', 'settings', 'notice', 'user', 'search', 'avatar', 'tag', 'tags', @@ -147,7 +150,7 @@ class User extends Memcached_DataObject $sub->subscriber = $this->id; $sub->subscribed = $other->id; - $sub->created = common_sql_now(); # current time + $sub->created = common_sql_now(); // current time if (!$sub->insert()) { return false; @@ -173,7 +176,7 @@ class User extends Memcached_DataObject static function register($fields) { - # MAGICALLY put fields into current scope + // MAGICALLY put fields into current scope extract($fields); @@ -211,11 +214,11 @@ class User extends Memcached_DataObject $user->id = $id; $user->nickname = $nickname; - if (!empty($password)) { # may not have a password for OpenID users + if (!empty($password)) { // may not have a password for OpenID users $user->password = common_munge_password($password, $id); } - # Users who respond to invite email have proven their ownership of that address + // Users who respond to invite email have proven their ownership of that address if (!empty($code)) { $invite = Invitation::staticGet($code); @@ -240,7 +243,7 @@ class User extends Memcached_DataObject return false; } - # Everyone is subscribed to themself + // Everyone is subscribed to themself $subscription = new Subscription(); $subscription->subscriber = $user->id; @@ -273,16 +276,58 @@ class User extends Memcached_DataObject $user->emailChanged(); } + // Default system subscription + + $defnick = common_config('newuser', 'default'); + + if (!empty($defnick)) { + $defuser = User::staticGet('nickname', $defnick); + if (empty($defuser)) { + common_log(LOG_WARNING, sprintf("Default user %s does not exist.", $defnick), + __FILE__); + } else { + $defsub = new Subscription(); + $defsub->subscriber = $user->id; + $defsub->subscribed = $defuser->id; + $defsub->created = $user->created; + + $result = $defsub->insert(); + + if (!$result) { + common_log_db_error($defsub, 'INSERT', __FILE__); + return false; + } + } + } + $profile->query('COMMIT'); if ($email && !$user->email) { mail_confirm_address($user, $confirm->code, $profile->nickname, $email); } + // Welcome message + + $welcome = common_config('newuser', 'welcome'); + + if (!empty($welcome)) { + $welcomeuser = User::staticGet('nickname', $welcome); + if (empty($welcomeuser)) { + common_log(LOG_WARNING, sprintf("Welcome user %s does not exist.", $defnick), + __FILE__); + } else { + $notice = Notice::saveNew($welcomeuser->id, + sprintf(_('Welcome to %1$s, @%2$s!'), + common_config('site', 'name'), + $user->nickname), + 'system'); + } + } + return $user; } - # Things we do when the email changes + // Things we do when the email changes function emailChanged() { @@ -303,46 +348,46 @@ class User extends Memcached_DataObject { $cache = common_memcache(); - # XXX: Kind of a hack. + // XXX: Kind of a hack. if ($cache) { - # This is the stream of favorite notices, in rev chron - # order. This forces it into cache. + // This is the stream of favorite notices, in rev chron + // order. This forces it into cache. $faves = $this->favoriteNotices(0, NOTICE_CACHE_WINDOW); $cnt = 0; while ($faves->fetch()) { if ($faves->id < $notice->id) { - # If we passed it, it's not a fave + // If we passed it, it's not a fave return false; } else if ($faves->id == $notice->id) { - # If it matches a cached notice, then it's a fave + // If it matches a cached notice, then it's a fave return true; } $cnt++; } - # If we're not past the end of the cache window, - # then the cache has all available faves, so this one - # is not a fave. + // If we're not past the end of the cache window, + // then the cache has all available faves, so this one + // is not a fave. if ($cnt < NOTICE_CACHE_WINDOW) { return false; } - # Otherwise, cache doesn't have all faves; - # fall through to the default + // Otherwise, cache doesn't have all faves; + // fall through to the default } $fave = Fave::pkeyGet(array('user_id' => $this->id, 'notice_id' => $notice->id)); return ((is_null($fave)) ? false : true); } + function mutuallySubscribed($other) { return $this->isSubscribed($other) && $other->isSubscribed($this); } - function mutuallySubscribedUsers() - { - - # 3-way join; probably should get cached - $UT = common_config('db','type')=='pgsql'?'"user"':'user'; + function mutuallySubscribedUsers() + { + // 3-way join; probably should get cached + $UT = common_config('db','type')=='pgsql'?'"user"':'user'; $qry = "SELECT $UT.* " . "FROM subscription sub1 JOIN $UT ON sub1.subscribed = $UT.id " . "JOIN subscription sub2 ON $UT.id = sub2.subscriber " . @@ -365,8 +410,8 @@ class User extends Memcached_DataObject $offset, $limit, $since_id, $before_id, null, $since); } - function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) - { + function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) + { $profile = $this->getProfile(); if (!$profile) { return null; @@ -375,8 +420,8 @@ class User extends Memcached_DataObject } } - function favoriteNotices($offset=0, $limit=NOTICES_PER_PAGE) - { + function favoriteNotices($offset=0, $limit=NOTICES_PER_PAGE) + { $qry = 'SELECT notice.* ' . 'FROM notice JOIN fave ON notice.id = fave.notice_id ' . @@ -386,12 +431,12 @@ class User extends Memcached_DataObject $offset, $limit); } - function noticesWithFriends($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) - { + function noticesWithFriends($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) + { $enabled = common_config('inboxes', 'enabled'); - # Complicated code, depending on whether we support inboxes yet - # XXX: make this go away when inboxes become mandatory + // Complicated code, depending on whether we support inboxes yet + // XXX: make this go away when inboxes become mandatory if ($enabled === false || ($enabled == 'transitional' && $this->inboxed == 0)) { @@ -401,13 +446,13 @@ class User extends Memcached_DataObject 'WHERE subscription.subscriber = %d '; $order = null; } else if ($enabled === true || - ($enabled == 'transitional' && $this->inboxed == 1)) { + ($enabled == 'transitional' && $this->inboxed == 1)) { $qry = 'SELECT notice.* ' . 'FROM notice JOIN notice_inbox ON notice.id = notice_inbox.notice_id ' . 'WHERE notice_inbox.user_id = %d '; - # NOTE: we override ORDER + // NOTE: we override ORDER $order = null; } return Notice::getStream(sprintf($qry, $this->id), @@ -416,35 +461,34 @@ class User extends Memcached_DataObject $order, $since); } - function blowFavesCache() - { + function blowFavesCache() + { $cache = common_memcache(); if ($cache) { - # Faves don't happen chronologically, so we need to blow - # ;last cache, too + // Faves don't happen chronologically, so we need to blow + // ;last cache, too $cache->delete(common_cache_key('user:faves:'.$this->id)); $cache->delete(common_cache_key('user:faves:'.$this->id).';last'); } } - function getSelfTags() - { + function getSelfTags() + { return Profile_tag::getTags($this->id, $this->id); } - function setSelfTags($newtags) - { + function setSelfTags($newtags) + { return Profile_tag::setTags($this->id, $this->id, $newtags); } function block($other) { - - # Add a new block record + // Add a new block record $block = new Profile_block(); - # Begin a transaction + // Begin a transaction $block->query('BEGIN'); @@ -458,7 +502,7 @@ class User extends Memcached_DataObject return false; } - # Cancel their subscription, if it exists + // Cancel their subscription, if it exists $sub = Subscription::pkeyGet(array('subscriber' => $other->id, 'subscribed' => $this->id)); @@ -478,8 +522,7 @@ class User extends Memcached_DataObject function unblock($other) { - - # Get the block record + // Get the block record $block = Profile_block::get($this->id, $other->id); diff --git a/config.php.sample b/config.php.sample index 529e86f15..0a01552fe 100644 --- a/config.php.sample +++ b/config.php.sample @@ -163,6 +163,10 @@ $config['sphinx']['port'] = 3312; # require_once('plugins/GoogleAnalyticsPlugin.php'); # $ga = new GoogleAnalyticsPlugin('your secret code'); +# Use Templating (template: /tpl/index.php) +# require_once('plugins/TemplatePlugin.php'); +# $tpl = new TemplatePlugin(); + #Don't allow saying the same thing more than once per hour #$config['site']['dupelimit'] = 3600; #Don't enforce the dupe limit @@ -174,3 +178,13 @@ $config['sphinx']['port'] = 3312; #http://taguri.org/ Examples: #$config['integration']['taguri'] = 'example.net,2008'; #$config['integration']['taguri'] = 'admin@example.net,2009-03-09' + +#Don't use SSL +#$config['site']['ssl'] = 'never'; +#Use SSL only for sensitive pages (like login, password change) +#$config['site']['ssl'] = 'sometimes'; +#Use SSL for all pages +#$config['site']['ssl'] = 'always'; + +#Use a different hostname for SSL-encrypted pages +#$config['site']['sslserver'] = 'secure.example.org'; diff --git a/db/laconica.sql b/db/laconica.sql index 098fa4fd1..a790a3fd2 100644 --- a/db/laconica.sql +++ b/db/laconica.sql @@ -13,7 +13,7 @@ create table profile ( index profile_nickname_idx (nickname), FULLTEXT(nickname, fullname, location, bio, homepage) -) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_bin; +) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci; create table avatar ( profile_id integer not null comment 'foreign key to profile table' references profile (id), @@ -73,7 +73,7 @@ create table user ( modified timestamp comment 'date this record was modified', index user_smsemail_idx (smsemail) -) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; +) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci; /* remote people */ @@ -103,7 +103,6 @@ create table subscription ( ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; create table notice ( - id integer auto_increment primary key comment 'unique identifier', profile_id integer not null comment 'who made the update' references profile (id), uri varchar(255) unique key comment 'universally unique identifier, usually a tag URI', @@ -119,7 +118,7 @@ create table notice ( index notice_profile_id_idx (profile_id), index notice_created_idx (created), FULLTEXT(content) -) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_bin; +) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci; create table notice_source ( code varchar(32) primary key not null comment 'source code', @@ -130,7 +129,6 @@ create table notice_source ( ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; create table reply ( - notice_id integer not null comment 'notice that is the reply' references notice (id), profile_id integer not null comment 'profile replied to' references profile (id), modified timestamp not null comment 'date this record was modified', @@ -144,7 +142,6 @@ create table reply ( ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; create table fave ( - notice_id integer not null comment 'notice that is the favorite' references notice (id), user_id integer not null comment 'user who likes this notice' references user (id), modified timestamp not null comment 'date this record was modified', @@ -321,7 +318,6 @@ create table invitation ( ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; create table message ( - id integer auto_increment primary key comment 'unique identifier', uri varchar(255) unique key comment 'universally unique identifier', from_profile integer not null comment 'who the message is from' references profile (id), @@ -336,10 +332,9 @@ create table message ( index message_from_idx (from_profile), index message_to_idx (to_profile), index message_created_idx (created) -) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; +) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci; create table notice_inbox ( - user_id integer not null comment 'user receiving the message' references user (id), notice_id integer not null comment 'notice received' references notice (id), created datetime not null comment 'date the notice was created', @@ -362,7 +357,6 @@ create table profile_tag ( ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; create table profile_block ( - blocker integer not null comment 'user making the block' references user (id), blocked integer not null comment 'profile that is blocked' references profile (id), modified timestamp comment 'date of blocking', @@ -372,7 +366,6 @@ create table profile_block ( ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; create table user_group ( - id integer auto_increment primary key comment 'unique identifier', nickname varchar(64) unique key comment 'nickname for addressing', @@ -391,10 +384,9 @@ create table user_group ( index user_group_nickname_idx (nickname) -) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; +) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci; create table group_member ( - group_id integer not null comment 'foreign key to user_group' references user_group (id), profile_id integer not null comment 'foreign key to profile table' references profile (id), is_admin boolean default false comment 'is this user an admin?', @@ -409,7 +401,6 @@ create table group_member ( ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; create table related_group ( - group_id integer not null comment 'foreign key to user_group' references user_group (id), related_group_id integer not null comment 'foreign key to user_group' references user_group (id), diff --git a/db/notice_source.sql b/db/notice_source.sql index d28a09383..52d555dbf 100644 --- a/db/notice_source.sql +++ b/db/notice_source.sql @@ -21,6 +21,7 @@ VALUES ('mbpidgin','mbpidgin','http://code.google.com/p/microblog-purple/', now()), ('Mobidentica', 'Mobidentica', 'http://www.substanceofcode.com/software/mobidentica/', now()), ('moconica','Moconica','http://moconica.com/', now()), + ('peoplebrowsr', 'PeopleBrowsr', 'http://www.peoplebrowsr.com/', now()), ('pocketwit','PockeTwit','http://code.google.com/p/pocketwit/', now()), ('posty','Posty','http://spreadingfunkyness.com/posty/', now()), ('royalewithcheese','Royale With Cheese','http://p.hellyeah.org/', now()), @@ -45,4 +46,5 @@ VALUES ('twitux','Twitux','http://live.gnome.org/DanielMorales/Twitux', now()), ('twitvim','TwitVim','http://vim.sourceforge.net/scripts/script.php?script_id=2204', now()), ('urfastr','urfastr','http://urfastr.net/', now()), - ('adium', 'Adium', 'http://www.adiumx.com/', now()); + ('adium', 'Adium', 'http://www.adiumx.com/', now()), + ('yatca','Yatca','http://www.yatca.com/', now()); diff --git a/doc-src/bookmarklet b/doc-src/bookmarklet new file mode 100644 index 000000000..6cd2c08f9 --- /dev/null +++ b/doc-src/bookmarklet @@ -0,0 +1,7 @@ +A bookmarklet is a small piece of javascript code used as a bookmark. This one will let you post to %%site.name%% simply by selecting some text on a page and pressing the bookmarklet. + +Drag-and-drop the following link to your bookmarks bar or right-click it and add it to your browser favorites to keep it handy. + +<MTMarkdownOptions output='raw'> +<a href="javascript:var%20d=document,w=window,e=w.getSelection,k=d.getSelection,x=d.selection,s=(e?e():(k)?k():(x?x.createRange().text:0)),f='http://%%site.server%%/%%site.path%%/index.php?action=newnotice',l=d.location,e=encodeURIComponent,g=f+'&status_textarea=%22'+((e(s))?e(s):e(document.title))+'%22 from '+l.href;function%20a(){if(!w.open(g,'t','toolbar=0,resizable=0,scrollbars=1,status=1,width=800,height=570')){l.href=g;}}a();void(0);">Post to %%site.name%%</a> +</MTMarkdownOptions> diff --git a/doc-src/help b/doc-src/help index a8cfccd2b..02cf0d14b 100644 --- a/doc-src/help +++ b/doc-src/help @@ -30,3 +30,5 @@ Here are some documents that you might find helpful in understanding * [OpenMicroBlogging](%%doc.openmublog%%) - subscribing to remote users * [Privacy](%%doc.privacy%%) - %%site.name%%'s privacy policy * [Source](%%doc.source%%) - How to get the Laconica source code +* [Badge](%%doc.badge%%) - How to put a Laconica badge on your blog or homepage +* [Bookmarklet](%%doc.bookmarklet%%) - Bookmarklet for posting Web pages
\ No newline at end of file diff --git a/js/jquery.js b/js/jquery.js index 94e9c1755..926357433 100644 --- a/js/jquery.js +++ b/js/jquery.js @@ -1,13 +1,13 @@ /*! - * jQuery JavaScript Library v1.3.1 + * jQuery JavaScript Library v1.3.2 * http://jquery.com/ * * Copyright (c) 2009 John Resig * Dual licensed under the MIT and GPL licenses. * http://docs.jquery.com/License * - * Date: 2009-01-21 20:42:16 -0500 (Wed, 21 Jan 2009) - * Revision: 6158 + * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) + * Revision: 6246 */ (function(){ @@ -88,14 +88,16 @@ jQuery.fn = jQuery.prototype = { this.context = selector.context; } - return this.setArray(jQuery.makeArray(selector)); + return this.setArray(jQuery.isArray( selector ) ? + selector : + jQuery.makeArray(selector)); }, // Start with an empty selector selector: "", // The current version of jQuery being used - jquery: "1.3.1", + jquery: "1.3.2", // The number of elements contained in the matched element set size: function() { @@ -108,7 +110,7 @@ jQuery.fn = jQuery.prototype = { return num === undefined ? // Return a 'clean' array - jQuery.makeArray( this ) : + Array.prototype.slice.call( this ) : // Return just the object this[ num ]; @@ -278,23 +280,21 @@ jQuery.fn = jQuery.prototype = { }, // For internal use only. - // Behaves like an Array's .push method, not like a jQuery method. + // Behaves like an Array's method, not like a jQuery method. push: [].push, + sort: [].sort, + splice: [].splice, find: function( selector ) { - if ( this.length === 1 && !/,/.test(selector) ) { + if ( this.length === 1 ) { var ret = this.pushStack( [], "find", selector ); ret.length = 0; jQuery.find( selector, this[0], ret ); return ret; } else { - var elems = jQuery.map(this, function(elem){ + return this.pushStack( jQuery.unique(jQuery.map(this, function(elem){ return jQuery.find( selector, elem ); - }); - - return this.pushStack( /[^+>] [^+>]/.test( selector ) ? - jQuery.unique( elems ) : - elems, "find", selector ); + })), "find", selector ); } }, @@ -310,33 +310,37 @@ jQuery.fn = jQuery.prototype = { // attributes in IE that are actually only stored // as properties will not be copied (such as the // the name attribute on an input). - var clone = this.cloneNode(true), - container = document.createElement("div"); - container.appendChild(clone); - return jQuery.clean([container.innerHTML])[0]; + var html = this.outerHTML; + if ( !html ) { + var div = this.ownerDocument.createElement("div"); + div.appendChild( this.cloneNode(true) ); + html = div.innerHTML; + } + + return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0]; } else return this.cloneNode(true); }); - // Need to set the expando to null on the cloned set if it exists - // removeData doesn't work here, IE removes it from the original as well - // this is primarily for IE but the data expando shouldn't be copied over in any browser - var clone = ret.find("*").andSelf().each(function(){ - if ( this[ expando ] !== undefined ) - this[ expando ] = null; - }); - // Copy the events from the original to the clone - if ( events === true ) - this.find("*").andSelf().each(function(i){ - if (this.nodeType == 3) + if ( events === true ) { + var orig = this.find("*").andSelf(), i = 0; + + ret.find("*").andSelf().each(function(){ + if ( this.nodeName !== orig[i].nodeName ) return; - var events = jQuery.data( this, "events" ); - for ( var type in events ) - for ( var handler in events[ type ] ) - jQuery.event.add( clone[ i ], type, events[ type ][ handler ], events[ type ][ handler ].data ); + var events = jQuery.data( orig[i], "events" ); + + for ( var type in events ) { + for ( var handler in events[ type ] ) { + jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data ); + } + } + + i++; }); + } // Return the cloned set return ret; @@ -355,14 +359,18 @@ jQuery.fn = jQuery.prototype = { }, closest: function( selector ) { - var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null; + var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null, + closer = 0; return this.map(function(){ var cur = this; while ( cur && cur.ownerDocument ) { - if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) + if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) { + jQuery.data(cur, "closest", closer); return cur; + } cur = cur.parentNode; + closer++; } }); }, @@ -475,7 +483,7 @@ jQuery.fn = jQuery.prototype = { html: function( value ) { return value === undefined ? (this[0] ? - this[0].innerHTML : + this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") : null) : this.empty().append( value ); }, @@ -507,13 +515,13 @@ jQuery.fn = jQuery.prototype = { if ( this[0] ) { var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(), scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ), - first = fragment.firstChild, - extra = this.length > 1 ? fragment.cloneNode(true) : fragment; + first = fragment.firstChild; if ( first ) for ( var i = 0, l = this.length; i < l; i++ ) - callback.call( root(this[i], first), i > 0 ? extra.cloneNode(true) : fragment ); - + callback.call( root(this[i], first), this.length > 1 || i > 0 ? + fragment.cloneNode(true) : fragment ); + if ( scripts ) jQuery.each( scripts, evalScript ); } @@ -636,9 +644,7 @@ jQuery.extend({ // Evalulates a script in a global context globalEval: function( data ) { - data = jQuery.trim( data ); - - if ( data ) { + if ( data && /\S/.test(data) ) { // Inspired by code by Andrea Giammarchi // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html var head = document.getElementsByTagName("head")[0] || document.documentElement, @@ -741,26 +747,32 @@ jQuery.extend({ elem.style[ name ] = old[ name ]; }, - css: function( elem, name, force ) { + css: function( elem, name, force, extra ) { if ( name == "width" || name == "height" ) { var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ]; function getWH() { val = name == "width" ? elem.offsetWidth : elem.offsetHeight; - var padding = 0, border = 0; + + if ( extra === "border" ) + return; + jQuery.each( which, function() { - padding += parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0; - border += parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0; + if ( !extra ) + val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0; + if ( extra === "margin" ) + val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0; + else + val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0; }); - val -= Math.round(padding + border); } - if ( jQuery(elem).is(":visible") ) + if ( elem.offsetWidth !== 0 ) getWH(); else jQuery.swap( elem, props, getWH ); - return Math.max(0, val); + return Math.max(0, Math.round(val)); } return jQuery.curCSS( elem, name, force ); @@ -866,7 +878,7 @@ jQuery.extend({ }); // Trim whitespace, otherwise indexOf won't work as expected - var tags = jQuery.trim( elem ).toLowerCase(); + var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase(); var wrap = // option or optgroup @@ -906,11 +918,12 @@ jQuery.extend({ if ( !jQuery.support.tbody ) { // String was a <table>, *may* have spurious <tbody> - var tbody = !tags.indexOf("<table") && tags.indexOf("<tbody") < 0 ? - div.firstChild && div.firstChild.childNodes : + var hasBody = /<tbody/i.test(elem), + tbody = !tags.indexOf("<table") && !hasBody ? + div.firstChild && div.firstChild.childNodes : // String was a bare <thead> or <tfoot> - wrap[1] == "<table>" && tags.indexOf("<tbody") < 0 ? + wrap[1] == "<table>" && !hasBody ? div.childNodes : []; @@ -1189,13 +1202,16 @@ jQuery.each({ insertAfter: "after", replaceAll: "replaceWith" }, function(name, original){ - jQuery.fn[ name ] = function() { - var args = arguments; + jQuery.fn[ name ] = function( selector ) { + var ret = [], insert = jQuery( selector ); - return this.each(function(){ - for ( var i = 0, length = args.length; i < length; i++ ) - jQuery( args[ i ] )[ original ]( this ); - }); + for ( var i = 0, l = insert.length; i < l; i++ ) { + var elems = (i > 0 ? this.clone(true) : this).get(); + jQuery.fn[ original ].apply( jQuery(insert[i]), elems ); + ret = ret.concat( elems ); + } + + return this.pushStack( ret, name, selector ); }; }); @@ -1234,7 +1250,7 @@ jQuery.each({ empty: function() { // Remove element nodes and prevent memory leaks - jQuery( ">*", this ).remove(); + jQuery(this).children().remove(); // Remove any remaining nodes while ( this.firstChild ) @@ -1402,7 +1418,7 @@ jQuery.fn.extend({ */ (function(){ -var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]+['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[]+)+|[>+~])(\s*,\s*)?/g, +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g, done = 0, toString = Object.prototype.toString; @@ -1507,6 +1523,19 @@ var Sizzle = function(selector, context, results, seed) { if ( extra ) { Sizzle( extra, context, results, seed ); + + if ( sortOrder ) { + hasDuplicate = false; + results.sort(sortOrder); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[i-1] ) { + results.splice(i--, 1); + } + } + } + } } return results; @@ -1548,7 +1577,8 @@ Sizzle.find = function(expr, context, isXML){ }; Sizzle.filter = function(expr, set, inplace, not){ - var old = expr, result = [], curLoop = set, match, anyFound; + var old = expr, result = [], curLoop = set, match, anyFound, + isXMLFilter = set && set[0] && isXML(set[0]); while ( expr && set.length ) { for ( var type in Expr.filter ) { @@ -1561,7 +1591,7 @@ Sizzle.filter = function(expr, set, inplace, not){ } if ( Expr.preFilter[ type ] ) { - match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not ); + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); if ( !match ) { anyFound = found = true; @@ -1606,8 +1636,6 @@ Sizzle.filter = function(expr, set, inplace, not){ } } - expr = expr.replace(/\s*,\s*/, ""); - // Improper expression if ( expr == old ) { if ( anyFound == null ) { @@ -1645,26 +1673,33 @@ var Expr = Sizzle.selectors = { } }, relative: { - "+": function(checkSet, part){ - for ( var i = 0, l = checkSet.length; i < l; i++ ) { - var elem = checkSet[i]; - if ( elem ) { - var cur = elem.previousSibling; - while ( cur && cur.nodeType !== 1 ) { - cur = cur.previousSibling; - } - checkSet[i] = typeof part === "string" ? - cur || false : - cur === part; + "+": function(checkSet, part, isXML){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !/\W/.test(part), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag && !isXML ) { + part = part.toUpperCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ? + elem || false : + elem === part; } } - if ( typeof part === "string" ) { + if ( isPartStrNotTag ) { Sizzle.filter( part, checkSet, true ); } }, ">": function(checkSet, part, isXML){ - if ( typeof part === "string" && !/\W/.test(part) ) { + var isPartStr = typeof part === "string"; + + if ( isPartStr && !/\W/.test(part) ) { part = isXML ? part : part.toUpperCase(); for ( var i = 0, l = checkSet.length; i < l; i++ ) { @@ -1678,19 +1713,19 @@ var Expr = Sizzle.selectors = { for ( var i = 0, l = checkSet.length; i < l; i++ ) { var elem = checkSet[i]; if ( elem ) { - checkSet[i] = typeof part === "string" ? + checkSet[i] = isPartStr ? elem.parentNode : elem.parentNode === part; } } - if ( typeof part === "string" ) { + if ( isPartStr ) { Sizzle.filter( part, checkSet, true ); } } }, "": function(checkSet, part, isXML){ - var doneName = "done" + (done++), checkFn = dirCheck; + var doneName = done++, checkFn = dirCheck; if ( !part.match(/\W/) ) { var nodeCheck = part = isXML ? part : part.toUpperCase(); @@ -1700,7 +1735,7 @@ var Expr = Sizzle.selectors = { checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML); }, "~": function(checkSet, part, isXML){ - var doneName = "done" + (done++), checkFn = dirCheck; + var doneName = done++, checkFn = dirCheck; if ( typeof part === "string" && !part.match(/\W/) ) { var nodeCheck = part = isXML ? part : part.toUpperCase(); @@ -1718,8 +1753,16 @@ var Expr = Sizzle.selectors = { } }, NAME: function(match, context, isXML){ - if ( typeof context.getElementsByName !== "undefined" && !isXML ) { - return context.getElementsByName(match[1]); + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], results = context.getElementsByName(match[1]); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; } }, TAG: function(match, context){ @@ -1727,13 +1770,16 @@ var Expr = Sizzle.selectors = { } }, preFilter: { - CLASS: function(match, curLoop, inplace, result, not){ + CLASS: function(match, curLoop, inplace, result, not, isXML){ match = " " + match[1].replace(/\\/g, "") + " "; - var elem; - for ( var i = 0; (elem = curLoop[i]) != null; i++ ) { + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { if ( elem ) { - if ( not ^ (" " + elem.className + " ").indexOf(match) >= 0 ) { + if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) { if ( !inplace ) result.push( elem ); } else if ( inplace ) { @@ -1764,14 +1810,14 @@ var Expr = Sizzle.selectors = { } // TODO: Move to normal caching system - match[0] = "done" + (done++); + match[0] = done++; return match; }, - ATTR: function(match){ + ATTR: function(match, curLoop, inplace, result, not, isXML){ var name = match[1].replace(/\\/g, ""); - if ( Expr.attrMap[name] ) { + if ( !isXML && Expr.attrMap[name] ) { match[1] = Expr.attrMap[name]; } @@ -1784,7 +1830,7 @@ var Expr = Sizzle.selectors = { PSEUDO: function(match, curLoop, inplace, result, not){ if ( match[1] === "not" ) { // If we're dealing with a complex expression, or a simple one - if ( match[3].match(chunker).length > 1 ) { + if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) { match[3] = Sizzle(match[3], null, null, curLoop); } else { var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); @@ -1793,7 +1839,7 @@ var Expr = Sizzle.selectors = { } return false; } - } else if ( Expr.match.POS.test( match[0] ) ) { + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { return true; } @@ -1890,47 +1936,6 @@ var Expr = Sizzle.selectors = { } }, filter: { - CHILD: function(elem, match){ - var type = match[1], parent = elem.parentNode; - - var doneName = match[0]; - - if ( parent && (!parent[ doneName ] || !elem.nodeIndex) ) { - var count = 1; - - for ( var node = parent.firstChild; node; node = node.nextSibling ) { - if ( node.nodeType == 1 ) { - node.nodeIndex = count++; - } - } - - parent[ doneName ] = count - 1; - } - - if ( type == "first" ) { - return elem.nodeIndex == 1; - } else if ( type == "last" ) { - return elem.nodeIndex == parent[ doneName ]; - } else if ( type == "only" ) { - return parent[ doneName ] == 1; - } else if ( type == "nth" ) { - var add = false, first = match[2], last = match[3]; - - if ( first == 1 && last == 0 ) { - return true; - } - - if ( first == 0 ) { - if ( elem.nodeIndex == last ) { - add = true; - } - } else if ( (elem.nodeIndex - last) % first == 0 && (elem.nodeIndex - last) / first >= 0 ) { - add = true; - } - - return add; - } - }, PSEUDO: function(elem, match, i, array){ var name = match[1], filter = Expr.filters[ name ]; @@ -1950,6 +1955,49 @@ var Expr = Sizzle.selectors = { return true; } }, + CHILD: function(elem, match){ + var type = match[1], node = elem; + switch (type) { + case 'only': + case 'first': + while (node = node.previousSibling) { + if ( node.nodeType === 1 ) return false; + } + if ( type == 'first') return true; + node = elem; + case 'last': + while (node = node.nextSibling) { + if ( node.nodeType === 1 ) return false; + } + return true; + case 'nth': + var first = match[2], last = match[3]; + + if ( first == 1 && last == 0 ) { + return true; + } + + var doneName = match[0], + parent = elem.parentNode; + + if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { + var count = 0; + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + parent.sizcache = doneName; + } + + var diff = elem.nodeIndex - last; + if ( first == 0 ) { + return diff == 0; + } else { + return ( diff % first == 0 && diff / first >= 0 ); + } + } + }, ID: function(elem, match){ return elem.nodeType === 1 && elem.getAttribute("id") === match; }, @@ -1957,10 +2005,20 @@ var Expr = Sizzle.selectors = { return (match === "*" && elem.nodeType === 1) || elem.nodeName === match; }, CLASS: function(elem, match){ - return match.test( elem.className ); + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; }, ATTR: function(elem, match){ - var result = Expr.attrHandle[ match[1] ] ? Expr.attrHandle[ match[1] ]( elem ) : elem[ match[1] ] || elem.getAttribute( match[1] ), value = result + "", type = match[2], check = match[4]; + var name = match[1], + result = Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + return result == null ? type === "!=" : type === "=" ? @@ -1969,8 +2027,8 @@ var Expr = Sizzle.selectors = { value.indexOf(check) >= 0 : type === "~=" ? (" " + value + " ").indexOf(check) >= 0 : - !match[4] ? - result : + !check ? + value && result !== false : type === "!=" ? value != check : type === "^=" ? @@ -2036,6 +2094,39 @@ try { }; } +var sortOrder; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1; + if ( ret === 0 ) { + hasDuplicate = true; + } + return ret; + }; +} else if ( "sourceIndex" in document.documentElement ) { + sortOrder = function( a, b ) { + var ret = a.sourceIndex - b.sourceIndex; + if ( ret === 0 ) { + hasDuplicate = true; + } + return ret; + }; +} else if ( document.createRange ) { + sortOrder = function( a, b ) { + var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange(); + aRange.selectNode(a); + aRange.collapse(true); + bRange.selectNode(b); + bRange.collapse(true); + var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange); + if ( ret === 0 ) { + hasDuplicate = true; + } + return ret; + }; +} + // Check to see if the browser returns elements by name when // querying by getElementById (and provide a workaround) (function(){ @@ -2099,7 +2190,8 @@ try { // Check to see if an attribute returns normalized href attributes div.innerHTML = "<a href='#'></a>"; - if ( div.firstChild && div.firstChild.getAttribute("href") !== "#" ) { + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { Expr.attrHandle.href = function(elem){ return elem.getAttribute("href", 2); }; @@ -2136,29 +2228,50 @@ if ( document.querySelectorAll ) (function(){ Sizzle.matches = oldSizzle.matches; })(); -if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) { +if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){ + var div = document.createElement("div"); + div.innerHTML = "<div class='test e'></div><div class='test'></div>"; + + // Opera can't find a second classname (in 9.6) + if ( div.getElementsByClassName("e").length === 0 ) + return; + + // Safari caches class attributes, doesn't catch changes (in 3.2) + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) + return; + Expr.order.splice(1, 0, "CLASS"); - Expr.find.CLASS = function(match, context) { - return context.getElementsByClassName(match[1]); + Expr.find.CLASS = function(match, context, isXML) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } }; -} +})(); function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + var sibDir = dir == "previousSibling" && !isXML; for ( var i = 0, l = checkSet.length; i < l; i++ ) { var elem = checkSet[i]; if ( elem ) { + if ( sibDir && elem.nodeType === 1 ){ + elem.sizcache = doneName; + elem.sizset = i; + } elem = elem[dir]; var match = false; - while ( elem && elem.nodeType ) { - var done = elem[doneName]; - if ( done ) { - match = checkSet[ done ]; + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; break; } - if ( elem.nodeType === 1 && !isXML ) - elem[doneName] = i; + if ( elem.nodeType === 1 && !isXML ){ + elem.sizcache = doneName; + elem.sizset = i; + } if ( elem.nodeName === cur ) { match = elem; @@ -2174,22 +2287,28 @@ function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { } function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + var sibDir = dir == "previousSibling" && !isXML; for ( var i = 0, l = checkSet.length; i < l; i++ ) { var elem = checkSet[i]; if ( elem ) { + if ( sibDir && elem.nodeType === 1 ) { + elem.sizcache = doneName; + elem.sizset = i; + } elem = elem[dir]; var match = false; - while ( elem && elem.nodeType ) { - if ( elem[doneName] ) { - match = checkSet[ elem[doneName] ]; + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; break; } if ( elem.nodeType === 1 ) { - if ( !isXML ) - elem[doneName] = i; - + if ( !isXML ) { + elem.sizcache = doneName; + elem.sizset = i; + } if ( typeof cur !== "string" ) { if ( elem === cur ) { match = true; @@ -2248,15 +2367,11 @@ jQuery.expr = Sizzle.selectors; jQuery.expr[":"] = jQuery.expr.filters; Sizzle.selectors.filters.hidden = function(elem){ - return "hidden" === elem.type || - jQuery.css(elem, "display") === "none" || - jQuery.css(elem, "visibility") === "hidden"; + return elem.offsetWidth === 0 || elem.offsetHeight === 0; }; Sizzle.selectors.filters.visible = function(elem){ - return "hidden" !== elem.type && - jQuery.css(elem, "display") !== "none" && - jQuery.css(elem, "visibility") !== "hidden"; + return elem.offsetWidth > 0 || elem.offsetHeight > 0; }; Sizzle.selectors.filters.animated = function(elem){ @@ -2552,7 +2667,8 @@ jQuery.event = { var all, handlers; event = arguments[0] = jQuery.event.fix( event || window.event ); - + event.currentTarget = this; + // Namespaced event handlers var namespaces = event.type.split("."); event.type = namespaces.shift(); @@ -2883,9 +2999,13 @@ function liveHandler( event ){ } }); + elems.sort(function(a,b) { + return jQuery.data(a.elem, "closest") - jQuery.data(b.elem, "closest"); + }); + jQuery.each(elems, function(){ if ( this.fn.call(this.elem, event, this.fn.data) === false ) - stop = false; + return (stop = false); }); return stop; @@ -2949,7 +3069,7 @@ function bindReady(){ // If IE and not an iframe // continually check to see if the document is ready - if ( document.documentElement.doScroll && typeof window.frameElement === "undefined" ) (function(){ + if ( document.documentElement.doScroll && window == window.top ) (function(){ if ( jQuery.isReady ) return; try { @@ -3079,12 +3199,11 @@ jQuery( window ).bind( 'unload', function(){ // document.body must exist before we can do this jQuery(function(){ var div = document.createElement("div"); - div.style.width = "1px"; - div.style.paddingLeft = "1px"; + div.style.width = div.style.paddingLeft = "1px"; document.body.appendChild( div ); jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2; - document.body.removeChild( div ); + document.body.removeChild( div ).style.display = 'none'; }); })(); @@ -3175,7 +3294,7 @@ jQuery.fn.extend({ .filter(function(){ return this.name && !this.disabled && (this.checked || /select|textarea/i.test(this.nodeName) || - /text|hidden|password/i.test(this.type)); + /text|hidden|password|search/i.test(this.type)); }) .map(function(i, elem){ var val = jQuery(this).val(); @@ -3371,6 +3490,9 @@ jQuery.extend({ done = true; success(); complete(); + + // Handle memory leak in IE + script.onload = script.onreadystatechange = null; head.removeChild( script ); } }; @@ -3686,9 +3808,15 @@ jQuery.fn.extend({ elemdisplay[ tagName ] = display; } - this[i].style.display = jQuery.data(this[i], "olddisplay", display); + jQuery.data(this[i], "olddisplay", display); } } + + // Set the display of the elements in a second loop + // to avoid the constant reflow + for ( var i = 0, l = this.length; i < l; i++ ){ + this[i].style.display = jQuery.data(this[i], "olddisplay") || ""; + } return this; } @@ -3702,8 +3830,14 @@ jQuery.fn.extend({ var old = jQuery.data(this[i], "olddisplay"); if ( !old && old !== "none" ) jQuery.data(this[i], "olddisplay", jQuery.css(this[i], "display")); + } + + // Set the display of the elements in a second loop + // to avoid the constant reflow + for ( var i = 0, l = this.length; i < l; i++ ){ this[i].style.display = "none"; } + return this; } }, @@ -3915,7 +4049,7 @@ jQuery.fx.prototype = { t.elem = this.elem; - if ( t() && jQuery.timers.push(t) == 1 ) { + if ( t() && jQuery.timers.push(t) && !timerId ) { timerId = setInterval(function(){ var timers = jQuery.timers; @@ -3925,6 +4059,7 @@ jQuery.fx.prototype = { if ( !timers.length ) { clearInterval( timerId ); + timerId = undefined; } }, 13); } @@ -4193,22 +4328,21 @@ jQuery.each( ['Left', 'Top'], function(i, name) { jQuery.each([ "Height", "Width" ], function(i, name){ var tl = i ? "Left" : "Top", // top or left - br = i ? "Right" : "Bottom"; // bottom or right + br = i ? "Right" : "Bottom", // bottom or right + lower = name.toLowerCase(); // innerHeight and innerWidth jQuery.fn["inner" + name] = function(){ - return this[ name.toLowerCase() ]() + - num(this, "padding" + tl) + - num(this, "padding" + br); + return this[0] ? + jQuery.css( this[0], lower, false, "padding" ) : + null; }; // outerHeight and outerWidth jQuery.fn["outer" + name] = function(margin) { - return this["inner" + name]() + - num(this, "border" + tl + "Width") + - num(this, "border" + br + "Width") + - (margin ? - num(this, "margin" + tl) + num(this, "margin" + br) : 0); + return this[0] ? + jQuery.css( this[0], lower, false, margin ? "margin" : "border" ) : + null; }; var type = name.toLowerCase(); @@ -4238,4 +4372,5 @@ jQuery.each([ "Height", "Width" ], function(i, name){ this.css( type, typeof size === "string" ? size : size + "px" ); }; -});})(); +}); +})(); diff --git a/js/jquery.min.js b/js/jquery.min.js index c327fae81..b1ae21d8b 100644 --- a/js/jquery.min.js +++ b/js/jquery.min.js @@ -1,19 +1,19 @@ /* - * jQuery JavaScript Library v1.3.1 + * jQuery JavaScript Library v1.3.2 * http://jquery.com/ * * Copyright (c) 2009 John Resig * Dual licensed under the MIT and GPL licenses. * http://docs.jquery.com/License * - * Date: 2009-01-21 20:42:16 -0500 (Wed, 21 Jan 2009) - * Revision: 6158 + * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) + * Revision: 6246 */ -(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.makeArray(E))},selector:"",jquery:"1.3.1",size:function(){return this.length},get:function(E){return E===g?o.makeArray(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,find:function(E){if(this.length===1&&!/,/.test(E)){var G=this.pushStack([],"find",E);G.length=0;o.find(E,this[0],G);return G}else{var F=o.map(this,function(H){return o.find(E,H)});return this.pushStack(/[^+>] [^+>]/.test(E)?o.unique(F):F,"find",E)}},clone:function(F){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.cloneNode(true),H=document.createElement("div");H.appendChild(I);return o.clean([H.innerHTML])[0]}else{return this.cloneNode(true)}});var G=E.find("*").andSelf().each(function(){if(this[h]!==g){this[h]=null}});if(F===true){this.find("*").andSelf().each(function(I){if(this.nodeType==3){return}var H=o.data(this,"events");for(var K in H){for(var J in H[K]){o.event.add(G[I],K,H[K][J],H[K][J].data)}}})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var F=o.expr.match.POS.test(E)?o(E):null;return this.map(function(){var G=this;while(G&&G.ownerDocument){if(F?F.index(G)>-1:o(G).is(E)){return G}G=G.parentNode}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F<J;F++){var G=M[F];if(G.selected){K=o(G).val();if(H){return K}L.push(K)}}return L}return(E.value||"").replace(/\r/g,"")}return g}if(typeof K==="number"){K+=""}return this.each(function(){if(this.nodeType!=1){return}if(o.isArray(K)&&/radio|checkbox/.test(this.type)){this.checked=(o.inArray(this.value,K)>=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML:null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(K,N,M){if(this[0]){var J=(this[0].ownerDocument||this[0]).createDocumentFragment(),G=o.clean(K,(this[0].ownerDocument||this[0]),J),I=J.firstChild,E=this.length>1?J.cloneNode(true):J;if(I){for(var H=0,F=this.length;H<F;H++){M.call(L(this[H],I),H>0?E.cloneNode(true):J)}}if(G){o.each(G,z)}}return this;function L(O,P){return N&&o.nodeName(O,"table")&&o.nodeName(P,"tr")?(O.getElementsByTagName("tbody")[0]||O.appendChild(O.ownerDocument.createElement("tbody"))):O}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H<I;H++){if((G=arguments[H])!=null){for(var F in G){var K=J[F],L=G[F];if(J===L){continue}if(E&&L&&typeof L==="object"&&!L.nodeType){J[F]=o.extend(E,K||(L.length!=null?[]:{}),L)}else{if(L!==g){J[F]=L}}}}}return J};var b=/z-?index|font-?weight|opacity|zoom|line-?height/i,q=document.defaultView||{},s=Object.prototype.toString;o.extend({noConflict:function(E){l.$=p;if(E){l.jQuery=y}return o},isFunction:function(E){return s.call(E)==="[object Function]"},isArray:function(E){return s.call(E)==="[object Array]"},isXMLDoc:function(E){return E.nodeType===9&&E.documentElement.nodeName!=="HTML"||!!E.ownerDocument&&o.isXMLDoc(E.ownerDocument)},globalEval:function(G){G=o.trim(G);if(G){var F=document.getElementsByTagName("head")[0]||document.documentElement,E=document.createElement("script");E.type="text/javascript";if(o.support.scriptEval){E.appendChild(document.createTextNode(G))}else{E.text=G}F.insertBefore(E,F.firstChild);F.removeChild(E)}},nodeName:function(F,E){return F.nodeName&&F.nodeName.toUpperCase()==E.toUpperCase()},each:function(G,K,F){var E,H=0,I=G.length;if(F){if(I===g){for(E in G){if(K.apply(G[E],F)===false){break}}}else{for(;H<I;){if(K.apply(G[H++],F)===false){break}}}}else{if(I===g){for(E in G){if(K.call(G[E],E,G[E])===false){break}}}else{for(var J=G[0];H<I&&K.call(J,H,J)!==false;J=G[++H]){}}}return G},prop:function(H,I,G,F,E){if(o.isFunction(I)){I=I.call(H,F)}return typeof I==="number"&&G=="curCSS"&&!b.test(E)?I+"px":I},className:{add:function(E,F){o.each((F||"").split(/\s+/),function(G,H){if(E.nodeType==1&&!o.className.has(E.className,H)){E.className+=(E.className?" ":"")+H}})},remove:function(E,F){if(E.nodeType==1){E.className=F!==g?o.grep(E.className.split(/\s+/),function(G){return !o.className.has(F,G)}).join(" "):""}},has:function(F,E){return F&&o.inArray(E,(F.className||F).toString().split(/\s+/))>-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(G,E,I){if(E=="width"||E=="height"){var K,F={position:"absolute",visibility:"hidden",display:"block"},J=E=="width"?["Left","Right"]:["Top","Bottom"];function H(){K=E=="width"?G.offsetWidth:G.offsetHeight;var M=0,L=0;o.each(J,function(){M+=parseFloat(o.curCSS(G,"padding"+this,true))||0;L+=parseFloat(o.curCSS(G,"border"+this+"Width",true))||0});K-=Math.round(M+L)}if(o(G).is(":visible")){H()}else{o.swap(G,F,H)}return Math.max(0,K)}return o.curCSS(G,E,I)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,R){if(typeof R==="number"){R+=""}if(!R){return}if(typeof R==="string"){R=R.replace(/(<(\w+)[^>]*?)\/>/g,function(T,U,S){return S.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?T:U+"></"+S+">"});var O=o.trim(R).toLowerCase();var Q=!O.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!O.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!O.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!O.indexOf("<td")||!O.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!O.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||!o.support.htmlSerialize&&[1,"div<div>","</div>"]||[0,"",""];L.innerHTML=Q[1]+R+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var N=!O.indexOf("<table")&&O.indexOf("<tbody")<0?L.firstChild&&L.firstChild.childNodes:Q[1]=="<table>"&&O.indexOf("<tbody")<0?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(R)){L.insertBefore(K.createTextNode(R.match(/^\s*/)[0]),L.firstChild)}R=o.makeArray(L.childNodes)}if(R.nodeType){G.push(R)}else{G=o.merge(G,R)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E<F;E++){if(H[E]===G){return E}}return -1},merge:function(H,E){var F=0,G,I=H.length;if(!o.support.getAll){while((G=E[F++])!=null){if(G.nodeType!=8){H[I++]=G}}}else{while((G=E[F++])!=null){H[I++]=G}}return H},unique:function(K){var F=[],E={};try{for(var G=0,H=K.length;G<H;G++){var J=o.data(K[G]);if(!E[J]){E[J]=true;F.push(K[G])}}}catch(I){F=K}return F},grep:function(F,J,E){var G=[];for(var H=0,I=F.length;H<I;H++){if(!E!=!J(F[H],H)){G.push(F[H])}}return G},map:function(E,J){var F=[];for(var G=0,H=E.length;G<H;G++){var I=J(E[G],G);if(I!=null){F[F.length]=I}}return F.concat.apply([],F)}});var C=navigator.userAgent.toLowerCase();o.browser={version:(C.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[0,"0"])[1],safari:/webkit/.test(C),opera:/opera/.test(C),msie:/msie/.test(C)&&!/opera/.test(C),mozilla:/mozilla/.test(C)&&!/(compatible|webkit)/.test(C)};o.each({parent:function(E){return E.parentNode},parents:function(E){return o.dir(E,"parentNode")},next:function(E){return o.nth(E,2,"nextSibling")},prev:function(E){return o.nth(E,2,"previousSibling")},nextAll:function(E){return o.dir(E,"nextSibling")},prevAll:function(E){return o.dir(E,"previousSibling")},siblings:function(E){return o.sibling(E.parentNode.firstChild,E)},children:function(E){return o.sibling(E.firstChild)},contents:function(E){return o.nodeName(E,"iframe")?E.contentDocument||E.contentWindow.document:o.makeArray(E.childNodes)}},function(E,F){o.fn[E]=function(G){var H=o.map(this,F);if(G&&typeof G=="string"){H=o.multiFilter(G,H)}return this.pushStack(o.unique(H),E,G)}});o.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(E,F){o.fn[E]=function(){var G=arguments;return this.each(function(){for(var H=0,I=G.length;H<I;H++){o(G[H])[F](this)}})}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(">*",this).remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}}); +(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F<J;F++){var G=M[F];if(G.selected){K=o(G).val();if(H){return K}L.push(K)}}return L}return(E.value||"").replace(/\r/g,"")}return g}if(typeof K==="number"){K+=""}return this.each(function(){if(this.nodeType!=1){return}if(o.isArray(K)&&/radio|checkbox/.test(this.type)){this.checked=(o.inArray(this.value,K)>=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G<E;G++){L.call(K(this[G],H),this.length>1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H<I;H++){if((G=arguments[H])!=null){for(var F in G){var K=J[F],L=G[F];if(J===L){continue}if(E&&L&&typeof L==="object"&&!L.nodeType){J[F]=o.extend(E,K||(L.length!=null?[]:{}),L)}else{if(L!==g){J[F]=L}}}}}return J};var b=/z-?index|font-?weight|opacity|zoom|line-?height/i,q=document.defaultView||{},s=Object.prototype.toString;o.extend({noConflict:function(E){l.$=p;if(E){l.jQuery=y}return o},isFunction:function(E){return s.call(E)==="[object Function]"},isArray:function(E){return s.call(E)==="[object Array]"},isXMLDoc:function(E){return E.nodeType===9&&E.documentElement.nodeName!=="HTML"||!!E.ownerDocument&&o.isXMLDoc(E.ownerDocument)},globalEval:function(G){if(G&&/\S/.test(G)){var F=document.getElementsByTagName("head")[0]||document.documentElement,E=document.createElement("script");E.type="text/javascript";if(o.support.scriptEval){E.appendChild(document.createTextNode(G))}else{E.text=G}F.insertBefore(E,F.firstChild);F.removeChild(E)}},nodeName:function(F,E){return F.nodeName&&F.nodeName.toUpperCase()==E.toUpperCase()},each:function(G,K,F){var E,H=0,I=G.length;if(F){if(I===g){for(E in G){if(K.apply(G[E],F)===false){break}}}else{for(;H<I;){if(K.apply(G[H++],F)===false){break}}}}else{if(I===g){for(E in G){if(K.call(G[E],E,G[E])===false){break}}}else{for(var J=G[0];H<I&&K.call(J,H,J)!==false;J=G[++H]){}}}return G},prop:function(H,I,G,F,E){if(o.isFunction(I)){I=I.call(H,F)}return typeof I==="number"&&G=="curCSS"&&!b.test(E)?I+"px":I},className:{add:function(E,F){o.each((F||"").split(/\s+/),function(G,H){if(E.nodeType==1&&!o.className.has(E.className,H)){E.className+=(E.className?" ":"")+H}})},remove:function(E,F){if(E.nodeType==1){E.className=F!==g?o.grep(E.className.split(/\s+/),function(G){return !o.className.has(F,G)}).join(" "):""}},has:function(F,E){return F&&o.inArray(E,(F.className||F).toString().split(/\s+/))>-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+"></"+T+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!O.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!O.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!O.indexOf("<td")||!O.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!O.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||!o.support.htmlSerialize&&[1,"div<div>","</div>"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/<tbody/i.test(S),N=!O.indexOf("<table")&&!R?L.firstChild&&L.firstChild.childNodes:Q[1]=="<table>"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E<F;E++){if(H[E]===G){return E}}return -1},merge:function(H,E){var F=0,G,I=H.length;if(!o.support.getAll){while((G=E[F++])!=null){if(G.nodeType!=8){H[I++]=G}}}else{while((G=E[F++])!=null){H[I++]=G}}return H},unique:function(K){var F=[],E={};try{for(var G=0,H=K.length;G<H;G++){var J=o.data(K[G]);if(!E[J]){E[J]=true;F.push(K[G])}}}catch(I){F=K}return F},grep:function(F,J,E){var G=[];for(var H=0,I=F.length;H<I;H++){if(!E!=!J(F[H],H)){G.push(F[H])}}return G},map:function(E,J){var F=[];for(var G=0,H=E.length;G<H;G++){var I=J(E[G],G);if(I!=null){F[F.length]=I}}return F.concat.apply([],F)}});var C=navigator.userAgent.toLowerCase();o.browser={version:(C.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[0,"0"])[1],safari:/webkit/.test(C),opera:/opera/.test(C),msie:/msie/.test(C)&&!/opera/.test(C),mozilla:/mozilla/.test(C)&&!/(compatible|webkit)/.test(C)};o.each({parent:function(E){return E.parentNode},parents:function(E){return o.dir(E,"parentNode")},next:function(E){return o.nth(E,2,"nextSibling")},prev:function(E){return o.nth(E,2,"previousSibling")},nextAll:function(E){return o.dir(E,"nextSibling")},prevAll:function(E){return o.dir(E,"previousSibling")},siblings:function(E){return o.sibling(E.parentNode.firstChild,E)},children:function(E){return o.sibling(E.firstChild)},contents:function(E){return o.nodeName(E,"iframe")?E.contentDocument||E.contentWindow.document:o.makeArray(E.childNodes)}},function(E,F){o.fn[E]=function(G){var H=o.map(this,F);if(G&&typeof G=="string"){H=o.multiFilter(G,H)}return this.pushStack(o.unique(H),E,G)}});o.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(E,F){o.fn[E]=function(G){var J=[],L=o(G);for(var K=0,H=L.length;K<H;K++){var I=(K>0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}}); /* * Sizzle CSS Selector Engine - v0.9.3 * Copyright 2009, The Dojo Foundation * Released under the MIT, BSD, and GPL Licenses. * More information: http://sizzlejs.com/ */ -(function(){var Q=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]+['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[]+)+|[>+~])(\s*,\s*)?/g,K=0,G=Object.prototype.toString;var F=function(X,T,aa,ab){aa=aa||[];T=T||document;if(T.nodeType!==1&&T.nodeType!==9){return[]}if(!X||typeof X!=="string"){return aa}var Y=[],V,ae,ah,S,ac,U,W=true;Q.lastIndex=0;while((V=Q.exec(X))!==null){Y.push(V[1]);if(V[2]){U=RegExp.rightContext;break}}if(Y.length>1&&L.exec(X)){if(Y.length===2&&H.relative[Y[0]]){ae=I(Y[0]+Y[1],T)}else{ae=H.relative[Y[0]]?[T]:F(Y.shift(),T);while(Y.length){X=Y.shift();if(H.relative[X]){X+=Y.shift()}ae=I(X,ae)}}}else{var ad=ab?{expr:Y.pop(),set:E(ab)}:F.find(Y.pop(),Y.length===1&&T.parentNode?T.parentNode:T,P(T));ae=F.filter(ad.expr,ad.set);if(Y.length>0){ah=E(ae)}else{W=false}while(Y.length){var ag=Y.pop(),af=ag;if(!H.relative[ag]){ag=""}else{af=Y.pop()}if(af==null){af=T}H.relative[ag](ah,af,P(T))}}if(!ah){ah=ae}if(!ah){throw"Syntax error, unrecognized expression: "+(ag||X)}if(G.call(ah)==="[object Array]"){if(!W){aa.push.apply(aa,ah)}else{if(T.nodeType===1){for(var Z=0;ah[Z]!=null;Z++){if(ah[Z]&&(ah[Z]===true||ah[Z].nodeType===1&&J(T,ah[Z]))){aa.push(ae[Z])}}}else{for(var Z=0;ah[Z]!=null;Z++){if(ah[Z]&&ah[Z].nodeType===1){aa.push(ae[Z])}}}}}else{E(ah,aa)}if(U){F(U,T,aa,ab)}return aa};F.matches=function(S,T){return F(S,null,null,T)};F.find=function(Z,S,aa){var Y,W;if(!Z){return[]}for(var V=0,U=H.order.length;V<U;V++){var X=H.order[V],W;if((W=H.match[X].exec(Z))){var T=RegExp.leftContext;if(T.substr(T.length-1)!=="\\"){W[1]=(W[1]||"").replace(/\\/g,"");Y=H.find[X](W,S,aa);if(Y!=null){Z=Z.replace(H.match[X],"");break}}}}if(!Y){Y=S.getElementsByTagName("*")}return{set:Y,expr:Z}};F.filter=function(ab,aa,ae,V){var U=ab,ag=[],Y=aa,X,S;while(ab&&aa.length){for(var Z in H.filter){if((X=H.match[Z].exec(ab))!=null){var T=H.filter[Z],af,ad;S=false;if(Y==ag){ag=[]}if(H.preFilter[Z]){X=H.preFilter[Z](X,Y,ae,ag,V);if(!X){S=af=true}else{if(X===true){continue}}}if(X){for(var W=0;(ad=Y[W])!=null;W++){if(ad){af=T(ad,X,W,Y);var ac=V^!!af;if(ae&&af!=null){if(ac){S=true}else{Y[W]=false}}else{if(ac){ag.push(ad);S=true}}}}}if(af!==g){if(!ae){Y=ag}ab=ab.replace(H.match[Z],"");if(!S){return[]}break}}}ab=ab.replace(/\s*,\s*/,"");if(ab==U){if(S==null){throw"Syntax error, unrecognized expression: "+ab}else{break}}U=ab}return Y};var H=F.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(S){return S.getAttribute("href")}},relative:{"+":function(W,T){for(var U=0,S=W.length;U<S;U++){var V=W[U];if(V){var X=V.previousSibling;while(X&&X.nodeType!==1){X=X.previousSibling}W[U]=typeof T==="string"?X||false:X===T}}if(typeof T==="string"){F.filter(T,W,true)}},">":function(X,T,Y){if(typeof T==="string"&&!/\W/.test(T)){T=Y?T:T.toUpperCase();for(var U=0,S=X.length;U<S;U++){var W=X[U];if(W){var V=W.parentNode;X[U]=V.nodeName===T?V:false}}}else{for(var U=0,S=X.length;U<S;U++){var W=X[U];if(W){X[U]=typeof T==="string"?W.parentNode:W.parentNode===T}}if(typeof T==="string"){F.filter(T,X,true)}}},"":function(V,T,X){var U="done"+(K++),S=R;if(!T.match(/\W/)){var W=T=X?T:T.toUpperCase();S=O}S("parentNode",T,U,V,W,X)},"~":function(V,T,X){var U="done"+(K++),S=R;if(typeof T==="string"&&!T.match(/\W/)){var W=T=X?T:T.toUpperCase();S=O}S("previousSibling",T,U,V,W,X)}},find:{ID:function(T,U,V){if(typeof U.getElementById!=="undefined"&&!V){var S=U.getElementById(T[1]);return S?[S]:[]}},NAME:function(S,T,U){if(typeof T.getElementsByName!=="undefined"&&!U){return T.getElementsByName(S[1])}},TAG:function(S,T){return T.getElementsByTagName(S[1])}},preFilter:{CLASS:function(V,T,U,S,Y){V=" "+V[1].replace(/\\/g,"")+" ";var X;for(var W=0;(X=T[W])!=null;W++){if(X){if(Y^(" "+X.className+" ").indexOf(V)>=0){if(!U){S.push(X)}}else{if(U){T[W]=false}}}}return false},ID:function(S){return S[1].replace(/\\/g,"")},TAG:function(T,S){for(var U=0;S[U]===false;U++){}return S[U]&&P(S[U])?T[1]:T[1].toUpperCase()},CHILD:function(S){if(S[1]=="nth"){var T=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(S[2]=="even"&&"2n"||S[2]=="odd"&&"2n+1"||!/\D/.test(S[2])&&"0n+"+S[2]||S[2]);S[2]=(T[1]+(T[2]||1))-0;S[3]=T[3]-0}S[0]="done"+(K++);return S},ATTR:function(T){var S=T[1].replace(/\\/g,"");if(H.attrMap[S]){T[1]=H.attrMap[S]}if(T[2]==="~="){T[4]=" "+T[4]+" "}return T},PSEUDO:function(W,T,U,S,X){if(W[1]==="not"){if(W[3].match(Q).length>1){W[3]=F(W[3],null,null,T)}else{var V=F.filter(W[3],T,U,true^X);if(!U){S.push.apply(S,V)}return false}}else{if(H.match.POS.test(W[0])){return true}}return W},POS:function(S){S.unshift(true);return S}},filters:{enabled:function(S){return S.disabled===false&&S.type!=="hidden"},disabled:function(S){return S.disabled===true},checked:function(S){return S.checked===true},selected:function(S){S.parentNode.selectedIndex;return S.selected===true},parent:function(S){return !!S.firstChild},empty:function(S){return !S.firstChild},has:function(U,T,S){return !!F(S[3],U).length},header:function(S){return/h\d/i.test(S.nodeName)},text:function(S){return"text"===S.type},radio:function(S){return"radio"===S.type},checkbox:function(S){return"checkbox"===S.type},file:function(S){return"file"===S.type},password:function(S){return"password"===S.type},submit:function(S){return"submit"===S.type},image:function(S){return"image"===S.type},reset:function(S){return"reset"===S.type},button:function(S){return"button"===S.type||S.nodeName.toUpperCase()==="BUTTON"},input:function(S){return/input|select|textarea|button/i.test(S.nodeName)}},setFilters:{first:function(T,S){return S===0},last:function(U,T,S,V){return T===V.length-1},even:function(T,S){return S%2===0},odd:function(T,S){return S%2===1},lt:function(U,T,S){return T<S[3]-0},gt:function(U,T,S){return T>S[3]-0},nth:function(U,T,S){return S[3]-0==T},eq:function(U,T,S){return S[3]-0==T}},filter:{CHILD:function(S,V){var Y=V[1],Z=S.parentNode;var X=V[0];if(Z&&(!Z[X]||!S.nodeIndex)){var W=1;for(var T=Z.firstChild;T;T=T.nextSibling){if(T.nodeType==1){T.nodeIndex=W++}}Z[X]=W-1}if(Y=="first"){return S.nodeIndex==1}else{if(Y=="last"){return S.nodeIndex==Z[X]}else{if(Y=="only"){return Z[X]==1}else{if(Y=="nth"){var ab=false,U=V[2],aa=V[3];if(U==1&&aa==0){return true}if(U==0){if(S.nodeIndex==aa){ab=true}}else{if((S.nodeIndex-aa)%U==0&&(S.nodeIndex-aa)/U>=0){ab=true}}return ab}}}}},PSEUDO:function(Y,U,V,Z){var T=U[1],W=H.filters[T];if(W){return W(Y,V,U,Z)}else{if(T==="contains"){return(Y.textContent||Y.innerText||"").indexOf(U[3])>=0}else{if(T==="not"){var X=U[3];for(var V=0,S=X.length;V<S;V++){if(X[V]===Y){return false}}return true}}}},ID:function(T,S){return T.nodeType===1&&T.getAttribute("id")===S},TAG:function(T,S){return(S==="*"&&T.nodeType===1)||T.nodeName===S},CLASS:function(T,S){return S.test(T.className)},ATTR:function(W,U){var S=H.attrHandle[U[1]]?H.attrHandle[U[1]](W):W[U[1]]||W.getAttribute(U[1]),X=S+"",V=U[2],T=U[4];return S==null?V==="!=":V==="="?X===T:V==="*="?X.indexOf(T)>=0:V==="~="?(" "+X+" ").indexOf(T)>=0:!U[4]?S:V==="!="?X!=T:V==="^="?X.indexOf(T)===0:V==="$="?X.substr(X.length-T.length)===T:V==="|="?X===T||X.substr(0,T.length+1)===T+"-":false},POS:function(W,T,U,X){var S=T[2],V=H.setFilters[S];if(V){return V(W,U,T,X)}}}};var L=H.match.POS;for(var N in H.match){H.match[N]=RegExp(H.match[N].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(T,S){T=Array.prototype.slice.call(T);if(S){S.push.apply(S,T);return S}return T};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(M){E=function(W,V){var T=V||[];if(G.call(W)==="[object Array]"){Array.prototype.push.apply(T,W)}else{if(typeof W.length==="number"){for(var U=0,S=W.length;U<S;U++){T.push(W[U])}}else{for(var U=0;W[U];U++){T.push(W[U])}}}return T}}(function(){var T=document.createElement("form"),U="script"+(new Date).getTime();T.innerHTML="<input name='"+U+"'/>";var S=document.documentElement;S.insertBefore(T,S.firstChild);if(!!document.getElementById(U)){H.find.ID=function(W,X,Y){if(typeof X.getElementById!=="undefined"&&!Y){var V=X.getElementById(W[1]);return V?V.id===W[1]||typeof V.getAttributeNode!=="undefined"&&V.getAttributeNode("id").nodeValue===W[1]?[V]:g:[]}};H.filter.ID=function(X,V){var W=typeof X.getAttributeNode!=="undefined"&&X.getAttributeNode("id");return X.nodeType===1&&W&&W.nodeValue===V}}S.removeChild(T)})();(function(){var S=document.createElement("div");S.appendChild(document.createComment(""));if(S.getElementsByTagName("*").length>0){H.find.TAG=function(T,X){var W=X.getElementsByTagName(T[1]);if(T[1]==="*"){var V=[];for(var U=0;W[U];U++){if(W[U].nodeType===1){V.push(W[U])}}W=V}return W}}S.innerHTML="<a href='#'></a>";if(S.firstChild&&S.firstChild.getAttribute("href")!=="#"){H.attrHandle.href=function(T){return T.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var S=F,T=document.createElement("div");T.innerHTML="<p class='TEST'></p>";if(T.querySelectorAll&&T.querySelectorAll(".TEST").length===0){return}F=function(X,W,U,V){W=W||document;if(!V&&W.nodeType===9&&!P(W)){try{return E(W.querySelectorAll(X),U)}catch(Y){}}return S(X,W,U,V)};F.find=S.find;F.filter=S.filter;F.selectors=S.selectors;F.matches=S.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){H.order.splice(1,0,"CLASS");H.find.CLASS=function(S,T){return T.getElementsByClassName(S[1])}}function O(T,Z,Y,ac,aa,ab){for(var W=0,U=ac.length;W<U;W++){var S=ac[W];if(S){S=S[T];var X=false;while(S&&S.nodeType){var V=S[Y];if(V){X=ac[V];break}if(S.nodeType===1&&!ab){S[Y]=W}if(S.nodeName===Z){X=S;break}S=S[T]}ac[W]=X}}}function R(T,Y,X,ab,Z,aa){for(var V=0,U=ab.length;V<U;V++){var S=ab[V];if(S){S=S[T];var W=false;while(S&&S.nodeType){if(S[X]){W=ab[S[X]];break}if(S.nodeType===1){if(!aa){S[X]=V}if(typeof Y!=="string"){if(S===Y){W=true;break}}else{if(F.filter(Y,[S]).length>0){W=S;break}}}S=S[T]}ab[V]=W}}}var J=document.compareDocumentPosition?function(T,S){return T.compareDocumentPosition(S)&16}:function(T,S){return T!==S&&(T.contains?T.contains(S):true)};var P=function(S){return S.nodeType===9&&S.documentElement.nodeName!=="HTML"||!!S.ownerDocument&&P(S.ownerDocument)};var I=function(S,Z){var V=[],W="",X,U=Z.nodeType?[Z]:Z;while((X=H.match.PSEUDO.exec(S))){W+=X[0];S=S.replace(H.match.PSEUDO,"")}S=H.relative[S]?S+"*":S;for(var Y=0,T=U.length;Y<T;Y++){F(S,U[Y],V)}return F.filter(W,V)};o.find=F;o.filter=F.filter;o.expr=F.selectors;o.expr[":"]=o.expr.filters;F.selectors.filters.hidden=function(S){return"hidden"===S.type||o.css(S,"display")==="none"||o.css(S,"visibility")==="hidden"};F.selectors.filters.visible=function(S){return"hidden"!==S.type&&o.css(S,"display")!=="none"&&o.css(S,"visibility")!=="hidden"};F.selectors.filters.animated=function(S){return o.grep(o.timers,function(T){return S===T.elem}).length};o.multiFilter=function(U,S,T){if(T){U=":not("+U+")"}return F.matches(U,S)};o.dir=function(U,T){var S=[],V=U[T];while(V&&V!=document){if(V.nodeType==1){S.push(V)}V=V[T]}return S};o.nth=function(W,S,U,V){S=S||1;var T=0;for(;W;W=W[U]){if(W.nodeType==1&&++T==S){break}}return W};o.sibling=function(U,T){var S=[];for(;U;U=U.nextSibling){if(U.nodeType==1&&U!=T){S.push(U)}}return S};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F<E.length){o.event.proxy(G,E[F++])}return this.click(o.event.proxy(G,function(H){this.lastToggle=(this.lastToggle||0)%F;H.preventDefault();return E[this.lastToggle++].apply(this,arguments)||false}))},hover:function(E,F){return this.mouseenter(E).mouseleave(F)},ready:function(E){B();if(o.isReady){E.call(document,o)}else{o.readyList.push(E)}return this},live:function(G,F){var E=o.event.proxy(F);E.guid+=this.selector+G;o(document).bind(i(G,this.selector),this.selector,E);return this},die:function(F,E){o(document).unbind(i(F,this.selector),E?{guid:E.guid+this.selector+F}:null);return this}});function c(H){var E=RegExp("(^|\\.)"+H.type+"(\\.|$)"),G=true,F=[];o.each(o.data(this,"events").live||[],function(I,J){if(E.test(J.type)){var K=o(H.target).closest(J.data)[0];if(K){F.push({elem:K,fn:J})}}});o.each(F,function(){if(this.fn.call(this.elem,H,this.fn.data)===false){G=false}});return G}function i(F,E){return["live",F,E.replace(/\./g,"`").replace(/ /g,"|")].join(".")}o.extend({isReady:false,readyList:[],ready:function(){if(!o.isReady){o.isReady=true;if(o.readyList){o.each(o.readyList,function(){this.call(document,o)});o.readyList=null}o(document).triggerHandler("ready")}}});var x=false;function B(){if(x){return}x=true;if(document.addEventListener){document.addEventListener("DOMContentLoaded",function(){document.removeEventListener("DOMContentLoaded",arguments.callee,false);o.ready()},false)}else{if(document.attachEvent){document.attachEvent("onreadystatechange",function(){if(document.readyState==="complete"){document.detachEvent("onreadystatechange",arguments.callee);o.ready()}});if(document.documentElement.doScroll&&typeof l.frameElement==="undefined"){(function(){if(o.isReady){return}try{document.documentElement.doScroll("left")}catch(E){setTimeout(arguments.callee,0);return}o.ready()})()}}}o.event.add(l,"load",o.ready)}o.each(("blur,focus,load,resize,scroll,unload,click,dblclick,mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave,change,select,submit,keydown,keypress,keyup,error").split(","),function(F,E){o.fn[E]=function(G){return G?this.bind(E,G):this.trigger(E)}});o(l).bind("unload",function(){for(var E in o.cache){if(E!=1&&o.cache[E].handle){o.event.remove(o.cache[E].handle.elem)}}});(function(){o.support={};var F=document.documentElement,G=document.createElement("script"),K=document.createElement("div"),J="script"+(new Date).getTime();K.style.display="none";K.innerHTML=' <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';var H=K.getElementsByTagName("*"),E=K.getElementsByTagName("a")[0];if(!H||!H.length||!E){return}o.support={leadingWhitespace:K.firstChild.nodeType==3,tbody:!K.getElementsByTagName("tbody").length,objectAll:!!K.getElementsByTagName("object")[0].getElementsByTagName("*").length,htmlSerialize:!!K.getElementsByTagName("link").length,style:/red/.test(E.getAttribute("style")),hrefNormalized:E.getAttribute("href")==="/a",opacity:E.style.opacity==="0.5",cssFloat:!!E.style.cssFloat,scriptEval:false,noCloneEvent:true,boxModel:null};G.type="text/javascript";try{G.appendChild(document.createTextNode("window."+J+"=1;"))}catch(I){}F.insertBefore(G,F.firstChild);if(l[J]){o.support.scriptEval=true;delete l[J]}F.removeChild(G);if(K.attachEvent&&K.fireEvent){K.attachEvent("onclick",function(){o.support.noCloneEvent=false;K.detachEvent("onclick",arguments.callee)});K.cloneNode(true).fireEvent("onclick")}o(function(){var L=document.createElement("div");L.style.width="1px";L.style.paddingLeft="1px";document.body.appendChild(L);o.boxModel=o.support.boxModel=L.offsetWidth===2;document.body.removeChild(L)})})();var w=o.support.cssFloat?"cssFloat":"styleFloat";o.props={"for":"htmlFor","class":"className","float":w,cssFloat:w,styleFloat:w,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",tabindex:"tabIndex"};o.fn.extend({_load:o.fn.load,load:function(G,J,K){if(typeof G!=="string"){return this._load(G)}var I=G.indexOf(" ");if(I>=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("<div/>").append(M.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H<F;H++){var E=o.data(this[H],"olddisplay");this[H].style.display=E||"";if(o.css(this[H],"display")==="none"){var G=this[H].tagName,K;if(m[G]){K=m[G]}else{var I=o("<"+G+" />").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}this[H].style.display=o.data(this[H],"olddisplay",K)}}return this}},hide:function(H,I){if(H){return this.animate(t("hide",3),H,I)}else{for(var G=0,F=this.length;G<F;G++){var E=o.data(this[G],"olddisplay");if(!E&&E!=="none"){o.data(this[G],"olddisplay",o.css(this[G],"display"))}this[G].style.display="none"}return this}},_toggle:o.fn.toggle,toggle:function(G,F){var E=typeof G==="boolean";return o.isFunction(G)&&o.isFunction(F)?this._toggle.apply(this,arguments):G==null||E?this.each(function(){var H=E?G:o(this).is(":hidden");o(this)[H?"show":"hide"]()}):this.animate(t("toggle",3),G,F)},fadeTo:function(E,G,F){return this.animate({opacity:G},E,F)},animate:function(I,F,H,G){var E=o.speed(F,H,G);return this[E.queue===false?"each":"queue"](function(){var K=o.extend({},E),M,L=this.nodeType==1&&o(this).is(":hidden"),J=this;for(M in I){if(I[M]=="hide"&&L||I[M]=="show"&&!L){return K.complete.call(this)}if((M=="height"||M=="width")&&this.style){K.display=o.css(this,"display");K.overflow=this.style.overflow}}if(K.overflow!=null){this.style.overflow="hidden"}K.curAnim=o.extend({},I);o.each(I,function(O,S){var R=new o.fx(J,K,O);if(/toggle|show|hide/.test(S)){R[S=="toggle"?L?"show":"hide":S](I)}else{var Q=S.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),T=R.cur(true)||0;if(Q){var N=parseFloat(Q[2]),P=Q[3]||"px";if(P!="px"){J.style[O]=(N||1)+P;T=((N||1)/R.cur(true))*T;J.style[O]=T+P}if(Q[1]){N=((Q[1]=="-="?-1:1)*N)+T}R.custom(T,N,P)}else{R.custom(T,S,"")}}});return true})},stop:function(F,E){var G=o.timers;if(F){this.queue([])}this.each(function(){for(var H=G.length-1;H>=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)==1){n=setInterval(function(){var K=o.timers;for(var J=0;J<K.length;J++){if(!K[J]()){K.splice(J--,1)}}if(!K.length){clearInterval(n)}},13)}},show:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.show=true;this.custom(this.prop=="width"||this.prop=="height"?1:0,this.cur());o(this.elem).show()},hide:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(H){var G=e();if(H||G>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(H,F){var E=H?"Left":"Top",G=H?"Right":"Bottom";o.fn["inner"+F]=function(){return this[F.toLowerCase()]()+j(this,"padding"+E)+j(this,"padding"+G)};o.fn["outer"+F]=function(J){return this["inner"+F]()+j(this,"border"+E+"Width")+j(this,"border"+G+"Width")+(J?j(this,"margin"+E)+j(this,"margin"+G):0)};var I=F.toLowerCase();o.fn[I]=function(J){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+F]||document.body["client"+F]:this[0]==document?Math.max(document.documentElement["client"+F],document.body["scroll"+F],document.documentElement["scroll"+F],document.body["offset"+F],document.documentElement["offset"+F]):J===g?(this.length?o.css(this[0],I):null):this.css(I,typeof J==="string"?J:J+"px")}})})();
\ No newline at end of file +(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa<ab.length;aa++){if(ab[aa]===ab[aa-1]){ab.splice(aa--,1)}}}}}return ab};F.matches=function(T,U){return F(T,null,null,U)};F.find=function(aa,T,ab){var Z,X;if(!aa){return[]}for(var W=0,V=I.order.length;W<V;W++){var Y=I.order[W],X;if((X=I.match[Y].exec(aa))){var U=RegExp.leftContext;if(U.substr(U.length-1)!=="\\"){X[1]=(X[1]||"").replace(/\\/g,"");Z=I.find[Y](X,T,ab);if(Z!=null){aa=aa.replace(I.match[Y],"");break}}}}if(!Z){Z=T.getElementsByTagName("*")}return{set:Z,expr:aa}};F.filter=function(ad,ac,ag,W){var V=ad,ai=[],aa=ac,Y,T,Z=ac&&ac[0]&&Q(ac[0]);while(ad&&ac.length){for(var ab in I.filter){if((Y=I.match[ab].exec(ad))!=null){var U=I.filter[ab],ah,af;T=false;if(aa==ai){ai=[]}if(I.preFilter[ab]){Y=I.preFilter[ab](Y,aa,ag,ai,W,Z);if(!Y){T=ah=true}else{if(Y===true){continue}}}if(Y){for(var X=0;(af=aa[X])!=null;X++){if(af){ah=U(af,Y,X,aa);var ae=W^!!ah;if(ag&&ah!=null){if(ae){T=true}else{aa[X]=false}}else{if(ae){ai.push(af);T=true}}}}}if(ah!==g){if(!ag){aa=ai}ad=ad.replace(I.match[ab],"");if(!T){return[]}break}}}if(ad==V){if(T==null){throw"Syntax error, unrecognized expression: "+ad}else{break}}V=ad}return aa};var I=F.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(T){return T.getAttribute("href")}},relative:{"+":function(aa,T,Z){var X=typeof T==="string",ab=X&&!/\W/.test(T),Y=X&&!ab;if(ab&&!Z){T=T.toUpperCase()}for(var W=0,V=aa.length,U;W<V;W++){if((U=aa[W])){while((U=U.previousSibling)&&U.nodeType!==1){}aa[W]=Y||U&&U.nodeName===T?U||false:U===T}}if(Y){F.filter(T,aa,true)}},">":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V<T;V++){var Y=Z[V];if(Y){var W=Y.parentNode;Z[V]=W.nodeName===U?W:false}}}else{for(var V=0,T=Z.length;V<T;V++){var Y=Z[V];if(Y){Z[V]=X?Y.parentNode:Y.parentNode===U}}if(X){F.filter(U,Z,true)}}},"":function(W,U,Y){var V=L++,T=S;if(!U.match(/\W/)){var X=U=Y?U:U.toUpperCase();T=P}T("parentNode",U,V,W,X,Y)},"~":function(W,U,Y){var V=L++,T=S;if(typeof U==="string"&&!U.match(/\W/)){var X=U=Y?U:U.toUpperCase();T=P}T("previousSibling",U,V,W,X,Y)}},find:{ID:function(U,V,W){if(typeof V.getElementById!=="undefined"&&!W){var T=V.getElementById(U[1]);return T?[T]:[]}},NAME:function(V,Y,Z){if(typeof Y.getElementsByName!=="undefined"){var U=[],X=Y.getElementsByName(V[1]);for(var W=0,T=X.length;W<T;W++){if(X[W].getAttribute("name")===V[1]){U.push(X[W])}}return U.length===0?null:U}},TAG:function(T,U){return U.getElementsByTagName(T[1])}},preFilter:{CLASS:function(W,U,V,T,Z,aa){W=" "+W[1].replace(/\\/g,"")+" ";if(aa){return W}for(var X=0,Y;(Y=U[X])!=null;X++){if(Y){if(Z^(Y.className&&(" "+Y.className+" ").indexOf(W)>=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return U<T[3]-0},gt:function(V,U,T){return U>T[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W<T;W++){if(Y[W]===Z){return false}}return true}}}},CHILD:function(T,W){var Z=W[1],U=T;switch(Z){case"only":case"first":while(U=U.previousSibling){if(U.nodeType===1){return false}}if(Z=="first"){return true}U=T;case"last":while(U=U.nextSibling){if(U.nodeType===1){return false}}return true;case"nth":var V=W[2],ac=W[3];if(V==1&&ac==0){return true}var Y=W[0],ab=T.parentNode;if(ab&&(ab.sizcache!==Y||!T.nodeIndex)){var X=0;for(U=ab.firstChild;U;U=U.nextSibling){if(U.nodeType===1){U.nodeIndex=++X}}ab.sizcache=Y}var aa=T.nodeIndex-ac;if(V==0){return aa==0}else{return(aa%V==0&&aa/V>=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V<T;V++){U.push(X[V])}}else{for(var V=0;X[V];V++){U.push(X[V])}}}return U}}var G;if(document.documentElement.compareDocumentPosition){G=function(U,T){var V=U.compareDocumentPosition(T)&4?-1:U===T?0:1;if(V===0){hasDuplicate=true}return V}}else{if("sourceIndex" in document.documentElement){G=function(U,T){var V=U.sourceIndex-T.sourceIndex;if(V===0){hasDuplicate=true}return V}}else{if(document.createRange){G=function(W,U){var V=W.ownerDocument.createRange(),T=U.ownerDocument.createRange();V.selectNode(W);V.collapse(true);T.selectNode(U);T.collapse(true);var X=V.compareBoundaryPoints(Range.START_TO_END,T);if(X===0){hasDuplicate=true}return X}}}}(function(){var U=document.createElement("form"),V="script"+(new Date).getTime();U.innerHTML="<input name='"+V+"'/>";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="<a href='#'></a>";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="<p class='TEST'></p>";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="<div class='test e'></div><div class='test'></div>";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W<V;W++){var T=ad[W];if(T){if(ab&&T.nodeType===1){T.sizcache=Y;T.sizset=W}T=T[U];var X=false;while(T){if(T.sizcache===Y){X=ad[T.sizset];break}if(T.nodeType===1&&!ac){T.sizcache=Y;T.sizset=W}if(T.nodeName===Z){X=T;break}T=T[U]}ad[W]=X}}}function S(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W<V;W++){var T=ad[W];if(T){if(ab&&T.nodeType===1){T.sizcache=Y;T.sizset=W}T=T[U];var X=false;while(T){if(T.sizcache===Y){X=ad[T.sizset];break}if(T.nodeType===1){if(!ac){T.sizcache=Y;T.sizset=W}if(typeof Z!=="string"){if(T===Z){X=true;break}}else{if(F.filter(Z,[T]).length>0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z<U;Z++){F(T,V[Z],W)}return F.filter(X,W)};o.find=F;o.filter=F.filter;o.expr=F.selectors;o.expr[":"]=o.expr.filters;F.selectors.filters.hidden=function(T){return T.offsetWidth===0||T.offsetHeight===0};F.selectors.filters.visible=function(T){return T.offsetWidth>0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F<E.length){o.event.proxy(G,E[F++])}return this.click(o.event.proxy(G,function(H){this.lastToggle=(this.lastToggle||0)%F;H.preventDefault();return E[this.lastToggle++].apply(this,arguments)||false}))},hover:function(E,F){return this.mouseenter(E).mouseleave(F)},ready:function(E){B();if(o.isReady){E.call(document,o)}else{o.readyList.push(E)}return this},live:function(G,F){var E=o.event.proxy(F);E.guid+=this.selector+G;o(document).bind(i(G,this.selector),this.selector,E);return this},die:function(F,E){o(document).unbind(i(F,this.selector),E?{guid:E.guid+this.selector+F}:null);return this}});function c(H){var E=RegExp("(^|\\.)"+H.type+"(\\.|$)"),G=true,F=[];o.each(o.data(this,"events").live||[],function(I,J){if(E.test(J.type)){var K=o(H.target).closest(J.data)[0];if(K){F.push({elem:K,fn:J})}}});F.sort(function(J,I){return o.data(J.elem,"closest")-o.data(I.elem,"closest")});o.each(F,function(){if(this.fn.call(this.elem,H,this.fn.data)===false){return(G=false)}});return G}function i(F,E){return["live",F,E.replace(/\./g,"`").replace(/ /g,"|")].join(".")}o.extend({isReady:false,readyList:[],ready:function(){if(!o.isReady){o.isReady=true;if(o.readyList){o.each(o.readyList,function(){this.call(document,o)});o.readyList=null}o(document).triggerHandler("ready")}}});var x=false;function B(){if(x){return}x=true;if(document.addEventListener){document.addEventListener("DOMContentLoaded",function(){document.removeEventListener("DOMContentLoaded",arguments.callee,false);o.ready()},false)}else{if(document.attachEvent){document.attachEvent("onreadystatechange",function(){if(document.readyState==="complete"){document.detachEvent("onreadystatechange",arguments.callee);o.ready()}});if(document.documentElement.doScroll&&l==l.top){(function(){if(o.isReady){return}try{document.documentElement.doScroll("left")}catch(E){setTimeout(arguments.callee,0);return}o.ready()})()}}}o.event.add(l,"load",o.ready)}o.each(("blur,focus,load,resize,scroll,unload,click,dblclick,mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave,change,select,submit,keydown,keypress,keyup,error").split(","),function(F,E){o.fn[E]=function(G){return G?this.bind(E,G):this.trigger(E)}});o(l).bind("unload",function(){for(var E in o.cache){if(E!=1&&o.cache[E].handle){o.event.remove(o.cache[E].handle.elem)}}});(function(){o.support={};var F=document.documentElement,G=document.createElement("script"),K=document.createElement("div"),J="script"+(new Date).getTime();K.style.display="none";K.innerHTML=' <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';var H=K.getElementsByTagName("*"),E=K.getElementsByTagName("a")[0];if(!H||!H.length||!E){return}o.support={leadingWhitespace:K.firstChild.nodeType==3,tbody:!K.getElementsByTagName("tbody").length,objectAll:!!K.getElementsByTagName("object")[0].getElementsByTagName("*").length,htmlSerialize:!!K.getElementsByTagName("link").length,style:/red/.test(E.getAttribute("style")),hrefNormalized:E.getAttribute("href")==="/a",opacity:E.style.opacity==="0.5",cssFloat:!!E.style.cssFloat,scriptEval:false,noCloneEvent:true,boxModel:null};G.type="text/javascript";try{G.appendChild(document.createTextNode("window."+J+"=1;"))}catch(I){}F.insertBefore(G,F.firstChild);if(l[J]){o.support.scriptEval=true;delete l[J]}F.removeChild(G);if(K.attachEvent&&K.fireEvent){K.attachEvent("onclick",function(){o.support.noCloneEvent=false;K.detachEvent("onclick",arguments.callee)});K.cloneNode(true).fireEvent("onclick")}o(function(){var L=document.createElement("div");L.style.width=L.style.paddingLeft="1px";document.body.appendChild(L);o.boxModel=o.support.boxModel=L.offsetWidth===2;document.body.removeChild(L).style.display="none"})})();var w=o.support.cssFloat?"cssFloat":"styleFloat";o.props={"for":"htmlFor","class":"className","float":w,cssFloat:w,styleFloat:w,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",tabindex:"tabIndex"};o.fn.extend({_load:o.fn.load,load:function(G,J,K){if(typeof G!=="string"){return this._load(G)}var I=G.indexOf(" ");if(I>=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("<div/>").append(M.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H<F;H++){var E=o.data(this[H],"olddisplay");this[H].style.display=E||"";if(o.css(this[H],"display")==="none"){var G=this[H].tagName,K;if(m[G]){K=m[G]}else{var I=o("<"+G+" />").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H<F;H++){this[H].style.display=o.data(this[H],"olddisplay")||""}return this}},hide:function(H,I){if(H){return this.animate(t("hide",3),H,I)}else{for(var G=0,F=this.length;G<F;G++){var E=o.data(this[G],"olddisplay");if(!E&&E!=="none"){o.data(this[G],"olddisplay",o.css(this[G],"display"))}}for(var G=0,F=this.length;G<F;G++){this[G].style.display="none"}return this}},_toggle:o.fn.toggle,toggle:function(G,F){var E=typeof G==="boolean";return o.isFunction(G)&&o.isFunction(F)?this._toggle.apply(this,arguments):G==null||E?this.each(function(){var H=E?G:o(this).is(":hidden");o(this)[H?"show":"hide"]()}):this.animate(t("toggle",3),G,F)},fadeTo:function(E,G,F){return this.animate({opacity:G},E,F)},animate:function(I,F,H,G){var E=o.speed(F,H,G);return this[E.queue===false?"each":"queue"](function(){var K=o.extend({},E),M,L=this.nodeType==1&&o(this).is(":hidden"),J=this;for(M in I){if(I[M]=="hide"&&L||I[M]=="show"&&!L){return K.complete.call(this)}if((M=="height"||M=="width")&&this.style){K.display=o.css(this,"display");K.overflow=this.style.overflow}}if(K.overflow!=null){this.style.overflow="hidden"}K.curAnim=o.extend({},I);o.each(I,function(O,S){var R=new o.fx(J,K,O);if(/toggle|show|hide/.test(S)){R[S=="toggle"?L?"show":"hide":S](I)}else{var Q=S.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),T=R.cur(true)||0;if(Q){var N=parseFloat(Q[2]),P=Q[3]||"px";if(P!="px"){J.style[O]=(N||1)+P;T=((N||1)/R.cur(true))*T;J.style[O]=T+P}if(Q[1]){N=((Q[1]=="-="?-1:1)*N)+T}R.custom(T,N,P)}else{R.custom(T,S,"")}}});return true})},stop:function(F,E){var G=o.timers;if(F){this.queue([])}this.each(function(){for(var H=G.length-1;H>=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J<K.length;J++){if(!K[J]()){K.splice(J--,1)}}if(!K.length){clearInterval(n);n=g}},13)}},show:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.show=true;this.custom(this.prop=="width"||this.prop=="height"?1:0,this.cur());o(this.elem).show()},hide:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(H){var G=e();if(H||G>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})();
\ No newline at end of file diff --git a/lib/action.php b/lib/action.php index f0baa062c..cc98d4445 100644 --- a/lib/action.php +++ b/lib/action.php @@ -97,9 +97,18 @@ class Action extends HTMLOutputter // lawsuit $this->startHTML(); Event::handle('EndShowHTML', array($this)); } + if (Event::handle('StartShowHead', array($this))) { $this->showHead(); + Event::handle('EndShowHead', array($this)); + } + if (Event::handle('StartShowBody', array($this))) { $this->showBody(); + Event::handle('EndShowBody', array($this)); + } + if (Event::handle('StartEndHTML', array($this))) { $this->endHTML(); + Event::handle('EndEndHTML', array($this)); + } } /** @@ -326,7 +335,9 @@ class Action extends HTMLOutputter // lawsuit */ function showBody() { - $this->elementStart('body', array('id' => $this->trimmed('action'))); + $this->elementStart('body', (common_current_user()) ? array('id' => $this->trimmed('action'), + 'class' => 'user_in') + : array('id' => $this->trimmed('action'))); $this->elementStart('div', array('id' => 'wrap')); if (Event::handle('StartShowHeader', array($this))) { $this->showHeader(); @@ -400,13 +411,8 @@ class Action extends HTMLOutputter // lawsuit if ($user) { $this->menuItem(common_local_url('all', array('nickname' => $user->nickname)), _('Home'), _('Personal profile and friends timeline'), false, 'nav_home'); - } - $this->menuItem(common_local_url('peoplesearch'), - _('Search'), _('Search for people or text'), false, 'nav_search'); - if ($user) { $this->menuItem(common_local_url('profilesettings'), _('Account'), _('Change your email, avatar, password, profile'), false, 'nav_account'); - if (common_config('xmpp', 'enabled')) { $this->menuItem(common_local_url('imsettings'), _('Connect'), _('Connect to IM, SMS, Twitter'), false, 'nav_connect'); @@ -414,20 +420,28 @@ class Action extends HTMLOutputter // lawsuit $this->menuItem(common_local_url('smssettings'), _('Connect'), _('Connect to SMS, Twitter'), false, 'nav_connect'); } + $this->menuItem(common_local_url('invite'), + _('Invite'), + sprintf(_('Invite friends and colleagues to join you on %s'), + common_config('site', 'name')), + false, 'nav_invitecontact'); $this->menuItem(common_local_url('logout'), _('Logout'), _('Logout from the site'), false, 'nav_logout'); - } else { - $this->menuItem(common_local_url('login'), - _('Login'), _('Login to the site'), false, 'nav_login'); + } + else { if (!common_config('site', 'closed')) { $this->menuItem(common_local_url('register'), _('Register'), _('Create an account'), false, 'nav_register'); } $this->menuItem(common_local_url('openidlogin'), _('OpenID'), _('Login with OpenID'), false, 'nav_openid'); + $this->menuItem(common_local_url('login'), + _('Login'), _('Login to the site'), false, 'nav_login'); } $this->menuItem(common_local_url('doc', array('title' => 'help')), _('Help'), _('Help me!'), false, 'nav_help'); + $this->menuItem(common_local_url('peoplesearch'), + _('Search'), _('Search for people or text'), false, 'nav_search'); Event::handle('EndPrimaryNav', array($this)); } $this->elementEnd('ul'); @@ -604,7 +618,10 @@ class Action extends HTMLOutputter // lawsuit { $this->elementStart('div', array('id' => 'aside_primary', 'class' => 'aside')); + if (Event::handle('StartShowExportData', array($this))) { $this->showExportData(); + Event::handle('EndShowExportData', array($this)); + } if (Event::handle('StartShowSections', array($this))) { $this->showSections(); Event::handle('EndShowSections', array($this)); @@ -919,11 +936,15 @@ class Action extends HTMLOutputter // lawsuit * * @return string current URL */ + function selfUrl() { $action = $this->trimmed('action'); $args = $this->args; unset($args['action']); + if (array_key_exists('submit', $args)) { + unset($args['submit']); + } foreach (array_keys($_COOKIE) as $cookie) { unset($args[$cookie]); } diff --git a/lib/common.php b/lib/common.php index 1ca9e521b..b3882d207 100644 --- a/lib/common.php +++ b/lib/common.php @@ -19,7 +19,7 @@ if (!defined('LACONICA')) { exit(1); } -define('LACONICA_VERSION', '0.7.2.1'); +define('LACONICA_VERSION', '0.7.3'); define('AVATAR_PROFILE_SIZE', 96); define('AVATAR_STREAM_SIZE', 48); @@ -87,6 +87,8 @@ $config = 'closed' => false, 'inviteonly' => false, 'private' => false, + 'ssl' => 'never', + 'sslserver' => null, 'dupelimit' => 60), # default for same person saying the same thing 'syslog' => array('appname' => 'laconica', # for syslog @@ -151,6 +153,9 @@ $config = array('notify' => array()), 'inboxes' => array('enabled' => true), # on by default for new sites + 'newuser' => + array('subscribe' => null, + 'welcome' => null), ); $config['db'] = &PEAR::getStaticProperty('DB_DataObject','options'); diff --git a/lib/featureduserssection.php b/lib/featureduserssection.php index aed94b1a5..4b9238d47 100644 --- a/lib/featureduserssection.php +++ b/lib/featureduserssection.php @@ -57,9 +57,14 @@ class FeaturedUsersSection extends ProfileSection $quoted[] = "'$nick'"; } + $table = "user"; + if(common_config('db','quote_identifiers')) { + $table = '"' . $table . '"'; + } + $qry = 'SELECT profile.* ' . - 'FROM profile JOIN user on profile.id = user.id ' . - 'WHERE user.nickname in (' . implode(',', $quoted) . ') ' . + 'FROM profile JOIN '. $table .' on profile.id = '. $table .'.id ' . + 'WHERE '. $table .'.nickname in (' . implode(',', $quoted) . ') ' . 'ORDER BY profile.created DESC '; $limit = PROFILES_PER_SECTION + 1; diff --git a/lib/galleryaction.php b/lib/galleryaction.php index 25a5e3fd5..8e21d7393 100644 --- a/lib/galleryaction.php +++ b/lib/galleryaction.php @@ -50,7 +50,7 @@ class GalleryAction extends Action if ($this->arg('page') && $this->arg('page') != 1) { $args['page'] = $this->arg['page']; } - common_redirect(common_local_url('subscriptions', $args), 301); + common_redirect(common_local_url($this->trimmed('action'), $args), 301); return false; } @@ -71,6 +71,7 @@ class GalleryAction extends Action $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; $this->tag = $this->trimmed('tag'); + $this->q = $this->trimmed('q'); return true; } @@ -87,7 +88,7 @@ class GalleryAction extends Action # Post from the tag dropdown; redirect to a GET if ($_SERVER['REQUEST_METHOD'] == 'POST') { - common_redirect($this->selfUrl(), 307); + common_redirect($this->selfUrl(), 303); return; } @@ -136,7 +137,7 @@ class GalleryAction extends Action 'method' => 'post')); $this->dropdown('tag', _('Tag'), $content, _('Choose a tag to narrow list'), false, $tag); - $this->submit('go', _('Go')); + $this->submit('submit', _('Go')); $this->elementEnd('form'); $this->elementEnd('li'); $this->elementEnd('ul'); diff --git a/lib/groupminilist.php b/lib/groupminilist.php index fe38d0340..ae2d237f1 100644 --- a/lib/groupminilist.php +++ b/lib/groupminilist.php @@ -33,7 +33,7 @@ if (!defined('LACONICA')) { require_once INSTALLDIR.'/lib/grouplist.php'; -define('GROUPS_PER_MINILIST', 80); +define('GROUPS_PER_MINILIST', 27); /** * Widget to show a list of groups, good for sidebar @@ -75,8 +75,9 @@ class GroupMiniList extends GroupList 'href' => $this->group->homeUrl(), 'rel' => 'contact group', 'class' => 'url')); - $logo = ($this->group->stream_logo) ? - $this->group->stream_logo : User_group::defaultLogo(AVATAR_STREAM_SIZE); + + $logo = ($this->group->mini_logo) ? + $this->group->mini_logo : User_group::defaultLogo(AVATAR_MINI_SIZE); $this->out->element('img', array('src' => $logo, 'width' => AVATAR_MINI_SIZE, diff --git a/lib/mailbox.php b/lib/mailbox.php index d77234549..01bbf5721 100644 --- a/lib/mailbox.php +++ b/lib/mailbox.php @@ -137,6 +137,9 @@ class MailboxAction extends PersonalAction $message->free(); unset($message); } + else { + $this->element('p', 'guide', _('You have no private messages. You can send private message to engage other users in conversation. People can send you messages for your eyes only.')); + } } function getMessages() diff --git a/lib/noticelist.php b/lib/noticelist.php index 9fc0126b3..4182d8808 100644 --- a/lib/noticelist.php +++ b/lib/noticelist.php @@ -258,8 +258,12 @@ class NoticeListItem extends Widget function showAuthor() { $this->out->elementStart('span', 'vcard author'); - $this->out->elementStart('a', array('href' => $this->profile->profileurl, - 'class' => 'url')); + $attrs = array('href' => $this->profile->profileurl, + 'class' => 'url'); + if (!empty($this->profile->fullname)) { + $attrs['title'] = $this->profile->fullname . ' (' . $this->profile->nickname . ') '; + } + $this->out->elementStart('a', $attrs); $this->showAvatar(); $this->showNickname(); $this->out->elementEnd('a'); @@ -387,6 +391,7 @@ class NoticeListItem extends Widget case 'xmpp': case 'mail': case 'omb': + case 'system': case 'api': $this->out->element('dd', null, $source_name); break; diff --git a/lib/openid.php b/lib/openid.php index 3aa488b6d..3af7a39cf 100644 --- a/lib/openid.php +++ b/lib/openid.php @@ -160,7 +160,7 @@ function oid_authenticate($openid_url, $returnto, $immediate=false) $auth_request->addExtension($sreg_request); } - $trust_root = common_path(''); + $trust_root = common_root_url(true); $process_url = common_local_url($returnto); if ($auth_request->shouldSendRedirect()) { @@ -171,7 +171,7 @@ function oid_authenticate($openid_url, $returnto, $immediate=false) } else if (Auth_OpenID::isFailure($redirect_url)) { return sprintf(_('Could not redirect to server: %s'), $redirect_url->message); } else { - common_redirect($redirect_url); + common_redirect($redirect_url, 303); } } else { // Generate form markup and render it. diff --git a/lib/profileaction.php b/lib/profileaction.php new file mode 100644 index 000000000..c81924e31 --- /dev/null +++ b/lib/profileaction.php @@ -0,0 +1,242 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * Common parent of Personal and Profile actions + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Personal + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @author Sarven Capadisli <csarven@controlyourself.ca> + * @copyright 2008-2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/profileminilist.php'; +require_once INSTALLDIR.'/lib/groupminilist.php'; + +/** + * Profile action common superclass + * + * Abstracts out common code from profile and personal tabs + * + * @category Personal + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +class ProfileAction extends Action +{ + var $user = null; + var $page = null; + var $profile = null; + + function prepare($args) + { + parent::prepare($args); + + $nickname_arg = $this->arg('nickname'); + $nickname = common_canonical_nickname($nickname_arg); + + // Permanent redirect on non-canonical nickname + + if ($nickname_arg != $nickname) { + $args = array('nickname' => $nickname); + if ($this->arg('page') && $this->arg('page') != 1) { + $args['page'] = $this->arg['page']; + } + common_redirect(common_local_url($this->trimmed('action'), $args), 301); + return false; + } + + $this->user = User::staticGet('nickname', $nickname); + + if (!$this->user) { + $this->clientError(_('No such user.'), 404); + return false; + } + + $this->profile = $this->user->getProfile(); + + if (!$this->profile) { + $this->serverError(_('User has no profile.')); + return false; + } + + $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; + + common_set_returnto($this->selfUrl()); + + return true; + } + + function showSections() + { + $this->showSubscriptions(); + $this->showSubscribers(); + $this->showGroups(); + $this->showStatistics(); + } + + function showSubscriptions() + { + $profile = $this->user->getSubscriptions(0, PROFILES_PER_MINILIST + 1); + + $this->elementStart('div', array('id' => 'entity_subscriptions', + 'class' => 'section')); + + $this->element('h2', null, _('Subscriptions')); + + if ($profile) { + $pml = new ProfileMiniList($profile, $this->user, $this); + $cnt = $pml->show(); + if ($cnt == 0) { + $this->element('p', null, _('(None)')); + } + } + + if ($cnt > PROFILES_PER_MINILIST) { + $this->elementStart('p'); + $this->element('a', array('href' => common_local_url('subscriptions', + array('nickname' => $this->profile->nickname)), + 'class' => 'more'), + _('All subscriptions')); + $this->elementEnd('p'); + } + + $this->elementEnd('div'); + } + + function showSubscribers() + { + $profile = $this->user->getSubscribers(0, PROFILES_PER_MINILIST + 1); + + $this->elementStart('div', array('id' => 'entity_subscribers', + 'class' => 'section')); + + $this->element('h2', null, _('Subscribers')); + + if ($profile) { + $pml = new ProfileMiniList($profile, $this->user, $this); + $cnt = $pml->show(); + if ($cnt == 0) { + $this->element('p', null, _('(None)')); + } + } + + if ($cnt > PROFILES_PER_MINILIST) { + $this->elementStart('p'); + $this->element('a', array('href' => common_local_url('subscribers', + array('nickname' => $this->profile->nickname)), + 'class' => 'more'), + _('All subscribers')); + $this->elementEnd('p'); + } + + $this->elementEnd('div'); + } + + function showStatistics() + { + // XXX: WORM cache this + $subs = new Subscription(); + $subs->subscriber = $this->profile->id; + $subs_count = (int) $subs->count() - 1; + + $subbed = new Subscription(); + $subbed->subscribed = $this->profile->id; + $subbed_count = (int) $subbed->count() - 1; + + $notices = new Notice(); + $notices->profile_id = $this->profile->id; + $notice_count = (int) $notices->count(); + + $this->elementStart('div', array('id' => 'entity_statistics', + 'class' => 'section')); + + $this->element('h2', null, _('Statistics')); + + // Other stats...? + $this->elementStart('dl', 'entity_member-since'); + $this->element('dt', null, _('Member since')); + $this->element('dd', null, date('j M Y', + strtotime($this->profile->created))); + $this->elementEnd('dl'); + + $this->elementStart('dl', 'entity_subscriptions'); + $this->elementStart('dt'); + $this->element('a', array('href' => common_local_url('subscriptions', + array('nickname' => $this->profile->nickname))), + _('Subscriptions')); + $this->elementEnd('dt'); + $this->element('dd', null, (is_int($subs_count)) ? $subs_count : '0'); + $this->elementEnd('dl'); + + $this->elementStart('dl', 'entity_subscribers'); + $this->elementStart('dt'); + $this->element('a', array('href' => common_local_url('subscribers', + array('nickname' => $this->profile->nickname))), + _('Subscribers')); + $this->elementEnd('dt'); + $this->element('dd', 'subscribers', (is_int($subbed_count)) ? $subbed_count : '0'); + $this->elementEnd('dl'); + + $this->elementStart('dl', 'entity_notices'); + $this->element('dt', null, _('Notices')); + $this->element('dd', null, (is_int($notice_count)) ? $notice_count : '0'); + $this->elementEnd('dl'); + + $this->elementEnd('div'); + } + + function showGroups() + { + $groups = $this->user->getGroups(0, GROUPS_PER_MINILIST + 1); + + $this->elementStart('div', array('id' => 'entity_groups', + 'class' => 'section')); + + $this->element('h2', null, _('Groups')); + + if ($groups) { + $gml = new GroupMiniList($groups, $this->user, $this); + $cnt = $gml->show(); + if ($cnt == 0) { + $this->element('p', null, _('(None)')); + } + } + + if ($cnt > GROUPS_PER_MINILIST) { + $this->elementStart('p'); + $this->element('a', array('href' => common_local_url('usergroups', + array('nickname' => $this->profile->nickname)), + 'class' => 'more'), + _('All groups')); + $this->elementEnd('p'); + } + + $this->elementEnd('div'); + } +}
\ No newline at end of file diff --git a/lib/profileminilist.php b/lib/profileminilist.php index 0d466bba8..57496d0e9 100644 --- a/lib/profileminilist.php +++ b/lib/profileminilist.php @@ -33,7 +33,7 @@ if (!defined('LACONICA')) { require_once INSTALLDIR.'/lib/profilelist.php'; -define('PROFILES_PER_MINILIST', 80); +define('PROFILES_PER_MINILIST', 27); /** * Widget to show a list of profiles, good for sidebar diff --git a/lib/router.php b/lib/router.php index ae5b5e16b..6fb2f9487 100644 --- a/lib/router.php +++ b/lib/router.php @@ -107,6 +107,9 @@ class Router $m->connect('main/'.$a, array('action' => $a)); } + $m->connect('main/sup/:seconds', array('action' => 'sup'), + array('seconds' => '[0-9]+')); + $m->connect('main/tagother/:id', array('action' => 'tagother')); // these take a code diff --git a/lib/searchaction.php b/lib/searchaction.php index c762db16f..e7ad4affd 100644 --- a/lib/searchaction.php +++ b/lib/searchaction.php @@ -133,5 +133,31 @@ class SearchAction extends Action $this->showResults($q, $page); } } + + function searchSuggestions($q) { + $qe = urlencode($q); + $message = sprintf(_(<<<E_O_T +* Make sure all words are spelled correctly. +* Try different keywords. +* Try more general keywords. +* Try fewer keywords. + +You can also try your search on other engines: + +* [Twingly](http://www.twingly.com/search?q=%s&content=microblog&site=identi.ca) +* [Tweet scan](http://www.tweetscan.com/indexi.php?s=%s) +* [Google](http://www.google.com/search?q=site%%3A%%%%site.server%%%%+%s) +* [Yahoo](http://search.yahoo.com/search?p=site%%3A%%%%site.server%%%%+%s) + + +E_O_T +), $qe, $qe, $qe, $qe); + $this->elementStart('dl', array('id' => 'help_search', 'class' => 'help')); + $this->element('dt', null, _('Search help')); + $this->elementStart('dd', 'instructions'); + $this->raw(common_markup_to_html($message)); + $this->elementEnd('dd'); + $this->elementEnd('div'); + } } diff --git a/lib/settingsaction.php b/lib/settingsaction.php index 53c807c6f..db20c5804 100644 --- a/lib/settingsaction.php +++ b/lib/settingsaction.php @@ -78,9 +78,9 @@ class SettingsAction extends Action common_set_returnto($this->selfUrl()); $user = common_current_user(); if ($user->hasOpenID()) { - common_redirect(common_local_url('openidlogin')); + common_redirect(common_local_url('openidlogin'), 303); } else { - common_redirect(common_local_url('login')); + common_redirect(common_local_url('login'), 303); } } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { $this->handlePost(); diff --git a/lib/twitter.php b/lib/twitter.php index 7abb40151..ccc6c93ca 100644 --- a/lib/twitter.php +++ b/lib/twitter.php @@ -19,7 +19,7 @@ if (!defined('LACONICA')) { exit(1); } -define("TWITTER_SERVICE", 1); // Twitter is foreign_service ID 1 +define('TWITTER_SERVICE', 1); // Twitter is foreign_service ID 1 function get_twitter_data($uri, $screen_name, $password) { @@ -45,6 +45,10 @@ function get_twitter_data($uri, $screen_name, $password) if ($errmsg) { common_debug("Twitter bridge - cURL error: $errmsg - trying to load: $uri with user $screen_name.", __FILE__); + + if (defined('SCRIPT_DEBUG')) { + print "cURL error: $errmsg - trying to load: $uri with user $screen_name.\n"; + } } curl_close($ch); @@ -52,63 +56,141 @@ function get_twitter_data($uri, $screen_name, $password) return $data; } -function twitter_user_info($screen_name, $password) +function twitter_json_data($uri, $screen_name, $password) { + $json_data = get_twitter_data($uri, $screen_name, $password); - $uri = "http://twitter.com/users/show/$screen_name.json"; - $data = get_twitter_data($uri, $screen_name, $password); - - if (!$data) { + if (!$json_data) { return false; } - $twit_user = json_decode($data); + $data = json_decode($json_data); - if (!$twit_user) { + if (!$data) { return false; } - return $twit_user; + return $data; } -function update_twitter_user($fuser, $twitter_id, $screen_name) +function twitter_user_info($screen_name, $password) { + $uri = "http://twitter.com/users/show/$screen_name.json"; + return twitter_json_data($uri, $screen_name, $password); +} - $original = clone($fuser); - $fuser->nickname = $screen_name; - $fuser->uri = 'http://twitter.com/' . $screen_name; - $result = $fuser->updateKeys($original); +function twitter_friends_ids($screen_name, $password) +{ + $uri = "http://twitter.com/friends/ids/$screen_name.json"; + return twitter_json_data($uri, $screen_name, $password); +} + +function update_twitter_user($twitter_id, $screen_name) +{ + $uri = 'http://twitter.com/' . $screen_name; + + $fuser = new Foreign_user(); + + $fuser->query('BEGIN'); + + // Dropping down to SQL because regular db_object udpate stuff doesn't seem + // to work so good with tables that have multiple column primary keys + + // Any time we update the uri for a forein user we have to make sure there + // are no dupe entries first -- unique constraint on the uri column + + $qry = 'UPDATE foreign_user set uri = \'\' WHERE uri = '; + $qry .= '\'' . $uri . '\'' . ' AND service = ' . TWITTER_SERVICE; + + $result = $fuser->query($qry); + + if ($result) { + common_debug("Removed uri ($uri) from another foreign_user who was squatting on it."); + if (defined('SCRIPT_DEBUG')) { + print("Removed uri ($uri) from another Twitter user who was squatting on it.\n"); + } + } + + // Update the user + $qry = 'UPDATE foreign_user SET nickname = '; + $qry .= '\'' . $screen_name . '\'' . ', uri = \'' . $uri . '\' '; + $qry .= 'WHERE id = ' . $twitter_id . ' AND service = ' . TWITTER_SERVICE; + + $result = $fuser->query($qry); if (!$result) { + common_log(LOG_WARNING, + "Couldn't update foreign_user data for Twitter user: $screen_name"); common_log_db_error($fuser, 'UPDATE', __FILE__); + if (defined('SCRIPT_DEBUG')) { + print "UPDATE failed: for Twitter user: $twitter_id - $screen_name. - "; + print common_log_objstring($fuser) . "\n"; + $error = &PEAR::getStaticProperty('DB_DataObject','lastError'); + print "DB_DataObject Error: " . $error->getMessage() . "\n"; + } return false; } + $fuser->query('COMMIT'); + + $fuser->free(); + unset($fuser); + return true; } function add_twitter_user($twitter_id, $screen_name) { + $new_uri = 'http://twitter.com/' . $screen_name; + + // Clear out any bad old foreign_users with the new user's legit URL + // This can happen when users move around or fakester accounts get + // repoed, and things like that. + $luser = new Foreign_user(); + $luser->uri = $new_uri; + $luser->service = TWITTER_SERVICE; + $result = $luser->delete(); + + if ($result) { + common_log(LOG_WARNING, + "Twitter bridge - removed invalid Twitter user squatting on uri: $new_uri"); + if (defined('SCRIPT_DEBUG')) { + print "Removed invalid Twitter user squatting on uri: $new_uri\n"; + } + } + + $luser->free(); + unset($luser); + // Otherwise, create a new Twitter user - $fuser = DB_DataObject::factory('foreign_user'); + $fuser = new Foreign_user(); $fuser->nickname = $screen_name; $fuser->uri = 'http://twitter.com/' . $screen_name; $fuser->id = $twitter_id; - $fuser->service = TWITTER_SERVICE; // Twitter + $fuser->service = TWITTER_SERVICE; $fuser->created = common_sql_now(); $result = $fuser->insert(); if (!$result) { - common_debug("Twitter bridge - failed to add new Twitter user: $twitter_id - $screen_name."); + common_log(LOG_WARNING, + "Twitter bridge - failed to add new Twitter user: $twitter_id - $screen_name."); common_log_db_error($fuser, 'INSERT', __FILE__); - return false; + if (defined('SCRIPT_DEBUG')) { + print "INSERT failed: could not add new Twitter user: $twitter_id - $screen_name. - "; + print common_log_objstring($fuser) . "\n"; + $error = &PEAR::getStaticProperty('DB_DataObject','lastError'); + print "DB_DataObject Error: " . $error->getMessage() . "\n"; + } + } else { + common_debug("Twitter bridge - Added new Twitter user: $screen_name ($twitter_id)."); + if (defined('SCRIPT_DEBUG')) { + print "Added new Twitter user: $screen_name ($twitter_id).\n"; + } } - common_debug("Twitter bridge - Added new Twitter user: $screen_name ($twitter_id)."); - - return true; + return $result; } // Creates or Updates a Twitter user @@ -117,53 +199,87 @@ function save_twitter_user($twitter_id, $screen_name) // Check to see whether the Twitter user is already in the system, // and update its screen name and uri if so. - $fuser = Foreign_user::getForeignUser($twitter_id, 1); + $fuser = Foreign_user::getForeignUser($twitter_id, TWITTER_SERVICE); if ($fuser) { + $result = true; + // Only update if Twitter screen name has changed if ($fuser->nickname != $screen_name) { + $result = update_twitter_user($twitter_id, $screen_name); common_debug('Twitter bridge - Updated nickname (and URI) for Twitter user ' . "$fuser->id to $screen_name, was $fuser->nickname"); - return update_twitter_user($fuser, $twitter_id, $screen_name); + if (defined('SCRIPT_DEBUG')) { + print 'Updated nickname (and URI) for Twitter user ' . + "$fuser->id to $screen_name, was $fuser->nickname\n"; + } } + return $result; + } else { return add_twitter_user($twitter_id, $screen_name); } + $fuser->free(); + unset($fuser); + return true; } function retreive_twitter_friends($twitter_id, $screen_name, $password) { + $friends = array(); $uri = "http://twitter.com/statuses/friends/$twitter_id.json?page="; - $twitter_user = twitter_user_info($screen_name, $password); + $friends_ids = twitter_friends_ids($screen_name, $password); + + if (!$friends_ids) { + return $friends; + } + + if (defined('SCRIPT_DEBUG')) { + print "Twitter 'social graph' ids method says $screen_name has " . + count($friends_ids) . " friends.\n"; + } // Calculate how many pages to get... - $pages = ceil($twitter_user->friends_count / 100); + $pages = ceil(count($friends_ids) / 100); if ($pages == 0) { - common_debug("Twitter bridge - Twitter user $screen_name has no friends! Lame."); + common_log(LOG_WARNING, + "Twitter bridge - $screen_name seems to have no friends."); + if (defined('SCRIPT_DEBUG')) { + print "$screen_name seems to have no friends.\n"; + } } - $friends = array(); - for ($i = 1; $i <= $pages; $i++) { $data = get_twitter_data($uri . $i, $screen_name, $password); if (!$data) { - return null; + common_log(LOG_WARNING, + "Twitter bridge - Couldn't retrieve page $i of $screen_name's friends."); + if (defined('SCRIPT_DEBUG')) { + print "Couldn't retrieve page $i of $screen_name's friends.\n"; + } + continue; } $more_friends = json_decode($data); if (!$more_friends) { - return null; + + common_log(LOG_WARNING, + "Twitter bridge - No data for page $i of $screen_name's friends."); + if (defined('SCRIPT_DEBUG')) { + print "No data for page $i of $screen_name's friends.\n"; + } + continue; } $friends = array_merge($friends, $more_friends); @@ -177,19 +293,27 @@ function save_twitter_friends($user, $twitter_id, $screen_name, $password) $friends = retreive_twitter_friends($twitter_id, $screen_name, $password); - if (is_null($friends)) { - common_debug("Twitter bridge - Couldn't get friends data from Twitter."); + if (empty($friends)) { + common_debug("Twitter bridge - Couldn't get friends data from Twitter for $screen_name."); + if (defined('SCRIPT_DEBUG')) { + print "Couldn't get friends data from Twitter for $screen_name.\n"; + } return false; } foreach ($friends as $friend) { $friend_name = $friend->screen_name; - $friend_id = $friend->id; + $friend_id = (int) $friend->id; // Update or create the Foreign_user record if (!save_twitter_user($friend_id, $friend_name)) { - return false; + common_log(LOG_WARNING, + "Twitter bridge - couldn't save $screen_name's friend, $friend_name."); + if (defined('SCRIPT_DEBUG')) { + print "Couldn't save $screen_name's friend, $friend_name.\n"; + } + continue; } // Check to see if there's a related local user @@ -199,8 +323,20 @@ function save_twitter_friends($user, $twitter_id, $screen_name, $password) // Get associated user and subscribe her $friend_user = User::staticGet('id', $flink->user_id); - subs_subscribe_to($user, $friend_user); - common_debug("Twitter bridge - subscribed $friend_user->nickname to $user->nickname."); + if (!empty($friend_user)) { + $result = subs_subscribe_to($user, $friend_user); + + if ($result === true) { + common_debug("Twitter bridge - subscribed $friend_user->nickname to $user->nickname."); + if (defined('SCRIPT_DEBUG')) { + print("Subscribed $friend_user->nickname to $user->nickname.\n"); + } + } else { + if (defined('SCRIPT_DEBUG')) { + print "$result ($friend_user->nickname to $user->nickname)\n"; + } + } + } } } @@ -218,7 +354,7 @@ function is_twitter_bound($notice, $flink) { return true; } } - + return false; } @@ -226,10 +362,10 @@ function broadcast_twitter($notice) { $success = true; - $flink = Foreign_link::getByUserID($notice->profile_id, + $flink = Foreign_link::getByUserID($notice->profile_id, TWITTER_SERVICE); - - // XXX: Not sure WHERE to check whether a notice should go to + + // XXX: Not sure WHERE to check whether a notice should go to // Twitter. Should we even put in the queue if it shouldn't? --Zach if (!is_null($flink) && is_twitter_bound($notice, $flink)) { @@ -244,7 +380,7 @@ function broadcast_twitter($notice) $options = array( CURLOPT_USERPWD => "$twitter_user:$twitter_password", CURLOPT_POST => true, - CURLOPT_POSTFIELDS => + CURLOPT_POSTFIELDS => array( 'status' => $statustxt, 'source' => common_config('integration', 'source') @@ -292,7 +428,6 @@ function broadcast_twitter($notice) $success = false; } } - + return $success; } - diff --git a/lib/util.php b/lib/util.php index a43666fa5..675ff51f0 100644 --- a/lib/util.php +++ b/lib/util.php @@ -581,10 +581,8 @@ function common_shorten_link($url, $reverse = false) function common_xml_safe_str($str) { - $xmlStr = htmlentities(iconv('UTF-8', 'UTF-8//IGNORE', $str), ENT_NOQUOTES, 'UTF-8'); - - // Replace control, formatting, and surrogate characters with '*', ala Twitter - return preg_replace('/[\p{Cc}\p{Cf}\p{Cs}]/u', '*', $str); + // Neutralize control codes and surrogates + return preg_replace('/[\p{Cc}\p{Cs}]/u', '*', $str); } function common_tag_link($tag) @@ -622,9 +620,13 @@ function common_at_link($sender_id, $nickname) $url = $recipient->profileurl; } $xs = new XMLStringer(false); + $attrs = array('href' => $url, + 'class' => 'url'); + if (!empty($recipient->fullname)) { + $attrs['title'] = $recipient->fullname . ' (' . $recipient->nickname . ')'; + } $xs->elementStart('span', 'vcard'); - $xs->elementStart('a', array('href' => $url, - 'class' => 'url')); + $xs->elementStart('a', $attrs); $xs->element('span', 'fn nickname', $nickname); $xs->elementEnd('a'); $xs->elementEnd('span'); @@ -639,10 +641,14 @@ function common_group_link($sender_id, $nickname) $sender = Profile::staticGet($sender_id); $group = User_group::staticGet('nickname', common_canonical_nickname($nickname)); if ($group && $sender->isMember($group)) { + $attrs = array('href' => $group->permalink(), + 'class' => 'url'); + if (!empty($group->fullname)) { + $attrs['title'] = $group->fullname . ' (' . $group->nickname . ')'; + } $xs = new XMLStringer(); $xs->elementStart('span', 'vcard'); - $xs->elementStart('a', array('href' => $group->permalink(), - 'class' => 'url')); + $xs->elementStart('a', $attrs); $xs->element('span', 'fn nickname', $nickname); $xs->elementEnd('a'); $xs->elementEnd('span'); @@ -713,25 +719,46 @@ function common_relative_profile($sender, $nickname, $dt=null) function common_local_url($action, $args=null, $params=null, $fragment=null) { + static $sensitive = array('login', 'register', 'passwordsettings', + 'twittersettings', 'finishopenidlogin', + 'finishaddopenid', 'api'); + $r = Router::get(); $path = $r->build($action, $args, $params, $fragment); + $ssl = in_array($action, $sensitive); + if (common_config('site','fancy')) { - $url = common_path(mb_substr($path, 1)); + $url = common_path(mb_substr($path, 1), $ssl); } else { if (mb_strpos($path, '/index.php') === 0) { - $url = common_path(mb_substr($path, 1)); + $url = common_path(mb_substr($path, 1), $ssl); } else { - $url = common_path('index.php'.$path); + $url = common_path('index.php'.$path, $ssl); } } return $url; } -function common_path($relative) +function common_path($relative, $ssl=false) { $pathpart = (common_config('site', 'path')) ? common_config('site', 'path')."/" : ''; - return "http://".common_config('site', 'server').'/'.$pathpart.$relative; + + if (($ssl && (common_config('site', 'ssl') === 'sometimes')) + || common_config('site', 'ssl') === 'always') { + $proto = 'https'; + if (is_string(common_config('site', 'sslserver')) && + mb_strlen(common_config('site', 'sslserver')) > 0) { + $serverpart = common_config('site', 'sslserver'); + } else { + $serverpart = common_config('site', 'server'); + } + } else { + $proto = 'http'; + $serverpart = common_config('site', 'server'); + } + + return $proto.'://'.$serverpart.'/'.$pathpart.$relative; } function common_date_string($dt) @@ -821,7 +848,7 @@ function common_redirect($url, $code=307) 303 => "See Other", 307 => "Temporary Redirect"); - header("Status: ${code} $status[$code]"); + header('HTTP/1.1 '.$code.' '.$status[$code]); header("Location: $url"); $xo = new XMLOutputter(); @@ -923,9 +950,9 @@ function common_profile_url($nickname) // Should make up a reasonable root URL -function common_root_url() +function common_root_url($ssl=false) { - return common_path(''); + return common_path('', $ssl); } // returns $bytes bytes of random data as a hexadecimal string @@ -1294,3 +1321,16 @@ function common_compatible_license($from, $to) // XXX: better compatibility check needed here! return ($from == $to); } + +/** + * returns a quoted table name, if required according to config + */ +function common_database_tablename($tablename) +{ + + if(common_config('db','quote_identifiers')) { + $tablename = '"'. $tablename .'"'; + } + //table prefixes could be added here later + return $tablename; +}
\ No newline at end of file diff --git a/plugins/LinkbackPlugin.php b/plugins/LinkbackPlugin.php index 1b5365100..56a26176b 100644 --- a/plugins/LinkbackPlugin.php +++ b/plugins/LinkbackPlugin.php @@ -105,12 +105,13 @@ class LinkbackPlugin extends Plugin $pb = $match[1]; } - $tb = $this->getTrackback($result->body, $result->final_url); - - if (!empty($tb)) { - $this->trackback($result->final_url, $tb); - } else if (!empty($pb)) { + if (!empty($pb)) { $this->pingback($result->final_url, $pb); + } else { + $tb = $this->getTrackback($result->body, $result->final_url); + if (!empty($tb)) { + $this->trackback($result->final_url, $tb); + } } return $orig; diff --git a/plugins/TemplatePlugin.php b/plugins/TemplatePlugin.php new file mode 100644 index 000000000..03daf6219 --- /dev/null +++ b/plugins/TemplatePlugin.php @@ -0,0 +1,344 @@ +<?php +/** + * Plugin to render old skool templates + * + * Captures rendered parts from the output buffer, passes them through a template file: tpl/index.html + * Adds an API method at index.php/template/update which lets you overwrite the template file + * Requires username/password and a single POST parameter called "template" + * The method is disabled unless the user is #1, the first user of the system + * + * @category Plugin + * @package Laconica + * @author Brian Hendrickson <brian@megapump.com> + * @copyright 2009 Megapump, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://megapump.com/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +define('TEMPLATEPLUGIN_VERSION', '0.1'); + +class TemplatePlugin extends Plugin { + + var $blocks = array(); + + function __construct() { + parent::__construct(); + } + + // capture the RouterInitialized event + // and connect a new API method + // for updating the template + function onRouterInitialized( &$m ) { + $m->connect( 'template/update', array( + 'action' => 'template', + )); + } + + + // <%styles%> + // <%scripts%> + // <%search%> + // <%feeds%> + // <%description%> + // <%head%> + function onStartShowHead( &$act ) { + $this->clear_xmlWriter($act); + $act->extraHead(); + $this->blocks['head'] = $act->xw->flush(); + $act->showStylesheets(); + $this->blocks['styles'] = $act->xw->flush(); + $act->showScripts(); + $this->blocks['scripts'] = $act->xw->flush(); + $act->showFeeds(); + $this->blocks['feeds'] = $act->xw->flush(); + $act->showOpenSearch(); + $this->blocks['search'] = $act->xw->flush(); + $act->showDescription(); + $this->blocks['description'] = $act->xw->flush(); + return false; + } + + // <%bodytext%> + function onStartShowContentBlock( &$act ) { + $this->clear_xmlWriter($act); + return true; + } + function onEndShowContentBlock( &$act ) { + $this->blocks['bodytext'] = $act->xw->flush(); + } + + // <%localnav%> + function onStartShowLocalNavBlock( &$act ) { + $this->clear_xmlWriter($act); + return true; + } + function onEndShowLocalNavBlock( &$act ) { + $this->blocks['localnav'] = $act->xw->flush(); + } + + // <%export%> + function onStartShowExportData( &$act ) { + $this->clear_xmlWriter($act); + return true; + } + function onEndShowExportData( &$act ) { + $this->blocks['export'] = $act->xw->flush(); + } + + // <%subscriptions%> + // <%subscribers%> + // <%groups%> + // <%statistics%> + // <%cloud%> + // <%groupmembers%> + // <%groupstatistics%> + // <%groupcloud%> + // <%popular%> + // <%groupsbyposts%> + // <%featuredusers%> + // <%groupsbymembers%> + function onStartShowSections( &$act ) { + global $action; + $this->clear_xmlWriter($act); + switch ($action) { + case "showstream": + $act->showSubscriptions(); + $this->blocks['subscriptions'] = $act->xw->flush(); + $act->showSubscribers(); + $this->blocks['subscribers'] = $act->xw->flush(); + $act->showGroups(); + $this->blocks['groups'] = $act->xw->flush(); + $act->showStatistics(); + $this->blocks['statistics'] = $act->xw->flush(); + $cloud = new PersonalTagCloudSection($act, $act->user); + $cloud->show(); + $this->blocks['cloud'] = $act->xw->flush(); + break; + case "showgroup": + $act->showMembers(); + $this->blocks['groupmembers'] = $act->xw->flush(); + $act->showStatistics(); + $this->blocks['groupstatistics'] = $act->xw->flush(); + $cloud = new GroupTagCloudSection($act, $act->group); + $cloud->show(); + $this->blocks['groupcloud'] = $act->xw->flush(); + break; + case "public": + $pop = new PopularNoticeSection($act); + $pop->show(); + $this->blocks['popular'] = $act->xw->flush(); + $gbp = new GroupsByPostsSection($act); + $gbp->show(); + $this->blocks['groupsbyposts'] = $act->xw->flush(); + $feat = new FeaturedUsersSection($act); + $feat->show(); + $this->blocks['featuredusers'] = $act->xw->flush(); + break; + case "groups": + $gbp = new GroupsByPostsSection($act); + $gbp->show(); + $this->blocks['groupsbyposts'] = $act->xw->flush(); + $gbm = new GroupsByMembersSection($act); + $gbm->show(); + $this->blocks['groupsbymembers'] = $act->xw->flush(); + break; + } + return false; + } + + // <%logo%> + // <%nav%> + // <%notice%> + // <%noticeform%> + function onStartShowHeader( &$act ) { + $this->clear_xmlWriter($act); + $act->showLogo(); + $this->blocks['logo'] = $act->xw->flush(); + $act->showPrimaryNav(); + $this->blocks['nav'] = $act->xw->flush(); + $act->showSiteNotice(); + $this->blocks['notice'] = $act->xw->flush(); + if (common_logged_in()) { + $act->showNoticeForm(); + } else { + $act->showAnonymousMessage(); + } + $this->blocks['noticeform'] = $act->xw->flush(); + return false; + } + + // <%secondarynav%> + // <%licenses%> + function onStartShowFooter( &$act ) { + $this->clear_xmlWriter($act); + $act->showSecondaryNav(); + $this->blocks['secondarynav'] = $act->xw->flush(); + $act->showLicenses(); + $this->blocks['licenses'] = $act->xw->flush(); + return false; + } + + // capture the EndHTML event + // and include the template + function onEndEndHTML($act) { + + global $action, $tags; + + // set the action and title values + $vars = array( + 'action'=>$action, + 'title'=>$act->title(). " - ". common_config('site', 'name') + ); + + // use the PHP template + // unless laconica config: + // $config['template']['mode'] = 'html'; + if (!(common_config('template', 'mode') == 'html')) { + $tpl_file = 'tpl/index.php'; + $tags = array_merge($vars,$this->blocks); + include $tpl_file; + return; + } + + $tpl_file = 'tpl/index.html'; + + // read the static template + $output = file_get_contents( $tpl_file ); + + $tags = array(); + + // get a list of the <%tags%> in the template + $pattern='/<%([a-z]+)%>/'; + + if ( 1 <= preg_match_all( $pattern, $output, $found )) + $tags[] = $found; + + // for each found tag, set its value from the rendered blocks + foreach( $tags[0][1] as $pos=>$tag ) { + if (isset($this->blocks[$tag])) + $vars[$tag] = $this->blocks[$tag]; + + // didn't find a block for the tag + elseif (!isset($vars[$tag])) + $vars[$tag] = ''; + } + + // replace the tags in the template + foreach( $vars as $key=>$val ) + $output = str_replace( '<%'.$key.'%>', $val, $output ); + + echo $output; + + return true; + + } + + // catching the StartShowHTML event to halt the rendering + function onStartShowHTML( &$act ) { + $this->clear_xmlWriter($act); + return true; + } + + // clear the xmlWriter + function clear_xmlWriter( &$act ) { + $act->xw->openMemory(); + $act->xw->setIndent(true); + } + +} + +/** + * Action for updating the template remotely + * + * "template/update" -- a POST method that requires a single + * parameter "template", containing the new template code + * + * @category Plugin + * @package Laconica + * @author Brian Hendrickson <brian@megapump.com> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://megapump.com/ + * + */ + +class TemplateAction extends Action +{ + + function prepare($args) { + parent::prepare($args); + return true; + } + + function handle($args) { + + parent::handle($args); + + if (!isset($_SERVER['PHP_AUTH_USER'])) { + + // not authenticated, show login form + header('WWW-Authenticate: Basic realm="Laconica API"'); + + // cancelled the browser login form + $this->clientError(_('Authentication error!'), $code = 401); + + } else { + + $nick = $_SERVER['PHP_AUTH_USER']; + $pass = $_SERVER['PHP_AUTH_PW']; + + // check username and password + $user = common_check_user($nick,$pass); + + if ($user) { + + // verify that user is admin + if (!($user->id == 1)) + $this->clientError(_('only User #1 can update the template'), $code = 401); + + // open the old template + $tpl_file = 'tpl/index.html'; + $fp = fopen( $tpl_file, 'w+' ); + + // overwrite with the new template + fwrite($fp, $this->arg('template')); + fclose($fp); + + header('HTTP/1.1 200 OK'); + header('Content-type: text/plain'); + print "Template Updated!"; + + } else { + + // bad username and password + $this->clientError(_('Authentication error!'), $code = 401); + + } + + } + } +} + +/** + * Function for retrieving a laconica display section + * + * requires one parameter, the name of the section + * section names are listed in the comments of the TemplatePlugin class + * + * @category Plugin + * @package Laconica + * @author Brian Hendrickson <brian@megapump.com> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://megapump.com/ + * + */ + +function section($tagname) { + global $tags; + if (isset($tags[$tagname])) + return $tags[$tagname]; +} + diff --git a/scripts/laconica.spec b/scripts/laconica.spec index 5f0ed5fa9..331e10671 100644 --- a/scripts/laconica.spec +++ b/scripts/laconica.spec @@ -1,11 +1,14 @@ +# This version needs to match the tarball and unpacked directory name. +%define LACVER 0.7.3 + BuildRequires: php-pear BuildRequires: httpd-devel Name: laconica -Version: 0.7.2 +Version: %{LACVER} Release: 1%{?dist} License: GAGPL v3 or later -Source: laconica-0.7.2.tar.gz +Source: laconica-%{version}.tar.gz Group: Applications/Internet Summary: Laconica, the Open Source microblogging platform BuildArch: noarch @@ -49,6 +52,8 @@ cp -a * %{buildroot}%{wwwpath} mkdir -p %{buildroot}%{_datadir}/laconica cp -a db %{buildroot}%{_datadir}/laconica/db +mkdir -p %{buildroot}%{_datadir}/laconica/avatar + mkdir -p %{buildroot}%{_sysconfdir}/httpd/conf.d cat > %{buildroot}%{_sysconfdir}/httpd/conf.d/laconica.conf <<"EOF" Alias /laconica/ "/var/www/laconica/" @@ -74,6 +79,12 @@ rm -rf %buildroot %config(noreplace) %{_sysconfdir}/httpd/conf.d/laconica.conf %changelog +* Wed Apr 03 2009 Zach Copley <zach@controlyourself.ca> - 0.7.3 +- Changed version number to 0.7.3. + +* Fri Mar 13 2009 Ken Sedgwick <ksedgwic@bonsai.com> - 0.7.2.1-1 +- Factored laconica version to the first line of the file. + * Wed Mar 03 2009 Zach Copley <zach@controlyourself.ca> - 0.7.2 - Changed version number to 0.7.2. diff --git a/scripts/synctwitterfriends.php b/scripts/synctwitterfriends.php index 0ce34c2ae..794301f0f 100755 --- a/scripts/synctwitterfriends.php +++ b/scripts/synctwitterfriends.php @@ -18,7 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -# Abort if called from a web server +// Abort if called from a web server if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { print "This script must be run from the command line\n"; exit(); @@ -27,11 +27,16 @@ if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); define('LACONICA', true); +// Uncomment this to get useful console output +//define('SCRIPT_DEBUG', true); + require_once(INSTALLDIR . '/lib/common.php'); $flink = new Foreign_link(); $flink->service = 1; // Twitter -$flink->find(); +$cnt = $flink->find(); + +print "Updating Twitter friends subscriptions for $cnt users.\n"; while ($flink->fetch()) { @@ -39,20 +44,30 @@ while ($flink->fetch()) { $user = User::staticGet($flink->user_id); - print "Updating Twitter friends for user $user->nickname ($user->id)\n"; + if (empty($user)) { + common_log(LOG_WARNING, "Unmatched user for ID " . $flink->user_id); + print "Unmatched user for ID $flink->user_id\n"; + continue; + } + + print "Updating Twitter friends for $user->nickname (Laconica ID: $user->id)... "; $fuser = $flink->getForeignUser(); - $result = save_twitter_friends($user, $fuser->id, $fuser->nickname, $flink->credentials); + if (empty($fuser)) { + common_log(LOG_WARNING, "Unmatched user for ID " . $flink->user_id); + print "Unmatched user for ID $flink->user_id\n"; + continue; + } - if ($result == false) { - print "Problems updating Twitter friends! Check the log.\n"; - exit(1); + $result = save_twitter_friends($user, $fuser->id, + $fuser->nickname, $flink->credentials); + if (defined('SCRIPT_DEBUG')) { + print "\nDONE\n"; + } else { + print "DONE\n"; } } - } exit(0); - - diff --git a/theme/base/css/display.css b/theme/base/css/display.css index 90cd00d55..3b4a2a4b3 100644 --- a/theme/base/css/display.css +++ b/theme/base/css/display.css @@ -83,12 +83,13 @@ left:0; border:0; } -#page_notice .error, -#page_notice .success { +.error, +.success { padding:4px 7px; border-radius:4px; -moz-border-radius:4px; -webkit-border-radius:4px; +margin-bottom:18px; } form label.submit { display:none; @@ -216,6 +217,9 @@ margin-right:0; address .fn { font-weight:bold; } +address img + .fn { +display:none; +} #header { width:100%; @@ -247,6 +251,7 @@ position:absolute; top:65px; right:18px; width:250px; +width:24%; } #page_notice { clear:both; @@ -256,9 +261,8 @@ margin-bottom:18px; #anon_notice { float:left; -width:432px; -width:28.052em; -padding:11px; +width:43.2%; +padding:1.1%; border-radius:7px; -moz-border-radius:7px; -webkit-border-radius:7px; @@ -366,7 +370,9 @@ margin-right:4px; #wrap { margin:0 auto; -width:1003px; +width:100%; +min-width:760px; +max-width:1003px; overflow:hidden; } @@ -378,14 +384,17 @@ margin-bottom:1em; } #content { -width:644px; -padding:18px; +width:64.009%; +min-height:259px; +padding:1.795%; float:left; border-radius:7px; -moz-border-radius:7px; -moz-border-radius-topleft:0; -webkit-border-radius:7px; -webkit-border-top-left-radius:0; +border-style:solid; +border-width:1px; } #content_inner { @@ -395,10 +404,11 @@ float:left; } #aside_primary { -width:280px; +width:27.917%; +min-height:259px; float:left; -margin-left:4px; -padding:18px; +margin-left:0.385%; +padding:1.795%; border-radius:7px; -moz-border-radius:7px; -webkit-border-radius:7px; @@ -406,9 +416,8 @@ border-width:1px; border-style:solid; } -/*Start: FORM NOTICE*/ #form_notice { -width:458px; +width:45.664%; float:left; position:relative; line-height:1; @@ -425,7 +434,7 @@ float:left; border-radius:7px; -moz-border-radius:7px; -webkit-border-radius:7px; -width:370px; +width:80.789%; height:67px; line-height:1.5; padding:7px 7px 16px 7px; @@ -455,27 +464,27 @@ line-height:1.15; padding:1px 2px; } #form_notice #notice_action-submit { -width:60px; -padding:8px; +width:14%; +height:47px; +padding:0; position:absolute; bottom:0; right:0; } #form_notice label[for=to] { -margin-top:11px; +margin-top:7px; } #form_notice select[id=to] { margin-bottom:7px; margin-left:18px; float:left; } -/*end FORM NOTICE*/ /* entity_profile */ .entity_profile { position:relative; -width:475px; +width:67.702%; min-height:123px; float:left; margin-bottom:18px; @@ -505,7 +514,6 @@ margin-bottom:18px; .entity_profile .entity_tags { margin-left:113px; margin-bottom:4px; -width:322px; } .entity_profile .entity_fn, @@ -533,14 +541,14 @@ display:none; .entity_profile h2 { display:none; } - /* entity_profile */ /*entity_actions*/ .entity_actions { float:right; -margin-left:28px; +margin-left:4.35%; +max-width:25%; } .entity_actions h2 { display:none; @@ -681,7 +689,7 @@ float:none; .profile .entity_profile .entity_note, .profile .entity_profile .entity_url, .profile .entity_profile .entity_tags, -.profile .entity_profile .form_subcription_edit { +.profile .entity_profile .form_subscription_edit { margin-left:59px; clear:none; display:block; @@ -693,7 +701,7 @@ margin-right:11px; } -.profile .entity_profile .form_subcription_edit label { +.profile .entity_profile .form_subscription_edit label { font-weight:normal; margin-right:11px; } @@ -908,8 +916,6 @@ display:none; border:0; padding:0; } -/*END: NOTICES */ - #new_group, #group_search { @@ -1140,4 +1146,16 @@ clear:both; margin-bottom:0; } - +.instructions ul { +list-style-position:inside; +} +.instructions p, +.instructions ul { +margin-bottom:18px; +} +.help dt { +display:none; +} +.guide { +clear:both; +}
\ No newline at end of file diff --git a/theme/base/css/ie.css b/theme/base/css/ie.css index 45176a201..5d8bea8ae 100644 --- a/theme/base/css/ie.css +++ b/theme/base/css/ie.css @@ -1,8 +1,21 @@ /* IE specific styles */ - -#aside_primary { -padding-left:11px; +legend { +margin-left:-7px; +} +input.checkbox { +top:0; +} +#form_notice textarea { +width:78%; +} +#form_notice #notice_action-submit { +width:17%; +max-width:17%; } +#anon_notice { +max-width:39%; +} + .notice-options input.submit { font-size:0; margin-top:3px; @@ -11,15 +24,9 @@ text-align:right; text-indent:0; width:24px; } - -input.checkbox { -top:0; -} - -legend { -margin-left:-7px; -} - .notice div.entry-content .timestamp a { margin-right:4px; } +.entity_profile { +width:64%; +} diff --git a/theme/base/css/ie6.css b/theme/base/css/ie6.css index fa6ec92d2..76a82c004 100644 --- a/theme/base/css/ie6.css +++ b/theme/base/css/ie6.css @@ -1,4 +1,17 @@ /* IE6 specific styles */ +address { +margin-left:7px; +} +address .fn { +display:none; +} +#content { +width:70%; +} +#aside_primary { +padding:5%; +width:29.5%; +} .entity_profile .entity_nickname, .entity_profile .entity_location, .entity_profile .entity_url, @@ -9,6 +22,9 @@ margin-left:0; .entity_profile .entity_depiction { margin-bottom:123px; } +.entity_actions { +width:20%; +} .notice div.entry-content { width:63%; } diff --git a/theme/base/css/print.css b/theme/base/css/print.css index 2da3e5e44..d76dd608c 100644 --- a/theme/base/css/print.css +++ b/theme/base/css/print.css @@ -21,7 +21,7 @@ p { orphans: 2; widows: 1; } .entity_actions, .notice-options, #aside_primary, -.form_subcription_edit .submit { +.form_subscription_edit .submit { display:none; } diff --git a/theme/default/css/display.css b/theme/default/css/display.css index bb0b84a67..42e29374f 100644 --- a/theme/default/css/display.css +++ b/theme/default/css/display.css @@ -17,10 +17,7 @@ font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif; font-size:1em; } address { -margin-right:71px; -} -address .fn { -display:none; +margin-right:7.18%; } input, textarea, select, option { @@ -40,7 +37,6 @@ background:none; input.submit, #form_notice.warning #notice_text-count, -#nav_register a, .form_settings .form_note, .entity_remote_subscribe { background-color:#A9BF4F; @@ -51,7 +47,6 @@ input:focus, textarea:focus, select:focus, border-color:#A9BF4F; } input.submit, -#nav_register a, .entity_remote_subscribe { color:#fff; } @@ -100,13 +95,6 @@ cursor:wait; text-indent:-9999px; } - -#nav_register a { -text-decoration:none; -font-weight:bold; -padding:2px 4px; -} - #content, #site_nav_local_views a, #aside_primary { @@ -125,10 +113,10 @@ background-color:rgba(255, 255, 255, 0.7); } -#page_notice .error { +.error { background-color:#F7E8E8; } -#page_notice .success { +.success { background-color:#EFF3DC; } diff --git a/theme/identica/css/display.css b/theme/identica/css/display.css index 69be11623..8edb005a6 100644 --- a/theme/identica/css/display.css +++ b/theme/identica/css/display.css @@ -17,10 +17,7 @@ font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif; font-size:1em; } address { -margin-right:71px; -} -address .fn { -display:none; +margin-right:7.18%; } input, textarea, select, option { @@ -40,7 +37,6 @@ background:none; input.submit, #form_notice.warning #notice_text-count, -#nav_register a, .form_settings .form_note, .entity_remote_subscribe { background-color:#9BB43E; @@ -51,7 +47,6 @@ input:focus, textarea:focus, select:focus, border-color:#9BB43E; } input.submit, -#nav_register a, .entity_remote_subscribe { color:#fff; } @@ -100,12 +95,6 @@ cursor:wait; text-indent:-9999px; } -#nav_register a { -text-decoration:none; -font-weight:bold; -padding:2px 4px; -} - #content, #site_nav_local_views a, #aside_primary { @@ -124,10 +113,10 @@ background-color:rgba(255, 255, 255, 0.7); } -#page_notice .error { +.error { background-color:#F7E8E8; } -#page_notice .success { +.success { background-color:#EFF3DC; } diff --git a/tpl/index.php b/tpl/index.php new file mode 100644 index 000000000..5f1ed8439 --- /dev/null +++ b/tpl/index.php @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html +PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> + <head> + <title><?php echo section('title'); ?></title> + <?php echo section('styles'); ?> + <?php echo section('scripts'); ?> + <?php echo section('search'); ?> + <?php echo section('feeds'); ?> + <?php echo section('description'); ?> + <?php echo section('head'); ?> + </head> + <body id="<?php echo section('action'); ?>"> + <div id="wrap"> + <div id="header"> + <?php echo section('logo'); ?> + <?php echo section('nav'); ?> + <?php echo section('notice'); ?> + <?php echo section('noticeform'); ?> + </div> + <div id="core"> + <?php echo section('localnav'); ?> + <?php echo section('bodytext'); ?> + <div id="aside_primary" class="aside"> + <?php echo section('export'); ?> + <?php echo section('subscriptions'); ?> + <?php echo section('subscribers'); ?> + <?php echo section('groups'); ?> + <?php echo section('statistics'); ?> + <?php echo section('cloud'); ?> + <?php echo section('groupmembers'); ?> + <?php echo section('groupstatistics'); ?> + <?php echo section('groupcloud'); ?> + <?php echo section('popular'); ?> + <?php echo section('groupsbyposts'); ?> + <?php echo section('featuredusers'); ?> + <?php echo section('groupsbymembers'); ?> + </div> + </div> + <div id="footer"> + <?php echo section('secondarynav'); ?> + <?php echo section('licenses'); ?> + </div> + </div> + </body> + </html>
\ No newline at end of file |