├── .gitignore ├── Procfile ├── README.md ├── package.json ├── resources └── noterlive.css ├── web.js └── web ├── bootstrap.css ├── cassis.js ├── hovercard.css ├── hovercards.js ├── index.fr.html ├── index.html ├── noterlive.js ├── noterlive.png ├── noterlive.svg ├── noterlive114.png ├── noterlive512.png └── tweetembed.css /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .svn 3 | log/*.log 4 | tmp/** 5 | node_modules/ 6 | .sass-cache 7 | .env -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node web.js -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | noterlive 2 | ========= 3 | 4 | A tool for indieweb live noting (aka live tweeting/live blogging). 5 | 6 | To try it go to http://www.noterlive.com 7 | 8 | # Instructions # 9 | See: [Noter Live Instruction Manual](https://github.com/kevinmarks/noterlive/wiki/Noter-Live-Instruction-Manual) 10 | 11 | For those wishing to properly display the available hovercard output on their websites, they should include the [javascript file](https://github.com/kevinmarks/noterlive/blob/master/web/hovercards.js) included in the repository in their root web folder and ensure that the displaying webpage includes ``. One will also need to include appropriate CSS for displaying these cards, a possible sample can be found in `/resources/noterlive.css`. 12 | 13 | 14 | TO DO 15 | ===== 16 | add a logout button for twitter 17 | 18 | add title and header/footer composition for full posts 19 | 20 | import hCards to speaker buttons as well as twitter handles 21 | 22 | persist to localStorage to survive accidental refresh 23 | 24 | add micropub support for noting elsewhere 25 | 26 | add micropub/atompub/metaweblog for posting articles elsewhere 27 | 28 | DONE 29 | ==== 30 | better twitter length estimation (via Tantek's cassis.js) 31 | autolink in the html version (via Tantek's cassis.js) 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "noterlive", 3 | "version": "0.0.3", 4 | "dependencies": { 5 | "express": "3.17.4", 6 | "twitter-api": "https://github.com/kevinmarks/node-twitter-api/tarball/master", 7 | "oauth": "0.9.x" 8 | }, 9 | "engines": { 10 | "node": "0.12.x" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/kevinmarks/noterlive.git" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /resources/noterlive.css: -------------------------------------------------------------------------------- 1 | div .notercite {display:webkit-flex; display:flex;} 2 | 3 | .hovercard{ 4 | position: relative; 5 | } 6 | 7 | .hovercard .hidden-info{ 8 | opacity: 0; 9 | max-height: 0; 10 | max-width: 0; 11 | overflow: hidden; 12 | position: absolute; 13 | top: 1.5em; 14 | left: 0; 15 | z-index:1; 16 | } 17 | 18 | .hovercard:hover .hidden-info{ 19 | border: #999 solid 1px; 20 | box-shadow: 0 0 1em #999; 21 | padding: 0.5em; 22 | background: #fcfcfc; 23 | opacity: 1; 24 | max-height: 20em; 25 | max-width: 20em; 26 | } 27 | -------------------------------------------------------------------------------- /web.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var consumerKey = process.env.TWITTER_CONSUMER_KEY; 3 | var consumerSecret = process.env.TWITTER_CONSUMER_SECRET; 4 | var appRoot = process.env.APP_ROOT || "localhost:5000"; 5 | var OAuth = require('oauth').OAuth 6 | , oauth = new OAuth( 7 | "https://api.twitter.com/oauth/request_token", 8 | "https://api.twitter.com/oauth/access_token", 9 | consumerKey, 10 | consumerSecret, 11 | "1.0", 12 | appRoot + "/auth/twitter/callback", 13 | "HMAC-SHA1" 14 | ); 15 | 16 | var twitter = require('twitter-api').createClient(); 17 | 18 | var app = express(); 19 | app.configure(function(){ 20 | app.use(express.static(__dirname + '/web')); 21 | app.use(express.bodyParser()); 22 | app.use(express.cookieParser()); 23 | app.use(express.cookieSession({secret:'refulgenceherringglueeffluent'})); 24 | }); 25 | 26 | app.get('/auth/twitter', function(req, res) { 27 | 28 | oauth.getOAuthRequestToken(function(error, oauth_token, oauth_token_secret, results) { 29 | if (error) { 30 | console.log(error); 31 | res.send("Authentication Failed!"); 32 | } 33 | else { 34 | req.session.oauth = { 35 | token: oauth_token, 36 | token_secret: oauth_token_secret 37 | }; 38 | console.log(req.session.oauth); 39 | res.redirect('https://twitter.com/oauth/authenticate?oauth_token='+oauth_token) 40 | } 41 | }); 42 | 43 | }); 44 | 45 | app.get('/auth/twitterlogout', function(req, res) { 46 | // Only destroys the session, doesn't revoke the key. 47 | // Twitter doesn't have an API for user-level revocation. 48 | req.session = null; 49 | res.redirect('/'); 50 | }); 51 | 52 | app.get('/auth/twitter/callback', function(req, res, next) { 53 | 54 | if (req.session.oauth) { 55 | req.session.oauth.verifier = req.query.oauth_verifier; 56 | var oauth_data = req.session.oauth; 57 | 58 | oauth.getOAuthAccessToken( 59 | oauth_data.token, 60 | oauth_data.token_secret, 61 | oauth_data.verifier, 62 | function(error, oauth_access_token, oauth_access_token_secret, results) { 63 | if (error) { 64 | console.log(error); 65 | res.send("Authentication Failure!"); 66 | } 67 | else { 68 | req.session.oauth.access_token = oauth_access_token; 69 | req.session.oauth.access_token_secret = oauth_access_token_secret; 70 | console.log(results, req.session.oauth); 71 | //res.send("Authenticated as"); 72 | twitter.setAuth ( 73 | consumerKey, 74 | consumerSecret, 75 | req.session.oauth.access_token, 76 | req.session.oauth.access_token_secret 77 | ); 78 | 79 | twitter.get( 'account/verify_credentials', { skip_status: true }, function( user, error, status ){ 80 | console.log( user ? 'Authenticated as @'+user.screen_name : 'Not authenticated' ); 81 | req.session.user = user; 82 | //console.log(req.session); 83 | //res.send("Logged in as @"+user.screen_name); 84 | res.redirect('/'); 85 | } ); 86 | } 87 | } 88 | ); 89 | } 90 | else { 91 | res.redirect('/'); // Redirect to login page 92 | } 93 | 94 | }); 95 | 96 | app.post('/auth/indie', function(req, res, next) { 97 | console.log("indieauth: "+ req.body.yoururl); 98 | }); 99 | 100 | app.get('/sendtweet', function(req, res, next) { 101 | //console.log("sendtweet: " + req.query.status +" lastid:"+ req.query.lastid ); 102 | twitter.setAuth ( 103 | consumerKey, 104 | consumerSecret, 105 | req.session.oauth.access_token, 106 | req.session.oauth.access_token_secret 107 | ); 108 | if ( twitter.hasAuth() ) { 109 | twitter.post('statuses/update',{'status':req.query.status,'in_reply_to_status_id':req.query.lastid}, function( tweet, error, status ){ 110 | //console.log( tweet ? 'posted as @'+tweet.user.screen_name : status+" "+error.message ); 111 | res.send(tweet ? ""+tweet.text+"":status+ + error.message ); 113 | } ); 114 | } else { 115 | res.send("login first "); 116 | } 117 | 118 | }); 119 | 120 | app.get('/showuser', function(req, res, next) { 121 | if (req.session.user) { 122 | var data = {"profileImage":req.session.user.profile_image_url, "screenName": req.session.user.screen_name, "fullName": req.session.user.name }; 123 | res.send(data); 124 | } else { 125 | res.send("not logged in"); 126 | } 127 | }); 128 | 129 | app.get('/lookupspeaker', function(req, res, next) { 130 | speaker = {'twitter':'@t', 'url':'http://tantek.com', 'name':'Tantek Çelik' }; 131 | twitter.setAuth ( 132 | consumerKey, 133 | consumerSecret, 134 | req.session.oauth.access_token, 135 | req.session.oauth.access_token_secret 136 | ); 137 | twitter.get('users/show',{'screen_name':req.query.handle}, function (user, error, status ) { 138 | if (user) { 139 | speaker.twitter = '@' + user.screen_name; 140 | speaker.name = user.name; 141 | speaker.url = user.entities.url ? user.entities.url.urls[0].expanded_url :""; 142 | speaker.url = speaker.url ? speaker.url : 'https://twitter.com/' + speaker.twitter; 143 | } 144 | astxt = JSON.stringify(speaker); 145 | res.send(astxt); 146 | }); 147 | }); 148 | 149 | var port = process.env.PORT || 5000; 150 | app.listen(port, function() { 151 | console.log("Listening on " + port); 152 | }); 153 | -------------------------------------------------------------------------------- /web/cassis.js: -------------------------------------------------------------------------------- 1 | /* 5 | if you see this or "/// var" in the browser, you need to 6 | wrap your PHP include of cassis.js and use of functions therein 7 | with calls to ob_start and ob_end_clean, e.g.: 8 | ob_start(); 9 | include 'cassis.js'; 10 | // your code that calls CASSIS functions goes here 11 | ob_end_clean(); 12 | /* 13 | // =================================================================== 14 | // PHP-only block. Processed only by PHP. Use only // comments here. 15 | // ------------------------------------------------------------------- 16 | function js() { 17 | return false; 18 | } 19 | 20 | // global configuration 21 | 22 | if (php_min_version("5.1.0")) { 23 | date_default_timezone_set("UTC"); 24 | } 25 | 26 | function php_min_version($s) { 27 | $s = explode(".", $s); 28 | $phpv = explode(".", phpversion()); 29 | for ($i=0; $i < count($s); $i+=1) { 30 | if ($s[$i] > $phpv[$i]) { 31 | return false; 32 | } 33 | } 34 | return true; 35 | } 36 | 37 | // ------------------------------------------------------------------- 38 | // date time functions 39 | 40 | function date_get_full_year($d = "") { 41 | if ($d == "") { 42 | $d = new DateTime(); 43 | } 44 | return $d->format('Y'); 45 | } 46 | 47 | function date_get_timestamp($d = "") { 48 | if ($d == "") { 49 | $d = new DateTime(); 50 | } 51 | return $d->format('U'); // $d->getTimestamp(); // in PHP 5.3+ 52 | } 53 | 54 | function date_get_ordinal_days($d) { 55 | return 1 + $d->format('z'); 56 | } 57 | 58 | // mixed-case function names are bad for PHP vs JS. Don't do it. 59 | //function Number($n) { 60 | // return $n-0; 61 | //} 62 | 63 | function date_get_rfc3339($d) { 64 | return $d->format('c'); 65 | } 66 | 67 | // ------------------------------------------------------------------- 68 | // old wrappers. transition code away from these 69 | // ** do not use these in new code. ** 70 | 71 | function getFullYear($d = "") { 72 | // 2010-020 obsoleted. Use date_get_full_year instead 73 | return date_get_full_year($d); 74 | } 75 | 76 | // =================================================================== 77 | /*/ // This comment inverter switches from PHP only to JS only. 78 | // JS-only block. Processed only by JS. Use only // comments here. 79 | // ------------------------------------------------------------------- 80 | function js() { 81 | return true; 82 | } 83 | 84 | // array functions 85 | 86 | function array() { // makes an array from arbitrary parameter list. 87 | return Array.prototype.slice.call(arguments); 88 | } 89 | 90 | function is_array(a) { 91 | return (typeof(a) === "object") && (a instanceof Array); 92 | } 93 | 94 | function count(a) { 95 | return a.length; 96 | } 97 | 98 | function array_slice(a, b, e) { // slice an array, begin, optional end 99 | if (a === undefined) { return array(); } 100 | if (b === undefined) { return a; } 101 | if (e === undefined) { return a.slice(b); } 102 | return a.slice(b, e); 103 | } 104 | 105 | // ------------------------------------------------------------------- 106 | // math and numerical functions 107 | 108 | function floor(n) { 109 | return Math.floor(n); 110 | } 111 | 112 | function intval(n) { 113 | return parseInt(n, 10); 114 | } 115 | 116 | Array.min = function(a) { 117 | // from http://ejohn.org/blog/fast-javascript-maxmin/ 118 | return Math.min.apply(Math, a); 119 | }; 120 | 121 | function min() { 122 | var m = arguments; 123 | if (m.length < 1) { 124 | return false; 125 | } 126 | if (m.length === 1) { 127 | m = m[0]; 128 | if (!is_array(m)) { 129 | return m; 130 | } 131 | } 132 | return Array.min(m); 133 | } 134 | 135 | function ctype_digit(s) { 136 | return (/^[0-9]+$/).test(s); 137 | } 138 | 139 | function ctype_lower(s) { 140 | return (/^[a-z]+$/).test(s); 141 | } 142 | 143 | function ctype_space(s) { 144 | return /\s/.test(s); 145 | } 146 | 147 | // ------------------------------------------------------------------- 148 | // date time functions 149 | 150 | function date_create(s) { 151 | if (s) return new Date(s); 152 | else return new Date(); 153 | } 154 | 155 | function date_get_full_year(d) { 156 | if (arguments.length < 1) { 157 | d = new Date(); 158 | } 159 | return d.getFullYear(); 160 | } 161 | 162 | function date_get_timestamp(d) { 163 | return floor(d.getTime() / 1000); 164 | } 165 | 166 | function date_get_rfc3339($d) { 167 | return strcat($d.getFullYear(),'-', 168 | str_pad_left(1 + $d.getUTCMonth(), 2, "0"), '-', 169 | str_pad_left($d.getDate(), 2, "0"), 'T', 170 | str_pad_left($d.getUTCHours(), 2, "0"), ':', 171 | str_pad_left($d.getUTCMinutes(), 2, "0"), ':', 172 | str_pad_left($d.getUTCSeconds(), 2, "0"), 'Z'); 173 | } 174 | 175 | // newcal 176 | 177 | function date_get_ordinal_days($d) { 178 | return ymdp_to_d($d.getFullYear(), 1 + $d.getMonth(), $d.getDate()); 179 | } 180 | 181 | 182 | // ------------------------------------------------------------------- 183 | // character and string functions 184 | 185 | function ord(s) { 186 | return s.charCodeAt(0); 187 | } 188 | 189 | function strlen(s) { 190 | return s.length; 191 | } 192 | 193 | function substr(s, o, n) { 194 | var m = strlen(s); 195 | if ((o < 0 ? -1-o : o) >= m) { return false; } 196 | if (o < 0) { o = m + o; } 197 | if (n < 0) { n = m - o + n; } 198 | if (n === undefined) { n = m - o; } 199 | return s.substring(o, o + n); 200 | } 201 | 202 | function substr_count(s, n) { 203 | return s.split(n).length - 1; 204 | } 205 | 206 | function strpos(h, n, o) { 207 | // clients must triple-equal test return for === false for no match! 208 | // or use offset(n, h) instead (0 = not found, else 1-based index) 209 | if (arguments.length === 2) { 210 | o = 0; 211 | } 212 | o = h.indexOf(n, o); 213 | if (o === -1) { return false; } 214 | else { return o; } 215 | } 216 | 217 | function stripos(h, n, o) { 218 | // clients must triple-equal test return for === false for no match! 219 | if (arguments.length === 2) { 220 | o = 0; 221 | } 222 | o = h.toLowerCase().indexOf(n.toLowerCase(), o); 223 | if (o === -1) { return false; } 224 | else { return o; } 225 | } 226 | 227 | function strncmp(s1, s2, n) { 228 | s1 = substr(String(s1), 0, n); 229 | s2 = substr(String(s2), 0, n); 230 | return (s1 === s2) ? 0 : 231 | ((s1 < s2) ? -1 : 1); 232 | } 233 | 234 | function explode(d, s, n) { 235 | if (arguments.length === 2) { 236 | return s.split(d); 237 | } 238 | return s.split(d, n); 239 | } 240 | 241 | function implode(d, a) { 242 | return a.join(d); 243 | } 244 | 245 | function rawurlencode(s) { 246 | return encodeURIComponent(s); 247 | } 248 | 249 | function htmlspecialchars(s) { 250 | var c, i; 251 | c = [["&","&"], ["<","<"], [">",">"], 252 | ["'","'"], ['"',"""]]; 253 | for (i = 0; i < c.length; i+=1) { 254 | s = s.replace(new RegExp(c[i][0], "g"), c[i][1]); 255 | } 256 | return s; 257 | } 258 | 259 | function str_ireplace(a, b, s) { 260 | var i; 261 | if (!is_array(a)) { 262 | return s.replace(new RegExp(a, "gi"), is_array(b) ? b[0] : b); 263 | } 264 | else { 265 | for (i=0; i 1 ? m[1] : " \t\n\r\f\x00\x0b\xa0"; 285 | i = 0; 286 | j = strlen(s); 287 | 288 | while (strpos(c,s[i])!==false && ii && strpos(c,s[j])!==false) { 293 | j-=1; 294 | } 295 | j+=1; 296 | if (j>i) { 297 | return substr(s,i,j-i); 298 | } 299 | else { 300 | return ''; 301 | } 302 | } 303 | 304 | function rtrim() { 305 | var c,j,m,s; 306 | m = arguments; 307 | s = m[0]; 308 | c = count(m)>1 ? m[1] : " \t\n\r\f\x00\x0b\xa0"; 309 | j = strlen(s)-1; 310 | while (j>=0 && strpos(c,s[j])!==false) { 311 | j-=1; 312 | } 313 | if (j>=0) { 314 | return substr(s,0,j+1); 315 | } 316 | else { 317 | return ''; 318 | } 319 | } 320 | 321 | function strtolower(s) { 322 | return s.toLowerCase(); 323 | } 324 | 325 | function ucfirst(s) { 326 | return s.charAt(0).toUpperCase() + substr(s, 1); 327 | } 328 | 329 | // ------------------------------------------------------------------- 330 | // more javascript-only php-equivalent functions here 331 | 332 | 333 | // ------------------------------------------------------------------- 334 | // pacify jslint/jshint 335 | // -- define functions and variables only used in PHP flow. 336 | function func_get_args() { } 337 | var FALSE = false; 338 | var PREG_PATTERN_ORDER; 339 | var STR_PAD_LEFT; 340 | // -- may eventually define these for JS. 341 | function date_format() { } 342 | function preg_match_all() { } 343 | function str_pad() { } 344 | function DateTime() { } 345 | 346 | // =================================================================== 347 | /**/ // unconditional comment closer exits PHP comment block. 348 | // JS+PHP block. Processed by both JS and PHP. /*...*/ comments ok. 349 | // ------------------------------------------------------------------- 350 | /* original js/php test - doesn't pass jslint/jshint. 351 | function js() { 352 | return "00"==false; 353 | } 354 | */ 355 | 356 | /*global document: false, window: false */ 357 | /// ?> =0; $i-=1) { 406 | $r = $isjs ? $args[$i] + $r : $args[$i] . $r; 407 | } 408 | return $r; 409 | } 410 | 411 | function number($s) { 412 | return $s - 0; 413 | } 414 | 415 | function string($n) { 416 | if (js()) { 417 | if (typeof($n)==="number") { 418 | return Number($n).toString(); 419 | } else if (typeof($n)==="undefined") { 420 | return ""; 421 | } else { 422 | return $n.toString(); 423 | } 424 | } 425 | else { 426 | return "" . $n; 427 | } 428 | } 429 | 430 | function str_pad_left($s1, $n, $s2) { 431 | $s1 = string($s1); 432 | $s2 = string($s2); 433 | if (js()) { 434 | $n -= strlen($s1); 435 | while ($n >= strlen($s2)) { 436 | $s1 = strcat($s2, $s1); 437 | $n -= strlen($s2); 438 | } 439 | if ($n > 0) { 440 | $s1 = strcat(substr($s2, 0, $n), $s1); 441 | } 442 | return $s1; 443 | } else { 444 | return str_pad($s1, $n, $s2, STR_PAD_LEFT); 445 | } 446 | } 447 | 448 | function trim_slashes($s) { 449 | if ($s[0]==="/") { // strip unnecessary / delim PHP regex funcs want 450 | return substr($s, 1, strlen($s)-2); 451 | } 452 | return $s; 453 | } 454 | 455 | // define a few JS functions that PHP already has, using CASSIS funcs 456 | /// ?> 0) { 528 | if(!js() && function_exists('bcmod')) { 529 | $d = bcmod($n, 60); 530 | $s = $m[$d] . $s; 531 | $n = bcdiv(bcsub($n, $d), 60); 532 | } else { 533 | $d = $n % 60; 534 | $s = strcat($m[$d], $s); 535 | $n = ($n-$d)/60; 536 | } 537 | } 538 | return strcat($p, $s); 539 | } 540 | 541 | function num_to_sxgf($n, $f) { 542 | if (!$f) { $f=1; } 543 | return str_pad_left(num_to_sxg($n), $f, "0"); 544 | } 545 | 546 | function sxg_to_num($s) { /// ?> =48 && $c<=57) { $c=$c-48; } 559 | else if ($c>=65 && $c<=72) { $c-=55; } 560 | else if ($c===73 || $c===108) { $c=1; } // typo cap I lower l to 1 561 | else if ($c>=74 && $c<=78) { $c-=56; } 562 | else if ($c===79) { $c=0; } // error correct typo capital O to 0 563 | else if ($c>=80 && $c<=90) { $c-=57; } 564 | else if ($c===95 || $c===45) { $c=34; } // _ and dash - to _ 565 | else if ($c>=97 && $c<=107) { $c-=62; } 566 | else if ($c>=109 && $c<=122) { $c-=63; } 567 | else { break; } // treat all other noise as end of number 568 | if(!js() && function_exists('bcadd')) { 569 | $n = bcadd(bcmul(60, $n), $c); 570 | } else { 571 | $n = 60*$n + $c; 572 | } 573 | } 574 | return $n*$m; 575 | } 576 | 577 | function sxg_to_numf($s, $f) { 578 | if ($f===undefined) { $f=1; } 579 | return str_pad_left(sxg_to_num($s), $f, "0"); 580 | } 581 | 582 | // ------------------------------------------------------------------- 583 | // == newbase60 compat functions only == (before 2011-149) 584 | function numtosxg($n) { 585 | return num_to_sxg($n); 586 | } 587 | 588 | function numtosxgf($n, $f) { 589 | return num_to_sxgf($n, $f); 590 | } 591 | 592 | function sxgtonum($s) { 593 | return sxg_to_num($s); 594 | } 595 | 596 | function sxgtonumf($s, $f) { 597 | return sxg_to_numf($s, $f); 598 | } 599 | /* == end compat functions == */ 600 | 601 | // ------------------------------------------------------------------- 602 | // date and time 603 | 604 | function date_create_ymd($s) { /// ?> 1) ? $dt[1] : "0:00"; 639 | } 640 | 641 | function dt_to_date($dt) { 642 | $dt = explode("T", $dt); 643 | if (count($dt)==1) { 644 | $dt = explode(" ", $dt); 645 | } 646 | return $dt[0]; 647 | } 648 | 649 | function dt_to_ordinal_date($dt) { 650 | return ymd_to_yd(dt_to_date($dt)); 651 | } 652 | 653 | // ------------------------------------------------------------------- 654 | // newcal 655 | 656 | function isleap($y) { 657 | return ($y % 4 === 0 && ($y % 100 !== 0 || $y % 400 === 0)); 658 | } 659 | 660 | function ymdp_to_d($y, $m, $d) { /// ?> 0) ? $args[0] : 0))); 701 | } 702 | 703 | function get_nm_str($m) { /// ?> 29) ? 2+2*(bim_from_od($d)-1) : 1+2*(bim_from_od($d)-1); 711 | } 712 | 713 | // date_get_ordinal_date: optional date argument 714 | function date_get_ordinal_date() { /// ?> 0) ? $args[0] : 0); 718 | return strcat(date_get_full_year($d), '-', 719 | str_pad_left(date_get_ordinal_days($d), 3, "0")); 720 | } 721 | 722 | // ------------------------------------------------------------------- 723 | // begin epochdays 724 | 725 | function y_to_days($y) { 726 | // convert y-01-01 to epoch days 727 | return floor( 728 | (date_get_timestamp(date_create_ymd(strcat($y, "-01-01"))) - 729 | date_get_timestamp(date_create_ymd("1970-01-01")))/86400); 730 | } 731 | 732 | // convert ymd to epoch days and sexagesimal epoch days (sd) 733 | 734 | function ymd_to_days($d) { 735 | return yd_to_days(ymd_to_yd($d)); 736 | } 737 | 738 | /* old: 739 | function ymd_to_days($d) { 740 | // fails in JS, "2013-03-10" and "2013-03-11" both return 15774 741 | return floor( 742 | (date_get_timestamp(date_create_ymd($d)) - 743 | date_get_timestamp(date_create_ymd("1970-01-01")))/86400); 744 | } 745 | */ 746 | 747 | function ymd_to_sd($d) { 748 | return num_to_sxg(ymd_to_days($d)); 749 | } 750 | 751 | function ymd_to_sdf($d, $f) { 752 | return num_to_sxgf(ymd_to_days($d), $f); 753 | } 754 | 755 | // ordinal date (YYYY-DDD) to ymd, epoch days, sexagesimal epoch days 756 | 757 | function ydp_to_ymd($y, $d) { /// ?> $d) $m -= 1; 765 | $d = $d - $md[isleap($y)-0][$m] + 1; 766 | $m += 1; 767 | return strcat($y, '-', str_pad_left($m, 2, '0'), 768 | '-', str_pad_left($d, 2, '0')); 769 | } 770 | 771 | function yd_to_ymd($d) { 772 | return ydp_to_ymd(substr($d, 0, 4), substr($d, 5, 3)); 773 | } 774 | 775 | function yd_to_days($d) { 776 | return y_to_days(substr($d, 0, 4)) - 1 + number(substr($d, 5, 3)); 777 | } 778 | 779 | function yd_to_sd($d) { 780 | return num_to_sxg(yd_to_days($d)); 781 | } 782 | 783 | function yd_to_sdf($d, $f) { 784 | return num_to_sxgf(yd_to_days($d), $f); 785 | } 786 | 787 | // convert epoch days or sexagesimal epoch days (sd) to ordinal date 788 | function days_to_yd($d) { /// ?> 2) { 893 | $uri = $uri[2]; 894 | if (offset(':', $uri) !== 0) { 895 | $uri = explode(':', $uri, 2); 896 | $uri = $uri[0]; 897 | } 898 | return $uri; 899 | } 900 | return ''; 901 | } 902 | 903 | function sld_of_uri($uri) { 904 | $uri = hostname_of_uri($uri); 905 | $uri = explode('.', $uri); 906 | if (count($uri) > 1) { 907 | return $uri[count($uri) - 2]; 908 | } 909 | return ""; 910 | } 911 | 912 | function path_of_uri($uri) { 913 | $uri = explode('/', $uri, 4); 914 | if (count($uri) > 3) { 915 | $uri = array_slice($uri, 3); 916 | $uri = strcat('/', implode('/', $uri)); 917 | if (offset('?', $uri) !== 0) { 918 | $uri = explode('?', $uri, 2); 919 | $uri = $uri[0]; 920 | } 921 | if (offset('#', $uri) !== 0) { 922 | $uri = explode('#', $uri, 2); 923 | $uri = $uri[0]; 924 | } 925 | return $uri; 926 | } 927 | return '/'; 928 | } 929 | 930 | function prepath_of_uri($uri) { 931 | $uri = explode('/', $uri); 932 | $uri = array_slice($uri, 0, 3); 933 | return implode('/', $uri); 934 | } 935 | 936 | function segment_of_uri($n, $u) { 937 | /* nth starting at 1 */ 938 | $u = path_of_uri($u); 939 | $u = explode('/', $u); 940 | if ($n>=0 && $n 0) { 996 | $d = $n % 36; 997 | $s = strcat($m[$d], $s); 998 | $n = ($n-$d)/36; 999 | } 1000 | return $s; 1001 | } 1002 | 1003 | function num_to_hxtf($n, $f) { 1004 | if ($f === undefined) { $f=1; } 1005 | return str_pad_left(num_to_hxt($n), $f, "0"); 1006 | } 1007 | 1008 | function hxt_to_num($h) { /// ?> =48 && $c<=57) { $c=$c-48; } // 0-9 1015 | else if ($c>=65 && $c<=90) { $c-=55; } // A-Z 1016 | else if ($c>=97 && $c<=122) { $c-=87; } // a-z treat as A-Z 1017 | else { $c = 0; } // treat all other noise as 0 1018 | $n = 36*$n + $c; 1019 | } 1020 | return $n; 1021 | } 1022 | 1023 | // ------------------------------------------------------------------- 1024 | // compat as of 2011-149 1025 | function numtohxt($n) { return num_to_hxt($n); } 1026 | function numtohxtf($n,$f) { return num_to_hxtf($n, $f); } 1027 | function hxttonum($h) { return hxt_to_num($h); } 1028 | 1029 | 1030 | // ------------------------------------------------------------------- 1031 | // ISBN-10 1032 | 1033 | function num_to_isbn10($n) { /// ?> =0; $i-=1) { 1039 | $d += $n[$i]*$f; 1040 | $f += 1; 1041 | } 1042 | $d = 11-($d % 11); 1043 | if ($d===10) {$d="X";} 1044 | else if ($d===11) {$d="0";} 1045 | else {$d=string($d);} 1046 | return strcat(str_pad_left($n, 9, "0"), $d); 1047 | } 1048 | 1049 | // ------------------------------------------------------------------- 1050 | // compat as of 2011-149 1051 | function numtoisbn10($n) { return num_to_isbn10($n); } 1052 | 1053 | 1054 | // ------------------------------------------------------------------- 1055 | // HyperTalk 1056 | 1057 | function trunc($n) { // just an alias from BASIC days 1058 | return floor($n); 1059 | } 1060 | 1061 | function offset($n, $h) { 1062 | $n = strpos($h, $n); 1063 | if ($n === false) { 1064 | return 0; 1065 | } else { 1066 | return $n+1; 1067 | } 1068 | } 1069 | 1070 | function contains($h, $n) { 1071 | // HyperTalk syntax haystack contains needle: if ("abc" contains "b") 1072 | return strpos($h, $n)!==false; 1073 | } 1074 | 1075 | function last_character_of($s) { 1076 | return (strlen($s) > 0) ? $s[strlen($s)-1] : ''; 1077 | } 1078 | 1079 | function line_1_of($s) { /// ?> 1) { 1136 | $r = explode("-", $d[1]); 1137 | if (count($d)==1) { 1138 | $r = explode("+", $d[1]); 1139 | } 1140 | if (count($d)>1) { 1141 | $r = strcat(' on '); 1143 | } 1144 | else { 1145 | $r = strcat(' on '); 1146 | } 1147 | } 1148 | return strcat($r, ''); 1149 | } 1150 | 1151 | 1152 | // ------------------------------------------------------------------- 1153 | // compat as of 2011-149 1154 | function xphasclass($s) { return xp_has_class($s); } 1155 | function xprhasclass($s) { return xpr_has_class($s); } 1156 | function xphasid($s) { return xp_has_id($s); } 1157 | function xpattrstartswith($a, $s) { 1158 | return xp_attr_starts_with($a, $s); 1159 | } 1160 | function xphasrel($s) { return xp_has_rel($s); } 1161 | function xprhasrel($s) { return xpr_has_rel($s); } 1162 | function xprattrstartswithhasrel($a, $s, $r) { 1163 | return xpr_attr_starts_with_has_rel($a, $s, $r); 1164 | } 1165 | function xprattrstartswithhasclass($a, $s, $c) { 1166 | return xpr_attr_starts_with_has_class($a, $s, $c); 1167 | } 1168 | function vcpdtreadable($d) { return vcp_dt_readable($d); } 1169 | 1170 | 1171 | // ------------------------------------------------------------------- 1172 | // whistle 1173 | // algorithmic URL shortener core 1174 | // YYYY/DDD/tnnn to tdddss 1175 | // ordinal date, type, decimal #, to sexagesimal epoch days, sexagesimal # 1176 | function whistle_short_path($p) { 1177 | return strcat(substr($p, 9, 1), 1178 | ((substr($p, 9, 1)!=='t') ? "/" : ""), 1179 | yd_to_sdf(substr($p, 0, 8), 3), 1180 | num_to_sxg(substr($p, 10, 3))); 1181 | } 1182 | 1183 | // ------------------------------------------------------------------- 1184 | // Falcon 1185 | 1186 | function html_unesc_amp_only($s) { 1187 | return str_ireplace('&', '&', $s); 1188 | } 1189 | 1190 | function html_esc_amper_once($s) { 1191 | return str_ireplace('&', '&', html_unesc_amp_only($s)); 1192 | } 1193 | 1194 | function html_esc_amp_ang($s) { 1195 | return str_ireplace('<', '<', 1196 | str_ireplace('>', '>', html_esc_amper_once($s))); 1197 | } 1198 | 1199 | function ellipsize_to_word($s, $max, $e, $min) { /// ?> $min && 1223 | !contains('@$ -~*()_+[]{}|;,<>', $s[$slen-1])) { 1224 | $slen-=1; 1225 | } 1226 | } 1227 | // at this point we've got a min length string, 1228 | // only do minimum trimming necessary to avoid a punctuation error. 1229 | 1230 | // trim slash after colon or slash 1231 | if ($s[$slen-1]==='/' && $slen > 2) { 1232 | if ($s[$slen-2]===':') { 1233 | $slen-=1; 1234 | } 1235 | if ($s[$slen-2]==='/') { 1236 | $slen-=2; 1237 | } 1238 | } 1239 | 1240 | //if trimmed at a ":" in a URL, trim the whole thing 1241 | //or trimmed at "http", trim the whole URL 1242 | if ($s[$slen-1]===':' && $slen > 5 && 1243 | substr($s, $slen-5, 5)==='http:') { 1244 | $slen -= 5; 1245 | } else if ($s[$slen-1]==='p' && $slen > 4 && 1246 | substr($s, $slen-4, 4)==='http') { 1247 | $slen -= 4; 1248 | } else if ($s[$slen-1]==='t' && $slen > 4 && 1249 | (substr($s, $slen-3, 4)==='http' || 1250 | substr($s, $slen-3, 4)===' htt')) { 1251 | $slen -= 3; 1252 | } else if ($s[$slen-1]==='h' && $slen > 4 && 1253 | substr($s, $slen-1, 4)==='http') { 1254 | $slen -= 1; 1255 | } 1256 | 1257 | // if char immediately before ellipsis would be @$ then trim it 1258 | if ($slen > 0 && contains('@$', $s[$slen-1])) { 1259 | $slen-=1; 1260 | } 1261 | 1262 | //if char before ellipsis would be sentence terminator, trim 2 more 1263 | while ($slen > 1 && contains('.!?', $s[$slen-1])) { 1264 | $slen-=2; 1265 | } 1266 | 1267 | // trim extra whitespace before ellipsis down to one space 1268 | if ($slen > 2 && contains("\n\r ", $s[$slen-1])) { 1269 | while (contains("\n\r ", $s[$slen-2]) && $slen > 2) { 1270 | --$slen; 1271 | } 1272 | } 1273 | 1274 | if ($slen < 1) { // somehow shortened too much 1275 | return $e; // or ellipsis by itself exceeded max, return ellipsis. 1276 | } 1277 | 1278 | // if last two chars are ': ', omit ellipsis. 1279 | if ($e==='...' && substr($s, $slen-2, 2)===': ') { 1280 | return substr($s, 0, $slen); 1281 | } 1282 | 1283 | return strcat(substr($s, 0, $slen), $e); 1284 | } 1285 | 1286 | function trim_leading_urls($s) { 1287 | // deliberately trim URLs with explicit http: / https: from start 1288 | // keep schemeless URLs, @-names as expected user-visible text 1289 | // if empty or just space after trimming, just return original 1290 | $r = trim($s); 1291 | while (substr($r, 0, 5) == 'http:' || substr($r, 0, 6) == 'https:') 1292 | { 1293 | $ws = offset(' ', $r); 1294 | $rs = offset("\r", $r); 1295 | if ($rs == 0) { $rs = offset("\n", $r); } 1296 | if ($rs != 0 && $rs < $ws) { $ws = $rs; } 1297 | if ($ws == 0) { return $s; } 1298 | $r = substr($r, $ws, strlen($r)-$ws); 1299 | } 1300 | $r = trim($r); 1301 | return ((strlen($r) > 0) ? $r : $s); 1302 | } 1303 | 1304 | function auto_space($s) { 1305 | // replace linebreaks with
1306 | // and one leading space with   1307 | // replace " " with "  " 1308 | // replace leading spaces (on a line or before spaces) with nbsp; 1309 | if ($s[0] === ' ') { 1310 | $s = strcat(' ', substr($s, 1, strlen($s)-1)); 1311 | } 1312 | return str_ireplace(array("\r\n", "\r", "\n ", "\n", " "), 1313 | array("\n", "\n", '
 ', 1314 | '
', 1315 | '  '), 1316 | $s); 1317 | } 1318 | 1319 | function auto_link_re() { 1320 | return '/(?:\\@[_a-zA-Z0-9]{1,17})(?:\\.[a-zA-Z0-9][-a-zA-Z0-9]*)*|(?:(?:(?:(?:http|https|irc)?:\\/\\/(?:(?:[!$&-.0-9;=?A-Z_a-z]|(?:\\%[a-fA-F0-9]{2}))+(?:\\:(?:[!$&-.0-9;=?A-Z_a-z]|(?:\\%[a-fA-F0-9]{2}))+)?\\@)?)?(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*\\.)+(?:(?:aero|arpa|asia|a[cdefgilmnoqrstuwxz])|(?:biz|blog|b[abdefghijmnorstvwyz])|(?:cat|com|coop|c[acdfghiklmnoruvxyz])|(?:design|d[ejkmoz])|(?:edu|e[cegrstu])|f[ijkmor]|(?:gov|g[abdefghilmnpqrstuwy])|h[kmnrtu]|(?:info|int|i[delmnoqrst])|j[emop]|k[eghimnrwyz]|l[abcikrstuvy]|(?:mil|museum|m[acdeghklmnopqrstuvwxyz])|(?:name|net|n[acefgilopruz])|(?:org|om)|(?:pro|p[aefghklmnrstwy])|qa|(?:rocks|r[eouw])|(?:space|s[abcdeghijklmnortuvyz])|(?:tech|tel|travel|t[cdfghjklmnoprtvwz])|u[agkmsyz]|v[aceginu]|(?:wtf|w[fs])|xyz|y[etu]|z[amw]))|(?:(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9])\\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9])\\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9])))(?:\\:\\d{1,5})?)(?:\\/(?:(?:[!#&-;=?-Z_a-z~])|(?:\\%[a-fA-F0-9]{2}))*)?)(?=\\b|\\s|$)/'; 1321 | // ccTLD compressed regular expression clauses (re)created. 1322 | // .mobi .jobs deliberately excluded to discourage layer violations. 1323 | // see http://flic.kr/p/2kmuSL for more on the problematic new gTLDs 1324 | // part of $re derived from Android Open Source Project, Apache 2.0 1325 | // with a bunch of subsequent fixes/improvements (e.g. ttk.me/t44H2) 1326 | // thus auto_link_re is also Apache 2.0 licensed 1327 | // http://www.apache.org/licenses/LICENSE-2.0 1328 | // - Tantek 2010-046 (moved to auto_link_re 2012-062) 1329 | } 1330 | 1331 | // auto_link: param 1: text; 1332 | // optional: param 2: do embeds or not (false), 1333 | // param 3: do auto_links or not (true) 1334 | // auto_link is idempotent, works on plain text or typical markup. 1335 | function auto_link() { 1336 | /// ?> 1) && ($args[1]!==false); 1349 | $do_link = (count($args) < 3) || ($args[2]!==false); 1350 | 1351 | $re = auto_link_re(); 1352 | $ms = preg_matches($re, $t); 1353 | if (!$ms) { 1354 | return $t; 1355 | } 1356 | 1357 | $mlen = count($ms); 1358 | $sp = preg_split($re, $t); 1359 | $t = ""; 1360 | $sp[0] = string($sp[0]); // force undefined to "" 1361 | for ($i=0; $i<$mlen; $i+=1) { 1362 | $mi = $ms[$i]; 1363 | $spliti = $sp[$i]; 1364 | $t = strcat($t, $spliti); 1365 | $sp[$i+1] = string($sp[$i+1]); // force undefined to "" 1366 | if (substr($sp[$i+1], 0, 1)==='/') { //regex omits end/ before '); 1403 | $enda = ''; 1404 | } 1405 | 1406 | if ($fe && 1407 | ($fe === '.jpeg' || $fe === '.jpg' || $fe === '.png' || 1408 | $fe === '.gif' || $fe === '.svg')) { 1409 | $alt = strcat('a ', 1410 | (offset('photo', $mi) != 0) ? 'photo' 1411 | : substr($fe, 1)); 1412 | $t = strcat($t, $ahref, '', 
1413 |                     $alt, '', $enda, $afterlink); 1414 | } else if ($fe && 1415 | ($fe === '.mp4' || $fe === '.mov' || 1416 | $fe === '.ogv' || $fe === '.webm')) 1417 | { 1418 | $t = strcat($t, $ahref, '', 1420 | $enda, $afterlink); 1421 | } else if ($hn === 'vimeo.com' 1422 | && ctype_digit(substr($pa, 1))) { 1423 | if ($do_link) { 1424 | $t = strcat($t, '', $mi, ' '); 1427 | } 1428 | if ($do_embed) { 1429 | $t = strcat($t, '', 1431 | $afterlink); 1432 | } 1433 | } else if ($hn === 'youtu.be' || 1434 | (($hn === 'youtube.com' || $hn === 'www.youtube.com') 1435 | && ($yvid = offset('watch?v=', $mi)) !== 0)) { 1436 | if ($hn === 'youtu.be') { 1437 | $yvid = substr($pa, 1); 1438 | } else { 1439 | $yvid = explode('&', substr($mi, $yvid+7)); 1440 | $yvid = $yvid[0]; 1441 | } 1442 | if ($do_link) { 1443 | $t = strcat($t, '', $mi, ' '); 1446 | } 1447 | if ($do_embed) { 1448 | $t = strcat($t, '', 1450 | $afterlink); 1451 | } 1452 | } else if ($mi[0] === '@' && $do_link && !contains($mi, '.')) { 1453 | if ($sp[$i+1][0] === '.' && 1454 | $spliti != '' && 1455 | ctype_email_local(substr($spliti, -1, 1))) { 1456 | // if email address, simply append info, no linking 1457 | $t = strcat($t, $mi, $afterlink); 1458 | } 1459 | else { 1460 | // treat it as a Twitter @-username reference and link it 1461 | $t = strcat($t, 1462 | '', $mi, '', 1464 | $afterlink); 1465 | } 1466 | } else if ($do_link) { 1467 | if ($mi[0] === '@') { 1468 | $wmi = web_address_to_uri(substr($mi, 1), true); 1469 | } 1470 | $t = strcat($t, '', $mi, '', 1472 | $afterlink); 1473 | } else { 1474 | $t = strcat($t, $mi, $afterlink); 1475 | } 1476 | } else { 1477 | $t = strcat($t, $mi); 1478 | } 1479 | } 1480 | return strcat($t, $sp[$mlen]); 1481 | } 1482 | 1483 | 1484 | function get_auto_linked_urls($s) { 1485 | // in: $s result of auto_link() applied to plain text 1486 | // out: array of urls from hyperlinks in $s 1487 | 1488 | $s = explode('href="', $s); 1489 | $irtn = count($s); 1490 | if ($irtn < 2) { return array(); } 1491 | $r = array(); 1492 | for ($i=1; $i<$irtn; $i++) { 1493 | $r[$i-1] = substr($s[$i], 0, offset('"', $s[$i])-1); 1494 | } 1495 | return $r; 1496 | } 1497 | 1498 | 1499 | // returns array of URLs after literal "in-reply-to:" in text 1500 | function get_in_reply_to_urls($s) { 1501 | /// ?> 1) 1588 | ) { 1589 | $afterlink = ''; 1590 | $afterchar = substr($mi, -1, 1); 1591 | while (contains('.!?,;"\')]}', $afterchar) && // trim punc @ end 1592 | ($afterchar !== ')' || !contains($mi, '('))) { 1593 | // allow one paren pair 1594 | // *** not sure twitter is this smart 1595 | $afterlink = strcat($afterchar, $afterlink); 1596 | $mi = substr($mi, 0, -1); 1597 | $afterchar = substr($mi, -1, 1); 1598 | } 1599 | 1600 | $prot = protocol_of_uri($mi); 1601 | $proxy_url = ''; 1602 | if ($prot === 'irc:') { 1603 | $proxy_url = $mi; // Twitter doesn't tco irc: URLs 1604 | } else { /* 'https:', 'http:' or presumed for schemeless URLs */ 1605 | $proxy_url = 'https://j.mp/0011235813'; 1606 | } 1607 | $t = strcat($t, $proxy_url, $afterlink); 1608 | } 1609 | else { 1610 | $t = strcat($t, $mi); 1611 | } 1612 | } 1613 | return strcat($t, $sp[$mlen]); 1614 | } 1615 | 1616 | 1617 | // note_length_check: 1618 | // checks if $note fits in $maxlen characters. 1619 | // if $username is non-empty, checks if RT'd $note fits in $maxlen 1620 | // 0 - bad params or other precondition failure error 1621 | // 200 - exactly fits max characters with RT if username provided 1622 | // 206 - less than max chars with RT if username provided 1623 | // 207 - more than RT safe length, but less than tweet max 1624 | // 208 - tweet max length but with RT would be over 1625 | // 413 - (entity too large) over max tweet length 1626 | // strlen('RT @: ') === 6. 1627 | function note_length_check($note, $maxlen, $username) { 1628 | /// ?> --> 1692 | -------------------------------------------------------------------------------- /web/hovercard.css: -------------------------------------------------------------------------------- 1 | *{ 2 | transition: all 0.25s ease-out; 3 | } 4 | 5 | .hovercard{ 6 | position: relative; 7 | } 8 | 9 | .hovercard .hidden-info{ 10 | opacity: 0; 11 | max-height: 0; 12 | max-width: 0; 13 | overflow: hidden; 14 | position: absolute; 15 | top: 1.5em; 16 | left: 0; 17 | z-index:1; 18 | } 19 | 20 | 21 | .hovercard:hover .hidden-info{ 22 | border: #999 solid 1px; 23 | box-shadow: 0 0 1em #999; 24 | padding: 0.5em; 25 | background: #fcfcfc; 26 | opacity: 1; 27 | max-height: 20em; 28 | max-width: 20em; 29 | } 30 | -------------------------------------------------------------------------------- /web/hovercards.js: -------------------------------------------------------------------------------- 1 | function showHoverCard(event) { 2 | event.currentTarget.stashHTML = event.currentTarget.innerHTML; 3 | event.currentTarget.innerHTML = ""; 4 | } 5 | function hideHoverCard(event) { 6 | event.currentTarget.innerHTML = event.currentTarget.stashHTML; 7 | } 8 | document.onreadystatechange = function () { 9 | if (document.readyState == "interactive") { 10 | var people = document.getElementsByClassName('h-card') 11 | for (var i=0;i 2 | 3 | Noter direct 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 32 | 33 | 34 | 35 |
36 | 55 | 56 |
57 |
58 | 59 |
60 |
61 |
62 |
63 |
64 | # 65 | 66 |
67 |
68 |
69 |
70 |
71 | 72 |
73 |
74 | 75 |
76 |
77 |
    78 |
    79 |
    80 |
    81 | @ 82 | 83 |
    84 |
    85 |
    86 | 87 |
    88 |
    89 | 90 |
    91 |
    92 | 93 |
    94 |
    95 | 96 |
    97 |
    98 |
    99 |
    100 |
    101 | 103 | 104 | 105 | 106 |
    107 |
    108 |
    109 |
    110 |
    111 | 112 | 0 113 |
    114 |
    115 |
    116 | 117 | 152 | 153 |
    154 |
    155 |
    156 | 157 |
    158 |
    159 |
    160 |
    161 | 162 |
    163 |
    164 |
    165 |
    166 | 167 | 168 | 169 | 170 |
    171 |
    172 |
    173 |
    174 |
    175 |
    176 |
    177 |
    178 | 179 | 180 | 181 | 182 |
    183 |
    184 |

    Une application Web pour rendre l'affichage de note en direct (live-tweeting aka, live-blogging) plus facile. Par Kevin Marks. 185 |

    manuel (en anglais). Code ici.

    186 |

    Fait beaucoup moins laid grâce à Frédéric de Villamil. 187 |

    Twitter Déconnexion ajoutée par James Williams. 188 |

    Construire fixé par Kyle 189 |

    La mise en cache locale par Peter Wilson code ici. 190 |

    191 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Noter Live 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 30 | 31 | 32 | 33 |
    34 | 55 | 56 |
    57 |
    58 | 59 |
    60 |
    61 |
    62 |
    63 |
    64 | # 65 | 66 |
    67 |
    68 |
    69 |
    70 |
    71 | 72 |
    73 |
    74 | 75 |
    76 |
    77 |
      78 |
      79 |
      80 |
      81 | @ 82 | 83 |
      84 |
      85 |
      86 | 87 |
      88 |
      89 | 90 |
      91 |
      92 | 93 |
      94 |
      95 | 96 |
      97 |
      98 |
      99 |
      100 |
      101 | 103 | 104 | 105 | 106 |
      107 |
      108 |
      109 |
      110 |
      111 | 112 | 0 113 |
      114 |
      115 |
      116 | 117 | 152 | 153 |
      154 |
      155 |
      156 | 157 |
      158 |
      159 |
      160 |
      161 | 162 |
      163 |
      164 |
      165 |
      166 | 167 | 168 | 169 | 170 |
      171 |
      172 |
      173 |
      174 |
      175 |
      176 |
      177 |
      178 | 179 | 180 | 181 | 182 |
      183 |
      184 |

      A web app to make live note posting (aka live-tweeting, live-blogging) easier. By Kevin Marks. Instructions here. Code here.

      185 |

      Made much less ugly thanks to Frédéric de Villamil.
      186 | Twitter logout added by James Williams.
      187 | Build fixed by Kyle.
      188 | Local caching by Peter Wilson.

      189 |
      190 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /web/noterlive.js: -------------------------------------------------------------------------------- 1 | var lastSpeakerHTML = ""; 2 | var localstoragemustard = supports_html5_storage(); 3 | function supports_html5_storage() { 4 | try { 5 | return 'localStorage' in window && window['localStorage'] !== null; 6 | } catch (e) { 7 | return false; 8 | } 9 | } 10 | 11 | 12 | 13 | function storeLocal () { 14 | if ( false == localstoragemustard ) { 15 | return; 16 | } 17 | 18 | var forStorage = { 19 | screenname: document.querySelector("#profilename").innerText, 20 | lastSpeakerHTML: lastSpeakerHTML, 21 | blogpost: document.note.blogpost.value, 22 | archive: document.note.archive.value, 23 | hashtag: document.note.hashtag.value, 24 | speakerhandle: document.note.speakerhandle.value, 25 | speakertwitter: document.note.speakertwitter.value, 26 | speakername: document.note.speakername.value, 27 | speakerurl: document.note.speakerurl.value, 28 | speakerlink: document.getElementById( 'speakerlist' ).innerHTML 29 | } 30 | 31 | var forStorageString = JSON.stringify( forStorage ); 32 | 33 | localStorage.setItem( 'noterliver', forStorageString ); 34 | } 35 | 36 | function recoverLocal( screenname ) { 37 | if ( false == localstoragemustard ) { 38 | return; 39 | } 40 | 41 | var forStorageString = localStorage.getItem( 'noterliver' ); 42 | 43 | if ( null == forStorageString ) { 44 | // got nothin' for you 45 | return; 46 | } 47 | 48 | var forStorage = JSON.parse( forStorageString ); 49 | 50 | if ( screenname != forStorage.screenname ) { 51 | // no longer correct 52 | clearLocal(); 53 | // head back to camp 54 | return; 55 | } 56 | 57 | 58 | var fields = ['blogpost','archive','hashtag','funk','speakerhandle','speakertwitter','speakername','speakerurl']; 59 | 60 | for ( var i=0,l=fields.length; i"; 126 | } 127 | html = html +"" + document.note.speakername.value + ""; 128 | if (addHovercard) { 129 | html = html+""; 130 | } 131 | html = html+": "; 132 | return html 133 | } 134 | function postit() { 135 | document.note.archive.value = document.note.archive.value + "\n" +document.note.composed.value; 136 | speakerHTML = getSpeakerHTML(); 137 | if (speakerHTML == lastSpeakerHTML) { 138 | speakerHTML = "

      "; // a new paragraph inside the blockquote 139 | } else { 140 | if (lastSpeakerHTML != "") { speakerHTML = "\n" + speakerHTML; } 141 | speakerHTML = speakerHTML + "

      "; 142 | lastSpeakerHTML = getSpeakerHTML(); 143 | } 144 | document.note.blogpost.value = document.note.blogpost.value + "\n" +speakerHTML + auto_link(document.note.quote.value, true); 145 | document.getElementById("preview").innerHTML = document.note.blogpost.value; 146 | var lastid = "0"; 147 | var firstlink = null; 148 | try { 149 | firstlink = document.getElementById('tweet').contentDocument.getElementsByTagName("a")[0] 150 | } catch (except) { 151 | console.log("no tweet iframe" + except); 152 | } 153 | if (firstlink) {lastid= firstlink.href.split('/').pop();} 154 | document.getElementById("tweet").src='/sendtweet?status='+encodeURIComponent(document.note.composed.value)+"&lastid="+encodeURIComponent(lastid); 155 | document.note.quote.value = ""; 156 | noteit(); //fix counter and button colour 157 | storeLocal(); 158 | } 159 | function changespeakerinfo() { 160 | handle=document.note.speakerhandle.value; 161 | req = new XMLHttpRequest(); 162 | req.onreadystatechange = function() {foundspeaker();}; 163 | req.open("GET", '/lookupspeaker?handle=' + handle, true); 164 | req.send(null); 165 | document.note.speakertwitter.value = '@' + handle; 166 | } 167 | function makespeakerbutton(speaker) { 168 | var id = speaker.twitter.trim().replace(/\s/g,'_') 169 | if (id =="" ) { 170 | id = speaker.name.trim().replace(/\s/g,'_') 171 | } 172 | if (id && !document.getElementById(id)) { 173 | var button = document.createElement("input"); 174 | button.type = "button"; 175 | button.value = id; 176 | button.id = id; 177 | button.setAttribute('onclick', 'setspeaker(' + JSON.stringify(speaker) +');'); 178 | document.getElementById('speakerlist').appendChild(button); 179 | } 180 | } 181 | function foundspeaker() { 182 | // only if req is "loaded" 183 | if (req.readyState == 4) { 184 | // only if "OK" 185 | if (req.status == 200 || req.status == 304) { 186 | //document.getElementById('error').innerHTML=req.responseText; 187 | speaker = JSON.parse(req.responseText); 188 | setspeaker(speaker); 189 | makespeakerbutton(speaker) 190 | } else { 191 | document.getElementById('error').innerHTML="ahah error:\n" + 192 | req.statusText; 193 | } 194 | } 195 | } 196 | function setspeaker(speaker) { 197 | document.note.speakertwitter.value = speaker.twitter; 198 | document.note.speakerurl.value = speaker.url ? speaker.url : "https://twitter.com/"+ speaker.twitter; 199 | document.note.speakername.value = speaker.name ? speaker.name : speaker.twitter ; 200 | } 201 | function savespeaker() { 202 | speaker= {}; 203 | speaker.twitter = document.note.speakertwitter.value; 204 | speaker.url = document.note.speakerurl.value; 205 | speaker.name = document.note.speakername.value; 206 | makespeakerbutton(speaker) 207 | 208 | } 209 | function deletespeaker() { 210 | var id = document.note.speakertwitter.value.trim().replace(/\s/g,'_') 211 | if (id =="" ) { 212 | id = document.note.speakername.value.trim().replace(/\s/g,'_') 213 | } 214 | var speakerbutton = document.getElementById(id); 215 | 216 | if (speakerbutton) { 217 | speakerbutton.parentNode.removeChild(speakerbutton); 218 | } 219 | 220 | }function loadprofiledata() { 221 | if (this.readyState === 4) { 222 | var data = JSON.parse(this.responseText); 223 | console.log(data); 224 | document.querySelector("#profileimage").src = data.profileImage; 225 | document.querySelector("#profilename").innerText = '@' +data.screenName; 226 | document.querySelector("#profileimage2").src = data.profileImage; 227 | document.querySelector("#profilename2").innerText = '@' +data.screenName; 228 | document.querySelector("#profilefullname").innerText = data.fullName; 229 | document.querySelector("#profile").classList.remove('hidden'); 230 | // Hide login button 231 | document.querySelector("a[href='/auth/twitter']").classList.add('hidden'); 232 | document.querySelector("a[href='/auth/twitterlogout']").classList.remove('hidden'); 233 | recoverLocal( '@' + data.screenName ); 234 | } 235 | } 236 | 237 | document.addEventListener("DOMContentLoaded", function() { 238 | var req = new XMLHttpRequest(); 239 | req.open("GET", "/showuser", true); 240 | req.onreadystatechange = loadprofiledata; 241 | req.send(); 242 | }); -------------------------------------------------------------------------------- /web/noterlive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinmarks/noterlive/6525efe156a25ec8d13491eba91ef03ff12f8b0b/web/noterlive.png -------------------------------------------------------------------------------- /web/noterlive.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 44 | 49 | -------------------------------------------------------------------------------- /web/noterlive114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinmarks/noterlive/6525efe156a25ec8d13491eba91ef03ff12f8b0b/web/noterlive114.png -------------------------------------------------------------------------------- /web/noterlive512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinmarks/noterlive/6525efe156a25ec8d13491eba91ef03ff12f8b0b/web/noterlive512.png -------------------------------------------------------------------------------- /web/tweetembed.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}.u-block{display:block!important}.u-hidden{display:none!important}.u-hiddenVisually{position:absolute!important;overflow:hidden!important;width:1px!important;height:1px!important;padding:0!important;border:0!important;clip:rect(1px,1px,1px,1px)!important}.u-inline{display:inline!important}.u-inlineBlock{display:inline-block!important;max-width:100%}.u-table{display:table!important}.u-tableCell{display:table-cell!important}.u-tableRow{display:table-row!important}.u-cf:after,.u-cf:before{content:" ";display:table}.u-cf:after{clear:both}.u-nbfc{overflow:hidden!important}.u-nbfcAlt{display:table-cell!important;width:10000px!important}.u-floatLeft{float:left!important}.u-floatRight{float:right!important}.u-textBreak{word-wrap:break-word!important}.u-textCenter{text-align:center!important}.u-textLeft{text-align:left!important}.u-textRight{text-align:right!important}.u-textInheritColor{color:inherit!important}.u-textKern{text-rendering:optimizeLegibility;-webkit-font-feature-settings:"kern" 1;-moz-font-feature-settings:"kern" 1;font-feature-settings:"kern" 1;-webkit-font-kerning:normal;-moz-font-kerning:normal;font-kerning:normal}.u-textNoWrap{white-space:nowrap!important}.u-textTruncate{max-width:100%;overflow:hidden!important;text-overflow:ellipsis!important;white-space:nowrap!important;word-wrap:normal!important}blockquote,button,h1,h2,h3,h4,h5,h6,iframe,ol,p,ul{margin:0;padding:0;list-style:none;border:none}b,i{font-weight:400;font-style:normal}.SandboxRoot{direction:ltr;text-align:left}.SandboxRoot{display:block;background:0 0;font:normal normal 16px/1.4 Helvetica,Roboto,"Segoe UI",Calibri,sans-serif;color:#1c2022}a{color:#2b7bb9;text-decoration:none;outline:0}a:visited{color:#2b7bb9;text-decoration:none;outline:0}a:focus{color:#3b94d9;text-decoration:underline;outline:0}a:hover{color:#3b94d9;text-decoration:none;outline:0}a:active{color:#2b7bb9;text-decoration:none;outline:0}.SandboxRoot.env-narrow{font-size:14px}.SandboxRoot:not(.env-narrow) .u-hiddenInWideEnv{display:none}.SandboxRoot.env-narrow .u-hiddenInNarrowEnv{display:none}.u-linkBlend:not(:focus):not(:hover):not(:active){font-weight:inherit;color:inherit;text-decoration:inherit}.Avatar{max-width:100%;max-height:100%}.Button,.Button:link,.Button:visited{-webkit-appearance:none;background-color:#f5f8fa;background-image:-webkit-linear-gradient(#fff,#f5f8fa);background-image:-moz-linear-gradient(#fff,#f5f8fa);background-image:-o-linear-gradient(#fff,#f5f8fa);background-image:linear-gradient(#fff,#f5f8fa);border:1px solid #e1e8ed;border-radius:4px;-moz-box-sizing:border-box;box-sizing:border-box;color:#1c2022;cursor:pointer;display:inline-block;font:inherit;line-height:normal;margin:0;padding:.5rem .9375rem .4375rem;position:relative;text-align:center;text-decoration:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;white-space:normal}.Button::-moz-focus-inner{border:0;padding:0}.Button:active,.Button:focus,.Button:hover{text-decoration:none}.Button:hover{background-color:#e1e8ed;background-image:-webkit-linear-gradient(#fff,#e1e8ed);background-image:-moz-linear-gradient(#fff,#e1e8ed);background-image:-o-linear-gradient(#fff,#e1e8ed);background-image:linear-gradient(#fff,#e1e8ed);border-color:#e1e8ed}.Button:focus{box-shadow:0 0 0 1px #fff,0 0 0 3px rgba(0,132,180,.5)}.Button:active{background-color:#e1e8ed;background-image:-webkit-linear-gradient(#fff,#f5f8fa);background-image:-moz-linear-gradient(#fff,#f5f8fa);background-image:-o-linear-gradient(#fff,#f5f8fa);background-image:linear-gradient(#fff,#f5f8fa);border-color:#ccd6dd;box-shadow:inset 0 1px 4px rgba(0,0,0,.2)}.Button.is-disabled,.Button:disabled{cursor:default;opacity:.6}.Button-label{font-weight:700}.Button--full{display:block;width:100%}.EmbeddedTweet{overflow:hidden;background-color:#fff;border:1px solid #e1e8ed;border-radius:4px}.EmbeddedTweet:hover{border-color:#ccd6dd}.EmbeddedTweet-ancestor{padding:1.25rem 1.25rem 1.1rem 1.25rem;background-color:#f5f8fa}.EmbeddedTweet-tweet{padding:1.25rem 1.25rem .725rem 1.25rem}.EmbeddedTweet--mediaForward{border:0}.EmbeddedTweet--mediaForward .EmbeddedTweet-tweet{padding-top:.9rem;border:1px solid #e1e8ed;border-width:0 1px 1px;border-radius:0 0 4px 4px}.EmbeddedTweet--mediaForward:hover .EmbeddedTweet-tweet{border-color:#ccd6dd}.EmbeddedTweet--mediaForward:hover .MediaCard-borderOverlay{border-color:rgba(204,214,221,.75)}.EmbeddedTweet.decider-tapOpensPermalink{cursor:pointer}.Emoji{height:1em;width:1em;padding:0 .05em 0 .1em;vertical-align:-.1em}.FollowButton{display:inline-block;padding:.34375rem .8125rem .40625rem .71875rem;font-size:.875rem;font-weight:700;line-height:1;color:#55acee;background-color:#fff;border:1px solid #55acee;border-radius:4px}.FollowButton:visited{color:#55acee}.FollowButton:active,.FollowButton:focus,.FollowButton:hover{color:#fff;text-decoration:none;background-color:#55acee}.FollowButton:active .Icon--twitter,.FollowButton:focus .Icon--twitter,.FollowButton:hover .Icon--twitter{background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNy4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSIwIDAgNzIgNzIiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDcyIDcyIiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxyZWN0IHg9IjAiIGZpbGw9Im5vbmUiIHdpZHRoPSI3MiIgaGVpZ2h0PSI3MiIvPg0KPHBhdGggZmlsbD0iI2ZmZmZmZiIgZD0iTTY4LjgxMiwxNS4xNDFjLTIuMzQ4LDEuMDM5LTQuODY5LDEuNzQzLTcuNTE5LDIuMDZjMi43MDMtMS42Miw0Ljc3OC00LjE4Nyw1Ljc1Ni03LjI0NGMtMi41MjksMS41LTUuMzMsMi41OTItOC4zMTMsMy4xNzYNCglDNTYuMzQ5LDEwLjU5MSw1Mi45NDgsOSw0OS4xODIsOWMtNy4yMjksMC0xMy4wOTIsNS44NjEtMTMuMDkyLDEzLjA5M2MwLDEuMDI2LDAuMTE4LDIuMDIxLDAuMzM4LDIuOTgxDQoJYy0xMC44ODUtMC41NDgtMjAuNTI4LTUuNzU3LTI2Ljk4Ny0xMy42NzljLTEuMTI2LDEuOTM2LTEuNzcxLDQuMTg0LTEuNzcxLDYuNTgxYzAsNC41NDIsMi4zMTIsOC41NTEsNS44MjQsMTAuODk4DQoJYy0yLjE0Ni0wLjA2OS00LjE2NS0wLjY1Ny01LjkzLTEuNjM4Yy0wLjAwMiwwLjA1NS0wLjAwMiwwLjExLTAuMDAyLDAuMTYyYzAsNi4zNDUsNC41MTMsMTEuNjM4LDEwLjUwNCwxMi44NA0KCWMtMS4xMDEsMC4yOTgtMi4yNTYsMC40NTctMy40NDksMC40NTdjLTAuODQ2LDAtMS42NjctMC4wNzgtMi40NjUtMC4yMzFjMS42NjcsNS4yLDYuNDk5LDguOTg2LDEyLjIzLDkuMDkNCgljLTQuNDgyLDMuNTEyLTEwLjEyOSw1LjYwNi0xNi4yNiw1LjYwNmMtMS4wNTUsMC0yLjA5Ni0wLjA2MS0zLjEyMi0wLjE4NGM1Ljc5NCwzLjcxNywxMi42NzYsNS44ODIsMjAuMDY3LDUuODgyDQoJYzI0LjA4MywwLDM3LjI1MS0xOS45NDksMzcuMjUxLTM3LjI0OWMwLTAuNTY2LTAuMDE0LTEuMTM0LTAuMDM5LTEuNjk0QzY0LjgzOCwyMC4wNjgsNjcuMDU4LDE3Ljc2NSw2OC44MTIsMTUuMTQxeiIvPg0KPC9zdmc+DQo=')}.FollowButton-bird,.FollowButton-plus{position:relative;top:.0625rem;display:inline-block}.FollowButton--compact{padding:.34375rem .75rem .375rem .8125rem}.FollowButton--compact:active .Icon--plus,.FollowButton--compact:focus .Icon--plus,.FollowButton--compact:hover .Icon--plus{background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNy4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSIwIDAgNDQgNzIiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDQ0IDcyIiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxyZWN0IHg9IjAiIGZpbGw9Im5vbmUiIHdpZHRoPSI0NCIgaGVpZ2h0PSI3MiIvPg0KPHBhdGggZmlsbD0iI2ZmZmZmZiIgZD0iTTM4LDMzSDI1VjIwLjAwMWMwLTEuNjU3LTEuMzQzLTMtMy0zYy0xLjY1NiwwLTMsMS4zNDMtMywzVjMzSDZjLTEuNjU2LDAtMywxLjM0My0zLDNzMS4zNDQsMywzLDNoMTN2MTMuMDAxDQoJYzAsMS42NTcsMS4zNDQsMywzLDNjMS42NTcsMCwzLTEuMzQzLDMtM1YzOWgxM2MxLjY1NiwwLDMtMS4zNDMsMy0zUzM5LjY1NiwzMywzOCwzM3oiLz4NCjwvc3ZnPg0K')}.Icon{display:inline-block;height:1.25em;background-repeat:no-repeat;background-size:contain;vertical-align:text-bottom}.Icon--alertsPill{width:1.0763888888888888em;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjxzdmcgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjYyIiBoZWlnaHQ9IjcyIiB2aWV3Qm94PSIwIDAgNjIgNzIiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDYyIDcyIiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGZpbGw9IiNkZDJlNDQiIGQ9Ik01MSwxNEgxMWMtNC40MTksMC04LDMuNTgxLTgsOHYyOGMwLDQuNDE4LDMuNTgxLDgsOCw4aDQwYzQuNDE4LDAsOC0zLjU4Miw4LThWMjJDNTksMTcuNTgxLDU1LjQxOCwxNCw1MSwxNHogTTEyLjEwNywzNi45OTdDMTIuMDcxLDM2Ljk5OSwxMi4wMzUsMzcsMTEuOTk5LDM3Yy0xLjA1NiwwLTEuOTM4LTAuODI2LTEuOTk1LTEuODk0Yy0wLjMzOS02LjM1NCwzLjEzMi0xMi4yNzYsOC44NDQtMTUuMDg4YzAuOTkxLTAuNDg3LDIuMTg5LTAuMDc5LDIuNjc3LDAuOTEyczAuMDgsMi4xODktMC45MTIsMi42NzhjLTQuMjcyLDIuMTAzLTYuODY5LDYuNTMyLTYuNjE1LDExLjI4NUMxNC4wNTcsMzUuOTk2LDEzLjIxLDM2LjkzOCwxMi4xMDcsMzYuOTk3eiBNMTkuMTA3LDM3Ljk5N0MxOS4wNzEsMzcuOTk5LDE5LjAzNSwzOCwxOC45OTksMzhjLTEuMDU2LDAtMS45MzgtMC44MjctMS45OTUtMS44OTRjLTAuMjM0LTQuMzksMi4xNjQtOC40NzgsNi4xMDgtMTAuNDEzYzAuOTkyLTAuNDg4LDIuMTg5LTAuMDc5LDIuNjc3LDAuOTE0YzAuNDg3LDAuOTkxLDAuMDc4LDIuMTg5LTAuOTE0LDIuNjc2Yy0yLjUwMywxLjIyOS00LjAyNSwzLjgyNC0zLjg3Niw2LjYxQzIxLjA1NiwzNi45OTcsMjAuMjEsMzcuOTM4LDE5LjEwNywzNy45OTd6IE00MC4yMTMsNDcuMTA3Yy0wLjIwOSwwLjc3NC0wLjkzOSwxLjI4Mi0xLjczMywxLjM4N2MtMC4wOTMsMC4wMTQtMC4xODgsMC4wMjEtMC4yODUsMC4wMjFjLTAuMDA3LDAtMC4wMTMsMC0wLjAyLDBoLTMuNzc2QzMzLjkzMSw0OS45NTMsMzIuNTk0LDUxLDMxLDUxYy0xLjU5NiwwLTIuOTMyLTEuMDQ3LTMuMzk4LTIuNDg1aC0zLjc3OWMtMC45MTEsMC0xLjgxOC0wLjU0NC0yLjA0Ny0xLjQyNmMtMC4yMjMtMC44NTksMC4wNDItMS42OTEsMC43OTItMi4xNDRjMC4yMDEtMC4yNDgsMS4wNDgtMS40ODcsMS4wNDgtNC43MTFjMC01LjQwNiwyLjQ2LTguMDQxLDUuMjczLTguODkyYzAuMTMxLTEuMDU0LDEuMDItMS44NzMsMi4xMDktMS44NzNjMS4wOTMsMCwxLjk4MywwLjgyNCwyLjExLDEuODgyYzIuODI3LDAuODU4LDUuMjcyLDMuNDg1LDUuMjg2LDguODU3YzAuMDA4LDMuMTkyLDAuODI3LDQuNDYyLDEuMDQ0LDQuNzQyYzAuMDE0LDAuMDExLDAuMDI3LDAuMDIxLDAuMDQsMC4wMzJDNDAuMTk2LDQ1LjQ0OCw0MC40MzksNDYuMjY4LDQwLjIxMyw0Ny4xMDd6IE00NC45OTgsMzYuMTA2QzQ0Ljk0MSwzNy4xNzMsNDQuMDU4LDM4LDQzLjAwMiwzOGMtMC4wMzYsMC0wLjA3Mi0wLjAwMS0wLjEwOC0wLjAwM2MtMS4xMDMtMC4wNTktMS45NDktMS0xLjg5MS0yLjEwNGMwLjE0OC0yLjc4Ni0xLjM3NC01LjM4MS0zLjg3Ni02LjYxYy0wLjk5Mi0wLjQ4Ni0xLjQwMS0xLjY4NS0wLjkxNC0yLjY3NmMwLjQ4Ny0wLjk5MSwxLjY4NS0xLjQwMSwyLjY3Ny0wLjkxNEM0Mi44MzQsMjcuNjI5LDQ1LjIzMSwzMS43MTcsNDQuOTk4LDM2LjEwNnogTTUxLjk5OCwzNS4xMDZDNTEuOTQsMzYuMTczLDUxLjA1OCwzNyw1MC4wMDIsMzdjLTAuMDM2LDAtMC4wNzItMC4wMDEtMC4xMDgtMC4wMDNjLTEuMTAzLTAuMDU5LTEuOTUtMS4wMDEtMS44OTEtMi4xMDRjMC4yNTQtNC43NTMtMi4zNDMtOS4xODMtNi42MTUtMTEuMjg1Yy0wLjk5MS0wLjQ4OC0xLjM5OS0xLjY4Ny0wLjkxMi0yLjY3OGMwLjQ4Ny0wLjk5MSwxLjY4Ni0xLjQsMi42NzctMC45MTJDNDguODY2LDIyLjgzLDUyLjMzNywyOC43NTIsNTEuOTk4LDM1LjEwNnoiLz4NCjxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik0zOC44OSwyNS42OTNjLTAuOTkyLTAuNDg3LTIuMTg5LTAuMDc3LTIuNjc3LDAuOTE0Yy0wLjQ4NywwLjk5MS0wLjA3OCwyLjE4OSwwLjkxNCwyLjY3NmMyLjUwMywxLjIyOSw0LjAyNSwzLjgyNCwzLjg3Niw2LjYxYy0wLjA1OSwxLjEwNCwwLjc4OCwyLjA0NSwxLjg5MSwyLjEwNEM0Mi45MywzNy45OTksNDIuOTY2LDM4LDQzLjAwMiwzOGMxLjA1NiwwLDEuOTM4LTAuODI3LDEuOTk1LTEuODk0QzQ1LjIzMSwzMS43MTcsNDIuODM0LDI3LjYyOSwzOC44OSwyNS42OTN6Ii8+DQo8cGF0aCBmaWxsPSIjRkZGRkZGIiBkPSJNNDMuMTU0LDIwLjAxOWMtMC45OTEtMC40ODgtMi4xOS0wLjA3OS0yLjY3NywwLjkxMmMtMC40ODgsMC45OTEtMC4wOCwyLjE4OSwwLjkxMiwyLjY3OGM0LjI3MiwyLjEwMyw2Ljg2OSw2LjUzMiw2LjYxNSwxMS4yODVjLTAuMDU5LDEuMTAzLDAuNzg4LDIuMDQ1LDEuODkxLDIuMTA0QzQ5LjkzLDM2Ljk5OSw0OS45NjYsMzcsNTAuMDAyLDM3YzEuMDU1LDAsMS45MzgtMC44MjcsMS45OTUtMS44OTRDNTIuMzM3LDI4Ljc1Miw0OC44NjYsMjIuODMsNDMuMTU0LDIwLjAxOXoiLz4NCjxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik0zOS40OCw0NC45ODJjLTAuMDEzLTAuMDExLTAuMDI2LTAuMDIxLTAuMDQtMC4wMzJjLTAuMjE3LTAuMjgtMS4wMzYtMS41NS0xLjA0NC00Ljc0MmMtMC4wMTMtNS4zNzEtMi40NTktNy45OTktNS4yODYtOC44NTdjLTAuMTI3LTEuMDU4LTEuMDE3LTEuODgyLTIuMTEtMS44ODJjLTEuMDg5LDAtMS45NzksMC44MTktMi4xMDksMS44NzNjLTIuODEzLDAuODUxLTUuMjczLDMuNDg2LTUuMjczLDguODkyYzAsMy4yMjQtMC44NDcsNC40NjMtMS4wNDgsNC43MTFjLTAuNzUsMC40NTItMS4wMTYsMS4yODQtMC43OTIsMi4xNDRjMC4yMjksMC44ODIsMS4xMzYsMS40MjYsMi4wNDcsMS40MjZoMy43NzlDMjguMDY5LDQ5Ljk1MywyOS40MDQsNTEsMzEsNTFjMS41OTMsMCwyLjkzLTEuMDQ3LDMuMzk4LTIuNDg1aDMuNzc2YzAuMDA3LDAsMC4wMTMsMCwwLjAyLDBjMC4wOTcsMCwwLjE5Mi0wLjAwNywwLjI4NS0wLjAyMWMwLjc5My0wLjEwNCwxLjUyNC0wLjYxMiwxLjczMy0xLjM4N0M0MC40MzksNDYuMjY4LDQwLjE5Niw0NS40NDgsMzkuNDgsNDQuOTgyeiIvPg0KPHBhdGggZmlsbD0iI0ZGRkZGRiIgZD0iTTI0Ljg3NCwyOS4yODNjMC45OTItMC40ODYsMS40MDEtMS42ODUsMC45MTQtMi42NzZjLTAuNDg3LTAuOTkzLTEuNjg1LTEuNDAyLTIuNjc3LTAuOTE0Yy0zLjk0NCwxLjkzNi02LjM0Miw2LjAyMy02LjEwOCwxMC40MTNDMTcuMDYsMzcuMTczLDE3Ljk0MywzOCwxOC45OTksMzhjMC4wMzYsMCwwLjA3Mi0wLjAwMSwwLjEwOC0wLjAwM2MxLjEwMy0wLjA1OSwxLjk0OS0xLDEuODkxLTIuMTA0QzIwLjg0OSwzMy4xMDcsMjIuMzcxLDMwLjUxMywyNC44NzQsMjkuMjgzeiIvPg0KPHBhdGggZmlsbD0iI0ZGRkZGRiIgZD0iTTIwLjYxMywyMy42MDhjMC45OTEtMC40ODgsMS4zOTktMS42ODcsMC45MTItMi42NzhzLTEuNjg3LTEuMzk5LTIuNjc3LTAuOTEyYy01LjcxMiwyLjgxMi05LjE4Myw4LjczMy04Ljg0NCwxNS4wODhDMTAuMDYxLDM2LjE3NCwxMC45NDMsMzcsMTEuOTk5LDM3YzAuMDM2LDAsMC4wNzItMC4wMDEsMC4xMDgtMC4wMDNjMS4xMDMtMC4wNTksMS45NS0xLjAwMSwxLjg5MS0yLjEwNEMxMy43NDQsMzAuMTQxLDE2LjM0LDI1LjcxMSwyMC42MTMsMjMuNjA4eiIvPg0KPC9zdmc+DQo=')}.Icon--favorite{width:.9722222222222222em;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjxzdmcgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjU2IiBoZWlnaHQ9IjcyIiB2aWV3Qm94PSIwIDAgNTYgNzIiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDU2IDcyIiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGZpbGw9IiM2OTc4ODIiIGQ9Ik01Mi4zNjYsMjguOTQzYy0wLjM3NC0xLjE2OS0xLjQ2My0xLjk2My0yLjY5My0xLjk2M0gzNS40ODRsLTQuODA5LTE0LjA3QzMwLjI4NSwxMS43NjcsMjkuMjEsMTEsMjgsMTFzLTIuMjg1LDAuNzY3LTIuNjc2LDEuOTFsLTQuODA5LDE0LjA3SDYuMzI3Yy0xLjIzLDAtMi4zMTksMC43OTQtMi42OTMsMS45NjNjLTAuMzc0LDEuMTY5LDAuMDUzLDIuNDQ2LDEuMDU1LDMuMTU2bDExLjMzMiw4LjAzMkwxMS4xOSw1NC4yN2MtMC4zOTYsMS4xNTgsMC4wMDIsMi40MzksMC45ODYsMy4xNzFjMC45ODMsMC43MywyLjMyNywwLjc0NywzLjMyOCwwLjAzOEwyOCw0OC42MjFsMTIuNDk3LDguODU3QzQwLjk4OCw1Ny44MjYsNDEuNTYxLDU4LDQyLjEzNSw1OGMwLjU5NSwwLDEuMTg5LTAuMTg3LDEuNjktMC41NTljMC45ODMtMC43MzIsMS4zODItMi4wMTMsMC45ODYtMy4xNzFsLTQuODMyLTE0LjEzOWwxMS4zMzItOC4wMzJDNTIuMzEzLDMxLjM4OCw1Mi43NCwzMC4xMTIsNTIuMzY2LDI4Ljk0M3oiLz4NCjwvc3ZnPg0K')}.Icon--playCircle{width:1.0416666666666667em;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNy4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSIwIDAgNjAgNzIiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDYwIDcyIiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxyZWN0IHg9IjAiIGZpbGw9Im5vbmUiIHdpZHRoPSI2MCIgaGVpZ2h0PSI3MiIvPg0KPHBhdGggZmlsbD0iI0ZGRkZGRiIgZD0iTTM5LjIwMSwzNC4zNGwtMTItOWMtMC42MDctMC40NTUtMS40MTktMC41MjgtMi4wOTUtMC4xODljLTAuNjc3LDAuMzM5LTEuMTA2LDEuMDMxLTEuMTA2LDEuNzg5djE4DQoJYzAsMC43NTgsMC40MjgsMS40NSwxLjEwNiwxLjc4OWMwLjI4MywwLjE0MiwwLjU4OSwwLjIxMSwwLjg5NCwwLjIxMWMwLjQyNSwwLDAuODQ3LTAuMTM2LDEuMi0wLjRsMTItOQ0KCWMwLjUwMy0wLjM3NywwLjgtMC45NywwLjgtMS42QzQwLjAwMSwzNS4zMSwzOS43MDUsMzQuNzE3LDM5LjIwMSwzNC4zNHoiLz4NCjxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik0zMCwxNWMxMS41OTgsMCwyMSw5LjQwMiwyMSwyMC45OTlzLTkuNDAxLDIwLjk5OS0yMSwyMC45OTljLTExLjU5OSwwLTIxLTkuNDAyLTIxLTIwLjk5OVMxOC40MDEsMTUsMzAsMTUgTTMwLDkNCglDMTUuMTEyLDksMywyMS4xMTEsMywzNS45OTlzMTIuMTEyLDI2Ljk5OSwyNywyNi45OTljMTQuODg4LDAsMjctMTIuMTExLDI3LTI2Ljk5OVM0NC44ODgsOSwzMCw5TDMwLDl6Ii8+DQo8L3N2Zz4NCg==')}.Icon--plus{width:.6944444444444444em;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNy4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSIwIDAgNDQgNzIiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDQ0IDcyIiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxyZWN0IHg9IjAiIGZpbGw9Im5vbmUiIHdpZHRoPSI0NCIgaGVpZ2h0PSI3MiIvPg0KPHBhdGggZmlsbD0iIzU1YWNlZSIgZD0iTTM4LDMzSDI1VjIwLjAwMWMwLTEuNjU3LTEuMzQzLTMtMy0zYy0xLjY1NiwwLTMsMS4zNDMtMywzVjMzSDZjLTEuNjU2LDAtMywxLjM0My0zLDNzMS4zNDQsMywzLDNoMTN2MTMuMDAxDQoJYzAsMS42NTcsMS4zNDQsMywzLDNjMS42NTcsMCwzLTEuMzQzLDMtM1YzOWgxM2MxLjY1NiwwLDMtMS4zNDMsMy0zUzM5LjY1NiwzMywzOCwzM3oiLz4NCjwvc3ZnPg0K')}.Icon--reply{width:1.0763888888888888em;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxOC4xLjEsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjYyIiBoZWlnaHQ9IjcyIiB2aWV3Qm94PSIwIDAgNjIgNzIiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDYyIDcyIiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGZpbGw9IiM2OTc4ODIiIGQ9Ik00MSwzMWgtOVYxOWMwLTEuMTQxLTAuNjQ3LTIuMTgzLTEuNjY4LTIuNjg4Yy0xLjAyMi0wLjUwNy0yLjI0My0wLjM4OS0zLjE0OSwwLjMwMmwtMjEsMTZDNS40MzcsMzMuMTgxLDUsMzQuMDYzLDUsMzVzMC40MzcsMS44MTksMS4xODIsMi4zODdsMjEsMTZDMjcuNzE1LDUzLjc5MiwyOC4zNTYsNTQsMjkuMDAxLDU0YzAuNDU0LDAsMC45MDktMC4xMDMsMS4zMzEtMC4zMTJDMzEuMzU0LDUzLjE4MywzMiw1Mi4xNDEsMzIsNTFWMzloOWM1LjUxNCwwLDEwLDQuNDg2LDEwLDEwYzAsMi4yMDksMS43OTEsNCw0LDRzNC0xLjc5MSw0LTRDNTksMzkuMDc1LDUwLjkyNSwzMSw0MSwzMXoiLz4NCjwvc3ZnPg0K');-webkit-transform:scaleX(1);-moz-transform:scaleX(1);-ms-transform:scaleX(1);-o-transform:scaleX(1);transform:scaleX(1)}.Icon--retweet{width:1.284722222222222em;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjxzdmcgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9Ijc0IiBoZWlnaHQ9IjcyIiB2aWV3Qm94PSIwIDAgNzQgNzIiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDc0IDcyIiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGZpbGw9IiM2OTc4ODIiIGQ9Ik03MC42NzYsMzYuNjQ0QzcwLjE2NSwzNS42MzYsNjkuMTMxLDM1LDY4LDM1aC03VjE5YzAtMi4yMDktMS43OTEtNC00LTRIMzRjLTIuMjA5LDAtNCwxLjc5MS00LDRzMS43OTEsNCw0LDRoMThjMC41NTIsMCwwLjk5OCwwLjQ0NiwxLDAuOTk4VjM1aC03Yy0xLjEzLDAtMi4xNjUsMC42MzYtMi42NzYsMS42NDRjLTAuNTExLDEuMDA5LTAuNDEyLDIuMjE5LDAuMjU3LDMuMTMxbDExLDE1QzU1LjE0Nyw1NS41NDUsNTYuMDQ1LDU2LDU3LjAwMSw1NnMxLjg1NC0wLjQ1NSwyLjQxOS0xLjIyNmwxMS0xNUM3MS4wODgsMzguODYyLDcxLjE4NywzNy42NTIsNzAuNjc2LDM2LjY0NHoiLz4NCjxwYXRoIGZpbGw9IiM2OTc4ODIiIGQ9Ik00MC4wMDEsNDhIMjJjLTAuNTM5LDAtMC45Ny0wLjQyNy0wLjk5Mi0wLjk2TDIxLjAwMSwzNmg3YzEuMTMsMCwyLjE2NS0wLjYzNiwyLjY3Ni0xLjY0NGMwLjUxMS0xLjAwOSwwLjQxMi0yLjIxOS0wLjI1Ny0zLjEzMWwtMTEtMTVDMTguODU0LDE1LjQ1NSwxNy45NTYsMTUsMTcsMTVzLTEuODU0LDAuNDU1LTIuNDE5LDEuMjI2bC0xMSwxNWMtMC42NjgsMC45MTItMC43NjgsMi4xMjItMC4yNTYsMy4xMzFDMy44MzYsMzUuMzY0LDQuODcxLDM2LDYuMDAxLDM2aDdsMC4wMTEsMTYuMDAzYzAuMDAyLDIuMjA4LDEuNzkyLDMuOTk3LDQsMy45OTdoMjIuOTg5YzIuMjA5LDAsNC0xLjc5MSw0LTRTNDIuMjEsNDgsNDAuMDAxLDQ4eiIvPg0KPC9zdmc+DQo=');-webkit-transform:scaleX(1);-moz-transform:scaleX(1);-ms-transform:scaleX(1);-o-transform:scaleX(1);transform:scaleX(1)}.Icon--twitter{width:1.25em;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNy4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSIwIDAgNzIgNzIiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDcyIDcyIiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxyZWN0IHg9IjAiIGZpbGw9Im5vbmUiIHdpZHRoPSI3MiIgaGVpZ2h0PSI3MiIvPg0KPHBhdGggZmlsbD0iIzU1YWNlZSIgZD0iTTY4LjgxMiwxNS4xNDFjLTIuMzQ4LDEuMDM5LTQuODY5LDEuNzQzLTcuNTE5LDIuMDZjMi43MDMtMS42Miw0Ljc3OC00LjE4Nyw1Ljc1Ni03LjI0NGMtMi41MjksMS41LTUuMzMsMi41OTItOC4zMTMsMy4xNzYNCglDNTYuMzQ5LDEwLjU5MSw1Mi45NDgsOSw0OS4xODIsOWMtNy4yMjksMC0xMy4wOTIsNS44NjEtMTMuMDkyLDEzLjA5M2MwLDEuMDI2LDAuMTE4LDIuMDIxLDAuMzM4LDIuOTgxDQoJYy0xMC44ODUtMC41NDgtMjAuNTI4LTUuNzU3LTI2Ljk4Ny0xMy42NzljLTEuMTI2LDEuOTM2LTEuNzcxLDQuMTg0LTEuNzcxLDYuNTgxYzAsNC41NDIsMi4zMTIsOC41NTEsNS44MjQsMTAuODk4DQoJYy0yLjE0Ni0wLjA2OS00LjE2NS0wLjY1Ny01LjkzLTEuNjM4Yy0wLjAwMiwwLjA1NS0wLjAwMiwwLjExLTAuMDAyLDAuMTYyYzAsNi4zNDUsNC41MTMsMTEuNjM4LDEwLjUwNCwxMi44NA0KCWMtMS4xMDEsMC4yOTgtMi4yNTYsMC40NTctMy40NDksMC40NTdjLTAuODQ2LDAtMS42NjctMC4wNzgtMi40NjUtMC4yMzFjMS42NjcsNS4yLDYuNDk5LDguOTg2LDEyLjIzLDkuMDkNCgljLTQuNDgyLDMuNTEyLTEwLjEyOSw1LjYwNi0xNi4yNiw1LjYwNmMtMS4wNTUsMC0yLjA5Ni0wLjA2MS0zLjEyMi0wLjE4NGM1Ljc5NCwzLjcxNywxMi42NzYsNS44ODIsMjAuMDY3LDUuODgyDQoJYzI0LjA4MywwLDM3LjI1MS0xOS45NDksMzcuMjUxLTM3LjI0OWMwLTAuNTY2LTAuMDE0LTEuMTM0LTAuMDM5LTEuNjk0QzY0LjgzOCwyMC4wNjgsNjcuMDU4LDE3Ljc2NSw2OC44MTIsMTUuMTQxeiIvPg0KPC9zdmc+DQo=')}.Icon--verified{width:1.1111111111111112em;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxOC4xLjEsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiDQoJIHZpZXdCb3g9IjAgMCA2NCA3MiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgNjQgNzIiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPHJlY3QgeD0iMCIgZmlsbD0ibm9uZSIgd2lkdGg9IjY0IiBoZWlnaHQ9IjcyIi8+DQo8cGF0aCBmaWxsPSIjODhjOWY5IiBkPSJNMy4wMDEsMzcuMzE1YzAsNC4xMjUsMi4xNjEsNy43MjYsNS4zNjIsOS42MjRjLTAuMDU2LDAuNDY4LTAuMDg5LDAuOTM4LTAuMDg5LDEuNDIxYzAsNi4xMDMsNC43MTgsMTEuMDQ1LDEwLjU0NSwxMS4wNDUNCgljMS4yOTYsMCwyLjU0My0wLjIzNCwzLjY4OC0wLjY4NmMxLjcxNCwzLjY4Myw1LjMxOSw2LjIxMyw5LjQ5Miw2LjIxM2M0LjE3NSwwLDcuNzgzLTIuNTMsOS40OTItNi4yMTMNCgljMS4xNDcsMC40NTEsMi4zODksMC42ODYsMy42OSwwLjY4NmM1LjgyNSwwLDEwLjU0NS00Ljk0MSwxMC41NDUtMTEuMDQ1YzAtMC40ODMtMC4wMzctMC45NTMtMC4wOTMtMS40MjENCgljMy4xOTctMS44OTksNS4zNjYtNS40OTksNS4zNjYtOS42MjRjMC00LjM3LTIuNDE4LTguMTUxLTUuOTMyLTkuOTQ2YzAuNDI3LTEuMjAyLDAuNjU4LTIuNSwwLjY1OC0zLjg2NA0KCWMwLTYuMTA0LTQuNzItMTEuMDQ1LTEwLjU0NS0xMS4wNDVjLTEuMzAyLDAtMi41NDMsMC4yMzItMy42OSwwLjY4OEMzOS43ODMsOS40NjMsMzYuMTc1LDYuOTMyLDMyLDYuOTMyDQoJYy00LjE3MywwLTcuNzc4LDIuNTMxLTkuNDkyLDYuMjE2Yy0xLjE0Ni0wLjQ1NS0yLjM5My0wLjY4OC0zLjY4OC0wLjY4OGMtNS44MjcsMC0xMC41NDUsNC45NC0xMC41NDUsMTEuMDQ1DQoJYzAsMS4zNjQsMC4yMzEsMi42NjIsMC42NTYsMy44NjRDNS40MTksMjkuMTY0LDMuMDAxLDMyLjk0NSwzLjAwMSwzNy4zMTV6Ii8+DQo8cGF0aCBmaWxsPSIjRkZGRkZGIiBkPSJNMTcuODcxLDM5LjA3OWw3LjAxNCw2Ljk3OWMwLjU4NSwwLjU4MiwxLjM1MSwwLjg3MywyLjExNiwwLjg3M2MwLjc3LDAsMS41NDEtMC4yOTUsMi4xMjYtMC44ODQNCgljMC4zNDQtMC4zNDYsMTUuOTgtMTUuOTc0LDE1Ljk4LTE1Ljk3NGMxLjE3Mi0xLjE3MiwxLjE3Mi0zLjA3MSwwLjAwMS00LjI0M2MtMS4xNzEtMS4xNzEtMy4wNzEtMS4xNzItNC4yNDMtMC4wMDFMMjYuOTk2LDM5LjY5Mw0KCWwtNC44OTMtNC44NjhjLTEuMTc0LTEuMTY4LTMuMDc0LTEuMTY0LTQuMjQyLDAuMDExQzE2LjY5MiwzNi4wMTEsMTYuNjk3LDM3LjkxLDE3Ljg3MSwzOS4wNzl6Ii8+DQo8L3N2Zz4NCg==')}.Identity-name{font-weight:700}.Identity-screenName{color:#697882}.Identity:focus{text-decoration:none}.Identity:focus .Identity-name{text-decoration:underline}.Identity--blended .Identity-screenName{color:inherit}.Identity--withInlineAvatar{line-height:1.125rem}.Identity--withInlineAvatar .Identity-avatar{width:1.125rem;height:1.125rem;border-radius:2px;vertical-align:top}.PrettyLink:focus{text-decoration:none}.PrettyLink:focus .PrettyLink-value{text-decoration:underline}.Tweet-header{position:relative;padding-left:45px;margin-bottom:.85rem;white-space:nowrap}.Tweet-followButton{position:relative;z-index:1}.Tweet-author{margin-top:2px;line-height:0}.Tweet-authorLink{line-height:1.2}.Tweet-authorAvatar{position:absolute;display:inline-block;top:0;left:0;width:36px;height:36px;overflow:hidden;background-color:transparent;border-radius:4px}.Tweet-authorScreenName{font-size:.875rem}.Tweet-authorScreenName:before{white-space:pre;content:"\A\200e"}.Tweet-authorVerifiedBadge{position:absolute;top:0}.Tweet-text{white-space:pre-wrap;cursor:text}.Tweet-text+.Tweet-alert,.Tweet-text+.Tweet-metadata{margin-top:.2rem}.Tweet-alert,.Tweet-metadata{font-size:.875rem;color:#697882}.Tweet-alert+.Tweet-metadata{margin-top:.65rem}.Tweet-card{margin-top:.65rem;font-size:.875rem}.Tweet-actions{margin-top:.525rem}.Tweet-action{display:inline-block}.Tweet-action+.Tweet-action{margin-left:1rem}.Tweet--compact{position:relative;padding-left:45px;font-size:.875rem}.Tweet--compact .Tweet-header{position:static;padding-left:0;margin-bottom:.4rem}.Tweet--compact .Tweet-author{margin-top:0}.Tweet--compact .Tweet-alert,.Tweet--compact .Tweet-metadata{margin-bottom:0;line-height:inherit}.TweetAction,.TweetAction:visited{color:#697882}.TweetAction-stat{display:inline-block;font-size:.875rem;vertical-align:text-bottom}.TweetAction--reply:active,.TweetAction--reply:focus,.TweetAction--reply:hover{color:#3b94d9;text-decoration:none}.TweetAction--reply:active .TweetAction-icon,.TweetAction--reply:focus .TweetAction-icon,.TweetAction--reply:hover .TweetAction-icon{background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxOC4xLjEsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjYyIiBoZWlnaHQ9IjcyIiB2aWV3Qm94PSIwIDAgNjIgNzIiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDYyIDcyIiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGZpbGw9IiMzYjk0ZDkiIGQ9Ik00MSwzMWgtOVYxOWMwLTEuMTQxLTAuNjQ3LTIuMTgzLTEuNjY4LTIuNjg4Yy0xLjAyMi0wLjUwNy0yLjI0My0wLjM4OS0zLjE0OSwwLjMwMmwtMjEsMTZDNS40MzcsMzMuMTgxLDUsMzQuMDYzLDUsMzVzMC40MzcsMS44MTksMS4xODIsMi4zODdsMjEsMTZDMjcuNzE1LDUzLjc5MiwyOC4zNTYsNTQsMjkuMDAxLDU0YzAuNDU0LDAsMC45MDktMC4xMDMsMS4zMzEtMC4zMTJDMzEuMzU0LDUzLjE4MywzMiw1Mi4xNDEsMzIsNTFWMzloOWM1LjUxNCwwLDEwLDQuNDg2LDEwLDEwYzAsMi4yMDksMS43OTEsNCw0LDRzNC0xLjc5MSw0LTRDNTksMzkuMDc1LDUwLjkyNSwzMSw0MSwzMXoiLz4NCjwvc3ZnPg0K')}.TweetAction--retweet:active,.TweetAction--retweet:focus,.TweetAction--retweet:hover{color:#5c913b;text-decoration:none}.TweetAction--retweet:active .TweetAction-icon,.TweetAction--retweet:focus .TweetAction-icon,.TweetAction--retweet:hover .TweetAction-icon{background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjxzdmcgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9Ijc0IiBoZWlnaHQ9IjcyIiB2aWV3Qm94PSIwIDAgNzQgNzIiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDc0IDcyIiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGZpbGw9IiM1YzkxM2IiIGQ9Ik03MC42NzYsMzYuNjQ0QzcwLjE2NSwzNS42MzYsNjkuMTMxLDM1LDY4LDM1aC03VjE5YzAtMi4yMDktMS43OTEtNC00LTRIMzRjLTIuMjA5LDAtNCwxLjc5MS00LDRzMS43OTEsNCw0LDRoMThjMC41NTIsMCwwLjk5OCwwLjQ0NiwxLDAuOTk4VjM1aC03Yy0xLjEzLDAtMi4xNjUsMC42MzYtMi42NzYsMS42NDRjLTAuNTExLDEuMDA5LTAuNDEyLDIuMjE5LDAuMjU3LDMuMTMxbDExLDE1QzU1LjE0Nyw1NS41NDUsNTYuMDQ1LDU2LDU3LjAwMSw1NnMxLjg1NC0wLjQ1NSwyLjQxOS0xLjIyNmwxMS0xNUM3MS4wODgsMzguODYyLDcxLjE4NywzNy42NTIsNzAuNjc2LDM2LjY0NHoiLz4NCjxwYXRoIGZpbGw9IiM1YzkxM2IiIGQ9Ik00MC4wMDEsNDhIMjJjLTAuNTM5LDAtMC45Ny0wLjQyNy0wLjk5Mi0wLjk2TDIxLjAwMSwzNmg3YzEuMTMsMCwyLjE2NS0wLjYzNiwyLjY3Ni0xLjY0NGMwLjUxMS0xLjAwOSwwLjQxMi0yLjIxOS0wLjI1Ny0zLjEzMWwtMTEtMTVDMTguODU0LDE1LjQ1NSwxNy45NTYsMTUsMTcsMTVzLTEuODU0LDAuNDU1LTIuNDE5LDEuMjI2bC0xMSwxNWMtMC42NjgsMC45MTItMC43NjgsMi4xMjItMC4yNTYsMy4xMzFDMy44MzYsMzUuMzY0LDQuODcxLDM2LDYuMDAxLDM2aDdsMC4wMTEsMTYuMDAzYzAuMDAyLDIuMjA4LDEuNzkyLDMuOTk3LDQsMy45OTdoMjIuOTg5YzIuMjA5LDAsNC0xLjc5MSw0LTRTNDIuMjEsNDgsNDAuMDAxLDQ4eiIvPg0KPC9zdmc+DQo=')}.TweetAction--favorite:active,.TweetAction--favorite:focus,.TweetAction--favorite:hover{color:#ffac33;text-decoration:none}.TweetAction--favorite:active .TweetAction-icon,.TweetAction--favorite:focus .TweetAction-icon,.TweetAction--favorite:hover .TweetAction-icon{background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjxzdmcgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjU2IiBoZWlnaHQ9IjcyIiB2aWV3Qm94PSIwIDAgNTYgNzIiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDU2IDcyIiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGZpbGw9IiNmZmFjMzMiIGQ9Ik01Mi4zNjYsMjguOTQzYy0wLjM3NC0xLjE2OS0xLjQ2My0xLjk2My0yLjY5My0xLjk2M0gzNS40ODRsLTQuODA5LTE0LjA3QzMwLjI4NSwxMS43NjcsMjkuMjEsMTEsMjgsMTFzLTIuMjg1LDAuNzY3LTIuNjc2LDEuOTFsLTQuODA5LDE0LjA3SDYuMzI3Yy0xLjIzLDAtMi4zMTksMC43OTQtMi42OTMsMS45NjNjLTAuMzc0LDEuMTY5LDAuMDUzLDIuNDQ2LDEuMDU1LDMuMTU2bDExLjMzMiw4LjAzMkwxMS4xOSw1NC4yN2MtMC4zOTYsMS4xNTgsMC4wMDIsMi40MzksMC45ODYsMy4xNzFjMC45ODMsMC43MywyLjMyNywwLjc0NywzLjMyOCwwLjAzOEwyOCw0OC42MjFsMTIuNDk3LDguODU3QzQwLjk4OCw1Ny44MjYsNDEuNTYxLDU4LDQyLjEzNSw1OGMwLjU5NSwwLDEuMTg5LTAuMTg3LDEuNjktMC41NTljMC45ODMtMC43MzIsMS4zODItMi4wMTMsMC45ODYtMy4xNzFsLTQuODMyLTE0LjEzOWwxMS4zMzItOC4wMzJDNTIuMzEzLDMxLjM4OCw1Mi43NCwzMC4xMTIsNTIuMzY2LDI4Ljk0M3oiLz4NCjwvc3ZnPg0K')}.CroppedImage{position:relative;display:inline-block;overflow:hidden}.CroppedImage-image{position:absolute;top:0;left:0;min-height:100%;min-width:100%}.CroppedImage--fillHeight .CroppedImage-image{height:100%;width:auto}.CroppedImage--fillWidth .CroppedImage-image{width:100%;height:auto}.FilledIframe{max-width:100%;max-height:100%}.ImageGrid{position:relative}.ImageGrid-image{position:absolute;width:50%;padding-bottom:25%;border:0 solid #fff}.ImageGrid--2 .ImageGrid-image{padding-bottom:50%}.ImageGrid--2 .ImageGrid-image-0{top:0;left:0}.ImageGrid--2 .ImageGrid-image-1{top:0;right:0;border-left-width:1px}.ImageGrid--3 .ImageGrid-image-0{float:left;padding-bottom:50%;top:0;left:0}.ImageGrid--3 .ImageGrid-image-1{top:0;right:0;border-left-width:1px}.ImageGrid--3 .ImageGrid-image-2{bottom:0;right:0;border-width:1px 0 0 1px}.ImageGrid--4 .ImageGrid-image-0{top:0;left:0}.ImageGrid--4 .ImageGrid-image-1{top:0;right:0;border-left-width:1px}.ImageGrid--4 .ImageGrid-image-2{bottom:0;left:0;border-top-width:1px}.ImageGrid--4 .ImageGrid-image-3{bottom:0;right:0;border-width:1px 0 0 1px}.ImageGrid--roundedTop.ImageGrid--2 .ImageGrid-image-0{border-top-left-radius:4px}.ImageGrid--roundedTop.ImageGrid--2 .ImageGrid-image-1{border-top-right-radius:4px}.ImageGrid--roundedTop.ImageGrid--3 .ImageGrid-image-0{border-top-left-radius:4px}.ImageGrid--roundedTop.ImageGrid--3 .ImageGrid-image-1{border-top-right-radius:4px}.ImageGrid--roundedTop.ImageGrid--4 .ImageGrid-image-0{border-top-left-radius:4px}.ImageGrid--roundedTop.ImageGrid--4 .ImageGrid-image-1{border-top-right-radius:4px}.ImageGrid--roundedBottom.ImageGrid--2 .ImageGrid-image-0{border-bottom-left-radius:4px}.ImageGrid--roundedBottom.ImageGrid--2 .ImageGrid-image-1{border-bottom-right-radius:4px}.ImageGrid--roundedBottom.ImageGrid--3 .ImageGrid-image-0{border-bottom-left-radius:4px}.ImageGrid--roundedBottom.ImageGrid--3 .ImageGrid-image-2{border-bottom-right-radius:4px}.ImageGrid--roundedBottom.ImageGrid--4 .ImageGrid-image-2{border-bottom-left-radius:4px}.ImageGrid--roundedBottom.ImageGrid--4 .ImageGrid-image-3{border-bottom-right-radius:4px}.PlayButton{border-radius:2rem;height:4rem;width:4rem;font-size:4rem}.PlayButton--centered{margin-left:-2rem;margin-top:-2rem}.NaturalImage{position:relative}.NaturalImage-image{max-width:100%;max-height:100%;border:0;line-height:0;height:auto}.NaturalImage-ctaOverlay{position:absolute;top:50%;left:50%}.NaturalImage--rounded .NaturalImage-image,.NaturalImage--roundedTop .NaturalImage-image{border-top-left-radius:4px;border-top-right-radius:4px}.NaturalImage--rounded .NaturalImage-image,.NaturalImage--roundedBottom .NaturalImage-image{border-bottom-left-radius:4px;border-bottom-right-radius:4px}.NaturalImage--fill .NaturalImage-image{width:100%}.SummaryCard-headline{font-size:inherit;font-weight:700;margin:.875rem 0 0}.SummaryCard-smallImage{float:right;max-width:120px;margin:0 0 0 1rem;overflow:hidden}.SummaryCard-siteUser{margin:0 0 .875rem;vertical-align:top}.SummaryCard-byline{color:#697882;font-size:.75rem}.SummaryCard-lead{margin:.625rem 0}.SummaryCard--withSmallImage .SummaryCard-body{min-height:120px}.MediaCard-media{position:relative;width:100%;overflow:hidden}.MediaCard-widthConstraint{max-width:100%}.MediaCard-mediaContainer{position:relative;padding-bottom:0;background-color:#f5f8fa}.MediaCard-borderOverlay{position:absolute;top:0;left:0;z-index:10;width:100%;height:100%;border:1px solid rgba(225,232,237,.75);border-radius:4px 4px 0 0;-moz-box-sizing:border-box;box-sizing:border-box}.MediaCard-nsfwInfo{display:none;position:absolute;top:0;left:0;z-index:30;width:100%;padding:1rem 1rem 0;-moz-box-sizing:border-box;box-sizing:border-box;text-align:center}.MediaCard-nsfwHeading{margin:.875rem;font-size:inherit;font-weight:700}.MediaCard-dismissNsfw{margin:.875rem}.MediaCard-mediaAsset{display:block;position:absolute;top:0;left:0;width:100%;height:100%;line-height:0;-webkit-transition:opacity .5s;-moz-transition:opacity .5s;-o-transition:opacity .5s;transition:opacity .5s;background-color:#fff}.MediaCard-attributionOverlay{position:absolute;bottom:.5rem;right:.75rem;z-index:20;padding:.25rem;padding-right:.5rem;border-radius:4px;background-color:rgba(0,0,0,.3);color:#ddd;text-shadow:0 0 2px rgba(0,0,0,.7);font-size:.75rem;line-height:1.125rem}.MediaCard-siteUser{margin:0 0 .875rem}.MediaCard-bylineUser{color:#697882;margin:.875rem 0}.MediaCard--mediaForward .MediaCard-media{background-color:#f5f8fa}.MediaCard--mediaForward .MediaCard-widthConstraint{margin:0 auto}.MediaCard--mediaForward .MediaCard-nsfwInfo{top:25%}.MediaCard--nsfw .MediaCard-nsfwInfo{display:block}.MediaCard--nsfw .MediaCard-mediaAsset{opacity:0}.env-narrow .Tweet-followButton{display:none} --------------------------------------------------------------------------------