diff --git a/.gitignore b/.gitignore
index f5a3e0212f..83a53dfa3f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,15 @@
avatar/*
files/*
_darcs/*
+logs/*
config.php
.htaccess
+httpd.conf
*.tmproj
dataobject.ini
*~
*.bak
*.orig
*.rej
+.#*
+*.swp
diff --git a/EVENTS.txt b/EVENTS.txt
index 4b8260b3ce..37e2203d50 100644
--- a/EVENTS.txt
+++ b/EVENTS.txt
@@ -15,6 +15,24 @@ StartSecondaryNav: Showing the secondary nav menu
EndSecondaryNav: At the end of the secondary nav menu
- $action: the current action
+StartShowStyles: Showing Style links; good place to add UA style resets
+- $action: the current action
+
+EndShowStyles: End showing Style links; good place to add custom styles
+- $action: the current action
+
+StartShowLaconicaStyles: Showing Laconica Style links
+- $action: the current action
+
+EndShowLaconicaStyles: End showing Laconica Style links; good place to add handheld or JavaScript dependant styles
+- $action: the current action
+
+StartShowUAStyles: Showing custom UA Style links
+- $action: the current action
+
+EndShowUAStyles: End showing custom UA Style links; good place to add user-agent (e.g., filter, -webkit, -moz) specific styles
+- $action: the current action
+
StartShowScripts: Showing JavaScript links
- $action: the current action
@@ -34,3 +52,39 @@ StartShowLaconicaScripts: Showing Laconica script links (use this to link to a C
EndShowLaconicaScripts: End showing Laconica script links
- $action: the current action
+StartShowSections: Start the list of sections in the sidebar
+- $action: the current action
+
+EndShowSections: End the list of sections in the sidebar
+- $action: the current action
+
+StartShowHeader: Showing before the header container
+- $action: the current action
+
+EndShowHeader: Showing after the header container
+- $action: the current action
+
+StartShowFooter: Showing before the footer container
+- $action: the current action
+
+EndShowFooter: Showing after the footer container
+- $action: the current action
+
+StartShowContentBlock: Showing before the content container
+- $action: the current action
+
+EndShowContentBlock: Showing after the content container
+- $action: the current action
+
+StartNoticeSave: before inserting a notice (good place for content filters)
+- $notice: notice being saved (no ID or URI)
+
+EndNoticeSave: after inserting a notice and related code
+- $notice: notice that was saved (with ID and URI)
+
+StartShowLocalNavBlock: Showing the local nav menu
+- $action: the current action
+
+EndShowLocalNavBlock: At the end of the local nav menu
+- $action: the current action
+
diff --git a/README b/README
index 2c9ae84d93..9534c74c19 100644
--- a/README
+++ b/README
@@ -511,7 +511,7 @@ server is probably a good idea for high-volume sites.
needs as a parameter the install path; if you run it from the
Laconica dir, "." should suffice.
-This will run six (for now) queue handlers:
+This will run eight (for now) queue handlers:
* xmppdaemon.php - listens for new XMPP messages from users and stores
them as notices in the database.
@@ -525,6 +525,10 @@ This will run six (for now) queue handlers:
of registered users.
* xmppconfirmhandler.php - sends confirmation messages to registered
users.
+* twitterqueuehandler.php - sends queued notices to Twitter for user
+ who have opted to set up Twitter bridging.
+* facebookqueuehandler.php - sends queued notices to Facebook for users
+ of the built-in Facebook application.
Note that these queue daemons are pretty raw, and need your care. In
particular, they leak memory, and you may want to restart them on a
@@ -557,6 +561,53 @@ Sample cron job:
# Update Twitter friends subscriptions every half hour
0,30 * * * * /path/to/php /path/to/laconica/scripts/synctwitterfriends.php>&/dev/null
+Built-in Facebook Application
+-----------------------------
+
+Laconica's Facebook application allows your users to automatically
+update their Facebook statuses with their latest notices, invite
+their friends to use the app (and thus your site), view their notice
+timelines, and post notices -- all from within Facebook. The application
+is built into Laconica and runs on your host. For automatic Facebook
+status updating to work you will need to enable queuing and run the
+facebookqueuehandler.php daemon (see the "Queues and daemons" section
+above).
+
+Quick setup instructions*:
+
+Install the Facebook Developer application on Facebook:
+
+ http://www.facebook.com/developers/
+
+Use it to create a new application and generate an API key and secret.
+Uncomment the Facebook app section of your config.php and copy in the
+key and secret, e.g.:
+
+ # Config section for the built-in Facebook application
+ $config['facebook']['apikey'] = 'APIKEY';
+ $config['facebook']['secret'] = 'SECRET';
+
+In Facebook's application editor, specify the following URLs for your app:
+
+- Callback URL: http://example.net/mublog/facebook/
+- Post-Remove URL: http://example.net/mublog/facebook/remove
+- Post-Add Redirect URL: http://apps.facebook.com/yourapp/
+- Canvas URL: http://apps.facebook.com/yourapp/
+
+(Replace 'example.net' with your host's URL, 'mublog' with the path
+to your Laconica installation, and 'yourapp' with the name of the
+Facebook application you created.)
+
+Additionally, Choose "Web" for Application type in the Advanced tab.
+In the "Canvas setting" section, choose the "FBML" for Render Method,
+"Smart Size" for IFrame size, and "Full width (760px)" for Canvas Width.
+Everything else can be left with default values.
+
+*For more detailed instructions please see the installation guide on the
+Laconica wiki:
+
+ http://laconi.ca/trac/wiki/FacebookApplication
+
Sitemaps
--------
diff --git a/actions/all.php b/actions/all.php
index d75d1b9461..08dcccbddb 100644
--- a/actions/all.php
+++ b/actions/all.php
@@ -42,9 +42,9 @@ class AllAction extends Action
if (!$this->page) {
$this->page = 1;
}
-
+
common_set_returnto($this->selfUrl());
-
+
return true;
}
@@ -69,13 +69,22 @@ class AllAction extends Action
}
}
- function showFeeds()
+ function getFeeds()
{
- $this->element('link', array('rel' => 'alternate',
- 'href' => common_local_url('allrss', array('nickname' =>
- $this->user->nickname)),
- 'type' => 'application/rss+xml',
- 'title' => sprintf(_('Feed for friends of %s'), $this->user->nickname)));
+ return array(new Feed(Feed::RSS1,
+ common_local_url('allrss', array('nickname' =>
+ $this->user->nickname)),
+ sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->user->nickname)),
+ new Feed(Feed::RSS2,
+ common_local_url('api', array('apiaction' => 'statuses',
+ 'method' => 'friends',
+ 'argument' => $this->user->nickname.'.rss')),
+ sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->user->nickname)),
+ new Feed(Feed::ATOM,
+ common_local_url('api', array('apiaction' => 'statuses',
+ 'method' => 'friends',
+ 'argument' => $this->user->nickname.'.atom')),
+ sprintf(_('Feed for friends of %s (Atom)'), $this->user->nickname)));
}
function showLocalNav()
@@ -84,15 +93,6 @@ class AllAction extends Action
$nav->show();
}
- function showExportData()
- {
- $fl = new FeedList($this);
- $fl->show(array(0=>array('href'=>common_local_url('allrss', array('nickname' => $this->user->nickname)),
- 'type' => 'rss',
- 'version' => 'RSS 1.0',
- 'item' => 'allrss')));
- }
-
function showContent()
{
$notice = $this->user->noticesWithFriends(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
@@ -110,7 +110,7 @@ class AllAction extends Action
$user =& common_current_user();
if ($user && ($user->id == $this->user->id)) {
$this->element('h1', NULL, _("You and friends"));
- } else {
+ } else {
$this->element('h1', NULL, sprintf(_('%s and friends'), $this->user->nickname));
}
}
diff --git a/actions/allrss.php b/actions/allrss.php
index 05787f3f73..0114c43962 100644
--- a/actions/allrss.php
+++ b/actions/allrss.php
@@ -53,7 +53,9 @@ class AllrssAction extends Rss10Action
/**
* Initialization.
- *
+ *
+ * @param array $args Web and URL arguments
+ *
* @return boolean false if user doesn't exist
*/
function prepare($args)
@@ -81,7 +83,7 @@ class AllrssAction extends Rss10Action
{
$user = $this->user;
$notice = $user->noticesWithFriends(0, $limit);
-
+
while ($notice->fetch()) {
$notices[] = clone($notice);
}
@@ -104,7 +106,8 @@ class AllrssAction extends Rss10Action
'link' => common_local_url('all',
array('nickname' =>
$user->nickname)),
- 'description' => sprintf(_('Feed for friends of %s'), $user->nickname));
+ 'description' => sprintf(_('Feed for friends of %s'),
+ $user->nickname));
return $c;
}
@@ -123,10 +126,5 @@ class AllrssAction extends Rss10Action
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
return $avatar ? $avatar->url : null;
}
-
- function isReadOnly()
- {
- return true;
- }
}
diff --git a/actions/api.php b/actions/api.php
index 21fe4eea32..a27d244929 100644
--- a/actions/api.php
+++ b/actions/api.php
@@ -131,13 +131,13 @@ class ApiAction extends Action
'statuses/followers',
'favorites/favorites');
- # If the site is "private", all API methods need authentication
-
- if (common_config('site', 'private')) {
- return true;
- }
-
$fullname = "$this->api_action/$this->api_method";
+
+ // If the site is "private", all API methods except laconica/config
+ // need authentication
+ if (common_config('site', 'private')) {
+ return $fullname != 'laconica/config' || false;
+ }
if (in_array($fullname, $bareauth)) {
# bareauth: only needs auth if without an argument
diff --git a/actions/avatarsettings.php b/actions/avatarsettings.php
index 7dd53f6eb5..f38a44a24a 100644
--- a/actions/avatarsettings.php
+++ b/actions/avatarsettings.php
@@ -145,6 +145,7 @@ class AvatarsettingsAction extends AccountSettingsAction
'height' => AVATAR_PROFILE_SIZE,
'alt' => $user->nickname));
$this->elementEnd('div');
+ $this->submit('delete', _('Delete'));
$this->elementEnd('li');
}
@@ -256,6 +257,8 @@ class AvatarsettingsAction extends AccountSettingsAction
$this->uploadAvatar();
} else if ($this->arg('crop')) {
$this->cropAvatar();
+ } else if ($this->arg('delete')) {
+ $this->deleteAvatar();
} else {
$this->showForm(_('Unexpected form submission.'));
}
@@ -344,6 +347,29 @@ class AvatarsettingsAction extends AccountSettingsAction
$this->showForm(_('Failed updating avatar.'));
}
}
+
+ /**
+ * Get rid of the current avatar.
+ *
+ * @return void
+ */
+
+ function deleteAvatar()
+ {
+ $user = common_current_user();
+ $profile = $user->getProfile();
+
+ $avatar = $profile->getOriginalAvatar();
+ $avatar->delete();
+ $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
+ $avatar->delete();
+ $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE);
+ $avatar->delete();
+ $avatar = $profile->getAvatar(AVATAR_MINI_SIZE);
+ $avatar->delete();
+
+ $this->showForm(_('Avatar deleted.'), true);
+ }
/**
* Add the jCrop stylesheet
diff --git a/actions/doc.php b/actions/doc.php
index 6957659add..ebffb7c154 100644
--- a/actions/doc.php
+++ b/actions/doc.php
@@ -50,7 +50,7 @@ class DocAction extends Action
/**
* Class handler.
- *
+ *
* @param array $args array of arguments
*
* @return nothing
@@ -59,7 +59,7 @@ class DocAction extends Action
{
parent::handle($args);
$this->title = $this->trimmed('title');
- $this->filename = INSTALLDIR.'/doc/'.$this->title;
+ $this->filename = INSTALLDIR.'/doc-src/'.$this->title;
if (!file_exists($this->filename)) {
$this->clientError(_('No such document.'));
return;
@@ -71,14 +71,14 @@ class DocAction extends Action
function showPageTitle() {
$this->element('h1', array('class' => 'entry-title'), $this->title());
}
-
+
// overrided to add hentry, and content-inner classes
function showContentBlock()
{
$this->elementStart('div', array('id' => 'content', 'class' => 'hentry'));
$this->showPageTitle();
$this->showPageNoticeBlock();
- $this->elementStart('div', array('id' => 'content_inner',
+ $this->elementStart('div', array('id' => 'content_inner',
'class' => 'entry-content'));
// show the actual content (forms, lists, whatever)
$this->showContent();
@@ -88,7 +88,7 @@ class DocAction extends Action
/**
* Display content.
- *
+ *
* @return nothing
*/
function showContent()
@@ -100,7 +100,7 @@ class DocAction extends Action
/**
* Page title.
- *
+ *
* @return page title
*/
function title()
diff --git a/actions/emailsettings.php b/actions/emailsettings.php
index b84acb2141..634388fddd 100644
--- a/actions/emailsettings.php
+++ b/actions/emailsettings.php
@@ -164,6 +164,11 @@ class EmailsettingsAction extends AccountSettingsAction
$user->emailnotifymsg);
$this->elementEnd('li');
$this->elementStart('li');
+ $this->checkbox('emailnotifyattn',
+ _('Send me email when someone sends me an "@-reply".'),
+ $user->emailnotifyattn);
+ $this->elementEnd('li');
+ $this->elementStart('li');
$this->checkbox('emailnotifynudge',
_('Allow friends to nudge me and send me an email.'),
$user->emailnotifynudge);
@@ -255,6 +260,7 @@ class EmailsettingsAction extends AccountSettingsAction
$emailnotifyfav = $this->boolean('emailnotifyfav');
$emailnotifymsg = $this->boolean('emailnotifymsg');
$emailnotifynudge = $this->boolean('emailnotifynudge');
+ $emailnotifyattn = $this->boolean('emailnotifyattn');
$emailmicroid = $this->boolean('emailmicroid');
$emailpost = $this->boolean('emailpost');
@@ -270,6 +276,7 @@ class EmailsettingsAction extends AccountSettingsAction
$user->emailnotifyfav = $emailnotifyfav;
$user->emailnotifymsg = $emailnotifymsg;
$user->emailnotifynudge = $emailnotifynudge;
+ $user->emailnotifyattn = $emailnotifyattn;
$user->emailmicroid = $emailmicroid;
$user->emailpost = $emailpost;
diff --git a/actions/finishopenidlogin.php b/actions/finishopenidlogin.php
index 1e7b73a7f3..6d92cb9aae 100644
--- a/actions/finishopenidlogin.php
+++ b/actions/finishopenidlogin.php
@@ -83,7 +83,7 @@ class FinishopenidloginAction extends Action
function showContent()
{
- if ($this->message_text) {
+ if (!empty($this->message_text)) {
$this->element('p', null, $this->message);
return;
}
@@ -232,7 +232,8 @@ class FinishopenidloginAction extends Action
return;
}
- if ($sreg['country']) {
+ $location = '';
+ if (!empty($sreg['country'])) {
if ($sreg['postcode']) {
# XXX: use postcode to get city and region
# XXX: also, store postcode somewhere -- it's valuable!
@@ -242,12 +243,16 @@ class FinishopenidloginAction extends Action
}
}
- if ($sreg['fullname'] && mb_strlen($sreg['fullname']) <= 255) {
+ if (!empty($sreg['fullname']) && mb_strlen($sreg['fullname']) <= 255) {
$fullname = $sreg['fullname'];
+ } else {
+ $fullname = '';
}
- if ($sreg['email'] && Validate::email($sreg['email'], true)) {
+ if (!empty($sreg['email']) && Validate::email($sreg['email'], true)) {
$email = $sreg['email'];
+ } else {
+ $email = '';
}
# XXX: add language
@@ -328,7 +333,7 @@ class FinishopenidloginAction extends Action
# Try the passed-in nickname
- if ($sreg['nickname']) {
+ if (!empty($sreg['nickname'])) {
$nickname = $this->nicknamize($sreg['nickname']);
if ($this->isNewNickname($nickname)) {
return $nickname;
@@ -337,7 +342,7 @@ class FinishopenidloginAction extends Action
# Try the full name
- if ($sreg['fullname']) {
+ if (!empty($sreg['fullname'])) {
$fullname = $this->nicknamize($sreg['fullname']);
if ($this->isNewNickname($fullname)) {
return $fullname;
diff --git a/actions/finishremotesubscribe.php b/actions/finishremotesubscribe.php
index f9094a50ca..76db887deb 100644
--- a/actions/finishremotesubscribe.php
+++ b/actions/finishremotesubscribe.php
@@ -237,7 +237,13 @@ class FinishremotesubscribeAction extends Action
{
$temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar');
copy($url, $temp_filename);
- return $profile->setOriginal($temp_filename);
+ $imagefile = new ImageFile($profile->id, $temp_filename);
+ $filename = Avatar::filename($profile->id,
+ image_type_to_extension($imagefile->type),
+ null,
+ common_timestamp());
+ rename($temp_filename, Avatar::path($filename));
+ return $profile->setOriginal($filename);
}
function access_token($omb)
diff --git a/actions/grouprss.php b/actions/grouprss.php
index 1a7b858b1e..de76a59600 100644
--- a/actions/grouprss.php
+++ b/actions/grouprss.php
@@ -111,13 +111,13 @@ class groupRssAction extends Rss10Action
{
$group = $this->group;
-
+
if (is_null($group)) {
return null;
}
-
+
$notice = $group->getNotices(0, ($limit == 0) ? NOTICES_PER_PAGE : $limit);
-
+
while ($notice->fetch()) {
$notices[] = clone($notice);
}
@@ -141,13 +141,4 @@ class groupRssAction extends Rss10Action
{
return $this->group->homepage_logo;
}
-
- # override parent to add X-SUP-ID URL
-
- function initRss($limit=0)
- {
- $url = common_local_url('sup', null, $this->group->id);
- header('X-SUP-ID: '.$url);
- parent::initRss($limit);
- }
}
diff --git a/actions/login.php b/actions/login.php
index 71e4679292..b049791fb1 100644
--- a/actions/login.php
+++ b/actions/login.php
@@ -108,13 +108,15 @@ class LoginAction extends Action
$nickname = common_canonical_nickname($this->trimmed('nickname'));
$password = $this->arg('password');
- if (!common_check_user($nickname, $password)) {
+ $user = common_check_user($nickname, $password);
+
+ if (!$user) {
$this->showForm(_('Incorrect username or password.'));
return;
}
// success!
- if (!common_set_user($nickname)) {
+ if (!common_set_user($user)) {
$this->serverError(_('Error setting user.'));
return;
}
diff --git a/actions/newmessage.php b/actions/newmessage.php
index f83015a372..82276ff341 100644
--- a/actions/newmessage.php
+++ b/actions/newmessage.php
@@ -201,7 +201,7 @@ class NewmessageAction extends Action
function showNoticeForm()
{
- $message_form = new MessageForm($this, $this->to, $this->content);
+ $message_form = new MessageForm($this, $this->other, $this->content);
$message_form->show();
}
}
diff --git a/actions/newnotice.php b/actions/newnotice.php
index 5e7691f33d..9face96443 100644
--- a/actions/newnotice.php
+++ b/actions/newnotice.php
@@ -98,7 +98,12 @@ class NewnoticeAction extends Action
return;
}
- $this->saveNewNotice();
+ try {
+ $this->saveNewNotice();
+ } catch (Exception $e) {
+ $this->showForm($e->getMessage());
+ return;
+ }
} else {
$this->showForm();
}
@@ -123,15 +128,13 @@ class NewnoticeAction extends Action
$content = $this->trimmed('status_textarea');
if (!$content) {
- $this->showForm(_('No content!'));
- return;
+ $this->clientError(_('No content!'));
} else {
$content_shortened = common_shorten_links($content);
if (mb_strlen($content_shortened) > 140) {
- $this->showForm(_('That\'s too long. '.
- 'Max notice size is 140 chars.'));
- return;
+ $this->clientError(_('That\'s too long. '.
+ 'Max notice size is 140 chars.'));
}
}
@@ -154,7 +157,7 @@ class NewnoticeAction extends Action
($replyto == 'false') ? null : $replyto);
if (is_string($notice)) {
- $this->showForm($notice);
+ $this->clientError($notice);
return;
}
diff --git a/actions/noticesearch.php b/actions/noticesearch.php
index a5f01350c4..dc58d7528a 100644
--- a/actions/noticesearch.php
+++ b/actions/noticesearch.php
@@ -57,11 +57,11 @@ class NoticesearchAction extends SearchAction
return true;
}
-
+
/**
* Get instructions
- *
- * @return string instruction text
+ *
+ * @return string instruction text
*/
function getInstructions()
{
@@ -70,7 +70,7 @@ class NoticesearchAction extends SearchAction
/**
* Get title
- *
+ *
* @return string title
*/
function title()
@@ -78,6 +78,20 @@ class NoticesearchAction extends SearchAction
return _('Text search');
}
+ function getFeeds()
+ {
+ $q = $this->trimmed('q');
+
+ if (!$q) {
+ return null;
+ }
+
+ return array(new Feed(Feed::RSS1, common_local_url('noticesearchrss',
+ array('q' => $q)),
+ sprintf(_('Search results for "%s" on %s'),
+ $q, common_config('site', 'name'))));
+ }
+
/**
* Show results
*
@@ -119,26 +133,6 @@ class NoticesearchAction extends SearchAction
$page, 'noticesearch', array('q' => $q));
}
- /**
- * Show header
- *
- * @param array $arr array containing the query
- *
- * @return void
- */
-
- function extraHead()
- {
- $q = $this->trimmed('q');
- if ($q) {
- $this->element('link', array('rel' => 'alternate',
- 'href' => common_local_url('noticesearchrss',
- array('q' => $q)),
- 'type' => 'application/rss+xml',
- 'title' => _('Search Stream Feed')));
- }
- }
-
/**
* Show notice
*
diff --git a/actions/peoplesearch.php b/actions/peoplesearch.php
index 615201c461..14177fcf0d 100644
--- a/actions/peoplesearch.php
+++ b/actions/peoplesearch.php
@@ -86,33 +86,9 @@ class PeoplesearchAction extends SearchAction
}
$profile->free();
-
+
$this->pagination($page > 1, $cnt > PROFILES_PER_PAGE,
$page, 'peoplesearch', array('q' => $q));
}
}
-class PeopleSearchResults extends ProfileList
-{
- var $terms = null;
- var $pattern = null;
-
- function __construct($profile, $terms, $action)
- {
- parent::__construct($profile, $terms, $action);
- $this->terms = array_map('preg_quote',
- array_map('htmlspecialchars', $terms));
- $this->pattern = '/('.implode('|',$terms).')/i';
- }
-
- function highlight($text)
- {
- return preg_replace($this->pattern, '\\1', htmlspecialchars($text));
- }
-
- function isReadOnly()
- {
- return true;
- }
-}
-
diff --git a/actions/public.php b/actions/public.php
index cc6537f74f..a20ae40321 100644
--- a/actions/public.php
+++ b/actions/public.php
@@ -73,9 +73,9 @@ class PublicAction extends Action
{
parent::prepare($args);
$this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
-
+
common_set_returnto($this->selfUrl());
-
+
return true;
}
@@ -119,12 +119,20 @@ class PublicAction extends Action
* @return void
*/
- function showFeeds()
+ function getFeeds()
{
- $this->element('link', array('rel' => 'alternate',
- 'href' => common_local_url('publicrss'),
- 'type' => 'application/rss+xml',
- 'title' => _('Public Stream Feed')));
+ return array(new Feed(Feed::RSS1, common_local_url('publicrss'),
+ _('Public Stream Feed (RSS 1.0)')),
+ new Feed(Feed::RSS2,
+ common_local_url('api',
+ array('apiaction' => 'statuses',
+ 'method' => 'public_timeline.rss')),
+ _('Public Stream Feed (RSS 2.0)')),
+ new Feed(Feed::ATOM,
+ common_local_url('api',
+ array('apiaction' => 'statuses',
+ 'method' => 'public_timeline.atom')),
+ _('Public Stream Feed (Atom)')));
}
/**
@@ -185,27 +193,6 @@ class PublicAction extends Action
$this->page, 'public');
}
- /**
- * Makes a list of exported feeds for this page
- *
- * @return void
- *
- * @todo I18N
- */
-
- function showExportData()
- {
- $fl = new FeedList($this);
- $fl->show(array(0 => array('href' => common_local_url('publicrss'),
- 'type' => 'rss',
- 'version' => 'RSS 1.0',
- 'item' => 'publicrss'),
- 1 => array('href' => common_local_url('publicatom'),
- 'type' => 'atom',
- 'version' => 'Atom 1.0',
- 'item' => 'publicatom')));
- }
-
function showSections()
{
// $top = new TopPostersSection($this);
diff --git a/actions/replies.php b/actions/replies.php
index 7eff74a669..4ab9b14ed2 100644
--- a/actions/replies.php
+++ b/actions/replies.php
@@ -84,7 +84,7 @@ class RepliesAction extends Action
$this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
common_set_returnto($this->selfUrl());
-
+
return true;
}
@@ -129,16 +129,13 @@ class RepliesAction extends Action
* @return void
*/
- function showFeeds()
+ function getFeeds()
{
$rssurl = common_local_url('repliesrss',
array('nickname' => $this->user->nickname));
$rsstitle = sprintf(_('Feed for replies to %s'), $this->user->nickname);
- $this->element('link', array('rel' => 'alternate',
- 'href' => $rssurl,
- 'type' => 'application/rss+xml',
- 'title' => $rsstitle));
+ return array(new Feed(Feed::RSS1, $rssurl, $rsstitle));
}
/**
@@ -153,25 +150,6 @@ class RepliesAction extends Action
$nav->show();
}
- /**
- * Show the replies feed links
- *
- * @return void
- */
-
- function showExportData()
- {
- $fl = new FeedList($this);
-
- $rssurl = common_local_url('repliesrss',
- array('nickname' => $this->user->nickname));
-
- $fl->show(array(0=>array('href'=> $rssurl,
- 'type' => 'rss',
- 'version' => 'RSS 1.0',
- 'item' => 'repliesrss')));
- }
-
/**
* Show the content
*
diff --git a/actions/showfavorites.php b/actions/showfavorites.php
index 31479e1a78..d1c9283f0f 100644
--- a/actions/showfavorites.php
+++ b/actions/showfavorites.php
@@ -113,7 +113,7 @@ class ShowfavoritesAction extends Action
}
common_set_returnto($this->selfUrl());
-
+
return true;
}
@@ -136,10 +136,10 @@ class ShowfavoritesAction extends Action
/**
* Feeds for the
section
*
- * @return void
+ * @return array Feed objects to show
*/
- function showFeeds()
+ function getFeeds()
{
$feedurl = common_local_url('favoritesrss',
array('nickname' =>
@@ -147,10 +147,7 @@ class ShowfavoritesAction extends Action
$feedtitle = sprintf(_('Feed for favorites of %s'),
$this->user->nickname);
- $this->element('link', array('rel' => 'alternate',
- 'href' => $feedurl,
- 'type' => 'application/rss+xml',
- 'title' => $feedtitle));
+ return array(new Feed(Feed::RSS1, $feedurl, $feedtitle));
}
/**
@@ -165,28 +162,6 @@ class ShowfavoritesAction extends Action
$nav->show();
}
- /**
- * Show the replies feed links
- *
- * @return void
- */
-
- function showExportData()
- {
- $feedurl = common_local_url('favoritesrss',
- array('nickname' =>
- $this->user->nickname));
-
- $fl = new FeedList($this);
-
- // XXX: I18N
-
- $fl->show(array(0=>array('href'=> $feedurl,
- 'type' => 'rss',
- 'version' => 'RSS 1.0',
- 'item' => 'Favorites')));
- }
-
/**
* Show the content
*
diff --git a/actions/showgroup.php b/actions/showgroup.php
index 7bc68fbc64..c20941a35e 100644
--- a/actions/showgroup.php
+++ b/actions/showgroup.php
@@ -244,7 +244,7 @@ class ShowgroupAction extends Action
if ($this->group->location) {
$this->elementStart('dl', 'entity_location');
$this->element('dt', null, _('Location'));
- $this->element('dd', 'location', $this->group->location);
+ $this->element('dd', 'label', $this->group->location);
$this->elementEnd('dl');
}
@@ -292,37 +292,18 @@ class ShowgroupAction extends Action
}
/**
- * Show a list of links to feeds this page produces
+ * Get a list of the feeds for this page
*
* @return void
*/
- function showExportData()
- {
- $fl = new FeedList($this);
- $fl->show(array(0=>array('href'=>common_local_url('grouprss',
- array('nickname' => $this->group->nickname)),
- 'type' => 'rss',
- 'version' => 'RSS 1.0',
- 'item' => 'notices')));
- }
-
- /**
- * Show a list of links to feeds this page produces
- *
- * @return void
- */
-
- function showFeeds()
+ function getFeeds()
{
$url =
common_local_url('grouprss',
array('nickname' => $this->group->nickname));
- $this->element('link', array('rel' => 'alternate',
- 'href' => $url,
- 'type' => 'application/rss+xml',
- 'title' => sprintf(_('Notice feed for %s group'),
+ return array(new Feed(Feed::RSS1, $url, sprintf(_('Notice feed for %s group'),
$this->group->nickname)));
}
diff --git a/actions/showstream.php b/actions/showstream.php
index 28bb8453f8..65482167e1 100644
--- a/actions/showstream.php
+++ b/actions/showstream.php
@@ -111,7 +111,7 @@ class ShowstreamAction extends Action
$this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
common_set_returnto($this->selfUrl());
-
+
return true;
}
@@ -155,58 +155,39 @@ class ShowstreamAction extends Action
return;
}
- function showExportData()
+ function getFeeds()
{
- $fl = new FeedList($this);
- $fl->show(array(0=>array('href'=>common_local_url('userrss',
- array('nickname' => $this->user->nickname)),
- 'type' => 'rss',
- 'version' => 'RSS 1.0',
- 'item' => 'notices'),
- 1=>array('href'=>common_local_url('usertimeline',
- array('nickname' => $this->user->nickname)),
- 'type' => 'atom',
- 'version' => 'Atom 1.0',
- 'item' => 'usertimeline'),
- 2=>array('href'=>common_local_url('foaf',
- array('nickname' => $this->user->nickname)),
- 'type' => 'rdf',
- 'version' => 'FOAF',
- 'item' => 'foaf')));
- }
-
- function showFeeds()
- {
- $this->element('link', array('rel' => 'alternate',
- 'type' => 'application/rss+xml',
- 'href' => common_local_url('userrss',
- array('nickname' => $this->user->nickname)),
- 'title' => sprintf(_('Notice feed for %s (RSS)'),
- $this->user->nickname)));
-
- $this->element('link',
- array('rel' => 'alternate',
- 'href' => common_local_url('api',
- array('apiaction' => 'statuses',
- 'method' => 'user_timeline.atom',
- 'argument' => $this->user->nickname)),
- 'type' => 'application/atom+xml',
- 'title' => sprintf(_('Notice feed for %s (Atom)'),
- $this->user->nickname)));
+ return array(new Feed(Feed::RSS1,
+ common_local_url('userrss',
+ array('nickname' => $this->user->nickname)),
+ sprintf(_('Notice feed for %s (RSS 1.0)'),
+ $this->user->nickname)),
+ new Feed(Feed::RSS2,
+ common_local_url('api',
+ array('apiaction' => 'statuses',
+ 'method' => 'user_timeline',
+ 'argument' => $this->user->nickname.'.rss')),
+ sprintf(_('Notice feed for %s (RSS 2.0)'),
+ $this->user->nickname)),
+ new Feed(Feed::ATOM,
+ common_local_url('api',
+ array('apiaction' => 'statuses',
+ 'method' => 'user_timeline',
+ 'argument' => $this->user->nickname.'.atom')),
+ sprintf(_('Notice feed for %s (Atom)'),
+ $this->user->nickname)),
+ new Feed(Feed::FOAF,
+ common_local_url('foaf', array('nickname' =>
+ $this->user->nickname)),
+ sprintf(_('FOAF for %s'), $this->user->nickname)));
}
function extraHead()
{
- // FOAF
- $this->element('link', array('rel' => 'meta',
- 'href' => common_local_url('foaf', array('nickname' =>
- $this->user->nickname)),
- 'type' => 'application/rdf+xml',
- 'title' => 'FOAF'));
// for remote subscriptions etc.
$this->element('meta', array('http-equiv' => 'X-XRDS-Location',
'content' => common_local_url('xrds', array('nickname' =>
- $this->user->nickname))));
+ $this->user->nickname))));
if ($this->profile->bio) {
$this->element('meta', array('name' => 'description',
@@ -248,6 +229,15 @@ class ShowstreamAction extends Action
'height' => AVATAR_PROFILE_SIZE,
'alt' => $this->profile->nickname));
$this->elementEnd('dd');
+
+ $user = User::staticGet('id', $this->profile->id);
+ $cur = common_current_user();
+ if ($cur && $cur->id == $user->id) {
+ $this->elementStart('dd');
+ $this->element('a', array('href' => common_local_url('avatarsettings')), _('Edit Avatar'));
+ $this->elementEnd('dd');
+ }
+
$this->elementEnd('dl');
$this->elementStart('dl', 'entity_nickname');
@@ -256,7 +246,7 @@ class ShowstreamAction extends Action
$hasFN = ($this->profile->fullname) ? 'nickname url uid' : 'fn nickname url uid';
$this->element('a', array('href' => $this->profile->profileurl,
'rel' => 'me', 'class' => $hasFN),
- $this->profile->nickname);
+ $this->profile->nickname);
$this->elementEnd('dd');
$this->elementEnd('dl');
@@ -272,7 +262,7 @@ class ShowstreamAction extends Action
if ($this->profile->location) {
$this->elementStart('dl', 'entity_location');
$this->element('dt', null, _('Location'));
- $this->element('dd', 'location', $this->profile->location);
+ $this->element('dd', 'label', $this->profile->location);
$this->elementEnd('dl');
}
@@ -302,11 +292,11 @@ class ShowstreamAction extends Action
$this->elementStart('ul', 'tags xoxo');
foreach ($tags as $tag) {
$this->elementStart('li');
- $this->element('span', 'mark_hash', '#');
- $this->element('a', array('rel' => 'tag',
- 'href' => common_local_url('peopletag',
- array('tag' => $tag))),
- $tag);
+ // Avoid space by using raw output.
+ $pt = '#' . $tag . '';
+ $this->raw($pt);
$this->elementEnd('li');
}
$this->elementEnd('ul');
@@ -324,7 +314,7 @@ class ShowstreamAction extends Action
$this->elementStart('li', 'entity_edit');
$this->element('a', array('href' => common_local_url('profilesettings'),
'title' => _('Edit profile settings')),
- _('Edit'));
+ _('Edit'));
$this->elementEnd('li');
}
@@ -346,9 +336,8 @@ class ShowstreamAction extends Action
$this->elementEnd('li');
}
- $user = User::staticGet('id', $this->profile->id);
if ($cur && $cur->id != $user->id && $cur->mutuallySubscribed($user)) {
- $this->elementStart('li', 'entity_send-a-message');
+ $this->elementStart('li', 'entity_send-a-message');
$this->element('a', array('href' => common_local_url('newmessage', array('to' => $user->id)),
'title' => _('Send a direct message to this user')),
_('Message'));
@@ -490,7 +479,7 @@ class ShowstreamAction extends Action
$this->elementStart('dl', 'entity_member-since');
$this->element('dt', null, _('Member since'));
$this->element('dd', null, date('j M Y',
- strtotime($this->profile->created)));
+ strtotime($this->profile->created)));
$this->elementEnd('dl');
$this->elementStart('dl', 'entity_subscriptions');
diff --git a/actions/tag.php b/actions/tag.php
index 4401f892a9..231f2c2992 100644
--- a/actions/tag.php
+++ b/actions/tag.php
@@ -37,9 +37,9 @@ class TagAction extends Action
}
$this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
-
+
common_set_returnto($this->selfUrl());
-
+
return true;
}
@@ -61,12 +61,11 @@ class TagAction extends Action
$this->showPage();
}
- function showFeeds()
+ function getFeeds()
{
- $this->element('link', array('rel' => 'alternate',
- 'href' => common_local_url('tagrss', array('tag' => $this->tag)),
- 'type' => 'application/rss+xml',
- 'title' => sprintf(_('Feed for tag %s'), $this->tag)));
+ return array(new Feed(Feed::RSS1,
+ common_local_url('tagrss', array('tag' => $this->tag)),
+ sprintf(_('Feed for tag %s'), $this->tag)));
}
function showPageNotice()
@@ -74,15 +73,6 @@ class TagAction extends Action
return sprintf(_('Messages tagged "%s", most recent first'), $this->tag);
}
- function showExportData()
- {
- $fl = new FeedList($this);
- $fl->show(array(0=>array('href'=>common_local_url('tagrss', array('tag' => $this->tag)),
- 'type' => 'rss',
- 'version' => 'RSS 1.0',
- 'item' => 'tagrss')));
- }
-
function showContent()
{
$notice = Notice_tag::getStream($this->tag, (($this->page-1)*NOTICES_PER_PAGE), NOTICES_PER_PAGE + 1);
diff --git a/actions/tagother.php b/actions/tagother.php
index 3e8a12fd69..0d18945a09 100644
--- a/actions/tagother.php
+++ b/actions/tagother.php
@@ -110,7 +110,7 @@ class TagotherAction extends Action
if ($this->profile->location) {
$this->elementStart('dl', 'entity_location');
$this->element('dt', null, _('Location'));
- $this->element('dd', 'location', $this->profile->location);
+ $this->element('dd', 'label', $this->profile->location);
$this->elementEnd('dl');
}
if ($this->profile->homepage) {
@@ -135,7 +135,8 @@ class TagotherAction extends Action
'id' => 'form_tag_user',
'class' => 'form_settings',
'name' => 'tagother',
- 'action' => $this->selfUrl()));
+ 'action' => common_local_url('tagother', array('id' => $this->profile->id))));
+
$this->elementStart('fieldset');
$this->element('legend', null, _('Tag user'));
$this->hidden('token', common_session_token());
diff --git a/actions/twitapiaccount.php b/actions/twitapiaccount.php
index b7c09cc9dc..c19cd370d9 100644
--- a/actions/twitapiaccount.php
+++ b/actions/twitapiaccount.php
@@ -24,20 +24,19 @@ require_once(INSTALLDIR.'/lib/twitterapi.php');
class TwitapiaccountAction extends TwitterapiAction
{
- function verify_credentials($args, $apidata)
+ function verify_credentials($args, $apidata)
{
-
- if ($apidata['content-type'] == 'xml') {
- header('Content-Type: application/xml; charset=utf-8');
- print 'true';
- } elseif ($apidata['content-type'] == 'json') {
- header('Content-Type: application/json; charset=utf-8');
- print '{"authorized":true}';
- } else {
- common_user_error(_('API method not found!'), $code=404);
- }
-
- }
+ if ($apidata['content-type'] == 'xml') {
+ header('Content-Type: application/xml; charset=utf-8');
+ print 'true';
+ } elseif ($apidata['content-type'] == 'json') {
+ header('Content-Type: application/json; charset=utf-8');
+ print '{"authorized":true}';
+ } else {
+ header('Content-Type: text/html; charset=utf-8');
+ print 'Authorized';
+ }
+ }
function end_session($args, $apidata)
{
diff --git a/actions/twitapistatuses.php b/actions/twitapistatuses.php
index 18e24c0f58..216835026d 100644
--- a/actions/twitapistatuses.php
+++ b/actions/twitapistatuses.php
@@ -204,7 +204,7 @@ class TwitapistatusesAction extends TwitterapiAction
# FriendFeed's SUP protocol
# Also added RSS and Atom feeds
- $suplink = common_local_url('sup', null, $user->id);
+ $suplink = common_local_url('sup', null, null, $user->id);
header('X-SUP-ID: '.$suplink);
# XXX: since
@@ -470,19 +470,28 @@ class TwitapistatusesAction extends TwitterapiAction
return $this->subscriptions($apidata, 'subscribed', 'subscriber');
}
+ function friendsIDs($args, $apidata)
+ {
+ parent::handle($args);
+ return $this->subscriptions($apidata, 'subscribed', 'subscriber', true);
+ }
+
function followers($args, $apidata)
{
parent::handle($args);
-
return $this->subscriptions($apidata, 'subscriber', 'subscribed');
}
- function subscriptions($apidata, $other_attr, $user_attr)
+ function followersIDs($args, $apidata)
+ {
+ parent::handle($args);
+ return $this->subscriptions($apidata, 'subscriber', 'subscribed', true);
+ }
+
+ function subscriptions($apidata, $other_attr, $user_attr, $onlyIDs=false)
{
- # XXX: lite
-
- $this->auth_user = $apidate['user'];
+ $this->auth_user = $apidata['user'];
$user = $this->get_user($apidata['api_arg'], $apidata);
if (!$user) {
@@ -514,7 +523,10 @@ class TwitapistatusesAction extends TwitterapiAction
}
$sub->orderBy('created DESC');
- $sub->limit(($page-1)*100, 100);
+
+ if (!$onlyIDs) {
+ $sub->limit(($page-1)*100, 100);
+ }
$others = array();
@@ -529,7 +541,13 @@ class TwitapistatusesAction extends TwitterapiAction
$type = $apidata['content-type'];
$this->init_document($type);
- $this->show_profiles($others, $type);
+
+ if ($onlyIDs) {
+ $this->showIDs($others, $type);
+ } else {
+ $this->show_profiles($others, $type);
+ }
+
$this->end_document($type);
}
@@ -555,6 +573,28 @@ class TwitapistatusesAction extends TwitterapiAction
}
}
+ function showIDs($profiles, $type)
+ {
+ switch ($type) {
+ case 'xml':
+ $this->elementStart('ids');
+ foreach ($profiles as $profile) {
+ $this->element('id', null, $profile->id);
+ }
+ $this->elementEnd('ids');
+ break;
+ case 'json':
+ $ids = array();
+ foreach ($profiles as $profile) {
+ $ids[] = (int)$profile->id;
+ }
+ print json_encode($ids);
+ break;
+ default:
+ $this->clientError(_('unsupported file type'));
+ }
+ }
+
function featured($args, $apidata)
{
parent::handle($args);
diff --git a/actions/twittersettings.php b/actions/twittersettings.php
index 2d41469bba..a79859bbf0 100644
--- a/actions/twittersettings.php
+++ b/actions/twittersettings.php
@@ -32,6 +32,7 @@ if (!defined('LACONICA')) {
}
require_once INSTALLDIR.'/lib/connectsettingsaction.php';
+require_once INSTALLDIR.'/lib/twitter.php';
define('SUBSCRIPTIONS', 80);
@@ -90,7 +91,7 @@ class TwittersettingsAction extends ConnectSettingsAction
$fuser = null;
- $flink = Foreign_link::getByUserID($user->id, 1); // 1 == Twitter
+ $flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
if ($flink) {
$fuser = $flink->getForeignUser();
@@ -358,7 +359,7 @@ class TwittersettingsAction extends ConnectSettingsAction
$flink->user_id = $user->id;
$flink->foreign_id = $twit_user->id;
- $flink->service = 1; // Twitter
+ $flink->service = TWITTER_SERVICE;
$flink->credentials = $password;
$flink->created = common_sql_now();
diff --git a/actions/userauthorization.php b/actions/userauthorization.php
index 7455a41a6f..ed17ceec97 100644
--- a/actions/userauthorization.php
+++ b/actions/userauthorization.php
@@ -330,7 +330,13 @@ class UserauthorizationAction extends Action
{
$temp_filename = tempnam(sys_get_temp_dir(), 'listenee_avatar');
copy($url, $temp_filename);
- return $profile->setOriginal($temp_filename);
+ $imagefile = new ImageFile($profile->id, $temp_filename);
+ $filename = Avatar::filename($profile->id,
+ image_type_to_extension($imagefile->type),
+ null,
+ common_timestamp());
+ rename($temp_filename, Avatar::path($filename));
+ return $profile->setOriginal($filename);
}
function showAcceptMessage($tok)
diff --git a/actions/userrss.php b/actions/userrss.php
index 04855cccad..a3e5a3aab7 100644
--- a/actions/userrss.php
+++ b/actions/userrss.php
@@ -46,13 +46,13 @@ class UserrssAction extends Rss10Action
{
$user = $this->user;
-
+
if (is_null($user)) {
return null;
}
-
+
$notice = $user->getNotices(0, ($limit == 0) ? NOTICES_PER_PAGE : $limit);
-
+
while ($notice->fetch()) {
$notices[] = clone($notice);
}
@@ -87,10 +87,10 @@ class UserrssAction extends Rss10Action
}
# override parent to add X-SUP-ID URL
-
+
function initRss($limit=0)
{
- $url = common_local_url('sup', null, $this->user->id);
+ $url = common_local_url('sup', null, null, $this->user->id);
header('X-SUP-ID: '.$url);
parent::initRss($limit);
}
@@ -100,4 +100,3 @@ class UserrssAction extends Rss10Action
return true;
}
}
-
diff --git a/bin/flowplayer-3.0.5.swf b/bin/flowplayer-3.0.5.swf
new file mode 100644
index 0000000000..05b64a032b
Binary files /dev/null and b/bin/flowplayer-3.0.5.swf differ
diff --git a/bin/flowplayer.audio-3.0.3.swf b/bin/flowplayer.audio-3.0.3.swf
new file mode 100644
index 0000000000..ef85f1bff0
Binary files /dev/null and b/bin/flowplayer.audio-3.0.3.swf differ
diff --git a/bin/flowplayer.controls-3.0.3.swf b/bin/flowplayer.controls-3.0.3.swf
new file mode 100644
index 0000000000..09a27e8a98
Binary files /dev/null and b/bin/flowplayer.controls-3.0.3.swf differ
diff --git a/classes/Notice.php b/classes/Notice.php
index 3299883686..8300667fa4 100644
--- a/classes/Notice.php
+++ b/classes/Notice.php
@@ -34,22 +34,23 @@ class Notice extends Memcached_DataObject
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
- public $__table = 'notice'; // table name
- public $id; // int(4) primary_key not_null
- public $profile_id; // int(4) not_null
+ public $__table = 'notice'; // table name
+ public $id; // int(4) primary_key not_null
+ public $profile_id; // int(4) not_null
public $uri; // varchar(255) unique_key
public $content; // varchar(140)
- public $rendered; // text()
+ public $rendered; // text()
public $url; // varchar(255)
- public $created; // datetime() not_null
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
- public $reply_to; // int(4)
- public $is_local; // tinyint(1)
- public $source; // varchar(32)
+ public $created; // datetime() not_null
+ public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+ public $reply_to; // int(4)
+ public $is_local; // tinyint(1)
+ public $source; // varchar(32)
/* Static get */
- function staticGet($k,$v=null)
- { return Memcached_DataObject::staticGet('Notice',$k,$v); }
+ function staticGet($k,$v=NULL) {
+ return Memcached_DataObject::staticGet('Notice',$k,$v);
+ }
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
@@ -94,23 +95,28 @@ class Notice extends Memcached_DataObject
/* Add them to the database */
foreach(array_unique($match[1]) as $hashtag) {
/* elide characters we don't want in the tag */
- $hashtag = common_canonical_tag($hashtag);
-
- $tag = DB_DataObject::factory('Notice_tag');
- $tag->notice_id = $this->id;
- $tag->tag = $hashtag;
- $tag->created = $this->created;
- $id = $tag->insert();
- if (!$id) {
- $last_error = PEAR::getStaticProperty('DB_DataObject','lastError');
- common_log(LOG_ERR, 'DB error inserting hashtag: ' . $last_error->message);
- common_server_error(sprintf(_('DB error inserting hashtag: %s'), $last_error->message));
- return;
- }
+ $this->saveTag($hashtag);
}
return true;
}
+ function saveTag($hashtag)
+ {
+ $hashtag = common_canonical_tag($hashtag);
+
+ $tag = new Notice_tag();
+ $tag->notice_id = $this->id;
+ $tag->tag = $hashtag;
+ $tag->created = $this->created;
+ $id = $tag->insert();
+
+ if (!$id) {
+ throw new ServerException(sprintf(_('DB error inserting hashtag: %s'),
+ $last_error->message));
+ return;
+ }
+ }
+
static function saveNew($profile_id, $content, $source=null, $is_local=1, $reply_to=null, $uri=null) {
$profile = Profile::staticGet($profile_id);
@@ -136,10 +142,12 @@ class Notice extends Memcached_DataObject
$notice->profile_id = $profile_id;
$blacklist = common_config('public', 'blacklist');
+ $autosource = common_config('public', 'autosource');
# Blacklisted are non-false, but not 1, either
- if ($blacklist && in_array($profile_id, $blacklist)) {
+ if (($blacklist && in_array($profile_id, $blacklist)) ||
+ ($source && $autosource && in_array($source, $autosource))) {
$notice->is_local = -1;
} else {
$notice->is_local = $is_local;
@@ -154,33 +162,38 @@ class Notice extends Memcached_DataObject
$notice->source = $source;
$notice->uri = $uri;
- $id = $notice->insert();
+ if (Event::handle('StartNoticeSave', array(&$notice))) {
- if (!$id) {
- common_log_db_error($notice, 'INSERT', __FILE__);
- return _('Problem saving notice.');
- }
+ $id = $notice->insert();
- # Update the URI after the notice is in the database
- if (!$uri) {
- $orig = clone($notice);
- $notice->uri = common_notice_uri($notice);
-
- if (!$notice->update($orig)) {
- common_log_db_error($notice, 'UPDATE', __FILE__);
+ if (!$id) {
+ common_log_db_error($notice, 'INSERT', __FILE__);
return _('Problem saving notice.');
}
+
+ # Update the URI after the notice is in the database
+ if (!$uri) {
+ $orig = clone($notice);
+ $notice->uri = common_notice_uri($notice);
+
+ if (!$notice->update($orig)) {
+ common_log_db_error($notice, 'UPDATE', __FILE__);
+ return _('Problem saving notice.');
+ }
+ }
+
+ # XXX: do we need to change this for remote users?
+
+ $notice->saveReplies();
+ $notice->saveTags();
+ $notice->saveGroups();
+
+ $notice->addToInboxes();
+ $notice->query('COMMIT');
+
+ Event::handle('EndNoticeSave', array($notice));
}
- # XXX: do we need to change this for remote users?
-
- $notice->saveReplies();
- $notice->saveTags();
- $notice->saveGroups();
-
- $notice->addToInboxes();
- $notice->query('COMMIT');
-
# Clear the cache for subscribed users, so they'll update at next request
# XXX: someone clever could prepend instead of clearing the cache
@@ -614,6 +627,15 @@ class Notice extends Memcached_DataObject
continue;
}
+ // we automatically add a tag for every group name, too
+
+ $tag = Notice_tag::pkeyGet(array('tag' => common_canonical_tag($nickname),
+ 'notice_id' => $this->id));
+
+ if (is_null($tag)) {
+ $this->saveTag($nickname);
+ }
+
if ($profile->isMember($group)) {
$gi = new Group_inbox();
@@ -725,10 +747,19 @@ class Notice extends Memcached_DataObject
if (!$id) {
common_log_db_error($reply, 'INSERT', __FILE__);
return;
+ } else {
+ $replied[$recipient->id] = 1;
}
}
}
}
}
+
+ foreach (array_keys($replied) as $recipient) {
+ $user = User::staticGet('id', $recipient);
+ if ($user) {
+ mail_notify_attn($user, $this);
+ }
+ }
}
}
diff --git a/classes/Notice_tag.php b/classes/Notice_tag.php
index 94f9296d60..0365973f56 100644
--- a/classes/Notice_tag.php
+++ b/classes/Notice_tag.php
@@ -19,7 +19,7 @@
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
-class Notice_tag extends Memcached_DataObject
+class Notice_tag extends Memcached_DataObject
{
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
@@ -35,9 +35,9 @@ class Notice_tag extends Memcached_DataObject
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
-
+
static function getStream($tag, $offset=0, $limit=20) {
- $qry =
+ $qry =
'SELECT notice.* ' .
'FROM notice JOIN notice_tag ON notice.id = notice_tag.notice_id ' .
'WHERE notice_tag.tag = "%s" ';
@@ -46,7 +46,7 @@ class Notice_tag extends Memcached_DataObject
'notice_tag:notice_stream:' . common_keyize($tag),
$offset, $limit);
}
-
+
function blowCache()
{
$cache = common_memcache();
@@ -54,4 +54,9 @@ class Notice_tag extends Memcached_DataObject
$cache->delete(common_cache_key('notice_tag:notice_stream:' . $this->tag));
}
}
+
+ function &pkeyGet($kv)
+ {
+ return Memcached_DataObject::pkeyGet('Notice_tag', $kv);
+ }
}
diff --git a/classes/User.php b/classes/User.php
index a6a1b11b9f..40cf18df67 100644
--- a/classes/User.php
+++ b/classes/User.php
@@ -40,6 +40,7 @@ class User extends Memcached_DataObject
public $emailnotifyfav; // tinyint(1) default_1
public $emailnotifynudge; // tinyint(1) default_1
public $emailnotifymsg; // tinyint(1) default_1
+ public $emailnotifyattn; // tinyint(1) default_1
public $emailmicroid; // tinyint(1) default_1
public $language; // varchar(50)
public $timezone; // varchar(50)
@@ -62,8 +63,10 @@ class User extends Memcached_DataObject
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
/* Static get */
- function staticGet($k,$v=null)
- { return Memcached_DataObject::staticGet('User',$k,$v); }
+ function staticGet($k,$v=NULL)
+ {
+ return Memcached_DataObject::staticGet('User',$k,$v);
+ }
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
@@ -180,16 +183,16 @@ class User extends Memcached_DataObject
$profile->nickname = $nickname;
$profile->profileurl = common_profile_url($nickname);
- if ($fullname) {
+ if (!empty($fullname)) {
$profile->fullname = $fullname;
}
- if ($homepage) {
+ if (!empty($homepage)) {
$profile->homepage = $homepage;
}
- if ($bio) {
+ if (!empty($bio)) {
$profile->bio = $bio;
}
- if ($location) {
+ if (!empty($location)) {
$profile->location = $location;
}
@@ -197,7 +200,7 @@ class User extends Memcached_DataObject
$id = $profile->insert();
- if (!$id) {
+ if (empty($id)) {
common_log_db_error($profile, 'INSERT', __FILE__);
return false;
}
@@ -207,13 +210,13 @@ class User extends Memcached_DataObject
$user->id = $id;
$user->nickname = $nickname;
- if ($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
- if ($code) {
+ if (!empty($code)) {
$invite = Invitation::staticGet($code);
if ($invite && $invite->address && $invite->address_type == 'email' && $invite->address == $email) {
$user->email = $invite->address;
@@ -250,7 +253,7 @@ class User extends Memcached_DataObject
return false;
}
- if ($email && !$user->email) {
+ if (!empty($email) && !$user->email) {
$confirm = new Confirm_address();
$confirm->code = common_confirmation_code(128);
@@ -265,7 +268,7 @@ class User extends Memcached_DataObject
}
}
- if ($code && $user->email) {
+ if (!empty($code) && $user->email) {
$user->emailChanged();
}
diff --git a/classes/laconica.ini b/classes/laconica.ini
index 255122a97f..5fd2cd1f86 100755
--- a/classes/laconica.ini
+++ b/classes/laconica.ini
@@ -292,7 +292,8 @@ created = 142
modified = 384
[sms_carrier__keys]
-id = N
+id = K
+name = U
[subscription]
subscriber = 129
@@ -331,6 +332,7 @@ emailnotifysub = 17
emailnotifyfav = 17
emailnotifynudge = 17
emailnotifymsg = 17
+emailnotifyattn = 17
emailmicroid = 17
language = 2
timezone = 2
diff --git a/config.php.sample b/config.php.sample
index a2c5801f45..6e55eaffc8 100644
--- a/config.php.sample
+++ b/config.php.sample
@@ -18,6 +18,8 @@ $config['site']['server'] = 'localhost';
$config['site']['path'] = 'laconica';
#$config['site']['fancy'] = false;
#$config['site']['theme'] = 'default';
+#To enable the built-in mobile style sheet, defaults to false.
+#$config['site']['mobile'] = true;
#For contact email, defaults to $_SERVER["SERVER_ADMIN"]
#$config['site']['email'] = 'admin@example.net';
#Brought by...
@@ -107,6 +109,14 @@ $config['sphinx']['port'] = 3312;
#$config['public']['blacklist'][] = 123;
#$config['public']['blacklist'][] = 2307;
+#Mark certain notice sources as automatic and thus not
+#appropriate for public feed
+#$config['public]['autosource'][] = 'twitterfeed';
+#$config['public]['autosource'][] = 'rssdent';
+#$config['public]['autosource'][] = 'Ping.Fm';
+#$config['public]['autosource'][] = 'HelloTxt';
+#$config['public]['autosource'][] = 'Updating.Me';
+
#Do notice broadcasts offline
#If you use this, you must run the six offline daemons in the
#background. See the README for details.
@@ -139,7 +149,7 @@ $config['sphinx']['port'] = 3312;
#$config['profile']['banned'][] = 'hacker';
#$config['profile']['banned'][] = 12345;
-# config section for the built-in Facebook application
+# Config section for the built-in Facebook application
#$config['facebook']['apikey'] = 'APIKEY';
#$config['facebook']['secret'] = 'SECRET';
diff --git a/db/carrier.sql b/db/carrier.sql
deleted file mode 100644
index 932f7c8bb0..0000000000
--- a/db/carrier.sql
+++ /dev/null
@@ -1,61 +0,0 @@
-insert into sms_carrier
- (name, email_pattern, created)
-values
- ('3 River Wireless', '%s@sms.3rivers.net', now()),
- ('7-11 Speakout', '%s@cingularme.com', now()),
- ('Airtel (Karnataka, India)', '%s@airtelkk.com', now()),
- ('Alaska Communications Systems', '%s@msg.acsalaska.com', now()),
- ('Alltel Wireless', '%s@message.alltel.com', now()),
- ('AT&T Wireless', '%s@txt.att.net', now()),
- ('Bell Mobility (Canada)', '%s@txt.bell.ca', now()),
- ('Boost Mobile', '%s@myboostmobile.com', now()),
- ('Cellular One (Dobson)', '%s@mobile.celloneusa.com', now()),
- ('Cincinnati Bell Wireless', '%s@gocbw.com', now()),
- ('Cingular (Postpaid)', '%s@cingularme.com', now()),
- ('Centennial Wireless', '%s@cwemail.com', now()),
- ('Cingular (GoPhone prepaid)', '%s@cingularme.com', now()),
- ('Claro (Nicaragua)', '%s@ideasclaro-ca.com', now()),
- ('Comcel', '%s@comcel.com.co', now()),
- ('Cricket', '%s@sms.mycricket.com', now()),
- ('CTI', '%s@sms.ctimovil.com.ar', now()),
- ('Emtel (Mauritius)', '%s@emtelworld.net', now()),
- ('Fido (Canada)', '%s@fido.ca', now()),
- ('General Communications Inc.', '%s@msg.gci.net', now()),
- ('Globalstar', '%s@msg.globalstarusa.com', now()),
- ('Helio', '%s@myhelio.com', now()),
- ('Illinois Valley Cellular', '%s@ivctext.com', now()),
- ('i wireless', '%s.iws@iwspcs.net', now()),
- ('Meteor (Ireland)', '%s@sms.mymeteor.ie', now()),
- ('Mero Mobile (Nepal)', '%s@sms.spicenepal.com', now()),
- ('MetroPCS', '%s@mymetropcs.com', now()),
- ('Movicom', '%s@movimensaje.com.ar', now()),
- ('Mobitel (Sri Lanka)', '%s@sms.mobitel.lk', now()),
- ('Movistar (Colombia)', '%s@movistar.com.co', now()),
- ('MTN (South Africa)', '%s@sms.co.za', now()),
- ('MTS (Canada)', '%s@text.mtsmobility.com', now()),
- ('Nextel (Argentina)', '%s@nextel.net.ar', now()),
- ('Orange (Poland)', '%s@orange.pl', now()),
- ('Orange (UK)', '%s@orange.net', now()),
- ('Personal (Argentina)', '%s@personal-net.com.ar', now()),
- ('Plus GSM (Poland)', '%s@text.plusgsm.pl', now()),
- ('President''s Choice (Canada)', '%s@txt.bell.ca', now()),
- ('Qwest', '%s@qwestmp.com', now()),
- ('Rogers (Canada)', '%s@pcs.rogers.com', now()),
- ('Sasktel (Canada)', '%s@sms.sasktel.com', now()),
- ('Setar Mobile email (Aruba)', '%s@mas.aw', now()),
- ('Solo Mobile', '%s@txt.bell.ca', now()),
- ('Sprint (PCS)', '%s@messaging.sprintpcs.com', now()),
- ('Sprint (Nextel)', '%s@page.nextel.com', now()),
- ('Suncom', '%s@tms.suncom.com', now()),
- ('T-Mobile', '%s@tmomail.net', now()),
- ('T-Mobile (Austria)', '%s@sms.t-mobile.at', now()),
- ('Telus Mobility (Canada)', '%s@msg.telus.com', now()),
- ('Thumb Cellular', '%s@sms.thumbcellular.com', now()),
- ('Tigo (Formerly Ola)', '%s@sms.tigo.com.co', now()),
- ('Unicel', '%s@utext.com', now()),
- ('US Cellular', '%s@email.uscc.net', now()),
- ('Verizon', '%s@vtext.com', now()),
- ('Virgin Mobile (Canada)', '%s@vmobile.ca', now()),
- ('Virgin Mobile (USA)', '%s@vmobl.com', now()),
- ('Vodafone NZ (txt ''R'' to 901 to enable first)', '%s@sms.vodafone.net.nz', now()),
- ('YCC', '%s@sms.ycc.ru', now());
diff --git a/db/laconica.sql b/db/laconica.sql
index 254cf5fabf..6cacdba44c 100644
--- a/db/laconica.sql
+++ b/db/laconica.sql
@@ -31,7 +31,7 @@ create table avatar (
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
create table sms_carrier (
- id integer auto_increment primary key comment 'primary key for SMS carrier',
+ id integer primary key comment 'primary key for SMS carrier',
name varchar(64) unique key comment 'name of the carrier',
email_pattern varchar(255) not null comment 'sprintf pattern for making an email address from a phone number',
created datetime not null comment 'date this record was created',
@@ -50,6 +50,7 @@ create table user (
emailnotifyfav tinyint default 1 comment 'Notify by email of favorites',
emailnotifynudge tinyint default 1 comment 'Notify by email of nudges',
emailnotifymsg tinyint default 1 comment 'Notify by email of direct messages',
+ emailnotifyattn tinyint default 1 comment 'Notify by email of @-replies',
emailmicroid tinyint default 1 comment 'whether to publish email microid',
language varchar(50) comment 'preferred language',
timezone varchar(50) comment 'timezone',
@@ -260,7 +261,8 @@ create table notice_tag (
created datetime not null comment 'date this record was created',
constraint primary key (tag, notice_id),
- index notice_tag_created_idx (created)
+ index notice_tag_created_idx (created),
+ index notice_tag_notice_id_idx (notice_id)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
/* Synching with foreign services */
@@ -358,7 +360,8 @@ create table profile_tag (
constraint primary key (tagger, tagged, tag),
index profile_tag_modified_idx (modified),
- index profile_tag_tagger_tag_idx (tagger, tag)
+ index profile_tag_tagger_tag_idx (tagger, tag),
+ index profile_tag_tagged_idx (tagged)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
create table profile_block (
@@ -402,7 +405,9 @@ create table group_member (
created datetime not null comment 'date this record was created',
modified timestamp comment 'date this record was modified',
- constraint primary key (group_id, profile_id)
+ constraint primary key (group_id, profile_id),
+ index group_member_profile_id_idx (profile_id),
+ index group_member_created_idx (created)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
diff --git a/db/sms_carrier.sql b/db/sms_carrier.sql
new file mode 100644
index 0000000000..6879f20890
--- /dev/null
+++ b/db/sms_carrier.sql
@@ -0,0 +1,63 @@
+INSERT INTO sms_carrier
+ (id, name, email_pattern, created)
+VALUES
+ (100056, '3 River Wireless', '%s@sms.3rivers.net', now()),
+ (100057, '7-11 Speakout', '%s@cingularme.com', now()),
+ (100058, 'Airtel (Karnataka, India)', '%s@airtelkk.com', now()),
+ (100059, 'Alaska Communications Systems', '%s@msg.acsalaska.com', now()),
+ (100060, 'Alltel Wireless', '%s@message.alltel.com', now()),
+ (100061, 'AT&T Wireless', '%s@txt.att.net', now()),
+ (100062, 'Bell Mobility (Canada)', '%s@txt.bell.ca', now()),
+ (100063, 'Boost Mobile', '%s@myboostmobile.com', now()),
+ (100064, 'Cellular One (Dobson)', '%s@mobile.celloneusa.com', now()),
+ (100065, 'Cingular (Postpaid)', '%s@cingularme.com', now()),
+ (100066, 'Centennial Wireless', '%s@cwemail.com', now()),
+ (100067, 'Cingular (GoPhone prepaid)', '%s@cingularme.com', now()),
+ (100068, 'Claro (Nicaragua)', '%s@ideasclaro-ca.com', now()),
+ (100069, 'Comcel', '%s@comcel.com.co', now()),
+ (100070, 'Cricket', '%s@sms.mycricket.com', now()),
+ (100071, 'CTI', '%s@sms.ctimovil.com.ar', now()),
+ (100072, 'Emtel (Mauritius)', '%s@emtelworld.net', now()),
+ (100073, 'Fido (Canada)', '%s@fido.ca', now()),
+ (100074, 'General Communications Inc.', '%s@msg.gci.net', now()),
+ (100075, 'Globalstar', '%s@msg.globalstarusa.com', now()),
+ (100076, 'Helio', '%s@myhelio.com', now()),
+ (100077, 'Illinois Valley Cellular', '%s@ivctext.com', now()),
+ (100078, 'i wireless', '%s.iws@iwspcs.net', now()),
+ (100079, 'Meteor (Ireland)', '%s@sms.mymeteor.ie', now()),
+ (100080, 'Mero Mobile (Nepal)', '%s@sms.spicenepal.com', now()),
+ (100081, 'MetroPCS', '%s@mymetropcs.com', now()),
+ (100082, 'Movicom', '%s@movimensaje.com.ar', now()),
+ (100083, 'Mobitel (Sri Lanka)', '%s@sms.mobitel.lk', now()),
+ (100084, 'Movistar (Colombia)', '%s@movistar.com.co', now()),
+ (100085, 'MTN (South Africa)', '%s@sms.co.za', now()),
+ (100086, 'MTS (Canada)', '%s@text.mtsmobility.com', now()),
+ (100087, 'Nextel (Argentina)', '%s@nextel.net.ar', now()),
+ (100088, 'Orange (Poland)', '%s@orange.pl', now()),
+ (100089, 'Personal (Argentina)', '%s@personal-net.com.ar', now()),
+ (100090, 'Plus GSM (Poland)', '%s@text.plusgsm.pl', now()),
+ (100091, 'President\'s Choice (Canada)', '%s@txt.bell.ca', now()),
+ (100092, 'Qwest', '%s@qwestmp.com', now()),
+ (100093, 'Rogers (Canada)', '%s@pcs.rogers.com', now()),
+ (100094, 'Sasktel (Canada)', '%s@sms.sasktel.com', now()),
+ (100095, 'Setar Mobile email (Aruba)', '%s@mas.aw', now()),
+ (100096, 'Solo Mobile', '%s@txt.bell.ca', now()),
+ (100097, 'Sprint (PCS)', '%s@messaging.sprintpcs.com', now()),
+ (100098, 'Sprint (Nextel)', '%s@page.nextel.com', now()),
+ (100099, 'Suncom', '%s@tms.suncom.com', now()),
+ (100100, 'T-Mobile', '%s@tmomail.net', now()),
+ (100101, 'T-Mobile (Austria)', '%s@sms.t-mobile.at', now()),
+ (100102, 'Telus Mobility (Canada)', '%s@msg.telus.com', now()),
+ (100103, 'Thumb Cellular', '%s@sms.thumbcellular.com', now()),
+ (100104, 'Tigo (Formerly Ola)', '%s@sms.tigo.com.co', now()),
+ (100105, 'Unicel', '%s@utext.com', now()),
+ (100106, 'US Cellular', '%s@email.uscc.net', now()),
+ (100107, 'Verizon', '%s@vtext.com', now()),
+ (100108, 'Virgin Mobile (Canada)', '%s@vmobile.ca', now()),
+ (100109, 'Virgin Mobile (USA)', '%s@vmobl.com', now()),
+ (100110, 'YCC', '%s@sms.ycc.ru', now()),
+ (100111, 'Orange (UK)', '%s@orange.net', now()),
+ (100112, 'Cincinnati Bell Wireless', '%s@gocbw.com', now()),
+ (100113, 'T-Mobile Germany', '%s@t-mobile-sms.de', now()),
+ (100114, 'Vodafone Germany', '%s@vodafone-sms.de', now()),
+ (100115, 'E-Plus', '%s@smsmail.eplus.de', now());
diff --git a/doc/about b/doc-src/about
similarity index 100%
rename from doc/about
rename to doc-src/about
diff --git a/doc-src/badge b/doc-src/badge
new file mode 100644
index 0000000000..1c368eb690
--- /dev/null
+++ b/doc-src/badge
@@ -0,0 +1,65 @@
+Install the %%site.name%% badge on you blog or web site to show the latest updates
+from you and your friends!
+
+
+
+
+
+Things to try
+--------------
+
+* Click an avatar and the badge will refresh with that user's timeline
+* Click a nickname to open a user's profile in your browser
+* Click a notice's timestamp to view the notice in your browser
+* @-replies and #tags are live links
+
+## Installation instructions
+
+Copy and paste the following JavaScript into an HTML page where
+you want the badge to show up. Substitute your own ID in the user
+parameter.
+
+
+ <script type="text/javascript" src="http://identi.ca/js/identica-badge.js">
+ {
+ "user":"kentbrew",
+ "server":"identi.ca",
+ "headerText":" and friends"
+ }
+ </script>
+
+
+
+
+
+Valid parameters for the badge:
+-------------------------------
+
+* user : defaults to 7000 (@kentbrew)
+* headerText : defaults to empty
+* height : defaults to 350px
+* width : defaults to 300px
+* background : defaults to #193441. If you set evenBackground, oddBackground,
+ and headerBackground, you won't see it at all.
+* border : defaults to 1px solid black
+* userColor : defaults to whatever link color is set to on your page
+* headerBackground : defaults to transparent
+* headerColor : defaults to white
+* evenBackground : defaults to #fff
+* oddBackground : defaults to #eee
+* thumbnailBorder : 1px solid black
+* thumbnailSize : defaults to 24px
+* padding : defaults to 3px
+* server : defaults to identi.ca
+
+Licence
+-------
+
+Identi.ca badge by [Kent Brewster](http://kentbrewster.com/identica-badge/).
+Licenced under [CC-BY-SA-3](http://kentbrewster.com/rights-and-permissions/).
diff --git a/doc/contact b/doc-src/contact
similarity index 100%
rename from doc/contact
rename to doc-src/contact
diff --git a/doc/faq b/doc-src/faq
similarity index 100%
rename from doc/faq
rename to doc-src/faq
diff --git a/doc/groups b/doc-src/groups
similarity index 100%
rename from doc/groups
rename to doc-src/groups
diff --git a/doc/help b/doc-src/help
similarity index 100%
rename from doc/help
rename to doc-src/help
diff --git a/doc/im b/doc-src/im
similarity index 100%
rename from doc/im
rename to doc-src/im
diff --git a/doc/openid b/doc-src/openid
similarity index 100%
rename from doc/openid
rename to doc-src/openid
diff --git a/doc/openmublog b/doc-src/openmublog
similarity index 100%
rename from doc/openmublog
rename to doc-src/openmublog
diff --git a/doc/privacy b/doc-src/privacy
similarity index 100%
rename from doc/privacy
rename to doc-src/privacy
diff --git a/doc/sms b/doc-src/sms
similarity index 100%
rename from doc/sms
rename to doc-src/sms
diff --git a/doc/source b/doc-src/source
similarity index 100%
rename from doc/source
rename to doc-src/source
diff --git a/doc/tags b/doc-src/tags
similarity index 100%
rename from doc/tags
rename to doc-src/tags
diff --git a/extlib/Net/URL.php b/extlib/Net/URL.php
new file mode 100644
index 0000000000..3dcfef60d0
--- /dev/null
+++ b/extlib/Net/URL.php
@@ -0,0 +1,485 @@
+ |
+// +-----------------------------------------------------------------------+
+//
+// $Id: URL.php,v 1.49 2007/06/28 14:43:07 davidc Exp $
+//
+// Net_URL Class
+
+
+class Net_URL
+{
+ var $options = array('encode_query_keys' => false);
+ /**
+ * Full url
+ * @var string
+ */
+ var $url;
+
+ /**
+ * Protocol
+ * @var string
+ */
+ var $protocol;
+
+ /**
+ * Username
+ * @var string
+ */
+ var $username;
+
+ /**
+ * Password
+ * @var string
+ */
+ var $password;
+
+ /**
+ * Host
+ * @var string
+ */
+ var $host;
+
+ /**
+ * Port
+ * @var integer
+ */
+ var $port;
+
+ /**
+ * Path
+ * @var string
+ */
+ var $path;
+
+ /**
+ * Query string
+ * @var array
+ */
+ var $querystring;
+
+ /**
+ * Anchor
+ * @var string
+ */
+ var $anchor;
+
+ /**
+ * Whether to use []
+ * @var bool
+ */
+ var $useBrackets;
+
+ /**
+ * PHP4 Constructor
+ *
+ * @see __construct()
+ */
+ function Net_URL($url = null, $useBrackets = true)
+ {
+ $this->__construct($url, $useBrackets);
+ }
+
+ /**
+ * PHP5 Constructor
+ *
+ * Parses the given url and stores the various parts
+ * Defaults are used in certain cases
+ *
+ * @param string $url Optional URL
+ * @param bool $useBrackets Whether to use square brackets when
+ * multiple querystrings with the same name
+ * exist
+ */
+ function __construct($url = null, $useBrackets = true)
+ {
+ $this->url = $url;
+ $this->useBrackets = $useBrackets;
+
+ $this->initialize();
+ }
+
+ function initialize()
+ {
+ $HTTP_SERVER_VARS = !empty($_SERVER) ? $_SERVER : $GLOBALS['HTTP_SERVER_VARS'];
+
+ $this->user = '';
+ $this->pass = '';
+ $this->host = '';
+ $this->port = 80;
+ $this->path = '';
+ $this->querystring = array();
+ $this->anchor = '';
+
+ // Only use defaults if not an absolute URL given
+ if (!preg_match('/^[a-z0-9]+:\/\//i', $this->url)) {
+ $this->protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ? 'https' : 'http');
+
+ /**
+ * Figure out host/port
+ */
+ if (!empty($HTTP_SERVER_VARS['HTTP_HOST']) &&
+ preg_match('/^(.*)(:([0-9]+))?$/U', $HTTP_SERVER_VARS['HTTP_HOST'], $matches))
+ {
+ $host = $matches[1];
+ if (!empty($matches[3])) {
+ $port = $matches[3];
+ } else {
+ $port = $this->getStandardPort($this->protocol);
+ }
+ }
+
+ $this->user = '';
+ $this->pass = '';
+ $this->host = !empty($host) ? $host : (isset($HTTP_SERVER_VARS['SERVER_NAME']) ? $HTTP_SERVER_VARS['SERVER_NAME'] : 'localhost');
+ $this->port = !empty($port) ? $port : (isset($HTTP_SERVER_VARS['SERVER_PORT']) ? $HTTP_SERVER_VARS['SERVER_PORT'] : $this->getStandardPort($this->protocol));
+ $this->path = !empty($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : '/';
+ $this->querystring = isset($HTTP_SERVER_VARS['QUERY_STRING']) ? $this->_parseRawQuerystring($HTTP_SERVER_VARS['QUERY_STRING']) : null;
+ $this->anchor = '';
+ }
+
+ // Parse the url and store the various parts
+ if (!empty($this->url)) {
+ $urlinfo = parse_url($this->url);
+
+ // Default querystring
+ $this->querystring = array();
+
+ foreach ($urlinfo as $key => $value) {
+ switch ($key) {
+ case 'scheme':
+ $this->protocol = $value;
+ $this->port = $this->getStandardPort($value);
+ break;
+
+ case 'user':
+ case 'pass':
+ case 'host':
+ case 'port':
+ $this->$key = $value;
+ break;
+
+ case 'path':
+ if ($value{0} == '/') {
+ $this->path = $value;
+ } else {
+ $path = dirname($this->path) == DIRECTORY_SEPARATOR ? '' : dirname($this->path);
+ $this->path = sprintf('%s/%s', $path, $value);
+ }
+ break;
+
+ case 'query':
+ $this->querystring = $this->_parseRawQueryString($value);
+ break;
+
+ case 'fragment':
+ $this->anchor = $value;
+ break;
+ }
+ }
+ }
+ }
+ /**
+ * Returns full url
+ *
+ * @return string Full url
+ * @access public
+ */
+ function getURL()
+ {
+ $querystring = $this->getQueryString();
+
+ $this->url = $this->protocol . '://'
+ . $this->user . (!empty($this->pass) ? ':' : '')
+ . $this->pass . (!empty($this->user) ? '@' : '')
+ . $this->host . ($this->port == $this->getStandardPort($this->protocol) ? '' : ':' . $this->port)
+ . $this->path
+ . (!empty($querystring) ? '?' . $querystring : '')
+ . (!empty($this->anchor) ? '#' . $this->anchor : '');
+
+ return $this->url;
+ }
+
+ /**
+ * Adds or updates a querystring item (URL parameter).
+ * Automatically encodes parameters with rawurlencode() if $preencoded
+ * is false.
+ * You can pass an array to $value, it gets mapped via [] in the URL if
+ * $this->useBrackets is activated.
+ *
+ * @param string $name Name of item
+ * @param string $value Value of item
+ * @param bool $preencoded Whether value is urlencoded or not, default = not
+ * @access public
+ */
+ function addQueryString($name, $value, $preencoded = false)
+ {
+ if ($this->getOption('encode_query_keys')) {
+ $name = rawurlencode($name);
+ }
+
+ if ($preencoded) {
+ $this->querystring[$name] = $value;
+ } else {
+ $this->querystring[$name] = is_array($value) ? array_map('rawurlencode', $value): rawurlencode($value);
+ }
+ }
+
+ /**
+ * Removes a querystring item
+ *
+ * @param string $name Name of item
+ * @access public
+ */
+ function removeQueryString($name)
+ {
+ if ($this->getOption('encode_query_keys')) {
+ $name = rawurlencode($name);
+ }
+
+ if (isset($this->querystring[$name])) {
+ unset($this->querystring[$name]);
+ }
+ }
+
+ /**
+ * Sets the querystring to literally what you supply
+ *
+ * @param string $querystring The querystring data. Should be of the format foo=bar&x=y etc
+ * @access public
+ */
+ function addRawQueryString($querystring)
+ {
+ $this->querystring = $this->_parseRawQueryString($querystring);
+ }
+
+ /**
+ * Returns flat querystring
+ *
+ * @return string Querystring
+ * @access public
+ */
+ function getQueryString()
+ {
+ if (!empty($this->querystring)) {
+ foreach ($this->querystring as $name => $value) {
+ // Encode var name
+ $name = rawurlencode($name);
+
+ if (is_array($value)) {
+ foreach ($value as $k => $v) {
+ $querystring[] = $this->useBrackets ? sprintf('%s[%s]=%s', $name, $k, $v) : ($name . '=' . $v);
+ }
+ } elseif (!is_null($value)) {
+ $querystring[] = $name . '=' . $value;
+ } else {
+ $querystring[] = $name;
+ }
+ }
+ $querystring = implode(ini_get('arg_separator.output'), $querystring);
+ } else {
+ $querystring = '';
+ }
+
+ return $querystring;
+ }
+
+ /**
+ * Parses raw querystring and returns an array of it
+ *
+ * @param string $querystring The querystring to parse
+ * @return array An array of the querystring data
+ * @access private
+ */
+ function _parseRawQuerystring($querystring)
+ {
+ $parts = preg_split('/[' . preg_quote(ini_get('arg_separator.input'), '/') . ']/', $querystring, -1, PREG_SPLIT_NO_EMPTY);
+ $return = array();
+
+ foreach ($parts as $part) {
+ if (strpos($part, '=') !== false) {
+ $value = substr($part, strpos($part, '=') + 1);
+ $key = substr($part, 0, strpos($part, '='));
+ } else {
+ $value = null;
+ $key = $part;
+ }
+
+ if (!$this->getOption('encode_query_keys')) {
+ $key = rawurldecode($key);
+ }
+
+ if (preg_match('#^(.*)\[([0-9a-z_-]*)\]#i', $key, $matches)) {
+ $key = $matches[1];
+ $idx = $matches[2];
+
+ // Ensure is an array
+ if (empty($return[$key]) || !is_array($return[$key])) {
+ $return[$key] = array();
+ }
+
+ // Add data
+ if ($idx === '') {
+ $return[$key][] = $value;
+ } else {
+ $return[$key][$idx] = $value;
+ }
+ } elseif (!$this->useBrackets AND !empty($return[$key])) {
+ $return[$key] = (array)$return[$key];
+ $return[$key][] = $value;
+ } else {
+ $return[$key] = $value;
+ }
+ }
+
+ return $return;
+ }
+
+ /**
+ * Resolves //, ../ and ./ from a path and returns
+ * the result. Eg:
+ *
+ * /foo/bar/../boo.php => /foo/boo.php
+ * /foo/bar/../../boo.php => /boo.php
+ * /foo/bar/.././/boo.php => /foo/boo.php
+ *
+ * This method can also be called statically.
+ *
+ * @param string $path URL path to resolve
+ * @return string The result
+ */
+ function resolvePath($path)
+ {
+ $path = explode('/', str_replace('//', '/', $path));
+
+ for ($i=0; $i 1 OR ($i == 1 AND $path[0] != '') ) ) {
+ unset($path[$i]);
+ unset($path[$i-1]);
+ $path = array_values($path);
+ $i -= 2;
+
+ } elseif ($path[$i] == '..' AND $i == 1 AND $path[0] == '') {
+ unset($path[$i]);
+ $path = array_values($path);
+ $i--;
+
+ } else {
+ continue;
+ }
+ }
+
+ return implode('/', $path);
+ }
+
+ /**
+ * Returns the standard port number for a protocol
+ *
+ * @param string $scheme The protocol to lookup
+ * @return integer Port number or NULL if no scheme matches
+ *
+ * @author Philippe Jausions
+ */
+ function getStandardPort($scheme)
+ {
+ switch (strtolower($scheme)) {
+ case 'http': return 80;
+ case 'https': return 443;
+ case 'ftp': return 21;
+ case 'imap': return 143;
+ case 'imaps': return 993;
+ case 'pop3': return 110;
+ case 'pop3s': return 995;
+ default: return null;
+ }
+ }
+
+ /**
+ * Forces the URL to a particular protocol
+ *
+ * @param string $protocol Protocol to force the URL to
+ * @param integer $port Optional port (standard port is used by default)
+ */
+ function setProtocol($protocol, $port = null)
+ {
+ $this->protocol = $protocol;
+ $this->port = is_null($port) ? $this->getStandardPort($protocol) : $port;
+ }
+
+ /**
+ * Set an option
+ *
+ * This function set an option
+ * to be used thorough the script.
+ *
+ * @access public
+ * @param string $optionName The optionname to set
+ * @param string $value The value of this option.
+ */
+ function setOption($optionName, $value)
+ {
+ if (!array_key_exists($optionName, $this->options)) {
+ return false;
+ }
+
+ $this->options[$optionName] = $value;
+ $this->initialize();
+ }
+
+ /**
+ * Get an option
+ *
+ * This function gets an option
+ * from the $this->options array
+ * and return it's value.
+ *
+ * @access public
+ * @param string $opionName The name of the option to retrieve
+ * @see $this->options
+ */
+ function getOption($optionName)
+ {
+ if (!isset($this->options[$optionName])) {
+ return false;
+ }
+
+ return $this->options[$optionName];
+ }
+
+}
+?>
diff --git a/extlib/Net/URL/Mapper.php b/extlib/Net/URL/Mapper.php
new file mode 100644
index 0000000000..65e38818bb
--- /dev/null
+++ b/extlib/Net/URL/Mapper.php
@@ -0,0 +1,324 @@
+
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * The names of the authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category Net
+ * @package Net_URL_Mapper
+ * @author Bertrand Mansion
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version CVS: $Id: Mapper.php,v 1.1 2007/03/28 10:23:04 mansion Exp $
+ * @link http://pear.php.net/package/Net_URL_Mapper
+ */
+
+require_once 'Net/URL/Mapper/Path.php';
+require_once 'Net/URL/Mapper/Exception.php';
+
+/**
+ * URL parser and mapper class
+ *
+ * This class takes an URL and a configuration and returns formatted data
+ * about the request according to a configuration parameter
+ *
+ * @category Net
+ * @package Net_URL_Mapper
+ * @author Bertrand Mansion
+ * @version Release: @package_version@
+ */
+class Net_URL_Mapper
+{
+ /**
+ * Array of Net_URL_Mapper instances
+ * @var array
+ */
+ private static $instances = array();
+
+ /**
+ * Mapped paths collection
+ * @var array
+ */
+ protected $paths = array();
+
+ /**
+ * Prefix used for url mapping
+ * @var string
+ */
+ protected $prefix = '';
+
+ /**
+ * Optional scriptname if mod_rewrite is not available
+ * @var string
+ */
+ protected $scriptname = '';
+
+ /**
+ * Mapper instance id
+ * @var string
+ */
+ protected $id = '__default__';
+
+ /**
+ * Class constructor
+ * Constructor is private, you should use getInstance() instead.
+ */
+ private function __construct() { }
+
+ /**
+ * Returns a singleton object corresponding to the requested instance id
+ * @param string Requested instance name
+ * @return Object Net_URL_Mapper Singleton
+ */
+ public static function getInstance($id = '__default__')
+ {
+ if (!isset(self::$instances[$id])) {
+ $m = new Net_URL_Mapper();
+ $m->id = $id;
+ self::$instances[$id] = $m;
+ }
+ return self::$instances[$id];
+ }
+
+ /**
+ * Returns the instance id
+ * @return string Mapper instance id
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * Parses a path and creates a connection
+ * @param string The path to connect
+ * @param array Default values for path parts
+ * @param array Regular expressions for path parts
+ * @return object Net_URL_Mapper_Path
+ */
+ public function connect($path, $defaults = array(), $rules = array())
+ {
+ $pathObj = new Net_URL_Mapper_Path($path, $defaults, $rules);
+ $this->addPath($pathObj);
+ return $pathObj;
+ }
+
+ /**
+ * Set the url prefix if needed
+ *
+ * Example: using the prefix to differenciate mapper instances
+ *
+ * $fr = Net_URL_Mapper::getInstance('fr');
+ * $fr->setPrefix('/fr');
+ * $en = Net_URL_Mapper::getInstance('en');
+ * $en->setPrefix('/en');
+ *
+ *
+ * @param string URL prefix
+ */
+ public function setPrefix($prefix)
+ {
+ $this->prefix = '/'.trim($prefix, '/');
+ }
+
+ /**
+ * Set the scriptname if mod_rewrite not available
+ *
+ * Example: will match and generate url like
+ * - index.php/view/product/1
+ *
+ * $m = Net_URL_Mapper::getInstance();
+ * $m->setScriptname('index.php');
+ *
+ * @param string URL prefix
+ */
+ public function setScriptname($scriptname)
+ {
+ $this->scriptname = $scriptname;
+ }
+
+ /**
+ * Will attempt to match an url with a defined path
+ *
+ * If an url corresponds to a path, the resulting values are returned
+ * in an array. If none is found, null is returned. In case an url is
+ * matched but its content doesn't validate the path rules, an exception is
+ * thrown.
+ *
+ * @param string URL
+ * @return array|null array if match found, null otherwise
+ * @throws Net_URL_Mapper_InvalidException
+ */
+ public function match($url)
+ {
+ $nurl = '/'.trim($url, '/');
+
+ // Remove scriptname if needed
+
+ if (!empty($this->scriptname) &&
+ strpos($nurl, $this->scriptname) === 0) {
+ $nurl = substr($nurl, strlen($this->scriptname));
+ if (empty($nurl)) {
+ $nurl = '/';
+ }
+ }
+
+ // Remove prefix
+
+ if (!empty($this->prefix)) {
+ if (strpos($nurl, $this->prefix) !== 0) {
+ return null;
+ }
+ $nurl = substr($nurl, strlen($this->prefix));
+ if (empty($nurl)) {
+ $nurl = '/';
+ }
+ }
+
+ // Remove query string
+
+ if (($pos = strpos($nurl, '?')) !== false) {
+ $nurl = substr($nurl, 0, $pos);
+ }
+
+ $paths = array();
+ $values = null;
+
+ // Make a list of paths that conform to route format
+
+ foreach ($this->paths as $path) {
+ $regex = $path->getFormat();
+ if (preg_match($regex, $nurl)) {
+ $paths[] = $path;
+ }
+ }
+
+ // Make sure one of the paths found is valid
+
+ foreach ($paths as $path) {
+ $regex = $path->getRule();
+ if (preg_match($regex, $nurl, $matches)) {
+ $values = $path->getDefaults();
+ array_shift($matches);
+ $clean = array();
+ foreach ($matches as $k => $v) {
+ $v = trim($v, '/');
+ if (!is_int($k) && $v !== '') {
+ $values[$k] = $v;
+ }
+ }
+ break;
+ }
+ }
+
+ // A path conforms but does not validate
+
+ if (is_null($values) && !empty($paths)) {
+ $e = new Net_URL_Mapper_InvalidException('A path was found but is invalid.');
+ $e->setPath($paths[0]);
+ $e->setUrl($url);
+ throw $e;
+ }
+
+ return $values;
+ }
+
+ /**
+ * Generate an url based on given parameters
+ *
+ * Will attempt to find a path definition that matches the given parameters and
+ * will generate an url based on this path.
+ *
+ * @param array Values to be used for the url generation
+ * @param array Key/value pairs for query string if needed
+ * @param string Anchor (fragment) if needed
+ * @return string|false String if a rule was found, false otherwise
+ */
+ public function generate($values = array(), $qstring = array(), $anchor = '')
+ {
+ // Use root path if any
+
+ if (empty($values) && isset($this->paths['/'])) {
+ return $this->scriptname.$this->prefix.$this->paths['/']->generate($values, $qstring, $anchor);
+ }
+
+ foreach ($this->paths as $path) {
+ $set = array();
+ foreach ($values as $k => $v) {
+ if ($path->hasKey($k, $v)) {
+ $set[$k] = $v;
+ }
+ }
+
+ if (count($set) == count($values) &&
+ count($set) <= $path->getMaxKeys()) {
+
+ $req = $path->getRequired();
+ if (count(array_intersect(array_keys($set), $req)) != count($req)) {
+ continue;
+ }
+ $gen = $path->generate($set, $qstring, $anchor);
+ return $this->scriptname.$this->prefix.$gen;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns defined paths
+ * @return array Array of paths
+ */
+ public function getPaths()
+ {
+ return $this->paths;
+ }
+
+ /**
+ * Reset all paths
+ * This is probably only useful for testing
+ */
+ public function reset()
+ {
+ $this->paths = array();
+ $this->prefix = '';
+ }
+
+ /**
+ * Add a new path to the mapper
+ * @param object Net_URL_Mapper_Path object
+ */
+ public function addPath(Net_URL_Mapper_Path $path)
+ {
+ $this->paths[$path->getPath()] = $path;
+ }
+
+}
+?>
\ No newline at end of file
diff --git a/extlib/Net/URL/Mapper/Exception.php b/extlib/Net/URL/Mapper/Exception.php
new file mode 100644
index 0000000000..ac3ad172b1
--- /dev/null
+++ b/extlib/Net/URL/Mapper/Exception.php
@@ -0,0 +1,104 @@
+
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * The names of the authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category Net
+ * @package Net_URL_Mapper
+ * @author Bertrand Mansion
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version CVS: $Id: Exception.php,v 1.1 2007/03/28 10:23:04 mansion Exp $
+ * @link http://pear.php.net/package/Net_URL_Mapper
+ */
+
+/**
+ * Base class for exceptions in PEAR
+ */
+require_once 'PEAR/Exception.php';
+
+/**
+ * Base class for exceptions in Net_URL_Mapper package
+ *
+ * Such a base class is required by the Exception RFC:
+ * http://pear.php.net/pepr/pepr-proposal-show.php?id=132
+ * It will rarely be thrown directly, its specialized subclasses will be
+ * thrown most of the time.
+ *
+ * @category Net
+ * @package Net_URL_Mapper
+ * @version Release: @package_version@
+ */
+class Net_URL_Mapper_Exception extends PEAR_Exception
+{
+}
+
+/**
+ * Exception thrown when a path is invalid
+ *
+ * A path can conform to a given structure, but contain invalid parameters.
+ *
+ * $m = Net_URL_Mapper::getInstance();
+ * $m->connect('hi/:name', null, array('name'=>'[a-z]+'));
+ * $m->match('/hi/FOXY'); // Will throw the exception
+ *
+ *
+ * @category Net
+ * @package Net_URL_Mapper
+ * @version Release: @package_version@
+ */
+class Net_URL_Mapper_InvalidException extends Net_URL_Mapper_Exception
+{
+ protected $path;
+ protected $url;
+
+ public function setPath($path)
+ {
+ $this->path = $path;
+ }
+
+ public function getPath()
+ {
+ return $this->path;
+ }
+
+ public function setUrl($url)
+ {
+ $this->url = $url;
+ }
+
+ public function getUrl()
+ {
+ return $this->url;
+ }
+}
+?>
\ No newline at end of file
diff --git a/extlib/Net/URL/Mapper/Part.php b/extlib/Net/URL/Mapper/Part.php
new file mode 100644
index 0000000000..2f15b2c162
--- /dev/null
+++ b/extlib/Net/URL/Mapper/Part.php
@@ -0,0 +1,142 @@
+
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * The names of the authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category Net
+ * @package Net_URL_Mapper
+ * @author Bertrand Mansion
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version CVS: $Id: Part.php,v 1.1 2007/03/28 10:23:04 mansion Exp $
+ * @link http://pear.php.net/package/Net_URL_Mapper
+ */
+
+abstract class Net_URL_Mapper_Part
+{
+ protected $defaults;
+ protected $rule;
+ protected $public;
+ protected $type;
+ protected $required = false;
+
+ /**
+ * Part name if dynamic or content, generated from path
+ * @var string
+ */
+ public $content;
+
+ const DYNAMIC = 1;
+ const WILDCARD = 2;
+ const FIXED = 3;
+
+ public function __construct($content, $path)
+ {
+ $this->content = $content;
+ $this->path = $path;
+ }
+
+ public function setRule($rule)
+ {
+ $this->rule = $rule;
+ }
+
+ abstract public function getFormat();
+
+ abstract public function getRule();
+
+ public function addSlash($str)
+ {
+ $str = trim($str, '/');
+ if (($pos = strpos($this->path, '/')) !== false) {
+ if ($pos == 0) {
+ $str = '/'.$str;
+ } else {
+ $str .= '/';
+ }
+ }
+ return $str;
+ }
+
+ public function addSlashRegex($str)
+ {
+ $str = trim($str, '/');
+ if (($pos = strpos($this->path, '/')) !== false) {
+ if ($pos == 0) {
+ $str = '\/'.$str;
+ } else {
+ $str .= '\/';
+ }
+ }
+ if (!$this->isRequired()) {
+ $str = '('.$str.'|)';
+ }
+ return $str;
+ }
+
+ public function setDefaults($defaults)
+ {
+ $this->defaults = (string)$defaults;
+ }
+
+ public function getType()
+ {
+ return $this->type;
+ }
+
+ public function accept($visitor, $method = null)
+ {
+ $args = func_get_args();
+ $visitor->$method($this, $args);
+ }
+
+ public function setRequired($required)
+ {
+ $this->required = $required;
+ }
+
+ public function isRequired()
+ {
+ return $this->required;
+ }
+
+ abstract public function generate($value = null);
+
+ public function match($value)
+ {
+ $rule = $this->getRule();
+ return preg_match('/^'.$rule.'$/', $this->addSlash($value));
+ }
+
+}
+
+?>
\ No newline at end of file
diff --git a/extlib/Net/URL/Mapper/Part/Dynamic.php b/extlib/Net/URL/Mapper/Part/Dynamic.php
new file mode 100644
index 0000000000..349d87338c
--- /dev/null
+++ b/extlib/Net/URL/Mapper/Part/Dynamic.php
@@ -0,0 +1,81 @@
+
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * The names of the authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category Net
+ * @package Net_URL_Mapper
+ * @author Bertrand Mansion
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version CVS: $Id: Dynamic.php,v 1.1 2007/03/28 10:23:04 mansion Exp $
+ * @link http://pear.php.net/package/Net_URL_Mapper
+ */
+
+require_once 'Net/URL/Mapper/Part.php';
+
+class Net_URL_Mapper_Part_Dynamic extends Net_URL_Mapper_Part
+{
+
+ public function __construct($content, $path)
+ {
+ $this->type = Net_URL_Mapper_Part::DYNAMIC;
+ $this->setRequired(true);
+ parent::__construct($content, $path);
+ }
+
+ public function getFormat()
+ {
+ return $this->addSlashRegex('[^\/]+');
+ }
+
+ public function getRule()
+ {
+ if (!empty($this->rule)) {
+ return '(?P<'.$this->content.'>'.$this->addSlashRegex($this->rule).')';
+ }
+ return '(?P<'.$this->content.'>'.$this->addSlashRegex('[^\/]+').')';
+ }
+
+ public function generate($value = null)
+ {
+ if (is_array($value) && isset($value[$this->content])) {
+ $val = $value[$this->content];
+ } elseif (!is_array($value) && !is_null($value)) {
+ $val = $value;
+ } else {
+ $val = $this->defaults;
+ }
+ return $this->addSlash(urlencode($val));
+ }
+}
+?>
\ No newline at end of file
diff --git a/extlib/Net/URL/Mapper/Part/Fixed.php b/extlib/Net/URL/Mapper/Part/Fixed.php
new file mode 100644
index 0000000000..b315b442db
--- /dev/null
+++ b/extlib/Net/URL/Mapper/Part/Fixed.php
@@ -0,0 +1,70 @@
+
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * The names of the authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category Net
+ * @package Net_URL_Mapper
+ * @author Bertrand Mansion
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version CVS: $Id: Fixed.php,v 1.1 2007/03/28 10:23:04 mansion Exp $
+ * @link http://pear.php.net/package/Net_URL_Mapper
+ */
+
+require_once 'Net/URL/Mapper/Part.php';
+
+class Net_URL_Mapper_Part_Fixed extends Net_URL_Mapper_Part
+{
+
+ public function __construct($content, $path)
+ {
+ $this->type = Net_URL_Mapper_Part::FIXED;
+ parent::__construct($content, $path);
+ }
+
+ public function getFormat()
+ {
+ return $this->getRule();
+ }
+
+ public function getRule()
+ {
+ return preg_quote($this->path, '/');
+ }
+
+ public function generate($value = null)
+ {
+ return $this->path;
+ }
+}
+?>
\ No newline at end of file
diff --git a/extlib/Net/URL/Mapper/Part/Wildcard.php b/extlib/Net/URL/Mapper/Part/Wildcard.php
new file mode 100644
index 0000000000..6085ff6489
--- /dev/null
+++ b/extlib/Net/URL/Mapper/Part/Wildcard.php
@@ -0,0 +1,80 @@
+
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * The names of the authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category Net
+ * @package Net_URL_Mapper
+ * @author Bertrand Mansion
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version CVS: $Id: Wildcard.php,v 1.1 2007/03/28 10:23:04 mansion Exp $
+ * @link http://pear.php.net/package/Net_URL_Mapper
+ */
+
+require_once 'Net/URL/Mapper/Part.php';
+
+class Net_URL_Mapper_Part_Wildcard extends Net_URL_Mapper_Part
+{
+
+ public function __construct($content, $path)
+ {
+ $this->type = Net_URL_Mapper_Part::WILDCARD;
+ $this->setRequired(true);
+ parent::__construct($content, $path);
+ }
+
+ public function getFormat()
+ {
+ return $this->addSlashRegex('.*');;
+ }
+
+ public function getRule()
+ {
+ return '(?P<'.$this->content.'>'.$this->addSlashRegex('.*').')';
+ }
+
+ public function generate($value = null)
+ {
+ if (is_array($value) && isset($value[$this->content])) {
+ $val = $value[$this->content];
+ } elseif (!is_array($value) && !is_null($value)) {
+ $val = $value;
+ } else {
+ $val = $this->defaults;
+ }
+ return $this->addSlash(str_replace(
+ array('%2F', '%23'),
+ array('/', '#'), urlencode($val)));
+ }
+}
+?>
\ No newline at end of file
diff --git a/extlib/Net/URL/Mapper/Path.php b/extlib/Net/URL/Mapper/Path.php
new file mode 100644
index 0000000000..b541002c7a
--- /dev/null
+++ b/extlib/Net/URL/Mapper/Path.php
@@ -0,0 +1,430 @@
+
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * The names of the authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category Net
+ * @package Net_URL_Mapper
+ * @author Bertrand Mansion
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version CVS: $Id: Path.php,v 1.1 2007/03/28 10:23:04 mansion Exp $
+ * @link http://pear.php.net/package/Net_URL_Mapper
+ */
+
+require_once 'Net/URL.php';
+require_once 'Net/URL/Mapper/Part/Dynamic.php';
+require_once 'Net/URL/Mapper/Part/Wildcard.php';
+require_once 'Net/URL/Mapper/Part/Fixed.php';
+
+class Net_URL_Mapper_Path
+{
+ private $path = '';
+ private $N = 0;
+ public $token;
+ public $value;
+ private $line = 1;
+ private $state = 1;
+
+
+ protected $alias;
+ protected $rules = array();
+ protected $defaults = array();
+ protected $parts = array();
+ protected $rule;
+ protected $format;
+ protected $minKeys;
+ protected $maxKeys;
+ protected $fixed = true;
+ protected $required;
+
+ public function __construct($path = '', $defaults = array(), $rules = array())
+ {
+ $this->path = '/'.trim(Net_URL::resolvePath($path), '/');
+ $this->setDefaults($defaults);
+ $this->setRules($rules);
+
+ try {
+ $this->parsePath();
+ } catch (Exception $e) {
+ // The path could not be parsed correctly, treat it as fixed
+ $this->fixed = true;
+ $part = self::createPart(Net_URL_Mapper_Part::FIXED, $this->path, $this->path);
+ $this->parts = array($part);
+ }
+ $this->getRequired();
+ }
+
+ public function getPath()
+ {
+ return $this->path;
+ }
+
+ protected function parsePath()
+ {
+ while ($this->yylex()) { }
+ }
+
+ /**
+ * Get the path alias
+ * Path aliases can be used instead of full path
+ * @return null|string
+ */
+ public function getAlias()
+ {
+ return $this->alias;
+ }
+
+ /**
+ * Set the path name
+ * @param string Set the path name
+ * @see getAlias()
+ */
+ public function setAlias($alias)
+ {
+ $this->alias = $alias;
+ return $this;
+ }
+
+ /**
+ * Get the path parts default values
+ * @return null|array
+ */
+ public function getDefaults()
+ {
+ return $this->defaults;
+ }
+
+ /**
+ * Set the path parts default values
+ * @param array Associative array with format partname => value
+ */
+ public function setDefaults($defaults)
+ {
+ if (is_array($defaults)) {
+ $this->defaults = $defaults;
+ } else {
+ $this->defaults = array();
+ }
+ }
+
+ /**
+ * Set the path parts default values
+ * @param array Associative array with format partname => value
+ */
+ public function setRules($rules)
+ {
+ if (is_array($rules)) {
+ $this->rules = $rules;
+ } else {
+ $this->rules = array();
+ }
+ }
+
+ /**
+ * Returns the regular expression used to match this path
+ * @return string PERL Regular expression
+ */
+ public function getRule()
+ {
+ if (is_null($this->rule)) {
+ $this->rule = '/^';
+ foreach ($this->parts as $path => $part) {
+ $this->rule .= $part->getRule();
+ }
+ $this->rule .= '$/';
+ }
+ return $this->rule;
+ }
+
+ public function getFormat()
+ {
+ if (is_null($this->format)) {
+ $this->format = '/^';
+ foreach ($this->parts as $path => $part) {
+ $this->format .= $part->getFormat();
+ }
+ $this->format .= '$/';
+ }
+ return $this->format;
+ }
+
+ protected function addPart($part)
+ {
+ if (array_key_exists($part->content, $this->defaults)) {
+ $part->setRequired(false);
+ $part->setDefaults($this->defaults[$part->content]);
+ }
+ if (isset($this->rules[$part->content])) {
+ $part->setRule($this->rules[$part->content]);
+ }
+ $this->rule = null;
+ if ($part->getType() != Net_URL_Mapper_Part::FIXED) {
+ $this->fixed = false;
+ $this->parts[$part->content] = $part;
+ } else {
+ $this->parts[] = $part;
+ }
+ return $part;
+ }
+
+ public static function createPart($type, $content, $path)
+ {
+ switch ($type) {
+ case Net_URL_Mapper_Part::DYNAMIC:
+ return new Net_URL_Mapper_Part_Dynamic($content, $path);
+ break;
+ case Net_URL_Mapper_Part::WILDCARD:
+ return new Net_URL_Mapper_Part_Wildcard($content, $path);
+ break;
+ default:
+ return new Net_URL_Mapper_Part_Fixed($content, $path);
+ }
+ }
+
+ /**
+ * Checks whether the path contains the given part by name
+ * If value parameter is given, the part also checks if the
+ * given value conforms to the part rule.
+ * @param string Part name
+ * @param mixed The value to check against
+ */
+ public function hasKey($partName, $value = null)
+ {
+ if (array_key_exists($partName, $this->parts)) {
+ if (!is_null($value) && $value !== false) {
+ return $this->parts[$partName]->match($value);
+ } else {
+ return true;
+ }
+ } elseif (array_key_exists($partName, $this->defaults) &&
+ $value == $this->defaults[$partName]) {
+ return true;
+ }
+ return false;
+ }
+
+ public function generate($values = array(), $qstring = array(), $anchor = '')
+ {
+ $path = '';
+ foreach ($this->parts as $part) {
+ $path .= $part->generate($values);
+ }
+ $path = '/'.trim(Net_URL::resolvePath($path), '/');
+ if (!empty($qstring)) {
+ $path .= '?'.http_build_query($qstring);
+ }
+ if (!empty($anchor)) {
+ $path .= '#'.ltrim($anchor, '#');
+ }
+ return $path;
+ }
+
+ public function getRequired()
+ {
+ if (!isset($this->required)) {
+ $req = array();
+ foreach ($this->parts as $part) {
+ if ($part->isRequired()) {
+ $req[] = $part->content;
+ }
+ }
+ $this->required = $req;
+ }
+ return $this->required;
+ }
+
+ public function getMaxKeys()
+ {
+ if (is_null($this->maxKeys)) {
+ $this->maxKeys = count($this->required);
+ $this->maxKeys += count($this->defaults);
+ }
+ return $this->maxKeys;
+ }
+
+
+
+
+ private $_yy_state = 1;
+ private $_yy_stack = array();
+
+ function yylex()
+ {
+ return $this->{'yylex' . $this->_yy_state}();
+ }
+
+ function yypushstate($state)
+ {
+ array_push($this->_yy_stack, $this->_yy_state);
+ $this->_yy_state = $state;
+ }
+
+ function yypopstate()
+ {
+ $this->_yy_state = array_pop($this->_yy_stack);
+ }
+
+ function yybegin($state)
+ {
+ $this->_yy_state = $state;
+ }
+
+
+
+ function yylex1()
+ {
+ $tokenMap = array (
+ 1 => 1,
+ 3 => 1,
+ 5 => 1,
+ 7 => 1,
+ 9 => 1,
+ );
+ if ($this->N >= strlen($this->path)) {
+ return false; // end of input
+ }
+ $yy_global_pattern = "/^(\/?:\/?\\(([a-zA-Z0-9_]+)\\))|^(\/?\\*\/?\\(([a-zA-Z0-9_]+)\\))|^(\/?:([a-zA-Z0-9_]+))|^(\/?\\*([a-zA-Z0-9_]+))|^(\/?([^\/:*]+))/";
+
+ do {
+ if (preg_match($yy_global_pattern, substr($this->path, $this->N), $yymatches)) {
+ $yysubmatches = $yymatches;
+ $yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
+ if (!count($yymatches)) {
+ throw new Exception('Error: lexing failed because a rule matched' .
+ 'an empty string. Input "' . substr($this->path,
+ $this->N, 5) . '... state START');
+ }
+ next($yymatches); // skip global match
+ $this->token = key($yymatches); // token number
+ if ($tokenMap[$this->token]) {
+ // extract sub-patterns for passing to lex function
+ $yysubmatches = array_slice($yysubmatches, $this->token + 1,
+ $tokenMap[$this->token]);
+ } else {
+ $yysubmatches = array();
+ }
+ $this->value = current($yymatches); // token value
+ $r = $this->{'yy_r1_' . $this->token}($yysubmatches);
+ if ($r === null) {
+ $this->N += strlen($this->value);
+ $this->line += substr_count("\n", $this->value);
+ // accept this token
+ return true;
+ } elseif ($r === true) {
+ // we have changed state
+ // process this token in the new state
+ return $this->yylex();
+ } elseif ($r === false) {
+ $this->N += strlen($this->value);
+ $this->line += substr_count("\n", $this->value);
+ if ($this->N >= strlen($this->path)) {
+ return false; // end of input
+ }
+ // skip this token
+ continue;
+ } else { $yy_yymore_patterns = array(
+ 1 => "^(\/?\\*\/?\\(([a-zA-Z0-9_]+)\\))|^(\/?:([a-zA-Z0-9_]+))|^(\/?\\*([a-zA-Z0-9_]+))|^(\/?([^\/:*]+))",
+ 3 => "^(\/?:([a-zA-Z0-9_]+))|^(\/?\\*([a-zA-Z0-9_]+))|^(\/?([^\/:*]+))",
+ 5 => "^(\/?\\*([a-zA-Z0-9_]+))|^(\/?([^\/:*]+))",
+ 7 => "^(\/?([^\/:*]+))",
+ 9 => "",
+ );
+
+ // yymore is needed
+ do {
+ if (!strlen($yy_yymore_patterns[$this->token])) {
+ throw new Exception('cannot do yymore for the last token');
+ }
+ if (preg_match($yy_yymore_patterns[$this->token],
+ substr($this->path, $this->N), $yymatches)) {
+ $yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
+ next($yymatches); // skip global match
+ $this->token = key($yymatches); // token number
+ $this->value = current($yymatches); // token value
+ $this->line = substr_count("\n", $this->value);
+ }
+ } while ($this->{'yy_r1_' . $this->token}() !== null);
+ // accept
+ $this->N += strlen($this->value);
+ $this->line += substr_count("\n", $this->value);
+ return true;
+ }
+ } else {
+ throw new Exception('Unexpected input at line' . $this->line .
+ ': ' . $this->path[$this->N]);
+ }
+ break;
+ } while (true);
+ } // end function
+
+
+ const START = 1;
+ function yy_r1_1($yy_subpatterns)
+ {
+
+ $c = $yy_subpatterns[0];
+ $part = self::createPart(Net_URL_Mapper_Part::DYNAMIC, $c, $this->value);
+ $this->addPart($part);
+ }
+ function yy_r1_3($yy_subpatterns)
+ {
+
+ $c = $yy_subpatterns[0];
+ $part = self::createPart(Net_URL_Mapper_Part::WILDCARD, $c, $this->value);
+ $this->addPart($part);
+ }
+ function yy_r1_5($yy_subpatterns)
+ {
+
+ $c = $yy_subpatterns[0];
+ $part = self::createPart(Net_URL_Mapper_Part::DYNAMIC, $c, $this->value);
+ $this->addPart($part);
+ }
+ function yy_r1_7($yy_subpatterns)
+ {
+
+ $c = $yy_subpatterns[0];
+ $part = self::createPart(Net_URL_Mapper_Part::WILDCARD, $c, $this->value);
+ $this->addPart($part);
+ }
+ function yy_r1_9($yy_subpatterns)
+ {
+
+ $c = $yy_subpatterns[0];
+ $part = self::createPart(Net_URL_Mapper_Part::FIXED, $c, $this->value);
+ $this->addPart($part);
+ }
+
+}
+
+?>
\ No newline at end of file
diff --git a/htaccess.sample b/htaccess.sample
index 5f9827f963..634900dbf6 100644
--- a/htaccess.sample
+++ b/htaccess.sample
@@ -4,165 +4,9 @@ RewriteEngine On
RewriteBase /mublog/
-RewriteRule ^$ index.php?action=public [L,QSA]
-RewriteRule ^rss$ index.php?action=publicrss [L,QSA]
-RewriteRule ^xrds$ index.php?action=publicxrds [L,QSA]
-RewriteRule ^featuredrss$ index.php?action=featuredrss [L,QSA]
-RewriteRule ^favoritedrss$ index.php?action=favoritedrss [L,QSA]
-RewriteRule ^opensearch/people$ index.php?action=opensearch&type=people [L,QSA]
-RewriteRule ^opensearch/notice$ index.php?action=opensearch&type=notice [L,QSA]
-
-RewriteRule ^doc/about$ index.php?action=doc&title=about [L,QSA]
-RewriteRule ^doc/contact$ index.php?action=doc&title=contact [L,QSA]
-RewriteRule ^doc/faq$ index.php?action=doc&title=faq [L,QSA]
-RewriteRule ^doc/help$ index.php?action=doc&title=help [L,QSA]
-RewriteRule ^doc/im$ index.php?action=doc&title=im [L,QSA]
-RewriteRule ^doc/openid$ index.php?action=doc&title=openid [L,QSA]
-RewriteRule ^doc/openmublog$ index.php?action=doc&title=openmublog [L,QSA]
-RewriteRule ^doc/privacy$ index.php?action=doc&title=privacy [L,QSA]
-RewriteRule ^doc/source$ index.php?action=doc&title=source [L,QSA]
-RewriteRule ^doc/tags$ index.php?action=doc&title=tags [L,QSA]
-RewriteRule ^doc/groups$ index.php?action=doc&title=groups [L,QSA]
-RewriteRule ^doc/sms$ index.php?action=doc&title=sms [L,QSA]
-
-RewriteRule ^facebook/$ index.php?action=facebookhome [L,QSA]
-RewriteRule ^facebook/index.php$ index.php?action=facebookhome [L,QSA]
-RewriteRule ^facebook/settings.php$ index.php?action=facebooksettings [L,QSA]
-RewriteRule ^facebook/invite.php$ index.php?action=facebookinvite [L,QSA]
-RewriteRule ^facebook/remove$ index.php?action=facebookremove [L,QSA]
-
-RewriteRule ^main/login$ index.php?action=login [L,QSA]
-RewriteRule ^main/logout$ index.php?action=logout [L,QSA]
-RewriteRule ^main/register/(.*)$ index.php?action=register&code=$1 [L,QSA]
-RewriteRule ^main/register$ index.php?action=register [L,QSA]
-RewriteRule ^main/openid$ index.php?action=openidlogin [L,QSA]
-RewriteRule ^main/remote$ index.php?action=remotesubscribe [L,QSA]
-
-RewriteRule ^main/subscribe$ index.php?action=subscribe [L,QSA]
-RewriteRule ^main/unsubscribe$ index.php?action=unsubscribe [L,QSA]
-RewriteRule ^main/confirmaddress$ index.php?action=confirmaddress [L,QSA]
-RewriteRule ^main/confirmaddress/(.*)$ index.php?action=confirmaddress&code=$1 [L,QSA]
-RewriteRule ^main/recoverpassword$ index.php?action=recoverpassword [L,QSA]
-RewriteRule ^main/recoverpassword/(.*)$ index.php?action=recoverpassword&code=$1 [L,QSA]
-RewriteRule ^main/invite$ index.php?action=invite [L,QSA]
-
-RewriteRule ^main/favor$ index.php?action=favor [L,QSA]
-RewriteRule ^main/disfavor$ index.php?action=disfavor [L,QSA]
-
-RewriteRule ^main/sup$ index.php?action=sup [L,QSA]
-
-RewriteRule ^main/tagother$ index.php?action=tagother [L,QSA]
-
-RewriteRule ^main/block$ index.php?action=block [L,QSA]
-
-RewriteRule ^settings/profile$ index.php?action=profilesettings [L,QSA]
-RewriteRule ^settings/avatar$ index.php?action=avatarsettings [L,QSA]
-RewriteRule ^settings/password$ index.php?action=passwordsettings [L,QSA]
-RewriteRule ^settings/openid$ index.php?action=openidsettings [L,QSA]
-RewriteRule ^settings/im$ index.php?action=imsettings [L,QSA]
-RewriteRule ^settings/email$ index.php?action=emailsettings [L,QSA]
-RewriteRule ^settings/sms$ index.php?action=smssettings [L,QSA]
-RewriteRule ^settings/twitter$ index.php?action=twittersettings [L,QSA]
-RewriteRule ^settings/other$ index.php?action=othersettings [L,QSA]
-
-RewriteRule ^search/group$ index.php?action=groupsearch [L,QSA]
-RewriteRule ^search/people$ index.php?action=peoplesearch [L,QSA]
-RewriteRule ^search/notice$ index.php?action=noticesearch [L,QSA]
-RewriteRule ^search/notice/rss$ index.php?action=noticesearchrss [L,QSA]
-
-RewriteRule ^notice/new$ index.php?action=newnotice [L,QSA]
-RewriteRule ^notice/(\d+)$ index.php?action=shownotice¬ice=$1 [L,QSA]
-RewriteRule ^notice/delete/((\d+))?$ index.php?action=deletenotice¬ice=$2 [L,QSA]
-RewriteRule ^notice/delete$ index.php?action=deletenotice [L,QSA]
-
-RewriteRule ^message/new$ index.php?action=newmessage [L,QSA]
-RewriteRule ^message/(\d+)$ index.php?action=showmessage&message=$1 [L,QSA]
-
-RewriteRule ^user/(\d+)$ index.php?action=userbyid&id=$1 [L,QSA]
-
-RewriteRule ^tags/?$ index.php?action=publictagcloud [L,QSA]
-RewriteRule ^tag/([a-zA-Z0-9]+)/rss$ index.php?action=tagrss&tag=$1 [L,QSA]
-RewriteRule ^tag(/(.*))?$ index.php?action=tag&tag=$2 [L,QSA]
-
-RewriteRule ^peopletag/([a-zA-Z0-9]+)$ index.php?action=peopletag&tag=$1 [L,QSA]
-
-RewriteRule ^featured/?$ index.php?action=featured [L,QSA]
-RewriteRule ^favorited/?$ index.php?action=favorited [L,QSA]
-
-RewriteRule ^group/new$ index.php?action=newgroup [L,QSA]
-RewriteRule ^group/([a-zA-Z0-9]+)/edit$ index.php?action=editgroup&nickname=$1 [L,QSA]
-RewriteRule ^group/([a-zA-Z0-9]+)/join$ index.php?action=joingroup&nickname=$1 [L,QSA]
-RewriteRule ^group/([a-zA-Z0-9]+)/leave$ index.php?action=leavegroup&nickname=$1 [L,QSA]
-RewriteRule ^group/([a-zA-Z0-9]+)/members$ index.php?action=groupmembers&nickname=$1 [L,QSA]
-RewriteRule ^group/([a-zA-Z0-9]+)/logo$ index.php?action=grouplogo&nickname=$1 [L,QSA]
-RewriteRule ^group/([0-9]+)/id$ index.php?action=groupbyid&id=$1 [L,QSA]
-RewriteRule ^group/([a-zA-Z0-9]+)/rss$ index.php?action=grouprss&nickname=$1 [L,QSA]
-RewriteRule ^group/([a-zA-Z0-9]+)$ index.php?action=showgroup&nickname=$1 [L,QSA]
-RewriteRule ^group$ index.php?action=groups [L,QSA]
-
-# Twitter-compatible API rewrites
-# XXX: Surely these can be refactored a little -- Zach
-RewriteRule ^api/statuses/public_timeline(.*)$ index.php?action=api&apiaction=statuses&method=public_timeline$1 [L,QSA]
-RewriteRule ^api/statuses/friends_timeline(.*)$ index.php?action=api&apiaction=statuses&method=friends_timeline$1 [L,QSA]
-RewriteRule ^api/statuses/user_timeline/(.*)$ index.php?action=api&apiaction=statuses&method=user_timeline&argument=$1 [L,QSA]
-RewriteRule ^api/statuses/user_timeline(.*)$ index.php?action=api&apiaction=statuses&method=user_timeline$1 [L,QSA]
-RewriteRule ^api/statuses/show/(.*)$ index.php?action=api&apiaction=statuses&method=show&argument=$1 [L,QSA]
-RewriteRule ^api/statuses/update(.*)$ index.php?action=api&apiaction=statuses&method=update$1 [L,QSA]
-RewriteRule ^api/statuses/replies(.*)$ index.php?action=api&apiaction=statuses&method=replies&argument=$1 [L,QSA]
-RewriteRule ^api/statuses/destroy/(.*)$ index.php?action=api&apiaction=statuses&method=destroy&argument=$1 [L,QSA]
-RewriteRule ^api/statuses/friends/(.*)$ index.php?action=api&apiaction=statuses&method=friends&argument=$1 [L,QSA]
-RewriteRule ^api/statuses/friends(.*)$ index.php?action=api&apiaction=statuses&method=friends$1 [L,QSA]
-RewriteRule ^api/statuses/followers/(.*)$ index.php?action=api&apiaction=statuses&method=followers&argument=$1 [L,QSA]
-RewriteRule ^api/statuses/followers(.*)$ index.php?action=api&apiaction=statuses&method=followers$1 [L,QSA]
-RewriteRule ^api/statuses/featured(.*)$ index.php?action=api&apiaction=statuses&method=featured$1 [L,QSA]
-RewriteRule ^api/users/show/(.*)$ index.php?action=api&apiaction=users&method=show&argument=$1 [L,QSA]
-RewriteRule ^api/users/show(.*)$ index.php?action=api&apiaction=users&method=show$1 [L,QSA]
-RewriteRule ^api/direct_messages/sent(.*)$ index.php?action=api&apiaction=direct_messages&method=sent$1 [L,QSA]
-RewriteRule ^api/direct_messages/destroy/(.*)$ index.php?action=api&apiaction=direct_messages&method=destroy&argument=$1 [L,QSA]
-RewriteRule ^api/direct_messages/new(.*)$ index.php?action=api&apiaction=direct_messages&method=create$1 [L,QSA]
-RewriteRule ^api/direct_messages(.*)$ index.php?action=api&apiaction=direct_messages&method=direct_messages$1 [L,QSA]
-RewriteRule ^api/friendships/create/(.*)$ index.php?action=api&apiaction=friendships&method=create&argument=$1 [L,QSA]
-RewriteRule ^api/friendships/destroy/(.*)$ index.php?action=api&apiaction=friendships&method=destroy&argument=$1 [L,QSA]
-RewriteRule ^api/friendships/exists(.*)$ index.php?action=api&apiaction=friendships&method=exists$1 [L,QSA]
-RewriteRule ^api/account/verify_credentials(.*)$ index.php?action=api&apiaction=account&method=verify_credentials$1 [L,QSA]
-RewriteRule ^api/account/end_session$ index.php?action=api&apiaction=account&method=end_session$1 [L,QSA]
-RewriteRule ^api/account/update_location(.*)$ index.php?action=api&apiaction=account&method=update_location$1 [L,QSA]
-RewriteRule ^api/account/update_delivery_device(.*)$ index.php?action=api&apiaction=account&method=update_delivery_device$1 [L,QSA]
-RewriteRule ^api/account/rate_limit_status(.*)$ index.php?action=api&apiaction=account&method=rate_limit_status$1 [L,QSA]
-RewriteRule ^api/favorites/create/(.*)$ index.php?action=api&apiaction=favorites&method=create&argument=$1 [L,QSA]
-RewriteRule ^api/favorites/destroy/(.*)$ index.php?action=api&apiaction=favorites&method=destroy&argument=$1 [L,QSA]
-RewriteRule ^api/favorites/(.*)$ index.php?action=api&apiaction=favorites&method=favorites&argument=$1 [L,QSA]
-RewriteRule ^api/favorites(.*)$ index.php?action=api&apiaction=favorites&method=favorites$1 [L,QSA]
-RewriteRule ^api/notifications/follow/(.*)$ index.php?action=api&apiaction=notifications&method=follow&argument=$1 [L,QSA]
-RewriteRule ^api/notifications/leave/(.*)$ index.php?action=api&apiaction=notifications&method=leave&argument=$1 [L,QSA]
-RewriteRule ^api/blocks/create/(.*)$ index.php?action=api&apiaction=blocks&method=create&argument=$1 [L,QSA]
-RewriteRule ^api/blocks/destroy/(.*)$ index.php?action=api&apiaction=blocks&method=destroy&argument=$1 [L,QSA]
-RewriteRule ^api/help/(.*)$ index.php?action=api&apiaction=help&method=$1 [L,QSA]
-RewriteRule ^api/laconica/version(.*)$ index.php?action=api&apiaction=laconica&method=version$1 [L,QSA]
-RewriteRule ^api/laconica/config(.*)$ index.php?action=api&apiaction=laconica&method=config$1 [L,QSA]
-RewriteRule ^api/laconica/wadl\.xml$ index.php?action=api&apiaction=laconica&method=wadl.xml [L,QSA]
-
-RewriteRule ^(\w+)/subscriptions$ index.php?action=subscriptions&nickname=$1 [L,QSA]
-RewriteRule ^(\w+)/subscriptions/([a-zA-Z0-9]+)$ index.php?action=subscriptions&nickname=$1&tag=$2 [L,QSA]
-RewriteRule ^(\w+)/subscribers/([a-zA-Z0-9]+)$ index.php?action=subscribers&nickname=$1&tag=$2 [L,QSA]
-RewriteRule ^(\w+)/subscribers$ index.php?action=subscribers&nickname=$1 [L,QSA]
-RewriteRule ^(\w+)/nudge$ index.php?action=nudge&nickname=$1 [L,QSA]
-RewriteRule ^(\w+)/xrds$ index.php?action=xrds&nickname=$1 [L,QSA]
-RewriteRule ^(\w+)/rss$ index.php?action=userrss&nickname=$1 [L,QSA]
-RewriteRule ^(\w+)/all$ index.php?action=all&nickname=$1 [L,QSA]
-RewriteRule ^(\w+)/all/rss$ index.php?action=allrss&nickname=$1 [L,QSA]
-RewriteRule ^(\w+)/foaf$ index.php?action=foaf&nickname=$1 [L,QSA]
-RewriteRule ^(\w+)/replies$ index.php?action=replies&nickname=$1 [L,QSA]
-RewriteRule ^(\w+)/replies/rss$ index.php?action=repliesrss&nickname=$1 [L,QSA]
-RewriteRule ^(\w+)/avatar/(original|96|48|24)$ index.php?action=avatarbynickname&nickname=$1&size=$2 [L,QSA]
-RewriteRule ^(\w+)/favorites$ index.php?action=showfavorites&nickname=$1 [L,QSA]
-RewriteRule ^(\w+)/favorites/rss$ index.php?action=favoritesrss&nickname=$1 [L,QSA]
-RewriteRule ^(\w+)/inbox$ index.php?action=inbox&nickname=$1 [L,QSA]
-RewriteRule ^(\w+)/outbox$ index.php?action=outbox&nickname=$1 [L,QSA]
-RewriteRule ^(\w+)/microsummary$ index.php?action=microsummary&nickname=$1 [L,QSA]
-RewriteRule ^(\w+)/groups$ index.php?action=usergroups&nickname=$1 [L,QSA]
-
-RewriteRule ^(\w+)$ index.php?action=showstream&nickname=$1 [L,QSA]
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_FILENAME} !-d
+RewriteRule (.*) index.php?p=$1 [L,QSA]
Order allow,deny
diff --git a/index.php b/index.php
index 0a79b9731d..914ba5bde1 100644
--- a/index.php
+++ b/index.php
@@ -22,70 +22,131 @@ define('LACONICA', true);
require_once INSTALLDIR . '/lib/common.php';
-// get and cache current user
+$user = null;
+$action = null;
-$user = common_current_user();
-
-// initialize language env
-
-common_init_language();
-
-$action = $_REQUEST['action'];
-
-if (!$action || !preg_match('/^[a-zA-Z0-9_-]*$/', $action)) {
- common_redirect(common_local_url('public'));
+function getPath($req)
+{
+ if ((common_config('site', 'fancy') || !array_key_exists('PATH_INFO', $_SERVER))
+ && array_key_exists('p', $req)) {
+ return $req['p'];
+ } else if (array_key_exists('PATH_INFO', $_SERVER)) {
+ return $_SERVER['PATH_INFO'];
+ } else {
+ return null;
+ }
}
-// If the site is private, and they're not on one of the "public"
-// parts of the site, redirect to login
+function handleError($error)
+{
+ if ($error->getCode() == DB_DATAOBJECT_ERROR_NODATA) {
+ return;
+ }
-if (!$user && common_config('site', 'private') &&
- !in_array($action, array('login', 'openidlogin', 'finishopenidlogin',
- 'recoverpassword', 'api', 'doc', 'register'))) {
- common_redirect(common_local_url('login'));
+ common_log(LOG_ERR, "PEAR error: " . $error->getMessage());
+ $msg = sprintf(_('The database for %s isn\'t responding correctly, '.
+ 'so the site won\'t work properly. '.
+ 'The site admins probably know about the problem, '.
+ 'but you can contact them at %s to make sure. '.
+ 'Otherwise, wait a few minutes and try again.'),
+ common_config('site', 'name'),
+ common_config('site', 'email'));
+
+ $dac = new DBErrorAction($msg, 500);
+ $dac->showPage();
+ exit(-1);
}
-$actionfile = INSTALLDIR."/actions/$action.php";
+function main()
+{
+ global $user, $action;
-if (!file_exists($actionfile)) {
- $cac = new ClientErrorAction(_('Unknown action'), 404);
- $cac->showPage();
-} else {
+ // For database errors
- include_once $actionfile;
+ PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'handleError');
+
+ // XXX: we need a little more structure in this script
+
+ // get and cache current user
+
+ $user = common_current_user();
+
+ // initialize language env
+
+ common_init_language();
+
+ $path = getPath($_REQUEST);
+
+ $r = Router::get();
+
+ $args = $r->map($path);
+
+ if (!$args) {
+ $cac = new ClientErrorAction(_('Unknown page'), 404);
+ $cac->showPage();
+ return;
+ }
+
+ $args = array_merge($args, $_REQUEST);
+
+ $action = $args['action'];
+
+ if (!$action || !preg_match('/^[a-zA-Z0-9_-]*$/', $action)) {
+ common_redirect(common_local_url('public'));
+ return;
+ }
+
+ // If the site is private, and they're not on one of the "public"
+ // parts of the site, redirect to login
+
+ if (!$user && common_config('site', 'private') &&
+ !in_array($action, array('login', 'openidlogin', 'finishopenidlogin',
+ 'recoverpassword', 'api', 'doc', 'register'))) {
+ common_redirect(common_local_url('login'));
+ return;
+ }
$action_class = ucfirst($action).'Action';
- $action_obj = new $action_class();
-
- if ($config['db']['mirror'] && $action_obj->isReadOnly()) {
- if (is_array($config['db']['mirror'])) {
- // "load balancing", ha ha
- $k = array_rand($config['db']['mirror']);
-
- $mirror = $config['db']['mirror'][$k];
- } else {
- $mirror = $config['db']['mirror'];
- }
- $config['db']['database'] = $mirror;
- }
-
- try {
- if ($action_obj->prepare($_REQUEST)) {
- $action_obj->handle($_REQUEST);
- }
- } catch (ClientException $cex) {
- $cac = new ClientErrorAction($cex->getMessage(), $cex->getCode());
+ if (!class_exists($action_class)) {
+ $cac = new ClientErrorAction(_('Unknown action'), 404);
$cac->showPage();
- } catch (ServerException $sex) { // snort snort guffaw
- $sac = new ServerErrorAction($sex->getMessage(), $sex->getCode());
- $sac->showPage();
- } catch (Exception $ex) {
- $sac = new ServerErrorAction($ex->getMessage());
- $sac->showPage();
+ } else {
+ $action_obj = new $action_class();
+
+ // XXX: find somewhere for this little block to live
+
+ if (common_config('db', 'mirror') && $action_obj->isReadOnly()) {
+ if (is_array(common_config('db', 'mirror'))) {
+ // "load balancing", ha ha
+ $k = array_rand($config['db']['mirror']);
+
+ $mirror = $config['db']['mirror'][$k];
+ } else {
+ $mirror = $config['db']['mirror'];
+ }
+ $config['db']['database'] = $mirror;
+ }
+
+ try {
+ if ($action_obj->prepare($args)) {
+ $action_obj->handle($args);
+ }
+ } catch (ClientException $cex) {
+ $cac = new ClientErrorAction($cex->getMessage(), $cex->getCode());
+ $cac->showPage();
+ } catch (ServerException $sex) { // snort snort guffaw
+ $sac = new ServerErrorAction($sex->getMessage(), $sex->getCode());
+ $sac->showPage();
+ } catch (Exception $ex) {
+ $sac = new ServerErrorAction($ex->getMessage());
+ $sac->showPage();
+ }
}
}
+main();
+
// XXX: cleanup exit() calls or add an exit handler so
// this always gets called
diff --git a/js/flowplayer-3.0.5.min.js b/js/flowplayer-3.0.5.min.js
new file mode 100644
index 0000000000..b1c33150ac
--- /dev/null
+++ b/js/flowplayer-3.0.5.min.js
@@ -0,0 +1,24 @@
+/**
+ * flowplayer.js 3.0.5. The Flowplayer API
+ *
+ * Copyright 2009 Flowplayer Oy
+ *
+ * This file is part of Flowplayer.
+ *
+ * Flowplayer is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Flowplayer 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Flowplayer. If not, see .
+ *
+ * Version: 3.0.5 - Tue Feb 03 2009 13:14:17 GMT-0000 (GMT+00:00)
+ */
+(function(){function log(args){console.log("$f.fireEvent",[].slice.call(args));}function clone(obj){if(!obj||typeof obj!='object'){return obj;}var temp=new obj.constructor();for(var key in obj){if(obj.hasOwnProperty(key)){temp[key]=clone(obj[key]);}}return temp;}function each(obj,fn){if(!obj){return;}var name,i=0,length=obj.length;if(length===undefined){for(name in obj){if(fn.call(obj[name],name,obj[name])===false){break;}}}else{for(var value=obj[0];i1){var swf=arguments[1];var conf=(arguments.length==3)?arguments[2]:{};if(typeof arg=='string'){if(arg.indexOf(".")!=-1){var instances=[];each(select(arg),function(){instances.push(new Player(this,clone(swf),clone(conf)));});return new Iterator(instances);}else{var node=el(arg);return new Player(node!==null?node:arg,swf,conf);}}else if(arg){return new Player(arg,swf,conf);}}return null;};extend(window.$f,{fireEvent:function(id,evt,a0,a1,a2){var p=$f(id);return p?p._fireEvent(evt,a0,a1,a2):null;},addPlugin:function(name,fn){Player.prototype[name]=fn;return $f;},each:each,extend:extend});if(document.all){window.onbeforeunload=function(){$f("*").each(function(){if(this.isLoaded()){this.close();}});};}if(typeof jQuery=='function'){jQuery.prototype.flowplayer=function(params,conf){if(!arguments.length||typeof arguments[0]=='number'){var arr=[];this.each(function(){var p=$f(this);if(p){arr.push(p);}});return arguments.length?arr[arguments[0]]:new Iterator(arr);}return this.each(function(){$f(this,clone(params),conf?clone(conf):{});});};}})();(function(){var jQ=typeof jQuery=='function';function isDomReady(){if(domReady.done){return false;}var d=document;if(d&&d.getElementsByTagName&&d.getElementById&&d.body){clearInterval(domReady.timer);domReady.timer=null;for(var i=0;i';if(p.w3c||ie){html+='';}var e=extend({},p);e.width=e.height=e.id=e.w3c=e.src=null;for(var k in e){if(e[k]!==null){html+='';}}var vars="";if(c){for(var key in c){if(c[key]!==null){vars+=key+'='+(typeof c[key]=='object'?asString(c[key]):c[key])+'&';}}vars=vars.substring(0,vars.length-1);html+='';}html+="";return html;}function Flash(root,opts,flashvars){var version=flashembed.getVersion();extend(this,{getContainer:function(){return root;},getConf:function(){return conf;},getVersion:function(){return version;},getFlashvars:function(){return flashvars;},getApi:function(){return root.firstChild;},getHTML:function(){return getHTML(opts,flashvars);}});var required=opts.version;var express=opts.expressInstall;var ok=!required||flashembed.isSupported(required);if(ok){opts.onFail=opts.version=opts.expressInstall=null;root.innerHTML=getHTML(opts,flashvars);}else if(required&&express&&flashembed.isSupported([6,65])){extend(opts,{src:express});flashvars={MMredirectURL:location.href,MMplayerType:'PlugIn',MMdoctitle:document.title};root.innerHTML=getHTML(opts,flashvars);}else{if(root.innerHTML.replace(/\s/g,'')!==''){}else{root.innerHTML="Flash version "+required+" or greater is required
"+""+(version[0]>0?"Your version is "+version:"You have no flash plugin installed")+"
"+"Download latest version from here
";}}if(!ok&&opts.onFail){var ret=opts.onFail.call(this);if(typeof ret=='string'){root.innerHTML=ret;}}}window.flashembed=function(root,conf,flashvars){if(typeof root=='string'){var el=document.getElementById(root);if(el){root=el;}else{domReady(function(){flashembed(root,conf,flashvars);});return;}}if(!root){return;}var opts={width:'100%',height:'100%',allowfullscreen:true,allowscriptaccess:'always',quality:'high',version:null,onFail:null,expressInstall:null,w3c:false};if(typeof conf=='string'){conf={src:conf};}extend(opts,conf);return new Flash(root,opts,flashvars);};extend(window.flashembed,{getVersion:function(){var version=[0,0];if(navigator.plugins&&typeof navigator.plugins["Shockwave Flash"]=="object"){var _d=navigator.plugins["Shockwave Flash"].description;if(typeof _d!="undefined"){_d=_d.replace(/^.*\s+(\S+\s+\S+$)/,"$1");var _m=parseInt(_d.replace(/^(.*)\..*$/,"$1"),10);var _r=/r/.test(_d)?parseInt(_d.replace(/^.*r(.*)$/,"$1"),10):0;version=[_m,_r];}}else if(window.ActiveXObject){try{var _a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");}catch(e){try{_a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");version=[6,0];_a.AllowScriptAccess="always";}catch(ee){if(version[0]==6){return;}}try{_a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");}catch(eee){}}if(typeof _a=="object"){_d=_a.GetVariable("$version");if(typeof _d!="undefined"){_d=_d.replace(/^\S+\s+(.*)$/,"$1").split(",");version=[parseInt(_d[0],10),parseInt(_d[2],10)];}}}return version;},isSupported:function(version){var now=flashembed.getVersion();var ret=(now[0]>version[0])||(now[0]==version[0]&&now[1]>=version[1]);return ret;},domReady:domReady,asString:asString,getHTML:getHTML});if(jQ){jQuery.prototype.flashembed=function(conf,flashvars){return this.each(function(){flashembed(this,conf,flashvars);});};}})();
\ No newline at end of file
diff --git a/js/identica-badge.js b/js/identica-badge.js
index 5c586b5d6a..869230b7a4 100644
--- a/js/identica-badge.js
+++ b/js/identica-badge.js
@@ -1,4 +1,5 @@
// identica badge -- updated to work with the native API, 12-4-2008
+// Modified to point to Identi.ca, 2-20-2009 by Zach
// copyright Kent Brewster 2008
// see http://kentbrewster.com/identica-badge for info
( function() {
@@ -127,7 +128,7 @@
var a = document.createElement('A');
a.innerHTML = 'get this';
a.target = '_blank';
- a.href = 'http://kentbrewster.com/identica-badge';
+ a.href = 'http://identica/doc/badge';
$.s.f.appendChild(a);
$.s.appendChild($.s.f);
$.f.getUser();
diff --git a/js/jquery.js b/js/jquery.js
index fc06ace272..94e9c1755e 100644
--- a/js/jquery.js
+++ b/js/jquery.js
@@ -1,13 +1,13 @@
/*!
- * jQuery JavaScript Library v1.3
+ * jQuery JavaScript Library v1.3.1
* http://jquery.com/
*
* Copyright (c) 2009 John Resig
* Dual licensed under the MIT and GPL licenses.
* http://docs.jquery.com/License
*
- * Date: 2009-01-13 12:50:31 -0500 (Tue, 13 Jan 2009)
- * Revision: 6104
+ * Date: 2009-01-21 20:42:16 -0500 (Wed, 21 Jan 2009)
+ * Revision: 6158
*/
(function(){
@@ -60,20 +60,16 @@ jQuery.fn = jQuery.prototype = {
else {
var elem = document.getElementById( match[3] );
- // Make sure an element was located
- if ( elem ){
- // Handle the case where IE and Opera return items
- // by name instead of ID
- if ( elem.id != match[3] )
- return jQuery().find( selector );
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem && elem.id != match[3] )
+ return jQuery().find( selector );
- // Otherwise, we inject the element directly into the jQuery object
- var ret = jQuery( elem );
- ret.context = document;
- ret.selector = selector;
- return ret;
- }
- selector = [];
+ // Otherwise, we inject the element directly into the jQuery object
+ var ret = jQuery( elem || [] );
+ ret.context = document;
+ ret.selector = selector;
+ return ret;
}
// HANDLE: $(expr, [context])
@@ -99,7 +95,7 @@ jQuery.fn = jQuery.prototype = {
selector: "",
// The current version of jQuery being used
- jquery: "1.3",
+ jquery: "1.3.1",
// The number of elements contained in the matched element set
size: function() {
@@ -634,8 +630,8 @@ jQuery.extend({
// check if an element is in a (or is an) XML document
isXMLDoc: function( elem ) {
- return elem.documentElement && !elem.body ||
- elem.tagName && elem.ownerDocument && !elem.ownerDocument.body;
+ return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
+ !!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument );
},
// Evalulates a script in a global context
@@ -725,7 +721,7 @@ jQuery.extend({
// internal only, use hasClass("class")
has: function( elem, className ) {
- return jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
+ return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
}
},
@@ -999,9 +995,11 @@ jQuery.extend({
var attributeNode = elem.getAttributeNode( "tabIndex" );
return attributeNode && attributeNode.specified
? attributeNode.value
- : elem.nodeName.match(/^(a|area|button|input|object|select|textarea)$/i)
+ : elem.nodeName.match(/(button|input|object|select|textarea)/i)
? 0
- : undefined;
+ : elem.nodeName.match(/^(a|area)$/i) && elem.href
+ ? 0
+ : undefined;
}
return elem[ name ];
@@ -1397,14 +1395,14 @@ jQuery.fn.extend({
});
}
});/*!
- * Sizzle CSS Selector Engine - v0.9.1
+ * 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 chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|[^[\]]+)+\]|\\.|[^ >+~,(\[]+)+|[>+~])(\s*,\s*)?/g,
+var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]+['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[]+)+|[>+~])(\s*,\s*)?/g,
done = 0,
toString = Object.prototype.toString;
@@ -1433,40 +1431,27 @@ var Sizzle = function(selector, context, results, seed) {
}
}
- if ( parts.length > 1 && Expr.match.POS.exec( selector ) ) {
+ if ( parts.length > 1 && origPOS.exec( selector ) ) {
if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
- var later = "", match;
-
- // Position selectors must be done after the filter
- while ( (match = Expr.match.POS.exec( selector )) ) {
- later += match[0];
- selector = selector.replace( Expr.match.POS, "" );
- }
-
- set = Sizzle.filter( later, Sizzle( /\s$/.test(selector) ? selector + "*" : selector, context ) );
+ set = posProcess( parts[0] + parts[1], context );
} else {
set = Expr.relative[ parts[0] ] ?
[ context ] :
Sizzle( parts.shift(), context );
while ( parts.length ) {
- var tmpSet = [];
-
selector = parts.shift();
+
if ( Expr.relative[ selector ] )
selector += parts.shift();
- for ( var i = 0, l = set.length; i < l; i++ ) {
- Sizzle( selector, set[i], tmpSet );
- }
-
- set = tmpSet;
+ set = posProcess( selector, set );
}
}
} else {
var ret = seed ?
{ expr: parts.pop(), set: makeArray(seed) } :
- Sizzle.find( parts.pop(), parts.length === 1 && context.parentNode ? context.parentNode : context );
+ Sizzle.find( parts.pop(), parts.length === 1 && context.parentNode ? context.parentNode : context, isXML(context) );
set = Sizzle.filter( ret.expr, ret.set );
if ( parts.length > 0 ) {
@@ -1531,7 +1516,7 @@ Sizzle.matches = function(expr, set){
return Sizzle(expr, null, null, set);
};
-Sizzle.find = function(expr, context){
+Sizzle.find = function(expr, context, isXML){
var set, match;
if ( !expr ) {
@@ -1546,7 +1531,7 @@ Sizzle.find = function(expr, context){
if ( left.substr( left.length - 1 ) !== "\\" ) {
match[1] = (match[1] || "").replace(/\\/g, "");
- set = Expr.find[ type ]( match, context );
+ set = Expr.find[ type ]( match, context, isXML );
if ( set != null ) {
expr = expr.replace( Expr.match[ type ], "" );
break;
@@ -1568,7 +1553,7 @@ Sizzle.filter = function(expr, set, inplace, not){
while ( expr && set.length ) {
for ( var type in Expr.filter ) {
if ( (match = Expr.match[ type ].exec( expr )) != null ) {
- var filter = Expr.filter[ type ], goodArray = null, goodPos = 0, found, item;
+ var filter = Expr.filter[ type ], found, item;
anyFound = false;
if ( curLoop == result ) {
@@ -1582,26 +1567,13 @@ Sizzle.filter = function(expr, set, inplace, not){
anyFound = found = true;
} else if ( match === true ) {
continue;
- } else if ( match[0] === true ) {
- goodArray = [];
- var last = null, elem;
- for ( var i = 0; (elem = curLoop[i]) !== undefined; i++ ) {
- if ( elem && last !== elem ) {
- goodArray.push( elem );
- last = elem;
- }
- }
}
}
if ( match ) {
- for ( var i = 0; (item = curLoop[i]) !== undefined; i++ ) {
+ for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
if ( item ) {
- if ( goodArray && item != goodArray[goodPos] ) {
- goodPos++;
- }
-
- found = filter( item, match, goodPos, goodArray );
+ found = filter( item, match, i, curLoop );
var pass = not ^ !!found;
if ( inplace && found != null ) {
@@ -1739,14 +1711,16 @@ var Expr = Sizzle.selectors = {
}
},
find: {
- ID: function(match, context){
- if ( context.getElementById ) {
+ ID: function(match, context, isXML){
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
var m = context.getElementById(match[1]);
return m ? [m] : [];
}
},
- NAME: function(match, context){
- return context.getElementsByName ? context.getElementsByName(match[1]) : null;
+ NAME: function(match, context, isXML){
+ if ( typeof context.getElementsByName !== "undefined" && !isXML ) {
+ return context.getElementsByName(match[1]);
+ }
},
TAG: function(match, context){
return context.getElementsByTagName(match[1]);
@@ -1756,12 +1730,15 @@ var Expr = Sizzle.selectors = {
CLASS: function(match, curLoop, inplace, result, not){
match = " " + match[1].replace(/\\/g, "") + " ";
- for ( var i = 0; curLoop[i]; i++ ) {
- if ( not ^ (" " + curLoop[i].className + " ").indexOf(match) >= 0 ) {
- if ( !inplace )
- result.push( curLoop[i] );
- } else if ( inplace ) {
- curLoop[i] = false;
+ var elem;
+ for ( var i = 0; (elem = curLoop[i]) != null; i++ ) {
+ if ( elem ) {
+ if ( not ^ (" " + elem.className + " ").indexOf(match) >= 0 ) {
+ if ( !inplace )
+ result.push( elem );
+ } else if ( inplace ) {
+ curLoop[i] = false;
+ }
}
}
@@ -1771,8 +1748,8 @@ var Expr = Sizzle.selectors = {
return match[1].replace(/\\/g, "");
},
TAG: function(match, curLoop){
- for ( var i = 0; !curLoop[i]; i++ ){}
- return isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
+ for ( var i = 0; curLoop[i] === false; i++ ){}
+ return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
},
CHILD: function(match){
if ( match[1] == "nth" ) {
@@ -1792,7 +1769,7 @@ var Expr = Sizzle.selectors = {
return match;
},
ATTR: function(match){
- var name = match[1];
+ var name = match[1].replace(/\\/g, "");
if ( Expr.attrMap[name] ) {
match[1] = Expr.attrMap[name];
@@ -1916,7 +1893,7 @@ var Expr = Sizzle.selectors = {
CHILD: function(elem, match){
var type = match[1], parent = elem.parentNode;
- var doneName = "child" + parent.childNodes.length;
+ var doneName = match[0];
if ( parent && (!parent[ doneName ] || !elem.nodeIndex) ) {
var count = 1;
@@ -1985,7 +1962,7 @@ var Expr = Sizzle.selectors = {
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];
return result == null ?
- false :
+ type === "!=" :
type === "=" ?
value === check :
type === "*=" ?
@@ -2014,6 +1991,8 @@ var Expr = Sizzle.selectors = {
}
};
+var origPOS = Expr.match.POS;
+
for ( var type in Expr.match ) {
Expr.match[ type ] = RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
}
@@ -2072,15 +2051,15 @@ try {
// The workaround has to do additional checks after a getElementById
// Which slows things down for other browsers (hence the branching)
if ( !!document.getElementById( id ) ) {
- Expr.find.ID = function(match, context){
- if ( context.getElementById ) {
+ Expr.find.ID = function(match, context, isXML){
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
var m = context.getElementById(match[1]);
- return m ? m.id === match[1] || m.getAttributeNode && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
+ return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
}
};
Expr.filter.ID = function(elem, match){
- var node = elem.getAttributeNode && elem.getAttributeNode("id");
+ var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
return elem.nodeType === 1 && node && node.nodeValue === match;
};
}
@@ -2120,7 +2099,7 @@ try {
// Check to see if an attribute returns normalized href attributes
div.innerHTML = "";
- if ( div.firstChild.getAttribute("href") !== "#" ) {
+ if ( div.firstChild && div.firstChild.getAttribute("href") !== "#" ) {
Expr.attrHandle.href = function(elem){
return elem.getAttribute("href", 2);
};
@@ -2128,12 +2107,21 @@ try {
})();
if ( document.querySelectorAll ) (function(){
- var oldSizzle = Sizzle;
+ var oldSizzle = Sizzle, div = document.createElement("div");
+ div.innerHTML = "";
+
+ // Safari can't handle uppercase or unicode characters when
+ // in quirks mode.
+ if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
+ return;
+ }
Sizzle = function(query, context, extra, seed){
context = context || document;
- if ( !seed && context.nodeType === 9 ) {
+ // Only use querySelectorAll on non-XML documents
+ // (ID selectors don't work in non-HTML documents)
+ if ( !seed && context.nodeType === 9 && !isXML(context) ) {
try {
return makeArray( context.querySelectorAll(query), extra );
} catch(e){}
@@ -2148,7 +2136,7 @@ if ( document.querySelectorAll ) (function(){
Sizzle.matches = oldSizzle.matches;
})();
-if ( document.documentElement.getElementsByClassName ) {
+if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) {
Expr.order.splice(1, 0, "CLASS");
Expr.find.CLASS = function(match, context) {
return context.getElementsByClassName(match[1]);
@@ -2229,8 +2217,28 @@ var contains = document.compareDocumentPosition ? function(a, b){
};
var isXML = function(elem){
- return elem.documentElement && !elem.body ||
- elem.tagName && elem.ownerDocument && !elem.ownerDocument.body;
+ return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
+ !!elem.ownerDocument && isXML( elem.ownerDocument );
+};
+
+var posProcess = function(selector, context){
+ var tmpSet = [], later = "", match,
+ root = context.nodeType ? [context] : context;
+
+ // Position selectors must be done after the filter
+ // And so must :not(positional) so we move all PSEUDOs to the end
+ while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
+ later += match[0];
+ selector = selector.replace( Expr.match.PSEUDO, "" );
+ }
+
+ selector = Expr.relative[selector] ? selector + "*" : selector;
+
+ for ( var i = 0, l = root.length; i < l; i++ ) {
+ Sizzle( selector, root[i], tmpSet );
+ }
+
+ return Sizzle.filter( later, tmpSet );
};
// EXPOSE
@@ -2681,13 +2689,13 @@ jQuery.Event = function( src ){
if( src && src.type ){
this.originalEvent = src;
this.type = src.type;
- this.timeStamp = src.timeStamp;
// Event type
}else
this.type = src;
- if( !this.timeStamp )
- this.timeStamp = now();
+ // timeStamp is buggy for some events on Firefox(#3843)
+ // So we won't rely on the native value
+ this.timeStamp = now();
// Mark it as fixed
this[expando] = true;
@@ -2876,9 +2884,8 @@ function liveHandler( event ){
});
jQuery.each(elems, function(){
- if ( !event.isImmediatePropagationStopped() &&
- this.fn.call(this.elem, event, this.fn.data) === false )
- stop = false;
+ if ( this.fn.call(this.elem, event, this.fn.data) === false )
+ stop = false;
});
return stop;
@@ -2942,7 +2949,7 @@ function bindReady(){
// If IE and not an iframe
// continually check to see if the document is ready
- if ( document.documentElement.doScroll && !window.frameElement ) (function(){
+ if ( document.documentElement.doScroll && typeof window.frameElement === "undefined" ) (function(){
if ( jQuery.isReady ) return;
try {
@@ -3477,6 +3484,9 @@ jQuery.extend({
// Fire the complete handlers
complete();
+ if ( isTimeout )
+ xhr.abort();
+
// Stop memory leaks
if ( s.async )
xhr = null;
@@ -3491,14 +3501,8 @@ jQuery.extend({
if ( s.timeout > 0 )
setTimeout(function(){
// Check to see if the request is still happening
- if ( xhr ) {
- if( !requestDone )
- onreadystatechange( "timeout" );
-
- // Cancel the request
- if ( xhr )
- xhr.abort();
- }
+ if ( xhr && !requestDone )
+ onreadystatechange( "timeout" );
}, s.timeout);
}
@@ -3637,6 +3641,7 @@ jQuery.extend({
});
var elemdisplay = {},
+ timerId,
fxAttrs = [
// height animations
[ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
@@ -3859,7 +3864,6 @@ jQuery.extend({
},
timers: [],
- timerId: null,
fx: function( elem, options, prop ){
this.options = options;
@@ -3911,10 +3915,8 @@ jQuery.fx.prototype = {
t.elem = this.elem;
- jQuery.timers.push(t);
-
- if ( t() && jQuery.timerId == null ) {
- jQuery.timerId = setInterval(function(){
+ if ( t() && jQuery.timers.push(t) == 1 ) {
+ timerId = setInterval(function(){
var timers = jQuery.timers;
for ( var i = 0; i < timers.length; i++ )
@@ -3922,8 +3924,7 @@ jQuery.fx.prototype = {
timers.splice(i--, 1);
if ( !timers.length ) {
- clearInterval( jQuery.timerId );
- jQuery.timerId = null;
+ clearInterval( timerId );
}
}, 13);
}
@@ -3989,11 +3990,10 @@ jQuery.fx.prototype = {
if ( this.options.hide || this.options.show )
for ( var p in this.options.curAnim )
jQuery.attr(this.elem.style, p, this.options.orig[p]);
- }
-
- if ( done )
+
// Execute the complete function
this.options.complete.call( this.elem );
+ }
return false;
} else {
@@ -4087,7 +4087,7 @@ jQuery.offset = {
initialize: function() {
if ( this.initialized ) return;
var body = document.body, container = document.createElement('div'), innerDiv, checkDiv, table, td, rules, prop, bodyMarginTop = body.style.marginTop,
- html = '';
+ html = '';
rules = { position: 'absolute', top: 0, left: 0, margin: 0, border: 0, width: '1px', height: '1px', visibility: 'hidden' };
for ( prop in rules ) container.style[prop] = rules[prop];
diff --git a/js/jquery.min.js b/js/jquery.min.js
index 396646c842..c327fae812 100644
--- a/js/jquery.min.js
+++ b/js/jquery.min.js
@@ -1,19 +1,19 @@
/*
- * jQuery JavaScript Library v1.3
+ * jQuery JavaScript Library v1.3.1
* http://jquery.com/
*
* Copyright (c) 2009 John Resig
* Dual licensed under the MIT and GPL licenses.
* http://docs.jquery.com/License
*
- * Date: 2009-01-13 12:50:31 -0500 (Tue, 13 Jan 2009)
- * Revision: 6104
+ * Date: 2009-01-21 20:42:16 -0500 (Wed, 21 Jan 2009)
+ * Revision: 6158
*/
-(function(){var l=this,g,x=l.jQuery,o=l.$,n=l.jQuery=l.$=function(D,E){return new n.fn.init(D,E)},C=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;n.fn=n.prototype={init:function(D,G){D=D||document;if(D.nodeType){this[0]=D;this.length=1;this.context=D;return this}if(typeof D==="string"){var F=C.exec(D);if(F&&(F[1]||!G)){if(F[1]){D=n.clean([F[1]],G)}else{var H=document.getElementById(F[3]);if(H){if(H.id!=F[3]){return n().find(D)}var E=n(H);E.context=document;E.selector=D;return E}D=[]}}else{return n(G).find(D)}}else{if(n.isFunction(D)){return n(document).ready(D)}}if(D.selector&&D.context){this.selector=D.selector;this.context=D.context}return this.setArray(n.makeArray(D))},selector:"",jquery:"1.3",size:function(){return this.length},get:function(D){return D===g?n.makeArray(this):this[D]},pushStack:function(E,G,D){var F=n(E);F.prevObject=this;F.context=this.context;if(G==="find"){F.selector=this.selector+(this.selector?" ":"")+D}else{if(G){F.selector=this.selector+"."+G+"("+D+")"}}return F},setArray:function(D){this.length=0;Array.prototype.push.apply(this,D);return this},each:function(E,D){return n.each(this,E,D)},index:function(D){return n.inArray(D&&D.jquery?D[0]:D,this)},attr:function(E,G,F){var D=E;if(typeof E==="string"){if(G===g){return this[0]&&n[F||"attr"](this[0],E)}else{D={};D[E]=G}}return this.each(function(H){for(E in D){n.attr(F?this.style:this,E,n.prop(this,D[E],F,H,E))}})},css:function(D,E){if((D=="width"||D=="height")&&parseFloat(E)<0){E=g}return this.attr(D,E,"curCSS")},text:function(E){if(typeof E!=="object"&&E!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(E))}var D="";n.each(E||this,function(){n.each(this.childNodes,function(){if(this.nodeType!=8){D+=this.nodeType!=1?this.nodeValue:n.fn.text([this])}})});return D},wrapAll:function(D){if(this[0]){var E=n(D,this[0].ownerDocument).clone();if(this[0].parentNode){E.insertBefore(this[0])}E.map(function(){var F=this;while(F.firstChild){F=F.firstChild}return F}).append(this)}return this},wrapInner:function(D){return this.each(function(){n(this).contents().wrapAll(D)})},wrap:function(D){return this.each(function(){n(this).wrapAll(D)})},append:function(){return this.domManip(arguments,true,function(D){if(this.nodeType==1){this.appendChild(D)}})},prepend:function(){return this.domManip(arguments,true,function(D){if(this.nodeType==1){this.insertBefore(D,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(D){this.parentNode.insertBefore(D,this)})},after:function(){return this.domManip(arguments,false,function(D){this.parentNode.insertBefore(D,this.nextSibling)})},end:function(){return this.prevObject||n([])},push:[].push,find:function(D){if(this.length===1&&!/,/.test(D)){var F=this.pushStack([],"find",D);F.length=0;n.find(D,this[0],F);return F}else{var E=n.map(this,function(G){return n.find(D,G)});return this.pushStack(/[^+>] [^+>]/.test(D)?n.unique(E):E,"find",D)}},clone:function(E){var D=this.map(function(){if(!n.support.noCloneEvent&&!n.isXMLDoc(this)){var H=this.cloneNode(true),G=document.createElement("div");G.appendChild(H);return n.clean([G.innerHTML])[0]}else{return this.cloneNode(true)}});var F=D.find("*").andSelf().each(function(){if(this[h]!==g){this[h]=null}});if(E===true){this.find("*").andSelf().each(function(H){if(this.nodeType==3){return}var G=n.data(this,"events");for(var J in G){for(var I in G[J]){n.event.add(F[H],J,G[J][I],G[J][I].data)}}})}return D},filter:function(D){return this.pushStack(n.isFunction(D)&&n.grep(this,function(F,E){return D.call(F,E)})||n.multiFilter(D,n.grep(this,function(E){return E.nodeType===1})),"filter",D)},closest:function(D){var E=n.expr.match.POS.test(D)?n(D):null;return this.map(function(){var F=this;while(F&&F.ownerDocument){if(E?E.index(F)>-1:n(F).is(D)){return F}F=F.parentNode}})},not:function(D){if(typeof D==="string"){if(f.test(D)){return this.pushStack(n.multiFilter(D,this,true),"not",D)}else{D=n.multiFilter(D,this)}}var E=D.length&&D[D.length-1]!==g&&!D.nodeType;return this.filter(function(){return E?n.inArray(this,D)<0:this!=D})},add:function(D){return this.pushStack(n.unique(n.merge(this.get(),typeof D==="string"?n(D):n.makeArray(D))))},is:function(D){return !!D&&n.multiFilter(D,this).length>0},hasClass:function(D){return !!D&&this.is("."+D)},val:function(J){if(J===g){var D=this[0];if(D){if(n.nodeName(D,"option")){return(D.attributes.value||{}).specified?D.value:D.text}if(n.nodeName(D,"select")){var H=D.selectedIndex,K=[],L=D.options,G=D.type=="select-one";if(H<0){return null}for(var E=G?H:0,I=G?H+1:L.length;E=0||n.inArray(this.name,J)>=0)}else{if(n.nodeName(this,"select")){var M=n.makeArray(J);n("option",this).each(function(){this.selected=(n.inArray(this.value,M)>=0||n.inArray(this.text,M)>=0)});if(!M.length){this.selectedIndex=-1}}else{this.value=J}}})},html:function(D){return D===g?(this[0]?this[0].innerHTML:null):this.empty().append(D)},replaceWith:function(D){return this.after(D).remove()},eq:function(D){return this.slice(D,+D+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(D){return this.pushStack(n.map(this,function(F,E){return D.call(F,E,F)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=n.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild,D=this.length>1?I.cloneNode(true):I;if(H){for(var G=0,E=this.length;G0?D.cloneNode(true):I)}}if(F){n.each(F,y)}}return this;function K(N,O){return M&&n.nodeName(N,"table")&&n.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};n.fn.init.prototype=n.fn;function y(D,E){if(E.src){n.ajax({url:E.src,async:false,dataType:"script"})}else{n.globalEval(E.text||E.textContent||E.innerHTML||"")}if(E.parentNode){E.parentNode.removeChild(E)}}function e(){return +new Date}n.extend=n.fn.extend=function(){var I=arguments[0]||{},G=1,H=arguments.length,D=false,F;if(typeof I==="boolean"){D=I;I=arguments[1]||{};G=2}if(typeof I!=="object"&&!n.isFunction(I)){I={}}if(H==G){I=this;--G}for(;G-1}},swap:function(G,F,H){var D={};for(var E in F){D[E]=G.style[E];G.style[E]=F[E]}H.call(G);for(var E in F){G.style[E]=D[E]}},css:function(F,D,H){if(D=="width"||D=="height"){var J,E={position:"absolute",visibility:"hidden",display:"block"},I=D=="width"?["Left","Right"]:["Top","Bottom"];function G(){J=D=="width"?F.offsetWidth:F.offsetHeight;var L=0,K=0;n.each(I,function(){L+=parseFloat(n.curCSS(F,"padding"+this,true))||0;K+=parseFloat(n.curCSS(F,"border"+this+"Width",true))||0});J-=Math.round(L+K)}if(n(F).is(":visible")){G()}else{n.swap(F,E,G)}return Math.max(0,J)}return n.curCSS(F,D,H)},curCSS:function(H,E,F){var K,D=H.style;if(E=="opacity"&&!n.support.opacity){K=n.attr(D,"opacity");return K==""?"1":K}if(E.match(/float/i)){E=v}if(!F&&D&&D[E]){K=D[E]}else{if(p.getComputedStyle){if(E.match(/float/i)){E="float"}E=E.replace(/([A-Z])/g,"-$1").toLowerCase();var L=p.getComputedStyle(H,null);if(L){K=L.getPropertyValue(E)}if(E=="opacity"&&K==""){K="1"}}else{if(H.currentStyle){var I=E.replace(/\-(\w)/g,function(M,N){return N.toUpperCase()});K=H.currentStyle[E]||H.currentStyle[I];if(!/^\d+(px)?$/i.test(K)&&/^\d/.test(K)){var G=D.left,J=H.runtimeStyle.left;H.runtimeStyle.left=H.currentStyle.left;D.left=K||0;K=D.pixelLeft+"px";D.left=G;H.runtimeStyle.left=J}}}}return K},clean:function(E,J,H){J=J||document;if(typeof J.createElement==="undefined"){J=J.ownerDocument||J[0]&&J[0].ownerDocument||document}if(!H&&E.length===1&&typeof E[0]==="string"){var G=/^<(\w+)\s*\/?>$/.exec(E[0]);if(G){return[J.createElement(G[1])]}}var F=[],D=[],K=J.createElement("div");n.each(E,function(O,Q){if(typeof Q==="number"){Q+=""}if(!Q){return}if(typeof Q==="string"){Q=Q.replace(/(<(\w+)[^>]*?)\/>/g,function(S,T,R){return R.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?S:T+">"+R+">"});var N=n.trim(Q).toLowerCase();var P=!N.indexOf("",""]||!N.indexOf("",""]||N.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,""]||!N.indexOf("
",""]||(!N.indexOf(" | ","
"]||!N.indexOf("",""]||!n.support.htmlSerialize&&[1,"div","
"]||[0,"",""];K.innerHTML=P[1]+Q+P[2];while(P[0]--){K=K.lastChild}if(!n.support.tbody){var M=!N.indexOf(""&&N.indexOf("=0;--L){if(n.nodeName(M[L],"tbody")&&!M[L].childNodes.length){M[L].parentNode.removeChild(M[L])}}}if(!n.support.leadingWhitespace&&/^\s/.test(Q)){K.insertBefore(J.createTextNode(Q.match(/^\s*/)[0]),K.firstChild)}Q=n.makeArray(K.childNodes)}if(Q.nodeType){F.push(Q)}else{F=n.merge(F,Q)}});if(H){for(var I=0;F[I];I++){if(n.nodeName(F[I],"script")&&(!F[I].type||F[I].type.toLowerCase()==="text/javascript")){D.push(F[I].parentNode?F[I].parentNode.removeChild(F[I]):F[I])}else{if(F[I].nodeType===1){F.splice.apply(F,[I+1,0].concat(n.makeArray(F[I].getElementsByTagName("script"))))}H.appendChild(F[I])}}return D}return F},attr:function(I,F,J){if(!I||I.nodeType==3||I.nodeType==8){return g}var G=!n.isXMLDoc(I),K=J!==g;F=G&&n.props[F]||F;if(I.tagName){var E=/href|src|style/.test(F);if(F=="selected"&&I.parentNode){I.parentNode.selectedIndex}if(F in I&&G&&!E){if(K){if(F=="type"&&n.nodeName(I,"input")&&I.parentNode){throw"type property can't be changed"}I[F]=J}if(n.nodeName(I,"form")&&I.getAttributeNode(F)){return I.getAttributeNode(F).nodeValue}if(F=="tabIndex"){var H=I.getAttributeNode("tabIndex");return H&&H.specified?H.value:I.nodeName.match(/^(a|area|button|input|object|select|textarea)$/i)?0:g}return I[F]}if(!n.support.style&&G&&F=="style"){return n.attr(I.style,"cssText",J)}if(K){I.setAttribute(F,""+J)}var D=!n.support.hrefNormalized&&G&&E?I.getAttribute(F,2):I.getAttribute(F);return D===null?g:D}if(!n.support.opacity&&F=="opacity"){if(K){I.zoom=1;I.filter=(I.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(J)+""=="NaN"?"":"alpha(opacity="+J*100+")")}return I.filter&&I.filter.indexOf("opacity=")>=0?(parseFloat(I.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}F=F.replace(/-([a-z])/ig,function(L,M){return M.toUpperCase()});if(K){I[F]=J}return I[F]},trim:function(D){return(D||"").replace(/^\s+|\s+$/g,"")},makeArray:function(F){var D=[];if(F!=null){var E=F.length;if(E==null||typeof F==="string"||n.isFunction(F)||F.setInterval){D[0]=F}else{while(E){D[--E]=F[E]}}}return D},inArray:function(F,G){for(var D=0,E=G.length;D*",this).remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(D,E){n.fn[D]=function(){return this.each(E,arguments)}});function j(D,E){return D[0]&&parseInt(n.curCSS(D[0],E,true),10)||0}var h="jQuery"+e(),u=0,z={};n.extend({cache:{},data:function(E,D,F){E=E==l?z:E;var G=E[h];if(!G){G=E[h]=++u}if(D&&!n.cache[G]){n.cache[G]={}}if(F!==g){n.cache[G][D]=F}return D?n.cache[G][D]:G},removeData:function(E,D){E=E==l?z:E;var G=E[h];if(D){if(n.cache[G]){delete n.cache[G][D];D="";for(D in n.cache[G]){break}if(!D){n.removeData(E)}}}else{try{delete E[h]}catch(F){if(E.removeAttribute){E.removeAttribute(h)}}delete n.cache[G]}},queue:function(E,D,G){if(E){D=(D||"fx")+"queue";var F=n.data(E,D);if(!F||n.isArray(G)){F=n.data(E,D,n.makeArray(G))}else{if(G){F.push(G)}}}return F},dequeue:function(G,F){var D=n.queue(G,F),E=D.shift();if(!F||F==="fx"){E=D[0]}if(E!==g){E.call(G)}}});n.fn.extend({data:function(D,F){var G=D.split(".");G[1]=G[1]?"."+G[1]:"";if(F===g){var E=this.triggerHandler("getData"+G[1]+"!",[G[0]]);if(E===g&&this.length){E=n.data(this[0],D)}return E===g&&G[1]?this.data(G[0]):E}else{return this.trigger("setData"+G[1]+"!",[G[0],F]).each(function(){n.data(this,D,F)})}},removeData:function(D){return this.each(function(){n.removeData(this,D)})},queue:function(D,E){if(typeof D!=="string"){E=D;D="fx"}if(E===g){return n.queue(this[0],D)}return this.each(function(){var F=n.queue(this,D,E);if(D=="fx"&&F.length==1){F[0].call(this)}})},dequeue:function(D){return this.each(function(){n.dequeue(this,D)})}});
+(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=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;H0?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-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("",""]||!O.indexOf("",""]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,""]||!O.indexOf("
","
"]||(!O.indexOf(" | ","
"]||!O.indexOf("",""]||!o.support.htmlSerialize&&[1,"div","
"]||[0,"",""];L.innerHTML=Q[1]+R+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var N=!O.indexOf(""&&O.indexOf("=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*",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)})}});
/*
- * Sizzle CSS Selector Engine - v0.9.1
+ * 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 N=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|[^[\]]+)+\]|\\.|[^ >+~,(\[]+)+|[>+~])(\s*,\s*)?/g,I=0,F=Object.prototype.toString;var E=function(ae,S,aa,V){aa=aa||[];S=S||document;if(S.nodeType!==1&&S.nodeType!==9){return[]}if(!ae||typeof ae!=="string"){return aa}var ab=[],ac,Y,ah,ag,Z,R,Q=true;N.lastIndex=0;while((ac=N.exec(ae))!==null){ab.push(ac[1]);if(ac[2]){R=RegExp.rightContext;break}}if(ab.length>1&&G.match.POS.exec(ae)){if(ab.length===2&&G.relative[ab[0]]){var U="",X;while((X=G.match.POS.exec(ae))){U+=X[0];ae=ae.replace(G.match.POS,"")}Y=E.filter(U,E(/\s$/.test(ae)?ae+"*":ae,S))}else{Y=G.relative[ab[0]]?[S]:E(ab.shift(),S);while(ab.length){var P=[];ae=ab.shift();if(G.relative[ae]){ae+=ab.shift()}for(var af=0,ad=Y.length;af0){ah=D(Y)}else{Q=false}while(ab.length){var T=ab.pop(),W=T;if(!G.relative[T]){T=""}else{W=ab.pop()}if(W==null){W=S}G.relative[T](ah,W,M(S))}}if(!ah){ah=Y}if(!ah){throw"Syntax error, unrecognized expression: "+(T||ae)}if(F.call(ah)==="[object Array]"){if(!Q){aa.push.apply(aa,ah)}else{if(S.nodeType===1){for(var af=0;ah[af]!=null;af++){if(ah[af]&&(ah[af]===true||ah[af].nodeType===1&&H(S,ah[af]))){aa.push(Y[af])}}}else{for(var af=0;ah[af]!=null;af++){if(ah[af]&&ah[af].nodeType===1){aa.push(Y[af])}}}}}else{D(ah,aa)}if(R){E(R,S,aa,V)}return aa};E.matches=function(P,Q){return E(P,null,null,Q)};E.find=function(V,S){var W,Q;if(!V){return[]}for(var R=0,P=G.order.length;R":function(U,Q,V){if(typeof Q==="string"&&!/\W/.test(Q)){Q=V?Q:Q.toUpperCase();for(var R=0,P=U.length;R
=0){if(!R){P.push(Q[T])}}else{if(R){Q[T]=false}}}return false},ID:function(P){return P[1].replace(/\\/g,"")},TAG:function(Q,P){for(var R=0;!P[R];R++){}return M(P[R])?Q[1]:Q[1].toUpperCase()},CHILD:function(P){if(P[1]=="nth"){var Q=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(P[2]=="even"&&"2n"||P[2]=="odd"&&"2n+1"||!/\D/.test(P[2])&&"0n+"+P[2]||P[2]);P[2]=(Q[1]+(Q[2]||1))-0;P[3]=Q[3]-0}P[0]="done"+(I++);return P},ATTR:function(Q){var P=Q[1];if(G.attrMap[P]){Q[1]=G.attrMap[P]}if(Q[2]==="~="){Q[4]=" "+Q[4]+" "}return Q},PSEUDO:function(T,Q,R,P,U){if(T[1]==="not"){if(T[3].match(N).length>1){T[3]=E(T[3],null,null,Q)}else{var S=E.filter(T[3],Q,R,true^U);if(!R){P.push.apply(P,S)}return false}}else{if(G.match.POS.test(T[0])){return true}}return T},POS:function(P){P.unshift(true);return P}},filters:{enabled:function(P){return P.disabled===false&&P.type!=="hidden"},disabled:function(P){return P.disabled===true},checked:function(P){return P.checked===true},selected:function(P){P.parentNode.selectedIndex;return P.selected===true},parent:function(P){return !!P.firstChild},empty:function(P){return !P.firstChild},has:function(R,Q,P){return !!E(P[3],R).length},header:function(P){return/h\d/i.test(P.nodeName)},text:function(P){return"text"===P.type},radio:function(P){return"radio"===P.type},checkbox:function(P){return"checkbox"===P.type},file:function(P){return"file"===P.type},password:function(P){return"password"===P.type},submit:function(P){return"submit"===P.type},image:function(P){return"image"===P.type},reset:function(P){return"reset"===P.type},button:function(P){return"button"===P.type||P.nodeName.toUpperCase()==="BUTTON"},input:function(P){return/input|select|textarea|button/i.test(P.nodeName)}},setFilters:{first:function(Q,P){return P===0},last:function(R,Q,P,S){return Q===S.length-1},even:function(Q,P){return P%2===0},odd:function(Q,P){return P%2===1},lt:function(R,Q,P){return Q
P[3]-0},nth:function(R,Q,P){return P[3]-0==Q},eq:function(R,Q,P){return P[3]-0==Q}},filter:{CHILD:function(P,S){var V=S[1],W=P.parentNode;var U="child"+W.childNodes.length;if(W&&(!W[U]||!P.nodeIndex)){var T=1;for(var Q=W.firstChild;Q;Q=Q.nextSibling){if(Q.nodeType==1){Q.nodeIndex=T++}}W[U]=T-1}if(V=="first"){return P.nodeIndex==1}else{if(V=="last"){return P.nodeIndex==W[U]}else{if(V=="only"){return W[U]==1}else{if(V=="nth"){var Y=false,R=S[2],X=S[3];if(R==1&&X==0){return true}if(R==0){if(P.nodeIndex==X){Y=true}}else{if((P.nodeIndex-X)%R==0&&(P.nodeIndex-X)/R>=0){Y=true}}return Y}}}}},PSEUDO:function(V,R,S,W){var Q=R[1],T=G.filters[Q];if(T){return T(V,S,R,W)}else{if(Q==="contains"){return(V.textContent||V.innerText||"").indexOf(R[3])>=0}else{if(Q==="not"){var U=R[3];for(var S=0,P=U.length;S
=0:S==="~="?(" "+U+" ").indexOf(Q)>=0:!R[4]?P:S==="!="?U!=Q:S==="^="?U.indexOf(Q)===0:S==="$="?U.substr(U.length-Q.length)===Q:S==="|="?U===Q||U.substr(0,Q.length+1)===Q+"-":false},POS:function(T,Q,R,U){var P=Q[2],S=G.setFilters[P];if(S){return S(T,R,Q,U)}}}};for(var K in G.match){G.match[K]=RegExp(G.match[K].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var D=function(Q,P){Q=Array.prototype.slice.call(Q);if(P){P.push.apply(P,Q);return P}return Q};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(J){D=function(T,S){var Q=S||[];if(F.call(T)==="[object Array]"){Array.prototype.push.apply(Q,T)}else{if(typeof T.length==="number"){for(var R=0,P=T.length;R
";var P=document.documentElement;P.insertBefore(Q,P.firstChild);if(!!document.getElementById(R)){G.find.ID=function(T,U){if(U.getElementById){var S=U.getElementById(T[1]);return S?S.id===T[1]||S.getAttributeNode&&S.getAttributeNode("id").nodeValue===T[1]?[S]:g:[]}};G.filter.ID=function(U,S){var T=U.getAttributeNode&&U.getAttributeNode("id");return U.nodeType===1&&T&&T.nodeValue===S}}P.removeChild(Q)})();(function(){var P=document.createElement("div");P.appendChild(document.createComment(""));if(P.getElementsByTagName("*").length>0){G.find.TAG=function(Q,U){var T=U.getElementsByTagName(Q[1]);if(Q[1]==="*"){var S=[];for(var R=0;T[R];R++){if(T[R].nodeType===1){S.push(T[R])}}T=S}return T}}P.innerHTML="";if(P.firstChild.getAttribute("href")!=="#"){G.attrHandle.href=function(Q){return Q.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var P=E;E=function(T,S,Q,R){S=S||document;if(!R&&S.nodeType===9){try{return D(S.querySelectorAll(T),Q)}catch(U){}}return P(T,S,Q,R)};E.find=P.find;E.filter=P.filter;E.selectors=P.selectors;E.matches=P.matches})()}if(document.documentElement.getElementsByClassName){G.order.splice(1,0,"CLASS");G.find.CLASS=function(P,Q){return Q.getElementsByClassName(P[1])}}function L(Q,W,V,Z,X,Y){for(var T=0,R=Z.length;T0){T=P;break}}}P=P[Q]}Y[S]=T}}}var H=document.compareDocumentPosition?function(Q,P){return Q.compareDocumentPosition(P)&16}:function(Q,P){return Q!==P&&(Q.contains?Q.contains(P):true)};var M=function(P){return P.documentElement&&!P.body||P.tagName&&P.ownerDocument&&!P.ownerDocument.body};n.find=E;n.filter=E.filter;n.expr=E.selectors;n.expr[":"]=n.expr.filters;E.selectors.filters.hidden=function(P){return"hidden"===P.type||n.css(P,"display")==="none"||n.css(P,"visibility")==="hidden"};E.selectors.filters.visible=function(P){return"hidden"!==P.type&&n.css(P,"display")!=="none"&&n.css(P,"visibility")!=="hidden"};E.selectors.filters.animated=function(P){return n.grep(n.timers,function(Q){return P===Q.elem}).length};n.multiFilter=function(R,P,Q){if(Q){R=":not("+R+")"}return E.matches(R,P)};n.dir=function(R,Q){var P=[],S=R[Q];while(S&&S!=document){if(S.nodeType==1){P.push(S)}S=S[Q]}return P};n.nth=function(T,P,R,S){P=P||1;var Q=0;for(;T;T=T[R]){if(T.nodeType==1&&++Q==P){break}}return T};n.sibling=function(R,Q){var P=[];for(;R;R=R.nextSibling){if(R.nodeType==1&&R!=Q){P.push(R)}}return P};return;l.Sizzle=E})();n.event={add:function(H,E,G,J){if(H.nodeType==3||H.nodeType==8){return}if(H.setInterval&&H!=l){H=l}if(!G.guid){G.guid=this.guid++}if(J!==g){var F=G;G=this.proxy(F);G.data=J}var D=n.data(H,"events")||n.data(H,"events",{}),I=n.data(H,"handle")||n.data(H,"handle",function(){return typeof n!=="undefined"&&!n.event.triggered?n.event.handle.apply(arguments.callee.elem,arguments):g});I.elem=H;n.each(E.split(/\s+/),function(L,M){var N=M.split(".");M=N.shift();G.type=N.slice().sort().join(".");var K=D[M];if(n.event.specialAll[M]){n.event.specialAll[M].setup.call(H,J,N)}if(!K){K=D[M]={};if(!n.event.special[M]||n.event.special[M].setup.call(H,J,N)===false){if(H.addEventListener){H.addEventListener(M,I,false)}else{if(H.attachEvent){H.attachEvent("on"+M,I)}}}}K[G.guid]=G;n.event.global[M]=true});H=null},guid:1,global:{},remove:function(J,G,I){if(J.nodeType==3||J.nodeType==8){return}var F=n.data(J,"events"),E,D;if(F){if(G===g||(typeof G==="string"&&G.charAt(0)==".")){for(var H in F){this.remove(J,H+(G||""))}}else{if(G.type){I=G.handler;G=G.type}n.each(G.split(/\s+/),function(L,N){var P=N.split(".");N=P.shift();var M=RegExp("(^|\\.)"+P.slice().sort().join(".*\\.")+"(\\.|$)");if(F[N]){if(I){delete F[N][I.guid]}else{for(var O in F[N]){if(M.test(F[N][O].type)){delete F[N][O]}}}if(n.event.specialAll[N]){n.event.specialAll[N].teardown.call(J,P)}for(E in F[N]){break}if(!E){if(!n.event.special[N]||n.event.special[N].teardown.call(J,P)===false){if(J.removeEventListener){J.removeEventListener(N,n.data(J,"handle"),false)}else{if(J.detachEvent){J.detachEvent("on"+N,n.data(J,"handle"))}}}E=null;delete F[N]}}})}for(E in F){break}if(!E){var K=n.data(J,"handle");if(K){K.elem=null}n.removeData(J,"events");n.removeData(J,"handle")}}},trigger:function(H,J,G,D){var F=H.type||H;if(!D){H=typeof H==="object"?H[h]?H:n.extend(n.Event(F),H):n.Event(F);if(F.indexOf("!")>=0){H.type=F=F.slice(0,-1);H.exclusive=true}if(!G){H.stopPropagation();if(this.global[F]){n.each(n.cache,function(){if(this.events&&this.events[F]){n.event.trigger(H,J,this.handle.elem)}})}}if(!G||G.nodeType==3||G.nodeType==8){return g}H.result=g;H.target=G;J=n.makeArray(J);J.unshift(H)}H.currentTarget=G;var I=n.data(G,"handle");if(I){I.apply(G,J)}if((!G[F]||(n.nodeName(G,"a")&&F=="click"))&&G["on"+F]&&G["on"+F].apply(G,J)===false){H.result=false}if(!D&&G[F]&&!H.isDefaultPrevented()&&!(n.nodeName(G,"a")&&F=="click")){this.triggered=true;try{G[F]()}catch(K){}}this.triggered=false;if(!H.isPropagationStopped()){var E=G.parentNode||G.ownerDocument;if(E){n.event.trigger(H,J,E,true)}}},handle:function(J){var I,D;J=arguments[0]=n.event.fix(J||l.event);var K=J.type.split(".");J.type=K.shift();I=!K.length&&!J.exclusive;var H=RegExp("(^|\\.)"+K.slice().sort().join(".*\\.")+"(\\.|$)");D=(n.data(this,"events")||{})[J.type];for(var F in D){var G=D[F];if(I||H.test(G.type)){J.handler=G;J.data=G.data;var E=G.apply(this,arguments);if(E!==g){J.result=E;if(E===false){J.preventDefault();J.stopPropagation()}}if(J.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(G){if(G[h]){return G}var E=G;G=n.Event(E);for(var F=this.props.length,I;F;){I=this.props[--F];G[I]=E[I]}if(!G.target){G.target=G.srcElement||document}if(G.target.nodeType==3){G.target=G.target.parentNode}if(!G.relatedTarget&&G.fromElement){G.relatedTarget=G.fromElement==G.target?G.toElement:G.fromElement}if(G.pageX==null&&G.clientX!=null){var H=document.documentElement,D=document.body;G.pageX=G.clientX+(H&&H.scrollLeft||D&&D.scrollLeft||0)-(H.clientLeft||0);G.pageY=G.clientY+(H&&H.scrollTop||D&&D.scrollTop||0)-(H.clientTop||0)}if(!G.which&&((G.charCode||G.charCode===0)?G.charCode:G.keyCode)){G.which=G.charCode||G.keyCode}if(!G.metaKey&&G.ctrlKey){G.metaKey=G.ctrlKey}if(!G.which&&G.button){G.which=(G.button&1?1:(G.button&2?3:(G.button&4?2:0)))}return G},proxy:function(E,D){D=D||function(){return E.apply(this,arguments)};D.guid=E.guid=E.guid||D.guid||this.guid++;return D},special:{ready:{setup:A,teardown:function(){}}},specialAll:{live:{setup:function(D,E){n.event.add(this,E[0],c)},teardown:function(F){if(F.length){var D=0,E=RegExp("(^|\\.)"+F[0]+"(\\.|$)");n.each((n.data(this,"events").live||{}),function(){if(E.test(this.type)){D++}});if(D<1){n.event.remove(this,F[0],c)}}}}}};n.Event=function(D){if(!this.preventDefault){return new n.Event(D)}if(D&&D.type){this.originalEvent=D;this.type=D.type;this.timeStamp=D.timeStamp}else{this.type=D}if(!this.timeStamp){this.timeStamp=e()}this[h]=true};function k(){return false}function t(){return true}n.Event.prototype={preventDefault:function(){this.isDefaultPrevented=t;var D=this.originalEvent;if(!D){return}if(D.preventDefault){D.preventDefault()}D.returnValue=false},stopPropagation:function(){this.isPropagationStopped=t;var D=this.originalEvent;if(!D){return}if(D.stopPropagation){D.stopPropagation()}D.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=t;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(E){var D=E.relatedTarget;while(D&&D!=this){try{D=D.parentNode}catch(F){D=this}}if(D!=this){E.type=E.data;n.event.handle.apply(this,arguments)}};n.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(E,D){n.event.special[D]={setup:function(){n.event.add(this,E,a,D)},teardown:function(){n.event.remove(this,E,a)}}});n.fn.extend({bind:function(E,F,D){return E=="unload"?this.one(E,F,D):this.each(function(){n.event.add(this,E,D||F,D&&F)})},one:function(F,G,E){var D=n.event.proxy(E||G,function(H){n(this).unbind(H,D);return(E||G).apply(this,arguments)});return this.each(function(){n.event.add(this,F,D,E&&G)})},unbind:function(E,D){return this.each(function(){n.event.remove(this,E,D)})},trigger:function(D,E){return this.each(function(){n.event.trigger(D,E,this)})},triggerHandler:function(D,F){if(this[0]){var E=n.Event(D);E.preventDefault();E.stopPropagation();n.event.trigger(E,F,this[0]);return E.result}},toggle:function(F){var D=arguments,E=1;while(Ea';var G=J.getElementsByTagName("*"),D=J.getElementsByTagName("a")[0];if(!G||!G.length||!D){return}n.support={leadingWhitespace:J.firstChild.nodeType==3,tbody:!J.getElementsByTagName("tbody").length,objectAll:!!J.getElementsByTagName("object")[0].getElementsByTagName("*").length,htmlSerialize:!!J.getElementsByTagName("link").length,style:/red/.test(D.getAttribute("style")),hrefNormalized:D.getAttribute("href")==="/a",opacity:D.style.opacity==="0.5",cssFloat:!!D.style.cssFloat,scriptEval:false,noCloneEvent:true,boxModel:null};F.type="text/javascript";try{F.appendChild(document.createTextNode("window."+I+"=1;"))}catch(H){}E.insertBefore(F,E.firstChild);if(l[I]){n.support.scriptEval=true;delete l[I]}E.removeChild(F);if(J.attachEvent&&J.fireEvent){J.attachEvent("onclick",function(){n.support.noCloneEvent=false;J.detachEvent("onclick",arguments.callee)});J.cloneNode(true).fireEvent("onclick")}n(function(){var K=document.createElement("div");K.style.width="1px";K.style.paddingLeft="1px";document.body.appendChild(K);n.boxModel=n.support.boxModel=K.offsetWidth===2;document.body.removeChild(K)})})();var v=n.support.cssFloat?"cssFloat":"styleFloat";n.props={"for":"htmlFor","class":"className","float":v,cssFloat:v,styleFloat:v,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",tabindex:"tabIndex"};n.fn.extend({_load:n.fn.load,load:function(F,I,J){if(typeof F!=="string"){return this._load(F)}var H=F.indexOf(" ");if(H>=0){var D=F.slice(H,F.length);F=F.slice(0,H)}var G="GET";if(I){if(n.isFunction(I)){J=I;I=null}else{if(typeof I==="object"){I=n.param(I);G="POST"}}}var E=this;n.ajax({url:F,type:G,dataType:"html",data:I,complete:function(L,K){if(K=="success"||K=="notmodified"){E.html(D?n("").append(L.responseText.replace(/