diff --git a/README b/README
index 70815c1412..f080d32bcc 100644
--- a/README
+++ b/README
@@ -877,7 +877,7 @@ This section is a catch-all for site-wide variables.
name: the name of your site, like 'YourCompany Microblog'.
server: the server part of your site's URLs, like 'example.net'.
-path: The path part of your site's URLs, like 'mublog' or '/'
+path: The path part of your site's URLs, like 'mublog' or ''
(installed in root).
fancy: whether or not your site uses fancy URLs (see Fancy URLs
section above). Default is false.
@@ -1184,10 +1184,10 @@ newuser
Options with new users.
-subscribe: nickname of a user account to automatically subscribe new
- users to. Typically this would be system account for e.g.
- service updates or announcements. Users are able to unsub
- if they want. Default is null; no auto subscribe.
+default: nickname of a user account to automatically subscribe new
+ users to. Typically this would be system account for e.g.
+ service updates or announcements. Users are able to unsub
+ if they want. Default is null; no auto subscribe.
welcome: nickname of a user account that sends welcome messages to new
users. Can be the same as 'subscribe' account, although on
busy servers it may be a good idea to keep that one just for
diff --git a/actions/public.php b/actions/public.php
index a20ae40321..96c766a57d 100644
--- a/actions/public.php
+++ b/actions/public.php
@@ -166,6 +166,50 @@ class PublicAction extends Action
$nav->show();
}
+ function showPageNotice()
+ {
+ $notice = Notice::publicStream(0, 1);
+
+ if (!$notice) {
+ $this->serverError(_('Could not retrieve public stream.'));
+ return;
+ }
+
+ // no notices in the public stream, let's get out of here
+ if ($notice->count()) {
+ return;
+ }
+
+ $message = _('This is the public timeline for %%site.name%% but noone has posted anything yet.') . ' ';
+
+ if (common_logged_in()) {
+ $message .= _('Be the first to post!');
+/*
+ sprintf(_('You are logged in... %%%%site.name%%%% groups let you find and talk with ' .
+ 'people of similar interests. After you join a group ' .
+ 'you can send messages to all other members using the ' .
+ 'syntax "!groupname". Don\'t see a group you like? Try ' .
+ '[searching for one](%%%%action.groupsearch%%%%) or ' .
+ '[start your own!](%%%%action.newgroup%%%%)'));
+*/
+ }
+ else {
+ $message .= _('Why not [register an account](%%action.register%%) and be the first to post!');
+/*
+ sprintf(_('You are not logged in... %%%%site.name%%%% groups let you find and talk with ' .
+ 'people of similar interests. After you join a group ' .
+ 'you can send messages to all other members using the ' .
+ 'syntax "!groupname". Don\'t see a group you like? Try ' .
+ '[searching for one](%%%%action.groupsearch%%%%) or ' .
+ '[start your own!](%%%%action.newgroup%%%%)'));
+*/
+ }
+
+ $this->elementStart('div', 'blankfiller');
+ $this->raw(common_markup_to_html($message));
+ $this->elementEnd('div');
+ }
+
/**
* Fill the content area
*
@@ -207,9 +251,14 @@ class PublicAction extends Action
function showAnonymousMessage()
{
- $m = _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
- 'based on the Free Software [Laconica](http://laconi.ca/) tool. ' .
- '[Join now](%%action.register%%) to share notices about yourself with friends, family, and colleagues! ([Read more](%%doc.help%%))');
+ if (! (common_config('site','closed') || common_config('site','inviteonly'))) {
+ $m = _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
+ 'based on the Free Software [Laconica](http://laconi.ca/) tool. ' .
+ '[Join now](%%action.register%%) to share notices about yourself with friends, family, and colleagues! ([Read more](%%doc.help%%))');
+ } else {
+ $m = _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
+ 'based on the Free Software [Laconica](http://laconi.ca/) tool.');
+ }
$this->elementStart('div', array('id' => 'anon_notice'));
$this->raw(common_markup_to_html($m));
$this->elementEnd('div');
diff --git a/actions/recoverpassword.php b/actions/recoverpassword.php
index eeb6b2516c..620fe7eb8e 100644
--- a/actions/recoverpassword.php
+++ b/actions/recoverpassword.php
@@ -181,13 +181,21 @@ class RecoverpasswordAction extends Action
function showRecoverForm()
{
$this->elementStart('form', array('method' => 'post',
- 'id' => 'recoverpassword',
+ 'id' => 'form_password_recover',
+ 'class' => 'form_settings',
'action' => common_local_url('recoverpassword')));
+ $this->elementStart('fieldset');
+ $this->element('legend', null, _('Password recover'));
+ $this->elementStart('ul', 'form_data');
+ $this->elementStart('li');
$this->input('nicknameoremail', _('Nickname or email'),
$this->trimmed('nicknameoremail'),
_('Your nickname on this server, ' .
'or your registered email address.'));
+ $this->elementEnd('li');
+ $this->elementEnd('ul');
$this->submit('recover', _('Recover'));
+ $this->elementEnd('fieldset');
$this->elementEnd('form');
}
@@ -213,14 +221,24 @@ class RecoverpasswordAction extends Action
function showResetForm()
{
$this->elementStart('form', array('method' => 'post',
- 'id' => 'recoverpassword',
+ 'id' => 'form_password_change',
+ 'class' => 'form_settings',
'action' => common_local_url('recoverpassword')));
+ $this->elementStart('fieldset');
+ $this->element('legend', null, _('Password change'));
$this->hidden('token', common_session_token());
+ $this->elementStart('ul', 'form_data');
+ $this->elementStart('li');
$this->password('newpassword', _('New password'),
_('6 or more characters, and don\'t forget it!'));
+ $this->elementEnd('li');
+ $this->elementStart('li');
$this->password('confirm', _('Confirm'),
_('Same as password above'));
+ $this->elementEnd('li');
+ $this->elementEnd('ul');
$this->submit('reset', _('Reset'));
+ $this->elementEnd('fieldset');
$this->elementEnd('form');
}
diff --git a/actions/showgroup.php b/actions/showgroup.php
index 95d2914ba0..79445851f9 100644
--- a/actions/showgroup.php
+++ b/actions/showgroup.php
@@ -396,11 +396,18 @@ class ShowgroupAction extends Action
function showAnonymousMessage()
{
- $m = sprintf(_('**%s** is a user group on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
- 'based on the Free Software [Laconica](http://laconi.ca/) tool. Its members share ' .
- 'short messages about their life and interests. '.
- '[Join now](%%%%action.register%%%%) to become part of this group and many more! ([Read more](%%%%doc.help%%%%))'),
+ if (!(common_config('site','closed') || common_config('site','inviteonly'))) {
+ $m = sprintf(_('**%s** is a user group on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
+ 'based on the Free Software [Laconica](http://laconi.ca/) tool. Its members share ' .
+ 'short messages about their life and interests. '.
+ '[Join now](%%%%action.register%%%%) to become part of this group and many more! ([Read more](%%%%doc.help%%%%))'),
$this->group->nickname);
+ } else {
+ $m = sprintf(_('**%s** is a user group on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
+ 'based on the Free Software [Laconica](http://laconi.ca/) tool. Its members share ' .
+ 'short messages about their life and interests. '),
+ $this->group->nickname);
+ }
$this->elementStart('div', array('id' => 'anon_notice'));
$this->raw(common_markup_to_html($m));
$this->elementEnd('div');
diff --git a/actions/showstream.php b/actions/showstream.php
index 9b65362938..f5886f3d33 100644
--- a/actions/showstream.php
+++ b/actions/showstream.php
@@ -535,10 +535,16 @@ class ShowstreamAction extends Action
function showAnonymousMessage()
{
- $m = sprintf(_('**%s** has an account on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
- 'based on the Free Software [Laconica](http://laconi.ca/) tool. ' .
- '[Join now](%%%%action.register%%%%) to follow **%s**\'s notices and many more! ([Read more](%%%%doc.help%%%%))'),
- $this->user->nickname, $this->user->nickname);
+ if (!(common_config('site','closed') || common_config('site','inviteonly'))) {
+ $m = sprintf(_('**%s** has an account on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
+ 'based on the Free Software [Laconica](http://laconi.ca/) tool. ' .
+ '[Join now](%%%%action.register%%%%) to follow **%s**\'s notices and many more! ([Read more](%%%%doc.help%%%%))'),
+ $this->user->nickname, $this->user->nickname);
+ } else {
+ $m = sprintf(_('**%s** has an account on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
+ 'based on the Free Software [Laconica](http://laconi.ca/) tool. '),
+ $this->user->nickname, $this->user->nickname);
+ }
$this->elementStart('div', array('id' => 'anon_notice'));
$this->raw(common_markup_to_html($m));
$this->elementEnd('div');
diff --git a/apple-touch-icon.png b/apple-touch-icon.png
new file mode 100644
index 0000000000..d129298d4a
Binary files /dev/null and b/apple-touch-icon.png differ
diff --git a/classes/Notice.php b/classes/Notice.php
index 3087e39a78..44a6aeb986 100644
--- a/classes/Notice.php
+++ b/classes/Notice.php
@@ -799,4 +799,98 @@ class Notice extends Memcached_DataObject
}
}
}
+
+ function asAtomEntry($namespace=false, $source=false)
+ {
+ $profile = $this->getProfile();
+
+ $xs = new XMLStringer(true);
+
+ if ($namespace) {
+ $attrs = array('xmlns' => 'http://www.w3.org/2005/Atom',
+ 'xmlns:thr' => 'http://purl.org/syndication/thread/1.0');
+ } else {
+ $attrs = array();
+ }
+
+ $xs->elementStart('entry', $attrs);
+
+ if ($source) {
+ $xs->elementStart('source');
+ $xs->element('title', null, $profile->nickname . " - " . common_config('site', 'name'));
+ $xs->element('link', array('href' => $profile->profileurl));
+ $user = User::staticGet('id', $profile->id);
+ if (!empty($user)) {
+ $atom_feed = common_local_url('api',
+ array('apiaction' => 'statuses',
+ 'method' => 'user_timeline',
+ 'argument' => $profile->nickname.'.atom'));
+ $xs->element('link', array('rel' => 'self',
+ 'type' => 'application/atom+xml',
+ 'href' => $profile->profileurl));
+ $xs->element('link', array('rel' => 'license',
+ 'href' => common_config('license', 'url')));
+ }
+
+ $xs->element('icon', null, $profile->avatarUrl(AVATAR_PROFILE_SIZE));
+ }
+
+ $xs->elementStart('author');
+ $xs->element('name', null, $profile->nickname);
+ $xs->element('uri', null, $profile->profileurl);
+ $xs->elementEnd('author');
+
+ if ($source) {
+ $xs->elementEnd('source');
+ }
+
+ $xs->element('title', null, $this->content);
+ $xs->element('summary', null, $this->content);
+
+ $xs->element('link', array('rel' => 'alternate',
+ 'href' => $this->bestUrl()));
+
+ $xs->element('id', null, $this->uri);
+
+ $xs->element('published', null, common_date_w3dtf($this->created));
+ $xs->element('updated', null, common_date_w3dtf($this->modified));
+
+ if ($this->reply_to) {
+ $reply_notice = Notice::staticGet('id', $this->reply_to);
+ if (!empty($reply_notice)) {
+ $xs->element('link', array('rel' => 'related',
+ 'href' => $reply_notice->bestUrl()));
+ $xs->element('thr:in-reply-to',
+ array('ref' => $reply_notice->uri,
+ 'href' => $reply_notice->bestUrl()));
+ }
+ }
+
+ $xs->element('content', array('type' => 'html'), $this->rendered);
+
+ $tag = new Notice_tag();
+ $tag->notice_id = $this->id;
+ if ($tag->find()) {
+ while ($tag->fetch()) {
+ $xs->element('category', array('term' => $tag->tag));
+ }
+ }
+ $tag->free();
+
+ $xs->elementEnd('entry');
+
+ return $xs->getString();
+ }
+
+ function bestUrl()
+ {
+ if (!empty($this->url)) {
+ return $this->url;
+ } else if (!empty($this->uri) && preg_match('/^https?:/', $this->uri)) {
+ return $this->uri;
+ } else {
+ return common_local_url('shownotice',
+ array('notice' => $this->id));
+ }
+ }
}
diff --git a/classes/User.php b/classes/User.php
index e9e472fe1b..3b9b5cd839 100644
--- a/classes/User.php
+++ b/classes/User.php
@@ -1,7 +1,7 @@
.
*/
-if (!defined('LACONICA')) { exit(1); }
+if (!defined('LACONICA')) {
+ exit(1);
+}
/**
* Table Definition for user
*/
+
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
require_once 'Validate.php';
@@ -79,13 +82,13 @@ class User extends Memcached_DataObject
function isSubscribed($other)
{
assert(!is_null($other));
- # XXX: cache results of this query
+ // XXX: cache results of this query
$sub = Subscription::pkeyGet(array('subscriber' => $this->id,
'subscribed' => $other->id));
return (is_null($sub)) ? false : true;
}
- # 'update' won't write key columns, so we have to do it ourselves.
+ // 'update' won't write key columns, so we have to do it ourselves.
function updateKeys(&$orig)
{
@@ -96,7 +99,7 @@ class User extends Memcached_DataObject
}
}
if (count($parts) == 0) {
- # No changes
+ // No changes
return true;
}
$toupdate = implode(', ', $parts);
@@ -117,7 +120,7 @@ class User extends Memcached_DataObject
function allowed_nickname($nickname)
{
- # XXX: should already be validated for size, content, etc.
+ // XXX: should already be validated for size, content, etc.
static $blacklist = array('rss', 'xrds', 'doc', 'main',
'settings', 'notice', 'user',
'search', 'avatar', 'tag', 'tags',
@@ -147,7 +150,7 @@ class User extends Memcached_DataObject
$sub->subscriber = $this->id;
$sub->subscribed = $other->id;
- $sub->created = common_sql_now(); # current time
+ $sub->created = common_sql_now(); // current time
if (!$sub->insert()) {
return false;
@@ -173,7 +176,7 @@ class User extends Memcached_DataObject
static function register($fields) {
- # MAGICALLY put fields into current scope
+ // MAGICALLY put fields into current scope
extract($fields);
@@ -211,11 +214,11 @@ class User extends Memcached_DataObject
$user->id = $id;
$user->nickname = $nickname;
- if (!empty($password)) { # may not have a password for OpenID users
+ if (!empty($password)) { // may not have a password for OpenID users
$user->password = common_munge_password($password, $id);
}
- # Users who respond to invite email have proven their ownership of that address
+ // Users who respond to invite email have proven their ownership of that address
if (!empty($code)) {
$invite = Invitation::staticGet($code);
@@ -240,7 +243,7 @@ class User extends Memcached_DataObject
return false;
}
- # Everyone is subscribed to themself
+ // Everyone is subscribed to themself
$subscription = new Subscription();
$subscription->subscriber = $user->id;
@@ -324,7 +327,7 @@ class User extends Memcached_DataObject
return $user;
}
- # Things we do when the email changes
+ // Things we do when the email changes
function emailChanged()
{
@@ -345,46 +348,46 @@ class User extends Memcached_DataObject
{
$cache = common_memcache();
- # XXX: Kind of a hack.
+ // XXX: Kind of a hack.
if ($cache) {
- # This is the stream of favorite notices, in rev chron
- # order. This forces it into cache.
+ // This is the stream of favorite notices, in rev chron
+ // order. This forces it into cache.
$faves = $this->favoriteNotices(0, NOTICE_CACHE_WINDOW);
$cnt = 0;
while ($faves->fetch()) {
if ($faves->id < $notice->id) {
- # If we passed it, it's not a fave
+ // If we passed it, it's not a fave
return false;
} else if ($faves->id == $notice->id) {
- # If it matches a cached notice, then it's a fave
+ // If it matches a cached notice, then it's a fave
return true;
}
$cnt++;
}
- # If we're not past the end of the cache window,
- # then the cache has all available faves, so this one
- # is not a fave.
+ // If we're not past the end of the cache window,
+ // then the cache has all available faves, so this one
+ // is not a fave.
if ($cnt < NOTICE_CACHE_WINDOW) {
return false;
}
- # Otherwise, cache doesn't have all faves;
- # fall through to the default
+ // Otherwise, cache doesn't have all faves;
+ // fall through to the default
}
$fave = Fave::pkeyGet(array('user_id' => $this->id,
'notice_id' => $notice->id));
return ((is_null($fave)) ? false : true);
}
+
function mutuallySubscribed($other)
{
return $this->isSubscribed($other) &&
$other->isSubscribed($this);
}
- function mutuallySubscribedUsers()
- {
-
- # 3-way join; probably should get cached
- $UT = common_config('db','type')=='pgsql'?'"user"':'user';
+ function mutuallySubscribedUsers()
+ {
+ // 3-way join; probably should get cached
+ $UT = common_config('db','type')=='pgsql'?'"user"':'user';
$qry = "SELECT $UT.* " .
"FROM subscription sub1 JOIN $UT ON sub1.subscribed = $UT.id " .
"JOIN subscription sub2 ON $UT.id = sub2.subscriber " .
@@ -407,8 +410,8 @@ class User extends Memcached_DataObject
$offset, $limit, $since_id, $before_id, null, $since);
}
- function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
- {
+ function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
+ {
$profile = $this->getProfile();
if (!$profile) {
return null;
@@ -417,8 +420,8 @@ class User extends Memcached_DataObject
}
}
- function favoriteNotices($offset=0, $limit=NOTICES_PER_PAGE)
- {
+ function favoriteNotices($offset=0, $limit=NOTICES_PER_PAGE)
+ {
$qry =
'SELECT notice.* ' .
'FROM notice JOIN fave ON notice.id = fave.notice_id ' .
@@ -428,12 +431,12 @@ class User extends Memcached_DataObject
$offset, $limit);
}
- function noticesWithFriends($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
- {
+ function noticesWithFriends($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
+ {
$enabled = common_config('inboxes', 'enabled');
- # Complicated code, depending on whether we support inboxes yet
- # XXX: make this go away when inboxes become mandatory
+ // Complicated code, depending on whether we support inboxes yet
+ // XXX: make this go away when inboxes become mandatory
if ($enabled === false ||
($enabled == 'transitional' && $this->inboxed == 0)) {
@@ -443,13 +446,13 @@ class User extends Memcached_DataObject
'WHERE subscription.subscriber = %d ';
$order = null;
} else if ($enabled === true ||
- ($enabled == 'transitional' && $this->inboxed == 1)) {
+ ($enabled == 'transitional' && $this->inboxed == 1)) {
$qry =
'SELECT notice.* ' .
'FROM notice JOIN notice_inbox ON notice.id = notice_inbox.notice_id ' .
'WHERE notice_inbox.user_id = %d ';
- # NOTE: we override ORDER
+ // NOTE: we override ORDER
$order = null;
}
return Notice::getStream(sprintf($qry, $this->id),
@@ -458,35 +461,34 @@ class User extends Memcached_DataObject
$order, $since);
}
- function blowFavesCache()
- {
+ function blowFavesCache()
+ {
$cache = common_memcache();
if ($cache) {
- # Faves don't happen chronologically, so we need to blow
- # ;last cache, too
+ // Faves don't happen chronologically, so we need to blow
+ // ;last cache, too
$cache->delete(common_cache_key('user:faves:'.$this->id));
$cache->delete(common_cache_key('user:faves:'.$this->id).';last');
}
}
- function getSelfTags()
- {
+ function getSelfTags()
+ {
return Profile_tag::getTags($this->id, $this->id);
}
- function setSelfTags($newtags)
- {
+ function setSelfTags($newtags)
+ {
return Profile_tag::setTags($this->id, $this->id, $newtags);
}
function block($other)
{
-
- # Add a new block record
+ // Add a new block record
$block = new Profile_block();
- # Begin a transaction
+ // Begin a transaction
$block->query('BEGIN');
@@ -500,7 +502,7 @@ class User extends Memcached_DataObject
return false;
}
- # Cancel their subscription, if it exists
+ // Cancel their subscription, if it exists
$sub = Subscription::pkeyGet(array('subscriber' => $other->id,
'subscribed' => $this->id));
@@ -520,8 +522,7 @@ class User extends Memcached_DataObject
function unblock($other)
{
-
- # Get the block record
+ // Get the block record
$block = Profile_block::get($this->id, $other->id);
diff --git a/db/laconica.sql b/db/laconica.sql
index 098fa4fd1a..a790a3fd27 100644
--- a/db/laconica.sql
+++ b/db/laconica.sql
@@ -13,7 +13,7 @@ create table profile (
index profile_nickname_idx (nickname),
FULLTEXT(nickname, fullname, location, bio, homepage)
-) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_bin;
+) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
create table avatar (
profile_id integer not null comment 'foreign key to profile table' references profile (id),
@@ -73,7 +73,7 @@ create table user (
modified timestamp comment 'date this record was modified',
index user_smsemail_idx (smsemail)
-) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
/* remote people */
@@ -103,7 +103,6 @@ create table subscription (
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
create table notice (
-
id integer auto_increment primary key comment 'unique identifier',
profile_id integer not null comment 'who made the update' references profile (id),
uri varchar(255) unique key comment 'universally unique identifier, usually a tag URI',
@@ -119,7 +118,7 @@ create table notice (
index notice_profile_id_idx (profile_id),
index notice_created_idx (created),
FULLTEXT(content)
-) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_bin;
+) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
create table notice_source (
code varchar(32) primary key not null comment 'source code',
@@ -130,7 +129,6 @@ create table notice_source (
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
create table reply (
-
notice_id integer not null comment 'notice that is the reply' references notice (id),
profile_id integer not null comment 'profile replied to' references profile (id),
modified timestamp not null comment 'date this record was modified',
@@ -144,7 +142,6 @@ create table reply (
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
create table fave (
-
notice_id integer not null comment 'notice that is the favorite' references notice (id),
user_id integer not null comment 'user who likes this notice' references user (id),
modified timestamp not null comment 'date this record was modified',
@@ -321,7 +318,6 @@ create table invitation (
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
create table message (
-
id integer auto_increment primary key comment 'unique identifier',
uri varchar(255) unique key comment 'universally unique identifier',
from_profile integer not null comment 'who the message is from' references profile (id),
@@ -336,10 +332,9 @@ create table message (
index message_from_idx (from_profile),
index message_to_idx (to_profile),
index message_created_idx (created)
-) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
create table notice_inbox (
-
user_id integer not null comment 'user receiving the message' references user (id),
notice_id integer not null comment 'notice received' references notice (id),
created datetime not null comment 'date the notice was created',
@@ -362,7 +357,6 @@ create table profile_tag (
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
create table profile_block (
-
blocker integer not null comment 'user making the block' references user (id),
blocked integer not null comment 'profile that is blocked' references profile (id),
modified timestamp comment 'date of blocking',
@@ -372,7 +366,6 @@ create table profile_block (
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
create table user_group (
-
id integer auto_increment primary key comment 'unique identifier',
nickname varchar(64) unique key comment 'nickname for addressing',
@@ -391,10 +384,9 @@ create table user_group (
index user_group_nickname_idx (nickname)
-) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
create table group_member (
-
group_id integer not null comment 'foreign key to user_group' references user_group (id),
profile_id integer not null comment 'foreign key to profile table' references profile (id),
is_admin boolean default false comment 'is this user an admin?',
@@ -409,7 +401,6 @@ create table group_member (
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
create table related_group (
-
group_id integer not null comment 'foreign key to user_group' references user_group (id),
related_group_id integer not null comment 'foreign key to user_group' references user_group (id),
diff --git a/db/notice_source.sql b/db/notice_source.sql
index 5e2d413c00..d28a09383f 100644
--- a/db/notice_source.sql
+++ b/db/notice_source.sql
@@ -19,6 +19,7 @@ VALUES
('identichat','identichat','http://identichat.prosody.im/', now()),
('identitwitch','IdentiTwitch','http://richfish.org/identitwitch/', now()),
('mbpidgin','mbpidgin','http://code.google.com/p/microblog-purple/', now()),
+ ('Mobidentica', 'Mobidentica', 'http://www.substanceofcode.com/software/mobidentica/', now()),
('moconica','Moconica','http://moconica.com/', now()),
('pocketwit','PockeTwit','http://code.google.com/p/pocketwit/', now()),
('posty','Posty','http://spreadingfunkyness.com/posty/', now()),
@@ -44,4 +45,4 @@ VALUES
('twitux','Twitux','http://live.gnome.org/DanielMorales/Twitux', now()),
('twitvim','TwitVim','http://vim.sourceforge.net/scripts/script.php?script_id=2204', now()),
('urfastr','urfastr','http://urfastr.net/', now()),
- ('adium', 'Adium', 'http://www.adiumx.com/', now()));
+ ('adium', 'Adium', 'http://www.adiumx.com/', now());
diff --git a/js/jquery.js b/js/jquery.js
index 94e9c1755e..926357433e 100644
--- a/js/jquery.js
+++ b/js/jquery.js
@@ -1,13 +1,13 @@
/*!
- * jQuery JavaScript Library v1.3.1
+ * jQuery JavaScript Library v1.3.2
* http://jquery.com/
*
* Copyright (c) 2009 John Resig
* Dual licensed under the MIT and GPL licenses.
* http://docs.jquery.com/License
*
- * Date: 2009-01-21 20:42:16 -0500 (Wed, 21 Jan 2009)
- * Revision: 6158
+ * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
+ * Revision: 6246
*/
(function(){
@@ -88,14 +88,16 @@ jQuery.fn = jQuery.prototype = {
this.context = selector.context;
}
- return this.setArray(jQuery.makeArray(selector));
+ return this.setArray(jQuery.isArray( selector ) ?
+ selector :
+ jQuery.makeArray(selector));
},
// Start with an empty selector
selector: "",
// The current version of jQuery being used
- jquery: "1.3.1",
+ jquery: "1.3.2",
// The number of elements contained in the matched element set
size: function() {
@@ -108,7 +110,7 @@ jQuery.fn = jQuery.prototype = {
return num === undefined ?
// Return a 'clean' array
- jQuery.makeArray( this ) :
+ Array.prototype.slice.call( this ) :
// Return just the object
this[ num ];
@@ -278,23 +280,21 @@ jQuery.fn = jQuery.prototype = {
},
// For internal use only.
- // Behaves like an Array's .push method, not like a jQuery method.
+ // Behaves like an Array's method, not like a jQuery method.
push: [].push,
+ sort: [].sort,
+ splice: [].splice,
find: function( selector ) {
- if ( this.length === 1 && !/,/.test(selector) ) {
+ if ( this.length === 1 ) {
var ret = this.pushStack( [], "find", selector );
ret.length = 0;
jQuery.find( selector, this[0], ret );
return ret;
} else {
- var elems = jQuery.map(this, function(elem){
+ return this.pushStack( jQuery.unique(jQuery.map(this, function(elem){
return jQuery.find( selector, elem );
- });
-
- return this.pushStack( /[^+>] [^+>]/.test( selector ) ?
- jQuery.unique( elems ) :
- elems, "find", selector );
+ })), "find", selector );
}
},
@@ -310,33 +310,37 @@ jQuery.fn = jQuery.prototype = {
// attributes in IE that are actually only stored
// as properties will not be copied (such as the
// the name attribute on an input).
- var clone = this.cloneNode(true),
- container = document.createElement("div");
- container.appendChild(clone);
- return jQuery.clean([container.innerHTML])[0];
+ var html = this.outerHTML;
+ if ( !html ) {
+ var div = this.ownerDocument.createElement("div");
+ div.appendChild( this.cloneNode(true) );
+ html = div.innerHTML;
+ }
+
+ return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0];
} else
return this.cloneNode(true);
});
- // Need to set the expando to null on the cloned set if it exists
- // removeData doesn't work here, IE removes it from the original as well
- // this is primarily for IE but the data expando shouldn't be copied over in any browser
- var clone = ret.find("*").andSelf().each(function(){
- if ( this[ expando ] !== undefined )
- this[ expando ] = null;
- });
-
// Copy the events from the original to the clone
- if ( events === true )
- this.find("*").andSelf().each(function(i){
- if (this.nodeType == 3)
- return;
- var events = jQuery.data( this, "events" );
+ if ( events === true ) {
+ var orig = this.find("*").andSelf(), i = 0;
- for ( var type in events )
- for ( var handler in events[ type ] )
- jQuery.event.add( clone[ i ], type, events[ type ][ handler ], events[ type ][ handler ].data );
+ ret.find("*").andSelf().each(function(){
+ if ( this.nodeName !== orig[i].nodeName )
+ return;
+
+ var events = jQuery.data( orig[i], "events" );
+
+ for ( var type in events ) {
+ for ( var handler in events[ type ] ) {
+ jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
+ }
+ }
+
+ i++;
});
+ }
// Return the cloned set
return ret;
@@ -355,14 +359,18 @@ jQuery.fn = jQuery.prototype = {
},
closest: function( selector ) {
- var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null;
+ var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null,
+ closer = 0;
return this.map(function(){
var cur = this;
while ( cur && cur.ownerDocument ) {
- if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) )
+ if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) {
+ jQuery.data(cur, "closest", closer);
return cur;
+ }
cur = cur.parentNode;
+ closer++;
}
});
},
@@ -475,7 +483,7 @@ jQuery.fn = jQuery.prototype = {
html: function( value ) {
return value === undefined ?
(this[0] ?
- this[0].innerHTML :
+ this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") :
null) :
this.empty().append( value );
},
@@ -507,13 +515,13 @@ jQuery.fn = jQuery.prototype = {
if ( this[0] ) {
var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(),
scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ),
- first = fragment.firstChild,
- extra = this.length > 1 ? fragment.cloneNode(true) : fragment;
+ first = fragment.firstChild;
if ( first )
for ( var i = 0, l = this.length; i < l; i++ )
- callback.call( root(this[i], first), i > 0 ? extra.cloneNode(true) : fragment );
-
+ callback.call( root(this[i], first), this.length > 1 || i > 0 ?
+ fragment.cloneNode(true) : fragment );
+
if ( scripts )
jQuery.each( scripts, evalScript );
}
@@ -636,9 +644,7 @@ jQuery.extend({
// Evalulates a script in a global context
globalEval: function( data ) {
- data = jQuery.trim( data );
-
- if ( data ) {
+ if ( data && /\S/.test(data) ) {
// Inspired by code by Andrea Giammarchi
// http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
var head = document.getElementsByTagName("head")[0] || document.documentElement,
@@ -741,26 +747,32 @@ jQuery.extend({
elem.style[ name ] = old[ name ];
},
- css: function( elem, name, force ) {
+ css: function( elem, name, force, extra ) {
if ( name == "width" || name == "height" ) {
var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ];
function getWH() {
val = name == "width" ? elem.offsetWidth : elem.offsetHeight;
- var padding = 0, border = 0;
+
+ if ( extra === "border" )
+ return;
+
jQuery.each( which, function() {
- padding += parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
- border += parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
+ if ( !extra )
+ val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
+ if ( extra === "margin" )
+ val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0;
+ else
+ val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
});
- val -= Math.round(padding + border);
}
- if ( jQuery(elem).is(":visible") )
+ if ( elem.offsetWidth !== 0 )
getWH();
else
jQuery.swap( elem, props, getWH );
- return Math.max(0, val);
+ return Math.max(0, Math.round(val));
}
return jQuery.curCSS( elem, name, force );
@@ -866,7 +878,7 @@ jQuery.extend({
});
// Trim whitespace, otherwise indexOf won't work as expected
- var tags = jQuery.trim( elem ).toLowerCase();
+ var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase();
var wrap =
// option or optgroup
@@ -906,11 +918,12 @@ jQuery.extend({
if ( !jQuery.support.tbody ) {
// String was a
, *may* have spurious
- var tbody = !tags.indexOf("
or
- wrap[1] == "
" && tags.indexOf("" && !hasBody ?
div.childNodes :
[];
@@ -1189,13 +1202,16 @@ jQuery.each({
insertAfter: "after",
replaceAll: "replaceWith"
}, function(name, original){
- jQuery.fn[ name ] = function() {
- var args = arguments;
+ jQuery.fn[ name ] = function( selector ) {
+ var ret = [], insert = jQuery( selector );
- return this.each(function(){
- for ( var i = 0, length = args.length; i < length; i++ )
- jQuery( args[ i ] )[ original ]( this );
- });
+ for ( var i = 0, l = insert.length; i < l; i++ ) {
+ var elems = (i > 0 ? this.clone(true) : this).get();
+ jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
+ ret = ret.concat( elems );
+ }
+
+ return this.pushStack( ret, name, selector );
};
});
@@ -1234,7 +1250,7 @@ jQuery.each({
empty: function() {
// Remove element nodes and prevent memory leaks
- jQuery( ">*", this ).remove();
+ jQuery(this).children().remove();
// Remove any remaining nodes
while ( this.firstChild )
@@ -1402,7 +1418,7 @@ jQuery.fn.extend({
*/
(function(){
-var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]+['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[]+)+|[>+~])(\s*,\s*)?/g,
+var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
done = 0,
toString = Object.prototype.toString;
@@ -1507,6 +1523,19 @@ var Sizzle = function(selector, context, results, seed) {
if ( extra ) {
Sizzle( extra, context, results, seed );
+
+ if ( sortOrder ) {
+ hasDuplicate = false;
+ results.sort(sortOrder);
+
+ if ( hasDuplicate ) {
+ for ( var i = 1; i < results.length; i++ ) {
+ if ( results[i] === results[i-1] ) {
+ results.splice(i--, 1);
+ }
+ }
+ }
+ }
}
return results;
@@ -1548,7 +1577,8 @@ Sizzle.find = function(expr, context, isXML){
};
Sizzle.filter = function(expr, set, inplace, not){
- var old = expr, result = [], curLoop = set, match, anyFound;
+ var old = expr, result = [], curLoop = set, match, anyFound,
+ isXMLFilter = set && set[0] && isXML(set[0]);
while ( expr && set.length ) {
for ( var type in Expr.filter ) {
@@ -1561,7 +1591,7 @@ Sizzle.filter = function(expr, set, inplace, not){
}
if ( Expr.preFilter[ type ] ) {
- match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not );
+ match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
if ( !match ) {
anyFound = found = true;
@@ -1606,8 +1636,6 @@ Sizzle.filter = function(expr, set, inplace, not){
}
}
- expr = expr.replace(/\s*,\s*/, "");
-
// Improper expression
if ( expr == old ) {
if ( anyFound == null ) {
@@ -1645,26 +1673,33 @@ var Expr = Sizzle.selectors = {
}
},
relative: {
- "+": function(checkSet, part){
- for ( var i = 0, l = checkSet.length; i < l; i++ ) {
- var elem = checkSet[i];
- if ( elem ) {
- var cur = elem.previousSibling;
- while ( cur && cur.nodeType !== 1 ) {
- cur = cur.previousSibling;
- }
- checkSet[i] = typeof part === "string" ?
- cur || false :
- cur === part;
+ "+": function(checkSet, part, isXML){
+ var isPartStr = typeof part === "string",
+ isTag = isPartStr && !/\W/.test(part),
+ isPartStrNotTag = isPartStr && !isTag;
+
+ if ( isTag && !isXML ) {
+ part = part.toUpperCase();
+ }
+
+ for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
+ if ( (elem = checkSet[i]) ) {
+ while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
+
+ checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
+ elem || false :
+ elem === part;
}
}
- if ( typeof part === "string" ) {
+ if ( isPartStrNotTag ) {
Sizzle.filter( part, checkSet, true );
}
},
">": function(checkSet, part, isXML){
- if ( typeof part === "string" && !/\W/.test(part) ) {
+ var isPartStr = typeof part === "string";
+
+ if ( isPartStr && !/\W/.test(part) ) {
part = isXML ? part : part.toUpperCase();
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
@@ -1678,19 +1713,19 @@ var Expr = Sizzle.selectors = {
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
var elem = checkSet[i];
if ( elem ) {
- checkSet[i] = typeof part === "string" ?
+ checkSet[i] = isPartStr ?
elem.parentNode :
elem.parentNode === part;
}
}
- if ( typeof part === "string" ) {
+ if ( isPartStr ) {
Sizzle.filter( part, checkSet, true );
}
}
},
"": function(checkSet, part, isXML){
- var doneName = "done" + (done++), checkFn = dirCheck;
+ var doneName = done++, checkFn = dirCheck;
if ( !part.match(/\W/) ) {
var nodeCheck = part = isXML ? part : part.toUpperCase();
@@ -1700,7 +1735,7 @@ var Expr = Sizzle.selectors = {
checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
},
"~": function(checkSet, part, isXML){
- var doneName = "done" + (done++), checkFn = dirCheck;
+ var doneName = done++, checkFn = dirCheck;
if ( typeof part === "string" && !part.match(/\W/) ) {
var nodeCheck = part = isXML ? part : part.toUpperCase();
@@ -1718,8 +1753,16 @@ var Expr = Sizzle.selectors = {
}
},
NAME: function(match, context, isXML){
- if ( typeof context.getElementsByName !== "undefined" && !isXML ) {
- return context.getElementsByName(match[1]);
+ if ( typeof context.getElementsByName !== "undefined" ) {
+ var ret = [], results = context.getElementsByName(match[1]);
+
+ for ( var i = 0, l = results.length; i < l; i++ ) {
+ if ( results[i].getAttribute("name") === match[1] ) {
+ ret.push( results[i] );
+ }
+ }
+
+ return ret.length === 0 ? null : ret;
}
},
TAG: function(match, context){
@@ -1727,13 +1770,16 @@ var Expr = Sizzle.selectors = {
}
},
preFilter: {
- CLASS: function(match, curLoop, inplace, result, not){
+ CLASS: function(match, curLoop, inplace, result, not, isXML){
match = " " + match[1].replace(/\\/g, "") + " ";
- var elem;
- for ( var i = 0; (elem = curLoop[i]) != null; i++ ) {
+ if ( isXML ) {
+ return match;
+ }
+
+ for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
if ( elem ) {
- if ( not ^ (" " + elem.className + " ").indexOf(match) >= 0 ) {
+ if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
if ( !inplace )
result.push( elem );
} else if ( inplace ) {
@@ -1764,14 +1810,14 @@ var Expr = Sizzle.selectors = {
}
// TODO: Move to normal caching system
- match[0] = "done" + (done++);
+ match[0] = done++;
return match;
},
- ATTR: function(match){
+ ATTR: function(match, curLoop, inplace, result, not, isXML){
var name = match[1].replace(/\\/g, "");
- if ( Expr.attrMap[name] ) {
+ if ( !isXML && Expr.attrMap[name] ) {
match[1] = Expr.attrMap[name];
}
@@ -1784,7 +1830,7 @@ var Expr = Sizzle.selectors = {
PSEUDO: function(match, curLoop, inplace, result, not){
if ( match[1] === "not" ) {
// If we're dealing with a complex expression, or a simple one
- if ( match[3].match(chunker).length > 1 ) {
+ if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) {
match[3] = Sizzle(match[3], null, null, curLoop);
} else {
var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
@@ -1793,7 +1839,7 @@ var Expr = Sizzle.selectors = {
}
return false;
}
- } else if ( Expr.match.POS.test( match[0] ) ) {
+ } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
return true;
}
@@ -1890,47 +1936,6 @@ var Expr = Sizzle.selectors = {
}
},
filter: {
- CHILD: function(elem, match){
- var type = match[1], parent = elem.parentNode;
-
- var doneName = match[0];
-
- if ( parent && (!parent[ doneName ] || !elem.nodeIndex) ) {
- var count = 1;
-
- for ( var node = parent.firstChild; node; node = node.nextSibling ) {
- if ( node.nodeType == 1 ) {
- node.nodeIndex = count++;
- }
- }
-
- parent[ doneName ] = count - 1;
- }
-
- if ( type == "first" ) {
- return elem.nodeIndex == 1;
- } else if ( type == "last" ) {
- return elem.nodeIndex == parent[ doneName ];
- } else if ( type == "only" ) {
- return parent[ doneName ] == 1;
- } else if ( type == "nth" ) {
- var add = false, first = match[2], last = match[3];
-
- if ( first == 1 && last == 0 ) {
- return true;
- }
-
- if ( first == 0 ) {
- if ( elem.nodeIndex == last ) {
- add = true;
- }
- } else if ( (elem.nodeIndex - last) % first == 0 && (elem.nodeIndex - last) / first >= 0 ) {
- add = true;
- }
-
- return add;
- }
- },
PSEUDO: function(elem, match, i, array){
var name = match[1], filter = Expr.filters[ name ];
@@ -1950,6 +1955,49 @@ var Expr = Sizzle.selectors = {
return true;
}
},
+ CHILD: function(elem, match){
+ var type = match[1], node = elem;
+ switch (type) {
+ case 'only':
+ case 'first':
+ while (node = node.previousSibling) {
+ if ( node.nodeType === 1 ) return false;
+ }
+ if ( type == 'first') return true;
+ node = elem;
+ case 'last':
+ while (node = node.nextSibling) {
+ if ( node.nodeType === 1 ) return false;
+ }
+ return true;
+ case 'nth':
+ var first = match[2], last = match[3];
+
+ if ( first == 1 && last == 0 ) {
+ return true;
+ }
+
+ var doneName = match[0],
+ parent = elem.parentNode;
+
+ if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
+ var count = 0;
+ for ( node = parent.firstChild; node; node = node.nextSibling ) {
+ if ( node.nodeType === 1 ) {
+ node.nodeIndex = ++count;
+ }
+ }
+ parent.sizcache = doneName;
+ }
+
+ var diff = elem.nodeIndex - last;
+ if ( first == 0 ) {
+ return diff == 0;
+ } else {
+ return ( diff % first == 0 && diff / first >= 0 );
+ }
+ }
+ },
ID: function(elem, match){
return elem.nodeType === 1 && elem.getAttribute("id") === match;
},
@@ -1957,10 +2005,20 @@ var Expr = Sizzle.selectors = {
return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
},
CLASS: function(elem, match){
- return match.test( elem.className );
+ return (" " + (elem.className || elem.getAttribute("class")) + " ")
+ .indexOf( match ) > -1;
},
ATTR: function(elem, match){
- var result = Expr.attrHandle[ match[1] ] ? Expr.attrHandle[ match[1] ]( elem ) : elem[ match[1] ] || elem.getAttribute( match[1] ), value = result + "", type = match[2], check = match[4];
+ var name = match[1],
+ result = Expr.attrHandle[ name ] ?
+ Expr.attrHandle[ name ]( elem ) :
+ elem[ name ] != null ?
+ elem[ name ] :
+ elem.getAttribute( name ),
+ value = result + "",
+ type = match[2],
+ check = match[4];
+
return result == null ?
type === "!=" :
type === "=" ?
@@ -1969,8 +2027,8 @@ var Expr = Sizzle.selectors = {
value.indexOf(check) >= 0 :
type === "~=" ?
(" " + value + " ").indexOf(check) >= 0 :
- !match[4] ?
- result :
+ !check ?
+ value && result !== false :
type === "!=" ?
value != check :
type === "^=" ?
@@ -2036,6 +2094,39 @@ try {
};
}
+var sortOrder;
+
+if ( document.documentElement.compareDocumentPosition ) {
+ sortOrder = function( a, b ) {
+ var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
+ if ( ret === 0 ) {
+ hasDuplicate = true;
+ }
+ return ret;
+ };
+} else if ( "sourceIndex" in document.documentElement ) {
+ sortOrder = function( a, b ) {
+ var ret = a.sourceIndex - b.sourceIndex;
+ if ( ret === 0 ) {
+ hasDuplicate = true;
+ }
+ return ret;
+ };
+} else if ( document.createRange ) {
+ sortOrder = function( a, b ) {
+ var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
+ aRange.selectNode(a);
+ aRange.collapse(true);
+ bRange.selectNode(b);
+ bRange.collapse(true);
+ var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
+ if ( ret === 0 ) {
+ hasDuplicate = true;
+ }
+ return ret;
+ };
+}
+
// Check to see if the browser returns elements by name when
// querying by getElementById (and provide a workaround)
(function(){
@@ -2099,7 +2190,8 @@ try {
// Check to see if an attribute returns normalized href attributes
div.innerHTML = "";
- if ( div.firstChild && div.firstChild.getAttribute("href") !== "#" ) {
+ if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
+ div.firstChild.getAttribute("href") !== "#" ) {
Expr.attrHandle.href = function(elem){
return elem.getAttribute("href", 2);
};
@@ -2136,29 +2228,50 @@ if ( document.querySelectorAll ) (function(){
Sizzle.matches = oldSizzle.matches;
})();
-if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) {
+if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
+ var div = document.createElement("div");
+ div.innerHTML = "";
+
+ // Opera can't find a second classname (in 9.6)
+ if ( div.getElementsByClassName("e").length === 0 )
+ return;
+
+ // Safari caches class attributes, doesn't catch changes (in 3.2)
+ div.lastChild.className = "e";
+
+ if ( div.getElementsByClassName("e").length === 1 )
+ return;
+
Expr.order.splice(1, 0, "CLASS");
- Expr.find.CLASS = function(match, context) {
- return context.getElementsByClassName(match[1]);
+ Expr.find.CLASS = function(match, context, isXML) {
+ if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
+ return context.getElementsByClassName(match[1]);
+ }
};
-}
+})();
function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ var sibDir = dir == "previousSibling" && !isXML;
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
var elem = checkSet[i];
if ( elem ) {
+ if ( sibDir && elem.nodeType === 1 ){
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
elem = elem[dir];
var match = false;
- while ( elem && elem.nodeType ) {
- var done = elem[doneName];
- if ( done ) {
- match = checkSet[ done ];
+ while ( elem ) {
+ if ( elem.sizcache === doneName ) {
+ match = checkSet[elem.sizset];
break;
}
- if ( elem.nodeType === 1 && !isXML )
- elem[doneName] = i;
+ if ( elem.nodeType === 1 && !isXML ){
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
if ( elem.nodeName === cur ) {
match = elem;
@@ -2174,22 +2287,28 @@ function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
}
function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ var sibDir = dir == "previousSibling" && !isXML;
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
var elem = checkSet[i];
if ( elem ) {
+ if ( sibDir && elem.nodeType === 1 ) {
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
elem = elem[dir];
var match = false;
- while ( elem && elem.nodeType ) {
- if ( elem[doneName] ) {
- match = checkSet[ elem[doneName] ];
+ while ( elem ) {
+ if ( elem.sizcache === doneName ) {
+ match = checkSet[elem.sizset];
break;
}
if ( elem.nodeType === 1 ) {
- if ( !isXML )
- elem[doneName] = i;
-
+ if ( !isXML ) {
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
if ( typeof cur !== "string" ) {
if ( elem === cur ) {
match = true;
@@ -2248,15 +2367,11 @@ jQuery.expr = Sizzle.selectors;
jQuery.expr[":"] = jQuery.expr.filters;
Sizzle.selectors.filters.hidden = function(elem){
- return "hidden" === elem.type ||
- jQuery.css(elem, "display") === "none" ||
- jQuery.css(elem, "visibility") === "hidden";
+ return elem.offsetWidth === 0 || elem.offsetHeight === 0;
};
Sizzle.selectors.filters.visible = function(elem){
- return "hidden" !== elem.type &&
- jQuery.css(elem, "display") !== "none" &&
- jQuery.css(elem, "visibility") !== "hidden";
+ return elem.offsetWidth > 0 || elem.offsetHeight > 0;
};
Sizzle.selectors.filters.animated = function(elem){
@@ -2552,7 +2667,8 @@ jQuery.event = {
var all, handlers;
event = arguments[0] = jQuery.event.fix( event || window.event );
-
+ event.currentTarget = this;
+
// Namespaced event handlers
var namespaces = event.type.split(".");
event.type = namespaces.shift();
@@ -2883,9 +2999,13 @@ function liveHandler( event ){
}
});
+ elems.sort(function(a,b) {
+ return jQuery.data(a.elem, "closest") - jQuery.data(b.elem, "closest");
+ });
+
jQuery.each(elems, function(){
if ( this.fn.call(this.elem, event, this.fn.data) === false )
- stop = false;
+ return (stop = false);
});
return stop;
@@ -2949,7 +3069,7 @@ function bindReady(){
// If IE and not an iframe
// continually check to see if the document is ready
- if ( document.documentElement.doScroll && typeof window.frameElement === "undefined" ) (function(){
+ if ( document.documentElement.doScroll && window == window.top ) (function(){
if ( jQuery.isReady ) return;
try {
@@ -3079,12 +3199,11 @@ jQuery( window ).bind( 'unload', function(){
// document.body must exist before we can do this
jQuery(function(){
var div = document.createElement("div");
- div.style.width = "1px";
- div.style.paddingLeft = "1px";
+ div.style.width = div.style.paddingLeft = "1px";
document.body.appendChild( div );
jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;
- document.body.removeChild( div );
+ document.body.removeChild( div ).style.display = 'none';
});
})();
@@ -3175,7 +3294,7 @@ jQuery.fn.extend({
.filter(function(){
return this.name && !this.disabled &&
(this.checked || /select|textarea/i.test(this.nodeName) ||
- /text|hidden|password/i.test(this.type));
+ /text|hidden|password|search/i.test(this.type));
})
.map(function(i, elem){
var val = jQuery(this).val();
@@ -3371,6 +3490,9 @@ jQuery.extend({
done = true;
success();
complete();
+
+ // Handle memory leak in IE
+ script.onload = script.onreadystatechange = null;
head.removeChild( script );
}
};
@@ -3686,9 +3808,15 @@ jQuery.fn.extend({
elemdisplay[ tagName ] = display;
}
- this[i].style.display = jQuery.data(this[i], "olddisplay", display);
+ jQuery.data(this[i], "olddisplay", display);
}
}
+
+ // Set the display of the elements in a second loop
+ // to avoid the constant reflow
+ for ( var i = 0, l = this.length; i < l; i++ ){
+ this[i].style.display = jQuery.data(this[i], "olddisplay") || "";
+ }
return this;
}
@@ -3702,8 +3830,14 @@ jQuery.fn.extend({
var old = jQuery.data(this[i], "olddisplay");
if ( !old && old !== "none" )
jQuery.data(this[i], "olddisplay", jQuery.css(this[i], "display"));
+ }
+
+ // Set the display of the elements in a second loop
+ // to avoid the constant reflow
+ for ( var i = 0, l = this.length; i < l; i++ ){
this[i].style.display = "none";
}
+
return this;
}
},
@@ -3915,7 +4049,7 @@ jQuery.fx.prototype = {
t.elem = this.elem;
- if ( t() && jQuery.timers.push(t) == 1 ) {
+ if ( t() && jQuery.timers.push(t) && !timerId ) {
timerId = setInterval(function(){
var timers = jQuery.timers;
@@ -3925,6 +4059,7 @@ jQuery.fx.prototype = {
if ( !timers.length ) {
clearInterval( timerId );
+ timerId = undefined;
}
}, 13);
}
@@ -4193,22 +4328,21 @@ jQuery.each( ['Left', 'Top'], function(i, name) {
jQuery.each([ "Height", "Width" ], function(i, name){
var tl = i ? "Left" : "Top", // top or left
- br = i ? "Right" : "Bottom"; // bottom or right
+ br = i ? "Right" : "Bottom", // bottom or right
+ lower = name.toLowerCase();
// innerHeight and innerWidth
jQuery.fn["inner" + name] = function(){
- return this[ name.toLowerCase() ]() +
- num(this, "padding" + tl) +
- num(this, "padding" + br);
+ return this[0] ?
+ jQuery.css( this[0], lower, false, "padding" ) :
+ null;
};
// outerHeight and outerWidth
jQuery.fn["outer" + name] = function(margin) {
- return this["inner" + name]() +
- num(this, "border" + tl + "Width") +
- num(this, "border" + br + "Width") +
- (margin ?
- num(this, "margin" + tl) + num(this, "margin" + br) : 0);
+ return this[0] ?
+ jQuery.css( this[0], lower, false, margin ? "margin" : "border" ) :
+ null;
};
var type = name.toLowerCase();
@@ -4238,4 +4372,5 @@ jQuery.each([ "Height", "Width" ], function(i, name){
this.css( type, typeof size === "string" ? size : size + "px" );
};
-});})();
+});
+})();
diff --git a/js/jquery.min.js b/js/jquery.min.js
index c327fae812..b1ae21d8b2 100644
--- a/js/jquery.min.js
+++ b/js/jquery.min.js
@@ -1,19 +1,19 @@
/*
- * jQuery JavaScript Library v1.3.1
+ * jQuery JavaScript Library v1.3.2
* http://jquery.com/
*
* Copyright (c) 2009 John Resig
* Dual licensed under the MIT and GPL licenses.
* http://docs.jquery.com/License
*
- * Date: 2009-01-21 20:42:16 -0500 (Wed, 21 Jan 2009)
- * Revision: 6158
+ * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
+ * Revision: 6246
*/
-(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.makeArray(E))},selector:"",jquery:"1.3.1",size:function(){return this.length},get:function(E){return E===g?o.makeArray(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,find:function(E){if(this.length===1&&!/,/.test(E)){var G=this.pushStack([],"find",E);G.length=0;o.find(E,this[0],G);return G}else{var F=o.map(this,function(H){return o.find(E,H)});return this.pushStack(/[^+>] [^+>]/.test(E)?o.unique(F):F,"find",E)}},clone:function(F){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.cloneNode(true),H=document.createElement("div");H.appendChild(I);return o.clean([H.innerHTML])[0]}else{return this.cloneNode(true)}});var G=E.find("*").andSelf().each(function(){if(this[h]!==g){this[h]=null}});if(F===true){this.find("*").andSelf().each(function(I){if(this.nodeType==3){return}var H=o.data(this,"events");for(var K in H){for(var J in H[K]){o.event.add(G[I],K,H[K][J],H[K][J].data)}}})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var F=o.expr.match.POS.test(E)?o(E):null;return this.map(function(){var G=this;while(G&&G.ownerDocument){if(F?F.index(G)>-1:o(G).is(E)){return G}G=G.parentNode}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F=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,"