]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch 'testing' into 0.9.x
authorBrion Vibber <brion@pobox.com>
Fri, 26 Feb 2010 17:35:28 +0000 (09:35 -0800)
committerBrion Vibber <brion@pobox.com>
Fri, 26 Feb 2010 17:35:28 +0000 (09:35 -0800)
88 files changed:
actions/apigroupcreate.php
actions/apigrouplistall.php
actions/apistatusnetconfig.php
actions/apitimelinegroup.php
actions/apitimelineuser.php
actions/blockedfromgroup.php
actions/editgroup.php
actions/foafgroup.php
actions/groupdesignsettings.php
actions/grouplogo.php
actions/groupmembers.php
actions/grouprss.php
actions/groups.php
actions/hcard.php [new file with mode: 0644]
actions/joingroup.php
actions/leavegroup.php
actions/newgroup.php
actions/showgroup.php
actions/subscribe.php
actions/twitapisearchatom.php
actions/twitapisearchjson.php
actions/twitapitrends.php
classes/Local_group.php [new file with mode: 0644]
classes/Notice.php
classes/User_group.php
classes/statusnet.ini
db/beta5tobeta6.sql [new file with mode: 0644]
db/statusnet.sql
js/jquery.js
js/jquery.min.js
js/util.js
lib/activity.php
lib/api.php [deleted file]
lib/apiaction.php [new file with mode: 0644]
lib/apiauth.php
lib/atomnoticefeed.php
lib/common.php
lib/default.php
lib/distribqueuehandler.php
lib/joinform.php
lib/leaveform.php
lib/profilequeuehandler.php
lib/router.php
lib/util.php
plugins/BlogspamNetPlugin.php
plugins/Mapstraction/map.php
plugins/OStatus/OStatusPlugin.php
plugins/OStatus/actions/hostmeta.php
plugins/OStatus/actions/ostatusinit.php
plugins/OStatus/actions/ostatussub.php
plugins/OStatus/actions/webfinger.php [deleted file]
plugins/OStatus/actions/xrd.php [new file with mode: 0644]
plugins/OStatus/classes/Magicsig.php
plugins/OStatus/classes/Ostatus_profile.php
plugins/OStatus/extlib/hkit/hcard.profile.php [new file with mode: 0644]
plugins/OStatus/extlib/hkit/hkit.class.php [new file with mode: 0644]
plugins/OStatus/lib/discovery.php [new file with mode: 0644]
plugins/OStatus/lib/magicenvelope.php
plugins/OStatus/lib/pushinqueuehandler.php
plugins/OStatus/lib/salmon.php
plugins/OStatus/lib/webfinger.php [deleted file]
plugins/Realtime/RealtimePlugin.php
plugins/Realtime/icon_external.gif [deleted file]
plugins/Realtime/icon_pause.gif [deleted file]
plugins/Realtime/icon_play.gif [deleted file]
plugins/Realtime/realtimeupdate.css
scripts/init_conversation.php [new file with mode: 0755]
scripts/updateavatarurl.php
scripts/updateavatarurl_group.php [new file with mode: 0644]
tests/ActivityParseTests.php
theme/base/images/icons/README [new file with mode: 0644]
theme/base/images/icons/icon_geo.png [deleted file]
theme/base/images/icons/icons-01.gif
theme/cloudy/css/display.css
theme/default/css/display.css
theme/identica/css/display.css
theme/otalk/css/base.css [deleted file]
theme/otalk/css/display.css [deleted file]
theme/otalk/css/ie.css [deleted file]
theme/otalk/default-avatar-mini.png [deleted file]
theme/otalk/default-avatar-profile.png [deleted file]
theme/otalk/default-avatar-stream.png [deleted file]
theme/otalk/images/illustrations/illu_arrow-left-01.gif [deleted file]
theme/otalk/images/illustrations/illu_pattern-01.png [deleted file]
theme/otalk/logo.png [deleted file]
theme/pigeonthoughts/css/base.css
theme/pigeonthoughts/css/display.css
theme/pigeonthoughts/logo.png

index 028d76a7822a99c5bf2f3b6a46d9743280a4054a..145806356c77d2b74224635ed19cc854e92d9629 100644 (file)
@@ -123,7 +123,9 @@ class ApiGroupCreateAction extends ApiAuthAction
                                             'description' => $this->description,
                                             'location' => $this->location,
                                             'aliases'  => $this->aliases,
-                                            'userid'   => $this->user->id));
+                                            'userid'   => $this->user->id,
+                                            'local'    => true));
+
         switch($this->format) {
         case 'xml':
             $this->showSingleXmlGroup($group);
@@ -306,9 +308,9 @@ class ApiGroupCreateAction extends ApiAuthAction
 
     function groupNicknameExists($nickname)
     {
-        $group = User_group::staticGet('nickname', $nickname);
+        $local = Local_group::staticGet('nickname', $nickname);
 
-        if (!empty($group)) {
+        if (!empty($local)) {
             return true;
         }
 
index d2ef2978aa5427cf1e0ec5dfb6b65b8332e48e2e..e1b54a83229ae1bcf985444d8ceebde0b572ace1 100644 (file)
@@ -134,13 +134,13 @@ class ApiGroupListAllAction extends ApiPrivateAuthAction
 
     function getGroups()
     {
-        $groups = array();
-
-        // XXX: Use the $page, $count, $max_id, $since_id, and $since parameters
+        $qry = 'SELECT user_group.* '.
+          'from user_group join local_group on user_group.id = local_group.group_id '.
+          'order by created desc ';
 
         $group = new User_group();
-        $group->orderBy('created DESC');
-        $group->find();
+
+        $group->query($qry);
 
         while ($group->fetch()) {
             $groups[] = clone($group);
index 0345a9bc0781e7cb986d8ac9207cb1eda53672d4..bff8313b5c55e993165ee7a798f015ae8e6fb90f 100644 (file)
@@ -32,8 +32,6 @@ if (!defined('STATUSNET')) {
     exit(1);
 }
 
-require_once INSTALLDIR . '/lib/api.php';
-
 /**
  * Gives a full dump of configuration variables for this instance
  * of StatusNet, minus variables that may be security-sensitive (like
index 0bb4860ea7f6009ac185d711e2cbf327d6410523..04456ffea4826456862de6de03b165f51f461919 100644 (file)
@@ -107,8 +107,6 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction
         $sitename   = common_config('site', 'name');
         $avatar     = $this->group->homepage_logo;
         $title      = sprintf(_("%s timeline"), $this->group->nickname);
-        $taguribase = TagURI::base();
-        $id         = "tag:$taguribase:GroupTimeline:" . $this->group->id;
 
         $subtitle   = sprintf(
             _('Updates from %1$s on %2$s!'),
@@ -138,19 +136,9 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction
 
             try {
 
-                // If this was called using an integer ID, i.e.: using the canonical
-                // URL for this group's feed, then pass the Group object into the feed, 
-                // so the OStatus plugin, and possibly other plugins, can access it. 
-                // Feels sorta hacky. -- Z
+                $atom = new AtomGroupNoticeFeed($this->group);
 
-                $atom = null;
-                $id = $this->arg('id');
-
-                if (strval(intval($id)) === strval($id)) {
-                    $atom = new AtomGroupNoticeFeed($this->group);
-                } else {
-                    $atom = new AtomGroupNoticeFeed();
-                }
+                // @todo set all this Atom junk up inside the feed class
 
                 $atom->setId($id);
                 $atom->setTitle($title);
@@ -169,6 +157,8 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction
                     $aargs['id'] = $id;
                 }
 
+                $atom->setId($this->getSelfUri('ApiTimelineGroup', $aargs));
+
                 $atom->addLink(
                     $this->getSelfUri('ApiTimelineGroup', $aargs),
                     array('rel' => 'self', 'type' => 'application/atom+xml')
index 3e849cc786c56b69c77b67c5ae6eee3685cdbd8b..b3ded97c0fc09152b985e62fa93c3fb3929e2627 100644 (file)
@@ -116,8 +116,6 @@ class ApiTimelineUserAction extends ApiBareAuthAction
 
         $sitename   = common_config('site', 'name');
         $title      = sprintf(_("%s timeline"), $this->user->nickname);
-        $taguribase = TagURI::base();
-        $id         = "tag:$taguribase:UserTimeline:" . $this->user->id;
         $link       = common_local_url(
             'showstream',
             array('nickname' => $this->user->nickname)
@@ -148,21 +146,10 @@ class ApiTimelineUserAction extends ApiBareAuthAction
 
             header('Content-Type: application/atom+xml; charset=utf-8');
 
-            // If this was called using an integer ID, i.e.: using the canonical
-            // URL for this user's feed, then pass the User object into the feed,
-            // so the OStatus plugin, and possibly other plugins, can access it.
-            // Feels sorta hacky. -- Z
+            // @todo set all this Atom junk up inside the feed class
 
-            $atom = null;
-            $id = $this->arg('id');
-
-            if (strval(intval($id)) === strval($id)) {
-                $atom = new AtomUserNoticeFeed($this->user);
-            } else {
-                $atom = new AtomUserNoticeFeed();
-            }
+            $atom = new AtomUserNoticeFeed($this->user);
 
-            $atom->setId($id);
             $atom->setTitle($title);
             $atom->setSubtitle($subtitle);
             $atom->setLogo($logo);
@@ -181,6 +168,8 @@ class ApiTimelineUserAction extends ApiBareAuthAction
                 $aargs['id'] = $id;
             }
 
+            $atom->setId($this->getSelfUri('ApiTimelineUser', $aargs));
+
             $atom->addLink(
                 $this->getSelfUri('ApiTimelineUser', $aargs),
                 array('rel' => 'self', 'type' => 'application/atom+xml')
index 0b4caf5bf34e742928bba6aed4e3651a271e2a43..a0598db270e64767f3432fd3ff5e18cf1d4601fe 100644 (file)
@@ -74,7 +74,14 @@ class BlockedfromgroupAction extends GroupDesignAction
             return false;
         }
 
-        $this->group = User_group::staticGet('nickname', $nickname);
+        $local = Local_group::staticGet('nickname', $nickname);
+
+        if (!$local) {
+            $this->clientError(_('No such group.'), 404);
+            return false;
+        }
+
+        $this->group = User_group::staticGet('id', $local->group_id);
 
         if (!$this->group) {
             $this->clientError(_('No such group.'), 404);
index ad0b6e185dcfe97c801a0787e382a4982bb21be6..4b596cade95cb22871904d1b0ed34781fea56f30 100644 (file)
@@ -86,10 +86,14 @@ class EditgroupAction extends GroupDesignAction
         }
 
         $groupid = $this->trimmed('groupid');
+
         if ($groupid) {
             $this->group = User_group::staticGet('id', $groupid);
         } else {
-            $this->group = User_group::staticGet('nickname', $nickname);
+            $local = Local_group::staticGet('nickname', $nickname);
+            if ($local) {
+                $this->group = User_group::staticGet('id', $local->group_id);
+            }
         }
 
         if (!$this->group) {
@@ -245,6 +249,7 @@ class EditgroupAction extends GroupDesignAction
         $this->group->homepage    = $homepage;
         $this->group->description = $description;
         $this->group->location    = $location;
+        $this->group->mainpage    = common_local_url('showgroup', array('nickname' => $nickname));
 
         $result = $this->group->update($orig);
 
@@ -259,6 +264,12 @@ class EditgroupAction extends GroupDesignAction
             $this->serverError(_('Could not create aliases.'));
         }
 
+        if ($nickname != $orig->nickname) {
+            common_log(LOG_INFO, "Saving local group info.");
+            $local = Local_group::staticGet('group_id', $this->group->id);
+            $local->setNickname($nickname);
+        }
+
         $this->group->query('COMMIT');
 
         if ($this->group->nickname != $orig->nickname) {
@@ -272,10 +283,10 @@ class EditgroupAction extends GroupDesignAction
 
     function nicknameExists($nickname)
     {
-        $group = User_group::staticGet('nickname', $nickname);
+        $group = Local_group::staticGet('nickname', $nickname);
 
         if (!empty($group) &&
-            $group->id != $this->group->id) {
+            $group->group_id != $this->group->id) {
             return true;
         }
 
index f5fd7fe885e993adb4e2fa1fe64b468a3d6bdb81..ebdf1cee2567555748fe04e9423203f4f1a7131b 100644 (file)
@@ -56,7 +56,14 @@ class FoafGroupAction extends Action
             return false;
         }
 
-        $this->group = User_group::staticGet('nickname', $this->nickname);
+        $local = Local_group::staticGet('nickname', $nickname);
+
+        if (!$local) {
+            $this->clientError(_('No such group.'), 404);
+            return false;
+        }
+
+        $this->group = User_group::staticGet('id', $local->group_id);
 
         if (!$this->group) {
             $this->clientError(_('No such group.'), 404);
@@ -113,7 +120,7 @@ class FoafGroupAction extends Action
         if ($this->group->homepage_logo) {
             $this->element('depiction', array('rdf:resource' => $this->group->homepage_logo));
         }
-        
+
         $members = $this->group->getMembers();
         $member_details = array();
         while ($members->fetch()) {
@@ -123,7 +130,7 @@ class FoafGroupAction extends Action
                                         );
             $this->element('member', array('rdf:resource' => $member_uri));
         }
-        
+
         $admins = $this->group->getAdmins();
         while ($admins->fetch()) {
             $admin_uri = common_local_url('userbyid', array('id'=>$admins->id));
@@ -132,7 +139,7 @@ class FoafGroupAction extends Action
         }
 
         $this->elementEnd('Group');
-        
+
         ksort($member_details);
         foreach ($member_details as $uri => $details) {
             if ($details['is_admin'])
@@ -158,7 +165,7 @@ class FoafGroupAction extends Action
                                         ));
             }
         }
-        
+
         $this->elementEnd('rdf:RDF');
         $this->endXML();
     }
index e290ba5141e270ac8d4295bba1eb88872f0d53fd..526226a285715914486ffc3eb0905f60c4ebf13c 100644 (file)
@@ -90,7 +90,10 @@ class GroupDesignSettingsAction extends DesignSettingsAction
         if ($groupid) {
             $this->group = User_group::staticGet('id', $groupid);
         } else {
-            $this->group = User_group::staticGet('nickname', $nickname);
+            $local = Local_group::staticGet('nickname', $nickname);
+            if ($local) {
+                $this->group = User_group::staticGet('id', $local->group_id);
+            }
         }
 
         if (!$this->group) {
index 3c9b562962e34944ab7864b8f96157de90d114b7..f414a23cc31369665abd68eb0b69e8b899c95b07 100644 (file)
@@ -92,7 +92,10 @@ class GrouplogoAction extends GroupDesignAction
         if ($groupid) {
             $this->group = User_group::staticGet('id', $groupid);
         } else {
-            $this->group = User_group::staticGet('nickname', $nickname);
+            $local = Local_group::staticGet('nickname', $nickname);
+            if ($local) {
+                $this->group = User_group::staticGet('id', $local->group_id);
+            }
         }
 
         if (!$this->group) {
index f16e972a4194a6eee1a0fd60e2c4fad70881b36d..a16debd7b068ecd4e69deec35d61e42475f83752 100644 (file)
@@ -77,7 +77,14 @@ class GroupmembersAction extends GroupDesignAction
             return false;
         }
 
-        $this->group = User_group::staticGet('nickname', $nickname);
+        $local = Local_group::staticGet('nickname', $nickname);
+
+        if (!$local) {
+            $this->clientError(_('No such group.'), 404);
+            return false;
+        }
+
+        $this->group = User_group::staticGet('id', $local->group_id);
 
         if (!$this->group) {
             $this->clientError(_('No such group.'), 404);
index 866fc66eb117685ef60c8ee6b21563d594097e63..490f6f945cac57b509f968b3f771aea5b3f3c5c5 100644 (file)
@@ -92,7 +92,14 @@ class groupRssAction extends Rss10Action
             return false;
         }
 
-        $this->group = User_group::staticGet('nickname', $nickname);
+        $local = Local_group::staticGet('nickname', $nickname);
+
+        if (!$local) {
+            $this->clientError(_('No such group.'), 404);
+            return false;
+        }
+
+        $this->group = User_group::staticGet('id', $local->group_id);
 
         if (!$this->group) {
             $this->clientError(_('No such group.'), 404);
index 10a1d5964d374ac2d18118223b3f297ca5211ed5..8aacff8b0ecd1cd2efe86de5b35a6a7b90312f03 100644 (file)
@@ -109,17 +109,21 @@ class GroupsAction extends Action
         }
 
         $offset = ($this->page-1) * GROUPS_PER_PAGE;
-        $limit =  GROUPS_PER_PAGE + 1;
+        $limit  = GROUPS_PER_PAGE + 1;
+
+        $qry = 'SELECT user_group.* '.
+          'from user_group join local_group on user_group.id = local_group.group_id '.
+          'order by user_group.created desc '.
+          'limit ' . $limit . ' offset ' . $offset;
 
         $groups = new User_group();
-        $groups->orderBy('created DESC');
-        $groups->limit($offset, $limit);
 
         $cnt = 0;
-        if ($groups->find()) {
-            $gl = new GroupList($groups, null, $this);
-            $cnt = $gl->show();
-        }
+
+        $groups->query($qry);
+
+        $gl = new GroupList($groups, null, $this);
+        $cnt = $gl->show();
 
         $this->pagination($this->page > 1, $cnt > GROUPS_PER_PAGE,
                           $this->page, 'groups');
diff --git a/actions/hcard.php b/actions/hcard.php
new file mode 100644 (file)
index 0000000..55d0f65
--- /dev/null
@@ -0,0 +1,120 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show the user's hcard
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Personal
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * User profile page
+ *
+ * @category Personal
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
+ * @link     http://status.net/
+ */
+
+class HcardAction extends Action
+{
+    var $user;
+    var $profile;
+
+    function prepare($args)
+    {
+        parent::prepare($args);
+
+        $nickname_arg = $this->arg('nickname');
+        $nickname     = common_canonical_nickname($nickname_arg);
+
+        // Permanent redirect on non-canonical nickname
+
+        if ($nickname_arg != $nickname) {
+            $args = array('nickname' => $nickname);
+            common_redirect(common_local_url('hcard', $args), 301);
+            return false;
+        }
+
+        $this->user = User::staticGet('nickname', $nickname);
+
+        if (!$this->user) {
+            $this->clientError(_('No such user.'), 404);
+            return false;
+        }
+
+        $this->profile = $this->user->getProfile();
+
+        if (!$this->profile) {
+            $this->serverError(_('User has no profile.'));
+            return false;
+        }
+
+        return true;
+    }
+
+    function handle($args)
+    {
+        parent::handle($args);
+        $this->showPage();
+    }
+
+    function title()
+    {
+        return $this->profile->getBestName();
+    }
+
+    function showContent()
+    {
+        $up = new ShortUserProfile($this, $this->user, $this->profile);
+        $up->show();
+    }
+
+    function showHeader()
+    {
+        return;
+    }
+
+    function showAside()
+    {
+        return;
+    }
+
+    function showSecondaryNav()
+    {
+        return;
+    }
+}
+
+class ShortUserProfile extends UserProfile
+{
+    function showEntityActions()
+    {
+        return;
+    }
+}
\ No newline at end of file
index 235e5ab4c2c8ea792de834ce745079bd7c7dc45c..f87e5dae294a281480fbd626026ce0a1a9f75f93 100644 (file)
@@ -62,23 +62,33 @@ class JoingroupAction extends Action
         }
 
         $nickname_arg = $this->trimmed('nickname');
-        $nickname = common_canonical_nickname($nickname_arg);
+        $id = intval($this->arg('id'));
+        if ($id) {
+            $this->group = User_group::staticGet('id', $id);
+        } else if ($nickname_arg) {
+            $nickname = common_canonical_nickname($nickname_arg);
+
+            // Permanent redirect on non-canonical nickname
+
+            if ($nickname_arg != $nickname) {
+                $args = array('nickname' => $nickname);
+                common_redirect(common_local_url('leavegroup', $args), 301);
+                return false;
+            }
 
-        // Permanent redirect on non-canonical nickname
+            $local = Local_group::staticGet('nickname', $nickname);
 
-        if ($nickname_arg != $nickname) {
-            $args = array('nickname' => $nickname);
-            common_redirect(common_local_url('joingroup', $args), 301);
-            return false;
-        }
+            if (!$local) {
+                $this->clientError(_('No such group.'), 404);
+                return false;
+            }
 
-        if (!$nickname) {
-            $this->clientError(_('No nickname.'), 404);
+            $this->group = User_group::staticGet('id', $local->group_id);
+        } else {
+            $this->clientError(_('No nickname or ID.'), 404);
             return false;
         }
 
-        $this->group = User_group::staticGet('nickname', $nickname);
-
         if (!$this->group) {
             $this->clientError(_('No such group.'), 404);
             return false;
index 9b9d83b6caae243befbd9e43a43eb6b137be53f5..329b5aafe1e87a306b515c889b52d906155b2aec 100644 (file)
@@ -62,23 +62,33 @@ class LeavegroupAction extends Action
         }
 
         $nickname_arg = $this->trimmed('nickname');
-        $nickname = common_canonical_nickname($nickname_arg);
+        $id = intval($this->arg('id'));
+        if ($id) {
+            $this->group = User_group::staticGet('id', $id);
+        } else if ($nickname_arg) {
+            $nickname = common_canonical_nickname($nickname_arg);
+
+            // Permanent redirect on non-canonical nickname
+
+            if ($nickname_arg != $nickname) {
+                $args = array('nickname' => $nickname);
+                common_redirect(common_local_url('leavegroup', $args), 301);
+                return false;
+            }
 
-        // Permanent redirect on non-canonical nickname
+            $local = Local_group::staticGet('nickname', $nickname);
 
-        if ($nickname_arg != $nickname) {
-            $args = array('nickname' => $nickname);
-            common_redirect(common_local_url('leavegroup', $args), 301);
-            return false;
-        }
+            if (!$local) {
+                $this->clientError(_('No such group.'), 404);
+                return false;
+            }
 
-        if (!$nickname) {
-            $this->clientError(_('No nickname.'), 404);
+            $this->group = User_group::staticGet('id', $local->group_id);
+        } else {
+            $this->clientError(_('No nickname or ID.'), 404);
             return false;
         }
 
-        $this->group = User_group::staticGet('nickname', $nickname);
-
         if (!$this->group) {
             $this->clientError(_('No such group.'), 404);
             return false;
index 25da7f8fc75ebcf679c88bb83bd4d8e9f94571fe..75bc293ec63e15e4df92af789e115e8a997b2cc2 100644 (file)
@@ -180,6 +180,8 @@ class NewgroupAction extends Action
             }
         }
 
+        $mainpage = common_local_url('showgroup', array('nickname' => $nickname));
+
         $cur = common_current_user();
 
         // Checked in prepare() above
@@ -192,16 +194,18 @@ class NewgroupAction extends Action
                                             'description' => $description,
                                             'location' => $location,
                                             'aliases'  => $aliases,
-                                            'userid'   => $cur->id));
+                                            'userid'   => $cur->id,
+                                            'mainpage' => $mainpage,
+                                            'local'    => true));
 
         common_redirect($group->homeUrl(), 303);
     }
 
     function nicknameExists($nickname)
     {
-        $group = User_group::staticGet('nickname', $nickname);
+        $local = Local_group::staticGet('nickname', $nickname);
 
-        if (!empty($group)) {
+        if (!empty($local)) {
             return true;
         }
 
index eb12389029a096dd0373ae7ed523ddc517092d62..0139ba157de75ca00496a695813d655931027ff5 100644 (file)
@@ -122,7 +122,15 @@ class ShowgroupAction extends GroupDesignAction
             return false;
         }
 
-        $this->group = User_group::staticGet('nickname', $nickname);
+        $local = Local_group::staticGet('nickname', $nickname);
+
+        if (!$local) {
+            common_log(LOG_NOTICE, "Couldn't find local group for nickname '$nickname'");
+            $this->clientError(_('No such group.'), 404);
+            return false;
+        }
+
+        $this->group = User_group::staticGet('id', $local->group_id);
 
         if (!$this->group) {
             $alias = Group_alias::staticGet('alias', $nickname);
index 3745311b6621998b9b4610e0f12fe377304a5126..b1243f393333fdea7245bead2c7c1bda9738e982 100644 (file)
@@ -145,7 +145,7 @@ class SubscribeAction extends Action
             $this->element('title', null, _('Subscribed'));
             $this->elementEnd('head');
             $this->elementStart('body');
-            $unsubscribe = new UnsubscribeForm($this, $this->other->getProfile());
+            $unsubscribe = new UnsubscribeForm($this, $this->other);
             $unsubscribe->show();
             $this->elementEnd('body');
             $this->elementEnd('html');
index e389ddec84f43b2bf44428d7bbf99314e19eed6c..24aa619bd71ff00abe81b9b7c6fba1f87763e04e 100644 (file)
@@ -31,8 +31,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
     exit(1);
 }
 
-require_once INSTALLDIR.'/lib/api.php';
-
 /**
  * Action for outputting search results in Twitter compatible Atom
  * format.
index 741ed78d6388cc19ccfa98979cc5d6a9e601341e..b5c006aa7b9f85f86463b30e9cb61dbcbccc54f6 100644 (file)
@@ -31,7 +31,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
     exit(1);
 }
 
-require_once INSTALLDIR.'/lib/api.php';
 require_once INSTALLDIR.'/lib/jsonsearchresultslist.php';
 
 /**
index 779405e6d64c7a41d2e88f77e9a0915691ca5350..5a04569a22d8d8fbb3d474ee83bbd378678912cc 100644 (file)
@@ -31,8 +31,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
     exit(1);
 }
 
-require_once INSTALLDIR.'/lib/api.php';
-
 /**
  *  Returns the top ten queries that are currently trending
  *
diff --git a/classes/Local_group.php b/classes/Local_group.php
new file mode 100644 (file)
index 0000000..42312ec
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+/**
+ * Table Definition for local_group
+ */
+
+class Local_group extends Memcached_DataObject
+{
+    ###START_AUTOCODE
+    /* the code below is auto generated do not remove the above tag */
+
+    public $__table = 'local_group';                     // table name
+    public $group_id;                        // int(4)  primary_key not_null
+    public $nickname;                        // varchar(64)  unique_key
+    public $created;                         // datetime   not_null default_0000-00-00%2000%3A00%3A00
+    public $modified;                        // timestamp   not_null default_CURRENT_TIMESTAMP
+
+    /* Static get */
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Local_group',$k,$v); }
+
+    /* the code above is auto generated do not remove the tag below */
+    ###END_AUTOCODE
+
+    function sequenceKey()
+    {
+        return array(false, false, false);
+    }
+
+    function setNickname($nickname)
+    {
+        $this->decache();
+        $qry = 'UPDATE local_group set nickname = "'.$nickname.'" where group_id = ' . $this->group_id;
+
+        $result = $this->query($qry);
+
+        if ($result) {
+            $this->nickname = $nickname;
+            $this->fixupTimestamps();
+            $this->encache();
+        } else {
+            common_log_db_error($local, 'UPDATE', __FILE__);
+            throw new ServerException(_('Could not update local group.'));
+        }
+
+        return $result;
+    }
+}
index e8d5c45cb2f2dae58887a9c10907300ae4306d20..ac4640534c48b15264ef8cc3fcd1206be144b4ac 100644 (file)
@@ -121,6 +121,9 @@ class Notice extends Memcached_DataObject
         $result = parent::delete();
     }
 
+    /**
+     * Extract #hashtags from this notice's content and save them to the database.
+     */
     function saveTags()
     {
         /* extract all #hastags */
@@ -129,14 +132,22 @@ class Notice extends Memcached_DataObject
             return true;
         }
 
+        /* Add them to the database */
+        return $this->saveKnownTags($match[1]);
+    }
+
+    /**
+     * Record the given set of hash tags in the db for this notice.
+     * Given tag strings will be normalized and checked for dupes.
+     */
+    function saveKnownTags($hashtags)
+    {
         //turn each into their canonical tag
         //this is needed to remove dupes before saving e.g. #hash.tag = #hashtag
-        $hashtags = array();
-        for($i=0; $i<count($match[1]); $i++) {
-            $hashtags[] = common_canonical_tag($match[1][$i]);
+        for($i=0; $i<count($hashtags); $i++) {
+            $hashtags[$i] = common_canonical_tag($hashtags[$i]);
         }
 
-        /* Add them to the database */
         foreach(array_unique($hashtags) as $hashtag) {
             /* elide characters we don't want in the tag */
             $this->saveTag($hashtag);
@@ -145,6 +156,10 @@ class Notice extends Memcached_DataObject
         return true;
     }
 
+    /**
+     * Record a single hash tag as associated with this notice.
+     * Tag format and uniqueness must be validated by caller.
+     */
     function saveTag($hashtag)
     {
         $tag = new Notice_tag();
@@ -194,6 +209,8 @@ class Notice extends Memcached_DataObject
      *                              place of extracting @-replies from content.
      *              array 'groups' list of group IDs to deliver to, in place of
      *                              extracting ! tags from content
+     *              array 'tags' list of hashtag strings to save with the notice
+     *                           in place of extracting # tags from content
      * @fixme tag override
      *
      * @return Notice
@@ -343,6 +360,8 @@ class Notice extends Memcached_DataObject
 
         $notice->blowOnInsert();
 
+        // Save per-notice metadata...
+
         if (isset($replies)) {
             $notice->saveKnownReplies($replies);
         } else {
@@ -355,6 +374,16 @@ class Notice extends Memcached_DataObject
             $notice->saveGroups();
         }
 
+        if (isset($tags)) {
+            $notice->saveKnownTags($tags);
+        } else {
+            $notice->saveTags();
+        }
+
+        // @fixme pass in data for URLs too?
+        $notice->saveUrls();
+
+        // Prepare inbox delivery, may be queued to background.
         $notice->distribute();
 
         return $notice;
@@ -1067,6 +1096,7 @@ class Notice extends Memcached_DataObject
                            'xmlns:thr' => 'http://purl.org/syndication/thread/1.0',
                            'xmlns:georss' => 'http://www.georss.org/georss',
                            'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/',
+                           'xmlns:media' => 'http://purl.org/syndication/atommedia',
                            'xmlns:poco' => 'http://portablecontacts.net/spec/1.0',
                            'xmlns:ostatus' => 'http://ostatus.org/schema/1.0');
         } else {
index 1382aa407c7f74eb585ba7a98a6a88d755cfdce0..7240e27037b32c91a6b6ba37a01e566fbb93b850 100644 (file)
@@ -10,21 +10,23 @@ class User_group extends Memcached_DataObject
 
     public $__table = 'user_group';                      // table name
     public $id;                              // int(4)  primary_key not_null
-    public $nickname;                        // varchar(64)  unique_key
+    public $nickname;                        // varchar(64)
     public $fullname;                        // varchar(255)
     public $homepage;                        // varchar(255)
-    public $description;                     // text()
+    public $description;                     // text
     public $location;                        // varchar(255)
     public $original_logo;                   // varchar(255)
     public $homepage_logo;                   // varchar(255)
     public $stream_logo;                     // varchar(255)
     public $mini_logo;                       // varchar(255)
     public $design_id;                       // int(4)
-    public $created;                         // datetime()   not_null
-    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
+    public $created;                         // datetime   not_null default_0000-00-00%2000%3A00%3A00
+    public $modified;                        // timestamp   not_null default_CURRENT_TIMESTAMP
+    public $uri;                             // varchar(255)  unique_key
+    public $mainpage;                        // varchar(255)
 
     /* Static get */
-    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('User_group',$k,$v); }
+    function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('User_group',$k,$v); }
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
@@ -41,13 +43,33 @@ class User_group extends Memcached_DataObject
     {
         $url = null;
         if (Event::handle('StartUserGroupHomeUrl', array($this, &$url))) {
-            $url = common_local_url('showgroup',
-                                    array('nickname' => $this->nickname));
+            // normally stored in mainpage, but older ones may be null
+            if (!empty($this->mainpage)) {
+                $url = $this->mainpage;
+            } else {
+                $url = common_local_url('showgroup',
+                                        array('nickname' => $this->nickname));
+            }
         }
         Event::handle('EndUserGroupHomeUrl', array($this, &$url));
         return $url;
     }
 
+    function getUri()
+    {
+        $uri = null;
+        if (Event::handle('StartUserGroupGetUri', array($this, &$uri))) {
+            if (!empty($this->uri)) {
+                $uri = $this->uri;
+            } else {
+                $uri = common_local_url('groupbyid',
+                                        array('id' => $this->id));
+            }
+        }
+        Event::handle('EndUserGroupGetUri', array($this, &$uri));
+        return $uri;
+    }
+
     function permalink()
     {
         $url = null;
@@ -377,25 +399,41 @@ class User_group extends Memcached_DataObject
         return $xs->getString();
     }
 
+    /**
+     * Returns an XML string fragment with group information as an
+     * Activity Streams <activity:subject> element.
+     *
+     * Assumes that 'activity' namespace has been previously defined.
+     *
+     * @return string
+     */
     function asActivitySubject()
     {
-        $xs = new XMLStringer(true);
+        return $this->asActivityNoun('subject');
+    }
 
-        $xs->elementStart('activity:subject');
-        $xs->element('activity:object', null, 'http://activitystrea.ms/schema/1.0/group');
-        $xs->element('id', null, $this->permalink());
-        $xs->element('title', null, $this->getBestName());
-        $xs->element(
-            'link', array(
-                'rel'  => 'avatar',
-                'href' =>  empty($this->homepage_logo)
-                    ? User_group::defaultLogo(AVATAR_PROFILE_SIZE)
-                    : $this->homepage_logo
-            )
-        );
-        $xs->elementEnd('activity:subject');
+    /**
+     * Returns an XML string fragment with group information as an
+     * Activity Streams noun object with the given element type.
+     *
+     * Assumes that 'activity', 'georss', and 'poco' namespace has been
+     * previously defined.
+     *
+     * @param string $element one of 'actor', 'subject', 'object', 'target'
+     *
+     * @return string
+     */
+    function asActivityNoun($element)
+    {
+        $noun = ActivityObject::fromGroup($this);
+        return $noun->asString('activity:' . $element);
+    }
 
-        return $xs->getString();
+    function getAvatar()
+    {
+        return empty($this->homepage_logo)
+            ? User_group::defaultLogo(AVATAR_PROFILE_SIZE)
+            : $this->homepage_logo;
     }
 
     static function register($fields) {
@@ -413,28 +451,31 @@ class User_group extends Memcached_DataObject
         $group->homepage    = $homepage;
         $group->description = $description;
         $group->location    = $location;
+        $group->uri         = $uri;
+        $group->mainpage    = $mainpage;
         $group->created     = common_sql_now();
 
         $result = $group->insert();
 
         if (!$result) {
             common_log_db_error($group, 'INSERT', __FILE__);
-            $this->serverError(
-                _('Could not create group.'),
-                500,
-                $this->format
-            );
-            return;
+            throw new ServerException(_('Could not create group.'));
         }
+
+        if (!isset($uri) || empty($uri)) {
+            $orig = clone($group);
+            $group->uri = common_local_url('groupbyid', array('id' => $group->id));
+            $result = $group->update($orig);
+            if (!$result) {
+                common_log_db_error($group, 'UPDATE', __FILE__);
+                throw new ServerException(_('Could not set group uri.'));
+            }
+        }
+
         $result = $group->setAliases($aliases);
 
         if (!$result) {
-            $this->serverError(
-                _('Could not create aliases.'),
-                500,
-                $this->format
-            );
-            return;
+            throw new ServerException(_('Could not create aliases.'));
         }
 
         $member = new Group_member();
@@ -448,12 +489,22 @@ class User_group extends Memcached_DataObject
 
         if (!$result) {
             common_log_db_error($member, 'INSERT', __FILE__);
-            $this->serverError(
-                _('Could not set group membership.'),
-                500,
-                $this->format
-            );
-            return;
+            throw new ServerException(_('Could not set group membership.'));
+        }
+
+        if ($local) {
+            $local_group = new Local_group();
+
+            $local_group->group_id = $group->id;
+            $local_group->nickname = $nickname;
+            $local_group->created  = common_sql_now();
+
+            $result = $local_group->insert();
+
+            if (!$result) {
+                common_log_db_error($local_group, 'INSERT', __FILE__);
+                throw new ServerException(_('Could not save local group info.'));
+            }
         }
 
         $group->query('COMMIT');
index 81c1b68b236eedc1b5994c12d7cd96aec11ff0cc..3fb8ee208ba17c850abd0df50544028b0e504712 100644 (file)
@@ -245,13 +245,6 @@ modified = 384
 group_id = K
 profile_id = K
 
-[invitation]
-code = 130
-user_id = 129
-address = 130
-address_type = 130
-created = 142
-
 [inbox]
 user_id = 129
 notice_ids = 66
@@ -259,9 +252,26 @@ notice_ids = 66
 [inbox__keys]
 user_id = K
 
+[invitation]
+code = 130
+user_id = 129
+address = 130
+address_type = 130
+created = 142
+
 [invitation__keys]
 code = K
 
+[local_group]
+group_id = 129
+nickname = 2
+created = 142
+modified = 384
+
+[local_group__keys]
+group_id = K
+nickname = U
+
 [location_namespace]
 id = 129
 description = 2
@@ -369,7 +379,7 @@ icon = 130
 source_url = 2
 organization = 2
 homepage = 2
-callback_url = 130
+callback_url = 2
 type = 17
 access_type = 17
 created = 142
@@ -440,13 +450,13 @@ tag = K
 
 [queue_item]
 id = 129
-frame = 66
+frame = 194
 transport = 130
 created = 142
 claimed = 14
 
 [queue_item__keys]
-id = K
+id = N
 
 [related_group]
 group_id = 129
@@ -593,10 +603,11 @@ mini_logo = 2
 design_id = 1
 created = 142
 modified = 384
+uri = 2
+mainpage = 2
 
 [user_group__keys]
 id = N
-nickname = U
 
 [user_openid]
 canonical = 130
@@ -627,4 +638,3 @@ modified = 384
 
 [user_location_prefs__keys]
 user_id = K
-
diff --git a/db/beta5tobeta6.sql b/db/beta5tobeta6.sql
new file mode 100644 (file)
index 0000000..e9dff17
--- /dev/null
@@ -0,0 +1,28 @@
+alter table oauth_application
+    modify column name varchar(255) not null unique key comment 'name of the application',
+    modify column access_type tinyint default 0 comment 'access type, bit 1 = read, bit 2 = write';
+
+alter table user_group
+add column uri varchar(255) unique key comment 'universal identifier',
+add column mainpage varchar(255) comment 'page for group info to link to',
+drop index nickname;
+
+create table conversation (
+     id integer auto_increment primary key comment 'unique identifier',
+     uri varchar(225) unique comment 'URI of the conversation',
+     created datetime not null comment 'date this record was created',
+     modified timestamp comment 'date this record was modified'
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table local_group (
+    group_id integer primary key comment 'group represented' references user_group (id),
+    nickname varchar(64) unique key comment 'group represented',
+
+    created datetime not null comment 'date this record was created',
+    modified timestamp comment 'date this record was modified'
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+insert into local_group (group_id, nickname, created)
+select id, nickname, created from user_group;
+
index 97117c80aab6b69898012f695d0902eb6ee1554c..4158f0167db2a9f9def64f29ac8fff1fef574382 100644 (file)
@@ -406,7 +406,7 @@ create table profile_block (
 create table user_group (
     id integer auto_increment primary key comment 'unique identifier',
 
-    nickname varchar(64) unique key comment 'nickname for addressing',
+    nickname varchar(64) comment 'nickname for addressing',
     fullname varchar(255) comment 'display name',
     homepage varchar(255) comment 'URL, cached so we dont regenerate',
     description text comment 'group description',
@@ -421,6 +421,9 @@ create table user_group (
     created datetime not null comment 'date this record was created',
     modified timestamp comment 'date this record was modified',
 
+    uri varchar(255) unique key comment 'universal identifier',
+    mainpage varchar(255) comment 'page for group info to link to',
+
     index user_group_nickname_idx (nickname)
 
 ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
@@ -641,3 +644,13 @@ create table conversation (
     modified timestamp comment 'date this record was modified'
 ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
 
+create table local_group (
+
+   group_id integer primary key comment 'group represented' references user_group (id),
+   nickname varchar(64) unique key comment 'group represented',
+
+   created datetime not null comment 'date this record was created',
+   modified timestamp comment 'date this record was modified'
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
index 237e1b9081302568b4286f9028e1380b1492db10..b3b95307a13602285313ecc90a9707b10bf270b6 100644 (file)
@@ -1,5 +1,5 @@
 /*!
- * jQuery JavaScript Library v1.4.1
+ * jQuery JavaScript Library v1.4.2
  * http://jquery.com/
  *
  * Copyright 2010, John Resig
@@ -11,7 +11,7 @@
  * Copyright 2010, The Dojo Foundation
  * Released under the MIT, BSD, and GPL Licenses.
  *
- * Date: Mon Jan 25 19:43:33 2010 -0500
+ * Date: Sat Feb 13 22:33:48 2010 -0500
  */
 (function( window, undefined ) {
 
@@ -86,6 +86,15 @@ jQuery.fn = jQuery.prototype = {
                        this.length = 1;
                        return this;
                }
+               
+               // The body element only exists once, optimize finding it
+               if ( selector === "body" && !context ) {
+                       this.context = document;
+                       this[0] = document.body;
+                       this.selector = "body";
+                       this.length = 1;
+                       return this;
+               }
 
                // Handle HTML strings
                if ( typeof selector === "string" ) {
@@ -116,7 +125,9 @@ jQuery.fn = jQuery.prototype = {
                                                ret = buildFragment( [ match[1] ], [ doc ] );
                                                selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes;
                                        }
-
+                                       
+                                       return jQuery.merge( this, selector );
+                                       
                                // HANDLE: $("#id")
                                } else {
                                        elem = document.getElementById( match[2] );
@@ -143,6 +154,7 @@ jQuery.fn = jQuery.prototype = {
                                this.selector = selector;
                                this.context = document;
                                selector = document.getElementsByTagName( selector );
+                               return jQuery.merge( this, selector );
 
                        // HANDLE: $(expr, $(...))
                        } else if ( !context || context.jquery ) {
@@ -165,16 +177,14 @@ jQuery.fn = jQuery.prototype = {
                        this.context = selector.context;
                }
 
-               return jQuery.isArray( selector ) ?
-                       this.setArray( selector ) :
-                       jQuery.makeArray( selector, this );
+               return jQuery.makeArray( selector, this );
        },
 
        // Start with an empty selector
        selector: "",
 
        // The current version of jQuery being used
-       jquery: "1.4.1",
+       jquery: "1.4.2",
 
        // The default length of a jQuery object is 0
        length: 0,
@@ -204,7 +214,14 @@ jQuery.fn = jQuery.prototype = {
        // (returning the new matched element set)
        pushStack: function( elems, name, selector ) {
                // Build a new jQuery matched element set
-               var ret = jQuery( elems || null );
+               var ret = jQuery();
+
+               if ( jQuery.isArray( elems ) ) {
+                       push.apply( ret, elems );
+               
+               } else {
+                       jQuery.merge( ret, elems );
+               }
 
                // Add the old object onto the stack (as a reference)
                ret.prevObject = this;
@@ -221,18 +238,6 @@ jQuery.fn = jQuery.prototype = {
                return ret;
        },
 
-       // Force the current matched set of elements to become
-       // the specified array of elements (destroying the stack in the process)
-       // You should use pushStack() in order to do this, but maintain the stack
-       setArray: function( elems ) {
-               // Resetting the length to 0, then using the native Array push
-               // is a super-fast way to populate an object with array-like properties
-               this.length = 0;
-               push.apply( this, elems );
-
-               return this;
-       },
-
        // Execute a callback for every element in the matched set.
        // (You can seed the arguments with an array of args, but this is
        // only used internally.)
@@ -492,6 +497,9 @@ jQuery.extend({
                if ( typeof data !== "string" || !data ) {
                        return null;
                }
+
+               // Make sure leading/trailing whitespace is removed (IE can't handle it)
+               data = jQuery.trim( data );
                
                // Make sure the incoming data is actual JSON
                // Logic borrowed from http://json.org/json2.js
@@ -619,6 +627,7 @@ jQuery.extend({
                        for ( var l = second.length; j < l; j++ ) {
                                first[ i++ ] = second[ j ];
                        }
+               
                } else {
                        while ( second[j] !== undefined ) {
                                first[ i++ ] = second[ j++ ];
@@ -807,7 +816,7 @@ function access( elems, key, value, exec, fn, pass ) {
        }
        
        // Getting an attribute
-       return length ? fn( elems[0], key ) : null;
+       return length ? fn( elems[0], key ) : undefined;
 }
 
 function now() {
@@ -871,7 +880,10 @@ function now() {
                // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
                optSelected: document.createElement("select").appendChild( document.createElement("option") ).selected,
 
+               parentNode: div.removeChild( div.appendChild( document.createElement("div") ) ).parentNode === null,
+
                // Will be defined later
+               deleteExpando: true,
                checkClone: false,
                scriptEval: false,
                noCloneEvent: true,
@@ -893,6 +905,15 @@ function now() {
                delete window[ id ];
        }
 
+       // Test to see if it's possible to delete an expando from an element
+       // Fails in Internet Explorer
+       try {
+               delete script.test;
+       
+       } catch(e) {
+               jQuery.support.deleteExpando = false;
+       }
+
        root.removeChild( script );
 
        if ( div.attachEvent && div.fireEvent ) {
@@ -923,6 +944,7 @@ function now() {
                document.body.appendChild( div );
                jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;
                document.body.removeChild( div ).style.display = 'none';
+
                div = null;
        });
 
@@ -962,7 +984,6 @@ jQuery.props = {
        frameborder: "frameBorder"
 };
 var expando = "jQuery" + now(), uuid = 0, windowData = {};
-var emptyObject = {};
 
 jQuery.extend({
        cache: {},
@@ -988,8 +1009,7 @@ jQuery.extend({
 
                var id = elem[ expando ], cache = jQuery.cache, thisCache;
 
-               // Handle the case where there's no name immediately
-               if ( !name && !id ) {
+               if ( !id && typeof name === "string" && data === undefined ) {
                        return null;
                }
 
@@ -1003,17 +1023,16 @@ jQuery.extend({
                if ( typeof name === "object" ) {
                        elem[ expando ] = id;
                        thisCache = cache[ id ] = jQuery.extend(true, {}, name);
-               } else if ( cache[ id ] ) {
-                       thisCache = cache[ id ];
-               } else if ( typeof data === "undefined" ) {
-                       thisCache = emptyObject;
-               } else {
-                       thisCache = cache[ id ] = {};
+
+               } else if ( !cache[ id ] ) {
+                       elem[ expando ] = id;
+                       cache[ id ] = {};
                }
 
+               thisCache = cache[ id ];
+
                // Prevent overriding the named cache with undefined values
                if ( data !== undefined ) {
-                       elem[ expando ] = id;
                        thisCache[ name ] = data;
                }
 
@@ -1045,15 +1064,11 @@ jQuery.extend({
 
                // Otherwise, we want to remove all of the element's data
                } else {
-                       // Clean up the element expando
-                       try {
-                               delete elem[ expando ];
-                       } catch( e ) {
-                               // IE has trouble directly removing the expando
-                               // but it's ok with using removeAttribute
-                               if ( elem.removeAttribute ) {
-                                       elem.removeAttribute( expando );
-                               }
+                       if ( jQuery.support.deleteExpando ) {
+                               delete elem[ jQuery.expando ];
+
+                       } else if ( elem.removeAttribute ) {
+                               elem.removeAttribute( jQuery.expando );
                        }
 
                        // Completely remove the data cache
@@ -1230,12 +1245,13 @@ jQuery.fn.extend({
                                                elem.className = value;
 
                                        } else {
-                                               var className = " " + elem.className + " ";
+                                               var className = " " + elem.className + " ", setClass = elem.className;
                                                for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
                                                        if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) {
-                                                               elem.className += " " + classNames[c];
+                                                               setClass += " " + classNames[c];
                                                        }
                                                }
+                                               elem.className = jQuery.trim( setClass );
                                        }
                                }
                        }
@@ -1264,7 +1280,7 @@ jQuery.fn.extend({
                                                for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
                                                        className = className.replace(" " + classNames[c] + " ", " ");
                                                }
-                                               elem.className = className.substring(1, className.length - 1);
+                                               elem.className = jQuery.trim( className );
 
                                        } else {
                                                elem.className = "";
@@ -1520,15 +1536,16 @@ jQuery.extend({
                }
 
                // elem is actually elem.style ... set the style
-               // Using attr for specific style information is now deprecated. Use style insead.
+               // Using attr for specific style information is now deprecated. Use style instead.
                return jQuery.style( elem, name, value );
        }
 });
-var fcleanup = function( nm ) {
-       return nm.replace(/[^\w\s\.\|`]/g, function( ch ) {
-               return "\\" + ch;
-       });
-};
+var rnamespaces = /\.(.*)$/,
+       fcleanup = function( nm ) {
+               return nm.replace(/[^\w\s\.\|`]/g, function( ch ) {
+                       return "\\" + ch;
+               });
+       };
 
 /*
  * A number of helper functions used for managing events.
@@ -1550,107 +1567,104 @@ jQuery.event = {
                        elem = window;
                }
 
+               var handleObjIn, handleObj;
+
+               if ( handler.handler ) {
+                       handleObjIn = handler;
+                       handler = handleObjIn.handler;
+               }
+
                // Make sure that the function being executed has a unique ID
                if ( !handler.guid ) {
                        handler.guid = jQuery.guid++;
                }
 
-               // if data is passed, bind to handler
-               if ( data !== undefined ) {
-                       // Create temporary function pointer to original handler
-                       var fn = handler;
-
-                       // Create unique handler function, wrapped around original handler
-                       handler = jQuery.proxy( fn );
+               // Init the element's event structure
+               var elemData = jQuery.data( elem );
 
-                       // Store data in unique handler
-                       handler.data = data;
+               // If no elemData is found then we must be trying to bind to one of the
+               // banned noData elements
+               if ( !elemData ) {
+                       return;
                }
 
-               // Init the element's event structure
-               var events = jQuery.data( elem, "events" ) || jQuery.data( elem, "events", {} ),
-                       handle = jQuery.data( elem, "handle" ), eventHandle;
+               var events = elemData.events = elemData.events || {},
+                       eventHandle = elemData.handle, eventHandle;
 
-               if ( !handle ) {
-                       eventHandle = function() {
+               if ( !eventHandle ) {
+                       elemData.handle = eventHandle = function() {
                                // Handle the second event of a trigger and when
                                // an event is called after a page has unloaded
                                return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
                                        jQuery.event.handle.apply( eventHandle.elem, arguments ) :
                                        undefined;
                        };
-
-                       handle = jQuery.data( elem, "handle", eventHandle );
-               }
-
-               // If no handle is found then we must be trying to bind to one of the
-               // banned noData elements
-               if ( !handle ) {
-                       return;
                }
 
                // Add elem as a property of the handle function
-               // This is to prevent a memory leak with non-native
-               // event in IE.
-               handle.elem = elem;
+               // This is to prevent a memory leak with non-native events in IE.
+               eventHandle.elem = elem;
 
                // Handle multiple events separated by a space
                // jQuery(...).bind("mouseover mouseout", fn);
-               types = types.split( /\s+/ );
+               types = types.split(" ");
 
-               var type, i = 0;
+               var type, i = 0, namespaces;
 
                while ( (type = types[ i++ ]) ) {
-                       // Namespaced event handlers
-                       var namespaces = type.split(".");
-                       type = namespaces.shift();
+                       handleObj = handleObjIn ?
+                               jQuery.extend({}, handleObjIn) :
+                               { handler: handler, data: data };
 
-                       if ( i > 1 ) {
-                               handler = jQuery.proxy( handler );
+                       // Namespaced event handlers
+                       if ( type.indexOf(".") > -1 ) {
+                               namespaces = type.split(".");
+                               type = namespaces.shift();
+                               handleObj.namespace = namespaces.slice(0).sort().join(".");
 
-                               if ( data !== undefined ) {
-                                       handler.data = data;
-                               }
+                       } else {
+                               namespaces = [];
+                               handleObj.namespace = "";
                        }
 
-                       handler.type = namespaces.slice(0).sort().join(".");
+                       handleObj.type = type;
+                       handleObj.guid = handler.guid;
 
                        // Get the current list of functions bound to this event
                        var handlers = events[ type ],
-                               special = this.special[ type ] || {};
+                               special = jQuery.event.special[ type ] || {};
 
                        // Init the event handler queue
                        if ( !handlers ) {
-                               handlers = events[ type ] = {};
+                               handlers = events[ type ] = [];
 
                                // Check for a special event handler
                                // Only use addEventListener/attachEvent if the special
                                // events handler returns false
-                               if ( !special.setup || special.setup.call( elem, data, namespaces, handler) === false ) {
+                               if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
                                        // Bind the global event handler to the element
                                        if ( elem.addEventListener ) {
-                                               elem.addEventListener( type, handle, false );
+                                               elem.addEventListener( type, eventHandle, false );
+
                                        } else if ( elem.attachEvent ) {
-                                               elem.attachEvent( "on" + type, handle );
+                                               elem.attachEvent( "on" + type, eventHandle );
                                        }
                                }
                        }
                        
                        if ( special.add ) { 
-                               var modifiedHandler = special.add.call( elem, handler, data, namespaces, handlers ); 
-                               if ( modifiedHandler && jQuery.isFunction( modifiedHandler ) ) { 
-                                       modifiedHandler.guid = modifiedHandler.guid || handler.guid; 
-                                       modifiedHandler.data = modifiedHandler.data || handler.data; 
-                                       modifiedHandler.type = modifiedHandler.type || handler.type; 
-                                       handler = modifiedHandler; 
-                               } 
-                       } 
-                       
+                               special.add.call( elem, handleObj ); 
+
+                               if ( !handleObj.handler.guid ) {
+                                       handleObj.handler.guid = handler.guid;
+                               }
+                       }
+
                        // Add the function to the element's handler list
-                       handlers[ handler.guid ] = handler;
+                       handlers.push( handleObj );
 
                        // Keep track of which events have been used, for global triggering
-                       this.global[ type ] = true;
+                       jQuery.event.global[ type ] = true;
                }
 
                // Nullify elem to prevent memory leaks in IE
@@ -1660,90 +1674,121 @@ jQuery.event = {
        global: {},
 
        // Detach an event or set of events from an element
-       remove: function( elem, types, handler ) {
+       remove: function( elem, types, handler, pos ) {
                // don't do events on text and comment nodes
                if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
                        return;
                }
 
-               var events = jQuery.data( elem, "events" ), ret, type, fn;
+               var ret, type, fn, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
+                       elemData = jQuery.data( elem ),
+                       events = elemData && elemData.events;
 
-               if ( events ) {
-                       // Unbind all events for the element
-                       if ( types === undefined || (typeof types === "string" && types.charAt(0) === ".") ) {
-                               for ( type in events ) {
-                                       this.remove( elem, type + (types || "") );
-                               }
-                       } else {
-                               // types is actually an event object here
-                               if ( types.type ) {
-                                       handler = types.handler;
-                                       types = types.type;
+               if ( !elemData || !events ) {
+                       return;
+               }
+
+               // types is actually an event object here
+               if ( types && types.type ) {
+                       handler = types.handler;
+                       types = types.type;
+               }
+
+               // Unbind all events for the element
+               if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
+                       types = types || "";
+
+                       for ( type in events ) {
+                               jQuery.event.remove( elem, type + types );
+                       }
+
+                       return;
+               }
+
+               // Handle multiple events separated by a space
+               // jQuery(...).unbind("mouseover mouseout", fn);
+               types = types.split(" ");
+
+               while ( (type = types[ i++ ]) ) {
+                       origType = type;
+                       handleObj = null;
+                       all = type.indexOf(".") < 0;
+                       namespaces = [];
+
+                       if ( !all ) {
+                               // Namespaced event handlers
+                               namespaces = type.split(".");
+                               type = namespaces.shift();
+
+                               namespace = new RegExp("(^|\\.)" + 
+                                       jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)")
+                       }
+
+                       eventType = events[ type ];
+
+                       if ( !eventType ) {
+                               continue;
+                       }
+
+                       if ( !handler ) {
+                               for ( var j = 0; j < eventType.length; j++ ) {
+                                       handleObj = eventType[ j ];
+
+                                       if ( all || namespace.test( handleObj.namespace ) ) {
+                                               jQuery.event.remove( elem, origType, handleObj.handler, j );
+                                               eventType.splice( j--, 1 );
+                                       }
                                }
 
-                               // Handle multiple events separated by a space
-                               // jQuery(...).unbind("mouseover mouseout", fn);
-                               types = types.split(/\s+/);
-                               var i = 0;
-                               while ( (type = types[ i++ ]) ) {
-                                       // Namespaced event handlers
-                                       var namespaces = type.split(".");
-                                       type = namespaces.shift();
-                                       var all = !namespaces.length,
-                                               cleaned = jQuery.map( namespaces.slice(0).sort(), fcleanup ),
-                                               namespace = new RegExp("(^|\\.)" + cleaned.join("\\.(?:.*\\.)?") + "(\\.|$)"),
-                                               special = this.special[ type ] || {};
-
-                                       if ( events[ type ] ) {
-                                               // remove the given handler for the given type
-                                               if ( handler ) {
-                                                       fn = events[ type ][ handler.guid ];
-                                                       delete events[ type ][ handler.guid ];
-
-                                               // remove all handlers for the given type
-                                               } else {
-                                                       for ( var handle in events[ type ] ) {
-                                                               // Handle the removal of namespaced events
-                                                               if ( all || namespace.test( events[ type ][ handle ].type ) ) {
-                                                                       delete events[ type ][ handle ];
-                                                               }
-                                                       }
+                               continue;
+                       }
+
+                       special = jQuery.event.special[ type ] || {};
+
+                       for ( var j = pos || 0; j < eventType.length; j++ ) {
+                               handleObj = eventType[ j ];
+
+                               if ( handler.guid === handleObj.guid ) {
+                                       // remove the given handler for the given type
+                                       if ( all || namespace.test( handleObj.namespace ) ) {
+                                               if ( pos == null ) {
+                                                       eventType.splice( j--, 1 );
                                                }
 
                                                if ( special.remove ) {
-                                                       special.remove.call( elem, namespaces, fn);
+                                                       special.remove.call( elem, handleObj );
                                                }
+                                       }
 
-                                               // remove generic event handler if no more handlers exist
-                                               for ( ret in events[ type ] ) {
-                                                       break;
-                                               }
-                                               if ( !ret ) {
-                                                       if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
-                                                               if ( elem.removeEventListener ) {
-                                                                       elem.removeEventListener( type, jQuery.data( elem, "handle" ), false );
-                                                               } else if ( elem.detachEvent ) {
-                                                                       elem.detachEvent( "on" + type, jQuery.data( elem, "handle" ) );
-                                                               }
-                                                       }
-                                                       ret = null;
-                                                       delete events[ type ];
-                                               }
+                                       if ( pos != null ) {
+                                               break;
                                        }
                                }
                        }
 
-                       // Remove the expando if it's no longer used
-                       for ( ret in events ) {
-                               break;
-                       }
-                       if ( !ret ) {
-                               var handle = jQuery.data( elem, "handle" );
-                               if ( handle ) {
-                                       handle.elem = null;
+                       // remove generic event handler if no more handlers exist
+                       if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
+                               if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
+                                       removeEvent( elem, type, elemData.handle );
                                }
-                               jQuery.removeData( elem, "events" );
-                               jQuery.removeData( elem, "handle" );
+
+                               ret = null;
+                               delete events[ type ];
+                       }
+               }
+
+               // Remove the expando if it's no longer used
+               if ( jQuery.isEmptyObject( events ) ) {
+                       var handle = elemData.handle;
+                       if ( handle ) {
+                               handle.elem = null;
+                       }
+
+                       delete elemData.events;
+                       delete elemData.handle;
+
+                       if ( jQuery.isEmptyObject( elemData ) ) {
+                               jQuery.removeData( elem );
                        }
                }
        },
@@ -1774,7 +1819,7 @@ jQuery.event = {
                                event.stopPropagation();
 
                                // Only trigger if we've ever bound an event for it
-                               if ( this.global[ type ] ) {
+                               if ( jQuery.event.global[ type ] ) {
                                        jQuery.each( jQuery.cache, function() {
                                                if ( this.events && this.events[type] ) {
                                                        jQuery.event.trigger( event, data, this.handle.elem );
@@ -1825,9 +1870,12 @@ jQuery.event = {
 
                } else if ( !event.isDefaultPrevented() ) {
                        var target = event.target, old,
-                               isClick = jQuery.nodeName(target, "a") && type === "click";
+                               isClick = jQuery.nodeName(target, "a") && type === "click",
+                               special = jQuery.event.special[ type ] || {};
+
+                       if ( (!special._default || special._default.call( elem, event ) === false) && 
+                               !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
 
-                       if ( !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
                                try {
                                        if ( target[ type ] ) {
                                                // Make sure that we don't accidentally re-trigger the onFOO events
@@ -1837,7 +1885,7 @@ jQuery.event = {
                                                        target[ "on" + type ] = null;
                                                }
 
-                                               this.triggered = true;
+                                               jQuery.event.triggered = true;
                                                target[ type ]();
                                        }
 
@@ -1848,53 +1896,57 @@ jQuery.event = {
                                        target[ "on" + type ] = old;
                                }
 
-                               this.triggered = false;
+                               jQuery.event.triggered = false;
                        }
                }
        },
 
        handle: function( event ) {
-               // returned undefined or false
-               var all, handlers;
+               var all, handlers, namespaces, namespace, events;
 
                event = arguments[0] = jQuery.event.fix( event || window.event );
                event.currentTarget = this;
 
                // Namespaced event handlers
-               var namespaces = event.type.split(".");
-               event.type = namespaces.shift();
+               all = event.type.indexOf(".") < 0 && !event.exclusive;
 
-               // Cache this now, all = true means, any handler
-               all = !namespaces.length && !event.exclusive;
-
-               var namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
+               if ( !all ) {
+                       namespaces = event.type.split(".");
+                       event.type = namespaces.shift();
+                       namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
+               }
 
-               handlers = ( jQuery.data(this, "events") || {} )[ event.type ];
+               var events = jQuery.data(this, "events"), handlers = events[ event.type ];
 
-               for ( var j in handlers ) {
-                       var handler = handlers[ j ];
+               if ( events && handlers ) {
+                       // Clone the handlers to prevent manipulation
+                       handlers = handlers.slice(0);
 
-                       // Filter the functions by class
-                       if ( all || namespace.test(handler.type) ) {
-                               // Pass in a reference to the handler function itself
-                               // So that we can later remove it
-                               event.handler = handler;
-                               event.data = handler.data;
+                       for ( var j = 0, l = handlers.length; j < l; j++ ) {
+                               var handleObj = handlers[ j ];
 
-                               var ret = handler.apply( this, arguments );
+                               // Filter the functions by class
+                               if ( all || namespace.test( handleObj.namespace ) ) {
+                                       // Pass in a reference to the handler function itself
+                                       // So that we can later remove it
+                                       event.handler = handleObj.handler;
+                                       event.data = handleObj.data;
+                                       event.handleObj = handleObj;
+       
+                                       var ret = handleObj.handler.apply( this, arguments );
 
-                               if ( ret !== undefined ) {
-                                       event.result = ret;
-                                       if ( ret === false ) {
-                                               event.preventDefault();
-                                               event.stopPropagation();
+                                       if ( ret !== undefined ) {
+                                               event.result = ret;
+                                               if ( ret === false ) {
+                                                       event.preventDefault();
+                                                       event.stopPropagation();
+                                               }
                                        }
-                               }
 
-                               if ( event.isImmediatePropagationStopped() ) {
-                                       break;
+                                       if ( event.isImmediatePropagationStopped() ) {
+                                               break;
+                                       }
                                }
-
                        }
                }
 
@@ -1973,44 +2025,39 @@ jQuery.event = {
                },
 
                live: {
-                       add: function( proxy, data, namespaces, live ) {
-                               jQuery.extend( proxy, data || {} );
-
-                               proxy.guid += data.selector + data.live; 
-                               data.liveProxy = proxy;
-
-                               jQuery.event.add( this, data.live, liveHandler, data ); 
-                               
+                       add: function( handleObj ) {
+                               jQuery.event.add( this, handleObj.origType, jQuery.extend({}, handleObj, {handler: liveHandler}) ); 
                        },
 
-                       remove: function( namespaces ) {
-                               if ( namespaces.length ) {
-                                       var remove = 0, name = new RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
-
-                                       jQuery.each( (jQuery.data(this, "events").live || {}), function() {
-                                               if ( name.test(this.type) ) {
-                                                       remove++;
-                                               }
-                                       });
-
-                                       if ( remove < 1 ) {
-                                               jQuery.event.remove( this, namespaces[0], liveHandler );
+                       remove: function( handleObj ) {
+                               var remove = true,
+                                       type = handleObj.origType.replace(rnamespaces, "");
+                               
+                               jQuery.each( jQuery.data(this, "events").live || [], function() {
+                                       if ( type === this.origType.replace(rnamespaces, "") ) {
+                                               remove = false;
+                                               return false;
                                        }
+                               });
+
+                               if ( remove ) {
+                                       jQuery.event.remove( this, handleObj.origType, liveHandler );
                                }
-                       },
-                       special: {}
+                       }
+
                },
+
                beforeunload: {
-                       setup: function( data, namespaces, fn ) {
+                       setup: function( data, namespaces, eventHandle ) {
                                // We only want to do this special case on windows
                                if ( this.setInterval ) {
-                                       this.onbeforeunload = fn;
+                                       this.onbeforeunload = eventHandle;
                                }
 
                                return false;
                        },
-                       teardown: function( namespaces, fn ) {
-                               if ( this.onbeforeunload === fn ) {
+                       teardown: function( namespaces, eventHandle ) {
+                               if ( this.onbeforeunload === eventHandle ) {
                                        this.onbeforeunload = null;
                                }
                        }
@@ -2018,6 +2065,14 @@ jQuery.event = {
        }
 };
 
+var removeEvent = document.removeEventListener ?
+       function( elem, type, handle ) {
+               elem.removeEventListener( type, handle, false );
+       } : 
+       function( elem, type, handle ) {
+               elem.detachEvent( "on" + type, handle );
+       };
+
 jQuery.Event = function( src ) {
        // Allow instantiation without the 'new' keyword
        if ( !this.preventDefault ) {
@@ -2095,27 +2150,24 @@ var withinElement = function( event ) {
        // Check if mouse(over|out) are still within the same parent element
        var parent = event.relatedTarget;
 
-       // Traverse up the tree
-       while ( parent && parent !== this ) {
-               // Firefox sometimes assigns relatedTarget a XUL element
-               // which we cannot access the parentNode property of
-               try {
+       // Firefox sometimes assigns relatedTarget a XUL element
+       // which we cannot access the parentNode property of
+       try {
+               // Traverse up the tree
+               while ( parent && parent !== this ) {
                        parent = parent.parentNode;
-
-               // assuming we've left the element since we most likely mousedover a xul element
-               } catch(e) {
-                       break;
                }
-       }
 
-       if ( parent !== this ) {
-               // set the correct event type
-               event.type = event.data;
+               if ( parent !== this ) {
+                       // set the correct event type
+                       event.type = event.data;
 
-               // handle event if we actually just moused on to a non sub-element
-               jQuery.event.handle.apply( this, arguments );
-       }
+                       // handle event if we actually just moused on to a non sub-element
+                       jQuery.event.handle.apply( this, arguments );
+               }
 
+       // assuming we've left the element since we most likely mousedover a xul element
+       } catch(e) { }
 },
 
 // In case of event delegation, we only need to rename the event.type,
@@ -2143,64 +2195,65 @@ jQuery.each({
 // submit delegation
 if ( !jQuery.support.submitBubbles ) {
 
-jQuery.event.special.submit = {
-       setup: function( data, namespaces, fn ) {
-               if ( this.nodeName.toLowerCase() !== "form" ) {
-                       jQuery.event.add(this, "click.specialSubmit." + fn.guid, function( e ) {
-                               var elem = e.target, type = elem.type;
+       jQuery.event.special.submit = {
+               setup: function( data, namespaces ) {
+                       if ( this.nodeName.toLowerCase() !== "form" ) {
+                               jQuery.event.add(this, "click.specialSubmit", function( e ) {
+                                       var elem = e.target, type = elem.type;
 
-                               if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
-                                       return trigger( "submit", this, arguments );
-                               }
-                       });
+                                       if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
+                                               return trigger( "submit", this, arguments );
+                                       }
+                               });
         
-                       jQuery.event.add(this, "keypress.specialSubmit." + fn.guid, function( e ) {
-                               var elem = e.target, type = elem.type;
+                               jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
+                                       var elem = e.target, type = elem.type;
 
-                               if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
-                                       return trigger( "submit", this, arguments );
-                               }
-                       });
+                                       if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
+                                               return trigger( "submit", this, arguments );
+                                       }
+                               });
 
-               } else {
-                       return false;
-               }
-       },
+                       } else {
+                               return false;
+                       }
+               },
 
-       remove: function( namespaces, fn ) {
-               jQuery.event.remove( this, "click.specialSubmit" + (fn ? "."+fn.guid : "") );
-               jQuery.event.remove( this, "keypress.specialSubmit" + (fn ? "."+fn.guid : "") );
-       }
-};
+               teardown: function( namespaces ) {
+                       jQuery.event.remove( this, ".specialSubmit" );
+               }
+       };
 
 }
 
 // change delegation, happens here so we have bind.
 if ( !jQuery.support.changeBubbles ) {
 
-var formElems = /textarea|input|select/i;
+       var formElems = /textarea|input|select/i,
 
-function getVal( elem ) {
-       var type = elem.type, val = elem.value;
+       changeFilters,
 
-       if ( type === "radio" || type === "checkbox" ) {
-               val = elem.checked;
+       getVal = function( elem ) {
+               var type = elem.type, val = elem.value;
 
-       } else if ( type === "select-multiple" ) {
-               val = elem.selectedIndex > -1 ?
-                       jQuery.map( elem.options, function( elem ) {
-                               return elem.selected;
-                       }).join("-") :
-                       "";
+               if ( type === "radio" || type === "checkbox" ) {
+                       val = elem.checked;
 
-       } else if ( elem.nodeName.toLowerCase() === "select" ) {
-               val = elem.selectedIndex;
-       }
+               } else if ( type === "select-multiple" ) {
+                       val = elem.selectedIndex > -1 ?
+                               jQuery.map( elem.options, function( elem ) {
+                                       return elem.selected;
+                               }).join("-") :
+                               "";
 
-       return val;
-}
+               } else if ( elem.nodeName.toLowerCase() === "select" ) {
+                       val = elem.selectedIndex;
+               }
+
+               return val;
+       },
 
-function testChange( e ) {
+       testChange = function testChange( e ) {
                var elem = e.target, data, val;
 
                if ( !formElems.test( elem.nodeName ) || elem.readOnly ) {
@@ -2223,61 +2276,61 @@ function testChange( e ) {
                        e.type = "change";
                        return jQuery.event.trigger( e, arguments[1], elem );
                }
-}
+       };
 
-jQuery.event.special.change = {
-       filters: {
-               focusout: testChange, 
+       jQuery.event.special.change = {
+               filters: {
+                       focusout: testChange, 
 
-               click: function( e ) {
-                       var elem = e.target, type = elem.type;
+                       click: function( e ) {
+                               var elem = e.target, type = elem.type;
 
-                       if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
-                               return testChange.call( this, e );
-                       }
-               },
+                               if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
+                                       return testChange.call( this, e );
+                               }
+                       },
+
+                       // Change has to be called before submit
+                       // Keydown will be called before keypress, which is used in submit-event delegation
+                       keydown: function( e ) {
+                               var elem = e.target, type = elem.type;
 
-               // Change has to be called before submit
-               // Keydown will be called before keypress, which is used in submit-event delegation
-               keydown: function( e ) {
-                       var elem = e.target, type = elem.type;
+                               if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
+                                       (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
+                                       type === "select-multiple" ) {
+                                       return testChange.call( this, e );
+                               }
+                       },
 
-                       if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
-                               (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
-                               type === "select-multiple" ) {
-                               return testChange.call( this, e );
+                       // Beforeactivate happens also before the previous element is blurred
+                       // with this event you can't trigger a change event, but you can store
+                       // information/focus[in] is not needed anymore
+                       beforeactivate: function( e ) {
+                               var elem = e.target;
+                               jQuery.data( elem, "_change_data", getVal(elem) );
                        }
                },
 
-               // Beforeactivate happens also before the previous element is blurred
-               // with this event you can't trigger a change event, but you can store
-               // information/focus[in] is not needed anymore
-               beforeactivate: function( e ) {
-                       var elem = e.target;
+               setup: function( data, namespaces ) {
+                       if ( this.type === "file" ) {
+                               return false;
+                       }
 
-                       if ( elem.nodeName.toLowerCase() === "input" && elem.type === "radio" ) {
-                               jQuery.data( elem, "_change_data", getVal(elem) );
+                       for ( var type in changeFilters ) {
+                               jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
                        }
-               }
-       },
-       setup: function( data, namespaces, fn ) {
-               for ( var type in changeFilters ) {
-                       jQuery.event.add( this, type + ".specialChange." + fn.guid, changeFilters[type] );
-               }
 
-               return formElems.test( this.nodeName );
-       },
-       remove: function( namespaces, fn ) {
-               for ( var type in changeFilters ) {
-                       jQuery.event.remove( this, type + ".specialChange" + (fn ? "."+fn.guid : ""), changeFilters[type] );
-               }
+                       return formElems.test( this.nodeName );
+               },
 
-               return formElems.test( this.nodeName );
-       }
-};
+               teardown: function( namespaces ) {
+                       jQuery.event.remove( this, ".specialChange" );
 
-var changeFilters = jQuery.event.special.change.filters;
+                       return formElems.test( this.nodeName );
+               }
+       };
 
+       changeFilters = jQuery.event.special.change.filters;
 }
 
 function trigger( type, elem, args ) {
@@ -2325,11 +2378,16 @@ jQuery.each(["bind", "one"], function( i, name ) {
                        return fn.apply( this, arguments );
                }) : fn;
 
-               return type === "unload" && name !== "one" ?
-                       this.one( type, data, fn ) :
-                       this.each(function() {
-                               jQuery.event.add( this, type, handler, data );
-                       });
+               if ( type === "unload" && name !== "one" ) {
+                       this.one( type, data, fn );
+
+               } else {
+                       for ( var i = 0, l = this.length; i < l; i++ ) {
+                               jQuery.event.add( this[i], type, handler, data );
+                       }
+               }
+
+               return this;
        };
 });
 
@@ -2340,13 +2398,29 @@ jQuery.fn.extend({
                        for ( var key in type ) {
                                this.unbind(key, type[key]);
                        }
-                       return this;
+
+               } else {
+                       for ( var i = 0, l = this.length; i < l; i++ ) {
+                               jQuery.event.remove( this[i], type, fn );
+                       }
                }
 
-               return this.each(function() {
-                       jQuery.event.remove( this, type, fn );
-               });
+               return this;
+       },
+       
+       delegate: function( selector, types, data, fn ) {
+               return this.live( types, data, fn, selector );
+       },
+       
+       undelegate: function( selector, types, fn ) {
+               if ( arguments.length === 0 ) {
+                               return this.unbind( "live" );
+               
+               } else {
+                       return this.die( types, null, fn, selector );
+               }
        },
+       
        trigger: function( type, data ) {
                return this.each(function() {
                        jQuery.event.trigger( type, data, this );
@@ -2390,32 +2464,60 @@ jQuery.fn.extend({
        }
 });
 
+var liveMap = {
+       focus: "focusin",
+       blur: "focusout",
+       mouseenter: "mouseover",
+       mouseleave: "mouseout"
+};
+
 jQuery.each(["live", "die"], function( i, name ) {
-       jQuery.fn[ name ] = function( types, data, fn ) {
-               var type, i = 0;
+       jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
+               var type, i = 0, match, namespaces, preType,
+                       selector = origSelector || this.selector,
+                       context = origSelector ? this : jQuery( this.context );
 
                if ( jQuery.isFunction( data ) ) {
                        fn = data;
                        data = undefined;
                }
 
-               types = (types || "").split( /\s+/ );
+               types = (types || "").split(" ");
 
                while ( (type = types[ i++ ]) != null ) {
-                       type = type === "focus" ? "focusin" : // focus --> focusin
-                                       type === "blur" ? "focusout" : // blur --> focusout
-                                       type === "hover" ? types.push("mouseleave") && "mouseenter" : // hover support
-                                       type;
-                       
+                       match = rnamespaces.exec( type );
+                       namespaces = "";
+
+                       if ( match )  {
+                               namespaces = match[0];
+                               type = type.replace( rnamespaces, "" );
+                       }
+
+                       if ( type === "hover" ) {
+                               types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
+                               continue;
+                       }
+
+                       preType = type;
+
+                       if ( type === "focus" || type === "blur" ) {
+                               types.push( liveMap[ type ] + namespaces );
+                               type = type + namespaces;
+
+                       } else {
+                               type = (liveMap[ type ] || type) + namespaces;
+                       }
+
                        if ( name === "live" ) {
                                // bind live handler
-                               jQuery( this.context ).bind( liveConvert( type, this.selector ), {
-                                       data: data, selector: this.selector, live: type
-                               }, fn );
+                               context.each(function(){
+                                       jQuery.event.add( this, liveConvert( type, selector ),
+                                               { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
+                               });
 
                        } else {
                                // unbind live handler
-                               jQuery( this.context ).unbind( liveConvert( type, this.selector ), fn ? { guid: fn.guid + this.selector + type } : null );
+                               context.unbind( liveConvert( type, selector ), fn );
                        }
                }
                
@@ -2425,45 +2527,46 @@ jQuery.each(["live", "die"], function( i, name ) {
 
 function liveHandler( event ) {
        var stop, elems = [], selectors = [], args = arguments,
-               related, match, fn, elem, j, i, l, data,
-               live = jQuery.extend({}, jQuery.data( this, "events" ).live);
+               related, match, handleObj, elem, j, i, l, data,
+               events = jQuery.data( this, "events" );
 
        // Make sure we avoid non-left-click bubbling in Firefox (#3861)
-       if ( event.button && event.type === "click" ) {
+       if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) {
                return;
        }
 
-       for ( j in live ) {
-               fn = live[j];
-               if ( fn.live === event.type ||
-                               fn.altLive && jQuery.inArray(event.type, fn.altLive) > -1 ) {
+       event.liveFired = this;
+
+       var live = events.live.slice(0);
+
+       for ( j = 0; j < live.length; j++ ) {
+               handleObj = live[j];
+
+               if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
+                       selectors.push( handleObj.selector );
 
-                       data = fn.data;
-                       if ( !(data.beforeFilter && data.beforeFilter[event.type] && 
-                                       !data.beforeFilter[event.type](event)) ) {
-                               selectors.push( fn.selector );
-                       }
                } else {
-                       delete live[j];
+                       live.splice( j--, 1 );
                }
        }
 
        match = jQuery( event.target ).closest( selectors, event.currentTarget );
 
        for ( i = 0, l = match.length; i < l; i++ ) {
-               for ( j in live ) {
-                       fn = live[j];
-                       elem = match[i].elem;
-                       related = null;
+               for ( j = 0; j < live.length; j++ ) {
+                       handleObj = live[j];
+
+                       if ( match[i].selector === handleObj.selector ) {
+                               elem = match[i].elem;
+                               related = null;
 
-                       if ( match[i].selector === fn.selector ) {
                                // Those two events require additional checking
-                               if ( fn.live === "mouseenter" || fn.live === "mouseleave" ) {
-                                       related = jQuery( event.relatedTarget ).closest( fn.selector )[0];
+                               if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
+                                       related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
                                }
 
                                if ( !related || related !== elem ) {
-                                       elems.push({ elem: elem, fn: fn });
+                                       elems.push({ elem: elem, handleObj: handleObj });
                                }
                        }
                }
@@ -2472,8 +2575,10 @@ function liveHandler( event ) {
        for ( i = 0, l = elems.length; i < l; i++ ) {
                match = elems[i];
                event.currentTarget = match.elem;
-               event.data = match.fn.data;
-               if ( match.fn.apply( match.elem, args ) === false ) {
+               event.data = match.handleObj.data;
+               event.handleObj = match.handleObj;
+
+               if ( match.handleObj.origHandler.apply( match.elem, args ) === false ) {
                        stop = false;
                        break;
                }
@@ -2483,7 +2588,7 @@ function liveHandler( event ) {
 }
 
 function liveConvert( type, selector ) {
-       return "live." + (type ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&");
+       return "live." + (type && type !== "*" ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&");
 }
 
 jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
@@ -3228,8 +3333,10 @@ var makeArray = function(array, results) {
 
 // Perform a simple check to determine if the browser is capable of
 // converting a NodeList to an array using builtin methods.
+// Also verifies that the returned array holds DOM nodes
+// (which is not the case in the Blackberry browser)
 try {
-       Array.prototype.slice.call( document.documentElement.childNodes, 0 );
+       Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
 
 // Provide a fallback method if it does not work
 } catch(e){
@@ -3533,7 +3640,7 @@ function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
 }
 
 var contains = document.compareDocumentPosition ? function(a, b){
-       return a.compareDocumentPosition(b) & 16;
+       return !!(a.compareDocumentPosition(b) & 16);
 } : function(a, b){
        return a !== b && (a.contains ? a.contains(b) : true);
 };
@@ -3570,7 +3677,7 @@ jQuery.find = Sizzle;
 jQuery.expr = Sizzle.selectors;
 jQuery.expr[":"] = jQuery.expr.filters;
 jQuery.unique = Sizzle.uniqueSort;
-jQuery.getText = getText;
+jQuery.text = getText;
 jQuery.isXMLDoc = isXML;
 jQuery.contains = contains;
 
@@ -3856,7 +3963,8 @@ var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
        rselfClosing = /^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,
        rtagName = /<([\w:]+)/,
        rtbody = /<tbody/i,
-       rhtml = /<|&\w+;/,
+       rhtml = /<|&#?\w+;/,
+       rnocache = /<script|<object|<embed|<option|<style/i,
        rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,  // checked="checked" or checked (html5)
        fcloseTag = function( all, front, tag ) {
                return rselfClosing.test( tag ) ?
@@ -3896,7 +4004,7 @@ jQuery.fn.extend({
                        return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
                }
 
-               return jQuery.getText( this );
+               return jQuery.text( this );
        },
 
        wrapAll: function( html ) {
@@ -4000,6 +4108,40 @@ jQuery.fn.extend({
                        return set;
                }
        },
+       
+       // keepData is for internal use only--do not document
+       remove: function( selector, keepData ) {
+               for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+                       if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
+                               if ( !keepData && elem.nodeType === 1 ) {
+                                       jQuery.cleanData( elem.getElementsByTagName("*") );
+                                       jQuery.cleanData( [ elem ] );
+                               }
+
+                               if ( elem.parentNode ) {
+                                        elem.parentNode.removeChild( elem );
+                               }
+                       }
+               }
+               
+               return this;
+       },
+
+       empty: function() {
+               for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+                       // Remove element nodes and prevent memory leaks
+                       if ( elem.nodeType === 1 ) {
+                               jQuery.cleanData( elem.getElementsByTagName("*") );
+                       }
+
+                       // Remove any remaining nodes
+                       while ( elem.firstChild ) {
+                               elem.removeChild( elem.firstChild );
+                       }
+               }
+               
+               return this;
+       },
 
        clone: function( events ) {
                // Do the clone
@@ -4021,6 +4163,8 @@ jQuery.fn.extend({
                                }
 
                                return jQuery.clean([html.replace(rinlinejQuery, "")
+                                       // Handle the case in IE 8 where action=/test/> self-closes a tag
+                                       .replace(/=([^="'>\s]+\/)>/g, '="$1">')
                                        .replace(rleadingWhitespace, "")], ownerDocument)[0];
                        } else {
                                return this.cloneNode(true);
@@ -4044,7 +4188,7 @@ jQuery.fn.extend({
                                null;
 
                // See if we can take a shortcut and just use innerHTML
-               } else if ( typeof value === "string" && !/<script/i.test( value ) &&
+               } else if ( typeof value === "string" && !rnocache.test( value ) &&
                        (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
                        !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
 
@@ -4083,16 +4227,17 @@ jQuery.fn.extend({
                if ( this[0] && this[0].parentNode ) {
                        // Make sure that the elements are removed from the DOM before they are inserted
                        // this can help fix replacing a parent with child elements
-                       if ( !jQuery.isFunction( value ) ) {
-                               value = jQuery( value ).detach();
-
-                       } else {
+                       if ( jQuery.isFunction( value ) ) {
                                return this.each(function(i) {
                                        var self = jQuery(this), old = self.html();
                                        self.replaceWith( value.call( this, i, old ) );
                                });
                        }
 
+                       if ( typeof value !== "string" ) {
+                               value = jQuery(value).detach();
+                       }
+
                        return this.each(function() {
                                var next = this.nextSibling, parent = this.parentNode;
 
@@ -4114,7 +4259,7 @@ jQuery.fn.extend({
        },
 
        domManip: function( args, table, callback ) {
-               var results, first, value = args[0], scripts = [];
+               var results, first, value = args[0], scripts = [], fragment, parent;
 
                // We can't cloneNode fragments that contain checked, in WebKit
                if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
@@ -4132,14 +4277,23 @@ jQuery.fn.extend({
                }
 
                if ( this[0] ) {
+                       parent = value && value.parentNode;
+
                        // If we're in a fragment, just use that instead of building a new one
-                       if ( args[0] && args[0].parentNode && args[0].parentNode.nodeType === 11 ) {
-                               results = { fragment: args[0].parentNode };
+                       if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
+                               results = { fragment: parent };
+
                        } else {
                                results = buildFragment( args, this, scripts );
                        }
-
-                       first = results.fragment.firstChild;
+                       
+                       fragment = results.fragment;
+                       
+                       if ( fragment.childNodes.length === 1 ) {
+                               first = fragment = fragment.firstChild;
+                       } else {
+                               first = fragment.firstChild;
+                       }
 
                        if ( first ) {
                                table = table && jQuery.nodeName( first, "tr" );
@@ -4149,14 +4303,14 @@ jQuery.fn.extend({
                                                table ?
                                                        root(this[i], first) :
                                                        this[i],
-                                               results.cacheable || this.length > 1 || i > 0 ?
-                                                       results.fragment.cloneNode(true) :
-                                                       results.fragment
+                                               i > 0 || results.cacheable || this.length > 1  ?
+                                                       fragment.cloneNode(true) :
+                                                       fragment
                                        );
                                }
                        }
 
-                       if ( scripts ) {
+                       if ( scripts.length ) {
                                jQuery.each( scripts, evalScript );
                        }
                }
@@ -4196,10 +4350,16 @@ function cloneCopyEvent(orig, ret) {
 }
 
 function buildFragment( args, nodes, scripts ) {
-       var fragment, cacheable, cacheresults, doc;
+       var fragment, cacheable, cacheresults,
+               doc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document);
+
+       // Only cache "small" (1/2 KB) strings that are associated with the main document
+       // Cloning options loses the selected state, so don't cache them
+       // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
+       // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
+       if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document &&
+               !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {
 
-       // webkit does not clone 'checked' attribute of radio inputs on cloneNode, so don't cache if string has a checked
-       if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && args[0].indexOf("<option") < 0 && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {
                cacheable = true;
                cacheresults = jQuery.fragments[ args[0] ];
                if ( cacheresults ) {
@@ -4210,7 +4370,6 @@ function buildFragment( args, nodes, scripts ) {
        }
 
        if ( !fragment ) {
-               doc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document);
                fragment = doc.createDocumentFragment();
                jQuery.clean( args, doc, fragment, scripts );
        }
@@ -4232,46 +4391,22 @@ jQuery.each({
        replaceAll: "replaceWith"
 }, function( name, original ) {
        jQuery.fn[ name ] = function( selector ) {
-               var ret = [], insert = jQuery( selector );
-
-               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, insert.selector );
-       };
-});
-
-jQuery.each({
-       // keepData is for internal use only--do not document
-       remove: function( selector, keepData ) {
-               if ( !selector || jQuery.filter( selector, [ this ] ).length ) {
-                       if ( !keepData && this.nodeType === 1 ) {
-                               jQuery.cleanData( this.getElementsByTagName("*") );
-                               jQuery.cleanData( [ this ] );
-                       }
-
-                       if ( this.parentNode ) {
-                                this.parentNode.removeChild( this );
+               var ret = [], insert = jQuery( selector ),
+                       parent = this.length === 1 && this[0].parentNode;
+               
+               if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
+                       insert[ original ]( this[0] );
+                       return this;
+                       
+               } else {
+                       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, insert.selector );
                }
-       },
-
-       empty: function() {
-               // Remove element nodes and prevent memory leaks
-               if ( this.nodeType === 1 ) {
-                       jQuery.cleanData( this.getElementsByTagName("*") );
-               }
-
-               // Remove any remaining nodes
-               while ( this.firstChild ) {
-                       this.removeChild( this.firstChild );
-               }
-       }
-}, function( name, fn ) {
-       jQuery.fn[ name ] = function() {
-               return this.each( fn, arguments );
        };
 });
 
@@ -4286,13 +4421,13 @@ jQuery.extend({
 
                var ret = [];
 
-               jQuery.each(elems, function( i, elem ) {
+               for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
                        if ( typeof elem === "number" ) {
                                elem += "";
                        }
 
                        if ( !elem ) {
-                               return;
+                               continue;
                        }
 
                        // Convert html string into DOM nodes
@@ -4343,7 +4478,7 @@ jQuery.extend({
                                        div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
                                }
 
-                               elem = jQuery.makeArray( div.childNodes );
+                               elem = div.childNodes;
                        }
 
                        if ( elem.nodeType ) {
@@ -4351,13 +4486,13 @@ jQuery.extend({
                        } else {
                                ret = jQuery.merge( ret, elem );
                        }
-
-               });
+               }
 
                if ( fragment ) {
                        for ( var i = 0; ret[i]; i++ ) {
                                if ( scripts && jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
                                        scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
+                               
                                } else {
                                        if ( ret[i].nodeType === 1 ) {
                                                ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
@@ -4371,9 +4506,36 @@ jQuery.extend({
        },
        
        cleanData: function( elems ) {
-               for ( var i = 0, elem, id; (elem = elems[i]) != null; i++ ) {
-                       jQuery.event.remove( elem );
-                       jQuery.removeData( elem );
+               var data, id, cache = jQuery.cache,
+                       special = jQuery.event.special,
+                       deleteExpando = jQuery.support.deleteExpando;
+               
+               for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+                       id = elem[ jQuery.expando ];
+                       
+                       if ( id ) {
+                               data = cache[ id ];
+                               
+                               if ( data.events ) {
+                                       for ( var type in data.events ) {
+                                               if ( special[ type ] ) {
+                                                       jQuery.event.remove( elem, type );
+
+                                               } else {
+                                                       removeEvent( elem, type, data.handle );
+                                               }
+                                       }
+                               }
+                               
+                               if ( deleteExpando ) {
+                                       delete elem[ jQuery.expando ];
+
+                               } else if ( elem.removeAttribute ) {
+                                       elem.removeAttribute( jQuery.expando );
+                               }
+                               
+                               delete cache[ id ];
+                       }
                }
        }
 });
@@ -4614,15 +4776,15 @@ var jsc = now(),
        rquery = /\?/,
        rts = /(\?|&)_=.*?(&|$)/,
        rurl = /^(\w+:)?\/\/([^\/?#]+)/,
-       r20 = /%20/g;
+       r20 = /%20/g,
 
-jQuery.fn.extend({
-       // Keep a copy of the old load
-       _load: jQuery.fn.load,
+       // Keep a copy of the old load method
+       _load = jQuery.fn.load;
 
+jQuery.fn.extend({
        load: function( url, params, callback ) {
                if ( typeof url !== "string" ) {
-                       return this._load( url );
+                       return _load.call( this, url );
 
                // Don't do a request if no elements are being requested
                } else if ( !this.length ) {
@@ -5243,7 +5405,7 @@ jQuery.extend({
                        if ( jQuery.isArray(obj) ) {
                                // Serialize array item.
                                jQuery.each( obj, function( i, v ) {
-                                       if ( traditional ) {
+                                       if ( traditional || /\[\]$/.test( prefix ) ) {
                                                // Treat each array item as a scalar.
                                                add( prefix, v );
                                        } else {
index 950198f4770f8135ea7708da4e6a73b33c0d40ec..b170a78f8fbda267b82abbe51da6dacb2157c7c0 100644 (file)
@@ -1,5 +1,5 @@
 /*!
- * jQuery JavaScript Library v1.4.1
+ * jQuery JavaScript Library v1.4.2
  * http://jquery.com/
  *
  * Copyright 2010, John Resig
  * Copyright 2010, The Dojo Foundation
  * Released under the MIT, BSD, and GPL Licenses.
  *
- * Date: Mon Jan 25 19:43:33 2010 -0500
+ * Date: Sat Feb 13 22:33:48 2010 -0500
  */
-(function(z,v){function la(){if(!c.isReady){try{r.documentElement.doScroll("left")}catch(a){setTimeout(la,1);return}c.ready()}}function Ma(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,i){var j=a.length;if(typeof b==="object"){for(var n in b)X(a,n,b[n],f,e,d);return a}if(d!==v){f=!i&&f&&c.isFunction(d);for(n=0;n<j;n++)e(a[n],b,f?d.call(a[n],n,e(a[n],b)):d,i);return a}return j?
-e(a[0],b):null}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function ma(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function na(a){var b,d=[],f=[],e=arguments,i,j,n,o,m,s,x=c.extend({},c.data(this,"events").live);if(!(a.button&&a.type==="click")){for(o in x){j=x[o];if(j.live===a.type||j.altLive&&c.inArray(a.type,j.altLive)>-1){i=j.data;i.beforeFilter&&i.beforeFilter[a.type]&&!i.beforeFilter[a.type](a)||f.push(j.selector)}else delete x[o]}i=c(a.target).closest(f,
-a.currentTarget);m=0;for(s=i.length;m<s;m++)for(o in x){j=x[o];n=i[m].elem;f=null;if(i[m].selector===j.selector){if(j.live==="mouseenter"||j.live==="mouseleave")f=c(a.relatedTarget).closest(j.selector)[0];if(!f||f!==n)d.push({elem:n,fn:j})}}m=0;for(s=d.length;m<s;m++){i=d[m];a.currentTarget=i.elem;a.data=i.fn.data;if(i.fn.apply(i.elem,e)===false){b=false;break}}return b}}function oa(a,b){return"live."+(a?a+".":"")+b.replace(/\./g,"`").replace(/ /g,"&")}function pa(a){return!a||!a.parentNode||a.parentNode.nodeType===
-11}function qa(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var i in f)for(var j in f[i])c.event.add(this,i,f[i][j],f[i][j].data)}}})}function ra(a,b,d){var f,e,i;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&a[0].indexOf("<option")<0&&(c.support.checkClone||!sa.test(a[0]))){e=true;if(i=c.fragments[a[0]])if(i!==1)f=i}if(!f){b=b&&b[0]?b[0].ownerDocument||b[0]:r;f=b.createDocumentFragment();
-c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=i?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(ta.concat.apply([],ta.slice(0,b)),function(){d[this]=a});return d}function ua(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Na=z.jQuery,Oa=z.$,r=z.document,S,Pa=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Qa=/^.[^:#\[\.,]*$/,Ra=/\S/,Sa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Ta=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,O=navigator.userAgent,
-va=false,P=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,Q=Array.prototype.slice,wa=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(typeof a==="string")if((d=Pa.exec(a))&&(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:r;if(a=Ta.exec(a))if(c.isPlainObject(b)){a=[r.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=ra([d[1]],
-[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}}else{if(b=r.getElementById(d[2])){if(b.id!==d[2])return S.find(a);this.length=1;this[0]=b}this.context=r;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=r;a=r.getElementsByTagName(a)}else return!b||b.jquery?(b||S).find(a):c(b).find(a);else if(c.isFunction(a))return S.ready(a);if(a.selector!==v){this.selector=a.selector;this.context=a.context}return c.isArray(a)?this.setArray(a):c.makeArray(a,
-this)},selector:"",jquery:"1.4.1",length:0,size:function(){return this.length},toArray:function(){return Q.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){a=c(a||null);a.prevObject=this;a.context=this.context;if(b==="find")a.selector=this.selector+(this.selector?" ":"")+d;else if(b)a.selector=this.selector+"."+b+"("+d+")";return a},setArray:function(a){this.length=0;ba.apply(this,a);return this},each:function(a,b){return c.each(this,
-a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(r,c);else P&&P.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(Q.apply(this,arguments),"slice",Q.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};
-c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,i,j,n;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(i in e){j=a[i];n=e[i];if(a!==n)if(f&&n&&(c.isPlainObject(n)||c.isArray(n))){j=j&&(c.isPlainObject(j)||c.isArray(j))?j:c.isArray(n)?[]:{};a[i]=c.extend(f,j,n)}else if(n!==v)a[i]=n}return a};c.extend({noConflict:function(a){z.$=
-Oa;if(a)z.jQuery=Na;return c},isReady:false,ready:function(){if(!c.isReady){if(!r.body)return setTimeout(c.ready,13);c.isReady=true;if(P){for(var a,b=0;a=P[b++];)a.call(r,c);P=null}c.fn.triggerHandler&&c(r).triggerHandler("ready")}},bindReady:function(){if(!va){va=true;if(r.readyState==="complete")return c.ready();if(r.addEventListener){r.addEventListener("DOMContentLoaded",L,false);z.addEventListener("load",c.ready,false)}else if(r.attachEvent){r.attachEvent("onreadystatechange",L);z.attachEvent("onload",
-c.ready);var a=false;try{a=z.frameElement==null}catch(b){}r.documentElement.doScroll&&a&&la()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,"isPrototypeOf"))return false;var b;for(b in a);return b===v||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;
-return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return z.JSON&&z.JSON.parse?z.JSON.parse(a):(new Function("return "+a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Ra.test(a)){var b=r.getElementsByTagName("head")[0]||
-r.documentElement,d=r.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(r.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,i=a.length,j=i===v||c.isFunction(a);if(d)if(j)for(f in a){if(b.apply(a[f],d)===false)break}else for(;e<i;){if(b.apply(a[e++],d)===false)break}else if(j)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=
-a[0];e<i&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Sa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==
-v;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,i=a.length;e<i;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,i=0,j=a.length;i<j;i++){e=b(a[i],i,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=v}else if(b&&!c.isFunction(b)){d=b;b=v}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},
-uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});O=c.uaMatch(O);if(O.browser){c.browser[O.browser]=true;c.browser.version=O.version}if(c.browser.webkit)c.browser.safari=true;if(wa)c.inArray=function(a,b){return wa.call(b,a)};S=c(r);if(r.addEventListener)L=function(){r.removeEventListener("DOMContentLoaded",
-L,false);c.ready()};else if(r.attachEvent)L=function(){if(r.readyState==="complete"){r.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=r.documentElement,b=r.createElement("script"),d=r.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML="   <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";var e=d.getElementsByTagName("*"),i=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!i)){c.support=
-{leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(i.getAttribute("style")),hrefNormalized:i.getAttribute("href")==="/a",opacity:/^0.55$/.test(i.style.opacity),cssFloat:!!i.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:r.createElement("select").appendChild(r.createElement("option")).selected,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};
-b.type="text/javascript";try{b.appendChild(r.createTextNode("window."+f+"=1;"))}catch(j){}a.insertBefore(b,a.firstChild);if(z[f]){c.support.scriptEval=true;delete z[f]}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function n(){c.support.noCloneEvent=false;d.detachEvent("onclick",n)});d.cloneNode(true).fireEvent("onclick")}d=r.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=r.createDocumentFragment();a.appendChild(d.firstChild);
-c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var n=r.createElement("div");n.style.width=n.style.paddingLeft="1px";r.body.appendChild(n);c.boxModel=c.support.boxModel=n.offsetWidth===2;r.body.removeChild(n).style.display="none"});a=function(n){var o=r.createElement("div");n="on"+n;var m=n in o;if(!m){o.setAttribute(n,"return;");m=typeof o[n]==="function"}return m};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=i=null}})();c.props=
-{"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ua=0,xa={},Va={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==z?xa:a;var f=a[G],e=c.cache;if(!b&&!f)return null;f||(f=++Ua);if(typeof b==="object"){a[G]=f;e=e[f]=c.extend(true,
-{},b)}else e=e[f]?e[f]:typeof d==="undefined"?Va:(e[f]={});if(d!==v){a[G]=f;e[b]=d}return typeof b==="string"?e[b]:e}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==z?xa:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{try{delete a[G]}catch(i){a.removeAttribute&&a.removeAttribute(G)}delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,
-a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===v){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===v&&this.length)f=c.data(this[0],a);return f===v&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);
-return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===v)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||
-a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var ya=/[\n\t]/g,ca=/\s+/,Wa=/\r/g,Xa=/href|src|style/,Ya=/(button|input)/i,Za=/(button|input|object|select|textarea)/i,$a=/^(a|area)$/i,za=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(o){var m=
-c(this);m.addClass(a.call(this,o,m.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className)for(var i=" "+e.className+" ",j=0,n=b.length;j<n;j++){if(i.indexOf(" "+b[j]+" ")<0)e.className+=" "+b[j]}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(o){var m=c(this);m.removeClass(a.call(this,o,m.attr("class")))});if(a&&typeof a==="string"||a===v)for(var b=(a||"").split(ca),
-d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var i=(" "+e.className+" ").replace(ya," "),j=0,n=b.length;j<n;j++)i=i.replace(" "+b[j]+" "," ");e.className=i.substring(1,i.length-1)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var i=c(this);i.toggleClass(a.call(this,e,i.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,i=0,j=c(this),n=b,o=
-a.split(ca);e=o[i++];){n=f?n:!j.hasClass(e);j[n?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(ya," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===v){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||
-{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var i=b?d:0;for(d=b?d+1:e.length;i<d;i++){var j=e[i];if(j.selected){a=c(j).val();if(b)return a;f.push(a)}}return f}if(za.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Wa,"")}return v}var n=c.isFunction(a);return this.each(function(o){var m=c(this),s=a;if(this.nodeType===1){if(n)s=a.call(this,o,m.val());
-if(typeof s==="number")s+="";if(c.isArray(s)&&za.test(this.type))this.checked=c.inArray(m.val(),s)>=0;else if(c.nodeName(this,"select")){var x=c.makeArray(s);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),x)>=0});if(!x.length)this.selectedIndex=-1}else this.value=s}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return v;if(f&&b in c.attrFn)return c(a)[b](d);
-f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==v;b=f&&c.props[b]||b;if(a.nodeType===1){var i=Xa.test(b);if(b in a&&f&&!i){if(e){b==="type"&&Ya.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:Za.test(a.nodeName)||$a.test(a.nodeName)&&a.href?0:v;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=
-""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&i?a.getAttribute(b,2):a.getAttribute(b);return a===null?v:a}return c.style(a,b,d)}});var ab=function(a){return a.replace(/[^\w\s\.\|`]/g,function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==z&&!a.frameElement)a=z;if(!d.guid)d.guid=c.guid++;if(f!==v){d=c.proxy(d);d.data=f}var e=c.data(a,"events")||c.data(a,"events",{}),i=c.data(a,"handle"),j;if(!i){j=
-function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(j.elem,arguments):v};i=c.data(a,"handle",j)}if(i){i.elem=a;b=b.split(/\s+/);for(var n,o=0;n=b[o++];){var m=n.split(".");n=m.shift();if(o>1){d=c.proxy(d);if(f!==v)d.data=f}d.type=m.slice(0).sort().join(".");var s=e[n],x=this.special[n]||{};if(!s){s=e[n]={};if(!x.setup||x.setup.call(a,f,m,d)===false)if(a.addEventListener)a.addEventListener(n,i,false);else a.attachEvent&&a.attachEvent("on"+n,i)}if(x.add)if((m=x.add.call(a,
-d,f,m,s))&&c.isFunction(m)){m.guid=m.guid||d.guid;m.data=m.data||d.data;m.type=m.type||d.type;d=m}s[d.guid]=d;this.global[n]=true}a=null}}},global:{},remove:function(a,b,d){if(!(a.nodeType===3||a.nodeType===8)){var f=c.data(a,"events"),e,i,j;if(f){if(b===v||typeof b==="string"&&b.charAt(0)===".")for(i in f)this.remove(a,i+(b||""));else{if(b.type){d=b.handler;b=b.type}b=b.split(/\s+/);for(var n=0;i=b[n++];){var o=i.split(".");i=o.shift();var m=!o.length,s=c.map(o.slice(0).sort(),ab);s=new RegExp("(^|\\.)"+
-s.join("\\.(?:.*\\.)?")+"(\\.|$)");var x=this.special[i]||{};if(f[i]){if(d){j=f[i][d.guid];delete f[i][d.guid]}else for(var A in f[i])if(m||s.test(f[i][A].type))delete f[i][A];x.remove&&x.remove.call(a,o,j);for(e in f[i])break;if(!e){if(!x.teardown||x.teardown.call(a,o)===false)if(a.removeEventListener)a.removeEventListener(i,c.data(a,"handle"),false);else a.detachEvent&&a.detachEvent("on"+i,c.data(a,"handle"));e=null;delete f[i]}}}}for(e in f)break;if(!e){if(A=c.data(a,"handle"))A.elem=null;c.removeData(a,
-"events");c.removeData(a,"handle")}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();this.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return v;a.result=v;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,
-b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(i){}if(!a.isPropagationStopped()&&f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){d=a.target;var j;if(!(c.nodeName(d,"a")&&e==="click")&&!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()])){try{if(d[e]){if(j=d["on"+e])d["on"+e]=null;this.triggered=true;d[e]()}}catch(n){}if(j)d["on"+e]=j;this.triggered=false}}},handle:function(a){var b,
-d;a=arguments[0]=c.event.fix(a||z.event);a.currentTarget=this;d=a.type.split(".");a.type=d.shift();b=!d.length&&!a.exclusive;var f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)");d=(c.data(this,"events")||{})[a.type];for(var e in d){var i=d[e];if(b||f.test(i.type)){a.handler=i;a.data=i.data;i=i.apply(this,arguments);if(i!==v){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
-fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||r;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=r.documentElement;d=r.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
-d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==v)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a,b){c.extend(a,b||{});a.guid+=b.selector+b.live;b.liveProxy=a;c.event.add(this,b.live,na,b)},remove:function(a){if(a.length){var b=
-0,d=new RegExp("(^|\\.)"+a[0]+"(\\.|$)");c.each(c.data(this,"events").live||{},function(){d.test(this.type)&&b++});b<1&&c.event.remove(this,a[0],na)}},special:{}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};
-c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,isImmediatePropagationStopped:Y};var Aa=function(a){for(var b=
-a.relatedTarget;b&&b!==this;)try{b=b.parentNode}catch(d){break}if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}},Ba=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ba:Aa,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ba:Aa)}}});if(!c.support.submitBubbles)c.event.special.submit={setup:function(a,b,d){if(this.nodeName.toLowerCase()!==
-"form"){c.event.add(this,"click.specialSubmit."+d.guid,function(f){var e=f.target,i=e.type;if((i==="submit"||i==="image")&&c(e).closest("form").length)return ma("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit."+d.guid,function(f){var e=f.target,i=e.type;if((i==="text"||i==="password")&&c(e).closest("form").length&&f.keyCode===13)return ma("submit",this,arguments)})}else return false},remove:function(a,b){c.event.remove(this,"click.specialSubmit"+(b?"."+b.guid:""));c.event.remove(this,
-"keypress.specialSubmit"+(b?"."+b.guid:""))}};if(!c.support.changeBubbles){var da=/textarea|input|select/i;function Ca(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d}function ea(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Ca(d);if(a.type!=="focusout"||
-d.type!=="radio")c.data(d,"_change_data",e);if(!(f===v||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}}c.event.special.change={filters:{focusout:ea,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return ea.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return ea.call(this,a)},beforeactivate:function(a){a=
-a.target;a.nodeName.toLowerCase()==="input"&&a.type==="radio"&&c.data(a,"_change_data",Ca(a))}},setup:function(a,b,d){for(var f in T)c.event.add(this,f+".specialChange."+d.guid,T[f]);return da.test(this.nodeName)},remove:function(a,b){for(var d in T)c.event.remove(this,d+".specialChange"+(b?"."+b.guid:""),T[d]);return da.test(this.nodeName)}};var T=c.event.special.change.filters}r.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,
-f)}c.event.special[b]={setup:function(){this.addEventListener(a,d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var i in d)this[b](i,f,d[i],e);return this}if(c.isFunction(f)){e=f;f=v}var j=b==="one"?c.proxy(e,function(n){c(this).unbind(n,j);return e.apply(this,arguments)}):e;return d==="unload"&&b!=="one"?this.one(d,f,e):this.each(function(){c.event.add(this,d,j,f)})}});c.fn.extend({unbind:function(a,
-b){if(typeof a==="object"&&!a.preventDefault){for(var d in a)this.unbind(d,a[d]);return this}return this.each(function(){c.event.remove(this,a,b)})},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+
-a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e){var i,j=0;if(c.isFunction(f)){e=f;f=v}for(d=(d||"").split(/\s+/);(i=d[j++])!=null;){i=i==="focus"?"focusin":i==="blur"?"focusout":i==="hover"?d.push("mouseleave")&&"mouseenter":i;b==="live"?c(this.context).bind(oa(i,this.selector),{data:f,selector:this.selector,
-live:i},e):c(this.context).unbind(oa(i,this.selector),e?{guid:e.guid+this.selector+i}:null)}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});z.attachEvent&&!z.addEventListener&&z.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});
-(function(){function a(g){for(var h="",k,l=0;g[l];l++){k=g[l];if(k.nodeType===3||k.nodeType===4)h+=k.nodeValue;else if(k.nodeType!==8)h+=a(k.childNodes)}return h}function b(g,h,k,l,q,p){q=0;for(var u=l.length;q<u;q++){var t=l[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===k){y=l[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=k;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}l[q]=y}}}function d(g,h,k,l,q,p){q=0;for(var u=l.length;q<u;q++){var t=l[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===
-k){y=l[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=k;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(o.filter(h,[t]).length>0){y=t;break}}t=t[g]}l[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,i=Object.prototype.toString,j=false,n=true;[0,0].sort(function(){n=false;return 0});var o=function(g,h,k,l){k=k||[];var q=h=h||r;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||
-typeof g!=="string")return k;for(var p=[],u,t,y,R,H=true,M=w(h),I=g;(f.exec(""),u=f.exec(I))!==null;){I=u[3];p.push(u[1]);if(u[2]){R=u[3];break}}if(p.length>1&&s.exec(g))if(p.length===2&&m.relative[p[0]])t=fa(p[0]+p[1],h);else for(t=m.relative[p[0]]?[h]:o(p.shift(),h);p.length;){g=p.shift();if(m.relative[g])g+=p.shift();t=fa(g,t)}else{if(!l&&p.length>1&&h.nodeType===9&&!M&&m.match.ID.test(p[0])&&!m.match.ID.test(p[p.length-1])){u=o.find(p.shift(),h,M);h=u.expr?o.filter(u.expr,u.set)[0]:u.set[0]}if(h){u=
-l?{expr:p.pop(),set:A(l)}:o.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=u.expr?o.filter(u.expr,u.set):u.set;if(p.length>0)y=A(t);else H=false;for(;p.length;){var D=p.pop();u=D;if(m.relative[D])u=p.pop();else D="";if(u==null)u=h;m.relative[D](y,u,M)}}else y=[]}y||(y=t);y||o.error(D||g);if(i.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))k.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&
-y[g].nodeType===1&&k.push(t[g]);else k.push.apply(k,y);else A(y,k);if(R){o(R,q,k,l);o.uniqueSort(k)}return k};o.uniqueSort=function(g){if(C){j=n;g.sort(C);if(j)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};o.matches=function(g,h){return o(g,null,null,h)};o.find=function(g,h,k){var l,q;if(!g)return[];for(var p=0,u=m.order.length;p<u;p++){var t=m.order[p];if(q=m.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");l=m.find[t](q,
-h,k);if(l!=null){g=g.replace(m.match[t],"");break}}}}l||(l=h.getElementsByTagName("*"));return{set:l,expr:g}};o.filter=function(g,h,k,l){for(var q=g,p=[],u=h,t,y,R=h&&h[0]&&w(h[0]);g&&h.length;){for(var H in m.filter)if((t=m.leftMatch[H].exec(g))!=null&&t[2]){var M=m.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-1)!=="\\"){if(u===p)p=[];if(m.preFilter[H])if(t=m.preFilter[H](t,u,k,p,l,R)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=u[U])!=null;U++)if(D){I=M(D,t,U,u);var Da=
-l^!!I;if(k&&I!=null)if(Da)y=true;else u[U]=false;else if(Da){p.push(D);y=true}}if(I!==v){k||(u=p);g=g.replace(m.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)o.error(g);else break;q=g}return u};o.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var m=o.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
-TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},relative:{"+":function(g,h){var k=typeof h==="string",l=k&&!/\W/.test(h);k=k&&!l;if(l)h=h.toLowerCase();l=0;for(var q=g.length,
-p;l<q;l++)if(p=g[l]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[l]=k||p&&p.nodeName.toLowerCase()===h?p||false:p===h}k&&o.filter(h,g,true)},">":function(g,h){var k=typeof h==="string";if(k&&!/\W/.test(h)){h=h.toLowerCase();for(var l=0,q=g.length;l<q;l++){var p=g[l];if(p){k=p.parentNode;g[l]=k.nodeName.toLowerCase()===h?k:false}}}else{l=0;for(q=g.length;l<q;l++)if(p=g[l])g[l]=k?p.parentNode:p.parentNode===h;k&&o.filter(h,g,true)}},"":function(g,h,k){var l=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=
-h=h.toLowerCase();q=b}q("parentNode",h,l,g,p,k)},"~":function(g,h,k){var l=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,l,g,p,k)}},find:{ID:function(g,h,k){if(typeof h.getElementById!=="undefined"&&!k)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var k=[];h=h.getElementsByName(g[1]);for(var l=0,q=h.length;l<q;l++)h[l].getAttribute("name")===g[1]&&k.push(h[l]);return k.length===0?null:k}},
-TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,k,l,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var u;(u=h[p])!=null;p++)if(u)if(q^(u.className&&(" "+u.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))k||l.push(u);else if(k)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&
-"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,k,l,q,p){h=g[1].replace(/\\/g,"");if(!p&&m.attrMap[h])g[1]=m.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,k,l,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=o(g[3],null,null,h);else{g=o.filter(g[3],h,k,true^q);k||l.push.apply(l,g);return false}else if(m.match.POS.test(g[0])||m.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);
-return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,k){return!!o(k[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===
-g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,h){return h===0},last:function(g,h,k,l){return h===l.length-1},even:function(g,h){return h%2===
-0},odd:function(g,h){return h%2===1},lt:function(g,h,k){return h<k[3]-0},gt:function(g,h,k){return h>k[3]-0},nth:function(g,h,k){return k[3]-0===h},eq:function(g,h,k){return k[3]-0===h}},filter:{PSEUDO:function(g,h,k,l){var q=h[1],p=m.filters[q];if(p)return p(g,k,h,l);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=h[3];k=0;for(l=h.length;k<l;k++)if(h[k]===g)return false;return true}else o.error("Syntax error, unrecognized expression: "+
-q)},CHILD:function(g,h){var k=h[1],l=g;switch(k){case "only":case "first":for(;l=l.previousSibling;)if(l.nodeType===1)return false;if(k==="first")return true;l=g;case "last":for(;l=l.nextSibling;)if(l.nodeType===1)return false;return true;case "nth":k=h[2];var q=h[3];if(k===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var u=0;for(l=p.firstChild;l;l=l.nextSibling)if(l.nodeType===1)l.nodeIndex=++u;p.sizcache=h}g=g.nodeIndex-q;return k===0?g===0:g%k===0&&g/k>=
-0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var k=h[1];g=m.attrHandle[k]?m.attrHandle[k](g):g[k]!=null?g[k]:g.getAttribute(k);k=g+"";var l=h[2];h=h[4];return g==null?l==="!=":l==="="?k===h:l==="*="?k.indexOf(h)>=0:l==="~="?(" "+k+" ").indexOf(h)>=0:!h?k&&g!==false:l==="!="?k!==h:l==="^="?
-k.indexOf(h)===0:l==="$="?k.substr(k.length-h.length)===h:l==="|="?k===h||k.substr(0,h.length+1)===h+"-":false},POS:function(g,h,k,l){var q=m.setFilters[h[2]];if(q)return q(g,k,h,l)}}},s=m.match.POS;for(var x in m.match){m.match[x]=new RegExp(m.match[x].source+/(?![^\[]*\])(?![^\(]*\))/.source);m.leftMatch[x]=new RegExp(/(^(?:.|\r|\n)*?)/.source+m.match[x].source.replace(/\\(\d+)/g,function(g,h){return"\\"+(h-0+1)}))}var A=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};
-try{Array.prototype.slice.call(r.documentElement.childNodes,0)}catch(B){A=function(g,h){h=h||[];if(i.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var k=0,l=g.length;k<l;k++)h.push(g[k]);else for(k=0;g[k];k++)h.push(g[k]);return h}}var C;if(r.documentElement.compareDocumentPosition)C=function(g,h){if(!g.compareDocumentPosition||!h.compareDocumentPosition){if(g==h)j=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===
-h?0:1;if(g===0)j=true;return g};else if("sourceIndex"in r.documentElement)C=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)j=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)j=true;return g};else if(r.createRange)C=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)j=true;return g.ownerDocument?-1:1}var k=g.ownerDocument.createRange(),l=h.ownerDocument.createRange();k.setStart(g,0);k.setEnd(g,0);l.setStart(h,0);l.setEnd(h,0);g=k.compareBoundaryPoints(Range.START_TO_END,
-l);if(g===0)j=true;return g};(function(){var g=r.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var k=r.documentElement;k.insertBefore(g,k.firstChild);if(r.getElementById(h)){m.find.ID=function(l,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(l[1]))?q.id===l[1]||typeof q.getAttributeNode!=="undefined"&&q.getAttributeNode("id").nodeValue===l[1]?[q]:v:[]};m.filter.ID=function(l,q){var p=typeof l.getAttributeNode!=="undefined"&&l.getAttributeNode("id");
-return l.nodeType===1&&p&&p.nodeValue===q}}k.removeChild(g);k=g=null})();(function(){var g=r.createElement("div");g.appendChild(r.createComment(""));if(g.getElementsByTagName("*").length>0)m.find.TAG=function(h,k){k=k.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var l=0;k[l];l++)k[l].nodeType===1&&h.push(k[l]);k=h}return k};g.innerHTML="<a href='#'></a>";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")m.attrHandle.href=function(h){return h.getAttribute("href",
-2)};g=null})();r.querySelectorAll&&function(){var g=o,h=r.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){o=function(l,q,p,u){q=q||r;if(!u&&q.nodeType===9&&!w(q))try{return A(q.querySelectorAll(l),p)}catch(t){}return g(l,q,p,u)};for(var k in g)o[k]=g[k];h=null}}();(function(){var g=r.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===
-0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){m.order.splice(1,0,"CLASS");m.find.CLASS=function(h,k,l){if(typeof k.getElementsByClassName!=="undefined"&&!l)return k.getElementsByClassName(h[1])};g=null}}})();var E=r.compareDocumentPosition?function(g,h){return g.compareDocumentPosition(h)&16}:function(g,h){return g!==h&&(g.contains?g.contains(h):true)},w=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},fa=function(g,h){var k=[],
-l="",q;for(h=h.nodeType?[h]:h;q=m.match.PSEUDO.exec(g);){l+=q[0];g=g.replace(m.match.PSEUDO,"")}g=m.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)o(g,h[q],k);return o.filter(l,k)};c.find=o;c.expr=o.selectors;c.expr[":"]=c.expr.filters;c.unique=o.uniqueSort;c.getText=a;c.isXMLDoc=w;c.contains=E})();var bb=/Until$/,cb=/^(?:parents|prevUntil|prevAll)/,db=/,/;Q=Array.prototype.slice;var Ea=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,i){return!!b.call(e,i,e)===d});else if(b.nodeType)return c.grep(a,
-function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Qa.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;c.find(a,this[f],b);if(f>0)for(var i=d;i<b.length;i++)for(var j=0;j<d;j++)if(b[j]===b[i]){b.splice(i--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=
-0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ea(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ea(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,i={},j;if(f&&a.length){e=0;for(var n=a.length;e<n;e++){j=a[e];i[j]||(i[j]=c.expr.match.POS.test(j)?c(j,b||this.context):j)}for(;f&&f.ownerDocument&&f!==b;){for(j in i){e=i[j];if(e.jquery?e.index(f)>
--1:c(f).is(e)){d.push({selector:j,elem:f});delete i[j]}}f=f.parentNode}}return d}var o=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(m,s){for(;s&&s.ownerDocument&&s!==b;){if(o?o.index(s)>-1:c(s).is(a))return s;s=s.parentNode}return null})},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),
-a);return this.pushStack(pa(a[0])||pa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},
-nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);bb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):
-e;if((this.length>1||db.test(f))&&cb.test(a))e=e.reverse();return this.pushStack(e,a,Q.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===v||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==
-b&&d.push(a);return d}});var Fa=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ga=/(<([\w:]+)[^>]*?)\/>/g,eb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,Ha=/<([\w:]+)/,fb=/<tbody/i,gb=/<|&\w+;/,sa=/checked\s*(?:[^=]|=\s*.checked.)/i,Ia=function(a,b,d){return eb.test(d)?a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],
-col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==v)return this.empty().append((this[0]&&this[0].ownerDocument||r).createTextNode(a));return c.getText(this)},
-wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?
-d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,
-false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&
-!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Fa,"").replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){qa(this,b);qa(this.find("*"),b.find("*"))}return b},html:function(a){if(a===v)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Fa,""):null;else if(typeof a==="string"&&!/<script/i.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(Ha.exec(a)||
-["",""])[1].toLowerCase()]){a=a.replace(Ga,Ia);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var i=c(this),j=i.html();i.empty().append(function(){return a.call(this,e,j)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,
-b,f))});else a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(s){return c.nodeName(s,"table")?s.getElementsByTagName("tbody")[0]||s.appendChild(s.ownerDocument.createElement("tbody")):s}var e,i,j=a[0],n=[];if(!c.support.checkClone&&arguments.length===3&&typeof j===
-"string"&&sa.test(j))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(j))return this.each(function(s){var x=c(this);a[0]=j.call(this,s,b?x.html():v);x.domManip(a,b,d)});if(this[0]){e=a[0]&&a[0].parentNode&&a[0].parentNode.nodeType===11?{fragment:a[0].parentNode}:ra(a,this,n);if(i=e.fragment.firstChild){b=b&&c.nodeName(i,"tr");for(var o=0,m=this.length;o<m;o++)d.call(b?f(this[o],i):this[o],e.cacheable||this.length>1||o>0?e.fragment.cloneNode(true):e.fragment)}n&&c.each(n,
-Ma)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);for(var e=0,i=d.length;e<i;e++){var j=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),j);f=f.concat(j)}return this.pushStack(f,a,d.selector)}});c.each({remove:function(a,b){if(!a||c.filter(a,[this]).length){if(!b&&this.nodeType===1){c.cleanData(this.getElementsByTagName("*"));c.cleanData([this])}this.parentNode&&
-this.parentNode.removeChild(this)}},empty:function(){for(this.nodeType===1&&c.cleanData(this.getElementsByTagName("*"));this.firstChild;)this.removeChild(this.firstChild)}},function(a,b){c.fn[a]=function(){return this.each(b,arguments)}});c.extend({clean:function(a,b,d,f){b=b||r;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||r;var e=[];c.each(a,function(i,j){if(typeof j==="number")j+="";if(j){if(typeof j==="string"&&!gb.test(j))j=b.createTextNode(j);else if(typeof j===
-"string"){j=j.replace(Ga,Ia);var n=(Ha.exec(j)||["",""])[1].toLowerCase(),o=F[n]||F._default,m=o[0];i=b.createElement("div");for(i.innerHTML=o[1]+j+o[2];m--;)i=i.lastChild;if(!c.support.tbody){m=fb.test(j);n=n==="table"&&!m?i.firstChild&&i.firstChild.childNodes:o[1]==="<table>"&&!m?i.childNodes:[];for(o=n.length-1;o>=0;--o)c.nodeName(n[o],"tbody")&&!n[o].childNodes.length&&n[o].parentNode.removeChild(n[o])}!c.support.leadingWhitespace&&V.test(j)&&i.insertBefore(b.createTextNode(V.exec(j)[0]),i.firstChild);
-j=c.makeArray(i.childNodes)}if(j.nodeType)e.push(j);else e=c.merge(e,j)}});if(d)for(a=0;e[a];a++)if(f&&c.nodeName(e[a],"script")&&(!e[a].type||e[a].type.toLowerCase()==="text/javascript"))f.push(e[a].parentNode?e[a].parentNode.removeChild(e[a]):e[a]);else{e[a].nodeType===1&&e.splice.apply(e,[a+1,0].concat(c.makeArray(e[a].getElementsByTagName("script"))));d.appendChild(e[a])}return e},cleanData:function(a){for(var b=0,d;(d=a[b])!=null;b++){c.event.remove(d);c.removeData(d)}}});var hb=/z-?index|font-?weight|opacity|zoom|line-?height/i,
-Ja=/alpha\([^)]*\)/,Ka=/opacity=([^)]*)/,ga=/float/i,ha=/-([a-z])/ig,ib=/([A-Z])/g,jb=/^-?\d+(?:px)?$/i,kb=/^-?\d/,lb={position:"absolute",visibility:"hidden",display:"block"},mb=["Left","Right"],nb=["Top","Bottom"],ob=r.defaultView&&r.defaultView.getComputedStyle,La=c.support.cssFloat?"cssFloat":"styleFloat",ia=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===v)return c.curCSS(d,f);if(typeof e==="number"&&!hb.test(f))e+="px";c.style(d,f,e)})};
-c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return v;if((b==="width"||b==="height")&&parseFloat(d)<0)d=v;var f=a.style||a,e=d!==v;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=Ja.test(a)?a.replace(Ja,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Ka.exec(f.filter)[1])/100+"":""}if(ga.test(b))b=La;b=b.replace(ha,ia);if(e)f[b]=d;return f[b]},css:function(a,
-b,d,f){if(b==="width"||b==="height"){var e,i=b==="width"?mb:nb;function j(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(i,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,"border"+this+"Width",true))||0})}a.offsetWidth!==0?j():c.swap(a,lb,j);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&
-a.currentStyle){f=Ka.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ga.test(b))b=La;if(!d&&e&&e[b])f=e[b];else if(ob){if(ga.test(b))b="float";b=b.replace(ib,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ha,ia);f=a.currentStyle[b]||a.currentStyle[d];if(!jb.test(f)&&kb.test(f)){b=e.left;var i=a.runtimeStyle.left;a.runtimeStyle.left=
-a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=i}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var pb=
-J(),qb=/<script(.|\s)*?\/script>/gi,rb=/select|textarea/i,sb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ja=/\?/,tb=/(\?|&)_=.*?(&|$)/,ub=/^(\w+:)?\/\/([^\/?#]+)/,vb=/%20/g;c.fn.extend({_load:c.fn.load,load:function(a,b,d){if(typeof a!=="string")return this._load(a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=
-c.param(b,c.ajaxSettings.traditional);f="POST"}var i=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(j,n){if(n==="success"||n==="notmodified")i.html(e?c("<div />").append(j.responseText.replace(qb,"")).find(e):j.responseText);d&&i.each(d,[j.responseText,n,j])}});return this},serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&
-(this.checked||rb.test(this.nodeName)||sb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,
-b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:z.XMLHttpRequest&&(z.location.protocol!=="file:"||!z.ActiveXObject)?function(){return new z.XMLHttpRequest}:
-function(){try{return new z.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&e.success.call(o,n,j,w);e.global&&f("ajaxSuccess",[w,e])}function d(){e.complete&&e.complete.call(o,w,j);e.global&&f("ajaxComplete",[w,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}
-function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),i,j,n,o=a&&a.context||e,m=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(m==="GET")N.test(e.url)||(e.url+=(ja.test(e.url)?"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||
-N.test(e.url))){i=e.jsonpCallback||"jsonp"+pb++;if(e.data)e.data=(e.data+"").replace(N,"="+i+"$1");e.url=e.url.replace(N,"="+i+"$1");e.dataType="script";z[i]=z[i]||function(q){n=q;b();d();z[i]=v;try{delete z[i]}catch(p){}A&&A.removeChild(B)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===false&&m==="GET"){var s=J(),x=e.url.replace(tb,"$1_="+s+"$2");e.url=x+(x===e.url?(ja.test(e.url)?"&":"?")+"_="+s:"")}if(e.data&&m==="GET")e.url+=(ja.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&
-c.event.trigger("ajaxStart");s=(s=ub.exec(e.url))&&(s[1]&&s[1]!==location.protocol||s[2]!==location.host);if(e.dataType==="script"&&m==="GET"&&s){var A=r.getElementsByTagName("head")[0]||r.documentElement,B=r.createElement("script");B.src=e.url;if(e.scriptCharset)B.charset=e.scriptCharset;if(!i){var C=false;B.onload=B.onreadystatechange=function(){if(!C&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){C=true;b();d();B.onload=B.onreadystatechange=null;A&&B.parentNode&&
-A.removeChild(B)}}}A.insertBefore(B,A.firstChild);return v}var E=false,w=e.xhr();if(w){e.username?w.open(m,e.url,e.async,e.username,e.password):w.open(m,e.url,e.async);try{if(e.data||a&&a.contentType)w.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&w.setRequestHeader("If-Modified-Since",c.lastModified[e.url]);c.etag[e.url]&&w.setRequestHeader("If-None-Match",c.etag[e.url])}s||w.setRequestHeader("X-Requested-With","XMLHttpRequest");w.setRequestHeader("Accept",
-e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(fa){}if(e.beforeSend&&e.beforeSend.call(o,w,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");w.abort();return false}e.global&&f("ajaxSend",[w,e]);var g=w.onreadystatechange=function(q){if(!w||w.readyState===0||q==="abort"){E||d();E=true;if(w)w.onreadystatechange=c.noop}else if(!E&&w&&(w.readyState===4||q==="timeout")){E=true;w.onreadystatechange=c.noop;j=q==="timeout"?"timeout":!c.httpSuccess(w)?
-"error":e.ifModified&&c.httpNotModified(w,e.url)?"notmodified":"success";var p;if(j==="success")try{n=c.httpData(w,e.dataType,e)}catch(u){j="parsererror";p=u}if(j==="success"||j==="notmodified")i||b();else c.handleError(e,w,j,p);d();q==="timeout"&&w.abort();if(e.async)w=null}};try{var h=w.abort;w.abort=function(){w&&h.call(w);g("abort")}}catch(k){}e.async&&e.timeout>0&&setTimeout(function(){w&&!E&&g("timeout")},e.timeout);try{w.send(m==="POST"||m==="PUT"||m==="DELETE"?e.data:null)}catch(l){c.handleError(e,
-w,null,l);d()}e.async||g();return w}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=
-f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(j,n){if(c.isArray(n))c.each(n,
-function(o,m){b?f(j,m):d(j+"["+(typeof m==="object"||c.isArray(m)?o:"")+"]",m)});else!b&&n!=null&&typeof n==="object"?c.each(n,function(o,m){d(j+"["+o+"]",m)}):f(j,n)}function f(j,n){n=c.isFunction(n)?n():n;e[e.length]=encodeURIComponent(j)+"="+encodeURIComponent(n)}var e=[];if(b===v)b=c.ajaxSettings.traditional;if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var i in a)d(i,a[i]);return e.join("&").replace(vb,"+")}});var ka={},wb=/toggle|show|hide/,xb=/^([+-]=)?([\d+-.]+)(.*)$/,
-W,ta=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(ka[d])f=ka[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();
-ka[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&
-c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var i=c.extend({},e),j,n=this.nodeType===1&&c(this).is(":hidden"),
-o=this;for(j in a){var m=j.replace(ha,ia);if(j!==m){a[m]=a[j];delete a[j];j=m}if(a[j]==="hide"&&n||a[j]==="show"&&!n)return i.complete.call(this);if((j==="height"||j==="width")&&this.style){i.display=c.css(this,"display");i.overflow=this.style.overflow}if(c.isArray(a[j])){(i.specialEasing=i.specialEasing||{})[j]=a[j][1];a[j]=a[j][0]}}if(i.overflow!=null)this.style.overflow="hidden";i.curAnim=c.extend({},a);c.each(a,function(s,x){var A=new c.fx(o,i,s);if(wb.test(x))A[x==="toggle"?n?"show":"hide":x](a);
-else{var B=xb.exec(x),C=A.cur(true)||0;if(B){x=parseFloat(B[2]);var E=B[3]||"px";if(E!=="px"){o.style[s]=(x||1)+E;C=(x||1)/A.cur(true)*C;o.style[s]=C+E}if(B[1])x=(B[1]==="-="?-1:1)*x+C;A.custom(C,x,E)}else A.custom(C,x,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",
-1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration==="number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,
-b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==
-null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(i){return e.step(i)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop===
-"width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=
-this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=
-c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=
-null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in r.documentElement?function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),
-f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=this[0];if(a)return this.each(function(s){c.offset.setOffset(this,a,s)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=
-b,e=b.ownerDocument,i,j=e.documentElement,n=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var o=b.offsetTop,m=b.offsetLeft;(b=b.parentNode)&&b!==n&&b!==j;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;i=e?e.getComputedStyle(b,null):b.currentStyle;o-=b.scrollTop;m-=b.scrollLeft;if(b===d){o+=b.offsetTop;m+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){o+=parseFloat(i.borderTopWidth)||
-0;m+=parseFloat(i.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&i.overflow!=="visible"){o+=parseFloat(i.borderTopWidth)||0;m+=parseFloat(i.borderLeftWidth)||0}f=i}if(f.position==="relative"||f.position==="static"){o+=n.offsetTop;m+=n.offsetLeft}if(c.offset.supportsFixedPosition&&f.position==="fixed"){o+=Math.max(j.scrollTop,n.scrollTop);m+=Math.max(j.scrollLeft,n.scrollLeft)}return{top:o,left:m}};c.offset={initialize:function(){var a=r.body,b=r.createElement("div"),
-d,f,e,i=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";a.insertBefore(b,a.firstChild);
-d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i;a.removeChild(b);c.offset.initialize=c.noop},
-bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),i=parseInt(c.curCSS(a,"top",true),10)||0,j=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,d,e);d={top:b.top-e.top+i,left:b.left-
-e.left+j};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=
-this.offsetParent||r.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],i;if(!e)return null;if(f!==v)return this.each(function(){if(i=ua(this))i.scrollTo(!a?f:c(i).scrollLeft(),a?f:c(i).scrollTop());else this[d]=f});else return(i=ua(e))?"pageXOffset"in i?i[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&i.document.documentElement[d]||i.document.body[d]:e[d]}});
-c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(i){var j=c(this);j[d](f.call(this,i,j[d]()))});return"scrollTo"in e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||
-e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===v?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});z.jQuery=z.$=c})(window);
+(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
+e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
+j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
+"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
+true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
+Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
+(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
+a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
+"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
+function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
+c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
+L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
+"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
+a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
+d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
+a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
+!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
+true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML="   <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
+parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
+false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
+s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
+applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
+else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
+a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
+w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
+cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
+i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
+" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
+this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
+e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
+c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
+a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
+function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
+k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
+C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
+null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
+e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
+f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
+if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
+d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
+"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
+a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
+isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
+{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
+if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
+e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
+"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
+d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
+!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
+toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
+u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
+function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
+if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
+t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
+g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
+for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
+1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
+relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
+l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
+h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
+CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
+g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
+text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
+setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
+h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
+m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
+"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
+h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
+!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
+h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
+q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
+if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
+(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
+function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
+gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
+c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
+{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
+"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
+d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
+a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
+1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
+a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
+c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
+wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
+prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
+this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
+return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
+""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
+this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
+u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
+1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
+return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
+""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
+c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
+c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
+function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
+Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
+"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
+a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
+a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
+"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
+serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
+function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
+global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
+e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
+"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
+false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
+false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
+c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
+d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
+g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
+1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
+"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
+if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
+this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
+"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
+animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
+j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
+this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
+"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
+c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
+this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
+this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
+e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
+c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
+function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
+this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
+k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
+f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
+a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
+c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
+d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
+f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
+"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
+e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
 
index 4b6c39a1dc42d613b65e83eb0bdaaa1719406035..949aec957d0b1d06b968cad9585e7185e3b919be 100644 (file)
@@ -320,18 +320,12 @@ var SN = { // StatusNet
             }
         },
 
-        NoticeReplyTo: function(notice_item) {
-            var notice = notice_item[0];
-            var notice_reply = $('.notice_reply', notice)[0];
-
-            if (jQuery.data(notice_reply, "ElementData") === undefined) {
-                jQuery.data(notice_reply, "ElementData", {Bind:'submit'});
-                $(notice_reply).bind('click', function() {
-                    var nickname = ($('.author .nickname', notice).length > 0) ? $($('.author .nickname', notice)[0]) : $('.author .nickname.uid');
-                    SN.U.NoticeReplySet(nickname.text(), $($('.notice_id', notice)[0]).text());
-                    return false;
-                });
-            }
+        NoticeReplyTo: function(notice) {
+            notice.find('.notice_reply').live('click', function() {
+                var nickname = ($('.author .nickname', notice).length > 0) ? $($('.author .nickname', notice)[0]) : $('.author .nickname.uid');
+                SN.U.NoticeReplySet(nickname.text(), $($('.notice_id', notice)[0]).text());
+                return false;
+            });
         },
 
         NoticeReplySet: function(nick,id) {
index 25727bf2b55314c806b39d390876e183be8d3bbe..b2015321386ae454df53ce32587c3d26f8fe2e0c 100644 (file)
@@ -154,7 +154,15 @@ class PoCo
                 PoCo::NS
             );
 
-            array_push($urls, new PoCoURL($type, $value, $primary));
+            $isPrimary = false;
+
+            if (isset($primary) && $primary == 'true') {
+                $isPrimary = true;
+            }
+
+            // @todo check to make sure a primary hasn't already been added
+
+            array_push($urls, new PoCoURL($type, $value, $isPrimary));
         }
         return $urls;
     }
@@ -215,6 +223,46 @@ class PoCo
         return $poco;
     }
 
+    function fromGroup($group)
+    {
+        if (empty($group)) {
+            return null;
+        }
+
+        $poco = new PoCo();
+
+        $poco->preferredUsername = $group->nickname;
+        $poco->displayName       = $group->getBestName();
+
+        $poco->note = $group->description;
+
+        $paddy = new PoCoAddress();
+        $paddy->formatted = $group->location;
+        $poco->address = $paddy;
+
+        if (!empty($group->homepage)) {
+            array_push(
+                $poco->urls,
+                new PoCoURL(
+                    'homepage',
+                    $group->homepage,
+                    true
+                )
+            );
+        }
+
+        return $poco;
+    }
+
+    function getPrimaryURL()
+    {
+        foreach ($this->urls as $url) {
+            if ($url->primary) {
+                return $url;
+            }
+        }
+    }
+
     function asString()
     {
         $xs = new XMLStringer(true);
@@ -312,6 +360,25 @@ class ActivityUtils
         return null;
     }
 
+    static function getLinks(DOMNode $element, $rel, $type=null)
+    {
+        $links = $element->getElementsByTagnameNS(self::ATOM, self::LINK);
+        $out = array();
+
+        foreach ($links as $link) {
+
+            $linkRel = $link->getAttribute(self::REL);
+            $linkType = $link->getAttribute(self::TYPE);
+
+            if ($linkRel == $rel &&
+                (is_null($type) || $linkType == $type)) {
+                $out[] = $link;
+            }
+        }
+
+        return $out;
+    }
+
     /**
      * Gets the first child element with the given tag
      *
@@ -417,6 +484,75 @@ class ActivityUtils
     }
 }
 
+// XXX: Arg! This wouldn't be necessary if we used Avatars conistently
+class AvatarLink
+{
+    public $url;
+    public $type;
+    public $size;
+    public $width;
+    public $height;
+
+    function __construct($element=null)
+    {
+        if ($element) {
+            // @fixme use correct namespaces
+            $this->url = $element->getAttribute('href');
+            $this->type = $element->getAttribute('type');
+            $width = $element->getAttribute('media:width');
+            if ($width != null) {
+                $this->width = intval($width);
+            }
+            $height = $element->getAttribute('media:height');
+            if ($height != null) {
+                $this->height = intval($height);
+            }
+        }
+    }
+
+    static function fromAvatar($avatar)
+    {
+        if (empty($avatar)) {
+            return null;
+        }
+        $alink = new AvatarLink();
+        $alink->type   = $avatar->mediatype;
+        $alink->height = $avatar->height;
+        $alink->width  = $avatar->width;
+        $alink->url    = $avatar->displayUrl();
+        return $alink;
+    }
+
+    static function fromFilename($filename, $size)
+    {
+        $alink = new AvatarLink();
+        $alink->url    = $filename;
+        $alink->height = $size;
+        if (!empty($filename)) {
+            $alink->width  = $size;
+            $alink->type   = self::mediatype($filename);
+        } else {
+            $alink->url    = User_group::defaultLogo($size);
+            $alink->type   = 'image/png';
+        }
+        return $alink;
+    }
+
+    // yuck!
+    static function mediatype($filename) {
+        $ext = strtolower(end(explode('.', $filename)));
+        if ($ext == 'jpeg') {
+            $ext = 'jpg';
+        }
+        // hope we don't support any others
+        $types = array('png', 'gif', 'jpg', 'jpeg');
+        if (in_array($ext, $types)) {
+            return 'image/' . $ext;
+        }
+        return null;
+    }
+}
+
 /**
  * A noun-ish thing in the activity universe
  *
@@ -473,7 +609,7 @@ class ActivityObject
     public $content;
     public $link;
     public $source;
-    public $avatar;
+    public $avatarLinks = array();
     public $geopoint;
     public $poco;
     public $displayName;
@@ -496,6 +632,12 @@ class ActivityObject
 
         $this->element = $element;
 
+        $this->geopoint = $this->_childContent(
+            $element,
+            ActivityContext::POINT,
+            ActivityContext::GEORSS
+        );
+
         if ($element->tagName == 'author') {
 
             $this->type  = self::PERSON; // XXX: is this fair?
@@ -535,8 +677,10 @@ class ActivityObject
         if ($this->type == self::PERSON || $this->type == self::GROUP) {
             $this->displayName = $this->title;
 
-            // @fixme we may have multiple avatars with different resolutions specified
-            $this->avatar = ActivityUtils::getLink($element, 'avatar');
+            $avatars = ActivityUtils::getLinks($element, 'avatar');
+            foreach ($avatars as $link) {
+                $this->avatarLinks[] = new AvatarLink($link);
+            }
 
             $this->poco = new PoCo($element);
         }
@@ -587,10 +731,40 @@ class ActivityObject
         $object->id     = $profile->getUri();
         $object->title  = $profile->getBestName();
         $object->link   = $profile->profileurl;
-        $object->avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
+
+        $orig = $profile->getOriginalAvatar();
+
+        if (!empty($orig)) {
+            $object->avatarLinks[] = AvatarLink::fromAvatar($orig);
+        }
+
+        $sizes = array(
+            AVATAR_PROFILE_SIZE,
+            AVATAR_STREAM_SIZE,
+            AVATAR_MINI_SIZE
+        );
+
+        foreach ($sizes as $size) {
+
+            $alink  = null;
+            $avatar = $profile->getAvatar($size);
+
+            if (!empty($avatar)) {
+                $alink = AvatarLink::fromAvatar($avatar);
+            } else {
+                $alink = new AvatarLink();
+                $alink->type   = 'image/png';
+                $alink->height = $size;
+                $alink->width  = $size;
+                $alink->url    = Avatar::defaultImage($size);
+            }
+
+            $object->avatarLinks[] = $alink;
+        }
 
         if (isset($profile->lat) && isset($profile->lon)) {
-            $object->geopoint = (float)$profile->lat . ' ' . (float)$profile->lon;
+            $object->geopoint = (float)$profile->lat
+                . ' ' . (float)$profile->lon;
         }
 
         $object->poco = PoCo::fromProfile($profile);
@@ -598,6 +772,36 @@ class ActivityObject
         return $object;
     }
 
+    static function fromGroup($group)
+    {
+        $object = new ActivityObject();
+
+        $object->type   = ActivityObject::GROUP;
+        $object->id     = $group->getUri();
+        $object->title  = $group->getBestName();
+        $object->link   = $group->getUri();
+
+        $object->avatarLinks[] = AvatarLink::fromFilename(
+            $group->homepage_logo,
+            AVATAR_PROFILE_SIZE
+        );
+
+        $object->avatarLinks[] = AvatarLink::fromFilename(
+            $group->stream_logo,
+            AVATAR_STREAM_SIZE
+        );
+
+        $object->avatarLinks[] = AvatarLink::fromFilename(
+            $group->mini_logo,
+            AVATAR_MINI_SIZE
+        );
+
+        $object->poco = PoCo::fromGroup($group);
+
+        return $object;
+    }
+
+
     function asString($tag='activity:object')
     {
         $xs = new XMLStringer(true);
@@ -635,16 +839,19 @@ class ActivityObject
 
         if ($this->type == ActivityObject::PERSON
             || $this->type == ActivityObject::GROUP) {
-            $xs->element(
-                'link', array(
-                    'type' => empty($this->avatar) ? 'image/png' : $this->avatar->mediatype,
-                    'rel'  => 'avatar',
-                    'href' => empty($this->avatar)
-                    ? Avatar::defaultImage(AVATAR_PROFILE_SIZE)
-                    : $this->avatar->displayUrl()
-                ),
-                null
-            );
+
+            foreach ($this->avatarLinks as $avatar) {
+                $xs->element(
+                    'link', array(
+                        'rel'  => 'avatar',
+                        'type'         => $avatar->type,
+                        'media:width'  => $avatar->width,
+                        'media:height' => $avatar->height,
+                        'href' => $avatar->url
+                    ),
+                    null
+                );
+            }
         }
 
         if (!empty($this->geopoint)) {
@@ -761,22 +968,29 @@ class ActivityContext
 
         for ($i = 0; $i < $points->length; $i++) {
             $point = $points->item($i)->textContent;
-            $point = str_replace(',', ' ', $point); // per spec "treat commas as whitespace"
-            $point = preg_replace('/\s+/', ' ', $point);
-            $point = trim($point);
-            $coords = explode(' ', $point);
-            if (count($coords) == 2) {
-                list($lat, $lon) = $coords;
-                if (is_numeric($lat) && is_numeric($lon)) {
-                    common_log(LOG_INFO, "Looking up location for $lat $lon from georss");
-                    return Location::fromLatLon($lat, $lon);
-                }
-            }
-            common_log(LOG_ERR, "Ignoring bogus georss:point value $point");
+            return self::locationFromPoint($point);
         }
 
         return null;
     }
+
+    // XXX: Move to ActivityUtils or Location?
+    static function locationFromPoint($point)
+    {
+        $point = str_replace(',', ' ', $point); // per spec "treat commas as whitespace"
+        $point = preg_replace('/\s+/', ' ', $point);
+        $point = trim($point);
+        $coords = explode(' ', $point);
+        if (count($coords) == 2) {
+            list($lat, $lon) = $coords;
+            if (is_numeric($lat) && is_numeric($lon)) {
+                common_log(LOG_INFO, "Looking up location for $lat $lon from georss point");
+                return Location::fromLatLon($lat, $lon);
+            }
+        }
+        common_log(LOG_ERR, "Ignoring bogus georss:point value $point");
+        return null;
+    }
 }
 
 /**
@@ -829,6 +1043,7 @@ class Activity
     public $content; // HTML content of activity
     public $id;      // ID of the activity
     public $title;   // title of the activity
+    public $categories = array(); // list of AtomCategory objects
 
     /**
      * Turns a regular old Atom <entry> into a magical activity
@@ -917,6 +1132,14 @@ class Activity
         $this->summary = ActivityUtils::childContent($entry, 'summary');
         $this->id      = ActivityUtils::childContent($entry, 'id');
         $this->content = ActivityUtils::getContent($entry);
+
+        $catEls = $entry->getElementsByTagNameNS(self::ATOM, 'category');
+        if ($catEls) {
+            for ($i = 0; $i < $catEls->length; $i++) {
+                $catEl = $catEls->item($i);
+                $this->categories[] = new AtomCategory($catEl);
+            }
+        }
     }
 
     /**
@@ -939,7 +1162,8 @@ class Activity
                            'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/',
                            'xmlns:georss' => 'http://www.georss.org/georss',
                            'xmlns:ostatus' => 'http://ostatus.org/schema/1.0',
-                           'xmlns:poco' => 'http://portablecontacts.net/spec/1.0');
+                           'xmlns:poco' => 'http://portablecontacts.net/spec/1.0',
+                           'xmlns:media' => 'http://purl.org/syndication/atommedia');
         } else {
             $attrs = array();
         }
@@ -981,6 +1205,10 @@ class Activity
             $xs->raw($this->target->asString('activity:target'));
         }
 
+        foreach ($this->categories as $cat) {
+            $xs->raw($cat->asString());
+        }
+
         $xs->elementEnd('entry');
 
         return $xs->getString();
@@ -990,4 +1218,50 @@ class Activity
     {
         return ActivityUtils::child($element, $tag, $namespace);
     }
-}
\ No newline at end of file
+}
+
+class AtomCategory
+{
+    public $term;
+    public $scheme;
+    public $label;
+
+    function __construct($element=null)
+    {
+        if ($element && $element->attributes) {
+            $this->term = $this->extract($element, 'term');
+            $this->scheme = $this->extract($element, 'scheme');
+            $this->label = $this->extract($element, 'label');
+        }
+    }
+
+    protected function extract($element, $attrib)
+    {
+        $node = $element->attributes->getNamedItemNS(Activity::ATOM, $attrib);
+        if ($node) {
+            return trim($node->textContent);
+        }
+        $node = $element->attributes->getNamedItem($attrib);
+        if ($node) {
+            return trim($node->textContent);
+        }
+        return null;
+    }
+
+    function asString()
+    {
+        $attribs = array();
+        if ($this->term !== null) {
+            $attribs['term'] = $this->term;
+        }
+        if ($this->scheme !== null) {
+            $attribs['scheme'] = $this->scheme;
+        }
+        if ($this->label !== null) {
+            $attribs['label'] = $this->label;
+        }
+        $xs = new XMLStringer();
+        $xs->element('category', $attribs);
+        return $xs->asString();
+    }
+}
diff --git a/lib/api.php b/lib/api.php
deleted file mode 100644 (file)
index 26977c9..0000000
+++ /dev/null
@@ -1,1342 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Base API action
- *
- * PHP version 5
- *
- * LICENCE: This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  API
- * @package   StatusNet
- * @author    Craig Andrews <candrews@integralblue.com>
- * @author    Dan Moore <dan@moore.cx>
- * @author    Evan Prodromou <evan@status.net>
- * @author    Jeffery To <jeffery.to@gmail.com>
- * @author    Toby Inkster <mail@tobyinkster.co.uk>
- * @author    Zach Copley <zach@status.net>
- * @copyright 2009 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Contains most of the Twitter-compatible API output functions.
- *
- * @category API
- * @package  StatusNet
- * @author   Craig Andrews <candrews@integralblue.com>
- * @author   Dan Moore <dan@moore.cx>
- * @author   Evan Prodromou <evan@status.net>
- * @author   Jeffery To <jeffery.to@gmail.com>
- * @author   Toby Inkster <mail@tobyinkster.co.uk>
- * @author   Zach Copley <zach@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-
-class ApiAction extends Action
-{
-    const READ_ONLY  = 1;
-    const READ_WRITE = 2;
-
-    var $format    = null;
-    var $user      = null;
-    var $auth_user = null;
-    var $page      = null;
-    var $count     = null;
-    var $max_id    = null;
-    var $since_id  = null;
-    var $since     = null;
-
-    var $access    = self::READ_ONLY;  // read (default) or read-write
-
-    /**
-     * Initialization.
-     *
-     * @param array $args Web and URL arguments
-     *
-     * @return boolean false if user doesn't exist
-     */
-
-    function prepare($args)
-    {
-        StatusNet::setApi(true); // reduce exception reports to aid in debugging
-        parent::prepare($args);
-
-        $this->format   = $this->arg('format');
-        $this->page     = (int)$this->arg('page', 1);
-        $this->count    = (int)$this->arg('count', 20);
-        $this->max_id   = (int)$this->arg('max_id', 0);
-        $this->since_id = (int)$this->arg('since_id', 0);
-        $this->since    = $this->arg('since');
-
-        return true;
-    }
-
-    /**
-     * Handle a request
-     *
-     * @param array $args Arguments from $_REQUEST
-     *
-     * @return void
-     */
-
-    function handle($args)
-    {
-        parent::handle($args);
-    }
-
-    /**
-     * Overrides XMLOutputter::element to write booleans as strings (true|false).
-     * See that method's documentation for more info.
-     *
-     * @param string $tag     Element type or tagname
-     * @param array  $attrs   Array of element attributes, as
-     *                        key-value pairs
-     * @param string $content string content of the element
-     *
-     * @return void
-     */
-    function element($tag, $attrs=null, $content=null)
-    {
-        if (is_bool($content)) {
-            $content = ($content ? 'true' : 'false');
-        }
-
-        return parent::element($tag, $attrs, $content);
-    }
-
-    function twitterUserArray($profile, $get_notice=false)
-    {
-        $twitter_user = array();
-
-        $twitter_user['id'] = intval($profile->id);
-        $twitter_user['name'] = $profile->getBestName();
-        $twitter_user['screen_name'] = $profile->nickname;
-        $twitter_user['location'] = ($profile->location) ? $profile->location : null;
-        $twitter_user['description'] = ($profile->bio) ? $profile->bio : null;
-
-        $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE);
-        $twitter_user['profile_image_url'] = ($avatar) ? $avatar->displayUrl() :
-            Avatar::defaultImage(AVATAR_STREAM_SIZE);
-
-        $twitter_user['url'] = ($profile->homepage) ? $profile->homepage : null;
-        $twitter_user['protected'] = false; # not supported by StatusNet yet
-        $twitter_user['followers_count'] = $profile->subscriberCount();
-
-        $design        = null;
-        $user          = $profile->getUser();
-
-        // Note: some profiles don't have an associated user
-
-        $defaultDesign = Design::siteDesign();
-
-        if (!empty($user)) {
-            $design = $user->getDesign();
-        }
-
-        if (empty($design)) {
-            $design = $defaultDesign;
-        }
-
-        $color = Design::toWebColor(empty($design->backgroundcolor) ? $defaultDesign->backgroundcolor : $design->backgroundcolor);
-        $twitter_user['profile_background_color'] = ($color == null) ? '' : '#'.$color->hexValue();
-        $color = Design::toWebColor(empty($design->textcolor) ? $defaultDesign->textcolor : $design->textcolor);
-        $twitter_user['profile_text_color'] = ($color == null) ? '' : '#'.$color->hexValue();
-        $color = Design::toWebColor(empty($design->linkcolor) ? $defaultDesign->linkcolor : $design->linkcolor);
-        $twitter_user['profile_link_color'] = ($color == null) ? '' : '#'.$color->hexValue();
-        $color = Design::toWebColor(empty($design->sidebarcolor) ? $defaultDesign->sidebarcolor : $design->sidebarcolor);
-        $twitter_user['profile_sidebar_fill_color'] = ($color == null) ? '' : '#'.$color->hexValue();
-        $twitter_user['profile_sidebar_border_color'] = '';
-
-        $twitter_user['friends_count'] = $profile->subscriptionCount();
-
-        $twitter_user['created_at'] = $this->dateTwitter($profile->created);
-
-        $twitter_user['favourites_count'] = $profile->faveCount(); // British spelling!
-
-        $timezone = 'UTC';
-
-        if (!empty($user) && $user->timezone) {
-            $timezone = $user->timezone;
-        }
-
-        $t = new DateTime;
-        $t->setTimezone(new DateTimeZone($timezone));
-
-        $twitter_user['utc_offset'] = $t->format('Z');
-        $twitter_user['time_zone'] = $timezone;
-
-        $twitter_user['profile_background_image_url']
-            = empty($design->backgroundimage)
-            ? '' : ($design->disposition & BACKGROUND_ON)
-            ? Design::url($design->backgroundimage) : '';
-
-        $twitter_user['profile_background_tile']
-            = empty($design->disposition)
-            ? '' : ($design->disposition & BACKGROUND_TILE) ? 'true' : 'false';
-
-        $twitter_user['statuses_count'] = $profile->noticeCount();
-
-        // Is the requesting user following this user?
-        $twitter_user['following'] = false;
-        $twitter_user['notifications'] = false;
-
-        if (isset($this->auth_user)) {
-
-            $twitter_user['following'] = $this->auth_user->isSubscribed($profile);
-
-            // Notifications on?
-            $sub = Subscription::pkeyGet(array('subscriber' =>
-                                               $this->auth_user->id,
-                                               'subscribed' => $profile->id));
-
-            if ($sub) {
-                $twitter_user['notifications'] = ($sub->jabber || $sub->sms);
-            }
-        }
-
-        if ($get_notice) {
-            $notice = $profile->getCurrentNotice();
-            if ($notice) {
-                # don't get user!
-                $twitter_user['status'] = $this->twitterStatusArray($notice, false);
-            }
-        }
-
-        return $twitter_user;
-    }
-
-    function twitterStatusArray($notice, $include_user=true)
-    {
-        $base = $this->twitterSimpleStatusArray($notice, $include_user);
-
-        if (!empty($notice->repeat_of)) {
-            $original = Notice::staticGet('id', $notice->repeat_of);
-            if (!empty($original)) {
-                $original_array = $this->twitterSimpleStatusArray($original, $include_user);
-                $base['retweeted_status'] = $original_array;
-            }
-        }
-
-        return $base;
-    }
-
-    function twitterSimpleStatusArray($notice, $include_user=true)
-    {
-        $profile = $notice->getProfile();
-
-        $twitter_status = array();
-        $twitter_status['text'] = $notice->content;
-        $twitter_status['truncated'] = false; # Not possible on StatusNet
-        $twitter_status['created_at'] = $this->dateTwitter($notice->created);
-        $twitter_status['in_reply_to_status_id'] = ($notice->reply_to) ?
-            intval($notice->reply_to) : null;
-        $twitter_status['source'] = $this->sourceLink($notice->source);
-        $twitter_status['id'] = intval($notice->id);
-
-        $replier_profile = null;
-
-        if ($notice->reply_to) {
-            $reply = Notice::staticGet(intval($notice->reply_to));
-            if ($reply) {
-                $replier_profile = $reply->getProfile();
-            }
-        }
-
-        $twitter_status['in_reply_to_user_id'] =
-            ($replier_profile) ? intval($replier_profile->id) : null;
-        $twitter_status['in_reply_to_screen_name'] =
-            ($replier_profile) ? $replier_profile->nickname : null;
-
-        if (isset($notice->lat) && isset($notice->lon)) {
-            // This is the format that GeoJSON expects stuff to be in
-            $twitter_status['geo'] = array('type' => 'Point',
-                                           'coordinates' => array((float) $notice->lat,
-                                                                  (float) $notice->lon));
-        } else {
-            $twitter_status['geo'] = null;
-        }
-
-        if (isset($this->auth_user)) {
-            $twitter_status['favorited'] = $this->auth_user->hasFave($notice);
-        } else {
-            $twitter_status['favorited'] = false;
-        }
-
-        // Enclosures
-        $attachments = $notice->attachments();
-
-        if (!empty($attachments)) {
-
-            $twitter_status['attachments'] = array();
-
-            foreach ($attachments as $attachment) {
-                $enclosure_o=$attachment->getEnclosure();
-                if ($enclosure_o) {
-                    $enclosure = array();
-                    $enclosure['url'] = $enclosure_o->url;
-                    $enclosure['mimetype'] = $enclosure_o->mimetype;
-                    $enclosure['size'] = $enclosure_o->size;
-                    $twitter_status['attachments'][] = $enclosure;
-                }
-            }
-        }
-
-        if ($include_user && $profile) {
-            # Don't get notice (recursive!)
-            $twitter_user = $this->twitterUserArray($profile, false);
-            $twitter_status['user'] = $twitter_user;
-        }
-
-        return $twitter_status;
-    }
-
-    function twitterGroupArray($group)
-    {
-        $twitter_group=array();
-        $twitter_group['id']=$group->id;
-        $twitter_group['url']=$group->permalink();
-        $twitter_group['nickname']=$group->nickname;
-        $twitter_group['fullname']=$group->fullname;
-        $twitter_group['homepage_url']=$group->homepage_url;
-        $twitter_group['original_logo']=$group->original_logo;
-        $twitter_group['homepage_logo']=$group->homepage_logo;
-        $twitter_group['stream_logo']=$group->stream_logo;
-        $twitter_group['mini_logo']=$group->mini_logo;
-        $twitter_group['homepage']=$group->homepage;
-        $twitter_group['description']=$group->description;
-        $twitter_group['location']=$group->location;
-        $twitter_group['created']=$this->dateTwitter($group->created);
-        $twitter_group['modified']=$this->dateTwitter($group->modified);
-        return $twitter_group;
-    }
-
-    function twitterRssGroupArray($group)
-    {
-        $entry = array();
-        $entry['content']=$group->description;
-        $entry['title']=$group->nickname;
-        $entry['link']=$group->permalink();
-        $entry['published']=common_date_iso8601($group->created);
-        $entry['updated']==common_date_iso8601($group->modified);
-        $taguribase = common_config('integration', 'groupuri');
-        $entry['id'] = "group:$groupuribase:$entry[link]";
-
-        $entry['description'] = $entry['content'];
-        $entry['pubDate'] = common_date_rfc2822($group->created);
-        $entry['guid'] = $entry['link'];
-
-        return $entry;
-    }
-
-    function twitterRssEntryArray($notice)
-    {
-        $profile = $notice->getProfile();
-        $entry = array();
-
-        // We trim() to avoid extraneous whitespace in the output
-
-        $entry['content'] = common_xml_safe_str(trim($notice->rendered));
-        $entry['title'] = $profile->nickname . ': ' . common_xml_safe_str(trim($notice->content));
-        $entry['link'] = common_local_url('shownotice', array('notice' => $notice->id));
-        $entry['published'] = common_date_iso8601($notice->created);
-
-        $taguribase = TagURI::base();
-        $entry['id'] = "tag:$taguribase:$entry[link]";
-
-        $entry['updated'] = $entry['published'];
-        $entry['author'] = $profile->getBestName();
-
-        // Enclosures
-        $attachments = $notice->attachments();
-        $enclosures = array();
-
-        foreach ($attachments as $attachment) {
-            $enclosure_o=$attachment->getEnclosure();
-            if ($enclosure_o) {
-                 $enclosure = array();
-                 $enclosure['url'] = $enclosure_o->url;
-                 $enclosure['mimetype'] = $enclosure_o->mimetype;
-                 $enclosure['size'] = $enclosure_o->size;
-                 $enclosures[] = $enclosure;
-            }
-        }
-
-        if (!empty($enclosures)) {
-            $entry['enclosures'] = $enclosures;
-        }
-
-        // Tags/Categories
-        $tag = new Notice_tag();
-        $tag->notice_id = $notice->id;
-        if ($tag->find()) {
-            $entry['tags']=array();
-            while ($tag->fetch()) {
-                $entry['tags'][]=$tag->tag;
-            }
-        }
-        $tag->free();
-
-        // RSS Item specific
-        $entry['description'] = $entry['content'];
-        $entry['pubDate'] = common_date_rfc2822($notice->created);
-        $entry['guid'] = $entry['link'];
-
-        if (isset($notice->lat) && isset($notice->lon)) {
-            // This is the format that GeoJSON expects stuff to be in.
-            // showGeoRSS() below uses it for XML output, so we reuse it
-            $entry['geo'] = array('type' => 'Point',
-                                  'coordinates' => array((float) $notice->lat,
-                                                         (float) $notice->lon));
-        } else {
-            $entry['geo'] = null;
-        }
-
-        return $entry;
-    }
-
-    function twitterRelationshipArray($source, $target)
-    {
-        $relationship = array();
-
-        $relationship['source'] =
-            $this->relationshipDetailsArray($source, $target);
-        $relationship['target'] =
-            $this->relationshipDetailsArray($target, $source);
-
-        return array('relationship' => $relationship);
-    }
-
-    function relationshipDetailsArray($source, $target)
-    {
-        $details = array();
-
-        $details['screen_name'] = $source->nickname;
-        $details['followed_by'] = $target->isSubscribed($source);
-        $details['following'] = $source->isSubscribed($target);
-
-        $notifications = false;
-
-        if ($source->isSubscribed($target)) {
-
-            $sub = Subscription::pkeyGet(array('subscriber' =>
-                $source->id, 'subscribed' => $target->id));
-
-            if (!empty($sub)) {
-                $notifications = ($sub->jabber || $sub->sms);
-            }
-        }
-
-        $details['notifications_enabled'] = $notifications;
-        $details['blocking'] = $source->hasBlocked($target);
-        $details['id'] = $source->id;
-
-        return $details;
-    }
-
-    function showTwitterXmlRelationship($relationship)
-    {
-        $this->elementStart('relationship');
-
-        foreach($relationship as $element => $value) {
-            if ($element == 'source' || $element == 'target') {
-                $this->elementStart($element);
-                $this->showXmlRelationshipDetails($value);
-                $this->elementEnd($element);
-            }
-        }
-
-        $this->elementEnd('relationship');
-    }
-
-    function showXmlRelationshipDetails($details)
-    {
-        foreach($details as $element => $value) {
-            $this->element($element, null, $value);
-        }
-    }
-
-    function showTwitterXmlStatus($twitter_status, $tag='status')
-    {
-        $this->elementStart($tag);
-        foreach($twitter_status as $element => $value) {
-            switch ($element) {
-            case 'user':
-                $this->showTwitterXmlUser($twitter_status['user']);
-                break;
-            case 'text':
-                $this->element($element, null, common_xml_safe_str($value));
-                break;
-            case 'attachments':
-                $this->showXmlAttachments($twitter_status['attachments']);
-                break;
-            case 'geo':
-                $this->showGeoRSS($value);
-                break;
-            case 'retweeted_status':
-                $this->showTwitterXmlStatus($value, 'retweeted_status');
-                break;
-            default:
-                $this->element($element, null, $value);
-            }
-        }
-        $this->elementEnd($tag);
-    }
-
-    function showTwitterXmlGroup($twitter_group)
-    {
-        $this->elementStart('group');
-        foreach($twitter_group as $element => $value) {
-            $this->element($element, null, $value);
-        }
-        $this->elementEnd('group');
-    }
-
-    function showTwitterXmlUser($twitter_user, $role='user')
-    {
-        $this->elementStart($role);
-        foreach($twitter_user as $element => $value) {
-            if ($element == 'status') {
-                $this->showTwitterXmlStatus($twitter_user['status']);
-            } else {
-                $this->element($element, null, $value);
-            }
-        }
-        $this->elementEnd($role);
-    }
-
-    function showXmlAttachments($attachments) {
-        if (!empty($attachments)) {
-            $this->elementStart('attachments', array('type' => 'array'));
-            foreach ($attachments as $attachment) {
-                $attrs = array();
-                $attrs['url'] = $attachment['url'];
-                $attrs['mimetype'] = $attachment['mimetype'];
-                $attrs['size'] = $attachment['size'];
-                $this->element('enclosure', $attrs, '');
-            }
-            $this->elementEnd('attachments');
-        }
-    }
-
-    function showGeoRSS($geo)
-    {
-        if (empty($geo)) {
-            // empty geo element
-            $this->element('geo');
-        } else {
-            $this->elementStart('geo', array('xmlns:georss' => 'http://www.georss.org/georss'));
-            $this->element('georss:point', null, $geo['coordinates'][0] . ' ' . $geo['coordinates'][1]);
-            $this->elementEnd('geo');
-        }
-    }
-
-    function showTwitterRssItem($entry)
-    {
-        $this->elementStart('item');
-        $this->element('title', null, $entry['title']);
-        $this->element('description', null, $entry['description']);
-        $this->element('pubDate', null, $entry['pubDate']);
-        $this->element('guid', null, $entry['guid']);
-        $this->element('link', null, $entry['link']);
-
-        # RSS only supports 1 enclosure per item
-        if(array_key_exists('enclosures', $entry) and !empty($entry['enclosures'])){
-            $enclosure = $entry['enclosures'][0];
-            $this->element('enclosure', array('url'=>$enclosure['url'],'type'=>$enclosure['mimetype'],'length'=>$enclosure['size']), null);
-        }
-
-        if(array_key_exists('tags', $entry)){
-            foreach($entry['tags'] as $tag){
-                $this->element('category', null,$tag);
-            }
-        }
-
-        $this->showGeoRSS($entry['geo']);
-        $this->elementEnd('item');
-    }
-
-    function showJsonObjects($objects)
-    {
-        print(json_encode($objects));
-    }
-
-    function showSingleXmlStatus($notice)
-    {
-        $this->initDocument('xml');
-        $twitter_status = $this->twitterStatusArray($notice);
-        $this->showTwitterXmlStatus($twitter_status);
-        $this->endDocument('xml');
-    }
-
-    function show_single_json_status($notice)
-    {
-        $this->initDocument('json');
-        $status = $this->twitterStatusArray($notice);
-        $this->showJsonObjects($status);
-        $this->endDocument('json');
-    }
-
-    function showXmlTimeline($notice)
-    {
-
-        $this->initDocument('xml');
-        $this->elementStart('statuses', array('type' => 'array'));
-
-        if (is_array($notice)) {
-            foreach ($notice as $n) {
-                $twitter_status = $this->twitterStatusArray($n);
-                $this->showTwitterXmlStatus($twitter_status);
-            }
-        } else {
-            while ($notice->fetch()) {
-                $twitter_status = $this->twitterStatusArray($notice);
-                $this->showTwitterXmlStatus($twitter_status);
-            }
-        }
-
-        $this->elementEnd('statuses');
-        $this->endDocument('xml');
-    }
-
-    function showRssTimeline($notice, $title, $link, $subtitle, $suplink=null, $logo=null)
-    {
-
-        $this->initDocument('rss');
-
-        $this->element('title', null, $title);
-        $this->element('link', null, $link);
-        if (!is_null($suplink)) {
-            // For FriendFeed's SUP protocol
-            $this->element('link', array('xmlns' => 'http://www.w3.org/2005/Atom',
-                                         'rel' => 'http://api.friendfeed.com/2008/03#sup',
-                                         'href' => $suplink,
-                                         'type' => 'application/json'));
-        }
-
-        if (!is_null($logo)) {
-            $this->elementStart('image');
-            $this->element('link', null, $link);
-            $this->element('title', null, $title);
-            $this->element('url', null, $logo);
-            $this->elementEnd('image');
-        }
-
-        $this->element('description', null, $subtitle);
-        $this->element('language', null, 'en-us');
-        $this->element('ttl', null, '40');
-
-        if (is_array($notice)) {
-            foreach ($notice as $n) {
-                $entry = $this->twitterRssEntryArray($n);
-                $this->showTwitterRssItem($entry);
-            }
-        } else {
-            while ($notice->fetch()) {
-                $entry = $this->twitterRssEntryArray($notice);
-                $this->showTwitterRssItem($entry);
-            }
-        }
-
-        $this->endTwitterRss();
-    }
-
-    function showAtomTimeline($notice, $title, $id, $link, $subtitle=null, $suplink=null, $selfuri=null, $logo=null)
-    {
-
-        $this->initDocument('atom');
-
-        $this->element('title', null, $title);
-        $this->element('id', null, $id);
-        $this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null);
-
-        if (!is_null($logo)) {
-            $this->element('logo',null,$logo);
-        }
-
-        if (!is_null($suplink)) {
-            # For FriendFeed's SUP protocol
-            $this->element('link', array('rel' => 'http://api.friendfeed.com/2008/03#sup',
-                                         'href' => $suplink,
-                                         'type' => 'application/json'));
-        }
-
-        if (!is_null($selfuri)) {
-            $this->element('link', array('href' => $selfuri,
-                'rel' => 'self', 'type' => 'application/atom+xml'), null);
-        }
-
-        $this->element('updated', null, common_date_iso8601('now'));
-        $this->element('subtitle', null, $subtitle);
-
-        if (is_array($notice)) {
-            foreach ($notice as $n) {
-                $this->raw($n->asAtomEntry());
-            }
-        } else {
-            while ($notice->fetch()) {
-                $this->raw($notice->asAtomEntry());
-            }
-        }
-
-        $this->endDocument('atom');
-
-    }
-
-    function showRssGroups($group, $title, $link, $subtitle)
-    {
-
-        $this->initDocument('rss');
-
-        $this->element('title', null, $title);
-        $this->element('link', null, $link);
-        $this->element('description', null, $subtitle);
-        $this->element('language', null, 'en-us');
-        $this->element('ttl', null, '40');
-
-        if (is_array($group)) {
-            foreach ($group as $g) {
-                $twitter_group = $this->twitterRssGroupArray($g);
-                $this->showTwitterRssItem($twitter_group);
-            }
-        } else {
-            while ($group->fetch()) {
-                $twitter_group = $this->twitterRssGroupArray($group);
-                $this->showTwitterRssItem($twitter_group);
-            }
-        }
-
-        $this->endTwitterRss();
-    }
-
-    function showTwitterAtomEntry($entry)
-    {
-        $this->elementStart('entry');
-        $this->element('title', null, $entry['title']);
-        $this->element('content', array('type' => 'html'), $entry['content']);
-        $this->element('id', null, $entry['id']);
-        $this->element('published', null, $entry['published']);
-        $this->element('updated', null, $entry['updated']);
-        $this->element('link', array('type' => 'text/html',
-                                     'href' => $entry['link'],
-                                     'rel' => 'alternate'));
-        $this->element('link', array('type' => $entry['avatar-type'],
-                                     'href' => $entry['avatar'],
-                                     'rel' => 'image'));
-        $this->elementStart('author');
-
-        $this->element('name', null, $entry['author-name']);
-        $this->element('uri', null, $entry['author-uri']);
-
-        $this->elementEnd('author');
-        $this->elementEnd('entry');
-    }
-
-    function showXmlDirectMessage($dm)
-    {
-        $this->elementStart('direct_message');
-        foreach($dm as $element => $value) {
-            switch ($element) {
-            case 'sender':
-            case 'recipient':
-                $this->showTwitterXmlUser($value, $element);
-                break;
-            case 'text':
-                $this->element($element, null, common_xml_safe_str($value));
-                break;
-            default:
-                $this->element($element, null, $value);
-                break;
-            }
-        }
-        $this->elementEnd('direct_message');
-    }
-
-    function directMessageArray($message)
-    {
-        $dmsg = array();
-
-        $from_profile = $message->getFrom();
-        $to_profile = $message->getTo();
-
-        $dmsg['id'] = $message->id;
-        $dmsg['sender_id'] = $message->from_profile;
-        $dmsg['text'] = trim($message->content);
-        $dmsg['recipient_id'] = $message->to_profile;
-        $dmsg['created_at'] = $this->dateTwitter($message->created);
-        $dmsg['sender_screen_name'] = $from_profile->nickname;
-        $dmsg['recipient_screen_name'] = $to_profile->nickname;
-        $dmsg['sender'] = $this->twitterUserArray($from_profile, false);
-        $dmsg['recipient'] = $this->twitterUserArray($to_profile, false);
-
-        return $dmsg;
-    }
-
-    function rssDirectMessageArray($message)
-    {
-        $entry = array();
-
-        $from = $message->getFrom();
-
-        $entry['title'] = sprintf('Message from %1$s to %2$s',
-            $from->nickname, $message->getTo()->nickname);
-
-        $entry['content'] = common_xml_safe_str($message->rendered);
-        $entry['link'] = common_local_url('showmessage', array('message' => $message->id));
-        $entry['published'] = common_date_iso8601($message->created);
-
-        $taguribase = TagURI::base();
-
-        $entry['id'] = "tag:$taguribase:$entry[link]";
-        $entry['updated'] = $entry['published'];
-
-        $entry['author-name'] = $from->getBestName();
-        $entry['author-uri'] = $from->homepage;
-
-        $avatar = $from->getAvatar(AVATAR_STREAM_SIZE);
-
-        $entry['avatar']      = (!empty($avatar)) ? $avatar->url : Avatar::defaultImage(AVATAR_STREAM_SIZE);
-        $entry['avatar-type'] = (!empty($avatar)) ? $avatar->mediatype : 'image/png';
-
-        // RSS item specific
-
-        $entry['description'] = $entry['content'];
-        $entry['pubDate'] = common_date_rfc2822($message->created);
-        $entry['guid'] = $entry['link'];
-
-        return $entry;
-    }
-
-    function showSingleXmlDirectMessage($message)
-    {
-        $this->initDocument('xml');
-        $dmsg = $this->directMessageArray($message);
-        $this->showXmlDirectMessage($dmsg);
-        $this->endDocument('xml');
-    }
-
-    function showSingleJsonDirectMessage($message)
-    {
-        $this->initDocument('json');
-        $dmsg = $this->directMessageArray($message);
-        $this->showJsonObjects($dmsg);
-        $this->endDocument('json');
-    }
-
-    function showAtomGroups($group, $title, $id, $link, $subtitle=null, $selfuri=null)
-    {
-
-        $this->initDocument('atom');
-
-        $this->element('title', null, $title);
-        $this->element('id', null, $id);
-        $this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null);
-
-        if (!is_null($selfuri)) {
-            $this->element('link', array('href' => $selfuri,
-                'rel' => 'self', 'type' => 'application/atom+xml'), null);
-        }
-
-        $this->element('updated', null, common_date_iso8601('now'));
-        $this->element('subtitle', null, $subtitle);
-
-        if (is_array($group)) {
-            foreach ($group as $g) {
-                $this->raw($g->asAtomEntry());
-            }
-        } else {
-            while ($group->fetch()) {
-                $this->raw($group->asAtomEntry());
-            }
-        }
-
-        $this->endDocument('atom');
-
-    }
-
-    function showJsonTimeline($notice)
-    {
-
-        $this->initDocument('json');
-
-        $statuses = array();
-
-        if (is_array($notice)) {
-            foreach ($notice as $n) {
-                $twitter_status = $this->twitterStatusArray($n);
-                array_push($statuses, $twitter_status);
-            }
-        } else {
-            while ($notice->fetch()) {
-                $twitter_status = $this->twitterStatusArray($notice);
-                array_push($statuses, $twitter_status);
-            }
-        }
-
-        $this->showJsonObjects($statuses);
-
-        $this->endDocument('json');
-    }
-
-    function showJsonGroups($group)
-    {
-
-        $this->initDocument('json');
-
-        $groups = array();
-
-        if (is_array($group)) {
-            foreach ($group as $g) {
-                $twitter_group = $this->twitterGroupArray($g);
-                array_push($groups, $twitter_group);
-            }
-        } else {
-            while ($group->fetch()) {
-                $twitter_group = $this->twitterGroupArray($group);
-                array_push($groups, $twitter_group);
-            }
-        }
-
-        $this->showJsonObjects($groups);
-
-        $this->endDocument('json');
-    }
-
-    function showXmlGroups($group)
-    {
-
-        $this->initDocument('xml');
-        $this->elementStart('groups', array('type' => 'array'));
-
-        if (is_array($group)) {
-            foreach ($group as $g) {
-                $twitter_group = $this->twitterGroupArray($g);
-                $this->showTwitterXmlGroup($twitter_group);
-            }
-        } else {
-            while ($group->fetch()) {
-                $twitter_group = $this->twitterGroupArray($group);
-                $this->showTwitterXmlGroup($twitter_group);
-            }
-        }
-
-        $this->elementEnd('groups');
-        $this->endDocument('xml');
-    }
-
-    function showTwitterXmlUsers($user)
-    {
-
-        $this->initDocument('xml');
-        $this->elementStart('users', array('type' => 'array'));
-
-        if (is_array($user)) {
-            foreach ($user as $u) {
-                $twitter_user = $this->twitterUserArray($u);
-                $this->showTwitterXmlUser($twitter_user);
-            }
-        } else {
-            while ($user->fetch()) {
-                $twitter_user = $this->twitterUserArray($user);
-                $this->showTwitterXmlUser($twitter_user);
-            }
-        }
-
-        $this->elementEnd('users');
-        $this->endDocument('xml');
-    }
-
-    function showJsonUsers($user)
-    {
-
-        $this->initDocument('json');
-
-        $users = array();
-
-        if (is_array($user)) {
-            foreach ($user as $u) {
-                $twitter_user = $this->twitterUserArray($u);
-                array_push($users, $twitter_user);
-            }
-        } else {
-            while ($user->fetch()) {
-                $twitter_user = $this->twitterUserArray($user);
-                array_push($users, $twitter_user);
-            }
-        }
-
-        $this->showJsonObjects($users);
-
-        $this->endDocument('json');
-    }
-
-    function showSingleJsonGroup($group)
-    {
-        $this->initDocument('json');
-        $twitter_group = $this->twitterGroupArray($group);
-        $this->showJsonObjects($twitter_group);
-        $this->endDocument('json');
-    }
-
-    function showSingleXmlGroup($group)
-    {
-        $this->initDocument('xml');
-        $twitter_group = $this->twitterGroupArray($group);
-        $this->showTwitterXmlGroup($twitter_group);
-        $this->endDocument('xml');
-    }
-
-    function dateTwitter($dt)
-    {
-        $dateStr = date('d F Y H:i:s', strtotime($dt));
-        $d = new DateTime($dateStr, new DateTimeZone('UTC'));
-        $d->setTimezone(new DateTimeZone(common_timezone()));
-        return $d->format('D M d H:i:s O Y');
-    }
-
-    function initDocument($type='xml')
-    {
-        switch ($type) {
-        case 'xml':
-            header('Content-Type: application/xml; charset=utf-8');
-            $this->startXML();
-            break;
-        case 'json':
-            header('Content-Type: application/json; charset=utf-8');
-
-            // Check for JSONP callback
-            $callback = $this->arg('callback');
-            if ($callback) {
-                print $callback . '(';
-            }
-            break;
-        case 'rss':
-            header("Content-Type: application/rss+xml; charset=utf-8");
-            $this->initTwitterRss();
-            break;
-        case 'atom':
-            header('Content-Type: application/atom+xml; charset=utf-8');
-            $this->initTwitterAtom();
-            break;
-        default:
-            $this->clientError(_('Not a supported data format.'));
-            break;
-        }
-
-        return;
-    }
-
-    function endDocument($type='xml')
-    {
-        switch ($type) {
-        case 'xml':
-            $this->endXML();
-            break;
-        case 'json':
-
-            // Check for JSONP callback
-            $callback = $this->arg('callback');
-            if ($callback) {
-                print ')';
-            }
-            break;
-        case 'rss':
-            $this->endTwitterRss();
-            break;
-        case 'atom':
-            $this->endTwitterRss();
-            break;
-        default:
-            $this->clientError(_('Not a supported data format.'));
-            break;
-        }
-        return;
-    }
-
-    function clientError($msg, $code = 400, $format = 'xml')
-    {
-        $action = $this->trimmed('action');
-
-        common_debug("User error '$code' on '$action': $msg", __FILE__);
-
-        if (!array_key_exists($code, ClientErrorAction::$status)) {
-            $code = 400;
-        }
-
-        $status_string = ClientErrorAction::$status[$code];
-
-        header('HTTP/1.1 '.$code.' '.$status_string);
-
-        if ($format == 'xml') {
-            $this->initDocument('xml');
-            $this->elementStart('hash');
-            $this->element('error', null, $msg);
-            $this->element('request', null, $_SERVER['REQUEST_URI']);
-            $this->elementEnd('hash');
-            $this->endDocument('xml');
-        } elseif ($format == 'json'){
-            $this->initDocument('json');
-            $error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']);
-            print(json_encode($error_array));
-            $this->endDocument('json');
-        } else {
-
-            // If user didn't request a useful format, throw a regular client error
-            throw new ClientException($msg, $code);
-        }
-    }
-
-    function serverError($msg, $code = 500, $content_type = 'xml')
-    {
-        $action = $this->trimmed('action');
-
-        common_debug("Server error '$code' on '$action': $msg", __FILE__);
-
-        if (!array_key_exists($code, ServerErrorAction::$status)) {
-            $code = 400;
-        }
-
-        $status_string = ServerErrorAction::$status[$code];
-
-        header('HTTP/1.1 '.$code.' '.$status_string);
-
-        if ($content_type == 'xml') {
-            $this->initDocument('xml');
-            $this->elementStart('hash');
-            $this->element('error', null, $msg);
-            $this->element('request', null, $_SERVER['REQUEST_URI']);
-            $this->elementEnd('hash');
-            $this->endDocument('xml');
-        } else {
-            $this->initDocument('json');
-            $error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']);
-            print(json_encode($error_array));
-            $this->endDocument('json');
-        }
-    }
-
-    function initTwitterRss()
-    {
-        $this->startXML();
-        $this->elementStart('rss', array('version' => '2.0', 'xmlns:atom'=>'http://www.w3.org/2005/Atom'));
-        $this->elementStart('channel');
-        Event::handle('StartApiRss', array($this));
-    }
-
-    function endTwitterRss()
-    {
-        $this->elementEnd('channel');
-        $this->elementEnd('rss');
-        $this->endXML();
-    }
-
-    function initTwitterAtom()
-    {
-        $this->startXML();
-        // FIXME: don't hardcode the language here!
-        $this->elementStart('feed', array('xmlns' => 'http://www.w3.org/2005/Atom',
-                                          'xml:lang' => 'en-US',
-                                          'xmlns:thr' => 'http://purl.org/syndication/thread/1.0'));
-    }
-
-    function endTwitterAtom()
-    {
-        $this->elementEnd('feed');
-        $this->endXML();
-    }
-
-    function showProfile($profile, $content_type='xml', $notice=null, $includeStatuses=true)
-    {
-        $profile_array = $this->twitterUserArray($profile, $includeStatuses);
-        switch ($content_type) {
-        case 'xml':
-            $this->showTwitterXmlUser($profile_array);
-            break;
-        case 'json':
-            $this->showJsonObjects($profile_array);
-            break;
-        default:
-            $this->clientError(_('Not a supported data format.'));
-            return;
-        }
-        return;
-    }
-
-    function getTargetUser($id)
-    {
-        if (empty($id)) {
-
-            // Twitter supports these other ways of passing the user ID
-            if (is_numeric($this->arg('id'))) {
-                return User::staticGet($this->arg('id'));
-            } else if ($this->arg('id')) {
-                $nickname = common_canonical_nickname($this->arg('id'));
-                return User::staticGet('nickname', $nickname);
-            } else if ($this->arg('user_id')) {
-                // This is to ensure that a non-numeric user_id still
-                // overrides screen_name even if it doesn't get used
-                if (is_numeric($this->arg('user_id'))) {
-                    return User::staticGet('id', $this->arg('user_id'));
-                }
-            } else if ($this->arg('screen_name')) {
-                $nickname = common_canonical_nickname($this->arg('screen_name'));
-                return User::staticGet('nickname', $nickname);
-            } else {
-                // Fall back to trying the currently authenticated user
-                return $this->auth_user;
-            }
-
-        } else if (is_numeric($id)) {
-            return User::staticGet($id);
-        } else {
-            $nickname = common_canonical_nickname($id);
-            return User::staticGet('nickname', $nickname);
-        }
-    }
-
-    function getTargetGroup($id)
-    {
-        if (empty($id)) {
-            if (is_numeric($this->arg('id'))) {
-                return User_group::staticGet($this->arg('id'));
-            } else if ($this->arg('id')) {
-                $nickname = common_canonical_nickname($this->arg('id'));
-                return User_group::staticGet('nickname', $nickname);
-            } else if ($this->arg('group_id')) {
-                // This is to ensure that a non-numeric user_id still
-                // overrides screen_name even if it doesn't get used
-                if (is_numeric($this->arg('group_id'))) {
-                    return User_group::staticGet('id', $this->arg('group_id'));
-                }
-            } else if ($this->arg('group_name')) {
-                $nickname = common_canonical_nickname($this->arg('group_name'));
-                return User_group::staticGet('nickname', $nickname);
-            }
-
-        } else if (is_numeric($id)) {
-            return User_group::staticGet($id);
-        } else {
-            $nickname = common_canonical_nickname($id);
-            return User_group::staticGet('nickname', $nickname);
-        }
-    }
-
-    function sourceLink($source)
-    {
-        $source_name = _($source);
-        switch ($source) {
-        case 'web':
-        case 'xmpp':
-        case 'mail':
-        case 'omb':
-        case 'api':
-            break;
-        default:
-
-            $name = null;
-            $url  = null;
-
-            $ns = Notice_source::staticGet($source);
-
-            if ($ns) {
-                $name = $ns->name;
-                $url  = $ns->url;
-            } else {
-                $app = Oauth_application::staticGet('name', $source);
-                if ($app) {
-                    $name = $app->name;
-                    $url  = $app->source_url;
-                }
-            }
-
-            if (!empty($name) && !empty($url)) {
-                $source_name = '<a href="' . $url . '">' . $name . '</a>';
-            }
-
-            break;
-        }
-        return $source_name;
-    }
-
-    /**
-     * Returns query argument or default value if not found. Certain
-     * parameters used throughout the API are lightly scrubbed and
-     * bounds checked.  This overrides Action::arg().
-     *
-     * @param string $key requested argument
-     * @param string $def default value to return if $key is not provided
-     *
-     * @return var $var
-     */
-    function arg($key, $def=null)
-    {
-
-        // XXX: Do even more input validation/scrubbing?
-
-        if (array_key_exists($key, $this->args)) {
-            switch($key) {
-            case 'page':
-                $page = (int)$this->args['page'];
-                return ($page < 1) ? 1 : $page;
-            case 'count':
-                $count = (int)$this->args['count'];
-                if ($count < 1) {
-                    return 20;
-                } elseif ($count > 200) {
-                    return 200;
-                } else {
-                    return $count;
-                }
-            case 'since_id':
-                $since_id = (int)$this->args['since_id'];
-                return ($since_id < 1) ? 0 : $since_id;
-            case 'max_id':
-                $max_id = (int)$this->args['max_id'];
-                return ($max_id < 1) ? 0 : $max_id;
-            case 'since':
-                return strtotime($this->args['since']);
-            default:
-                return parent::arg($key, $def);
-            }
-        } else {
-            return $def;
-        }
-    }
-
-    function getSelfUri($action, $aargs)
-    {
-        parse_str($_SERVER['QUERY_STRING'], $params);
-        $pstring = '';
-        if (!empty($params)) {
-            unset($params['p']);
-            $pstring = http_build_query($params);
-        }
-
-        $uri = common_local_url($action, $aargs);
-
-        if (!empty($pstring)) {
-            $uri .= '?' . $pstring;
-        }
-
-        return $uri;
-    }
-
-}
diff --git a/lib/apiaction.php b/lib/apiaction.php
new file mode 100644 (file)
index 0000000..9bfca3b
--- /dev/null
@@ -0,0 +1,1357 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Base API action
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  API
+ * @package   StatusNet
+ * @author    Craig Andrews <candrews@integralblue.com>
+ * @author    Dan Moore <dan@moore.cx>
+ * @author    Evan Prodromou <evan@status.net>
+ * @author    Jeffery To <jeffery.to@gmail.com>
+ * @author    Toby Inkster <mail@tobyinkster.co.uk>
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Contains most of the Twitter-compatible API output functions.
+ *
+ * @category API
+ * @package  StatusNet
+ * @author   Craig Andrews <candrews@integralblue.com>
+ * @author   Dan Moore <dan@moore.cx>
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Jeffery To <jeffery.to@gmail.com>
+ * @author   Toby Inkster <mail@tobyinkster.co.uk>
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+
+class ApiAction extends Action
+{
+    const READ_ONLY  = 1;
+    const READ_WRITE = 2;
+
+    var $format    = null;
+    var $user      = null;
+    var $auth_user = null;
+    var $page      = null;
+    var $count     = null;
+    var $max_id    = null;
+    var $since_id  = null;
+    var $since     = null;
+
+    var $access    = self::READ_ONLY;  // read (default) or read-write
+
+    /**
+     * Initialization.
+     *
+     * @param array $args Web and URL arguments
+     *
+     * @return boolean false if user doesn't exist
+     */
+
+    function prepare($args)
+    {
+        StatusNet::setApi(true); // reduce exception reports to aid in debugging
+        parent::prepare($args);
+
+        $this->format   = $this->arg('format');
+        $this->page     = (int)$this->arg('page', 1);
+        $this->count    = (int)$this->arg('count', 20);
+        $this->max_id   = (int)$this->arg('max_id', 0);
+        $this->since_id = (int)$this->arg('since_id', 0);
+        $this->since    = $this->arg('since');
+
+        return true;
+    }
+
+    /**
+     * Handle a request
+     *
+     * @param array $args Arguments from $_REQUEST
+     *
+     * @return void
+     */
+
+    function handle($args)
+    {
+        parent::handle($args);
+    }
+
+    /**
+     * Overrides XMLOutputter::element to write booleans as strings (true|false).
+     * See that method's documentation for more info.
+     *
+     * @param string $tag     Element type or tagname
+     * @param array  $attrs   Array of element attributes, as
+     *                        key-value pairs
+     * @param string $content string content of the element
+     *
+     * @return void
+     */
+    function element($tag, $attrs=null, $content=null)
+    {
+        if (is_bool($content)) {
+            $content = ($content ? 'true' : 'false');
+        }
+
+        return parent::element($tag, $attrs, $content);
+    }
+
+    function twitterUserArray($profile, $get_notice=false)
+    {
+        $twitter_user = array();
+
+        $twitter_user['id'] = intval($profile->id);
+        $twitter_user['name'] = $profile->getBestName();
+        $twitter_user['screen_name'] = $profile->nickname;
+        $twitter_user['location'] = ($profile->location) ? $profile->location : null;
+        $twitter_user['description'] = ($profile->bio) ? $profile->bio : null;
+
+        $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE);
+        $twitter_user['profile_image_url'] = ($avatar) ? $avatar->displayUrl() :
+            Avatar::defaultImage(AVATAR_STREAM_SIZE);
+
+        $twitter_user['url'] = ($profile->homepage) ? $profile->homepage : null;
+        $twitter_user['protected'] = false; # not supported by StatusNet yet
+        $twitter_user['followers_count'] = $profile->subscriberCount();
+
+        $design        = null;
+        $user          = $profile->getUser();
+
+        // Note: some profiles don't have an associated user
+
+        $defaultDesign = Design::siteDesign();
+
+        if (!empty($user)) {
+            $design = $user->getDesign();
+        }
+
+        if (empty($design)) {
+            $design = $defaultDesign;
+        }
+
+        $color = Design::toWebColor(empty($design->backgroundcolor) ? $defaultDesign->backgroundcolor : $design->backgroundcolor);
+        $twitter_user['profile_background_color'] = ($color == null) ? '' : '#'.$color->hexValue();
+        $color = Design::toWebColor(empty($design->textcolor) ? $defaultDesign->textcolor : $design->textcolor);
+        $twitter_user['profile_text_color'] = ($color == null) ? '' : '#'.$color->hexValue();
+        $color = Design::toWebColor(empty($design->linkcolor) ? $defaultDesign->linkcolor : $design->linkcolor);
+        $twitter_user['profile_link_color'] = ($color == null) ? '' : '#'.$color->hexValue();
+        $color = Design::toWebColor(empty($design->sidebarcolor) ? $defaultDesign->sidebarcolor : $design->sidebarcolor);
+        $twitter_user['profile_sidebar_fill_color'] = ($color == null) ? '' : '#'.$color->hexValue();
+        $twitter_user['profile_sidebar_border_color'] = '';
+
+        $twitter_user['friends_count'] = $profile->subscriptionCount();
+
+        $twitter_user['created_at'] = $this->dateTwitter($profile->created);
+
+        $twitter_user['favourites_count'] = $profile->faveCount(); // British spelling!
+
+        $timezone = 'UTC';
+
+        if (!empty($user) && $user->timezone) {
+            $timezone = $user->timezone;
+        }
+
+        $t = new DateTime;
+        $t->setTimezone(new DateTimeZone($timezone));
+
+        $twitter_user['utc_offset'] = $t->format('Z');
+        $twitter_user['time_zone'] = $timezone;
+
+        $twitter_user['profile_background_image_url']
+            = empty($design->backgroundimage)
+            ? '' : ($design->disposition & BACKGROUND_ON)
+            ? Design::url($design->backgroundimage) : '';
+
+        $twitter_user['profile_background_tile']
+            = empty($design->disposition)
+            ? '' : ($design->disposition & BACKGROUND_TILE) ? 'true' : 'false';
+
+        $twitter_user['statuses_count'] = $profile->noticeCount();
+
+        // Is the requesting user following this user?
+        $twitter_user['following'] = false;
+        $twitter_user['notifications'] = false;
+
+        if (isset($this->auth_user)) {
+
+            $twitter_user['following'] = $this->auth_user->isSubscribed($profile);
+
+            // Notifications on?
+            $sub = Subscription::pkeyGet(array('subscriber' =>
+                                               $this->auth_user->id,
+                                               'subscribed' => $profile->id));
+
+            if ($sub) {
+                $twitter_user['notifications'] = ($sub->jabber || $sub->sms);
+            }
+        }
+
+        if ($get_notice) {
+            $notice = $profile->getCurrentNotice();
+            if ($notice) {
+                # don't get user!
+                $twitter_user['status'] = $this->twitterStatusArray($notice, false);
+            }
+        }
+
+        return $twitter_user;
+    }
+
+    function twitterStatusArray($notice, $include_user=true)
+    {
+        $base = $this->twitterSimpleStatusArray($notice, $include_user);
+
+        if (!empty($notice->repeat_of)) {
+            $original = Notice::staticGet('id', $notice->repeat_of);
+            if (!empty($original)) {
+                $original_array = $this->twitterSimpleStatusArray($original, $include_user);
+                $base['retweeted_status'] = $original_array;
+            }
+        }
+
+        return $base;
+    }
+
+    function twitterSimpleStatusArray($notice, $include_user=true)
+    {
+        $profile = $notice->getProfile();
+
+        $twitter_status = array();
+        $twitter_status['text'] = $notice->content;
+        $twitter_status['truncated'] = false; # Not possible on StatusNet
+        $twitter_status['created_at'] = $this->dateTwitter($notice->created);
+        $twitter_status['in_reply_to_status_id'] = ($notice->reply_to) ?
+            intval($notice->reply_to) : null;
+        $twitter_status['source'] = $this->sourceLink($notice->source);
+        $twitter_status['id'] = intval($notice->id);
+
+        $replier_profile = null;
+
+        if ($notice->reply_to) {
+            $reply = Notice::staticGet(intval($notice->reply_to));
+            if ($reply) {
+                $replier_profile = $reply->getProfile();
+            }
+        }
+
+        $twitter_status['in_reply_to_user_id'] =
+            ($replier_profile) ? intval($replier_profile->id) : null;
+        $twitter_status['in_reply_to_screen_name'] =
+            ($replier_profile) ? $replier_profile->nickname : null;
+
+        if (isset($notice->lat) && isset($notice->lon)) {
+            // This is the format that GeoJSON expects stuff to be in
+            $twitter_status['geo'] = array('type' => 'Point',
+                                           'coordinates' => array((float) $notice->lat,
+                                                                  (float) $notice->lon));
+        } else {
+            $twitter_status['geo'] = null;
+        }
+
+        if (isset($this->auth_user)) {
+            $twitter_status['favorited'] = $this->auth_user->hasFave($notice);
+        } else {
+            $twitter_status['favorited'] = false;
+        }
+
+        // Enclosures
+        $attachments = $notice->attachments();
+
+        if (!empty($attachments)) {
+
+            $twitter_status['attachments'] = array();
+
+            foreach ($attachments as $attachment) {
+                $enclosure_o=$attachment->getEnclosure();
+                if ($enclosure_o) {
+                    $enclosure = array();
+                    $enclosure['url'] = $enclosure_o->url;
+                    $enclosure['mimetype'] = $enclosure_o->mimetype;
+                    $enclosure['size'] = $enclosure_o->size;
+                    $twitter_status['attachments'][] = $enclosure;
+                }
+            }
+        }
+
+        if ($include_user && $profile) {
+            # Don't get notice (recursive!)
+            $twitter_user = $this->twitterUserArray($profile, false);
+            $twitter_status['user'] = $twitter_user;
+        }
+
+        return $twitter_status;
+    }
+
+    function twitterGroupArray($group)
+    {
+        $twitter_group=array();
+        $twitter_group['id']=$group->id;
+        $twitter_group['url']=$group->permalink();
+        $twitter_group['nickname']=$group->nickname;
+        $twitter_group['fullname']=$group->fullname;
+        $twitter_group['homepage_url']=$group->homepage_url;
+        $twitter_group['original_logo']=$group->original_logo;
+        $twitter_group['homepage_logo']=$group->homepage_logo;
+        $twitter_group['stream_logo']=$group->stream_logo;
+        $twitter_group['mini_logo']=$group->mini_logo;
+        $twitter_group['homepage']=$group->homepage;
+        $twitter_group['description']=$group->description;
+        $twitter_group['location']=$group->location;
+        $twitter_group['created']=$this->dateTwitter($group->created);
+        $twitter_group['modified']=$this->dateTwitter($group->modified);
+        return $twitter_group;
+    }
+
+    function twitterRssGroupArray($group)
+    {
+        $entry = array();
+        $entry['content']=$group->description;
+        $entry['title']=$group->nickname;
+        $entry['link']=$group->permalink();
+        $entry['published']=common_date_iso8601($group->created);
+        $entry['updated']==common_date_iso8601($group->modified);
+        $taguribase = common_config('integration', 'groupuri');
+        $entry['id'] = "group:$groupuribase:$entry[link]";
+
+        $entry['description'] = $entry['content'];
+        $entry['pubDate'] = common_date_rfc2822($group->created);
+        $entry['guid'] = $entry['link'];
+
+        return $entry;
+    }
+
+    function twitterRssEntryArray($notice)
+    {
+        $profile = $notice->getProfile();
+        $entry = array();
+
+        // We trim() to avoid extraneous whitespace in the output
+
+        $entry['content'] = common_xml_safe_str(trim($notice->rendered));
+        $entry['title'] = $profile->nickname . ': ' . common_xml_safe_str(trim($notice->content));
+        $entry['link'] = common_local_url('shownotice', array('notice' => $notice->id));
+        $entry['published'] = common_date_iso8601($notice->created);
+
+        $taguribase = TagURI::base();
+        $entry['id'] = "tag:$taguribase:$entry[link]";
+
+        $entry['updated'] = $entry['published'];
+        $entry['author'] = $profile->getBestName();
+
+        // Enclosures
+        $attachments = $notice->attachments();
+        $enclosures = array();
+
+        foreach ($attachments as $attachment) {
+            $enclosure_o=$attachment->getEnclosure();
+            if ($enclosure_o) {
+                 $enclosure = array();
+                 $enclosure['url'] = $enclosure_o->url;
+                 $enclosure['mimetype'] = $enclosure_o->mimetype;
+                 $enclosure['size'] = $enclosure_o->size;
+                 $enclosures[] = $enclosure;
+            }
+        }
+
+        if (!empty($enclosures)) {
+            $entry['enclosures'] = $enclosures;
+        }
+
+        // Tags/Categories
+        $tag = new Notice_tag();
+        $tag->notice_id = $notice->id;
+        if ($tag->find()) {
+            $entry['tags']=array();
+            while ($tag->fetch()) {
+                $entry['tags'][]=$tag->tag;
+            }
+        }
+        $tag->free();
+
+        // RSS Item specific
+        $entry['description'] = $entry['content'];
+        $entry['pubDate'] = common_date_rfc2822($notice->created);
+        $entry['guid'] = $entry['link'];
+
+        if (isset($notice->lat) && isset($notice->lon)) {
+            // This is the format that GeoJSON expects stuff to be in.
+            // showGeoRSS() below uses it for XML output, so we reuse it
+            $entry['geo'] = array('type' => 'Point',
+                                  'coordinates' => array((float) $notice->lat,
+                                                         (float) $notice->lon));
+        } else {
+            $entry['geo'] = null;
+        }
+
+        return $entry;
+    }
+
+    function twitterRelationshipArray($source, $target)
+    {
+        $relationship = array();
+
+        $relationship['source'] =
+            $this->relationshipDetailsArray($source, $target);
+        $relationship['target'] =
+            $this->relationshipDetailsArray($target, $source);
+
+        return array('relationship' => $relationship);
+    }
+
+    function relationshipDetailsArray($source, $target)
+    {
+        $details = array();
+
+        $details['screen_name'] = $source->nickname;
+        $details['followed_by'] = $target->isSubscribed($source);
+        $details['following'] = $source->isSubscribed($target);
+
+        $notifications = false;
+
+        if ($source->isSubscribed($target)) {
+
+            $sub = Subscription::pkeyGet(array('subscriber' =>
+                $source->id, 'subscribed' => $target->id));
+
+            if (!empty($sub)) {
+                $notifications = ($sub->jabber || $sub->sms);
+            }
+        }
+
+        $details['notifications_enabled'] = $notifications;
+        $details['blocking'] = $source->hasBlocked($target);
+        $details['id'] = $source->id;
+
+        return $details;
+    }
+
+    function showTwitterXmlRelationship($relationship)
+    {
+        $this->elementStart('relationship');
+
+        foreach($relationship as $element => $value) {
+            if ($element == 'source' || $element == 'target') {
+                $this->elementStart($element);
+                $this->showXmlRelationshipDetails($value);
+                $this->elementEnd($element);
+            }
+        }
+
+        $this->elementEnd('relationship');
+    }
+
+    function showXmlRelationshipDetails($details)
+    {
+        foreach($details as $element => $value) {
+            $this->element($element, null, $value);
+        }
+    }
+
+    function showTwitterXmlStatus($twitter_status, $tag='status')
+    {
+        $this->elementStart($tag);
+        foreach($twitter_status as $element => $value) {
+            switch ($element) {
+            case 'user':
+                $this->showTwitterXmlUser($twitter_status['user']);
+                break;
+            case 'text':
+                $this->element($element, null, common_xml_safe_str($value));
+                break;
+            case 'attachments':
+                $this->showXmlAttachments($twitter_status['attachments']);
+                break;
+            case 'geo':
+                $this->showGeoRSS($value);
+                break;
+            case 'retweeted_status':
+                $this->showTwitterXmlStatus($value, 'retweeted_status');
+                break;
+            default:
+                $this->element($element, null, $value);
+            }
+        }
+        $this->elementEnd($tag);
+    }
+
+    function showTwitterXmlGroup($twitter_group)
+    {
+        $this->elementStart('group');
+        foreach($twitter_group as $element => $value) {
+            $this->element($element, null, $value);
+        }
+        $this->elementEnd('group');
+    }
+
+    function showTwitterXmlUser($twitter_user, $role='user')
+    {
+        $this->elementStart($role);
+        foreach($twitter_user as $element => $value) {
+            if ($element == 'status') {
+                $this->showTwitterXmlStatus($twitter_user['status']);
+            } else {
+                $this->element($element, null, $value);
+            }
+        }
+        $this->elementEnd($role);
+    }
+
+    function showXmlAttachments($attachments) {
+        if (!empty($attachments)) {
+            $this->elementStart('attachments', array('type' => 'array'));
+            foreach ($attachments as $attachment) {
+                $attrs = array();
+                $attrs['url'] = $attachment['url'];
+                $attrs['mimetype'] = $attachment['mimetype'];
+                $attrs['size'] = $attachment['size'];
+                $this->element('enclosure', $attrs, '');
+            }
+            $this->elementEnd('attachments');
+        }
+    }
+
+    function showGeoRSS($geo)
+    {
+        if (empty($geo)) {
+            // empty geo element
+            $this->element('geo');
+        } else {
+            $this->elementStart('geo', array('xmlns:georss' => 'http://www.georss.org/georss'));
+            $this->element('georss:point', null, $geo['coordinates'][0] . ' ' . $geo['coordinates'][1]);
+            $this->elementEnd('geo');
+        }
+    }
+
+    function showTwitterRssItem($entry)
+    {
+        $this->elementStart('item');
+        $this->element('title', null, $entry['title']);
+        $this->element('description', null, $entry['description']);
+        $this->element('pubDate', null, $entry['pubDate']);
+        $this->element('guid', null, $entry['guid']);
+        $this->element('link', null, $entry['link']);
+
+        # RSS only supports 1 enclosure per item
+        if(array_key_exists('enclosures', $entry) and !empty($entry['enclosures'])){
+            $enclosure = $entry['enclosures'][0];
+            $this->element('enclosure', array('url'=>$enclosure['url'],'type'=>$enclosure['mimetype'],'length'=>$enclosure['size']), null);
+        }
+
+        if(array_key_exists('tags', $entry)){
+            foreach($entry['tags'] as $tag){
+                $this->element('category', null,$tag);
+            }
+        }
+
+        $this->showGeoRSS($entry['geo']);
+        $this->elementEnd('item');
+    }
+
+    function showJsonObjects($objects)
+    {
+        print(json_encode($objects));
+    }
+
+    function showSingleXmlStatus($notice)
+    {
+        $this->initDocument('xml');
+        $twitter_status = $this->twitterStatusArray($notice);
+        $this->showTwitterXmlStatus($twitter_status);
+        $this->endDocument('xml');
+    }
+
+    function show_single_json_status($notice)
+    {
+        $this->initDocument('json');
+        $status = $this->twitterStatusArray($notice);
+        $this->showJsonObjects($status);
+        $this->endDocument('json');
+    }
+
+    function showXmlTimeline($notice)
+    {
+
+        $this->initDocument('xml');
+        $this->elementStart('statuses', array('type' => 'array'));
+
+        if (is_array($notice)) {
+            foreach ($notice as $n) {
+                $twitter_status = $this->twitterStatusArray($n);
+                $this->showTwitterXmlStatus($twitter_status);
+            }
+        } else {
+            while ($notice->fetch()) {
+                $twitter_status = $this->twitterStatusArray($notice);
+                $this->showTwitterXmlStatus($twitter_status);
+            }
+        }
+
+        $this->elementEnd('statuses');
+        $this->endDocument('xml');
+    }
+
+    function showRssTimeline($notice, $title, $link, $subtitle, $suplink=null, $logo=null)
+    {
+
+        $this->initDocument('rss');
+
+        $this->element('title', null, $title);
+        $this->element('link', null, $link);
+        if (!is_null($suplink)) {
+            // For FriendFeed's SUP protocol
+            $this->element('link', array('xmlns' => 'http://www.w3.org/2005/Atom',
+                                         'rel' => 'http://api.friendfeed.com/2008/03#sup',
+                                         'href' => $suplink,
+                                         'type' => 'application/json'));
+        }
+
+        if (!is_null($logo)) {
+            $this->elementStart('image');
+            $this->element('link', null, $link);
+            $this->element('title', null, $title);
+            $this->element('url', null, $logo);
+            $this->elementEnd('image');
+        }
+
+        $this->element('description', null, $subtitle);
+        $this->element('language', null, 'en-us');
+        $this->element('ttl', null, '40');
+
+        if (is_array($notice)) {
+            foreach ($notice as $n) {
+                $entry = $this->twitterRssEntryArray($n);
+                $this->showTwitterRssItem($entry);
+            }
+        } else {
+            while ($notice->fetch()) {
+                $entry = $this->twitterRssEntryArray($notice);
+                $this->showTwitterRssItem($entry);
+            }
+        }
+
+        $this->endTwitterRss();
+    }
+
+    function showAtomTimeline($notice, $title, $id, $link, $subtitle=null, $suplink=null, $selfuri=null, $logo=null)
+    {
+
+        $this->initDocument('atom');
+
+        $this->element('title', null, $title);
+        $this->element('id', null, $id);
+        $this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null);
+
+        if (!is_null($logo)) {
+            $this->element('logo',null,$logo);
+        }
+
+        if (!is_null($suplink)) {
+            # For FriendFeed's SUP protocol
+            $this->element('link', array('rel' => 'http://api.friendfeed.com/2008/03#sup',
+                                         'href' => $suplink,
+                                         'type' => 'application/json'));
+        }
+
+        if (!is_null($selfuri)) {
+            $this->element('link', array('href' => $selfuri,
+                'rel' => 'self', 'type' => 'application/atom+xml'), null);
+        }
+
+        $this->element('updated', null, common_date_iso8601('now'));
+        $this->element('subtitle', null, $subtitle);
+
+        if (is_array($notice)) {
+            foreach ($notice as $n) {
+                $this->raw($n->asAtomEntry());
+            }
+        } else {
+            while ($notice->fetch()) {
+                $this->raw($notice->asAtomEntry());
+            }
+        }
+
+        $this->endDocument('atom');
+
+    }
+
+    function showRssGroups($group, $title, $link, $subtitle)
+    {
+
+        $this->initDocument('rss');
+
+        $this->element('title', null, $title);
+        $this->element('link', null, $link);
+        $this->element('description', null, $subtitle);
+        $this->element('language', null, 'en-us');
+        $this->element('ttl', null, '40');
+
+        if (is_array($group)) {
+            foreach ($group as $g) {
+                $twitter_group = $this->twitterRssGroupArray($g);
+                $this->showTwitterRssItem($twitter_group);
+            }
+        } else {
+            while ($group->fetch()) {
+                $twitter_group = $this->twitterRssGroupArray($group);
+                $this->showTwitterRssItem($twitter_group);
+            }
+        }
+
+        $this->endTwitterRss();
+    }
+
+    function showTwitterAtomEntry($entry)
+    {
+        $this->elementStart('entry');
+        $this->element('title', null, $entry['title']);
+        $this->element('content', array('type' => 'html'), $entry['content']);
+        $this->element('id', null, $entry['id']);
+        $this->element('published', null, $entry['published']);
+        $this->element('updated', null, $entry['updated']);
+        $this->element('link', array('type' => 'text/html',
+                                     'href' => $entry['link'],
+                                     'rel' => 'alternate'));
+        $this->element('link', array('type' => $entry['avatar-type'],
+                                     'href' => $entry['avatar'],
+                                     'rel' => 'image'));
+        $this->elementStart('author');
+
+        $this->element('name', null, $entry['author-name']);
+        $this->element('uri', null, $entry['author-uri']);
+
+        $this->elementEnd('author');
+        $this->elementEnd('entry');
+    }
+
+    function showXmlDirectMessage($dm)
+    {
+        $this->elementStart('direct_message');
+        foreach($dm as $element => $value) {
+            switch ($element) {
+            case 'sender':
+            case 'recipient':
+                $this->showTwitterXmlUser($value, $element);
+                break;
+            case 'text':
+                $this->element($element, null, common_xml_safe_str($value));
+                break;
+            default:
+                $this->element($element, null, $value);
+                break;
+            }
+        }
+        $this->elementEnd('direct_message');
+    }
+
+    function directMessageArray($message)
+    {
+        $dmsg = array();
+
+        $from_profile = $message->getFrom();
+        $to_profile = $message->getTo();
+
+        $dmsg['id'] = $message->id;
+        $dmsg['sender_id'] = $message->from_profile;
+        $dmsg['text'] = trim($message->content);
+        $dmsg['recipient_id'] = $message->to_profile;
+        $dmsg['created_at'] = $this->dateTwitter($message->created);
+        $dmsg['sender_screen_name'] = $from_profile->nickname;
+        $dmsg['recipient_screen_name'] = $to_profile->nickname;
+        $dmsg['sender'] = $this->twitterUserArray($from_profile, false);
+        $dmsg['recipient'] = $this->twitterUserArray($to_profile, false);
+
+        return $dmsg;
+    }
+
+    function rssDirectMessageArray($message)
+    {
+        $entry = array();
+
+        $from = $message->getFrom();
+
+        $entry['title'] = sprintf('Message from %1$s to %2$s',
+            $from->nickname, $message->getTo()->nickname);
+
+        $entry['content'] = common_xml_safe_str($message->rendered);
+        $entry['link'] = common_local_url('showmessage', array('message' => $message->id));
+        $entry['published'] = common_date_iso8601($message->created);
+
+        $taguribase = TagURI::base();
+
+        $entry['id'] = "tag:$taguribase:$entry[link]";
+        $entry['updated'] = $entry['published'];
+
+        $entry['author-name'] = $from->getBestName();
+        $entry['author-uri'] = $from->homepage;
+
+        $avatar = $from->getAvatar(AVATAR_STREAM_SIZE);
+
+        $entry['avatar']      = (!empty($avatar)) ? $avatar->url : Avatar::defaultImage(AVATAR_STREAM_SIZE);
+        $entry['avatar-type'] = (!empty($avatar)) ? $avatar->mediatype : 'image/png';
+
+        // RSS item specific
+
+        $entry['description'] = $entry['content'];
+        $entry['pubDate'] = common_date_rfc2822($message->created);
+        $entry['guid'] = $entry['link'];
+
+        return $entry;
+    }
+
+    function showSingleXmlDirectMessage($message)
+    {
+        $this->initDocument('xml');
+        $dmsg = $this->directMessageArray($message);
+        $this->showXmlDirectMessage($dmsg);
+        $this->endDocument('xml');
+    }
+
+    function showSingleJsonDirectMessage($message)
+    {
+        $this->initDocument('json');
+        $dmsg = $this->directMessageArray($message);
+        $this->showJsonObjects($dmsg);
+        $this->endDocument('json');
+    }
+
+    function showAtomGroups($group, $title, $id, $link, $subtitle=null, $selfuri=null)
+    {
+
+        $this->initDocument('atom');
+
+        $this->element('title', null, $title);
+        $this->element('id', null, $id);
+        $this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null);
+
+        if (!is_null($selfuri)) {
+            $this->element('link', array('href' => $selfuri,
+                'rel' => 'self', 'type' => 'application/atom+xml'), null);
+        }
+
+        $this->element('updated', null, common_date_iso8601('now'));
+        $this->element('subtitle', null, $subtitle);
+
+        if (is_array($group)) {
+            foreach ($group as $g) {
+                $this->raw($g->asAtomEntry());
+            }
+        } else {
+            while ($group->fetch()) {
+                $this->raw($group->asAtomEntry());
+            }
+        }
+
+        $this->endDocument('atom');
+
+    }
+
+    function showJsonTimeline($notice)
+    {
+
+        $this->initDocument('json');
+
+        $statuses = array();
+
+        if (is_array($notice)) {
+            foreach ($notice as $n) {
+                $twitter_status = $this->twitterStatusArray($n);
+                array_push($statuses, $twitter_status);
+            }
+        } else {
+            while ($notice->fetch()) {
+                $twitter_status = $this->twitterStatusArray($notice);
+                array_push($statuses, $twitter_status);
+            }
+        }
+
+        $this->showJsonObjects($statuses);
+
+        $this->endDocument('json');
+    }
+
+    function showJsonGroups($group)
+    {
+
+        $this->initDocument('json');
+
+        $groups = array();
+
+        if (is_array($group)) {
+            foreach ($group as $g) {
+                $twitter_group = $this->twitterGroupArray($g);
+                array_push($groups, $twitter_group);
+            }
+        } else {
+            while ($group->fetch()) {
+                $twitter_group = $this->twitterGroupArray($group);
+                array_push($groups, $twitter_group);
+            }
+        }
+
+        $this->showJsonObjects($groups);
+
+        $this->endDocument('json');
+    }
+
+    function showXmlGroups($group)
+    {
+
+        $this->initDocument('xml');
+        $this->elementStart('groups', array('type' => 'array'));
+
+        if (is_array($group)) {
+            foreach ($group as $g) {
+                $twitter_group = $this->twitterGroupArray($g);
+                $this->showTwitterXmlGroup($twitter_group);
+            }
+        } else {
+            while ($group->fetch()) {
+                $twitter_group = $this->twitterGroupArray($group);
+                $this->showTwitterXmlGroup($twitter_group);
+            }
+        }
+
+        $this->elementEnd('groups');
+        $this->endDocument('xml');
+    }
+
+    function showTwitterXmlUsers($user)
+    {
+
+        $this->initDocument('xml');
+        $this->elementStart('users', array('type' => 'array'));
+
+        if (is_array($user)) {
+            foreach ($user as $u) {
+                $twitter_user = $this->twitterUserArray($u);
+                $this->showTwitterXmlUser($twitter_user);
+            }
+        } else {
+            while ($user->fetch()) {
+                $twitter_user = $this->twitterUserArray($user);
+                $this->showTwitterXmlUser($twitter_user);
+            }
+        }
+
+        $this->elementEnd('users');
+        $this->endDocument('xml');
+    }
+
+    function showJsonUsers($user)
+    {
+
+        $this->initDocument('json');
+
+        $users = array();
+
+        if (is_array($user)) {
+            foreach ($user as $u) {
+                $twitter_user = $this->twitterUserArray($u);
+                array_push($users, $twitter_user);
+            }
+        } else {
+            while ($user->fetch()) {
+                $twitter_user = $this->twitterUserArray($user);
+                array_push($users, $twitter_user);
+            }
+        }
+
+        $this->showJsonObjects($users);
+
+        $this->endDocument('json');
+    }
+
+    function showSingleJsonGroup($group)
+    {
+        $this->initDocument('json');
+        $twitter_group = $this->twitterGroupArray($group);
+        $this->showJsonObjects($twitter_group);
+        $this->endDocument('json');
+    }
+
+    function showSingleXmlGroup($group)
+    {
+        $this->initDocument('xml');
+        $twitter_group = $this->twitterGroupArray($group);
+        $this->showTwitterXmlGroup($twitter_group);
+        $this->endDocument('xml');
+    }
+
+    function dateTwitter($dt)
+    {
+        $dateStr = date('d F Y H:i:s', strtotime($dt));
+        $d = new DateTime($dateStr, new DateTimeZone('UTC'));
+        $d->setTimezone(new DateTimeZone(common_timezone()));
+        return $d->format('D M d H:i:s O Y');
+    }
+
+    function initDocument($type='xml')
+    {
+        switch ($type) {
+        case 'xml':
+            header('Content-Type: application/xml; charset=utf-8');
+            $this->startXML();
+            break;
+        case 'json':
+            header('Content-Type: application/json; charset=utf-8');
+
+            // Check for JSONP callback
+            $callback = $this->arg('callback');
+            if ($callback) {
+                print $callback . '(';
+            }
+            break;
+        case 'rss':
+            header("Content-Type: application/rss+xml; charset=utf-8");
+            $this->initTwitterRss();
+            break;
+        case 'atom':
+            header('Content-Type: application/atom+xml; charset=utf-8');
+            $this->initTwitterAtom();
+            break;
+        default:
+            $this->clientError(_('Not a supported data format.'));
+            break;
+        }
+
+        return;
+    }
+
+    function endDocument($type='xml')
+    {
+        switch ($type) {
+        case 'xml':
+            $this->endXML();
+            break;
+        case 'json':
+
+            // Check for JSONP callback
+            $callback = $this->arg('callback');
+            if ($callback) {
+                print ')';
+            }
+            break;
+        case 'rss':
+            $this->endTwitterRss();
+            break;
+        case 'atom':
+            $this->endTwitterRss();
+            break;
+        default:
+            $this->clientError(_('Not a supported data format.'));
+            break;
+        }
+        return;
+    }
+
+    function clientError($msg, $code = 400, $format = 'xml')
+    {
+        $action = $this->trimmed('action');
+
+        common_debug("User error '$code' on '$action': $msg", __FILE__);
+
+        if (!array_key_exists($code, ClientErrorAction::$status)) {
+            $code = 400;
+        }
+
+        $status_string = ClientErrorAction::$status[$code];
+
+        header('HTTP/1.1 '.$code.' '.$status_string);
+
+        if ($format == 'xml') {
+            $this->initDocument('xml');
+            $this->elementStart('hash');
+            $this->element('error', null, $msg);
+            $this->element('request', null, $_SERVER['REQUEST_URI']);
+            $this->elementEnd('hash');
+            $this->endDocument('xml');
+        } elseif ($format == 'json'){
+            $this->initDocument('json');
+            $error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']);
+            print(json_encode($error_array));
+            $this->endDocument('json');
+        } else {
+
+            // If user didn't request a useful format, throw a regular client error
+            throw new ClientException($msg, $code);
+        }
+    }
+
+    function serverError($msg, $code = 500, $content_type = 'xml')
+    {
+        $action = $this->trimmed('action');
+
+        common_debug("Server error '$code' on '$action': $msg", __FILE__);
+
+        if (!array_key_exists($code, ServerErrorAction::$status)) {
+            $code = 400;
+        }
+
+        $status_string = ServerErrorAction::$status[$code];
+
+        header('HTTP/1.1 '.$code.' '.$status_string);
+
+        if ($content_type == 'xml') {
+            $this->initDocument('xml');
+            $this->elementStart('hash');
+            $this->element('error', null, $msg);
+            $this->element('request', null, $_SERVER['REQUEST_URI']);
+            $this->elementEnd('hash');
+            $this->endDocument('xml');
+        } else {
+            $this->initDocument('json');
+            $error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']);
+            print(json_encode($error_array));
+            $this->endDocument('json');
+        }
+    }
+
+    function initTwitterRss()
+    {
+        $this->startXML();
+        $this->elementStart('rss', array('version' => '2.0', 'xmlns:atom'=>'http://www.w3.org/2005/Atom'));
+        $this->elementStart('channel');
+        Event::handle('StartApiRss', array($this));
+    }
+
+    function endTwitterRss()
+    {
+        $this->elementEnd('channel');
+        $this->elementEnd('rss');
+        $this->endXML();
+    }
+
+    function initTwitterAtom()
+    {
+        $this->startXML();
+        // FIXME: don't hardcode the language here!
+        $this->elementStart('feed', array('xmlns' => 'http://www.w3.org/2005/Atom',
+                                          'xml:lang' => 'en-US',
+                                          'xmlns:thr' => 'http://purl.org/syndication/thread/1.0'));
+    }
+
+    function endTwitterAtom()
+    {
+        $this->elementEnd('feed');
+        $this->endXML();
+    }
+
+    function showProfile($profile, $content_type='xml', $notice=null, $includeStatuses=true)
+    {
+        $profile_array = $this->twitterUserArray($profile, $includeStatuses);
+        switch ($content_type) {
+        case 'xml':
+            $this->showTwitterXmlUser($profile_array);
+            break;
+        case 'json':
+            $this->showJsonObjects($profile_array);
+            break;
+        default:
+            $this->clientError(_('Not a supported data format.'));
+            return;
+        }
+        return;
+    }
+
+    function getTargetUser($id)
+    {
+        if (empty($id)) {
+
+            // Twitter supports these other ways of passing the user ID
+            if (is_numeric($this->arg('id'))) {
+                return User::staticGet($this->arg('id'));
+            } else if ($this->arg('id')) {
+                $nickname = common_canonical_nickname($this->arg('id'));
+                return User::staticGet('nickname', $nickname);
+            } else if ($this->arg('user_id')) {
+                // This is to ensure that a non-numeric user_id still
+                // overrides screen_name even if it doesn't get used
+                if (is_numeric($this->arg('user_id'))) {
+                    return User::staticGet('id', $this->arg('user_id'));
+                }
+            } else if ($this->arg('screen_name')) {
+                $nickname = common_canonical_nickname($this->arg('screen_name'));
+                return User::staticGet('nickname', $nickname);
+            } else {
+                // Fall back to trying the currently authenticated user
+                return $this->auth_user;
+            }
+
+        } else if (is_numeric($id)) {
+            return User::staticGet($id);
+        } else {
+            $nickname = common_canonical_nickname($id);
+            return User::staticGet('nickname', $nickname);
+        }
+    }
+
+    function getTargetGroup($id)
+    {
+        if (empty($id)) {
+            if (is_numeric($this->arg('id'))) {
+                return User_group::staticGet($this->arg('id'));
+            } else if ($this->arg('id')) {
+                $nickname = common_canonical_nickname($this->arg('id'));
+                $local = Local_group::staticGet('nickname', $nickname);
+                if (empty($local)) {
+                    return null;
+                } else {
+                    return User_group::staticGet('id', $local->id);
+                }
+            } else if ($this->arg('group_id')) {
+                // This is to ensure that a non-numeric user_id still
+                // overrides screen_name even if it doesn't get used
+                if (is_numeric($this->arg('group_id'))) {
+                    return User_group::staticGet('id', $this->arg('group_id'));
+                }
+            } else if ($this->arg('group_name')) {
+                $nickname = common_canonical_nickname($this->arg('group_name'));
+                $local = Local_group::staticGet('nickname', $nickname);
+                if (empty($local)) {
+                    return null;
+                } else {
+                    return User_group::staticGet('id', $local->id);
+                }
+            }
+
+        } else if (is_numeric($id)) {
+            return User_group::staticGet($id);
+        } else {
+            $nickname = common_canonical_nickname($id);
+            $local = Local_group::staticGet('nickname', $nickname);
+            if (empty($local)) {
+                return null;
+            } else {
+                return User_group::staticGet('id', $local->group_id);
+            }
+        }
+    }
+
+    function sourceLink($source)
+    {
+        $source_name = _($source);
+        switch ($source) {
+        case 'web':
+        case 'xmpp':
+        case 'mail':
+        case 'omb':
+        case 'api':
+            break;
+        default:
+
+            $name = null;
+            $url  = null;
+
+            $ns = Notice_source::staticGet($source);
+
+            if ($ns) {
+                $name = $ns->name;
+                $url  = $ns->url;
+            } else {
+                $app = Oauth_application::staticGet('name', $source);
+                if ($app) {
+                    $name = $app->name;
+                    $url  = $app->source_url;
+                }
+            }
+
+            if (!empty($name) && !empty($url)) {
+                $source_name = '<a href="' . $url . '">' . $name . '</a>';
+            }
+
+            break;
+        }
+        return $source_name;
+    }
+
+    /**
+     * Returns query argument or default value if not found. Certain
+     * parameters used throughout the API are lightly scrubbed and
+     * bounds checked.  This overrides Action::arg().
+     *
+     * @param string $key requested argument
+     * @param string $def default value to return if $key is not provided
+     *
+     * @return var $var
+     */
+    function arg($key, $def=null)
+    {
+
+        // XXX: Do even more input validation/scrubbing?
+
+        if (array_key_exists($key, $this->args)) {
+            switch($key) {
+            case 'page':
+                $page = (int)$this->args['page'];
+                return ($page < 1) ? 1 : $page;
+            case 'count':
+                $count = (int)$this->args['count'];
+                if ($count < 1) {
+                    return 20;
+                } elseif ($count > 200) {
+                    return 200;
+                } else {
+                    return $count;
+                }
+            case 'since_id':
+                $since_id = (int)$this->args['since_id'];
+                return ($since_id < 1) ? 0 : $since_id;
+            case 'max_id':
+                $max_id = (int)$this->args['max_id'];
+                return ($max_id < 1) ? 0 : $max_id;
+            case 'since':
+                return strtotime($this->args['since']);
+            default:
+                return parent::arg($key, $def);
+            }
+        } else {
+            return $def;
+        }
+    }
+
+    function getSelfUri($action, $aargs)
+    {
+        parse_str($_SERVER['QUERY_STRING'], $params);
+        $pstring = '';
+        if (!empty($params)) {
+            unset($params['p']);
+            $pstring = http_build_query($params);
+        }
+
+        $uri = common_local_url($action, $aargs);
+
+        if (!empty($pstring)) {
+            $uri .= '?' . $pstring;
+        }
+
+        return $uri;
+    }
+
+}
index 25e2196cf2c4bc0e004d5eb930146961c35112ab..5090871cfe18a931600558e06c202b68e437d08b 100644 (file)
@@ -38,7 +38,6 @@ if (!defined('STATUSNET')) {
     exit(1);
 }
 
-require_once INSTALLDIR . '/lib/api.php';
 require_once INSTALLDIR . '/lib/apioauth.php';
 
 /**
index d2bf2a416f3bb26be64e0857b2ae43970239d7cc..3c3556cb95c789c59c951440cf73c6b12ad64801 100644 (file)
@@ -64,6 +64,11 @@ class AtomNoticeFeed extends Atom10Feed
             'http://activitystrea.ms/spec/1.0/'
         );
 
+        $this->addNamespace(
+            'media',
+            'http://purl.org/syndication/atommedia'
+        );
+
         $this->addNamespace(
             'poco',
             'http://portablecontacts.net/spec/1.0'
index 68723955ecc93289929d6d81330932a69f259c50..2dbe3b3c531ba481060854e7a7ad0f0d66b8ac65 100644 (file)
@@ -22,7 +22,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
 //exit with 200 response, if this is checking fancy from the installer
 if (isset($_REQUEST['p']) && $_REQUEST['p'] == 'check-fancy') {  exit; }
 
-define('STATUSNET_VERSION', '0.9.0beta5');
+define('STATUSNET_VERSION', '0.9.0beta6');
 define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility
 
 define('STATUSNET_CODENAME', 'Stand');
index 70e00ea75080db91bc32f6f0c90d7fabf5f09de9..b06b9615762072721ed31d8b30f08248a1b6d1af 100644 (file)
@@ -278,7 +278,6 @@ $default =
                                  'TightUrl' => array('shortenerName' => '2tu.us', 'freeService' => true,'serviceUrl'=>'http://2tu.us/?save=y&url=%1$s'),
                                  'Geonames' => null,
                                  'Mapstraction' => null,
-                                 'Linkback' => null,
                                  'WikiHashtags' => null,
                                  'RSSCloud' => null,
                                  'OpenID' => null),
index dc183fb36a415f901ca22c219b22a8b1d17b09a7..d2be7a92c72488d846374bb27413f103490fd34d 100644 (file)
@@ -62,24 +62,12 @@ class DistribQueueHandler
     {
         // XXX: do we need to change this for remote users?
 
-        try {
-            $notice->saveTags();
-        } catch (Exception $e) {
-            $this->logit($notice, $e);
-        }
-
         try {
             $notice->addToInboxes();
         } catch (Exception $e) {
             $this->logit($notice, $e);
         }
 
-        try {
-            $notice->saveUrls();
-        } catch (Exception $e) {
-            $this->logit($notice, $e);
-        }
-
         try {
             Event::handle('EndNoticeSave', array($notice));
             // Enqueue for other handlers
index aefb553aac928b311ace9b44aa65f324adab002c..aa8bc20e24511c5078b55c6ffc63c9e10bef2c9a 100644 (file)
@@ -100,7 +100,7 @@ class JoinForm extends Form
     function action()
     {
         return common_local_url('joingroup',
-                                array('nickname' => $this->group->nickname));
+                                array('id' => $this->group->id));
     }
 
     /**
index e63d96ee8080148a183d855dce49c84344d86a39..5469b5704c0afcad57095b5b9bf52f38a786e6d6 100644 (file)
@@ -100,7 +100,7 @@ class LeaveForm extends Form
     function action()
     {
         return common_local_url('leavegroup',
-                                array('nickname' => $this->group->nickname));
+                                array('id' => $this->group->id));
     }
 
     /**
index e8a00aef307bfb4aa7e92fb945a9c963f7366e9e..6ce93229b249abe5d31ea5d839b60e3f87e85951 100644 (file)
@@ -39,7 +39,11 @@ class ProfileQueueHandler extends QueueHandler
 
         if (Event::handle('StartBroadcastProfile', array($profile))) {
             require_once(INSTALLDIR.'/lib/omb.php');
-            omb_broadcast_profile($profile);
+            try {
+                omb_broadcast_profile($profile);
+            } catch (Exception $e) {
+                common_log(LOG_ERR, "Failed sending OMB profiles: " . $e->getMessage());
+            }
         }
         Event::handle('EndBroadcastProfile', array($profile));
         return true;
index 987d0152e462e266a8ddc40d169e66882a990c79..abbce041dbfe5ac5ac504491fa1c4e5b128708b0 100644 (file)
@@ -247,6 +247,9 @@ class Router
                 $m->connect('group/:nickname/'.$v,
                             array('action' => $v.'group'),
                             array('nickname' => '[a-zA-Z0-9]+'));
+                $m->connect('group/:id/id/'.$v,
+                            array('action' => $v.'group'),
+                            array('id' => '[0-9]+'));
             }
 
             foreach (array('members', 'logo', 'rss', 'designsettings') as $n) {
@@ -668,7 +671,7 @@ class Router
 
                 foreach (array('subscriptions', 'subscribers',
                                'all', 'foaf', 'xrds',
-                               'replies', 'microsummary') as $a) {
+                               'replies', 'microsummary', 'hcard') as $a) {
                     $m->connect($a,
                                 array('action' => $a,
                                       'nickname' => $nickname));
@@ -734,7 +737,7 @@ class Router
 
                 foreach (array('subscriptions', 'subscribers',
                                'nudge', 'all', 'foaf', 'xrds',
-                               'replies', 'inbox', 'outbox', 'microsummary') as $a) {
+                               'replies', 'inbox', 'outbox', 'microsummary', 'hcard') as $a) {
                     $m->connect(':nickname/'.$a,
                                 array('action' => $a),
                                 array('nickname' => '[a-zA-Z0-9]{1,64}'));
index 094cd8772de33fe2a4f2f89866c2569afca1d387..1231f4c8d1cd3dd9ef39973c2f4c65e28575a189 100644 (file)
@@ -134,7 +134,7 @@ function common_check_user($nickname, $password)
     $authenticatedUser = false;
 
     if (Event::handle('StartCheckPassword', array($nickname, $password, &$authenticatedUser))) {
-        $user = User::staticGet('nickname', $nickname);
+        $user = User::staticGet('nickname', common_canonical_nickname($nickname));
         if (!empty($user)) {
             if (!empty($password)) { // never allow login with blank password
                 if (0 == strcmp(common_munge_password($password, $user->id),
@@ -1599,6 +1599,7 @@ function common_database_tablename($tablename)
  */
 function common_shorten_url($long_url)
 {
+    $long_url = trim($long_url);
     $user = common_current_user();
     if (empty($user)) {
         // common current user does not find a user when called from the XMPP daemon
@@ -1613,7 +1614,7 @@ function common_shorten_url($long_url)
         return $long_url;
     }else{
         //URL was shortened, so return the result
-        return $shortenedUrl;
+        return trim($shortenedUrl);
     }
 }
 
@@ -1690,7 +1691,8 @@ function common_url_to_nickname($url)
             # Strip starting, ending slashes
             $path = preg_replace('@/$@', '', $parts['path']);
             $path = preg_replace('@^/@', '', $path);
-            if (strpos($path, '/') === false) {
+            $path = basename($path);
+            if ($path) {
                 return common_nicknamize($path);
             }
         }
index 51236001aa3fb9aca78ed0a65de62a4a299c7622..d52e6006ac8dbb4bc33bd203af9d59521782826f 100644 (file)
@@ -72,8 +72,10 @@ class BlogspamNetPlugin extends Plugin
         common_debug("Blogspamnet args = " . print_r($args, TRUE));
         $requestBody = xmlrpc_encode_request('testComment', array($args));
 
-        $request = HTTPClient::start();
-        $httpResponse = $request->post($this->baseUrl, array('Content-Type: text/xml'), $requestBody);
+        $request = new HTTPClient($this->baseUrl, HTTPClient::METHOD_POST);
+        $request->setHeader('Content-Type', 'text/xml');
+        $request->setBody($requestBody);
+        $httpResponse = $request->send();
 
         $response = xmlrpc_decode($httpResponse->getBody());
         if (xmlrpc_is_fault($response)) {
@@ -118,7 +120,7 @@ class BlogspamNetPlugin extends Plugin
         $args['site'] = common_root_url();
         $args['version'] = $this->userAgent();
 
-        $args['options'] = "max-size=140,min-size=0,min-words=0,exclude=bayasian";
+        $args['options'] = "max-size=" . common_config('site','textlimit') . ",min-size=0,min-words=0,exclude=bayasian";
 
         return $args;
     }
index a33dfc7360a82e1ffd40be0df97019e402f46b65..b809c1b8e2e11d086af84e63eefd247966ce073b 100644 (file)
@@ -142,8 +142,6 @@ class MapAction extends OwnerDesignAction
         // of refactoring from within a plugin, so I'm just abusing
         // the ApiAction method. Don't do this unless you're me!
 
-        require_once(INSTALLDIR.'/lib/api.php');
-
         $act = new ApiAction('/dev/null');
 
         $arr = $act->twitterStatusArray($notice, true);
index e7ffd6cc2c765647edafdefa4f9a513f449f42a2..543944454952cf6e64b07fe6ff6487bb5c870bdc 100644 (file)
@@ -43,8 +43,8 @@ class OStatusPlugin extends Plugin
         // Discovery actions
         $m->connect('.well-known/host-meta',
                     array('action' => 'hostmeta'));
-        $m->connect('main/webfinger',
-                    array('action' => 'webfinger'));
+        $m->connect('main/xrd',
+                    array('action' => 'xrd'));
         $m->connect('main/ostatus',
                     array('action' => 'ostatusinit'));
         $m->connect('main/ostatus?nickname=:nickname',
@@ -102,6 +102,20 @@ class OStatusPlugin extends Plugin
         return true;
     }
 
+    /**
+     * Add a link header for LRDD Discovery
+     */
+    function onStartShowHTML($action)
+    {
+        if ($action instanceof ShowstreamAction) {
+            $acct = 'acct:'. $action->profile->nickname .'@'. common_config('site', 'server');
+            $url = common_local_url('xrd');
+            $url.= '?uri='. $acct;
+            
+            header('Link: <'.$url.'>; rel="'. Discovery::LRDD_REL.'"; type="application/xrd+xml"');
+        }
+    }
+    
     /**
      * Set up a PuSH hub link to our internal link for canonical timeline
      * Atom feeds for users and groups.
@@ -210,7 +224,7 @@ class OStatusPlugin extends Plugin
      *
      */
 
-    function onStartFindMentions($sender, $text, &$mentions)
+    function onEndFindMentions($sender, $text, &$mentions)
     {
         preg_match_all('/(?:^|\s+)@((?:\w+\.)*\w+@(?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+)/',
                        $text,
@@ -233,11 +247,21 @@ class OStatusPlugin extends Plugin
 
                 $this->log(LOG_INFO, "Ostatus_profile found for address '$webfinger'");
 
+                if ($oprofile->isGroup()) {
+                    continue;
+                }
                 $profile = $oprofile->localProfile();
 
+                $pos = $wmatch[1];
+                foreach ($mentions as $i => $other) {
+                    // If we share a common prefix with a local user, override it!
+                    if ($other['position'] == $pos) {
+                        unset($mentions[$i]);
+                    }
+                }
                 $mentions[] = array('mentioned' => array($profile),
                                     'text' => $wmatch[0],
-                                    'position' => $wmatch[1],
+                                    'position' => $pos,
                                     'url' => $profile->profileurl);
             }
         }
@@ -634,7 +658,7 @@ class OStatusPlugin extends Plugin
 
     function onStartUserGroupHomeUrl($group, &$url)
     {
-        return $this->onStartUserGroupPermalink($group, &$url);
+        return $this->onStartUserGroupPermalink($group, $url);
     }
 
     function onStartUserGroupPermalink($group, &$url)
index 850b8a0fe89d47bab373327abab797db13dbc5b9..3d00b98ae0d359687380fbdbe5cf4c27e1466244 100644 (file)
@@ -31,12 +31,18 @@ class HostMetaAction extends Action
     {
         parent::handle();
 
-        $w = new Webfinger();
-
-
         $domain = common_config('site', 'server');
-        $url = common_local_url('webfinger');
+        $url = common_local_url('xrd');
         $url.= '?uri={uri}';
-        print $w->getHostMeta($domain, $url);
+
+        $xrd = new XRD();
+        
+        $xrd = new XRD();
+        $xrd->host = $domain;
+        $xrd->links[] = array('rel' => Discovery::LRDD_REL,
+                              'template' => $url,
+                              'title' => array('Resource Descriptor'));
+
+        print $xrd->toXML();
     }
 }
index 3f2f6368f6038b94092e8fc0090fd8cfa4c5e18f..8ba8dcdcc7514ae18dd84fcd02f9f9e51fce7260 100644 (file)
@@ -131,9 +131,9 @@ class OStatusInitAction extends Action
 
     function connectWebfinger($acct)
     {
-        $w = new Webfinger;
+        $disco = new Discovery;
 
-        $result = $w->lookup($acct);
+        $result = $disco->lookup($acct);
         if (!$result) {
             $this->clientError(_m("Couldn't look up OStatus account profile."));
         }
@@ -144,7 +144,7 @@ class OStatusInitAction extends Action
                 $user = User::staticGet('nickname', $this->nickname);
                 $target_profile = common_local_url('userbyid', array('id' => $user->id));
 
-                $url = $w->applyTemplate($link['template'], $target_profile);
+                $url = Discovery::applyTemplate($link['template'], $target_profile);
                 common_log(LOG_INFO, "Sending remote subscriber $acct to $url");
                 common_redirect($url, 303);
             }
index 12832cdcfb2fab8b2a0e4a8298ef42ecd99bd68e..aae22f868a315582098b70df06c7f99e8b09521c 100644 (file)
@@ -333,10 +333,18 @@ class OStatusSubAction extends Action
             $group = $this->oprofile->localGroup();
             if ($user->isMember($group)) {
                 $this->showForm(_m('Already a member!'));
-            } elseif (Group_member::join($this->oprofile->group_id, $user->id)) {
-                $this->successGroup();
+                return;
+            }
+            if (Event::handle('StartJoinGroup', array($group, $user))) {
+                $ok = Group_member::join($this->oprofile->group_id, $user->id);
+                if ($ok) {
+                    Event::handle('EndJoinGroup', array($group, $user));
+                    $this->successGroup();
+                } else {
+                    $this->showForm(_m('Remote group join failed!'));
+                }
             } else {
-                $this->showForm(_m('Remote group join failed!'));
+                $this->showForm(_m('Remote group join aborted!'));
             }
         } else {
             $local = $this->oprofile->localProfile();
diff --git a/plugins/OStatus/actions/webfinger.php b/plugins/OStatus/actions/webfinger.php
deleted file mode 100644 (file)
index 34336a9..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-/**
- * @package OStatusPlugin
- * @maintainer James Walker <james@status.net>
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-
-class WebfingerAction extends Action
-{
-
-    public $uri;
-
-    function prepare($args)
-    {
-        parent::prepare($args);
-
-        $this->uri = $this->trimmed('uri');
-
-        return true;
-    }
-
-    function handle()
-    {
-        $acct = Webfinger::normalize($this->uri);
-
-        $xrd = new XRD();
-
-        list($nick, $domain) = explode('@', urldecode($acct));
-        $nick = common_canonical_nickname($nick);
-
-        $this->user = User::staticGet('nickname', $nick);
-        if (!$this->user) {
-            $this->clientError(_('No such user.'), 404);
-            return false;
-        }
-
-        $xrd->subject = $this->uri;
-        $xrd->alias[] = common_profile_url($nick);
-        $xrd->links[] = array('rel' => Webfinger::PROFILEPAGE,
-                              'type' => 'text/html',
-                              'href' => common_profile_url($nick));
-
-        $xrd->links[] = array('rel' => Webfinger::UPDATESFROM,
-                              'href' => common_local_url('ApiTimelineUser',
-                                                         array('id' => $this->user->id,
-                                                               'format' => 'atom')),
-                              'type' => 'application/atom+xml');
-
-        // hCard
-        $xrd->links[] = array('rel' => 'http://microformats.org/profile/hcard',
-                              'type' => 'text/html',
-                              'href' => common_profile_url($nick));
-
-        // XFN
-        $xrd->links[] = array('rel' => 'http://gmpg.org/xfn/11',
-                              'type' => 'text/html',
-                              'href' => common_profile_url($nick));
-        // FOAF
-        $xrd->links[] = array('rel' => 'describedby',
-                              'type' => 'application/rdf+xml',
-                              'href' => common_local_url('foaf',
-                                                         array('nickname' => $nick)));                        
-        
-        $salmon_url = common_local_url('salmon',
-                                       array('id' => $this->user->id));
-
-        $xrd->links[] = array('rel' => 'salmon',
-                              'href' => $salmon_url);
-
-        // Get this user's keypair
-        $magickey = Magicsig::staticGet('user_id', $this->user->id);
-        if (!$magickey) {
-            // No keypair yet, let's generate one.
-            $magickey = new Magicsig();
-            $magickey->generate();
-        }
-        
-        $xrd->links[] = array('rel' => Magicsig::PUBLICKEYREL,
-                              'href' => 'data:application/magic-public-key;'. $magickey->keypair);
-        
-        // TODO - finalize where the redirect should go on the publisher
-        $url = common_local_url('ostatussub') . '?profile={uri}';
-        $xrd->links[] = array('rel' => 'http://ostatus.org/schema/1.0/subscribe',
-                              'template' => $url );
-
-        header('Content-type: text/xml');
-        print $xrd->toXML();
-    }
-
-}
diff --git a/plugins/OStatus/actions/xrd.php b/plugins/OStatus/actions/xrd.php
new file mode 100644 (file)
index 0000000..e6b694d
--- /dev/null
@@ -0,0 +1,109 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @package OStatusPlugin
+ * @maintainer James Walker <james@status.net>
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+
+class XrdAction extends Action
+{
+
+    public $uri;
+
+    function prepare($args)
+    {
+        parent::prepare($args);
+
+        $this->uri = $this->trimmed('uri');
+
+        return true;
+    }
+
+    function handle()
+    {
+        $acct = Discovery::normalize($this->uri);
+
+        $xrd = new XRD();
+
+        list($nick, $domain) = explode('@', substr(urldecode($acct), 5));
+        $nick = common_canonical_nickname($nick);
+
+        $this->user = User::staticGet('nickname', $nick);
+        if (!$this->user) {
+            $this->clientError(_('No such user.'), 404);
+            return false;
+        }
+
+        $xrd->subject = $this->uri;
+        $xrd->alias[] = common_profile_url($nick);
+        $xrd->links[] = array('rel' => Discovery::PROFILEPAGE,
+                              'type' => 'text/html',
+                              'href' => common_profile_url($nick));
+
+        $xrd->links[] = array('rel' => Discovery::UPDATESFROM,
+                              'href' => common_local_url('ApiTimelineUser',
+                                                         array('id' => $this->user->id,
+                                                               'format' => 'atom')),
+                              'type' => 'application/atom+xml');
+
+        // hCard
+        $xrd->links[] = array('rel' => Discovery::HCARD,
+                              'type' => 'text/html',
+                              'href' => common_local_url('hcard', array('nickname' => $nick)));
+
+        // XFN
+        $xrd->links[] = array('rel' => 'http://gmpg.org/xfn/11',
+                              'type' => 'text/html',
+                              'href' => common_profile_url($nick));
+        // FOAF
+        $xrd->links[] = array('rel' => 'describedby',
+                              'type' => 'application/rdf+xml',
+                              'href' => common_local_url('foaf',
+                                                         array('nickname' => $nick)));
+
+        $salmon_url = common_local_url('salmon',
+                                       array('id' => $this->user->id));
+
+        $xrd->links[] = array('rel' => 'salmon',
+                              'href' => $salmon_url);
+
+        // Get this user's keypair
+        $magickey = Magicsig::staticGet('user_id', $this->user->id);
+        if (!$magickey) {
+            // No keypair yet, let's generate one.
+            $magickey = new Magicsig();
+            $magickey->generate($this->user->id);
+        }
+
+        $xrd->links[] = array('rel' => Magicsig::PUBLICKEYREL,
+                              'href' => 'data:application/magic-public-key;'. $magickey->keypair);
+
+        // TODO - finalize where the redirect should go on the publisher
+        $url = common_local_url('ostatussub') . '?profile={uri}';
+        $xrd->links[] = array('rel' => 'http://ostatus.org/schema/1.0/subscribe',
+                              'template' => $url );
+
+        header('Content-type: text/xml');
+        print $xrd->toXML();
+    }
+
+}
index 681aec184d0ec8672cf01342826a52abb4b54ac5..02882d19b13e1750e43e1b31c7fe7c3a55263d62 100644 (file)
@@ -90,7 +90,7 @@ class Magicsig extends Memcached_DataObject
         return parent::insert();
     }
 
-    public function generate($key_length = 512)
+    public function generate($user_id, $key_length = 512)
     {
         PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
 
@@ -101,6 +101,7 @@ class Magicsig extends Memcached_DataObject
         $this->_rsa = new Crypt_RSA($params);
         PEAR::popErrorHandling();
 
+        $this->user_id = $user_id;
         $this->insert();
     }
 
index a366c1c2cf4b96fe5d7eea042c1358bedc774ca2..091056c54180a4c6624a256a0de6cc8c535571ac 100644 (file)
@@ -150,27 +150,7 @@ class Ostatus_profile extends Memcached_DataObject
     function asActivityObject()
     {
         if ($this->isGroup()) {
-            $object = new ActivityObject();
-            $object->type = 'http://activitystrea.ms/schema/1.0/group';
-            $object->id = $this->uri;
-            $self = $this->localGroup();
-
-            // @fixme put a standard getAvatar() interface on groups too
-            if ($self->homepage_logo) {
-                $object->avatar = $self->homepage_logo;
-                $map = array('png' => 'image/png',
-                             'jpg' => 'image/jpeg',
-                             'jpeg' => 'image/jpeg',
-                             'gif' => 'image/gif');
-                $extension = pathinfo(parse_url($avatarHref, PHP_URL_PATH), PATHINFO_EXTENSION);
-                if (isset($map[$extension])) {
-                    // @fixme this ain't used/saved yet
-                    $object->avatarType = $map[$extension];
-                }
-            }
-
-            $object->link = $this->uri; // @fixme accurate?
-            return $object;
+            return ActivityObject::fromGroup($this->localGroup());
         } else {
             return ActivityObject::fromProfile($this->localProfile());
         }
@@ -189,57 +169,13 @@ class Ostatus_profile extends Memcached_DataObject
      */
     function asActivityNoun($element)
     {
-        $xs = new XMLStringer(true);
-        $avatarHref = Avatar::defaultImage(AVATAR_PROFILE_SIZE);
-        $avatarType = 'image/png';
         if ($this->isGroup()) {
-            $type = 'http://activitystrea.ms/schema/1.0/group';
-            $self = $this->localGroup();
-
-            // @fixme put a standard getAvatar() interface on groups too
-            if ($self->homepage_logo) {
-                $avatarHref = $self->homepage_logo;
-                $map = array('png' => 'image/png',
-                             'jpg' => 'image/jpeg',
-                             'jpeg' => 'image/jpeg',
-                             'gif' => 'image/gif');
-                $extension = pathinfo(parse_url($avatarHref, PHP_URL_PATH), PATHINFO_EXTENSION);
-                if (isset($map[$extension])) {
-                    $avatarType = $map[$extension];
-                }
-            }
+            $noun = ActivityObject::fromGroup($this->localGroup());
+            return $noun->asString('activity:' . $element);
         } else {
-            $type = 'http://activitystrea.ms/schema/1.0/person';
-            $self = $this->localProfile();
-            $avatar = $self->getAvatar(AVATAR_PROFILE_SIZE);
-            if ($avatar) {
-                  $avatarHref = $avatar->url;
-                  $avatarType = $avatar->mediatype;
-            }
+            $noun = ActivityObject::fromProfile($this->localProfile());
+            return $noun->asString('activity:' . $element);
         }
-        $xs->elementStart('activity:' . $element);
-        $xs->element(
-            'activity:object-type',
-            null,
-            $type
-        );
-        $xs->element(
-            'id',
-            null,
-            $this->uri); // ?
-        $xs->element('title', null, $self->getBestName());
-
-        $xs->element(
-            'link', array(
-                'type' => $avatarType,
-                'href' => $avatarHref
-            ),
-            ''
-        );
-
-        $xs->elementEnd('activity:' . $element);
-
-        return $xs->getString();
     }
 
     /**
@@ -332,6 +268,9 @@ class Ostatus_profile extends Memcached_DataObject
      */
     public function unsubscribe() {
         $feedsub = FeedSub::staticGet('uri', $this->feeduri);
+        if (!$feedsub) {
+            return true;
+        }
         if ($feedsub->sub_state == 'active') {
             return $feedsub->unsubscribe();
         } else if ($feedsub->sub_state == '' || $feedsub->sub_state == 'inactive' || $feedsub->sub_state == 'unsubscribe') {
@@ -356,7 +295,7 @@ class Ostatus_profile extends Memcached_DataObject
             $count = $this->localProfile()->subscriberCount();
         }
         if ($count == 0) {
-            common_log(LOG_INFO, "Unsubscribing from now-unused remote feed $oprofile->feeduri");
+            common_log(LOG_INFO, "Unsubscribing from now-unused remote feed $this->feeduri");
             $this->unsubscribe();
             return true;
         } else {
@@ -398,7 +337,8 @@ class Ostatus_profile extends Memcached_DataObject
                                 'xmlns:thr' => 'http://purl.org/syndication/thread/1.0',
                                 'xmlns:georss' => 'http://www.georss.org/georss',
                                 'xmlns:ostatus' => 'http://ostatus.org/schema/1.0',
-                                'xmlns:poco' => 'http://portablecontacts.net/spec/1.0');
+                                'xmlns:poco' => 'http://portablecontacts.net/spec/1.0',
+                                'xmlns:media' => 'http://purl.org/syndication/atommedia');
 
             $entry = new XMLStringer();
             $entry->elementStart('entry', $attributes);
@@ -482,36 +422,6 @@ class Ostatus_profile extends Memcached_DataObject
         }
     }
 
-    function atomFeed($actor)
-    {
-        $feed = new Atom10Feed();
-        // @fixme should these be set up somewhere else?
-        $feed->addNamespace('activity', 'http://activitystrea.ms/spec/1.0/');
-        $feed->addNamespace('thr', 'http://purl.org/syndication/thread/1.0');
-        $feed->addNamespace('georss', 'http://www.georss.org/georss');
-        $feed->addNamespace('ostatus', 'http://ostatus.org/schema/1.0');
-
-        $taguribase = common_config('integration', 'taguri');
-        $feed->setId("tag:{$taguribase}:UserTimeline:{$actor->id}"); // ???
-
-        $feed->setTitle($actor->getBestName() . ' timeline'); // @fixme
-        $feed->setUpdated(time());
-        $feed->setPublished(time());
-
-        $feed->addLink(common_local_url('ApiTimelineUser',
-                                        array('id' => $actor->id,
-                                              'type' => 'atom')),
-                       array('rel' => 'self',
-                             'type' => 'application/atom+xml'));
-
-        $feed->addLink(common_local_url('userbyid',
-                                        array('id' => $actor->id)),
-                       array('rel' => 'alternate',
-                             'type' => 'text/html'));
-
-        return $feed;
-    }
-
     /**
      * Read and post notices for updates from the feed.
      * Currently assumes that all items in the feed are new,
@@ -607,12 +517,39 @@ class Ostatus_profile extends Memcached_DataObject
         $rendered = $this->purify($activity->object->content);
         $content = html_entity_decode(strip_tags($rendered));
 
+        $shortened = common_shorten_links($content);
+
+        // If it's too long, try using the summary, and make the
+        // HTML an attachment.
+
+        $attachment = null;
+
+        if (Notice::contentTooLong($shortened)) {
+            $attachment = $this->saveHTMLFile($activity->object->title, $rendered);
+            $summary = $activity->object->summary;
+            if (empty($summary)) {
+                $summary = $content;
+            }
+            $shortSummary = common_shorten_links($summary);
+            if (Notice::contentTooLong($shortSummary)) {
+                $url = common_shorten_url(common_local_url('attachment',
+                                                           array('attachment' => $attachment->id)));
+                $shortSummary = substr($shortSummary,
+                                       0,
+                                       Notice::maxContent() - (mb_strlen($url) + 2));
+                $shortSummary .= '… ' . $url;
+                $content = $shortSummary;
+                $rendered = common_render_text($content);
+            }
+        }
+
         $options = array('is_local' => Notice::REMOTE_OMB,
                         'url' => $sourceUrl,
                         'uri' => $sourceUri,
                         'rendered' => $rendered,
                         'replies' => array(),
-                        'groups' => array());
+                        'groups' => array(),
+                        'tags' => array());
 
         // Check for optional attributes...
 
@@ -647,6 +584,16 @@ class Ostatus_profile extends Memcached_DataObject
             }
         }
 
+        // Atom categories <-> hashtags
+        foreach ($activity->categories as $cat) {
+            if ($cat->term) {
+                $term = common_canonical_tag($cat->term);
+                if ($term) {
+                    $options['tags'][] = $term;
+                }
+            }
+        }
+
         try {
             $saved = Notice::saveNew($oprofile->profile_id,
                                      $content,
@@ -654,6 +601,9 @@ class Ostatus_profile extends Memcached_DataObject
                                      $options);
             if ($saved) {
                 Ostatus_source::saveNew($saved, $this, $method);
+                if (!empty($attachment)) {
+                    File_to_post::processNew($attachment->id, $saved->id);
+                }
             }
         } catch (Exception $e) {
             common_log(LOG_ERR, "OStatus save of remote message $sourceUri failed: " . $e->getMessage());
@@ -747,11 +697,18 @@ class Ostatus_profile extends Memcached_DataObject
     {
         // Get the canonical feed URI and check it
         $discover = new FeedDiscovery();
-        $feeduri = $discover->discoverFromURL($profile_uri);
+        if ($hints['feedurl']) {
+            $feeduri = $hints['feedurl'];
+            $feeduri = $discover->discoverFromFeedURL($feeduri);
+        } else {
+            $feeduri = $discover->discoverFromURL($profile_uri);
+            $hints['feedurl'] = $feeduri;
+        }
 
-        //$feedsub = FeedSub::ensureFeed($feeduri, $discover->feed);
         $huburi = $discover->getAtomLink('hub');
+        $hints['hub'] = $huburi;
         $salmonuri = $discover->getAtomLink('salmon');
+        $hints['salmon'] = $salmonuri;
 
         if (!$huburi) {
             // We can only deal with folks with a PuSH hub
@@ -766,7 +723,7 @@ class Ostatus_profile extends Memcached_DataObject
 
         if (!empty($subject)) {
             $subjObject = new ActivityObject($subject);
-            return self::ensureActivityObjectProfile($subjObject, $feeduri, $salmonuri, $hints);
+            return self::ensureActivityObjectProfile($subjObject, $hints);
         }
 
         // Otherwise, try the feed author
@@ -775,7 +732,7 @@ class Ostatus_profile extends Memcached_DataObject
 
         if (!empty($author)) {
             $authorObject = new ActivityObject($author);
-            return self::ensureActivityObjectProfile($authorObject, $feeduri, $salmonuri, $hints);
+            return self::ensureActivityObjectProfile($authorObject, $hints);
         }
 
         // Sheesh. Not a very nice feed! Let's try fingerpoken in the
@@ -791,7 +748,7 @@ class Ostatus_profile extends Memcached_DataObject
 
             if (!empty($actor)) {
                 $actorObject = new ActivityObject($actor);
-                return self::ensureActivityObjectProfile($actorObject, $feeduri, $salmonuri, $hints);
+                return self::ensureActivityObjectProfile($actorObject, $hints);
 
             }
 
@@ -799,7 +756,7 @@ class Ostatus_profile extends Memcached_DataObject
 
             if (!empty($author)) {
                 $authorObject = new ActivityObject($author);
-                return self::ensureActivityObjectProfile($authorObject, $feeduri, $salmonuri, $hints);
+                return self::ensureActivityObjectProfile($authorObject, $hints);
             }
         }
 
@@ -868,8 +825,20 @@ class Ostatus_profile extends Memcached_DataObject
 
     protected static function getActivityObjectAvatar($object, $hints=array())
     {
-        if ($object->avatar) {
-            return $object->avatar;
+        if ($object->avatarLinks) {
+            $best = false;
+            // Take the exact-size avatar, or the largest avatar, or the first avatar if all sizeless
+            foreach ($object->avatarLinks as $avatar) {
+                if ($avatar->width == AVATAR_PROFILE_SIZE && $avatar->height = AVATAR_PROFILE_SIZE) {
+                    // Exact match!
+                    $best = $avatar;
+                    break;
+                }
+                if (!$best || $avatar->width > $best->width) {
+                    $best = $avatar;
+                }
+            }
+            return $best->url;
         } else if (array_key_exists('avatar', $hints)) {
             return $hints['avatar'];
         }
@@ -932,18 +901,18 @@ class Ostatus_profile extends Memcached_DataObject
      * @return Ostatus_profile
      */
 
-    public static function ensureActorProfile($activity, $feeduri=null, $salmonuri=null)
+    public static function ensureActorProfile($activity, $hints=array())
     {
-        return self::ensureActivityObjectProfile($activity->actor, $feeduri, $salmonuri);
+        return self::ensureActivityObjectProfile($activity->actor, $hints);
     }
 
-    public static function ensureActivityObjectProfile($object, $feeduri=null, $salmonuri=null, $hints=array())
+    public static function ensureActivityObjectProfile($object, $hints=array())
     {
         $profile = self::getActivityObjectProfile($object);
         if ($profile) {
             $profile->updateFromActivityObject($object, $hints);
         } else {
-            $profile = self::createActivityObjectProfile($object, $feeduri, $salmonuri, $hints);
+            $profile = self::createActivityObjectProfile($object, $hints);
         }
         return $profile;
     }
@@ -989,58 +958,55 @@ class Ostatus_profile extends Memcached_DataObject
      * @fixme validate stuff somewhere
      */
 
-    protected static function createActorProfile($activity, $feeduri=null, $salmonuri=null)
-    {
-        $actor = $activity->actor;
-
-        self::createActivityObjectProfile($actor, $feeduri, $salmonuri);
-    }
-
     /**
      * Create local ostatus_profile and profile/user_group entries for
      * the provided remote user or group.
      *
      * @param ActivityObject $object
-     * @param string $feeduri
-     * @param string $salmonuri
      * @param array $hints
      *
-     * @fixme fold $feeduri/$salmonuri into $hints
      * @return Ostatus_profile
      */
-    protected static function createActivityObjectProfile($object, $feeduri=null, $salmonuri=null, $hints=array())
+    protected static function createActivityObjectProfile($object, $hints=array())
     {
-        $homeuri  = $object->id;
+        $homeuri = $object->id;
+        $discover = false;
 
         if (!$homeuri) {
             common_log(LOG_DEBUG, __METHOD__ . " empty actor profile URI: " . var_export($activity, true));
             throw new ServerException("No profile URI");
         }
 
-        if (empty($feeduri)) {
-            if (array_key_exists('feedurl', $hints)) {
-                $feeduri = $hints['feedurl'];
-            }
+        if (array_key_exists('feedurl', $hints)) {
+            $feeduri = $hints['feedurl'];
+        } else {
+            $discover = new FeedDiscovery();
+            $feeduri = $discover->discoverFromURL($homeuri);
         }
 
-        if (empty($salmonuri)) {
-            if (array_key_exists('salmon', $hints)) {
-                $salmonuri = $hints['salmon'];
+        if (array_key_exists('salmon', $hints)) {
+            $salmonuri = $hints['salmon'];
+        } else {
+            if (!$discover) {
+                $discover = new FeedDiscovery();
+                $discover->discoverFromFeedURL($hints['feedurl']);
             }
+            $salmonuri = $discover->getAtomLink('salmon');
         }
 
-        if (!$feeduri || !$salmonuri) {
-            // Get the canonical feed URI and check it
-            $discover = new FeedDiscovery();
-            $feeduri = $discover->discoverFromURL($homeuri);
-
+        if (array_key_exists('hub', $hints)) {
+            $huburi = $hints['hub'];
+        } else {
+            if (!$discover) {
+                $discover = new FeedDiscovery();
+                $discover->discoverFromFeedURL($hints['feedurl']);
+            }
             $huburi = $discover->getAtomLink('hub');
-            $salmonuri = $discover->getAtomLink('salmon');
+        }
 
-            if (!$huburi) {
-                // We can only deal with folks with a PuSH hub
-                throw new FeedSubNoHubException();
-            }
+        if (!$huburi) {
+            // We can only deal with folks with a PuSH hub
+            throw new FeedSubNoHubException();
         }
 
         $oprofile = new Ostatus_profile();
@@ -1054,8 +1020,8 @@ class Ostatus_profile extends Memcached_DataObject
 
         if ($object->type == ActivityObject::PERSON) {
             $profile = new Profile();
+            $profile->created = common_sql_now();
             self::updateProfile($profile, $object, $hints);
-            $profile->created  = common_sql_now();
 
             $oprofile->profile_id = $profile->insert();
             if (!$oprofile->profile_id) {
@@ -1063,6 +1029,7 @@ class Ostatus_profile extends Memcached_DataObject
             }
         } else {
             $group = new User_group();
+            $group->uri = $homeuri;
             $group->created = common_sql_now();
             self::updateGroup($group, $object, $hints);
 
@@ -1110,18 +1077,35 @@ class Ostatus_profile extends Memcached_DataObject
         $orig = clone($profile);
 
         $profile->nickname = self::getActivityObjectNickname($object, $hints);
-        $profile->fullname = $object->title;
+
+        if (!empty($object->title)) {
+            $profile->fullname = $object->title;
+        } else if (array_key_exists('fullname', $hints)) {
+            $profile->fullname = $hints['fullname'];
+        }
+
         if (!empty($object->link)) {
             $profile->profileurl = $object->link;
         } else if (array_key_exists('profileurl', $hints)) {
             $profile->profileurl = $hints['profileurl'];
+        } else if (Validate::uri($object->id, array('allowed_schemes' => array('http', 'https')))) {
+            $profile->profileurl = $object->id;
+        }
+
+        $profile->bio      = self::getActivityObjectBio($object, $hints);
+        $profile->location = self::getActivityObjectLocation($object, $hints);
+        $profile->homepage = self::getActivityObjectHomepage($object, $hints);
+
+        if (!empty($object->geopoint)) {
+            $location = ActivityContext::locationFromPoint($object->geopoint);
+            if (!empty($location)) {
+                $profile->lat = $location->lat;
+                $profile->lon = $location->lon;
+            }
         }
 
-        // @fixme bio
         // @fixme tags/categories
-        // @fixme location?
         // @todo tags from categories
-        // @todo lat/lon/location?
 
         if ($profile->id) {
             common_log(LOG_DEBUG, "Updating OStatus profile $profile->id from remote info $object->id: " . var_export($object, true) . var_export($hints, true));
@@ -1133,19 +1117,19 @@ class Ostatus_profile extends Memcached_DataObject
     {
         $orig = clone($group);
 
-        // @fixme need to make nick unique etc *hack hack*
         $group->nickname = self::getActivityObjectNickname($object, $hints);
         $group->fullname = $object->title;
 
-        // @fixme no canonical profileurl; using homepage instead for now
-        $group->homepage = $object->id;
+        if (!empty($object->link)) {
+            $group->mainpage = $object->link;
+        } else if (array_key_exists('profileurl', $hints)) {
+            $group->mainpage = $hints['profileurl'];
+        }
 
-        // @fixme homepage
-        // @fixme bio
-        // @fixme tags/categories
-        // @fixme location?
         // @todo tags from categories
-        // @todo lat/lon/location?
+        $group->description = self::getActivityObjectBio($object, $hints);
+        $group->location = self::getActivityObjectLocation($object, $hints);
+        $group->homepage = self::getActivityObjectHomepage($object, $hints);
 
         if ($group->id) {
             common_log(LOG_DEBUG, "Updating OStatus group $group->id from remote info $object->id: " . var_export($object, true) . var_export($hints, true));
@@ -1153,6 +1137,69 @@ class Ostatus_profile extends Memcached_DataObject
         }
     }
 
+    protected static function getActivityObjectHomepage($object, $hints=array())
+    {
+        $homepage = null;
+        $poco     = $object->poco;
+
+        if (!empty($poco)) {
+            $url = $poco->getPrimaryURL();
+            if ($url->type == 'homepage') {
+                $homepage = $url->value;
+            }
+        }
+
+        // @todo Try for a another PoCo URL?
+
+        return $homepage;
+    }
+
+    protected static function getActivityObjectLocation($object, $hints=array())
+    {
+        $location = null;
+
+        if (!empty($object->poco) &&
+            isset($object->poco->address->formatted)) {
+            $location = $object->poco->address->formatted;
+        } else if (array_key_exists('location', $hints)) {
+            $location = $hints['location'];
+        }
+
+        if (!empty($location)) {
+            if (mb_strlen($location) > 255) {
+                $location = mb_substr($note, 0, 255 - 3) . ' â€¦ ';
+            }
+        }
+
+        // @todo Try to find location some othe way? Via goerss point?
+
+        return $location;
+    }
+
+    protected static function getActivityObjectBio($object, $hints=array())
+    {
+        $bio  = null;
+
+        if (!empty($object->poco)) {
+            $note = $object->poco->note;
+        } else if (array_key_exists('bio', $hints)) {
+            $note = $hints['bio'];
+        }
+
+        if (!empty($note)) {
+            if (Profile::bioTooLong($note)) {
+                // XXX: truncate ok?
+                $bio = mb_substr($note, 0, Profile::maxBio() - 3) . ' â€¦ ';
+            } else {
+                $bio = $note;
+            }
+        }
+
+        // @todo Try to get bio info some other way?
+
+        return $bio;
+    }
+
     protected static function getActivityObjectNickname($object, $hints=array())
     {
         if ($object->poco) {
@@ -1160,10 +1207,15 @@ class Ostatus_profile extends Memcached_DataObject
                 return common_nicknamize($object->poco->preferredUsername);
             }
         }
+
         if (!empty($object->nickname)) {
             return common_nicknamize($object->nickname);
         }
 
+        if (array_key_exists('nickname', $hints)) {
+            return $hints['nickname'];
+        }
+
         // Try the definitive ID
 
         $nickname = self::nicknameFromURI($object->id);
@@ -1208,35 +1260,54 @@ class Ostatus_profile extends Memcached_DataObject
 
     public static function ensureWebfinger($addr)
     {
+        // First, try the cache
+
+        $uri = self::cacheGet(sprintf('ostatus_profile:webfinger:%s', $addr));
+
+        if ($uri !== false) {
+            if (is_null($uri)) {
+                return null;
+            }
+            $oprofile = Ostatus_profile::staticGet('uri', $uri);
+            if (!empty($oprofile)) {
+                return $oprofile;
+            }
+        }
+
         // First, look it up
 
         $oprofile = Ostatus_profile::staticGet('uri', 'acct:'.$addr);
 
         if (!empty($oprofile)) {
+            self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), $oprofile->uri);
             return $oprofile;
         }
 
         // Now, try some discovery
 
-        $wf = new Webfinger();
+        $disco = new Discovery();
 
-        $result = $wf->lookup($addr);
+        $result = $disco->lookup($addr);
 
         if (!$result) {
+            self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), null);
             return null;
         }
 
         foreach ($result->links as $link) {
             switch ($link['rel']) {
-            case Webfinger::PROFILEPAGE:
+            case Discovery::PROFILEPAGE:
                 $profileUrl = $link['href'];
                 break;
             case 'salmon':
                 $salmonEndpoint = $link['href'];
                 break;
-            case Webfinger::UPDATESFROM:
+            case Discovery::UPDATESFROM:
                 $feedUrl = $link['href'];
                 break;
+            case Discovery::HCARD:
+                $hcardUrl = $link['href'];
+                break;
             default:
                 common_log(LOG_NOTICE, "Don't know what to do with rel = '{$link['rel']}'");
                 break;
@@ -1248,11 +1319,19 @@ class Ostatus_profile extends Memcached_DataObject
                        'feedurl' => $feedUrl,
                        'salmon' => $salmonEndpoint);
 
+        if (isset($hcardUrl)) {
+            $hcardHints = self::slurpHcard($hcardUrl);
+            // Note: Webfinger > hcard
+            $hints = array_merge($hcardHints, $hints);
+        }
+
         // If we got a feed URL, try that
 
         if (isset($feedUrl)) {
             try {
+                common_log(LOG_INFO, "Discovery on acct:$addr with feed URL $feedUrl");
                 $oprofile = self::ensureProfile($feedUrl, $hints);
+                self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), $oprofile->uri);
                 return $oprofile;
             } catch (Exception $e) {
                 common_log(LOG_WARNING, "Failed creating profile from feed URL '$feedUrl': " . $e->getMessage());
@@ -1264,7 +1343,9 @@ class Ostatus_profile extends Memcached_DataObject
 
         if (isset($profileUrl)) {
             try {
+                common_log(LOG_INFO, "Discovery on acct:$addr with profile URL $profileUrl");
                 $oprofile = self::ensureProfile($profileUrl, $hints);
+                self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), $oprofile->uri);
                 return $oprofile;
             } catch (Exception $e) {
                 common_log(LOG_WARNING, "Failed creating profile from profile URL '$profileUrl': " . $e->getMessage());
@@ -1316,9 +1397,106 @@ class Ostatus_profile extends Memcached_DataObject
                 throw new Exception("Couldn't save ostatus_profile for '$addr'");
             }
 
+            self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), $oprofile->uri);
             return $oprofile;
         }
 
         return null;
     }
+
+    function saveHTMLFile($title, $rendered)
+    {
+        $final = sprintf("<!DOCTYPE html>\n<html><head><title>%s</title></head>".
+                         '<body><div>%s</div></body></html>',
+                         htmlspecialchars($title),
+                         $rendered);
+
+        $filename = File::filename($this->localProfile(),
+                                   'ostatus', // ignored?
+                                   'text/html');
+
+        $filepath = File::path($filename);
+
+        file_put_contents($filepath, $final);
+
+        $file = new File;
+
+        $file->filename = $filename;
+        $file->url      = File::url($filename);
+        $file->size     = filesize($filepath);
+        $file->date     = time();
+        $file->mimetype = 'text/html';
+
+        $file_id = $file->insert();
+
+        if ($file_id === false) {
+            common_log_db_error($file, "INSERT", __FILE__);
+            throw new ServerException(_('Could not store HTML content of long post as file.'));
+        }
+
+        return $file;
+    }
+
+    protected static function slurpHcard($url)
+    {
+        set_include_path(get_include_path() . PATH_SEPARATOR . INSTALLDIR . '/plugins/OStatus/extlib/hkit/');
+        require_once('hkit.class.php');
+
+        $h     = new hKit;
+
+        // Google Buzz hcards need to be tidied. Probably others too.
+
+        $h->tidy_mode = 'proxy'; // 'proxy', 'exec', 'php' or 'none'
+
+        // Get by URL
+        $hcards = $h->getByURL('hcard', $url);
+
+        if (empty($hcards)) {
+            return array();
+        }
+
+        // @fixme more intelligent guess on multi-hcard pages
+        $hcard = $hcards[0];
+
+        $hints = array();
+
+        $hints['profileurl'] = $url;
+
+        if (array_key_exists('nickname', $hcard)) {
+            $hints['nickname'] = $hcard['nickname'];
+        }
+
+        if (array_key_exists('fn', $hcard)) {
+            $hints['fullname'] = $hcard['fn'];
+        } else if (array_key_exists('n', $hcard)) {
+            $hints['fullname'] = implode(' ', $hcard['n']);
+        }
+
+        if (array_key_exists('photo', $hcard)) {
+            $hints['avatar'] = $hcard['photo'];
+        }
+
+        if (array_key_exists('note', $hcard)) {
+            $hints['bio'] = $hcard['note'];
+        }
+
+        if (array_key_exists('adr', $hcard)) {
+            if (is_string($hcard['adr'])) {
+                $hints['location'] = $hcard['adr'];
+            } else if (is_array($hcard['adr'])) {
+                $hints['location'] = implode(' ', $hcard['adr']);
+            }
+        }
+
+        if (array_key_exists('url', $hcard)) {
+            if (is_string($hcard['url'])) {
+                $hints['homepage'] = $hcard['url'];
+            } else if (is_array($hcard['adr'])) {
+                // HACK get the last one; that's how our hcards look
+                $hints['homepage'] = $hcard['url'][count($hcard['url'])-1];
+            }
+        }
+
+        return $hints;
+    }
 }
diff --git a/plugins/OStatus/extlib/hkit/hcard.profile.php b/plugins/OStatus/extlib/hkit/hcard.profile.php
new file mode 100644 (file)
index 0000000..6ec0dc8
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+       // hcard profile for hkit
+       
+       $this->root_class = 'vcard';
+       
+       $this->classes = array( 
+               'fn', array('honorific-prefix', 'given-name', 'additional-name', 'family-name', 'honorific-suffix'),
+               'n', array('honorific-prefix', 'given-name', 'additional-name', 'family-name', 'honorific-suffix'),
+               'adr', array('post-office-box', 'extended-address', 'street-address', 'postal-code', 'country-name', 'type', 'region', 'locality'),
+               'label', 'bday', 'agent', 'nickname', 'photo', 'class', 
+               'email', array('type', 'value'), 
+               'category', 'key', 'logo', 'mailer', 'note',
+               'org', array('organization-name', 'organization-unit'),
+               'tel', array('type', 'value'),
+               'geo', array('latitude', 'longitude'),
+               'tz', 'uid', 'url', 'rev', 'role', 'sort-string', 'sound', 'title'              
+       );
+       
+       // classes that must only appear once per card
+       $this->singles = array(
+               'fn'
+       );
+       
+       // classes that are required (not strictly enforced - give at least one!)
+       $this->required = array(
+               'fn'
+       );
+
+       $this->att_map = array(
+               'fn'    => array('IMG|alt'),
+               'url'   => array('A|href', 'IMG|src', 'AREA|href'),
+               'photo' => array('IMG|src'),
+               'bday'  => array('ABBR|title'),
+               'logo'  => array('IMG|src'),
+               'email' => array('A|href'),
+               'geo'   => array('ABBR|title')
+       );
+
+       
+       $this->callbacks = array(
+               'url'   => array($this, 'resolvePath'),
+               'photo' => array($this, 'resolvePath'),
+               'logo'  => array($this, 'resolvePath'),
+               'email' => array($this, 'resolveEmail')
+       );
+
+
+
+       function hKit_hcard_post($a)
+       {
+               
+               foreach ($a as &$vcard){
+                       
+                       hKit_implied_n_optimization($vcard);
+                       hKit_implied_n_from_fn($vcard);
+                       
+               }
+               
+               return $a;
+       
+       }
+       
+       
+       function hKit_implied_n_optimization(&$vcard)
+       {
+               if (array_key_exists('fn', $vcard) && !is_array($vcard['fn']) && 
+                       !array_key_exists('n', $vcard) && (!array_key_exists('org', $vcard) || $vcard['fn'] != $vcard['org'])){
+                       
+                       if (sizeof(explode(' ', $vcard['fn'])) == 2){
+                               $patterns       = array();
+                               $patterns[] = array('/^(\S+),\s*(\S{1})$/', 2, 1);              // Lastname, Initial
+                               $patterns[] = array('/^(\S+)\s*(\S{1})\.*$/', 2, 1);    // Lastname Initial(.)
+                               $patterns[] = array('/^(\S+),\s*(\S+)$/', 2, 1);                // Lastname, Firstname
+                               $patterns[] = array('/^(\S+)\s*(\S+)$/', 1, 2);                 // Firstname Lastname
+                       
+                               foreach ($patterns as $pattern){
+                                       if (preg_match($pattern[0], $vcard['fn'], $matches) === 1){
+                                               $n                                      = array();
+                                               $n['given-name']        = $matches[$pattern[1]];
+                                               $n['family-name']       = $matches[$pattern[2]];
+                                               $vcard['n']                     = $n;
+                                               
+                                               
+                                               break;
+                                       }
+                               }
+                       }
+               }
+       }
+       
+       
+       function hKit_implied_n_from_fn(&$vcard)
+       {
+               if (array_key_exists('fn', $vcard) && is_array($vcard['fn']) 
+                       && !array_key_exists('n', $vcard) && (!array_key_exists('org', $vcard) || $vcard['fn'] != $vcard['org'])){
+                               
+                       $vcard['n']             = $vcard['fn'];
+               }
+
+               if (array_key_exists('fn', $vcard) && is_array($vcard['fn'])){
+                       $vcard['fn']    = $vcard['fn']['text'];
+               }
+       }
+
+?>
\ No newline at end of file
diff --git a/plugins/OStatus/extlib/hkit/hkit.class.php b/plugins/OStatus/extlib/hkit/hkit.class.php
new file mode 100644 (file)
index 0000000..c3a54cf
--- /dev/null
@@ -0,0 +1,475 @@
+<?php
+
+       /* 
+       
+       hKit Library for PHP5 - a generic library for parsing Microformats
+       Copyright (C) 2006  Drew McLellan
+
+       This library is free software; you can redistribute it and/or
+       modify it under the terms of the GNU Lesser General Public
+       License as published by the Free Software Foundation; either
+       version 2.1 of the License, or (at your option) any later version.
+
+       This library 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
+       Lesser General Public License for more details.
+
+       You should have received a copy of the GNU Lesser General Public
+       License along with this library; if not, write to the Free Software
+       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+       
+       Author  
+               Drew McLellan - http://allinthehead.com/
+               
+       Contributors:
+               Scott Reynen - http://www.randomchaos.com/
+               
+       Version 0.5, 22-Jul-2006
+               fixed by-ref issue cropping up in PHP 5.0.5
+               fixed a bug with a@title
+               added support for new fn=n optimisation
+               added support for new a.include include-pattern
+       Version 0.4, 23-Jun-2006
+               prevented nested includes from causing infinite loops
+               returns false if URL can't be fetched
+               added pre-flight check for base support level
+               added deduping of once-only classnames
+               prevented accumulation of multiple 'value' values
+               tuned whitespace handling and treatment of DEL elements
+       Version 0.3, 21-Jun-2006
+               added post-processor callback method into profiles
+               fixed minor problems raised by hcard testsuite
+               added support for include-pattern
+               added support for td@headers pattern
+               added implied-n optimization into default hcard profile
+       Version 0.2, 20-Jun-2006
+               added class callback mechanism
+               added resolvePath & resolveEmail
+               added basic BASE support
+       Version 0.1.1, 19-Jun-2006 (different timezone, no time machine)
+               added external Tidy option
+       Version 0.1, 20-Jun-2006
+               initial release
+               
+       
+       
+       
+       */
+
+       class hKit
+       {
+               
+               public $tidy_mode       = 'proxy'; // 'proxy', 'exec', 'php' or 'none'
+               public $tidy_proxy      = 'http://cgi.w3.org/cgi-bin/tidy?forceXML=on&docAddr='; // required only for tidy_mode=proxy
+               public $tmp_dir         = '/path/to/writable/dir/'; // required only for tidy_mode=exec
+               
+               private $root_class = '';
+               private $classes        = '';
+               private $singles        = '';
+               private $required       = '';
+               private $att_map        = '';
+               private $callbacks      = '';
+               private $processor      = '';
+               
+               private $url            = '';
+               private $base           = '';
+               private $doc            = '';
+               
+               
+               public function hKit()
+               {
+                       // pre-flight checks
+                       $pass           = true; 
+                       $required       = array('dom_import_simplexml', 'file_get_contents', 'simplexml_load_string');
+                       $missing        = array();
+                       
+                       foreach ($required as $f){
+                               if (!function_exists($f)){
+                                       $pass           = false;
+                                       $missing[]      = $f . '()';
+                               }
+                       }
+                       
+                       if (!$pass)
+                               die('hKit error: these required functions are not available: <strong>' . implode(', ', $missing) . '</strong>');
+                       
+               }
+               
+
+               public function getByURL($profile='', $url='')
+               {
+                       
+                       if ($profile=='' || $url == '') return false;
+                       
+                       $this->loadProfile($profile);
+                       
+                       $source         = $this->loadURL($url);
+                       
+                       if ($source){
+                               $tidy_xhtml     = $this->tidyThis($source);
+
+                               $fragment       = false;
+                       
+                               if (strrchr($url, '#'))
+                               $fragment       = array_pop(explode('#', $url));
+                       
+                               $doc            = $this->loadDoc($tidy_xhtml, $fragment);
+                               $s                      = $this->processNodes($doc, $this->classes);
+                               $s                      = $this->postProcess($profile, $s);
+                       
+                               return $s;
+                       }else{
+                               return false;
+                       }
+               }
+               
+               public function getByString($profile='', $input_xml='')
+               {
+                       if ($profile=='' || $input_xml == '') return false;
+                       
+                       $this->loadProfile($profile);
+
+                       $doc    = $this->loadDoc($input_xml);
+                       $s              = $this->processNodes($doc, $this->classes);
+                       $s              = $this->postProcess($profile, $s);
+                       
+                       return $s;
+                       
+               }
+               
+               private function processNodes($items, $classes, $allow_includes=true){
+
+                       $out    = array();
+
+                       foreach($items as $item){
+                               $data   = array();
+
+                               for ($i=0; $i<sizeof($classes); $i++){
+                                       
+                                       if (!is_array($classes[$i])){
+
+                                               $xpath                  = ".//*[contains(concat(' ',normalize-space(@class),' '),' " . $classes[$i] . " ')]";
+                                               $results                = $item->xpath($xpath);
+                                               
+                                               if ($results){
+                                                       foreach ($results as $result){ 
+                                                               if (isset($classes[$i+1]) && is_array($classes[$i+1])){
+                                                                       $nodes                          = $this->processNodes($results, $classes[$i+1]);
+                                                                       if (sizeof($nodes) > 0){
+                                                                               $nodes = array_merge(array('text'=>$this->getNodeValue($result, $classes[$i])), $nodes);
+                                                                               $data[$classes[$i]]     = $nodes;
+                                                                       }else{
+                                                                               $data[$classes[$i]]     = $this->getNodeValue($result, $classes[$i]);
+                                                                       }
+                                                                       
+                                                               }else{                                                          
+                                                                       if (isset($data[$classes[$i]])){
+                                                                               if (is_array($data[$classes[$i]])){
+                                                                                       // is already an array - append
+                                                                                       $data[$classes[$i]][]   = $this->getNodeValue($result, $classes[$i]);
+
+                                                                               }else{
+                                                                                       // make it an array
+                                                                                       if ($classes[$i] == 'value'){ // unless it's the 'value' of a type/value pattern
+                                                                                               $data[$classes[$i]] .= $this->getNodeValue($result, $classes[$i]);
+                                                                                       }else{
+                                                                                               $old_val                        = $data[$classes[$i]];
+                                                                                               $data[$classes[$i]]     = array($old_val, $this->getNodeValue($result, $classes[$i]));
+                                                                                               $old_val                        = false;
+                                                                                       }
+                                                                               }
+                                                                       }else{                                                                          
+                                                                               // set as normal value
+                                                                               $data[$classes[$i]]     = $this->getNodeValue($result, $classes[$i]);
+
+                                                                       }
+                                                               }
+                                                       
+                                                               // td@headers pattern
+                                                               if (strtoupper(dom_import_simplexml($result)->tagName)== "TD" && $result['headers']){
+                                                                       $include_ids    = explode(' ', $result['headers']);
+                                                                       $doc                    = $this->doc;
+                                                                       foreach ($include_ids as $id){
+                                                                               $xpath                  = "//*[@id='$id']/..";
+                                                                               $includes               = $doc->xpath($xpath);
+                                                                               foreach ($includes as $include){
+                                                                                       $tmp = $this->processNodes($include, $this->classes);
+                                                                                       if (is_array($tmp)) $data = array_merge($data, $tmp);
+                                                                               }
+                                                                       }
+                                                               }
+                                                       }                                       
+                                               }                               
+                                       }
+                                       $result = false;
+                               }
+                               
+                               // include-pattern
+                               if ($allow_includes){
+                                       $xpath                  = ".//*[contains(concat(' ',normalize-space(@class),' '),' include ')]";
+                                       $results                = $item->xpath($xpath);
+                               
+                                       if ($results){
+                                               foreach ($results as $result){
+                                                       $tagName = strtoupper(dom_import_simplexml($result)->tagName);
+                                                       if ((($tagName == "OBJECT" && $result['data']) || ($tagName == "A" && $result['href'])) 
+                                                                       && preg_match('/\binclude\b/', $result['class'])){      
+                                                               $att            = ($tagName == "OBJECT" ? 'data' : 'href');                                             
+                                                               $id                     = str_replace('#', '', $result[$att]);
+                                                               $doc            = $this->doc;
+                                                               $xpath          = "//*[@id='$id']";
+                                                               $includes       = $doc->xpath($xpath);
+                                                               foreach ($includes as $include){
+                                                                       $include        = simplexml_load_string('<root1><root2>'.$include->asXML().'</root2></root1>'); // don't ask.
+                                                                       $tmp            = $this->processNodes($include, $this->classes, false);
+                                                                       if (is_array($tmp)) $data = array_merge($data, $tmp);
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                               $out[]  = $data;
+                       }
+                       
+                       if (sizeof($out) > 1){
+                               return $out;
+                       }else if (isset($data)){
+                               return $data;
+                       }else{
+                               return array();
+                       }
+               }
+
+
+               private function getNodeValue($node, $className)
+               {
+
+                       $tag_name       = strtoupper(dom_import_simplexml($node)->tagName);
+                       $s                      = false;
+                       
+                       // ignore DEL tags
+                       if ($tag_name == 'DEL') return $s;
+                       
+                       // look up att map values
+                       if (array_key_exists($className, $this->att_map)){
+                               
+                               foreach ($this->att_map[$className] as $map){                                   
+                                       if (preg_match("/$tag_name\|/", $map)){
+                                               $s      = ''.$node[array_pop($foo = explode('|', $map))];
+                                       }
+                               }
+                       }
+                       
+                       // if nothing and OBJ, try data.
+                       if (!$s && $tag_name=='OBJECT' && $node['data'])        $s      = ''.$node['data'];
+                       
+                       // if nothing and IMG, try alt.
+                       if (!$s && $tag_name=='IMG' && $node['alt'])    $s      = ''.$node['alt'];
+                       
+                       // if nothing and AREA, try alt.
+                       if (!$s && $tag_name=='AREA' && $node['alt'])   $s      = ''.$node['alt'];
+                       
+                       //if nothing and not A, try title.
+                       if (!$s && $tag_name!='A' && $node['title'])    $s      = ''.$node['title'];
+                               
+                       
+                       // if nothing found, go with node text
+                       $s      = ($s ? $s : implode(array_filter($node->xpath('child::node()'), array(&$this, "filterBlankValues")), ' '));                    
+
+                       // callbacks                    
+                       if (array_key_exists($className, $this->callbacks)){
+                               $s      = preg_replace_callback('/.*/', $this->callbacks[$className], $s, 1);
+                       }
+                       
+                       // trim and remove line breaks
+                       if ($tag_name != 'PRE'){
+                               $s      = trim(preg_replace('/[\r\n\t]+/', '', $s));
+                               $s      = trim(preg_replace('/(\s{2})+/', ' ', $s));
+                       }
+                       
+                       return $s;
+               }
+
+               private function filterBlankValues($s){
+                       return preg_match("/\w+/", $s);
+               }
+               
+               
+               private function tidyThis($source)
+               {
+                       switch ( $this->tidy_mode )
+                       {
+                               case 'exec':
+                                       $tmp_file       = $this->tmp_dir.md5($source).'.txt';
+                                       file_put_contents($tmp_file, $source);
+                                       exec("tidy -utf8 -indent -asxhtml -numeric -bare -quiet $tmp_file", $tidy);
+                                       unlink($tmp_file);
+                                       return implode("\n", $tidy);
+                               break;
+                               
+                               case 'php':
+                                       $tidy   = tidy_parse_string($source);
+                                       return tidy_clean_repair($tidy);
+                               break;
+                                               
+                               default:
+                                       return $source;
+                               break;
+                       }
+                       
+               }
+               
+               
+               private function loadProfile($profile)
+               {
+                       require_once("$profile.profile.php");
+               }
+               
+               
+               private function loadDoc($input_xml, $fragment=false)
+               {
+                       $xml            = simplexml_load_string($input_xml);
+                       
+                       $this->doc      = $xml;
+                       
+                       if ($fragment){
+                               $doc    = $xml->xpath("//*[@id='$fragment']");
+                               $xml    = simplexml_load_string($doc[0]->asXML());
+                               $doc    = null;
+                       }
+                       
+                       // base tag
+                       if ($xml->head->base['href']) $this->base = $xml->head->base['href'];                   
+
+                       // xml:base attribute - PITA with SimpleXML
+                       preg_match('/xml:base="(.*)"/', $xml->asXML(), $matches);
+                       if (is_array($matches) && sizeof($matches)>1) $this->base = $matches[1];
+                                                               
+                       return  $xml->xpath("//*[contains(concat(' ',normalize-space(@class),' '),' $this->root_class ')]");
+                       
+               }
+               
+               
+               private function loadURL($url)
+               {
+                       $this->url      = $url;
+                       
+                       if ($this->tidy_mode == 'proxy' && $this->tidy_proxy != ''){
+                               $url    = $this->tidy_proxy . $url;
+                       }
+               
+                       return @file_get_contents($url);
+                       
+               }
+               
+               
+               private function postProcess($profile, $s)
+               {
+                       $required       = $this->required;
+                       
+                       if (is_array($s) && array_key_exists($required[0], $s)){
+                               $s      = array($s);
+                       }
+                       
+                       $s      = $this->dedupeSingles($s);
+                       
+                       if (function_exists('hKit_'.$profile.'_post')){
+                               $s              = call_user_func('hKit_'.$profile.'_post', $s);
+                       }
+                       
+                       $s      = $this->removeTextVals($s);
+                       
+                       return $s;
+               }
+               
+               
+               private function resolvePath($filepath)
+               {       // ugly code ahoy: needs a serious tidy up
+                                       
+                       $filepath       = $filepath[0];
+                       
+                       $base   = $this->base;
+                       $url    = $this->url;
+                       
+                       if ($base != '' &&  strpos($base, '://') !== false)
+                               $url    = $base;
+                       
+                       $r              = parse_url($url);
+                       $domain = $r['scheme'] . '://' . $r['host'];
+
+                       if (!isset($r['path'])) $r['path'] = '/';
+                       $path   = explode('/', $r['path']);
+                       $file   = explode('/', $filepath);
+                       $new    = array('');
+
+                       if (strpos($filepath, '://') !== false || strpos($filepath, 'data:') !== false){
+                               return $filepath;
+                       }
+
+                       if ($file[0] == ''){
+                               // absolute path
+                               return ''.$domain . implode('/', $file);
+                       }else{
+                               // relative path
+                               if ($path[sizeof($path)-1] == '') array_pop($path);
+                               if (strpos($path[sizeof($path)-1], '.') !== false) array_pop($path);
+
+                               foreach ($file as $segment){
+                                       if ($segment == '..'){
+                                               array_pop($path);
+                                       }else{
+                                               $new[]  = $segment;
+                                       }
+                               }
+                               return ''.$domain . implode('/', $path) . implode('/', $new);
+                       }       
+               }
+               
+               private function resolveEmail($v)
+               {
+                       $parts  = parse_url($v[0]);
+                       return ($parts['path']);
+               }
+               
+               
+               private function dedupeSingles($s)
+               {
+                       $singles        = $this->singles;
+                       
+                       foreach ($s as &$item){
+                               foreach ($singles as $classname){
+                                       if (array_key_exists($classname, $item) && is_array($item[$classname])){
+                                               if (isset($item[$classname][0])) $item[$classname]      = $item[$classname][0];
+                                       }
+                               }
+                       }
+                       
+                       return $s;
+               }
+               
+               private function removeTextVals($s)
+               {
+                       foreach ($s as $key => &$val){
+                               if ($key){
+                                       $k = $key;
+                               }else{
+                                       $k = '';
+                               }
+                               
+                               if (is_array($val)){
+                                       $val = $this->removeTextVals($val);
+                               }else{
+                                       if ($k == 'text'){
+                                               $val = '';
+                                       }
+                               }
+                       }
+                       
+                       return array_filter($s);
+               }
+
+       }
+
+
+?>
\ No newline at end of file
diff --git a/plugins/OStatus/lib/discovery.php b/plugins/OStatus/lib/discovery.php
new file mode 100644 (file)
index 0000000..388df0a
--- /dev/null
@@ -0,0 +1,310 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * A sample module to show best practices for StatusNet plugins
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   StatusNet
+ * @author    James Walker <james@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+/**
+ * This class implements LRDD-based service discovery based on the "Hammer Draft"
+ * (including webfinger)
+ *
+ * @see http://groups.google.com/group/webfinger/browse_thread/thread/9f3d93a479e91bbf
+ */
+class Discovery
+{
+
+    const LRDD_REL = 'lrdd';
+    const PROFILEPAGE = 'http://webfinger.net/rel/profile-page';
+    const UPDATESFROM = 'http://schemas.google.com/g/2010#updates-from';
+    const HCARD = 'http://microformats.org/profile/hcard';
+    
+    public $methods = array();
+
+    public function __construct()
+    {
+        $this->registerMethod('Discovery_LRDD_Host_Meta');
+        $this->registerMethod('Discovery_LRDD_Link_Header');
+        $this->registerMethod('Discovery_LRDD_Link_HTML');
+    }
+
+
+    public function registerMethod($class)
+    {
+        $this->methods[] = $class;
+    }
+    
+    /**
+     * Given a "user id" make sure it's normalized to either a webfinger
+     * acct: uri or a profile HTTP URL.
+     */
+    public static function normalize($user_id)
+    {
+        if (substr($user_id, 0, 5) == 'http:' ||
+            substr($user_id, 0, 6) == 'https:' ||
+            substr($user_id, 0, 5) == 'acct:') {
+            return $user_id;
+        }
+
+        if (strpos($user_id, '@') !== FALSE) {
+            return 'acct:' . $user_id;
+        }
+
+        return 'http://' . $user_id;
+    }
+
+    public static function isWebfinger($user_id)
+    {
+        $uri = Discovery::normalize($user_id);
+        
+        return (substr($uri, 0, 5) == 'acct:');
+    }
+
+    /**
+     * This implements the actual lookup procedure
+     */
+    public function lookup($id)
+    {
+        // Normalize the incoming $id to make sure we have a uri
+        $uri = $this->normalize($id);
+
+        foreach ($this->methods as $class) {
+            $links = call_user_func(array($class, 'discover'), $uri);
+            if ($link = Discovery::getService($links, Discovery::LRDD_REL)) {
+                // Load the LRDD XRD
+                if ($link['template']) {
+                    $xrd_uri = Discovery::applyTemplate($link['template'], $uri);
+                } else {
+                    $xrd_uri = $link['href'];
+                }
+                
+                $xrd = $this->fetchXrd($xrd_uri);
+                if ($xrd) {
+                    return $xrd;
+                }
+            }
+        }
+
+        throw new Exception('Unable to find services for '. $id);
+    }
+
+    public static function getService($links, $service) {
+        if (!is_array($links)) {
+            return false;
+        }
+        
+        foreach ($links as $link) {
+            if ($link['rel'] == $service) {
+                return $link;
+            }
+        }
+    }
+    
+
+    public static function applyTemplate($template, $id)
+    {
+        $template = str_replace('{uri}', urlencode($id), $template);
+
+        return $template;
+    }
+
+    
+    public static function fetchXrd($url)
+    {
+        try {
+            $client = new HTTPClient();
+            $response = $client->get($url);
+        } catch (HTTP_Request2_Exception $e) {
+            return false;
+        }
+
+        if ($response->getStatus() != 200) {
+            return false;
+        }
+
+        return XRD::parse($response->getBody());
+    }
+}
+
+interface Discovery_LRDD
+{
+    public function discover($uri);
+}
+
+class Discovery_LRDD_Host_Meta implements Discovery_LRDD
+{
+    public function discover($uri)
+    {
+        if (!Discovery::isWebfinger($uri)) {
+            return false;
+        }
+
+        // We have a webfinger acct: - start with host-meta
+        list($name, $domain) = explode('@', $uri);
+        $url = 'http://'. $domain .'/.well-known/host-meta';
+
+        $xrd = Discovery::fetchXrd($url);
+
+        if ($xrd) {
+            if ($xrd->host != $domain) {
+                return false;
+            }
+            
+            return $xrd->links;
+        }
+    }
+}
+
+class Discovery_LRDD_Link_Header implements Discovery_LRDD
+{
+    public function discover($uri)
+    {
+        try {
+            $client = new HTTPClient();
+            $response = $client->get($uri);
+        } catch (HTTP_Request2_Exception $e) {
+            return false;
+        }
+             
+        if ($response->getStatus() != 200) {
+            return false;
+        }
+
+        $link_header = $response->getHeader('Link');
+        if (!$link_header) {
+            //            return false;
+        }
+        
+        return Discovery_LRDD_Link_Header::parseHeader($link_header);
+    }
+
+    protected static function parseHeader($header)
+    {
+        preg_match('/^<[^>]+>/', $header, $uri_reference);
+        //if (empty($uri_reference)) return;
+
+        $links = array();
+        
+        $link_uri = trim($uri_reference[0], '<>');
+        $link_rel = array();
+        $link_type = null;
+        
+        // remove uri-reference from header
+        $header = substr($header, strlen($uri_reference[0]));
+        
+        // parse link-params
+        $params = explode(';', $header);
+        
+        foreach ($params as $param) {
+            if (empty($param)) continue;
+            list($param_name, $param_value) = explode('=', $param, 2);
+            $param_name = trim($param_name);
+            $param_value = preg_replace('(^"|"$)', '', trim($param_value));
+            
+            // for now we only care about 'rel' and 'type' link params
+            // TODO do something with the other links-params
+            switch ($param_name) {
+            case 'rel':
+                $link_rel = trim($param_value);
+                break;
+                
+            case 'type':
+                $link_type = trim($param_value);
+            }
+        }
+
+        $links[] =  array(
+            'href' => $link_uri,
+            'rel' => $link_rel,
+            'type' => $link_type);
+
+        return $links;
+    }
+}
+
+class Discovery_LRDD_Link_HTML implements Discovery_LRDD
+{
+    public function discover($uri)
+    {
+        try {
+            $client = new HTTPClient();
+            $response = $client->get($uri);
+        } catch (HTTP_Request2_Exception $e) {
+            return false;
+        }
+
+        if ($response->getStatus() != 200) {
+            return false;
+        }
+
+        return Discovery_LRDD_Link_HTML::parse($response->getBody());
+    }
+
+
+    public function parse($html)
+    {
+        $links = array();
+        
+        preg_match('/<head(\s[^>]*)?>(.*?)<\/head>/is', $html, $head_matches);
+        $head_html = $head_matches[2];
+        
+        preg_match_all('/<link\s[^>]*>/i', $head_html, $link_matches);
+        
+        foreach ($link_matches[0] as $link_html) {
+            $link_url = null;
+            $link_rel = null;
+            $link_type = null;
+            
+            preg_match('/\srel=(("|\')([^\\2]*?)\\2|[^"\'\s]+)/i', $link_html, $rel_matches);
+            if ( isset($rel_matches[3]) ) {
+                $link_rel = $rel_matches[3];
+            } else if ( isset($rel_matches[1]) ) {
+                $link_rel = $rel_matches[1];
+            }
+            
+            preg_match('/\shref=(("|\')([^\\2]*?)\\2|[^"\'\s]+)/i', $link_html, $href_matches);
+            if ( isset($href_matches[3]) ) {
+                $link_uri = $href_matches[3];
+            } else if ( isset($href_matches[1]) ) {
+                $link_uri = $href_matches[1];
+            }
+            
+            preg_match('/\stype=(("|\')([^\\2]*?)\\2|[^"\'\s]+)/i', $link_html, $type_matches);
+            if ( isset($type_matches[3]) ) {
+                $link_type = $type_matches[3];
+            } else if ( isset($type_matches[1]) ) {
+                $link_type = $type_matches[1];
+            }
+            
+            $links[] = array(
+                'href' => $link_url,
+                'rel' => $link_rel,
+                'type' => $link_type,
+            );
+        }
+        
+        return $links;
+    }
+}
index 81f4609c5cd94a138ab795381486c967f0e00913..457c0fba221a269695c3b97f81ad7c3159e28182 100644 (file)
@@ -50,7 +50,20 @@ class MagicEnvelope
 
     public function getKeyPair($signer_uri)
     {
-        return 'RSA.79_L2gq-TD72Nsb5yGS0r9stLLpJZF5AHXyxzWmQmlqKl276LEJEs8CppcerLcR90MbYQUwt-SX9slx40Yq3vA==.AQAB.AR-jo5KMfSISmDAT2iMs2_vNFgWRjl5rbJVvA0SpGIEWyPdCGxlPtCbTexp8-0ZEIe8a4SyjatBECH5hxgMTpw==';
+        $disco = new Discovery();
+
+        try {
+            $xrd = $disco->lookup($signer_uri);
+        } catch (Exception $e) {
+            return false;
+        }
+        if ($xrd->links) {
+            if ($link = Discovery::getService($xrd->links, Magicsig::PUBLICKEYREL)) {
+                list($type, $keypair) = explode(';', $link['href']);
+                return $keypair;
+            }
+        }
+        throw new Exception('Unable to locate signer public key');
     }
 
 
@@ -59,10 +72,14 @@ class MagicEnvelope
         $signer_uri = $this->normalizeUser($signer_uri);
 
         if (!$this->checkAuthor($text, $signer_uri)) {
-            return false;
+            throw new Exception("Unable to determine entry author.");
         }
 
-        $signature_alg = Magicsig::fromString($this->getKeyPair($signer_uri));
+        $keypair = $this->getKeyPair($signer_uri);
+        if (!$keypair) {
+            throw new Exception("Unable to retrive keypair for ". $signer_uri);
+        }
+        $signature_alg = Magicsig::fromString($keypair);
         $armored_text = base64_encode($text);
 
         return array(
index a90f52df26fce37b93b7155ad9a217c343aa1481..1fd29ae301b9203595016b4210550260507efde8 100644 (file)
@@ -40,7 +40,11 @@ class PushInQueueHandler extends QueueHandler
 
         $feedsub = FeedSub::staticGet('id', $feedsub_id);
         if ($feedsub) {
-            $feedsub->receive($post, $hmac);
+            try {
+                $feedsub->receive($post, $hmac);
+            } catch(Exception $e) {
+                common_log(LOG_ERR, "Exception during PuSH input processing for $feedsub->uri: " . $e->getMessage());
+            }
         } else {
             common_log(LOG_ERR, "Discarding POST to unknown feed subscription id $feedsub_id");
         }
index b5f178cc6a2e4cec2f191dad33d96ed39aa9a4ad..9d4359f74f5e5c875b7faabd6d7a823bdeb7f013 100644 (file)
@@ -72,8 +72,12 @@ class Salmon
         // TODO: Should probably be getting the signer uri as an argument?
         $signer_uri = $magic_env->getAuthor($text);
 
-        $env = $magic_env->signMessage($text, 'application/atom+xml', $signer_uri);
-
+        try {
+            $env = $magic_env->signMessage($text, 'application/atom+xml', $signer_uri);
+        } catch (Exception $e) {
+            common_log(LOG_ERR, "Salmon signing failed: ". $e->getMessage());
+            return $text;
+        }
         return $magic_env->unfold($env);
     }
 
diff --git a/plugins/OStatus/lib/webfinger.php b/plugins/OStatus/lib/webfinger.php
deleted file mode 100644 (file)
index 8a50376..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * A sample module to show best practices for StatusNet plugins
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @package   StatusNet
- * @author    James Walker <james@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-define('WEBFINGER_SERVICE_REL_VALUE', 'lrdd');
-
-/**
- * Implement the webfinger protocol.
- */
-
-class Webfinger
-{
-    const PROFILEPAGE = 'http://webfinger.net/rel/profile-page';
-    const UPDATESFROM = 'http://schemas.google.com/g/2010#updates-from';
-
-    /**
-     * Perform a webfinger lookup given an account.
-     */
-
-    public function lookup($id)
-    {
-        $id = $this->normalize($id);
-        list($name, $domain) = explode('@', $id);
-
-        $links = $this->getServiceLinks($domain);
-        if (!$links) {
-            return false;
-        }
-
-        $services = array();
-        foreach ($links as $link) {
-            if ($link['template']) {
-                return $this->getServiceDescription($link['template'], $id);
-            }
-            if ($link['href']) {
-                return $this->getServiceDescription($link['href'], $id);
-            }
-        }
-    }
-
-    /**
-     * Normalize an account ID
-     */
-    function normalize($id)
-    {
-        if (substr($id, 0, 7) == 'acct://') {
-            return substr($id, 7);
-        } else if (substr($id, 0, 5) == 'acct:') {
-            return substr($id, 5);
-        }
-
-        return $id;
-    }
-
-    function getServiceLinks($domain)
-    {
-        $url = 'http://'. $domain .'/.well-known/host-meta';
-        $content = $this->fetchURL($url);
-        if (empty($content)) {
-            common_log(LOG_DEBUG, 'Error fetching host-meta');
-            return false;
-        }
-        $result = XRD::parse($content);
-
-        // Ensure that the host == domain (spec may include signing later)
-        if ($result->host != $domain) {
-            return false;
-        }
-
-        $links = array();
-        foreach ($result->links as $link) {
-            if ($link['rel'] == WEBFINGER_SERVICE_REL_VALUE) {
-                $links[] = $link;
-            }
-
-        }
-        return $links;
-    }
-
-    function getServiceDescription($template, $id)
-    {
-        $url = $this->applyTemplate($template, 'acct:' . $id);
-
-        $content = $this->fetchURL($url);
-
-        if (!$content) {
-            return false;
-        }
-
-        return XRD::parse($content);
-    }
-
-    function fetchURL($url)
-    {
-        try {
-            $client = new HTTPClient();
-            $response = $client->get($url);
-        } catch (HTTP_Request2_Exception $e) {
-            return false;
-        }
-
-        if ($response->getStatus() != 200) {
-            return false;
-        }
-
-        return $response->getBody();
-    }
-
-    function applyTemplate($template, $id)
-    {
-        $template = str_replace('{uri}', urlencode($id), $template);
-
-        return $template;
-    }
-
-    function getHostMeta($domain, $template) {
-        $xrd = new XRD();
-        $xrd->host = $domain;
-        $xrd->links[] = array('rel' => 'lrdd',
-                              'template' => $template,
-                              'title' => array('Resource Descriptor'));
-
-        return $xrd->toXML();
-    }
-}
-
index e8c44a743ad2ac119bec1d8d9376367c338bc13d..b559d80c605a540aa333299fb3e25deb998a0f35 100644 (file)
@@ -244,8 +244,6 @@ class RealtimePlugin extends Plugin
         // of refactoring from within a plugin, so I'm just abusing
         // the ApiAction method. Don't do this unless you're me!
 
-        require_once(INSTALLDIR.'/lib/api.php');
-
         $act = new ApiAction('/dev/null');
 
         $arr = $act->twitterStatusArray($notice, true);
diff --git a/plugins/Realtime/icon_external.gif b/plugins/Realtime/icon_external.gif
deleted file mode 100644 (file)
index c4118d5..0000000
Binary files a/plugins/Realtime/icon_external.gif and /dev/null differ
diff --git a/plugins/Realtime/icon_pause.gif b/plugins/Realtime/icon_pause.gif
deleted file mode 100644 (file)
index ced0b64..0000000
Binary files a/plugins/Realtime/icon_pause.gif and /dev/null differ
diff --git a/plugins/Realtime/icon_play.gif b/plugins/Realtime/icon_play.gif
deleted file mode 100644 (file)
index 794ec85..0000000
Binary files a/plugins/Realtime/icon_play.gif and /dev/null differ
index 31e7c2ae6638c4b49537022c1b25f54d1230b6ca..f43c97de52e71bb8053b18ffaf38ed70899a6e9b 100644 (file)
@@ -64,18 +64,9 @@ float: left;
 }
 
 #realtime_play {
-background: url(icon_play.gif) no-repeat 47% 47%;
 margin-left: 4px;
 }
 
-#realtime_pause {
-background: url(icon_pause.gif) no-repeat 47% 47%;
-}
-
-#realtime_popup {
-background: url(icon_external.gif) no-repeat 0 30%;
-}
-
 #queued_counter {
 float:left;
 line-height:1.2;
diff --git a/scripts/init_conversation.php b/scripts/init_conversation.php
new file mode 100755 (executable)
index 0000000..675e7ca
--- /dev/null
@@ -0,0 +1,49 @@
+#!/usr/bin/env php
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+common_log(LOG_INFO, 'Initializing conversation table...');
+
+$notice = new Notice();
+$notice->query('select distinct conversation from notice');
+
+while ($notice->fetch()) {
+    $id = $notice->conversation;
+
+    if ($id) {
+        $uri = common_local_url('conversation', array('id' => $id));
+
+        // @fixme db_dataobject won't save our value for an autoincrement
+        // so we're bypassing the insert wrappers
+        $conv = new Conversation();
+        $sql = "insert into conversation (id,uri,created) values(%d,'%s','%s')";
+        $sql = sprintf($sql,
+                       $id,
+                       $conv->escape($uri),
+                       $conv->escape(common_sql_now()));
+        echo "$id ";
+        $conv->query($sql);
+        print "... ";
+    }
+}
+print "done.\n";
index 617c2e24c744233990b651010bb09fe45138d1ca..3b6681bae907a952b7314ad80f02a4bb24ac99db 100644 (file)
@@ -94,11 +94,11 @@ function updateAvatars($user)
                 }
             }
 
-            $orig = clone($avatar);
+            $orig_url = $avatar->url;
 
             $avatar->url = Avatar::url($avatar->filename);
 
-            if ($avatar->url != $orig->url) {
+            if ($avatar->url != $orig_url) {
                 $sql =
                   "UPDATE avatar SET url = '" . $avatar->url . "' ".
                   "WHERE profile_id = " . $avatar->profile_id . " ".
diff --git a/scripts/updateavatarurl_group.php b/scripts/updateavatarurl_group.php
new file mode 100644 (file)
index 0000000..ada42de
--- /dev/null
@@ -0,0 +1,99 @@
+#!/usr/bin/env php
+<?php
+/*
+ * StatusNet - a distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
+
+$shortoptions = 'i:n:a';
+$longoptions = array('id=', 'nickname=', 'all');
+
+$helptext = <<<END_OF_UPDATEAVATARURL_HELP
+updateavatarurl_group.php [options]
+update the URLs of all group avatars in the system
+
+  -i --id       ID of group to update
+  -n --nickname nickname of the group to update
+  -a --all      update all
+
+END_OF_UPDATEAVATARURL_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+try {
+    $user = null;
+
+    if (have_option('i', 'id')) {
+        $id = get_option_value('i', 'id');
+        $group = User_group::staticGet('id', $id);
+        if (empty($group)) {
+            throw new Exception("Can't find group with id '$id'.");
+        }
+        updateGroupAvatars($group);
+    } else if (have_option('n', 'nickname')) {
+        $nickname = get_option_value('n', 'nickname');
+        $group = User_group::staticGet('nickname', $nickname);
+        if (empty($group)) {
+            throw new Exception("Can't find group with nickname '$nickname'");
+        }
+        updateGroupAvatars($group);
+    } else if (have_option('a', 'all')) {
+        $group = new User_group();
+        if ($group->find()) {
+            while ($group->fetch()) {
+                updateGroupAvatars($group);
+            }
+        }
+    } else {
+        show_help();
+        exit(1);
+    }
+} catch (Exception $e) {
+    print $e->getMessage()."\n";
+    exit(1);
+}
+
+function updateGroupAvatars($group)
+{
+    if (!have_option('q', 'quiet')) {
+        print "Updating avatars for group '".$group->nickname."' (".$group->id.")...";
+    }
+
+    if (empty($group->original_logo)) {
+        print "(none found)...";
+    } else {
+        // Using clone here was screwing up the group->find() iteration
+        $orig = User_group::staticGet('id', $group->id);
+
+        $group->original_logo = Avatar::url(basename($group->original_logo));
+        $group->homepage_logo = Avatar::url(basename($group->homepage_logo));
+        $group->stream_logo = Avatar::url(basename($group->stream_logo));
+        $group->mini_logo = Avatar::url(basename($group->mini_logo));
+
+        if (!$group->update($orig)) {
+            throw new Exception("Can't update avatars for group " . $group->nickname . ".");
+        }
+    }
+
+    if (have_option('v', 'verbose')) {
+        print "DONE.";
+    }
+    if (!have_option('q', 'quiet') || have_option('v', 'verbose')) {
+        print "\n";
+    }
+}
index 5de97d2e2ed2117e157fdc16ab93fd97381a7ad1..7bf9cec7c453d57e048b15ec2671ba31c602faf2 100644 (file)
@@ -121,10 +121,14 @@ class ActivityParseTests extends PHPUnit_Framework_TestCase
         $this->assertEquals($act->actor->title, 'Test User');
         $this->assertEquals($act->actor->id, 'http://example.net/mysite/user/3');
         $this->assertEquals($act->actor->link, 'http://example.net/mysite/testuser');
+
+        $avatars = $act->actor->avatarLinks;
+
         $this->assertEquals(
-            $act->actor->avatar,
-            'http://example.net/mysite/avatar/3-96-20100224004207.jpeg'
+                $avatars[0]->url,
+                'http://example.net/mysite/avatar/3-96-20100224004207.jpeg'
         );
+
         $this->assertEquals($act->actor->displayName, 'Test User');
 
         $poco = $act->actor->poco;
@@ -133,6 +137,8 @@ class ActivityParseTests extends PHPUnit_Framework_TestCase
         $this->assertEquals($poco->urls[0]->type, 'homepage');
         $this->assertEquals($poco->urls[0]->value, 'http://example.com/blog.html');
         $this->assertEquals($poco->urls[0]->primary, 'true');
+        $this->assertEquals($act->actor->geopoint, '37.7749295 -122.4194155');
+
     }
 
 }
diff --git a/theme/base/images/icons/README b/theme/base/images/icons/README
new file mode 100644 (file)
index 0000000..ea58214
--- /dev/null
@@ -0,0 +1,54 @@
+/**
+ * @author    Paul Jarvis http://code.google.com/p/twotiny/
+ * @license   http://dev.perl.org/licenses/ Artistic License/GPL
+ * @note
+        White left arrow with green background
+        White right arrow with green background
+        White clip with green background
+        White heart with green background
+        White reply with green background
+        White garbage with green background
+        White pencil with green background
+        White envelope with green background
+        White speech bubble with green background
+        White shield with green background
+        White asterisk with green background
+        White x with green background
+        White plus with green background
+        White minus with green background
+        White skull with green background
+        White recycle with green background
+        White external with green background
+        White key with green background
+        White flag with green background
+        White checkmark with green background
+        White reject with green background
+        White play with green background
+        White pause with green background
+ */
+
+
+/**
+ * @author    Sarven Capadisli <csarven@status.net>
+ * @copyright 2008-2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ * @note
+        Green clip with transparent background
+        Green heart with white background
+        White person with tie with green background
+        White sherif badge with green background
+        White boxes with green background
+        White speech bubble broken with green background
+        Green recycle with transparent background
+        Green pin with white background
+        White pin with green background
+        White underscore with green background
+        White C with green background
+ */
+
+Created by various authors
+* FOAF icon from http://iandavis.com/2006/foaf-icons/ with Public Domain license
+* Atom feed icon from http://intertwingly.net/wiki/pie/Icon with Public Domain license
+* RSS feed icon from http://www.feedicons.com/ (Mozilla, Microsoft, Matt Brett) with MPL/GPL/LGPL tri-license
+* Processing icon from/by Unknown with Unknown license //FIXME
diff --git a/theme/base/images/icons/icon_geo.png b/theme/base/images/icons/icon_geo.png
deleted file mode 100644 (file)
index 8df2456..0000000
Binary files a/theme/base/images/icons/icon_geo.png and /dev/null differ
index 6f284f023ee7c7c1fef480c26e7ccfd2b27ba32c..be884ff48944d2ec4694f426ada7644b0aa0e101 100644 (file)
Binary files a/theme/base/images/icons/icons-01.gif and b/theme/base/images/icons/icons-01.gif differ
index 726062e473f17b2b5c06c2803835800b3c4f0518..285c2ad836f88615faea0d1f8434c8358050bd25 100644 (file)
@@ -1628,15 +1628,23 @@ button.close,
 .form_user_unsubscribe input.submit,
 .form_group_join input.submit,
 .form_user_subscribe input.submit,
+.form_remote_authorize input.submit,
 .entity_subscribe a,
 .entity_moderation p,
 .entity_sandbox input.submit,
 .entity_silence input.submit,
 .entity_delete input.submit,
 .notice-options .repeated,
-.form_notice a#notice_data-geo_name,
 .form_notice label[for=notice_data-geo],
-button.minimize {
+button.minimize,
+.form_reset_key input.submit,
+.entity_clear input.submit,
+.entity_flag input.submit,
+.entity_flag p,
+.entity_subscribe input.submit,
+#realtime_play,
+#realtime_pause,
+#realtime_popup {
 background-image:url(../../base/images/icons/icons-01.gif);
 background-repeat:no-repeat;
 background-color:transparent;
@@ -1899,6 +1907,31 @@ background-position: 5px -1445px;
 .entity_delete input.submit {
 background-position: 5px -1511px;
 }
+.form_reset_key input.submit {
+background-position: 5px -1973px;
+}
+.entity_clear input.submit {
+background-position: 5px -2039px;
+}
+.entity_flag input.submit,
+.entity_flag p {
+background-position: 5px -2105px;
+}
+.entity_subscribe input.accept {
+background-position: 5px -2171px;
+}
+.entity_subscribe input.reject {
+background-position: 5px -2237px;
+}
+#realtime_play {
+background-position: 0 -2308px;
+}
+#realtime_pause {
+background-position: 0 -2374px;
+}
+#realtime_popup {
+background-position: 0 -1714px;
+}
 
 
 /* NOTICES */
index deb985909e8cc05e8e334d6ee53230fadb5debd7..8ae2b4014199ef77d605a0f3c89e75b98bce7e81 100644 (file)
@@ -197,7 +197,10 @@ button.minimize,
 .entity_clear input.submit,
 .entity_flag input.submit,
 .entity_flag p,
-.entity_subscribe input.submit {
+.entity_subscribe input.submit,
+#realtime_play,
+#realtime_pause,
+#realtime_popup {
 background-image:url(../../base/images/icons/icons-01.gif);
 background-repeat:no-repeat;
 background-color:transparent;
@@ -363,6 +366,16 @@ background-position: 5px -2171px;
 .entity_subscribe input.reject {
 background-position: 5px -2237px;
 }
+#realtime_play {
+background-position: 0 -2308px;
+}
+#realtime_pause {
+background-position: 0 -2374px;
+}
+#realtime_popup {
+background-position: 0 -1714px;
+}
+
 
 /* NOTICES */
 .notice .attachment {
index 0e54d82e2f94d6df3162f8a71a71b8852e1dbd41..737e3a10326d13fd1db8d7de858c49252759c367 100644 (file)
@@ -81,7 +81,8 @@ background-color:transparent;
 input:focus, textarea:focus, select:focus,
 .form_notice.warning #notice_data-text,
 .form_notice.warning #notice_text-count,
-.form_settings .form_note {
+.form_settings .form_note,
+.entity_actions .dialogbox .form_data input:focus {
 border-color:#9BB43E;
 }
 input.submit {
@@ -197,7 +198,10 @@ button.minimize,
 .entity_clear input.submit,
 .entity_flag input.submit,
 .entity_flag p,
-.entity_subscribe input.submit {
+.entity_subscribe input.submit,
+#realtime_play,
+#realtime_pause,
+#realtime_popup {
 background-image:url(../../base/images/icons/icons-01.gif);
 background-repeat:no-repeat;
 background-color:transparent;
@@ -362,6 +366,15 @@ background-position: 5px -2171px;
 .entity_subscribe input.reject {
 background-position: 5px -2237px;
 }
+#realtime_play {
+background-position: 0 -2308px;
+}
+#realtime_pause {
+background-position: 0 -2374px;
+}
+#realtime_popup {
+background-position: 0 -1714px;
+}
 
 /* NOTICES */
 .notice .attachment {
diff --git a/theme/otalk/css/base.css b/theme/otalk/css/base.css
deleted file mode 100644 (file)
index 8af86f9..0000000
+++ /dev/null
@@ -1,1211 +0,0 @@
-/** theme: otalk base
- *
- * @package   StatusNet
- * @author Sarven Capadisli <csarven@status.net>
- * @copyright 2009 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-* { margin:0; padding:0; }
-img { display:block; border:0; }
-a abbr { cursor: pointer; border-bottom:0; }
-table { border-collapse:collapse; }
-ol { list-style-position:inside; }
-html { font-size: 87.5%; background-color:#fff; }
-body {
-background-color:#fff;
-color:#000;
-font-family:sans-serif;
-font-size:1em;
-line-height:1.65;
-position:relative;
-}
-h1,h2,h3,h4,h5,h6 {
-margin-bottom:7px;
-overflow:hidden;
-}
-h1 {
-font-size:1.4em;
-margin-bottom:18px;
-}
-#showstream h1 { display:none; }
-h2 { font-size:1.3em; }
-h3 { font-size:1.2em; }
-h4 { font-size:1.1em; }
-h5 { font-size:1em; }
-h6 { font-size:0.9em; }
-
-caption {
-font-weight:bold;
-}
-legend {
-font-weight:bold;
-font-size:1.3em;
-}
-input, textarea, select, option {
-padding:4px;
-font-family:sans-serif;
-font-size:1em;
-}
-input, textarea, select {
-border-width:2px;
-border-style: solid;
-border-radius:4px;
--moz-border-radius:4px;
--webkit-border-radius:4px;
-}
-
-input.submit {
-font-weight:bold;
-cursor:pointer;
-}
-textarea {
-overflow:auto;
-}
-option {
-padding-bottom:0;
-}
-fieldset {
-padding:0;
-border:0;
-}
-form ul li {
-list-style-type:none;
-margin:0 0 18px 0;
-}
-form label {
-font-weight:bold;
-}
-input.checkbox {
-position:relative;
-top:2px;
-left:0;
-border:0;
-}
-
-.error,
-.success {
-padding:4px 7px;
-border-radius:4px;
--moz-border-radius:4px;
--webkit-border-radius:4px;
-margin-bottom:18px;
-}
-form label.submit {
-display:none;
-}
-
-.form_settings {
-clear:both;
-}
-
-.form_settings fieldset {
-margin-bottom:29px;
-}
-.form_settings input.remove {
-margin-left:11px;
-}
-.form_settings .form_data li {
-width:100%;
-float:left;
-}
-.form_settings .form_data label {
-float:left;
-}
-.form_settings .form_data textarea,
-.form_settings .form_data select,
-.form_settings .form_data input {
-margin-left:11px;
-float:left;
-}
-.form_settings .form_data input.submit {
-margin-left:0;
-}
-
-.form_settings label {
-margin-top:2px;
-width:152px;
-}
-
-.form_actions label {
-display:none;
-}
-.form_guide {
-font-style:italic;
-}
-
-.form_settings #settings_autosubscribe label {
-display:inline;
-font-weight:bold;
-}
-
-#form_settings_profile legend,
-#form_login legend,
-#form_register legend,
-#form_password legend,
-#form_settings_avatar legend,
-#newgroup legend,
-#editgroup legend,
-#form_tag_user legend,
-#form_remote_subscribe legend,
-#form_openid_login legend,
-#form_search legend,
-#form_invite legend,
-#form_notice_delete legend,
-#form_password_recover legend,
-#form_password_change legend {
-display:none;
-}
-
-.form_settings .form_data p.form_guide {
-clear:both;
-margin-left:163px;
-margin-bottom:0;
-}
-
-.form_settings p {
-margin-bottom:11px;
-}
-
-.form_settings input.checkbox {
-margin-top:3px;
-margin-left:0;
-}
-.form_settings label.checkbox {
-font-weight:normal;
-margin-top:0;
-margin-right:0;
-margin-left:11px;
-float:left;
-width:90%;
-}
-
-
-#form_login p.form_guide,
-#form_register #settings_rememberme p.form_guide,
-#form_openid_login #settings_rememberme p.form_guide,
-#settings_twitter_remove p.form_guide,
-#form_search ul.form_data #q {
-margin-left:0;
-}
-
-.form_settings .form_note {
-border-radius:4px;
--moz-border-radius:4px;
--webkit-border-radius:4px;
-padding:0 7px;
-}
-
-
-.form_settings input.form_action-primary {
-padding:0;
-}
-.form_settings input.form_action-secondary {
-margin-left:29px;
-}
-
-#form_search .submit {
-margin-left:11px;
-}
-
-address {
-float:left;
-margin-bottom:18px;
-margin-left:18px;
-}
-address.vcard img.logo {
-margin-right:0;
-}
-address .fn {
-font-weight:bold;
-}
-address img + .fn {
-display:none;
-}
-
-#header {
-width:100%;
-position:relative;
-float:left;
-padding-top:18px;
-margin-bottom:29px;
-}
-
-#site_nav_global_primary {
-float:right;
-margin-right:18px;
-margin-bottom:11px;
-margin-left:18px;
-}
-#site_nav_global_primary ul li {
-display:inline;
-margin-left:11px;
-}
-
-.system_notice dt {
-font-weight:bold;
-text-transform:uppercase;
-display:none;
-}
-
-#site_notice {
-position:absolute;
-top:65px;
-right:18px;
-width:250px;
-width:24%;
-}
-#page_notice {
-clear:both;
-margin-bottom:18px;
-}
-
-
-#anon_notice {
-float:left;
-width:43.2%;
-padding:1.1%;
-border-radius:7px;
--moz-border-radius:7px;
--webkit-border-radius:7px;
-border-width:2px;
-border-style:solid;
-line-height:1.5;
-font-size:1.1em;
-font-weight:bold;
-}
-
-
-#footer {
-float:left;
-width:64%;
-padding:18px;
-}
-
-#site_nav_local_views {
-float:left;
-}
-#site_nav_local_views dt {
-display:none;
-}
-#site_nav_local_views li {
-float:left;
-margin-right:18px;
-list-style-type:none;
-}
-#site_nav_local_views a {
-float:left;
-text-decoration:none;
-padding:4px 11px;
--moz-border-radius-topleft:4px;
--moz-border-radius-topright:4px;
--webkit-border-top-left-radius:4px;
--webkit-border-top-right-radius:4px;
-border-width:0;
-border-style:solid;
-border-bottom:0;
-text-shadow: 2px 2px 2px #ddd;
-font-weight:bold;
-}
-#site_nav_local_views .nav {
-float:left;
-width:100%;
-border-bottom-width:1px;
-border-bottom-style:solid;
-}
-
-#site_nav_global_primary dt,
-#site_nav_global_secondary dt {
-display:none;
-}
-
-#site_nav_global_secondary {
-margin-bottom:11px;
-}
-
-#site_nav_global_secondary ul li {
-display:inline;
-margin-right:11px;
-}
-#export_data li a {
-padding-left:20px;
-}
-#export_data li a.foaf {
-padding-left:30px;
-}
-#export_data li a.export_vcard {
-padding-left:28px;
-}
-
-#export_data ul {
-display:inline;
-}
-#export_data li {
-list-style-type:none;
-display:inline;
-margin-left:11px;
-}
-#export_data li:first-child {
-margin-left:0;
-}
-
-#licenses {
-font-size:0.9em;
-}
-
-#licenses dt {
-font-weight:bold;
-display:none;
-}
-#licenses dd {
-margin-bottom:11px;
-line-height:1.5;
-}
-
-#site_content_license_cc {
-margin-bottom:0;
-}
-#site_content_license_cc img {
-display:inline;
-vertical-align:top;
-margin-right:4px;
-}
-
-#wrap {
-margin:0 auto;
-width:100%;
-min-width:760px;
-max-width:1003px;
-overflow:hidden;
-}
-
-#core {
-position:relative;
-width:100%;
-float:left;
-margin-bottom:1em;
-}
-
-#content {
-width:67.9%;
-min-height:259px;
-padding-top:1.795%;
-padding-bottom:1.795%;
-float:left;
-clear:left;
-border-radius:7px;
--moz-border-radius:7px;
--moz-border-radius-topleft:0;
--webkit-border-radius:7px;
--webkit-border-top-left-radius:0;
-border-style:solid;
-border-width:0;
-margin-bottom:18px;
-}
-
-#content_inner {
-position:relative;
-width:100%;
-float:left;
-}
-
-#aside_primary {
-width:27.917%;
-min-height:259px;
-float:left;
-padding:1.795%;
-margin-left:0.385%;
-border-radius:7px;
--moz-border-radius:7px;
--webkit-border-radius:7px;
-border-width:1px;
-border-style:solid;
-}
-
-#form_notice {
-width:45.664%;
-float:left;
-position:relative;
-line-height:1;
-}
-#form_notice fieldset {
-border:0;
-padding:0;
-}
-#form_notice legend {
-display:none;
-}
-#form_notice textarea {
-float:left;
-border-radius:7px;
--moz-border-radius:7px;
--webkit-border-radius:7px;
-width:80.789%;
-height:67px;
-line-height:1.5;
-padding:7px 7px 16px 7px;
-}
-#form_notice label {
-display:block;
-float:left;
-font-size:1.3em;
-margin-bottom:7px;
-}
-#form_notice #notice_submit label {
-display:none;
-}
-#form_notice .form_note {
-position:absolute;
-top:99px;
-right:98px;
-z-index:9;
-}
-#form_notice .form_note dt {
-font-weight:bold;
-display:none;
-}
-#notice_text-count {
-font-weight:bold;
-line-height:1.15;
-padding:1px 2px;
-}
-#form_notice #notice_action-submit {
-width:14%;
-height:47px;
-padding:0;
-position:absolute;
-bottom:0;
-right:0;
-}
-#form_notice label[for=to] {
-margin-top:7px;
-}
-#form_notice select[id=to] {
-margin-bottom:7px;
-margin-left:18px;
-float:left;
-}
-
-
-/* entity_profile */
-.entity_profile {
-position:relative;
-width:521px;
-min-height:123px;
-float:left;
-margin-bottom:18px;
-margin-left:0;
-overflow:hidden;
-}
-.entity_profile dt,
-#entity_statistics dt {
-font-weight:bold;
-}
-.entity_profile dd {
-display:inline;
-}
-
-.entity_profile .entity_depiction {
-float:left;
-width:96px;
-margin-right:18px;
-margin-bottom:18px;
-}
-
-.entity_profile .entity_fn,
-.entity_profile .entity_nickname,
-.entity_profile .entity_location,
-.entity_profile .entity_url,
-.entity_profile .entity_note,
-.entity_profile .entity_tags {
-margin-left:113px;
-margin-bottom:4px;
-}
-
-.entity_profile .entity_fn,
-.entity_profile .entity_nickname {
-margin-left:11px;
-display:inline;
-font-weight:bold;
-}
-.entity_profile .entity_nickname {
-margin-left:0;
-}
-
-.entity_profile .entity_fn dd:before {
-content: "(";
-font-weight:normal;
-}
-.entity_profile .entity_fn dd:after {
-content: ")";
-font-weight:normal;
-}
-
-.entity_profile dt {
-display:none;
-}
-.entity_profile h2 {
-display:none;
-}
-/* entity_profile */
-
-
-/*entity_actions*/
-.entity_actions {
-float:left;
-margin-left:4.35%;
-max-width:25%;
-}
-.entity_actions h2 {
-display:none;
-}
-.entity_actions ul {
-list-style-type:none;
-}
-.entity_actions li {
-margin-bottom:4px;
-}
-.entity_actions li:first-child {
-border-top:0;
-}
-.entity_actions fieldset {
-border:0;
-padding:0;
-}
-.entity_actions legend {
-display:none;
-}
-
-.entity_actions input.submit {
-display:block;
-text-align:left;
-width:100%;
-}
-.entity_actions a,
-.entity_nudge p,
-.entity_remote_subscribe {
-text-decoration:none;
-font-weight:bold;
-display:block;
-}
-
-.form_user_block input.submit,
-.form_user_unblock input.submit,
-.entity_send-a-message a,
-.entity_edit a,
-.form_user_nudge input.submit,
-.entity_nudge p {
-border:0;
-padding-left:20px;
-}
-
-.entity_edit a,
-.entity_send-a-message a,
-.entity_nudge p {
-padding:4px 4px 4px 23px;
-}
-
-.entity_remote_subscribe {
-padding:4px;
-border-width:2px;
-border-style:solid;
-border-radius:4px;
--moz-border-radius:4px;
--webkit-border-radius:4px;
-}
-.entity_actions .accept {
-margin-bottom:18px;
-}
-
-.entity_tags ul {
-list-style-type:none;
-display:inline;
-}
-.entity_tags li {
-display:inline;
-margin-right:4px;
-}
-
-.aside .section {
-margin-bottom:29px;
-clear:both;
-float:left;
-width:100%;
-}
-.aside .section h2 {
-text-transform:uppercase;
-font-size:1em;
-}
-
-#entity_statistics dt,
-#entity_statistics dd {
-display:inline;
-}
-#entity_statistics dt:after {
-content: ":";
-}
-
-.section ul.entities {
-float:left;
-width:100%;
-}
-.section .entities li {
-list-style-type:none;
-float:left;
-margin-right:7px;
-margin-bottom:7px;
-}
-.section .entities li .photo {
-margin-right:0;
-margin-bottom:0;
-}
-.section .entities li .fn {
-display:none;
-}
-
-.aside .section p,
-.aside .section .more {
-clear:both;
-}
-
-.profile .entity_profile {
-margin-bottom:0;
-min-height:60px;
-}
-
-
-.profile .form_group_join legend,
-.profile .form_group_leave legend,
-.profile .form_user_subscribe legend,
-.profile .form_user_unsubscribe legend {
-display:none;
-}
-
-.profiles {
-list-style-type:none;
-}
-.profile .entity_profile .entity_location {
-width:auto;
-clear:none;
-margin-left:11px;
-}
-.profile .entity_profile dl,
-.profile .entity_profile dd {
-display:inline;
-float:none;
-}
-.profile .entity_profile .entity_note,
-.profile .entity_profile .entity_url,
-.profile .entity_profile .entity_tags,
-.profile .entity_profile .form_subscription_edit {
-margin-left:59px;
-clear:none;
-display:block;
-width:auto;
-}
-.profile .entity_profile .entity_tags dt {
-display:inline;
-margin-right:11px;
-}
-
-
-.profile .entity_profile .form_subscription_edit label {
-font-weight:normal;
-margin-right:11px;
-}
-
-
-/* NOTICE */
-.notice,
-.profile {
-position:relative;
-clear:both;
-float:left;
-width:100%;
-border-width:0;
-border-style:solid;
-margin-bottom:29px;
-}
-.notices li {
-list-style-type:none;
-}
-
-#content .notice {
-width:37%;
-margin-left:17px;
-margin-bottom:47px;
-clear:none;
-overflow:hidden;
-padding: 0 0 0 65px;
-min-height:235px;
-}
-
-#aside_primary .notice {
-margin-bottom:18px;
-}
-
-#shownotice #content .notice {
-width:96%;
-}
-
-
-/* NOTICES */
-#notices_primary {
-float:left;
-width:100%;
-border-radius:7px;
--moz-border-radius:7px;
--webkit-border-radius:7px;
-}
-#notices_primary h2 {
-display:none;
-}
-.notice-data a span {
-display:block;
-padding-left:28px;
-}
-
-.notice .author {
-margin-right:11px;
-}
-
-#content .notice .author {
-/*overflow:hidden;*/
-}
-
-.fn {
-overflow:hidden;
-}
-
-.notice .author .fn {
-font-weight:bold;
-}
-
-.notice .author .photo {
-margin-bottom:0;
-}
-
-#content .notice .author .photo {
-margin-left:-83px;
-padding-right:17px;
-}
-
-
-.vcard .photo {
-display:inline;
-margin-right:11px;
-margin-bottom:11px;
-float:left;
-}
-.vcard .url {
-text-decoration:none;
-}
-.vcard .url:hover {
-text-decoration:underline;
-}
-
-.notice .entry-title {
-float:left;
-width:100%;
-overflow:hidden;
-}
-#content .notice .entry-title {
-overflow:visible;
-margin-bottom:11px;
-padding:18px;
-width:85%;
-border-radius:7px;
--moz-border-radius:7px;
--webkit-border-radius:7px;
-min-height:161px;
-}
-
-#shownotice .notice .entry-title {
-font-size:2.2em;
-}
-
-.notice p.entry-content {
-display:inline;
-}
-
-#content .notice p.entry-content
-overflow:hidden;
-}
-
-.notice p.entry-content .vcard a {
-border-radius:4px;
--moz-border-radius:4px;
--webkit-border-radius:4px;
-}
-
-.notice div.entry-content {
-clear:left;
-float:left;
-font-size:0.95em;
-}
-#showstream .notice div.entry-content {
-margin-left:0;
-}
-
-.notice .notice-options a,
-.notice .notice-options input {
-float:left;
-font-size:1.025em;
-}
-
-.notice div.entry-content dl,
-.notice div.entry-content dt,
-.notice div.entry-content dd {
-display:inline;
-}
-
-.notice div.entry-content .timestamp dt,
-.notice div.entry-content .response dt {
-display:none;
-}
-.notice div.entry-content .timestamp a {
-display:inline-block;
-}
-.notice div.entry-content .device dt {
-text-transform:lowercase;
-}
-
-
-
-.notice-data {
-position:absolute;
-top:18px;
-right:0;
-min-height:50px;
-margin-bottom:4px;
-}
-.notice .entry-content .notice-data dt {
-display:none;
-}
-
-.notice-data a {
-display:block;
-outline:none;
-}
-
-.notice-options {
-position:absolute;
-top:120px;
-left:30px;
-font-size:0.95em;
-}
-
-.notice-options a {
-float:left;
-}
-.notice-options .notice_delete,
-.notice-options .notice_reply,
-.notice-options .form_favor,
-.notice-options .form_disfavor {
-position:absolute;
-left:0;
-}
-.notice-options .form_favor,
-.notice-options .form_disfavor {
-top:0;
-}
-.notice-options .notice_reply {
-top:29px;
-}
-.notice-options .notice_delete {
-top:58px;
-}
-.notice-options .notice_reply dt {
-display:none;
-}
-
-.notice-options input,
-.notice-options a {
-text-indent:-9999px;
-outline:none;
-}
-
-.notice-options .notice_reply a,
-.notice-options input.submit {
-display:block;
-border:0;
-}
-.notice-options .notice_reply a,
-.notice-options .notice_delete a {
-text-decoration:none;
-padding-left:16px;
-}
-
-.notice-options form input.submit {
-width:16px;
-padding:2px 0;
-}
-
-.notice-options .notice_delete dt,
-.notice-options .form_favor legend,
-.notice-options .form_disfavor legend {
-display:none;
-}
-.notice-options .notice_delete fieldset,
-.notice-options .form_favor fieldset,
-.notice-options .form_disfavor fieldset {
-border:0;
-padding:0;
-}
-
-
-#usergroups #new_group {
-float: left;
-margin-right: 2em;
-}
-#new_group, #group_search {
-margin-bottom:18px;
-}
-#new_group a {
-padding-left:20px;
-}
-
-
-#filter_tags {
-margin-bottom:11px;
-float:left;
-}
-#filter_tags dt {
-display:none;
-}
-#filter_tags ul {
-list-style-type:none;
-}
-#filter_tags ul li {
-float:left;
-margin-left:7px;
-padding-left:7px;
-border-left-width:1px;
-border-left-style:solid;
-}
-#filter_tags ul li.child_1 {
-margin-left:0;
-border-left:0;
-padding-left:0;
-}
-#filter_tags ul li#filter_tags_all a {
-font-weight:bold;
-margin-top:7px;
-float:left;
-}
-
-#filter_tags ul li#filter_tags_item label {
-margin-right:7px;
-}
-#filter_tags ul li#filter_tags_item label,
-#filter_tags ul li#filter_tags_item select {
-display:inline;
-}
-#filter_tags ul li#filter_tags_item p {
-float:left;
-margin-left:38px;
-}
-#filter_tags ul li#filter_tags_item input {
-position:relative;
-top:3px;
-left:3px;
-}
-
-
-
-.pagination {
-float:left;
-clear:both;
-width:100%;
-margin-top:18px;
-}
-
-.pagination dt {
-font-weight:bold;
-display:none;
-}
-
-.pagination .nav {
-float:left;
-width:100%;
-list-style-type:none;
-}
-
-.pagination .nav_prev {
-float:left;
-}
-.pagination .nav_next {
-float:right;
-}
-
-.pagination a {
-display:block;
-text-decoration:none;
-font-weight:bold;
-padding:7px;
-border-width:1px;
-border-style:solid;
--moz-border-radius:7px;
--webkit-border-radius:7px;
-border-radius:7px;
-}
-
-.pagination .nav_prev a {
-padding-left:30px;
-}
-.pagination .nav_next a {
-padding-right:30px;
-}
-/* END: NOTICE */
-
-
-.hentry .entry-content p {
-margin-bottom:18px;
-}
-.hentry entry-content ol,
-.hentry .entry-content ul {
-list-style-position:inside;
-}
-.hentry .entry-content li {
-margin-bottom:18px;
-}
-.hentry .entry-content li li {
-margin-left:18px;
-}
-
-
-
-
-/* TOP_POSTERS */
-.section tbody td {
-padding-right:11px;
-padding-bottom:11px;
-}
-.section .vcard .photo {
-margin-right:7px;
-margin-bottom:0;
-}
-
-.section .notice {
-padding-top:7px;
-padding-bottom:7px;
-border-top:0;
-}
-
-.section .notice:first-child {
-padding-top:0;
-}
-
-.section .notice .author {
-margin-right:0;
-}
-.section .notice .author .fn {
-display:none;
-}
-
-
-/* tagcloud */
-.tag-cloud {
-list-style-type:none;
-text-align:center;
-}
-.aside .tag-cloud {
-font-size:0.8em;
-}
-.tag-cloud li {
-display:inline;
-margin-right:7px;
-line-height:1.25;
-}
-.aside .tag-cloud li {
-line-height:1.5;
-}
-.tag-cloud li a {
-text-decoration:none;
-}
-#tagcloud.section dt {
-text-transform:uppercase;
-font-weight:bold;
-}
-.tag-cloud-1 {
-font-size:1em;
-}
-.tag-cloud-2 {
-font-size:1.25em;
-}
-.tag-cloud-3 {
-font-size:1.75em;
-}
-.tag-cloud-4 {
-font-size:2em;
-}
-.tag-cloud-5 {
-font-size:2.25em;
-}
-.tag-cloud-6 {
-font-size:2.75em;
-}
-.tag-cloud-7 {
-font-size:3.25em;
-}
-
-#publictagcloud #tagcloud.section dt {
-display:none;
-}
-
-#form_settings_photo .form_data {
-clear:both;
-}
-
-#form_settings_avatar li {
-width:auto;
-}
-#form_settings_avatar input {
-margin-left:0;
-}
-#avatar_original,
-#avatar_preview {
-float:left;
-}
-#avatar_preview {
-margin-left:29px;
-}
-#avatar_preview_view {
-height:96px;
-width:96px;
-margin-bottom:18px;
-overflow:hidden;
-}
-
-#settings_attach,
-#form_settings_avatar .form_actions {
-clear:both;
-}
-
-#form_settings_avatar .form_actions {
-margin-bottom:0;
-}
-
-#form_settings_design #settings_design_color .form_data,
-#form_settings_design #color-picker {
-float:left;
-}
-#form_settings_design #settings_design_color .form_data {
-width:400px;
-margin-right:28px;
-}
-
-.instructions ul {
-list-style-position:inside;
-}
-.instructions p,
-.instructions ul {
-margin-bottom:18px;
-}
-.help dt {
-display:none;
-}
-.guide {
-clear:both;
-}
diff --git a/theme/otalk/css/display.css b/theme/otalk/css/display.css
deleted file mode 100644 (file)
index bdfaea7..0000000
+++ /dev/null
@@ -1,292 +0,0 @@
-/** theme: otalk
- *
- * @package   StatusNet
- * @author Sarven Capadisli <csarven@status.net>
- * @copyright 2009 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-@import url(base.css);
-
-html {
-}
-
-html,
-body,
-a:active {
-}
-body {
-font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;
-font-size:1em;
-background:#ddd url(../images/illustrations/illu_pattern-01.png) repeat 0 0;
-background-color:rgba(127, 127, 127, 0.1);
-}
-address {
-margin-right:7.18%;
-}
-
-input, textarea, select, option {
-font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;
-}
-input, textarea, select,
-.entity_remote_subscribe {
-border-color:#aaa;
-}
-#filter_tags ul li {
-border-color:#ddd;
-}
-
-.form_settings input.form_action-primary {
-background:none;
-}
-
-input.submit,
-#form_notice.warning #notice_text-count,
-.form_settings .form_note,
-.entity_remote_subscribe {
-background-color:#9BB43E;
-}
-
-input:focus, textarea:focus, select:focus,
-#form_notice.warning #notice_data-text {
-border-color:#9BB43E;
-}
-input.submit,
-.entity_remote_subscribe {
-color:#fff;
-}
-
-a,
-div.notice-options input,
-.form_user_block input.submit,
-.form_user_unblock input.submit,
-.entity_send-a-message a,
-.form_user_nudge input.submit,
-.entity_nudge p,
-.form_settings input.form_action-primary {
-color:#8F0000;
-}
-
-.notice,
-.profile {
-border-color:#CEE1E9;
-}
-#content .notice .entry-title,
-input, textarea, select, option,
-.pagination .nav_prev a,
-.pagination .nav_next a {
-background-color:rgba(255,255,255,0.8);
-}
-
-#content .notices li.hover .entry-title {
-background-color:rgba(255,255,255,0.9);
-}
-
-#content .notice:nth-child(1) .entry-title {
-background-color:rgba(255,255,255,0.95);
-}
-#content .notice:nth-child(2) .entry-title {
-background-color:rgba(255,255,255,0.9);
-}
-#content .notice:nth-child(3) .entry-title {
-background-color:rgba(255,255,255,0.8);
-}
-#content .notice:nth-child(4) .entry-title {
-background-color:rgba(255,255,255,0.7);
-}
-#content .notice:nth-child(5) .entry-title {
-background-color:rgba(255,255,255,0.6);
-}
-#content .notice:nth-child(6) .entry-title {
-background-color:rgba(255,255,255,0.5);
-}
-#content .notice:nth-child(7) .entry-title {
-background-color:rgba(255,255,255,0.4);
-}
-#content .notice:nth-child(8) .entry-title {
-background-color:rgba(255,255,255,0.3);
-}
-#content .notice:nth-child(9) .entry-title {
-background-color:rgba(255,255,255,0.2);
-}
-#content .notice:nth-child(10) {
-background-color:rgba(255,255,255,0.1);
-}
-
-
-#content .notice .author .photo {
-background:url(../images/illustrations/illu_arrow-left-01.gif) no-repeat 100% 0;
-}
-
-.section .profile {
-border-top-color:#87B4C8;
-}
-
-#aside_primary {
-background-color:rgba(206, 225, 233,0.5);
-}
-
-#notice_text-count {
-color:#333;
-}
-#form_notice.warning #notice_text-count {
-color:#000;
-}
-#form_notice.processing #notice_action-submit {
-background:#fff url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%;
-cursor:wait;
-text-indent:-9999px;
-}
-
-#content,
-#site_nav_local_views .nav,
-#site_nav_local_views a,
-#aside_primary {
-border-color:#fff;
-}
-#content,
-#site_nav_local_views .current a {
-background-color:transparent;
-/*background-color:red;*/
-}
-
-#site_nav_local_views .current a {
-background-color:transparent;
-}
-
-#site_nav_local_views a {
-background-color:rgba(127, 127, 127, 0.2);
-}
-#site_nav_local_views a:hover {
-background-color:rgba(255, 255, 255, 0.8);
-}
-
-.error {
-background-color:#F7E8E8;
-}
-.success {
-background-color:#EFF3DC;
-}
-
-#anon_notice {
-background-color:rgba(206, 225, 233, 0.7);
-color:#fff;
-border-color:#fff;
-}
-
-#showstream #anon_notice {
-background-color:rgba(155, 180, 62, 0.7);
-}
-
-#export_data li a {
-background-repeat:no-repeat;
-background-position:0 45%;
-}
-#export_data li a.rss {
-background-image:url(../../base/images/icons/icon_rss.png);
-}
-#export_data li a.atom {
-background-image:url(../../base/images/icons/icon_atom.png);
-}
-#export_data li a.foaf {
-background-image:url(../../base/images/icons/icon_foaf.gif);
-}
-
-.entity_edit a,
-.entity_send-a-message a,
-.form_user_nudge input.submit,
-.form_user_block input.submit,
-.form_user_unblock input.submit,
-.entity_nudge p {
-background-position: 0 40%;
-background-repeat: no-repeat;
-background-color:transparent;
-}
-.form_group_join input.submit,
-.form_group_leave input.submit
-.form_user_subscribe input.submit,
-.form_user_unsubscribe input.submit {
-background-color:#9BB43E;
-color:#fff;
-}
-.form_user_unsubscribe input.submit,
-.form_group_leave input.submit,
-.form_user_authorization input.reject {
-background-color:#87B4C8;
-}
-
-.entity_edit a {
-background-image:url(../../base/images/icons/twotone/green/edit.gif);
-}
-.entity_send-a-message a {
-background-image:url(../../base/images/icons/twotone/green/quote.gif);
-}
-.entity_nudge p,
-.form_user_nudge input.submit {
-background-image:url(../../base/images/icons/twotone/green/mail.gif);
-}
-.form_user_block input.submit,
-.form_user_unblock input.submit {
-background-image:url(../../base/images/icons/twotone/green/shield.gif);
-}
-
-/* NOTICES */
-.notices li.over {
-background-color:#fcfcfc;
-}
-
-.notice-options .notice_reply a,
-.notice-options form input.submit {
-background-color:transparent;
-}
-.notice-options .notice_reply a {
-background:transparent url(../../base/images/icons/twotone/green/reply.gif) no-repeat 0 45%;
-}
-.notice-options form.form_favor input.submit {
-background:transparent url(../../base/images/icons/twotone/green/favourite.gif) no-repeat 0 45%;
-}
-.notice-options form.form_disfavor input.submit {
-background:transparent url(../../base/images/icons/twotone/green/disfavourite.gif) no-repeat 0 45%;
-}
-.notice-options .notice_delete a {
-background:transparent url(../../base/images/icons/twotone/green/trash.gif) no-repeat 0 45%;
-}
-
-.notices div.entry-content,
-.notices div.notice-options {
-opacity:0.4;
-}
-.notices li.hover div.entry-content,
-.notices li.hover div.notice-options {
-opacity:1;
-}
-div.entry-content {
-color:#333;
-}
-div.notice-options a,
-div.notice-options input {
-font-family:sans-serif;
-}
-.notices li.hover {
-/*background-color:#fcfcfc;*/
-}
-/*END: NOTICES */
-
-#new_group a {
-background:transparent url(../../base/images/icons/twotone/green/news.gif) no-repeat 0 45%;
-}
-
-.pagination .nav_prev a,
-.pagination .nav_next a {
-background-repeat:no-repeat;
-border-color:#CEE1E9;
-}
-.pagination .nav_prev a {
-background-image:url(../../base/images/icons/twotone/green/arrow-left.gif);
-background-position:10% 45%;
-}
-.pagination .nav_next a {
-background-image:url(../../base/images/icons/twotone/green/arrow-right.gif);
-background-position:90% 45%;
-}
diff --git a/theme/otalk/css/ie.css b/theme/otalk/css/ie.css
deleted file mode 100644 (file)
index 2f463bb..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-/* IE specific styles */
-
-.notice-options input.submit {
-color:#fff;
-}
-
-#site_nav_local_views a {
-background-color:#D0DFE7;
-}
diff --git a/theme/otalk/default-avatar-mini.png b/theme/otalk/default-avatar-mini.png
deleted file mode 100644 (file)
index 38b8692..0000000
Binary files a/theme/otalk/default-avatar-mini.png and /dev/null differ
diff --git a/theme/otalk/default-avatar-profile.png b/theme/otalk/default-avatar-profile.png
deleted file mode 100644 (file)
index f8357d4..0000000
Binary files a/theme/otalk/default-avatar-profile.png and /dev/null differ
diff --git a/theme/otalk/default-avatar-stream.png b/theme/otalk/default-avatar-stream.png
deleted file mode 100644 (file)
index 6b63baa..0000000
Binary files a/theme/otalk/default-avatar-stream.png and /dev/null differ
diff --git a/theme/otalk/images/illustrations/illu_arrow-left-01.gif b/theme/otalk/images/illustrations/illu_arrow-left-01.gif
deleted file mode 100644 (file)
index 1977759..0000000
Binary files a/theme/otalk/images/illustrations/illu_arrow-left-01.gif and /dev/null differ
diff --git a/theme/otalk/images/illustrations/illu_pattern-01.png b/theme/otalk/images/illustrations/illu_pattern-01.png
deleted file mode 100644 (file)
index 5a72eaf..0000000
Binary files a/theme/otalk/images/illustrations/illu_pattern-01.png and /dev/null differ
diff --git a/theme/otalk/logo.png b/theme/otalk/logo.png
deleted file mode 100644 (file)
index fdead6c..0000000
Binary files a/theme/otalk/logo.png and /dev/null differ
index 4b30710fb636b86163a998e8d1692860cdc27068..2814260bd3c9990fb002a3900c635f8e091f85e8 100644 (file)
@@ -232,6 +232,17 @@ font-weight:bold;
 address img + .fn {
 display:none;
 }
+address a {
+text-decoration:none;
+}
+address .poweredby {
+float:left;
+clear:left;
+display:block;
+position:relative;
+top:7px;
+margin-right:-47px;
+}
 
 #header {
 width:98.5%;
@@ -486,13 +497,59 @@ margin-bottom:7px;
 margin-left:18px;
 float:left;
 }
-#form_notice .error {
+.form_notice .error,
+.form_notice .success {
 float:left;
 clear:both;
-width:96.9%;
+width:81.5%;
 margin-bottom:0;
 line-height:1.618;
 }
+.form_notice #notice_data-attach_selected code {
+float:left;
+width:80%;
+display:block;
+overflow:auto;
+margin-right:2.5%;
+font-size:1.1em;
+}
+.form_notice #notice_data-attach_selected button.close {
+float:right;
+font-size:0.8em;
+}
+
+.form_notice #notice_data-geo_wrap label,
+.form_notice #notice_data-geo_wrap input {
+position:absolute;
+top:25px;
+right:4px;
+left:auto;
+cursor:pointer;
+width:16px;
+height:16px;
+display:block;
+}
+.form_notice #notice_data-geo_wrap input {
+visibility:hidden;
+}
+.form_notice #notice_data-geo_wrap label {
+font-weight:normal;
+font-size:1em;
+margin-bottom:0;
+text-indent:-9999px;
+}
+
+button.close,
+button.minimize {
+width:16px;
+height:16px;
+text-indent:-9999px;
+padding:0;
+border:0;
+text-align:center;
+font-weight:bold;
+cursor:pointer;
+}
 
 /* entity_profile */
 .entity_profile {
@@ -850,6 +907,67 @@ font-size:1.025em;
 .notice div.entry-content .timestamp {
 display:inline-block;
 }
+.entry-content .repeat {
+display:block;
+}
+.entry-content .repeat .photo {
+float:none;
+margin-right:1px;
+position:relative;
+top:4px;
+left:0;
+}
+
+.dialogbox {
+position:absolute;
+top:-1px;
+right:-1px;
+z-index:9;
+float:none;
+padding:11px;
+border-radius:7px;
+-moz-border-radius:7px;
+-webkit-border-radius:7px;
+border-style:solid;
+border-width:1px;
+}
+
+.dialogbox legend {
+display:block !important;
+margin-right:18px;
+margin-bottom:18px;
+}
+
+.dialogbox button.close {
+position:absolute;
+right:3px;
+top:3px;
+}
+
+.dialogbox .form_guide {
+font-weight:normal;
+padding:0;
+}
+
+.dialogbox .submit_dialogbox {
+font-weight:bold;
+text-indent:0;
+min-width:46px;
+}
+.dialogbox input {
+padding-left:4px;
+}
+.dialogbox fieldset {
+margin-bottom:0;
+}
+
+#wrap form.processing input.submit,
+.entity_actions a.processing,
+.dialogbox.processing .submit_dialogbox {
+cursor:wait;
+outline:none;
+text-indent:-9999px;
+}
 
 .notice-options {
 position:relative;
index 2b917418252bb34cfc02835648732ea7bbe25186..dfeb01b48a952c12771a798444ada6c970a348e6 100644 (file)
@@ -60,6 +60,36 @@ input.submit,
 color:#FFFFFF;
 }
 
+.dialogbox .submit_dialogbox,
+input.submit,
+.form_notice input.submit {
+background:#AAAAAA url(../../base/images/illustrations/illu_pattern-01.png) 0 0 repeat-x;
+text-shadow:0 1px 0 #FFFFFF;
+color:#000000;
+border-color:#AAAAAA;
+border-top-color:#CCCCCC;
+border-left-color:#CCCCCC;
+}
+.dialogbox .submit_dialogbox:hover,
+input.submit:hover {
+background-position:0 -5px;
+}
+.dialogbox .submit_dialogbox:focus,
+input.submit:focus {
+background-position:0 -15px;
+box-shadow:3px 3px 3px rgba(194, 194, 194, 0.1);
+-moz-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.1);
+-webkit-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.1);
+text-shadow:none;
+}
+
+.form_notice label[for=notice_data-geo] {
+background-position:0 -1780px;
+}
+.form_notice label[for=notice_data-geo].checked {
+background-position:0 -1846px;
+}
+
 a,
 div.notice-options input,
 .form_user_block input.submit,
@@ -158,16 +188,69 @@ color:#333333;
 color:#000000;
 }
 #form_notice label[for=notice_data-attach] {
-background:transparent url(../../base/images/icons/twotone/green/clip-01.gif) no-repeat 0 45%;
+background-position:0 -328px;
 }
 #form_notice #notice_data-attach {
 opacity:0;
 }
 
-#form_notice.processing #notice_action-submit {
+.form_notice label[for=notice_data-attach],
+#export_data li a.rss,
+#export_data li a.atom,
+#export_data li a.foaf,
+.entity_edit a,
+.entity_send-a-message a,
+.entity_nudge p,
+.form_user_nudge input.submit,
+.form_user_block input.submit,
+.form_user_unblock input.submit,
+.form_group_block input.submit,
+.form_group_unblock input.submit,
+.form_make_admin input.submit,
+.notice .attachment,
+.notice-options .notice_reply,
+.notice-options form.form_favor input.submit,
+.notice-options form.form_disfavor input.submit,
+.notice-options .notice_delete,
+.notice-options form.form_repeat input.submit,
+#new_group a,
+.pagination .nav_prev a,
+.pagination .nav_next a,
+button.close,
+.form_group_leave input.submit,
+.form_user_unsubscribe input.submit,
+.form_group_join input.submit,
+.form_user_subscribe input.submit,
+.form_remote_authorize input.submit,
+.entity_subscribe a,
+.entity_moderation p,
+.entity_sandbox input.submit,
+.entity_silence input.submit,
+.entity_delete input.submit,
+.notice-options .repeated,
+.form_notice label[for=notice_data-geo],
+button.minimize,
+.form_reset_key input.submit,
+.entity_clear input.submit,
+.entity_flag input.submit,
+.entity_flag p,
+.entity_subscribe input.submit,
+#realtime_play,
+#realtime_pause,
+#realtime_popup {
+background-image:url(../../base/images/icons/icons-01.gif);
+background-repeat:no-repeat;
+background-color:transparent;
+}
+
+
+#wrap form.processing input.submit,
+.entity_actions a.processing,
+.dialogbox.processing .submit_dialogbox {
 background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%;
-cursor:wait;
-text-indent:-9999px;
+}
+.notice-options .form_repeat.processing {
+background-image:none;
 }
 
 #content,
@@ -190,6 +273,12 @@ color:#8F0000;
 text-shadow: rgba(194,194,194,0.5) 1px 1px 1px;
 }
 
+.processing {
+background-image:url(../../base/images/icons/icon_processing.gif);
+background-repeat:no-repeat;
+background-position:47% 47%;
+}
+
 .error {
 background-color:#F7E8E8;
 }
@@ -197,6 +286,14 @@ background-color:#F7E8E8;
 background-color:#EFF3DC;
 }
 
+button.close {
+background-position:0 -1120px;
+}
+button.minimize {
+background-position:0 -1912px;
+}
+
+
 #anon_notice {
 color:#000000;
 }
@@ -207,81 +304,138 @@ background-repeat:no-repeat;
 background-position:0 45%;
 }
 #export_data li a.rss {
-background-image:url(../../base/images/icons/icon_rss.png);
+background-position:0 -130px;
 }
 #export_data li a.atom {
-background-image:url(../../base/images/icons/icon_atom.png);
+background-position:0 -64px;
 }
 #export_data li a.foaf {
-background-image:url(../../base/images/icons/icon_foaf.gif);
+background-position:0 1px;
 }
 
-.entity_edit a,
-.entity_send-a-message a,
-.form_user_nudge input.submit,
-.form_user_block input.submit,
-.form_user_unblock input.submit,
-.form_group_block input.submit,
-.form_group_unblock input.submit,
-.entity_nudge p,
-.form_make_admin input.submit {
-background-position: 0 40%;
-background-repeat: no-repeat;
-background-color:transparent;
+#export_data li a.rss {
+background-position:0 -130px;
+}
+#export_data li a.atom {
+background-position:0 -64px;
 }
+#export_data li a.foaf {
+background-position:0 1px;
+}
+
 .form_group_join input.submit,
-.form_group_leave input.submit
+.form_group_leave input.submit,
 .form_user_subscribe input.submit,
-.form_user_unsubscribe input.submit {
+.form_user_unsubscribe input.submit,
+.form_remote_authorize input.submit,
+.entity_subscribe a {
 background-color:#8F0000;
 color:#FFFFFF;
 }
-.form_user_unsubscribe input.submit,
 .form_group_leave input.submit,
-.form_user_authorization input.reject {
+.form_user_unsubscribe input.submit {
+background-position:5px -1246px;
 background-color:#87B4C8;
 }
+.form_group_join input.submit,
+.form_user_subscribe input.submit,
+.form_remote_authorize input.submit,
+.entity_subscribe a {
+background-position:5px -1181px;
+}
 
 .entity_edit a {
-background-image:url(../../base/images/icons/twotone/green/edit.gif);
+background-position: 5px -719px;
 }
 .entity_send-a-message a {
-background-image:url(../../base/images/icons/twotone/green/quote.gif);
+background-position: 5px -852px;
 }
+.entity_send-a-message .form_notice,
+.entity_moderation:hover ul,
+.dialogbox {
+box-shadow:3px 7px 5px rgba(194, 194, 194, 0.7);
+-moz-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.7);
+-webkit-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.7);
+}
+
 .entity_nudge p,
 .form_user_nudge input.submit {
-background-image:url(../../base/images/icons/twotone/green/mail.gif);
+background-position: 5px -785px;
 }
 .form_user_block input.submit,
 .form_user_unblock input.submit,
 .form_group_block input.submit,
 .form_group_unblock input.submit {
-background-image:url(../../base/images/icons/twotone/green/shield.gif);
+background-position: 5px -918px;
 }
 .form_make_admin input.submit {
-background-image:url(../../base/images/icons/twotone/green/admin.gif);
+background-position: 5px -983px;
+}
+.entity_moderation p {
+background-position: 5px -1313px;
+}
+.entity_sandbox input.submit {
+background-position: 5px -1380px;
+}
+.entity_silence input.submit {
+background-position: 5px -1445px;
+}
+.entity_delete input.submit {
+background-position: 5px -1511px;
+}
+.form_reset_key input.submit {
+background-position: 5px -1973px;
+}
+.entity_clear input.submit {
+background-position: 5px -2039px;
+}
+.entity_flag input.submit,
+.entity_flag p {
+background-position: 5px -2105px;
+}
+.entity_subscribe input.accept {
+background-position: 5px -2171px;
+}
+.entity_subscribe input.reject {
+background-position: 5px -2237px;
+}
+#realtime_play {
+background-position: 0 -2308px;
+}
+#realtime_pause {
+background-position: 0 -2374px;
+}
+#realtime_popup {
+background-position: 0 -1714px;
 }
 
 /* NOTICES */
 .notice .attachment {
-background:transparent url(../../base/images/icons/twotone/green/clip-02.gif) no-repeat 0 45%;
+background-position:0 -394px;
 }
 #attachments .attachment {
 background:none;
 }
 .notice-options .notice_reply {
-background:transparent url(../../base/images/icons/twotone/green/reply.gif) no-repeat 0 45%;
+background-position:0 -592px;
 }
 .notice-options form.form_favor input.submit {
-background:transparent url(../../base/images/icons/twotone/green/favourite.gif) no-repeat 0 45%;
+background-position:0 -460px;
 }
 .notice-options form.form_disfavor input.submit {
-background:transparent url(../../base/images/icons/twotone/green/disfavourite.gif) no-repeat 0 45%;
+background-position:0 -526px;
 }
 .notice-options .notice_delete {
-background:transparent url(../../base/images/icons/twotone/green/trash.gif) no-repeat 0 45%;
+background-position:0 -658px;
+}
+.notice-options form.form_repeat input.submit {
+background-position:0 -1582px;
+}
+.notice-options .repeated {
+background-position:0 -1648px;
 }
 
+
 .notices div.entry-content,
 .notices div.notice-options {
 opacity:0.4;
@@ -319,19 +473,26 @@ background-color:rgba(200, 200, 200, 0.300);
 /*END: NOTICES */
 
 #new_group a {
-background:transparent url(../../base/images/icons/twotone/green/news.gif) no-repeat 0 45%;
+background-position:0 -1054px;
 }
 
-.pagination .nav_prev a,
-.pagination .nav_next a {
-background-repeat:no-repeat;
-border-color:#000000;
-}
 .pagination .nav_prev a {
-background-image:url(../../base/images/icons/twotone/green/arrow-left.gif);
-background-position:10% 45%;
+background-position:10% -187px;
 }
 .pagination .nav_next a {
-background-image:url(../../base/images/icons/twotone/green/arrow-right.gif);
-background-position:90% 45%;
+background-position:105% -252px;
+}
+.pagination .nav .processing {
+background-image:url(../../base/images/icons/icon_processing.gif);
+box-shadow:none;
+-moz-box-shadow:none;
+-webkit-box-shadow:none;
+outline:none;
+}
+.pagination .nav_next a.processing {
+background-position:90% 47%;
 }
+.pagination .nav_prev a.processing {
+background-position:10% 47%;
+}
+
index 550d373fef4005342c4e1daa7cb2c115db54f46c..cf1839194a6d8e91d3ec988abe7d5be227143d28 100644 (file)
Binary files a/theme/pigeonthoughts/logo.png and b/theme/pigeonthoughts/logo.png differ