├── .gitignore ├── LICENSE.txt ├── README.md ├── composer.json ├── composer.lock ├── controllers ├── controllers.php ├── static.php └── webmention.php ├── lib ├── config.template.php ├── helpers.php └── mf2.php ├── public ├── .htaccess ├── bootstrap │ ├── css │ │ ├── bootstrap-responsive.css │ │ ├── bootstrap-responsive.min.css │ │ ├── bootstrap.css │ │ └── bootstrap.min.css │ ├── img │ │ ├── glyphicons-halflings-white.png │ │ └── glyphicons-halflings.png │ └── js │ │ ├── bootstrap.js │ │ └── bootstrap.min.js ├── css │ └── style.css ├── index.php └── js │ ├── jquery-1.7.1.min.js │ ├── jquery.timeago.js │ └── timeago │ ├── README.md │ ├── jquery.timeago.ar.js │ ├── jquery.timeago.bg.js │ ├── jquery.timeago.bs.js │ ├── jquery.timeago.ca.js │ ├── jquery.timeago.cs.js │ ├── jquery.timeago.cy.js │ ├── jquery.timeago.da.js │ ├── jquery.timeago.de.js │ ├── jquery.timeago.dv.js │ ├── jquery.timeago.el.js │ ├── jquery.timeago.en-short.js │ ├── jquery.timeago.en.js │ ├── jquery.timeago.es-short.js │ ├── jquery.timeago.es.js │ ├── jquery.timeago.et.js │ ├── jquery.timeago.fa.js │ ├── jquery.timeago.fi.js │ ├── jquery.timeago.fr-short.js │ ├── jquery.timeago.fr.js │ ├── jquery.timeago.gl.js │ ├── jquery.timeago.he.js │ ├── jquery.timeago.hr.js │ ├── jquery.timeago.hu.js │ ├── jquery.timeago.hy.js │ ├── jquery.timeago.id.js │ ├── jquery.timeago.is.js │ ├── jquery.timeago.it.js │ ├── jquery.timeago.ja.js │ ├── jquery.timeago.jv.js │ ├── jquery.timeago.ko.js │ ├── jquery.timeago.ky.js │ ├── jquery.timeago.lt.js │ ├── jquery.timeago.mk.js │ ├── jquery.timeago.nl.js │ ├── jquery.timeago.no.js │ ├── jquery.timeago.pl.js │ ├── jquery.timeago.pt-br-short.js │ ├── jquery.timeago.pt-br.js │ ├── jquery.timeago.pt.js │ ├── jquery.timeago.ro.js │ ├── jquery.timeago.rs.js │ ├── jquery.timeago.ru.js │ ├── jquery.timeago.rw.js │ ├── jquery.timeago.si.js │ ├── jquery.timeago.sk.js │ ├── jquery.timeago.sl.js │ ├── jquery.timeago.sv.js │ ├── jquery.timeago.th.js │ ├── jquery.timeago.tr.js │ ├── jquery.timeago.uk.js │ ├── jquery.timeago.uz.js │ ├── jquery.timeago.vi.js │ ├── jquery.timeago.zh-CN.js │ └── jquery.timeago.zh-TW.js ├── resources └── locales │ ├── de │ └── default.po │ ├── default.pot │ ├── en │ └── default.po │ ├── es │ └── default.po │ ├── fr │ └── default.po │ ├── nl │ └── default.po │ ├── ru │ └── default.po │ └── sv │ └── default.po ├── sql ├── 0001.sql ├── 0002.sql ├── 0003.sql └── schema.sql ├── test.php └── views ├── _post-row.php ├── calendar.php ├── comment-full.php ├── index.php ├── layout.php ├── members.php ├── post.php ├── posts.php ├── submit-full.php ├── submit.php ├── webmention-error.php └── webmention.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | composer.phar 3 | lib/config.php 4 | .DS_Store 5 | .idea/ 6 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | BSD License 2 | ----------- 3 | 4 | Copyright (c) 2013-2017 by Aaron Parecki. All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of Django nor the names of its contributors may be used 17 | to endorse or promote products derived from this software without 18 | specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 24 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 27 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IndieNews 2 | 3 | This site is live on https://news.indieweb.org. It is a news aggregator for [IndieWeb](https://indieweb.org)-related posts. 4 | 5 | To submit one of your posts, link to https://news.indieweb.org/en from one the post and send a [Webmention](https://indieweb.org/webmention) to https://news.indieweb.org/webmention. 6 | 7 | [Full instructions](https://news.indieweb.org/how-to-submit-a-post) 8 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "slim/slim": "^4", 4 | "j4mie/idiorm": "1.3.*", 5 | "cakephp/i18n": "^5.0", 6 | "tantek/cassis": "dev-main", 7 | "p3k/utils": "^1.0", 8 | "p3k/xray": "^1.15", 9 | "mf2/mf2": "^0.4.6", 10 | "p3k/http": "^0.1.8", 11 | "slim/php-view": "^3.2", 12 | "slim/psr7": "^1.6" 13 | }, 14 | "autoload": { 15 | "files": [ 16 | "lib/helpers.php", 17 | "lib/mf2.php" 18 | ] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /controllers/controllers.php: -------------------------------------------------------------------------------- 1 | redirect('/home.json', '/en.json', 301); 7 | $app->redirect('/newest.json', '/en.json', 301); 8 | 9 | // Redirect post IDs to the post URL version 10 | $app->get('/post/{id:[0-9]+}{format:|\.json}', function($request, $response, $args) { 11 | $post = ORM::for_table('posts')->where('id', $args['id'])->find_one(); 12 | 13 | if(!$post) 14 | return $response->withStatus(404); 15 | 16 | $url = Config::$baseURL . '/post/'. slugForURL($post->href) . ($args['format']); 17 | 18 | return $response 19 | ->withHeader('Location', $url) 20 | ->withStatus(302); 21 | }); 22 | 23 | // Language-specific feeds 24 | $app->get('/{lang:'.LANG_REGEX.'}{format:|\.json|\.jf2}', function($request, $response, $args) { 25 | 26 | $params = $request->getQueryParams(); 27 | 28 | $format = formatFromRouteArgs($args); 29 | 30 | I18n::setLocale($args['lang']); 31 | 32 | // Get posts ordered by date submitted 33 | $posts = ORM::for_table('posts') 34 | ->where('lang', $args['lang']) 35 | ->order_by_desc('date_submitted'); 36 | 37 | if(array_key_exists('before', $params)) { 38 | $before = date('Y-m-d H:i:s', b60to10($params['before'])); 39 | $posts = $posts->where_lt('date_submitted', $before); 40 | } 41 | 42 | $posts = $posts->limit(20)->find_many(); 43 | 44 | $atomFeed = ''; 45 | $webSubTags = '' . "\n" . ''; 46 | 47 | $renderer = new PhpRenderer(__DIR__.'/../views/'); 48 | $renderer->setLayout('layout.php'); 49 | 50 | $temp = $renderer->render(new \Slim\Psr7\Response(), "posts.php", [ 51 | 'title' => 'IndieNews ' . $args['lang'], 52 | 'posts' => $posts, 53 | 'lang' => $args['lang'], 54 | 'meta' => $atomFeed.$webSubTags, 55 | ]); 56 | 57 | $html = $temp->getBody()->__toString(); 58 | 59 | $response = $response->withAddedHeader('Link', '<' . Config::$baseURL . '/'.$args['lang'].'/webmention>; rel="webmention"'); 60 | $response = $response->withAddedHeader('Link', '<' . Config::$baseURL . '/'.$args['lang'].'>; rel="self"'); 61 | $response = $response->withAddedHeader('Link', '<' . Config::$hubURL . '>; rel="hub"'); 62 | 63 | return respondWithFormat($response, $html, $format); 64 | }); 65 | 66 | 67 | $app->get('/{lang:'.LANG_REGEX.'}/{year:\d{4}}/{month:\d{2}}', function($request, $response, $args) { 68 | 69 | $year = $args['year']; 70 | $month = $args['month']; 71 | $lang = $args['lang']; 72 | 73 | I18n::setLocale($args['lang']); 74 | setlocale(LC_ALL, localeFromLangCode($args['lang'])); 75 | 76 | $date = new DateTime($year.'-'.$month.'-01'); 77 | 78 | $posts = ORM::for_table('posts') 79 | ->where('lang', $lang) 80 | ->where_lte('date_submitted', $date->format('Y-m-t').' 23:59:59') 81 | ->where_gte('date_submitted', $date->format('Y-m-01')) 82 | ->order_by_asc('date_submitted') 83 | ->find_many(); 84 | 85 | $prev = false; 86 | $next = false; 87 | 88 | $prevPost = ORM::for_table('posts') 89 | ->where('lang', $lang) 90 | ->where_lt('date_submitted', $date->format('Y-m-01')) 91 | ->order_by_desc('date_submitted') 92 | ->find_one(); 93 | if($prevPost) { 94 | $prev = new DateTime($prevPost->date_submitted); 95 | } 96 | 97 | $nextPost = ORM::for_table('posts') 98 | ->where('lang', $lang) 99 | ->where_gt('date_submitted', $date->format('Y-m-t').' 23:59:59') 100 | ->order_by_asc('date_submitted') 101 | ->find_one(); 102 | if($nextPost) { 103 | $next = new DateTime($nextPost->date_submitted); 104 | } 105 | 106 | $calendar = []; 107 | foreach($posts as $post) { 108 | $postDate = $post->post_date ?: $post->date_submitted; 109 | $day = printLocalDate('j', $postDate, $post->tzoffset); 110 | if(!array_key_exists($day, $calendar)) 111 | $calendar[(int)$day] = []; 112 | $calendar[(int)$day][] = $post; 113 | } 114 | ksort($calendar); 115 | 116 | return render($response, 'calendar', [ 117 | 'title' => 'IndieNews ' . $lang, 118 | 'date' => $date, 119 | 'year' => $year, 120 | 'month' => $month, 121 | 'calendar' => $calendar, 122 | 'meta' => '', 123 | 'lang' => $lang, 124 | 'next' => $next, 125 | 'prev' => $prev 126 | ]); 127 | }); 128 | 129 | 130 | 131 | 132 | // Language-specific submit instructions 133 | $app->get('/{lang:'.LANG_REGEX.'}/submit', function($request, $response, $args) { 134 | I18n::setLocale($args['lang']); 135 | 136 | return render($response, 'submit', array( 137 | 'title' => __('About IndieNews'), 138 | 'meta' => '', 139 | 'lang' => $args['lang'] 140 | )); 141 | }); 142 | 143 | 144 | $app->get('/{lang:'.LANG_REGEX.'}/members', function($request, $response, $args) { 145 | I18n::setLocale($args['lang']); 146 | 147 | $users = ORM::for_table('users') 148 | ->select('users.*') 149 | ->select_expr('COUNT(posts.id) AS num_posts') 150 | ->join('posts', ['posts.user_id', '=', 'users.id']) 151 | ->where('posts.lang', $args['lang']) 152 | ->where_gt('posts.date_submitted', date('Y-m-d H:i:s', strtotime('1 year ago'))) 153 | ->group_by('users.id') 154 | ->order_by_desc('num_posts') 155 | ->find_many(); 156 | 157 | return render($response, 'members', array( 158 | 'title' => __('IndieNews Members'), 159 | 'meta' => '', 160 | 'lang' => $args['lang'], 161 | 'users' => $users 162 | )); 163 | }); 164 | 165 | 166 | // Language-specific permalinks 167 | $app->get('/{lang:'.LANG_REGEX.'}/{slug:.*?}{format:|\.json|\.jf2}', function($request, $response, $args) { 168 | $format = formatFromRouteArgs($args); 169 | $slug = $args['slug']; 170 | 171 | I18n::setLocale($args['lang']); 172 | 173 | $post = ORM::for_table('posts')->where_in('href', array('http://'.$slug,'https://'.$slug))->find_one(); 174 | $posts = array($post); 175 | 176 | $response = $response->withAddedHeader('Link', '<' . Config::$baseURL . '/'.$args['lang'].'/webmention>; rel="webmention"'); 177 | 178 | if(!$post) { 179 | return $response->withStatus(404); 180 | } 181 | 182 | $renderer = new PhpRenderer(__DIR__.'/../views/'); 183 | $renderer->setLayout('layout.php'); 184 | 185 | $temp = $renderer->render(new \Slim\Psr7\Response(), "post.php", [ 186 | 'title' => $post->title, 187 | 'post' => $post, 188 | 'view' => 'single', 189 | 'meta' => '', 190 | 'lang' => $args['lang'] 191 | ]); 192 | 193 | $html = $temp->getBody()->__toString(); 194 | 195 | return respondWithFormat($response, $html, $format); 196 | }); 197 | -------------------------------------------------------------------------------- /controllers/static.php: -------------------------------------------------------------------------------- 1 | get('/', function($request, $response) { 3 | return render($response, 'index', array( 4 | 'title' => 'IndieNews homepage', 5 | 'meta' => '', 6 | 'lang' => 'en' 7 | )); 8 | }); 9 | 10 | $app->get('/how-to-submit-a-post', function($request, $response) use($app) { 11 | return render($response, 'submit-full', array( 12 | 'title' => 'IndieNews - How to submit a post', 13 | 'meta' => '', 14 | 'lang' => 'en' 15 | )); 16 | }); 17 | 18 | $app->redirect('/how', '/how-to-submit-a-post', 301); 19 | $app->redirect('/how-to-comment', '/how-to-submit-a-post', 301); 20 | $app->redirect('/constructing-post-urls', '/how-to-submit-a-post', 301); 21 | -------------------------------------------------------------------------------- /controllers/webmention.php: -------------------------------------------------------------------------------- 1 | get('/{lang:'.LANG_REGEX.'}/webmention', function($request, $response, $args) { 5 | I18n::setLocale($args['lang']); 6 | 7 | return render($response, 'webmention', array( 8 | 'title' => 'IndieNews Webmention Endpoint', 9 | 'meta' => '', 10 | 'lang' => $args['lang'] 11 | )); 12 | }); 13 | 14 | $app->post('/{lang:'.LANG_REGEX.'}/webmention', function($request, $response, $args) { 15 | 16 | $params = (array)$request->getParsedBody(); 17 | $is_html = $params['html'] ?? false; 18 | 19 | $sourceURL = $params['source'] ?? null; 20 | $targetURL = $params['target'] ?? null; 21 | 22 | $error = function($err, $description=false) use($is_html, $response, $args) { 23 | if($is_html) { 24 | $response = $response->withStatus(400); 25 | return render($response, 'webmention-error', [ 26 | 'title' => 'Webmention Error', 27 | 'error' => $err, 28 | 'description' => $description, 29 | 'meta' => '', 30 | 'lang' => $args['lang'] 31 | ]); 32 | } else { 33 | $error = array( 34 | 'error' => $err 35 | ); 36 | if($description) 37 | $error['error_description'] = $description; 38 | 39 | $response->getBody()->write(json_encode($error)); 40 | return $response->withHeader('Content-Type', 'application/json')->withStatus(400); 41 | } 42 | }; 43 | 44 | if($sourceURL == null) { 45 | return $error('missing_source_url', 'No source URL was provided in the request.'); 46 | } 47 | 48 | $source = parse_url($sourceURL); 49 | 50 | # Verify $source is valid 51 | if($source == null 52 | || !array_key_exists('scheme', $source) 53 | || !in_array($source['scheme'], array('http','https')) 54 | || !array_key_exists('host', $source) 55 | ) { 56 | return $error('invalid_source_url', 'The source URL was not valid. Ensure the URL is an http or https URL.'); 57 | } 58 | 59 | if($targetURL == null) { 60 | return $error('missing_target_url', 'No target URL was provided in the request.'); 61 | } 62 | 63 | # Verify $target is actually a resource under our control (home page, individual post) 64 | $target = parse_url($targetURL); 65 | 66 | # Verify $source is valid 67 | if($target == null 68 | || !array_key_exists('scheme', $target) 69 | || !in_array($target['scheme'], array('http','https')) 70 | || !array_key_exists('host', $target) 71 | || $target['host'] != parse_url(Config::$baseURL, PHP_URL_HOST) 72 | ) { 73 | return $error('target_not_supported', 'The target URL provided is not supported. Only '.Config::$baseURL.' URLs are accepted.'); 74 | } 75 | 76 | if(!preg_match('/^' . str_replace('/', '\/', Config::$baseURL) . '(?:\/('.LANG_REGEX.'))\/?$/', $targetURL, $match)) { 77 | return $error('target_not_supported', 'The target you specified does not match a supported URL on this site.'); 78 | } 79 | 80 | // Parse the language from the target URL, so that the story ends up on the specified 81 | // feed regardless of which endpoint it was sent to. 82 | // If no lang was sent in the target param (like if they just linked to the indienews home page), 83 | // then use the language specified by the webmention endpoint. 84 | if(array_key_exists(1, $match)) 85 | $lang = $match[1]; 86 | 87 | $record = array( 88 | 'post_author' => $source['scheme'].'://'.$source['host'], 89 | 'title' => false, 90 | 'body' => false, 91 | 'date' => false 92 | ); 93 | $notices = array(); 94 | 95 | # Now fetch and parse the page looking for Microformats 96 | $xray = new p3k\XRay(); 97 | $xray->http = new p3k\HTTP('IndieNews/1.0.0 (https://news.indieweb.org/)'); 98 | $xrayresponse = $xray->parse($sourceURL, ['ignore-as2' => true]); 99 | 100 | if(isset($xrayresponse['error'])) { 101 | return $error($xrayresponse['error'], 'An error occurred while attempting to fetch the source URL: ' . $xrayresponse['error_description']); 102 | } 103 | 104 | $post = $xrayresponse['data']; 105 | if(isset($xrayresponse['refs'])) 106 | $refs = $xrayresponse['refs']; 107 | else 108 | $refs = []; 109 | 110 | if(!isset($post['type']) || !in_array($post['type'], ['entry','event'])) { 111 | return $error('no_link_found', 'No h-entry or h-event was found on the page, so we were unable to find a u-syndication or u-category URL. If you have multiple top-level h-* objects, ensure that one of them has a u-url property set to the URL of the page.'); 112 | } 113 | 114 | $authorURL = false; 115 | 116 | if($post['type'] == 'entry') { 117 | 118 | if(isset($post['name'])) { 119 | $record['title'] = ellipsize_to_word($post['name'], 200, '...', 10); 120 | } else { 121 | $notices[] = 'No "name" property found on the h-entry.'; 122 | } 123 | 124 | if(isset($post['content'])) { 125 | $record['body'] = ellipsize_to_word($post['content']['text'], 500, '...', 10); 126 | } 127 | 128 | if(array_key_exists('published', $post)) { 129 | try { 130 | $published = new DateTime($post['published']); 131 | } catch(Exception $e) { 132 | $notices[] = 'Failed to parse published date'; 133 | } 134 | if(isset($published)) { 135 | $record['date'] = $published; 136 | $utcdate = clone $published; 137 | $utcdate->setTimeZone(new DateTimeZone('UTC')); 138 | } 139 | } else { 140 | $notices[] = 'No published date found'; 141 | } 142 | 143 | 144 | } elseif($post['type'] == 'event') { 145 | if(isset($post['name'])) { 146 | $record['title'] = ellipsize_to_word($post['name'], 200, '...', 10); 147 | } else { 148 | $notices[] = 'No "name" was found for this h-event'; 149 | } 150 | 151 | if(isset($post['start'])) { 152 | try { 153 | $start = new DateTime($post['start']); 154 | } catch(Exception $e) { 155 | $notices[] = 'Failed to parse start date'; 156 | } 157 | if(isset($start) && $start) { 158 | $record['date'] = $start; 159 | $utcdate = clone $start; 160 | $utcdate->setTimeZone(new DateTimeZone('UTC')); 161 | } 162 | } 163 | 164 | if($locations=$post['location']) { 165 | $locationURL = $locations[0]; 166 | if(array_key_exists($locationURL, $refs)) { 167 | $location = $refs[$locationURL]; 168 | if(array_key_exists('name', $location)) { 169 | $record['title'] .= ' at ' . ellipsize_to_word($location['name'], 200, '...', 10); 170 | } 171 | } 172 | } 173 | } 174 | 175 | if(isset($post['author']) && $post['author']['url']) { 176 | $authorURL = parse_url($post['author']['url']); 177 | if($authorURL && array_key_exists('host', $authorURL)) { 178 | $record['post_author'] = $post['author']['url']; 179 | } else { 180 | $notices[] = 'No host was found on the author URL (' . $post['author']['url'] . ')'; 181 | } 182 | } else { 183 | $notices[] = 'No author URL was found for the h-entry. Using the domain name instead.'; 184 | } 185 | 186 | $synURL = false; 187 | if(array_key_exists('syndication', $post)) { 188 | foreach($post['syndication'] as $syn) { 189 | if(strpos($syn, Config::$baseURL) === 0) { 190 | $synURL = $syn; 191 | } 192 | } 193 | } 194 | if(array_key_exists('category', $post)) { 195 | foreach($post['category'] as $cat) { 196 | if(strpos($cat, Config::$baseURL) === 0) { 197 | $synURL = $cat; 198 | } 199 | } 200 | } 201 | if(!$synURL) { 202 | return $error('no_link_found', 'Could not find a syndication or category link for this entry to news.indieweb.org. Please see https://news.indieweb.org/how for more information.'); 203 | } 204 | if($synURL != $targetURL) { 205 | return $error('target_mismatch', 'The URL on the page did not match the target URL of the Webmention. Make sure your post links to ' . $targetURL); 206 | } 207 | 208 | if(array_key_exists('in-reply-to', $post)) { 209 | // We can only use the first in-reply-to. Not sure what the correct behavior would be for multiple. 210 | $inReplyTo = $post['in-reply-to'][0]; 211 | } else { 212 | $inReplyTo = false; 213 | } 214 | 215 | # Get the domain of $source and find or create a user account 216 | $user = ORM::for_table('users')->where('url', $record['post_author'])->find_one(); 217 | 218 | if($user == FALSE) { 219 | $user = ORM::for_table('users')->create(); 220 | $user->url = $record['post_author']; 221 | $user->date_created = date('Y-m-d H:i:s'); 222 | $user->save(); 223 | } 224 | 225 | $href = $sourceURL; 226 | 227 | if(array_key_exists('bookmark-of', $post)) { 228 | // Strip utm tracking params 229 | $href = p3k\url\strip_tracking_params($post['bookmark-of'][0]); 230 | if(array_key_exists($href, $refs)) { 231 | if(array_key_exists('name', $refs[$href])) { 232 | $record['title'] = $refs[$href]['name']; 233 | } else { 234 | // TODO: Parse the bookmark URL and find the canonical post title 235 | } 236 | } 237 | // If this is a submission of a bookmark, set the post author to the bookmark website. 238 | // For now, just set it to the domain of the bookmark. Later we could parse the bookmark for an h-card. 239 | if($href != $sourceURL) { 240 | $record['post_author'] = parse_url($href, PHP_URL_SCHEME) . '://' . parse_url($href, PHP_URL_HOST); 241 | } 242 | } 243 | 244 | $indieNewsPermalink = permalinkForURL($lang, $href); 245 | 246 | # If there is no existing post for $sourceURL, update the properties 247 | $post = ORM::for_table('posts')->where('lang', $lang)->where('source_url', $sourceURL)->find_one(); 248 | if($post != FALSE) { 249 | if($record['date']) { 250 | $post->post_date = $utcdate->format('Y-m-d H:i:s'); 251 | $post->tzoffset = $record['date']->format('Z'); 252 | } 253 | $post->post_author = $record['post_author']; 254 | $post->title = $record['title']; 255 | if($inReplyTo) 256 | $post->in_reply_to = $inReplyTo; 257 | if($record['body']) 258 | $post->body = $record['body']; 259 | $post->save(); 260 | $notices[] = 'Already registered, updating properties of the post.'; 261 | $update = true; 262 | } else { 263 | # Record a new post 264 | $post = ORM::for_table('posts')->create(); 265 | $post->lang = $lang; 266 | $post->user_id = $user->id; 267 | $post->date_submitted = date('Y-m-d H:i:s'); 268 | if($record['date']) { 269 | $post->post_date = $utcdate->format('Y-m-d H:i:s'); 270 | $post->tzoffset = $record['date']->format('Z'); 271 | } 272 | $post->post_author = $record['post_author']; 273 | $post->title = $record['title']; 274 | if($inReplyTo) 275 | $post->in_reply_to = $inReplyTo; 276 | if($record['body']) 277 | $post->body = $record['body']; 278 | $post->href = $href; 279 | $post->source_url = $sourceURL; 280 | $post->save(); 281 | $update = false; 282 | 283 | irc_notice('[indienews' . ($lang == 'en' ? '' : '/'.strtolower($lang)) . '] New post: ' . ($post->title ? '"'.$post->title.'" ' : '') . $post->href . ($sourceURL == $href ? '' : ' (from ' . $sourceURL . ')')); 284 | 285 | # Ping the hub 286 | if(Config::$hubURL) { 287 | $ch = curl_init(Config::$hubURL); 288 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 289 | curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([ 290 | 'hub.mode' => 'publish', 291 | 'hub.topic' => Config::$baseURL . '/' . $lang 292 | ])); 293 | curl_setopt($ch, CURLOPT_TIMEOUT, 4); 294 | curl_exec($ch); 295 | } 296 | } 297 | 298 | if($is_html) { 299 | return $response 300 | ->withHeader('Location', $indieNewsPermalink) 301 | ->withStatus(302); 302 | } else { 303 | $response = $response 304 | ->withHeader('Content-Type', 'application/json') 305 | ->withStatus(201); 306 | 307 | $responseData = array( 308 | 'title' => $record['title'], 309 | 'body' => $record['body'] ? true : false, 310 | 'author' => $record['post_author'], 311 | 'date' => ($record['date'] ? $record['date']->format('c') : false) 312 | ); 313 | if($inReplyTo) 314 | $responseData['in-reply-to'] = $inReplyTo; 315 | 316 | $data = array( 317 | 'result' => 'success', 318 | 'notices' => $notices, 319 | 'data' => $responseData, 320 | 'source' => $sourceURL, 321 | 'url' => $indieNewsPermalink 322 | ); 323 | 324 | $response->getBody()->write(json_encode($data, JSON_PRETTY_PRINT)); 325 | 326 | return $response->withHeader('Location', $indieNewsPermalink); 327 | } 328 | }); 329 | 330 | -------------------------------------------------------------------------------- /lib/config.template.php: -------------------------------------------------------------------------------- 1 | 'English', 15 | 'sv' => 'Svenska', 16 | 'de' => 'Deutsch', 17 | 'fr' => 'Français', 18 | 'nl' => 'Nederlands', 19 | 'ru' => 'русский', 20 | 'es' => 'Español', 21 | ]; 22 | } 23 | 24 | function localeFromLangCode($code) { 25 | switch($code) { 26 | case 'en': 27 | return 'en_US'; 28 | case 'sv': 29 | return 'sv_SE.UTF-8'; 30 | case 'de': 31 | return 'de_DE.UTF-8'; 32 | case 'fr': 33 | return 'fr_FR.UTF-8'; 34 | case 'nl': 35 | return 'nl_NL.UTF-8'; 36 | case 'ru': 37 | return 'ru_RU.UTF-8'; 38 | case 'es': 39 | return 'es_MX.UTF-8'; 40 | } 41 | } 42 | 43 | function getPostsForParentID($parentID) { 44 | return ORM::for_table('posts')->raw_query(' 45 | SELECT *, GREATEST(1, TIMESTAMPDIFF(HOUR, date_submitted, NOW())) AS age 46 | FROM posts 47 | ORDER BY date_submitted DESC 48 | ')->find_many(); 49 | } 50 | 51 | function respondWithFormat($response, $html, $format) { 52 | if($format == 'json') { 53 | $parser = new mf2\Parser($html); 54 | $output = $parser->parse(); 55 | $output['note'][] = "This JSON is automatically generated by parsing the microformats from the HTML representation of this page using the php-mf2 library."; 56 | $output['note'][] = "You can find the php-mf2 library at github.com/microformats/php-mf2"; 57 | $output['note'][] = "If you see a problem with the output, please let me know! (github.com/aaronpk/IndieNews/issues)"; 58 | $response->getBody()->write(json_encode($output)); 59 | return $response->withHeader('Content-Type', 'application/json'); 60 | } elseif($format == 'jf2') { 61 | $xray = new p3k\XRay(); 62 | $parsed = $xray->parse(Config::$baseURL, $html); 63 | $response->getBody()->write(json_encode($parsed)); 64 | return $response->withHeader('Content-Type', 'application/json'); 65 | } else { 66 | $response->getBody()->write($html); 67 | return $response; 68 | } 69 | } 70 | 71 | // $format - one of the php.net/date format strings 72 | // $date - a string that will be passed to DateTime() 73 | // $offset - numeric timezone offset 74 | function printLocalDate($format, $date, $offset) { 75 | if($offset != 0) 76 | $tz = new DateTimeZone(($offset < 0 ? '-' : '+') . sprintf('%02d:%02d', abs(floor($offset / 60 / 60)), (($offset / 60) % 60))); 77 | else 78 | $tz = new DateTimeZone('UTC'); 79 | $d = new DateTime($date); 80 | $d->setTimeZone($tz); 81 | return $d->format($format); 82 | } 83 | 84 | function formatFromRouteArgs($args) { 85 | return empty($args['format']) ? 'html' : trim($args['format'], '.'); 86 | } 87 | 88 | // Strip the http:// prefix 89 | function slugForURL($url) { 90 | return preg_replace('/https?:\/\//', '', $url); 91 | } 92 | 93 | function permalinkForURL($lang, $url) { 94 | return Config::$baseURL . '/' . $lang . '/' . slugForURL($url); 95 | } 96 | 97 | function shouldDisplayPostName($name) { 98 | if(!$name) return false; 99 | $name = str_replace('http://','https://',$name); 100 | return strlen($name) < 200 # must be less than 200 chars 101 | && substr_count($name, "\n") <= 1 # must be less than 2 lines 102 | && substr_count($name, "https://") <= 1; # must have at most 1 URL 103 | } 104 | 105 | function render($response, $page, $data) { 106 | $renderer = new PhpRenderer(__DIR__.'/../views/'); 107 | $renderer->setLayout('layout.php'); 108 | return $renderer->render($response, "$page.php", $data); 109 | }; 110 | 111 | function session($key) { 112 | if(array_key_exists($key, $_SESSION)) 113 | return $_SESSION[$key]; 114 | else 115 | return null; 116 | } 117 | 118 | function getLoggedInUser() { 119 | if(session('user')) { 120 | return ORM::for_table('users')->where('url', session('user'))->find_one(); 121 | } else { 122 | return false; 123 | } 124 | } 125 | 126 | function display_url($url) { 127 | return preg_replace(['/https?:\/\//','/\/$/'],'',$url); 128 | } 129 | 130 | function pa($a) { 131 | echo '
'; print_r($a); echo '
'; 132 | } 133 | 134 | function irc_notice($msg) { 135 | if(isset(Config::$ircURL)) { 136 | $ch = curl_init(Config::$ircURL); 137 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 138 | curl_setopt($ch, CURLOPT_POST, true); 139 | curl_setopt($ch, CURLOPT_HTTPHEADER, [ 140 | 'Authorization: Bearer ' . Config::$ircToken 141 | ]); 142 | curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array( 143 | 'content' => $msg, 144 | 'channel' => Config::$ircChannel 145 | ))); 146 | curl_exec($ch); 147 | } 148 | } 149 | 150 | /** 151 | * Converts base 10 to base 60. 152 | * http://tantek.pbworks.com/NewBase60 153 | * @param int $n 154 | * @return string 155 | */ 156 | function b10to60($n) 157 | { 158 | $s = ""; 159 | $m = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ_abcdefghijkmnopqrstuvwxyz"; 160 | if ($n==0) 161 | return 0; 162 | 163 | while ($n>0) 164 | { 165 | $d = $n % 60; 166 | $s = $m[$d] . $s; 167 | $n = ($n-$d)/60; 168 | } 169 | return $s; 170 | } 171 | 172 | /** 173 | * Converts base 60 to base 10, with error checking 174 | * http://tantek.pbworks.com/NewBase60 175 | * @param string $s 176 | * @return int 177 | */ 178 | function b60to10($s) 179 | { 180 | $n = 0; 181 | for($i = 0; $i < strlen($s); $i++) // iterate from first to last char of $s 182 | { 183 | $c = ord($s[$i]); // put current ASCII of char into $c 184 | if ($c>=48 && $c<=57) { $c=$c-48; } 185 | else if ($c>=65 && $c<=72) { $c-=55; } 186 | else if ($c==73 || $c==108) { $c=1; } // typo capital I, lowercase l to 1 187 | else if ($c>=74 && $c<=78) { $c-=56; } 188 | else if ($c==79) { $c=0; } // error correct typo capital O to 0 189 | else if ($c>=80 && $c<=90) { $c-=57; } 190 | else if ($c==95) { $c=34; } // underscore 191 | else if ($c>=97 && $c<=107) { $c-=62; } 192 | else if ($c>=109 && $c<=122) { $c-=63; } 193 | else { $c = 0; } // treat all other noise as 0 194 | $n = (60 * $n) + $c; 195 | } 196 | return $n; 197 | } 198 | -------------------------------------------------------------------------------- /lib/mf2.php: -------------------------------------------------------------------------------- 1 | _data = json_decode(json_encode($data)); 9 | else 10 | $this->_data = $data; 11 | $this->_parent = $parent; 12 | } 13 | 14 | public function __get($key) { 15 | $cacheKey = '_'.$key; 16 | 17 | if(method_exists($this, $key) && property_exists($this, $cacheKey)) { 18 | if($this->{$cacheKey} !== null) 19 | return $this->{$cacheKey}; 20 | $val = $this->$key(); 21 | $this->{$cacheKey} = $val; 22 | return $val; 23 | } 24 | 25 | return null; 26 | } 27 | 28 | protected function _is($obj, $type) { 29 | return in_array($type, $obj->type); 30 | } 31 | 32 | protected function _findFirstItem($type) { 33 | foreach($this->_data->items as $i) { 34 | if($this->_is($i, $type)) 35 | return $i; 36 | } 37 | return null; 38 | } 39 | 40 | // Find the property in the 'properties' object and return the values 41 | // If $first is true, returns only the first value, otherwise returns an array 42 | public function property($key, $first=false) { 43 | if($this->_data && property_exists($this->_data, 'properties')) { 44 | if(property_exists($this->_data->properties, $key)) { 45 | if($first) 46 | return $this->_data->properties->{$key}[0]; 47 | else 48 | return $this->_data->properties->{$key}; 49 | } 50 | } 51 | if($first) 52 | return null; 53 | else 54 | return array(); 55 | } 56 | 57 | protected function _stringForProperty($key, $single=false) { 58 | if($content=$this->property($key)) { 59 | if($single) { 60 | return $content[0]; 61 | } else{ 62 | return implode(' ', $content); 63 | } 64 | } 65 | return ''; 66 | } 67 | } 68 | 69 | class MF2Page extends MF2Object { 70 | protected $_hentry = null; 71 | protected $_hevent = null; 72 | protected $_author = null; 73 | 74 | protected function hentry() { 75 | $item = $this->_findFirstItem('h-entry'); 76 | if($item) 77 | return new HEntry($item, $this); 78 | else 79 | return null; 80 | } 81 | 82 | protected function hevent() { 83 | $item = $this->_findFirstItem('h-event'); 84 | if($item) 85 | return new HEvent($item, $this); 86 | else 87 | return null; 88 | } 89 | 90 | protected function author() { 91 | $author = null; 92 | $entry = $this->_findFirstItem('h-entry'); 93 | if($entry) { 94 | if(property_exists($entry->properties, 'author')) { 95 | $author = $entry->properties->author; 96 | } else { 97 | 98 | } 99 | } 100 | } 101 | 102 | } 103 | 104 | class HEntry extends MF2Object { 105 | protected $_content = null; 106 | protected $_published = null; 107 | protected $_author = null; 108 | 109 | protected function content() { 110 | if($content=$this->property('name')) 111 | return implode(' ', $content); 112 | if($content=$this->property('content')) 113 | return implode(' ', $content); 114 | return ''; 115 | } 116 | 117 | protected function published() { 118 | if($time=$this->property('published', true)) 119 | return new DateTime($time); 120 | return null; 121 | } 122 | 123 | // Search the h-entry for an author. If none is found, fall back to the parent's h-card. 124 | protected function author() { 125 | if($author=$this->property('author', true)) 126 | return new HCard($author); 127 | if($author=$this->_parent->_findFirstItem('h-card')) 128 | return new HCard($author); 129 | return null; 130 | } 131 | } 132 | 133 | class HEvent extends MF2Object { 134 | protected $_location = null; 135 | protected $_published = null; 136 | protected $_author = null; 137 | 138 | protected function location() { 139 | $locations = array(); 140 | if($location=$this->property('location')) { 141 | foreach($location as $l) 142 | $locations[] = new HCard($l); 143 | return $locations; 144 | } else { 145 | return null; 146 | } 147 | } 148 | 149 | protected function published() { 150 | if($time=$this->property('published', true)) 151 | return new DateTime($time); 152 | return null; 153 | } 154 | 155 | // Search the h-entry for an author. If none is found, fall back to the parent's h-card. 156 | protected function author() { 157 | if($author=$this->property('author', true)) 158 | return new HCard($author); 159 | if($author=$this->_parent->_findFirstItem('h-card')) 160 | return new HCard($author); 161 | return null; 162 | } 163 | } 164 | 165 | class HCard extends MF2Object { 166 | protected $_name; 167 | protected $_nickname; 168 | protected $_given_name; 169 | protected $_family_name; 170 | protected $_adr; 171 | protected $_note; 172 | protected $_photo; 173 | protected $_url; 174 | 175 | protected function name() { 176 | return $this->_stringForProperty('name', true); 177 | } 178 | 179 | protected function nickname() { 180 | return $this->_stringForProperty('nickname', true); 181 | } 182 | 183 | protected function given_name() { 184 | return $this->_stringForProperty('given-name', true); 185 | } 186 | 187 | protected function family_name() { 188 | return $this->_stringForProperty('family-name', true); 189 | } 190 | 191 | protected function adr() { 192 | return $this->_stringForProperty('adr'. true); 193 | } 194 | 195 | protected function note() { 196 | return $this->_stringForProperty('note', true); 197 | } 198 | 199 | protected function photo() { 200 | return $this->_stringForProperty('photo', true); 201 | } 202 | 203 | protected function url() { 204 | return $this->_stringForProperty('url', true); 205 | } 206 | 207 | } 208 | 209 | class ParserPlus extends mf2\Parser { 210 | public function xpath($query) { 211 | return $this->xpath->query($query); 212 | } 213 | } 214 | 215 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine On 2 | RewriteCond %{REQUEST_FILENAME} !-f 3 | RewriteRule ^ index.php [QSA,L] 4 | -------------------------------------------------------------------------------- /public/bootstrap/css/bootstrap-responsive.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Responsive v2.2.2 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */ 10 | 11 | @-ms-viewport { 12 | width: device-width; 13 | } 14 | 15 | .clearfix { 16 | *zoom: 1; 17 | } 18 | 19 | .clearfix:before, 20 | .clearfix:after { 21 | display: table; 22 | line-height: 0; 23 | content: ""; 24 | } 25 | 26 | .clearfix:after { 27 | clear: both; 28 | } 29 | 30 | .hide-text { 31 | font: 0/0 a; 32 | color: transparent; 33 | text-shadow: none; 34 | background-color: transparent; 35 | border: 0; 36 | } 37 | 38 | .input-block-level { 39 | display: block; 40 | width: 100%; 41 | min-height: 30px; 42 | -webkit-box-sizing: border-box; 43 | -moz-box-sizing: border-box; 44 | box-sizing: border-box; 45 | } 46 | 47 | .hidden { 48 | display: none; 49 | visibility: hidden; 50 | } 51 | 52 | .visible-phone { 53 | display: none !important; 54 | } 55 | 56 | .visible-tablet { 57 | display: none !important; 58 | } 59 | 60 | .hidden-desktop { 61 | display: none !important; 62 | } 63 | 64 | .visible-desktop { 65 | display: inherit !important; 66 | } 67 | 68 | @media (min-width: 768px) and (max-width: 979px) { 69 | .hidden-desktop { 70 | display: inherit !important; 71 | } 72 | .visible-desktop { 73 | display: none !important ; 74 | } 75 | .visible-tablet { 76 | display: inherit !important; 77 | } 78 | .hidden-tablet { 79 | display: none !important; 80 | } 81 | } 82 | 83 | @media (max-width: 767px) { 84 | .hidden-desktop { 85 | display: inherit !important; 86 | } 87 | .visible-desktop { 88 | display: none !important; 89 | } 90 | .visible-phone { 91 | display: inherit !important; 92 | } 93 | .hidden-phone { 94 | display: none !important; 95 | } 96 | } 97 | 98 | @media (min-width: 1200px) { 99 | .row { 100 | margin-left: -30px; 101 | *zoom: 1; 102 | } 103 | .row:before, 104 | .row:after { 105 | display: table; 106 | line-height: 0; 107 | content: ""; 108 | } 109 | .row:after { 110 | clear: both; 111 | } 112 | [class*="span"] { 113 | float: left; 114 | min-height: 1px; 115 | margin-left: 30px; 116 | } 117 | .container, 118 | .navbar-static-top .container, 119 | .navbar-fixed-top .container, 120 | .navbar-fixed-bottom .container { 121 | width: 1170px; 122 | } 123 | .span12 { 124 | width: 1170px; 125 | } 126 | .span11 { 127 | width: 1070px; 128 | } 129 | .span10 { 130 | width: 970px; 131 | } 132 | .span9 { 133 | width: 870px; 134 | } 135 | .span8 { 136 | width: 770px; 137 | } 138 | .span7 { 139 | width: 670px; 140 | } 141 | .span6 { 142 | width: 570px; 143 | } 144 | .span5 { 145 | width: 470px; 146 | } 147 | .span4 { 148 | width: 370px; 149 | } 150 | .span3 { 151 | width: 270px; 152 | } 153 | .span2 { 154 | width: 170px; 155 | } 156 | .span1 { 157 | width: 70px; 158 | } 159 | .offset12 { 160 | margin-left: 1230px; 161 | } 162 | .offset11 { 163 | margin-left: 1130px; 164 | } 165 | .offset10 { 166 | margin-left: 1030px; 167 | } 168 | .offset9 { 169 | margin-left: 930px; 170 | } 171 | .offset8 { 172 | margin-left: 830px; 173 | } 174 | .offset7 { 175 | margin-left: 730px; 176 | } 177 | .offset6 { 178 | margin-left: 630px; 179 | } 180 | .offset5 { 181 | margin-left: 530px; 182 | } 183 | .offset4 { 184 | margin-left: 430px; 185 | } 186 | .offset3 { 187 | margin-left: 330px; 188 | } 189 | .offset2 { 190 | margin-left: 230px; 191 | } 192 | .offset1 { 193 | margin-left: 130px; 194 | } 195 | .row-fluid { 196 | width: 100%; 197 | *zoom: 1; 198 | } 199 | .row-fluid:before, 200 | .row-fluid:after { 201 | display: table; 202 | line-height: 0; 203 | content: ""; 204 | } 205 | .row-fluid:after { 206 | clear: both; 207 | } 208 | .row-fluid [class*="span"] { 209 | display: block; 210 | float: left; 211 | width: 100%; 212 | min-height: 30px; 213 | margin-left: 2.564102564102564%; 214 | *margin-left: 2.5109110747408616%; 215 | -webkit-box-sizing: border-box; 216 | -moz-box-sizing: border-box; 217 | box-sizing: border-box; 218 | } 219 | .row-fluid [class*="span"]:first-child { 220 | margin-left: 0; 221 | } 222 | .row-fluid .controls-row [class*="span"] + [class*="span"] { 223 | margin-left: 2.564102564102564%; 224 | } 225 | .row-fluid .span12 { 226 | width: 100%; 227 | *width: 99.94680851063829%; 228 | } 229 | .row-fluid .span11 { 230 | width: 91.45299145299145%; 231 | *width: 91.39979996362975%; 232 | } 233 | .row-fluid .span10 { 234 | width: 82.90598290598291%; 235 | *width: 82.8527914166212%; 236 | } 237 | .row-fluid .span9 { 238 | width: 74.35897435897436%; 239 | *width: 74.30578286961266%; 240 | } 241 | .row-fluid .span8 { 242 | width: 65.81196581196582%; 243 | *width: 65.75877432260411%; 244 | } 245 | .row-fluid .span7 { 246 | width: 57.26495726495726%; 247 | *width: 57.21176577559556%; 248 | } 249 | .row-fluid .span6 { 250 | width: 48.717948717948715%; 251 | *width: 48.664757228587014%; 252 | } 253 | .row-fluid .span5 { 254 | width: 40.17094017094017%; 255 | *width: 40.11774868157847%; 256 | } 257 | .row-fluid .span4 { 258 | width: 31.623931623931625%; 259 | *width: 31.570740134569924%; 260 | } 261 | .row-fluid .span3 { 262 | width: 23.076923076923077%; 263 | *width: 23.023731587561375%; 264 | } 265 | .row-fluid .span2 { 266 | width: 14.52991452991453%; 267 | *width: 14.476723040552828%; 268 | } 269 | .row-fluid .span1 { 270 | width: 5.982905982905983%; 271 | *width: 5.929714493544281%; 272 | } 273 | .row-fluid .offset12 { 274 | margin-left: 105.12820512820512%; 275 | *margin-left: 105.02182214948171%; 276 | } 277 | .row-fluid .offset12:first-child { 278 | margin-left: 102.56410256410257%; 279 | *margin-left: 102.45771958537915%; 280 | } 281 | .row-fluid .offset11 { 282 | margin-left: 96.58119658119658%; 283 | *margin-left: 96.47481360247316%; 284 | } 285 | .row-fluid .offset11:first-child { 286 | margin-left: 94.01709401709402%; 287 | *margin-left: 93.91071103837061%; 288 | } 289 | .row-fluid .offset10 { 290 | margin-left: 88.03418803418803%; 291 | *margin-left: 87.92780505546462%; 292 | } 293 | .row-fluid .offset10:first-child { 294 | margin-left: 85.47008547008548%; 295 | *margin-left: 85.36370249136206%; 296 | } 297 | .row-fluid .offset9 { 298 | margin-left: 79.48717948717949%; 299 | *margin-left: 79.38079650845607%; 300 | } 301 | .row-fluid .offset9:first-child { 302 | margin-left: 76.92307692307693%; 303 | *margin-left: 76.81669394435352%; 304 | } 305 | .row-fluid .offset8 { 306 | margin-left: 70.94017094017094%; 307 | *margin-left: 70.83378796144753%; 308 | } 309 | .row-fluid .offset8:first-child { 310 | margin-left: 68.37606837606839%; 311 | *margin-left: 68.26968539734497%; 312 | } 313 | .row-fluid .offset7 { 314 | margin-left: 62.393162393162385%; 315 | *margin-left: 62.28677941443899%; 316 | } 317 | .row-fluid .offset7:first-child { 318 | margin-left: 59.82905982905982%; 319 | *margin-left: 59.72267685033642%; 320 | } 321 | .row-fluid .offset6 { 322 | margin-left: 53.84615384615384%; 323 | *margin-left: 53.739770867430444%; 324 | } 325 | .row-fluid .offset6:first-child { 326 | margin-left: 51.28205128205128%; 327 | *margin-left: 51.175668303327875%; 328 | } 329 | .row-fluid .offset5 { 330 | margin-left: 45.299145299145295%; 331 | *margin-left: 45.1927623204219%; 332 | } 333 | .row-fluid .offset5:first-child { 334 | margin-left: 42.73504273504273%; 335 | *margin-left: 42.62865975631933%; 336 | } 337 | .row-fluid .offset4 { 338 | margin-left: 36.75213675213675%; 339 | *margin-left: 36.645753773413354%; 340 | } 341 | .row-fluid .offset4:first-child { 342 | margin-left: 34.18803418803419%; 343 | *margin-left: 34.081651209310785%; 344 | } 345 | .row-fluid .offset3 { 346 | margin-left: 28.205128205128204%; 347 | *margin-left: 28.0987452264048%; 348 | } 349 | .row-fluid .offset3:first-child { 350 | margin-left: 25.641025641025642%; 351 | *margin-left: 25.53464266230224%; 352 | } 353 | .row-fluid .offset2 { 354 | margin-left: 19.65811965811966%; 355 | *margin-left: 19.551736679396257%; 356 | } 357 | .row-fluid .offset2:first-child { 358 | margin-left: 17.094017094017094%; 359 | *margin-left: 16.98763411529369%; 360 | } 361 | .row-fluid .offset1 { 362 | margin-left: 11.11111111111111%; 363 | *margin-left: 11.004728132387708%; 364 | } 365 | .row-fluid .offset1:first-child { 366 | margin-left: 8.547008547008547%; 367 | *margin-left: 8.440625568285142%; 368 | } 369 | input, 370 | textarea, 371 | .uneditable-input { 372 | margin-left: 0; 373 | } 374 | .controls-row [class*="span"] + [class*="span"] { 375 | margin-left: 30px; 376 | } 377 | input.span12, 378 | textarea.span12, 379 | .uneditable-input.span12 { 380 | width: 1156px; 381 | } 382 | input.span11, 383 | textarea.span11, 384 | .uneditable-input.span11 { 385 | width: 1056px; 386 | } 387 | input.span10, 388 | textarea.span10, 389 | .uneditable-input.span10 { 390 | width: 956px; 391 | } 392 | input.span9, 393 | textarea.span9, 394 | .uneditable-input.span9 { 395 | width: 856px; 396 | } 397 | input.span8, 398 | textarea.span8, 399 | .uneditable-input.span8 { 400 | width: 756px; 401 | } 402 | input.span7, 403 | textarea.span7, 404 | .uneditable-input.span7 { 405 | width: 656px; 406 | } 407 | input.span6, 408 | textarea.span6, 409 | .uneditable-input.span6 { 410 | width: 556px; 411 | } 412 | input.span5, 413 | textarea.span5, 414 | .uneditable-input.span5 { 415 | width: 456px; 416 | } 417 | input.span4, 418 | textarea.span4, 419 | .uneditable-input.span4 { 420 | width: 356px; 421 | } 422 | input.span3, 423 | textarea.span3, 424 | .uneditable-input.span3 { 425 | width: 256px; 426 | } 427 | input.span2, 428 | textarea.span2, 429 | .uneditable-input.span2 { 430 | width: 156px; 431 | } 432 | input.span1, 433 | textarea.span1, 434 | .uneditable-input.span1 { 435 | width: 56px; 436 | } 437 | .thumbnails { 438 | margin-left: -30px; 439 | } 440 | .thumbnails > li { 441 | margin-left: 30px; 442 | } 443 | .row-fluid .thumbnails { 444 | margin-left: 0; 445 | } 446 | } 447 | 448 | @media (min-width: 768px) and (max-width: 979px) { 449 | .row { 450 | margin-left: -20px; 451 | *zoom: 1; 452 | } 453 | .row:before, 454 | .row:after { 455 | display: table; 456 | line-height: 0; 457 | content: ""; 458 | } 459 | .row:after { 460 | clear: both; 461 | } 462 | [class*="span"] { 463 | float: left; 464 | min-height: 1px; 465 | margin-left: 20px; 466 | } 467 | .container, 468 | .navbar-static-top .container, 469 | .navbar-fixed-top .container, 470 | .navbar-fixed-bottom .container { 471 | width: 724px; 472 | } 473 | .span12 { 474 | width: 724px; 475 | } 476 | .span11 { 477 | width: 662px; 478 | } 479 | .span10 { 480 | width: 600px; 481 | } 482 | .span9 { 483 | width: 538px; 484 | } 485 | .span8 { 486 | width: 476px; 487 | } 488 | .span7 { 489 | width: 414px; 490 | } 491 | .span6 { 492 | width: 352px; 493 | } 494 | .span5 { 495 | width: 290px; 496 | } 497 | .span4 { 498 | width: 228px; 499 | } 500 | .span3 { 501 | width: 166px; 502 | } 503 | .span2 { 504 | width: 104px; 505 | } 506 | .span1 { 507 | width: 42px; 508 | } 509 | .offset12 { 510 | margin-left: 764px; 511 | } 512 | .offset11 { 513 | margin-left: 702px; 514 | } 515 | .offset10 { 516 | margin-left: 640px; 517 | } 518 | .offset9 { 519 | margin-left: 578px; 520 | } 521 | .offset8 { 522 | margin-left: 516px; 523 | } 524 | .offset7 { 525 | margin-left: 454px; 526 | } 527 | .offset6 { 528 | margin-left: 392px; 529 | } 530 | .offset5 { 531 | margin-left: 330px; 532 | } 533 | .offset4 { 534 | margin-left: 268px; 535 | } 536 | .offset3 { 537 | margin-left: 206px; 538 | } 539 | .offset2 { 540 | margin-left: 144px; 541 | } 542 | .offset1 { 543 | margin-left: 82px; 544 | } 545 | .row-fluid { 546 | width: 100%; 547 | *zoom: 1; 548 | } 549 | .row-fluid:before, 550 | .row-fluid:after { 551 | display: table; 552 | line-height: 0; 553 | content: ""; 554 | } 555 | .row-fluid:after { 556 | clear: both; 557 | } 558 | .row-fluid [class*="span"] { 559 | display: block; 560 | float: left; 561 | width: 100%; 562 | min-height: 30px; 563 | margin-left: 2.7624309392265194%; 564 | *margin-left: 2.709239449864817%; 565 | -webkit-box-sizing: border-box; 566 | -moz-box-sizing: border-box; 567 | box-sizing: border-box; 568 | } 569 | .row-fluid [class*="span"]:first-child { 570 | margin-left: 0; 571 | } 572 | .row-fluid .controls-row [class*="span"] + [class*="span"] { 573 | margin-left: 2.7624309392265194%; 574 | } 575 | .row-fluid .span12 { 576 | width: 100%; 577 | *width: 99.94680851063829%; 578 | } 579 | .row-fluid .span11 { 580 | width: 91.43646408839778%; 581 | *width: 91.38327259903608%; 582 | } 583 | .row-fluid .span10 { 584 | width: 82.87292817679558%; 585 | *width: 82.81973668743387%; 586 | } 587 | .row-fluid .span9 { 588 | width: 74.30939226519337%; 589 | *width: 74.25620077583166%; 590 | } 591 | .row-fluid .span8 { 592 | width: 65.74585635359117%; 593 | *width: 65.69266486422946%; 594 | } 595 | .row-fluid .span7 { 596 | width: 57.18232044198895%; 597 | *width: 57.12912895262725%; 598 | } 599 | .row-fluid .span6 { 600 | width: 48.61878453038674%; 601 | *width: 48.56559304102504%; 602 | } 603 | .row-fluid .span5 { 604 | width: 40.05524861878453%; 605 | *width: 40.00205712942283%; 606 | } 607 | .row-fluid .span4 { 608 | width: 31.491712707182323%; 609 | *width: 31.43852121782062%; 610 | } 611 | .row-fluid .span3 { 612 | width: 22.92817679558011%; 613 | *width: 22.87498530621841%; 614 | } 615 | .row-fluid .span2 { 616 | width: 14.3646408839779%; 617 | *width: 14.311449394616199%; 618 | } 619 | .row-fluid .span1 { 620 | width: 5.801104972375691%; 621 | *width: 5.747913483013988%; 622 | } 623 | .row-fluid .offset12 { 624 | margin-left: 105.52486187845304%; 625 | *margin-left: 105.41847889972962%; 626 | } 627 | .row-fluid .offset12:first-child { 628 | margin-left: 102.76243093922652%; 629 | *margin-left: 102.6560479605031%; 630 | } 631 | .row-fluid .offset11 { 632 | margin-left: 96.96132596685082%; 633 | *margin-left: 96.8549429881274%; 634 | } 635 | .row-fluid .offset11:first-child { 636 | margin-left: 94.1988950276243%; 637 | *margin-left: 94.09251204890089%; 638 | } 639 | .row-fluid .offset10 { 640 | margin-left: 88.39779005524862%; 641 | *margin-left: 88.2914070765252%; 642 | } 643 | .row-fluid .offset10:first-child { 644 | margin-left: 85.6353591160221%; 645 | *margin-left: 85.52897613729868%; 646 | } 647 | .row-fluid .offset9 { 648 | margin-left: 79.8342541436464%; 649 | *margin-left: 79.72787116492299%; 650 | } 651 | .row-fluid .offset9:first-child { 652 | margin-left: 77.07182320441989%; 653 | *margin-left: 76.96544022569647%; 654 | } 655 | .row-fluid .offset8 { 656 | margin-left: 71.2707182320442%; 657 | *margin-left: 71.16433525332079%; 658 | } 659 | .row-fluid .offset8:first-child { 660 | margin-left: 68.50828729281768%; 661 | *margin-left: 68.40190431409427%; 662 | } 663 | .row-fluid .offset7 { 664 | margin-left: 62.70718232044199%; 665 | *margin-left: 62.600799341718584%; 666 | } 667 | .row-fluid .offset7:first-child { 668 | margin-left: 59.94475138121547%; 669 | *margin-left: 59.838368402492065%; 670 | } 671 | .row-fluid .offset6 { 672 | margin-left: 54.14364640883978%; 673 | *margin-left: 54.037263430116376%; 674 | } 675 | .row-fluid .offset6:first-child { 676 | margin-left: 51.38121546961326%; 677 | *margin-left: 51.27483249088986%; 678 | } 679 | .row-fluid .offset5 { 680 | margin-left: 45.58011049723757%; 681 | *margin-left: 45.47372751851417%; 682 | } 683 | .row-fluid .offset5:first-child { 684 | margin-left: 42.81767955801105%; 685 | *margin-left: 42.71129657928765%; 686 | } 687 | .row-fluid .offset4 { 688 | margin-left: 37.01657458563536%; 689 | *margin-left: 36.91019160691196%; 690 | } 691 | .row-fluid .offset4:first-child { 692 | margin-left: 34.25414364640884%; 693 | *margin-left: 34.14776066768544%; 694 | } 695 | .row-fluid .offset3 { 696 | margin-left: 28.45303867403315%; 697 | *margin-left: 28.346655695309746%; 698 | } 699 | .row-fluid .offset3:first-child { 700 | margin-left: 25.69060773480663%; 701 | *margin-left: 25.584224756083227%; 702 | } 703 | .row-fluid .offset2 { 704 | margin-left: 19.88950276243094%; 705 | *margin-left: 19.783119783707537%; 706 | } 707 | .row-fluid .offset2:first-child { 708 | margin-left: 17.12707182320442%; 709 | *margin-left: 17.02068884448102%; 710 | } 711 | .row-fluid .offset1 { 712 | margin-left: 11.32596685082873%; 713 | *margin-left: 11.219583872105325%; 714 | } 715 | .row-fluid .offset1:first-child { 716 | margin-left: 8.56353591160221%; 717 | *margin-left: 8.457152932878806%; 718 | } 719 | input, 720 | textarea, 721 | .uneditable-input { 722 | margin-left: 0; 723 | } 724 | .controls-row [class*="span"] + [class*="span"] { 725 | margin-left: 20px; 726 | } 727 | input.span12, 728 | textarea.span12, 729 | .uneditable-input.span12 { 730 | width: 710px; 731 | } 732 | input.span11, 733 | textarea.span11, 734 | .uneditable-input.span11 { 735 | width: 648px; 736 | } 737 | input.span10, 738 | textarea.span10, 739 | .uneditable-input.span10 { 740 | width: 586px; 741 | } 742 | input.span9, 743 | textarea.span9, 744 | .uneditable-input.span9 { 745 | width: 524px; 746 | } 747 | input.span8, 748 | textarea.span8, 749 | .uneditable-input.span8 { 750 | width: 462px; 751 | } 752 | input.span7, 753 | textarea.span7, 754 | .uneditable-input.span7 { 755 | width: 400px; 756 | } 757 | input.span6, 758 | textarea.span6, 759 | .uneditable-input.span6 { 760 | width: 338px; 761 | } 762 | input.span5, 763 | textarea.span5, 764 | .uneditable-input.span5 { 765 | width: 276px; 766 | } 767 | input.span4, 768 | textarea.span4, 769 | .uneditable-input.span4 { 770 | width: 214px; 771 | } 772 | input.span3, 773 | textarea.span3, 774 | .uneditable-input.span3 { 775 | width: 152px; 776 | } 777 | input.span2, 778 | textarea.span2, 779 | .uneditable-input.span2 { 780 | width: 90px; 781 | } 782 | input.span1, 783 | textarea.span1, 784 | .uneditable-input.span1 { 785 | width: 28px; 786 | } 787 | } 788 | 789 | @media (max-width: 767px) { 790 | body { 791 | padding-right: 20px; 792 | padding-left: 20px; 793 | } 794 | .navbar-fixed-top, 795 | .navbar-fixed-bottom, 796 | .navbar-static-top { 797 | margin-right: -20px; 798 | margin-left: -20px; 799 | } 800 | .container-fluid { 801 | padding: 0; 802 | } 803 | .dl-horizontal dt { 804 | float: none; 805 | width: auto; 806 | clear: none; 807 | text-align: left; 808 | } 809 | .dl-horizontal dd { 810 | margin-left: 0; 811 | } 812 | .container { 813 | width: auto; 814 | } 815 | .row-fluid { 816 | width: 100%; 817 | } 818 | .row, 819 | .thumbnails { 820 | margin-left: 0; 821 | } 822 | .thumbnails > li { 823 | float: none; 824 | margin-left: 0; 825 | } 826 | [class*="span"], 827 | .uneditable-input[class*="span"], 828 | .row-fluid [class*="span"] { 829 | display: block; 830 | float: none; 831 | width: 100%; 832 | margin-left: 0; 833 | -webkit-box-sizing: border-box; 834 | -moz-box-sizing: border-box; 835 | box-sizing: border-box; 836 | } 837 | .span12, 838 | .row-fluid .span12 { 839 | width: 100%; 840 | -webkit-box-sizing: border-box; 841 | -moz-box-sizing: border-box; 842 | box-sizing: border-box; 843 | } 844 | .row-fluid [class*="offset"]:first-child { 845 | margin-left: 0; 846 | } 847 | .input-large, 848 | .input-xlarge, 849 | .input-xxlarge, 850 | input[class*="span"], 851 | select[class*="span"], 852 | textarea[class*="span"], 853 | .uneditable-input { 854 | display: block; 855 | width: 100%; 856 | min-height: 30px; 857 | -webkit-box-sizing: border-box; 858 | -moz-box-sizing: border-box; 859 | box-sizing: border-box; 860 | } 861 | .input-prepend input, 862 | .input-append input, 863 | .input-prepend input[class*="span"], 864 | .input-append input[class*="span"] { 865 | display: inline-block; 866 | width: auto; 867 | } 868 | .controls-row [class*="span"] + [class*="span"] { 869 | margin-left: 0; 870 | } 871 | .modal { 872 | position: fixed; 873 | top: 20px; 874 | right: 20px; 875 | left: 20px; 876 | width: auto; 877 | margin: 0; 878 | } 879 | .modal.fade { 880 | top: -100px; 881 | } 882 | .modal.fade.in { 883 | top: 20px; 884 | } 885 | } 886 | 887 | @media (max-width: 480px) { 888 | .nav-collapse { 889 | -webkit-transform: translate3d(0, 0, 0); 890 | } 891 | .page-header h1 small { 892 | display: block; 893 | line-height: 20px; 894 | } 895 | input[type="checkbox"], 896 | input[type="radio"] { 897 | border: 1px solid #ccc; 898 | } 899 | .form-horizontal .control-label { 900 | float: none; 901 | width: auto; 902 | padding-top: 0; 903 | text-align: left; 904 | } 905 | .form-horizontal .controls { 906 | margin-left: 0; 907 | } 908 | .form-horizontal .control-list { 909 | padding-top: 0; 910 | } 911 | .form-horizontal .form-actions { 912 | padding-right: 10px; 913 | padding-left: 10px; 914 | } 915 | .media .pull-left, 916 | .media .pull-right { 917 | display: block; 918 | float: none; 919 | margin-bottom: 10px; 920 | } 921 | .media-object { 922 | margin-right: 0; 923 | margin-left: 0; 924 | } 925 | .modal { 926 | top: 10px; 927 | right: 10px; 928 | left: 10px; 929 | } 930 | .modal-header .close { 931 | padding: 10px; 932 | margin: -10px; 933 | } 934 | .carousel-caption { 935 | position: static; 936 | } 937 | } 938 | 939 | @media (max-width: 979px) { 940 | body { 941 | padding-top: 0; 942 | } 943 | .navbar-fixed-top, 944 | .navbar-fixed-bottom { 945 | position: static; 946 | } 947 | .navbar-fixed-top { 948 | margin-bottom: 20px; 949 | } 950 | .navbar-fixed-bottom { 951 | margin-top: 20px; 952 | } 953 | .navbar-fixed-top .navbar-inner, 954 | .navbar-fixed-bottom .navbar-inner { 955 | padding: 5px; 956 | } 957 | .navbar .container { 958 | width: auto; 959 | padding: 0; 960 | } 961 | .navbar .brand { 962 | padding-right: 10px; 963 | padding-left: 10px; 964 | margin: 0 0 0 -5px; 965 | } 966 | .nav-collapse { 967 | clear: both; 968 | } 969 | .nav-collapse .nav { 970 | float: none; 971 | margin: 0 0 10px; 972 | } 973 | .nav-collapse .nav > li { 974 | float: none; 975 | } 976 | .nav-collapse .nav > li > a { 977 | margin-bottom: 2px; 978 | } 979 | .nav-collapse .nav > .divider-vertical { 980 | display: none; 981 | } 982 | .nav-collapse .nav .nav-header { 983 | color: #777777; 984 | text-shadow: none; 985 | } 986 | .nav-collapse .nav > li > a, 987 | .nav-collapse .dropdown-menu a { 988 | padding: 9px 15px; 989 | font-weight: bold; 990 | color: #777777; 991 | -webkit-border-radius: 3px; 992 | -moz-border-radius: 3px; 993 | border-radius: 3px; 994 | } 995 | .nav-collapse .btn { 996 | padding: 4px 10px 4px; 997 | font-weight: normal; 998 | -webkit-border-radius: 4px; 999 | -moz-border-radius: 4px; 1000 | border-radius: 4px; 1001 | } 1002 | .nav-collapse .dropdown-menu li + li a { 1003 | margin-bottom: 2px; 1004 | } 1005 | .nav-collapse .nav > li > a:hover, 1006 | .nav-collapse .dropdown-menu a:hover { 1007 | background-color: #f2f2f2; 1008 | } 1009 | .navbar-inverse .nav-collapse .nav > li > a, 1010 | .navbar-inverse .nav-collapse .dropdown-menu a { 1011 | color: #999999; 1012 | } 1013 | .navbar-inverse .nav-collapse .nav > li > a:hover, 1014 | .navbar-inverse .nav-collapse .dropdown-menu a:hover { 1015 | background-color: #111111; 1016 | } 1017 | .nav-collapse.in .btn-group { 1018 | padding: 0; 1019 | margin-top: 5px; 1020 | } 1021 | .nav-collapse .dropdown-menu { 1022 | position: static; 1023 | top: auto; 1024 | left: auto; 1025 | display: none; 1026 | float: none; 1027 | max-width: none; 1028 | padding: 0; 1029 | margin: 0 15px; 1030 | background-color: transparent; 1031 | border: none; 1032 | -webkit-border-radius: 0; 1033 | -moz-border-radius: 0; 1034 | border-radius: 0; 1035 | -webkit-box-shadow: none; 1036 | -moz-box-shadow: none; 1037 | box-shadow: none; 1038 | } 1039 | .nav-collapse .open > .dropdown-menu { 1040 | display: block; 1041 | } 1042 | .nav-collapse .dropdown-menu:before, 1043 | .nav-collapse .dropdown-menu:after { 1044 | display: none; 1045 | } 1046 | .nav-collapse .dropdown-menu .divider { 1047 | display: none; 1048 | } 1049 | .nav-collapse .nav > li > .dropdown-menu:before, 1050 | .nav-collapse .nav > li > .dropdown-menu:after { 1051 | display: none; 1052 | } 1053 | .nav-collapse .navbar-form, 1054 | .nav-collapse .navbar-search { 1055 | float: none; 1056 | padding: 10px 15px; 1057 | margin: 10px 0; 1058 | border-top: 1px solid #f2f2f2; 1059 | border-bottom: 1px solid #f2f2f2; 1060 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); 1061 | -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); 1062 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); 1063 | } 1064 | .navbar-inverse .nav-collapse .navbar-form, 1065 | .navbar-inverse .nav-collapse .navbar-search { 1066 | border-top-color: #111111; 1067 | border-bottom-color: #111111; 1068 | } 1069 | .navbar .nav-collapse .nav.pull-right { 1070 | float: none; 1071 | margin-left: 0; 1072 | } 1073 | .nav-collapse, 1074 | .nav-collapse.collapse { 1075 | height: 0; 1076 | overflow: hidden; 1077 | } 1078 | .navbar .btn-navbar { 1079 | display: block; 1080 | } 1081 | .navbar-static .navbar-inner { 1082 | padding-right: 10px; 1083 | padding-left: 10px; 1084 | } 1085 | } 1086 | 1087 | @media (min-width: 980px) { 1088 | .nav-collapse.collapse { 1089 | height: auto !important; 1090 | overflow: visible !important; 1091 | } 1092 | } 1093 | -------------------------------------------------------------------------------- /public/bootstrap/css/bootstrap-responsive.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Responsive v2.2.2 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */@-ms-viewport{width:device-width}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .dropdown-menu a:hover{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:hover{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}} 10 | -------------------------------------------------------------------------------- /public/bootstrap/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronpk/IndieNews/be5543af847ef6974d8ccc1a7d533536e3e80023/public/bootstrap/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /public/bootstrap/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronpk/IndieNews/be5543af847ef6974d8ccc1a7d533536e3e80023/public/bootstrap/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /public/bootstrap/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap.js by @fat & @mdo 3 | * Copyright 2012 Twitter, Inc. 4 | * http://www.apache.org/licenses/LICENSE-2.0.txt 5 | */ 6 | !function($){"use strict";$(function(){$.support.transition=function(){var transitionEnd=function(){var name,el=document.createElement("bootstrap"),transEndEventNames={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(name in transEndEventNames)if(void 0!==el.style[name])return transEndEventNames[name]}();return transitionEnd&&{end:transitionEnd}}()})}(window.jQuery),!function($){"use strict";var dismiss='[data-dismiss="alert"]',Alert=function(el){$(el).on("click",dismiss,this.close)};Alert.prototype.close=function(e){function removeElement(){$parent.trigger("closed").remove()}var $parent,$this=$(this),selector=$this.attr("data-target");selector||(selector=$this.attr("href"),selector=selector&&selector.replace(/.*(?=#[^\s]*$)/,"")),$parent=$(selector),e&&e.preventDefault(),$parent.length||($parent=$this.hasClass("alert")?$this:$this.parent()),$parent.trigger(e=$.Event("close")),e.isDefaultPrevented()||($parent.removeClass("in"),$.support.transition&&$parent.hasClass("fade")?$parent.on($.support.transition.end,removeElement):removeElement())};var old=$.fn.alert;$.fn.alert=function(option){return this.each(function(){var $this=$(this),data=$this.data("alert");data||$this.data("alert",data=new Alert(this)),"string"==typeof option&&data[option].call($this)})},$.fn.alert.Constructor=Alert,$.fn.alert.noConflict=function(){return $.fn.alert=old,this},$(document).on("click.alert.data-api",dismiss,Alert.prototype.close)}(window.jQuery),!function($){"use strict";var Button=function(element,options){this.$element=$(element),this.options=$.extend({},$.fn.button.defaults,options)};Button.prototype.setState=function(state){var d="disabled",$el=this.$element,data=$el.data(),val=$el.is("input")?"val":"html";state+="Text",data.resetText||$el.data("resetText",$el[val]()),$el[val](data[state]||this.options[state]),setTimeout(function(){"loadingText"==state?$el.addClass(d).attr(d,d):$el.removeClass(d).removeAttr(d)},0)},Button.prototype.toggle=function(){var $parent=this.$element.closest('[data-toggle="buttons-radio"]');$parent&&$parent.find(".active").removeClass("active"),this.$element.toggleClass("active")};var old=$.fn.button;$.fn.button=function(option){return this.each(function(){var $this=$(this),data=$this.data("button"),options="object"==typeof option&&option;data||$this.data("button",data=new Button(this,options)),"toggle"==option?data.toggle():option&&data.setState(option)})},$.fn.button.defaults={loadingText:"loading..."},$.fn.button.Constructor=Button,$.fn.button.noConflict=function(){return $.fn.button=old,this},$(document).on("click.button.data-api","[data-toggle^=button]",function(e){var $btn=$(e.target);$btn.hasClass("btn")||($btn=$btn.closest(".btn")),$btn.button("toggle")})}(window.jQuery),!function($){"use strict";var Carousel=function(element,options){this.$element=$(element),this.options=options,"hover"==this.options.pause&&this.$element.on("mouseenter",$.proxy(this.pause,this)).on("mouseleave",$.proxy(this.cycle,this))};Carousel.prototype={cycle:function(e){return e||(this.paused=!1),this.options.interval&&!this.paused&&(this.interval=setInterval($.proxy(this.next,this),this.options.interval)),this},to:function(pos){var $active=this.$element.find(".item.active"),children=$active.parent().children(),activePos=children.index($active),that=this;if(!(pos>children.length-1||0>pos))return this.sliding?this.$element.one("slid",function(){that.to(pos)}):activePos==pos?this.pause().cycle():this.slide(pos>activePos?"next":"prev",$(children[pos]))},pause:function(e){return e||(this.paused=!0),this.$element.find(".next, .prev").length&&$.support.transition.end&&(this.$element.trigger($.support.transition.end),this.cycle()),clearInterval(this.interval),this.interval=null,this},next:function(){return this.sliding?void 0:this.slide("next")},prev:function(){return this.sliding?void 0:this.slide("prev")},slide:function(type,next){var e,$active=this.$element.find(".item.active"),$next=next||$active[type](),isCycling=this.interval,direction="next"==type?"left":"right",fallback="next"==type?"first":"last",that=this;if(this.sliding=!0,isCycling&&this.pause(),$next=$next.length?$next:this.$element.find(".item")[fallback](),e=$.Event("slide",{relatedTarget:$next[0]}),!$next.hasClass("active")){if($.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(e),e.isDefaultPrevented())return;$next.addClass(type),$next[0].offsetWidth,$active.addClass(direction),$next.addClass(direction),this.$element.one($.support.transition.end,function(){$next.removeClass([type,direction].join(" ")).addClass("active"),$active.removeClass(["active",direction].join(" ")),that.sliding=!1,setTimeout(function(){that.$element.trigger("slid")},0)})}else{if(this.$element.trigger(e),e.isDefaultPrevented())return;$active.removeClass("active"),$next.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return isCycling&&this.cycle(),this}}};var old=$.fn.carousel;$.fn.carousel=function(option){return this.each(function(){var $this=$(this),data=$this.data("carousel"),options=$.extend({},$.fn.carousel.defaults,"object"==typeof option&&option),action="string"==typeof option?option:options.slide;data||$this.data("carousel",data=new Carousel(this,options)),"number"==typeof option?data.to(option):action?data[action]():options.interval&&data.cycle()})},$.fn.carousel.defaults={interval:5e3,pause:"hover"},$.fn.carousel.Constructor=Carousel,$.fn.carousel.noConflict=function(){return $.fn.carousel=old,this},$(document).on("click.carousel.data-api","[data-slide]",function(e){var href,$this=$(this),$target=$($this.attr("data-target")||(href=$this.attr("href"))&&href.replace(/.*(?=#[^\s]+$)/,"")),options=$.extend({},$target.data(),$this.data());$target.carousel(options),e.preventDefault()})}(window.jQuery),!function($){"use strict";var Collapse=function(element,options){this.$element=$(element),this.options=$.extend({},$.fn.collapse.defaults,options),this.options.parent&&(this.$parent=$(this.options.parent)),this.options.toggle&&this.toggle()};Collapse.prototype={constructor:Collapse,dimension:function(){var hasWidth=this.$element.hasClass("width");return hasWidth?"width":"height"},show:function(){var dimension,scroll,actives,hasData;if(!this.transitioning){if(dimension=this.dimension(),scroll=$.camelCase(["scroll",dimension].join("-")),actives=this.$parent&&this.$parent.find("> .accordion-group > .in"),actives&&actives.length){if(hasData=actives.data("collapse"),hasData&&hasData.transitioning)return;actives.collapse("hide"),hasData||actives.data("collapse",null)}this.$element[dimension](0),this.transition("addClass",$.Event("show"),"shown"),$.support.transition&&this.$element[dimension](this.$element[0][scroll])}},hide:function(){var dimension;this.transitioning||(dimension=this.dimension(),this.reset(this.$element[dimension]()),this.transition("removeClass",$.Event("hide"),"hidden"),this.$element[dimension](0))},reset:function(size){var dimension=this.dimension();return this.$element.removeClass("collapse")[dimension](size||"auto")[0].offsetWidth,this.$element[null!==size?"addClass":"removeClass"]("collapse"),this},transition:function(method,startEvent,completeEvent){var that=this,complete=function(){"show"==startEvent.type&&that.reset(),that.transitioning=0,that.$element.trigger(completeEvent)};this.$element.trigger(startEvent),startEvent.isDefaultPrevented()||(this.transitioning=1,this.$element[method]("in"),$.support.transition&&this.$element.hasClass("collapse")?this.$element.one($.support.transition.end,complete):complete())},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var old=$.fn.collapse;$.fn.collapse=function(option){return this.each(function(){var $this=$(this),data=$this.data("collapse"),options="object"==typeof option&&option;data||$this.data("collapse",data=new Collapse(this,options)),"string"==typeof option&&data[option]()})},$.fn.collapse.defaults={toggle:!0},$.fn.collapse.Constructor=Collapse,$.fn.collapse.noConflict=function(){return $.fn.collapse=old,this},$(document).on("click.collapse.data-api","[data-toggle=collapse]",function(e){var href,$this=$(this),target=$this.attr("data-target")||e.preventDefault()||(href=$this.attr("href"))&&href.replace(/.*(?=#[^\s]+$)/,""),option=$(target).data("collapse")?"toggle":$this.data();$this[$(target).hasClass("in")?"addClass":"removeClass"]("collapsed"),$(target).collapse(option)})}(window.jQuery),!function($){"use strict";function clearMenus(){$(toggle).each(function(){getParent($(this)).removeClass("open")})}function getParent($this){var $parent,selector=$this.attr("data-target");return selector||(selector=$this.attr("href"),selector=selector&&/#/.test(selector)&&selector.replace(/.*(?=#[^\s]*$)/,"")),$parent=$(selector),$parent.length||($parent=$this.parent()),$parent}var toggle="[data-toggle=dropdown]",Dropdown=function(element){var $el=$(element).on("click.dropdown.data-api",this.toggle);$("html").on("click.dropdown.data-api",function(){$el.parent().removeClass("open")})};Dropdown.prototype={constructor:Dropdown,toggle:function(){var $parent,isActive,$this=$(this);if(!$this.is(".disabled, :disabled"))return $parent=getParent($this),isActive=$parent.hasClass("open"),clearMenus(),isActive||$parent.toggleClass("open"),$this.focus(),!1},keydown:function(e){var $this,$items,$parent,isActive,index;if(/(38|40|27)/.test(e.keyCode)&&($this=$(this),e.preventDefault(),e.stopPropagation(),!$this.is(".disabled, :disabled"))){if($parent=getParent($this),isActive=$parent.hasClass("open"),!isActive||isActive&&27==e.keyCode)return $this.click();$items=$("[role=menu] li:not(.divider):visible a",$parent),$items.length&&(index=$items.index($items.filter(":focus")),38==e.keyCode&&index>0&&index--,40==e.keyCode&&$items.length-1>index&&index++,~index||(index=0),$items.eq(index).focus())}}};var old=$.fn.dropdown;$.fn.dropdown=function(option){return this.each(function(){var $this=$(this),data=$this.data("dropdown");data||$this.data("dropdown",data=new Dropdown(this)),"string"==typeof option&&data[option].call($this)})},$.fn.dropdown.Constructor=Dropdown,$.fn.dropdown.noConflict=function(){return $.fn.dropdown=old,this},$(document).on("click.dropdown.data-api touchstart.dropdown.data-api",clearMenus).on("click.dropdown touchstart.dropdown.data-api",".dropdown form",function(e){e.stopPropagation()}).on("touchstart.dropdown.data-api",".dropdown-menu",function(e){e.stopPropagation()}).on("click.dropdown.data-api touchstart.dropdown.data-api",toggle,Dropdown.prototype.toggle).on("keydown.dropdown.data-api touchstart.dropdown.data-api",toggle+", [role=menu]",Dropdown.prototype.keydown)}(window.jQuery),!function($){"use strict";var Modal=function(element,options){this.options=options,this.$element=$(element).delegate('[data-dismiss="modal"]',"click.dismiss.modal",$.proxy(this.hide,this)),this.options.remote&&this.$element.find(".modal-body").load(this.options.remote)};Modal.prototype={constructor:Modal,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var that=this,e=$.Event("show");this.$element.trigger(e),this.isShown||e.isDefaultPrevented()||(this.isShown=!0,this.escape(),this.backdrop(function(){var transition=$.support.transition&&that.$element.hasClass("fade");that.$element.parent().length||that.$element.appendTo(document.body),that.$element.show(),transition&&that.$element[0].offsetWidth,that.$element.addClass("in").attr("aria-hidden",!1),that.enforceFocus(),transition?that.$element.one($.support.transition.end,function(){that.$element.focus().trigger("shown")}):that.$element.focus().trigger("shown")}))},hide:function(e){e&&e.preventDefault(),e=$.Event("hide"),this.$element.trigger(e),this.isShown&&!e.isDefaultPrevented()&&(this.isShown=!1,this.escape(),$(document).off("focusin.modal"),this.$element.removeClass("in").attr("aria-hidden",!0),$.support.transition&&this.$element.hasClass("fade")?this.hideWithTransition():this.hideModal())},enforceFocus:function(){var that=this;$(document).on("focusin.modal",function(e){that.$element[0]===e.target||that.$element.has(e.target).length||that.$element.focus()})},escape:function(){var that=this;this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.modal",function(e){27==e.which&&that.hide()}):this.isShown||this.$element.off("keyup.dismiss.modal")},hideWithTransition:function(){var that=this,timeout=setTimeout(function(){that.$element.off($.support.transition.end),that.hideModal()},500);this.$element.one($.support.transition.end,function(){clearTimeout(timeout),that.hideModal()})},hideModal:function(){this.$element.hide().trigger("hidden"),this.backdrop()},removeBackdrop:function(){this.$backdrop.remove(),this.$backdrop=null},backdrop:function(callback){var animate=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var doAnimate=$.support.transition&&animate;this.$backdrop=$('