summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Parecki <aaron@parecki.com>2016-04-06 11:21:23 -0700
committerAaron Parecki <aaron@parecki.com>2016-04-06 11:21:23 -0700
commitaf92c0deac162cba11dba2e13d2d8e4801af00b2 (patch)
tree1789cc4e580bc9985908b8d56e80f2c568fc6f3c
parent8409b668d89294aada62bdebb7a69c504ad3833e (diff)
add tweet text length countdown on note interface
-rw-r--r--controllers/controllers.php1
-rw-r--r--public/images/twitter.icobin0 -> 1150 bytes
-rw-r--r--public/js/cassis.js1584
-rw-r--r--views/layout.php1
-rw-r--r--views/new-post.php26
5 files changed, 1612 insertions, 0 deletions
diff --git a/controllers/controllers.php b/controllers/controllers.php
index 475e620..95d3aa5 100644
--- a/controllers/controllers.php
+++ b/controllers/controllers.php
@@ -83,6 +83,7 @@ $app->get('/new', function() use($app) {
'syndication_targets' => json_decode($user->syndication_targets, true),
'test_response' => $test_response,
'location_enabled' => $user->location_enabled,
+ 'user' => $user,
'authorizing' => false
));
$app->response()->body($html);
diff --git a/public/images/twitter.ico b/public/images/twitter.ico
new file mode 100644
index 0000000..b4a7169
--- /dev/null
+++ b/public/images/twitter.ico
Binary files differ
diff --git a/public/js/cassis.js b/public/js/cassis.js
new file mode 100644
index 0000000..aee3b62
--- /dev/null
+++ b/public/js/cassis.js
@@ -0,0 +1,1584 @@
+/* <!--
+ cassis.js Copyright 2008-2015 Tantek Çelik http://tantek.com
+ http://cassisproject.com conceived:2008-254; created:2009-299;
+ license:http://creativecommons.org/licenses/by-sa/3.0/ -->
+if you see this in the browser, you need to wrap your PHP include of cassis.js and use thereof with calls to ob_start and ob_end_clean, e.g.:
+ob_start();
+include 'cassis.js';
+// your code that calls CASSIS functions goes here
+ob_end_clean();
+/* <!-- <?php // CASSIS v0.1 start -->
+// ===================================================================
+// PHP-only block. Processed only by PHP. Use only // comments here.
+// -------------------------------------------------------------------
+function js() {
+ return false;
+}
+
+// global configuration
+
+if (php_min_version("5.1.0")) {
+ date_default_timezone_set("UTC");
+}
+
+function php_min_version($s) {
+ $s = explode(".", $s);
+ $phpv = explode(".", phpversion());
+ for ($i=0; $i < count($s); $i+=1) {
+ if ($s[$i] > $phpv[$i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// -------------------------------------------------------------------
+// date time functions
+
+function date_get_full_year($d = "") {
+ if ($d == "") {
+ $d = new DateTime();
+ }
+ return $d->format('Y');
+}
+
+function date_get_timestamp($d) {
+ return $d->format('U'); // $d->getTimestamp(); // in PHP 5.3+
+}
+
+// mixed-case function names are bad for PHP vs JS. Don't do it.
+//function Number($n) {
+// return $n-0;
+//}
+
+
+// -------------------------------------------------------------------
+// old wrappers. transition code away from these
+// ** do not use these in new code. **
+
+function getFullYear($d = "") {
+ // 2010-020 obsoleted. Use date_get_full_year instead
+ return date_get_full_year($d);
+}
+
+// ===================================================================
+/*/ // This comment inverter switches from PHP only to JS only.
+// JS-only block. Processed only by JS. Use only // comments here.
+// -------------------------------------------------------------------
+function js() {
+ return true;
+}
+
+// array functions
+
+function array() { // makes an array from arbitrary parameter list.
+ return Array.prototype.slice.call(arguments);
+}
+
+function is_array(a) {
+ return (typeof(a) === "object") && (a instanceof Array);
+}
+
+function count(a) {
+ return a.length;
+}
+
+function array_slice(a, b, e) { // slice an array, begin, optional end
+ if (a === undefined) { return array(); }
+ if (b === undefined) { return a; }
+ if (e === undefined) { return a.slice(b); }
+ return a.slice(b, e);
+}
+
+// -------------------------------------------------------------------
+// math and numerical functions
+
+function floor(n) {
+ return Math.floor(n);
+}
+
+function intval(n) {
+ return parseInt(n, 10);
+}
+
+Array.min = function(a) {
+// from http://ejohn.org/blog/fast-javascript-maxmin/
+ return Math.min.apply(Math, a);
+};
+
+function min() {
+ var m = arguments;
+ if (m.length < 1) {
+ return false;
+ }
+ if (m.length === 1) {
+ m = m[0];
+ if (!is_array(m)) {
+ return m;
+ }
+ }
+ return Array.min(m);
+}
+
+function ctype_digit(s) {
+ return (/^[0-9]+$/).test(s);
+}
+
+function ctype_space(s) {
+ return /\s/.test(s);
+}
+
+// -------------------------------------------------------------------
+// date time functions
+
+function date_create(s) {
+ var d = new Date();
+ d.parse(s);
+ return d;
+}
+
+function date_get_full_year(d) {
+ if (arguments.length < 1) {
+ d = new Date();
+ }
+ return d.getFullYear();
+}
+
+function date_get_timestamp(d) {
+ return floor(d.getTime() / 1000);
+}
+
+
+// -------------------------------------------------------------------
+// character and string functions
+
+function ord(s) {
+ return s.charCodeAt(0);
+}
+
+function strlen(s) {
+ return s.length;
+}
+
+function substr(s, o, n) {
+ var m = strlen(s);
+ if ((o < 0 ? -1-o : o) >= m) { return false; }
+ if (o < 0) { o = m + o; }
+ if (n < 0) { n = m - o + n; }
+ if (n === undefined) { n = m - o; }
+ return s.substring(o, o + n);
+}
+
+function substr_count(s, n) {
+ return s.split(n).length - 1;
+}
+
+function strpos(h, n, o) {
+ // clients must triple-equal test return for === false for no match!
+ // or use offset(n, h) instead (0 = not found, else 1-based index)
+ if (arguments.length === 2) {
+ o = 0;
+ }
+ o = h.indexOf(n, o);
+ if (o === -1) { return false; }
+ else { return o; }
+}
+
+function strncmp(s1, s2, n) {
+ s1 = substr(String(s1), 0, n);
+ s2 = substr(String(s2), 0, n);
+ return (s1 === s2) ? 0 :
+ ((s1 < s2) ? -1 : 1);
+}
+
+function explode(d, s, n) {
+ if (arguments.length === 2) {
+ return s.split(d);
+ }
+ return s.split(d, n);
+}
+
+function implode(d, a) {
+ return a.join(d);
+}
+
+function rawurlencode(s) {
+ return encodeURIComponent(s);
+}
+
+function htmlspecialchars(s) {
+ var c, i;
+ c = [["&","&amp;"], ["<","&lt;"], [">","&gt;"],
+ ["'","&#039;"], ['"',"&quot;"]];
+ for (i = 0; i < c.length; i+=1) {
+ s = s.replace(new RegExp(c[i][0], "g"), c[i][1]);
+ }
+ return s;
+}
+
+function str_ireplace(a, b, s) {
+ var i;
+ if (!is_array(a)) {
+ return s.replace(new RegExp(a, "gi"), is_array(b) ? b[0] : b);
+ }
+ else {
+ for (i=0; i<a.length; i++) {
+ s = s.replace(new RegExp(a[i], "gi"), is_array(b) ? b[i] : b);
+ }
+ return s;
+ }
+}
+
+function trim() {
+ var c, i, j, m, s;
+ m = arguments;
+ s = m[0];
+ c = count(m) > 1 ? m[1] : " \t\n\r\f\x00\x0b\xa0";
+ i = 0;
+ j = strlen(s);
+
+ while (strpos(c,s[i])!==false && i<j) {
+ i+=1;
+ }
+ j-=1;
+ while (j>i && strpos(c,s[j])!==false) {
+ j-=1;
+ }
+ j+=1;
+ if (j>i) {
+ return substr(s,i,j-i);
+ }
+ else {
+ return '';
+ }
+}
+
+function rtrim() {
+ var c,j,m,s;
+ m = arguments;
+ s = m[0];
+ c = count(m)>1 ? m[1] : " \t\n\r\f\x00\x0b\xa0";
+ j = strlen(s)-1;
+ while (j>=0 && strpos(c,s[j])!==false) {
+ j-=1;
+ }
+ if (j>=0) {
+ return substr(s,0,j+1);
+ }
+ else {
+ return '';
+ }
+}
+
+function strtolower(s) {
+ return s.toLowerCase();
+}
+
+function ucfirst(s) {
+ return s.charAt(0).toUpperCase() + substr(s, 1);
+}
+
+// -------------------------------------------------------------------
+// more javascript-only php-equivalent functions here
+
+
+// -------------------------------------------------------------------
+// pacify jslint/jshint
+// -- define functions and variables only used in PHP flow.
+function func_get_args() { }
+var FALSE = false;
+var PREG_PATTERN_ORDER;
+var STR_PAD_LEFT;
+// -- may eventually define these for JS.
+function date_format() { }
+function preg_match_all() { }
+function str_pad() { }
+function DateTime() { }
+
+// ===================================================================
+/**/ // unconditional comment closer exits PHP comment block.
+// JS+PHP block. Processed by both JS and PHP. /*...*/ comments ok.
+// -------------------------------------------------------------------
+/* original js/php test - doesn't pass jslint/jshint.
+function js() {
+ return "00"==false;
+}
+*/
+
+/*global document: false, window: false */
+/// ?> <!-- ///
+// -------------------------------------------------------------------
+// javascript-only framework functions
+
+function doevent(el, evt) {
+ if (evt==="click" && el.tagName==='A') {
+ // dispatch/fireEvent fails FF3.5+/IE8+ on [a href] w "click" event
+ window.location = el.href; // workaround
+ return true;
+ }
+ if (document.createEvent) {
+ var eo = document.createEvent("HTMLEvents");
+ eo.initEvent(evt, true, true);
+ return !el.dispatchEvent(eo);
+ }
+ else if (document.createEventObject) {
+ return el.fireEvent("on"+evt);
+ }
+}
+
+function targetelement(e) {
+ var t;
+ e = e ? e : window.event;
+ t = e.target ? e.target : e.srcElement;
+ t = (t.nodeType == 3) ? t.parentNode : t; // Safari workaround
+ return t;
+}
+
+// CommonJS (node, browserify, npm) Exports
+if (typeof exports !== 'undefined' &&
+ typeof module !== 'undefined' && module.exports) {
+ exports.auto_link = auto_link;
+ exports.num_to_sxg = num_to_sxg;
+ exports.sxg_to_num = sxg_to_num;
+}
+
+/// --> <?php ///
+
+
+// -------------------------------------------------------------------
+// character and string functions
+
+// strcat: takes as many strings as you want to give it.
+function strcat() { /// ?> <!-- ///
+ var $args, $i, $isjs, $r; /// --> <?php ///
+ $r = "";
+ $isjs = js();
+ $args = $isjs ? arguments : func_get_args();
+ for ($i=count($args)-1; $i>=0; $i-=1) {
+ $r = $isjs ? $args[$i] + $r : $args[$i] . $r;
+ }
+ return $r;
+}
+
+function number($s) {
+ return $s - 0;
+}
+
+function string($n) {
+ if (js()) {
+ if (typeof($n)==="number") {
+ return Number($n).toString();
+ } else if (typeof($n)==="undefined") {
+ return "";
+ } else {
+ return $n.toString();
+ }
+ }
+ else {
+ return "" . $n;
+ }
+}
+
+function str_pad_left($s1, $n, $s2) {
+ $s1 = string($s1);
+ $s2 = string($s2);
+ if (js()) {
+ $n -= strlen($s1);
+ while ($n >= strlen($s2)) {
+ $s1 = strcat($s2, $s1);
+ $n -= strlen($s2);
+ }
+ if ($n > 0) {
+ $s1 = strcat(substr($s2, 0, $n), $s1);
+ }
+ return $s1;
+ } else {
+ return str_pad($s1, $n, $s2, STR_PAD_LEFT);
+ }
+}
+
+function trim_slashes($s) {
+ if ($s[0]==="/") { // strip unnecessary / delim PHP regex funcs want
+ return substr($s, 1, strlen($s)-2);
+ }
+ return $s;
+}
+
+// define a few JS functions that PHP already has, using CASSIS funcs
+/// ?> <!-- ///
+function preg_match(p, s) {
+ return (s.match(trim_slashes(p)) ? 1 : 0);
+}
+
+function preg_split(p, s) {
+ return s.split(new RegExp(trim_slashes(p), "gi"));
+}
+/// --> <?php ///
+
+function preg_match_1($p, $s) { /// ?> <!-- ///
+ var $m; /// --> <?php ///
+ if (js()) {
+ return $s.match(new RegExp(trim_slashes($p), "i"));
+ } else {
+ $m = array();
+ if (preg_match($p, $s, $m) !== FALSE) {
+ return $m[0];
+ } else {
+ return null; //array();
+ }
+ }
+}
+
+function preg_replace_1($p, $r, $s) {
+ if (js()) {
+ return $s.replace(new RegExp(trim_slashes($p), "i"), $r);
+ }
+ else {
+ $r = preg_replace($p, $r, $s, 1);
+ if ($r !== null) { return $r; }
+ else { return $s; }
+ }
+}
+
+function preg_matches($p, $s) { /// ?> <!-- ///
+ var $m; /// --> <?php ///
+ if (js()) {
+ return $s.match(new RegExp(trim_slashes($p), "gi"));
+ } else {
+ $m = array();
+ if (preg_match_all($p, $s, $m, PREG_PATTERN_ORDER) !== FALSE) {
+ return $m[0];
+ } else {
+ return null; //array();
+ }
+ }
+}
+
+function ctype_email_local($s) {
+ // close enough. no '.' because this is used for last char of.
+ return (preg_match("/^[a-zA-Z0-9_%+-]+$/", $s));
+}
+
+function ctype_uri_scheme($s) {
+ return (preg_match("/^[a-zA-Z][a-zA-Z0-9+.-]*$/", $s));
+}
+
+// -------------------------------------------------------------------
+// newbase60
+
+function num_to_sxg($n) { /// ?> <!-- ///
+ var $d, $m, $p, $s; /// --> <?php ///
+ $m = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ_abcdefghijkmnopqrstuvwxyz";
+ $p = "";
+ $s = "";
+ if ($n==="" || $n===0) { return "0"; }
+ if ($n<0) {
+ $n = -$n;
+ $p = "-";
+ }
+ while ($n>0) {
+ if(!js() && function_exists('bcmod')) {
+ $d = bcmod($n, 60);
+ $s = $m[$d] . $s;
+ $n = bcdiv(bcsub($n, $d), 60);
+ } else {
+ $d = $n % 60;
+ $s = strcat($m[$d], $s);
+ $n = ($n-$d)/60;
+ }
+ }
+ return strcat($p, $s);
+}
+
+function num_to_sxgf($n, $f) {
+ if (!$f) { $f=1; }
+ return str_pad_left(num_to_sxg($n), $f, "0");
+}
+
+function sxg_to_num($s) { /// ?> <!-- ///
+ var $c, $i, $j, $m, $n; /// --> <?php ///
+ $j = strlen($s);
+ $m = 1;
+ $n = 0;
+ if ($s[0]==="-") {
+ $m = -1;
+ $j-=1;
+ $s = substr($s, 1, $j);
+ }
+ for ($i=0; $i<$j; $i+=1) { // iterate from first to last char of $s
+ $c = ord($s[$i]); // put current ASCII of char into $c
+ if ($c>=48 && $c<=57) { $c=$c-48; }
+ else if ($c>=65 && $c<=72) { $c-=55; }
+ else if ($c===73 || $c===108) { $c=1; } // typo cap I lower l to 1
+ else if ($c>=74 && $c<=78) { $c-=56; }
+ else if ($c===79) { $c=0; } // error correct typo capital O to 0
+ else if ($c>=80 && $c<=90) { $c-=57; }
+ else if ($c===95 || $c===45) { $c=34; } // _ and dash - to _
+ else if ($c>=97 && $c<=107) { $c-=62; }
+ else if ($c>=109 && $c<=122) { $c-=63; }
+ else { break; } // treat all other noise as end of number
+ if(!js() && function_exists('bcadd')) {
+ $n = bcadd(bcmul(60, $n), $c);
+ } else {
+ $n = 60*$n + $c;
+ }
+ }
+ return $n*$m;
+}
+
+function sxg_to_numf($s, $f) {
+ if ($f===undefined) { $f=1; }
+ return str_pad_left(sxg_to_num($s), $f, "0");
+}
+
+// -------------------------------------------------------------------
+// == newbase60 compat functions only == (before 2011-149)
+function numtosxg($n) {
+ return num_to_sxg($n);
+}
+
+function numtosxgf($n, $f) {
+ return num_to_sxgf($n, $f);
+}
+
+function sxgtonum($s) {
+ return sxg_to_num($s);
+}
+
+function sxgtonumf($s, $f) {
+ return sxg_to_numf($s, $f);
+}
+
+// -------------------------------------------------------------------
+// date and time
+
+function date_create_ymd($s) { /// ?> <!-- ///
+ var $d; /// --> <?php ///
+ if (!$s) {
+ return (js() ? new Date() : new DateTime());
+ }
+ if (js()) {
+ if (substr($s,4,1)==='-') {
+ $s=strcat(substr($s,0,4), substr($s,5,2), substr($s,8,2));
+ }
+ $d = new Date(substr($s,0,4),substr($s,4,2)-1,substr($s,6,2));
+ $d.setHours(0); // was setUTCHours, avoiding bc JS no default tz
+ return $d;
+ } else {
+ return date_create(strcat($s, " 00:00:00"));
+ }
+}
+
+function date_create_timestamp($s) {
+ if (js()) {
+ return new Date(1000*$s);
+ } else {
+ return new DateTime(strcat("@", string($s)));
+ }
+}
+
+// function date_get_timestamp($d) // in PHP/JS specific code above.
+
+function date_get_rfc3339($d) {
+ if (js()) {
+ return strcat($d.getFullYear(), '-',
+ str_pad_left(1+$d.getUTCMonth(), 2, "0"), '-',
+ str_pad_left($d.getDate(), 2, "0"), 'T',
+ str_pad_left($d.getUTCHours(), 2, "0"), ':',
+ str_pad_left($d.getUTCMinutes(), 2, "0"), ':',
+ str_pad_left($d.getUTCSeconds(), 2, "0"), 'Z');
+ } else {
+ return date_format($d, 'c');
+ }
+}
+
+function dt_to_time($dt) {
+ $dt = explode("T", $dt);
+ if (count($dt)==1) {
+ $dt = explode(" ", $dt);
+ }
+ return (count($dt)>1) ? $dt[1] : "0:00";
+}
+
+function dt_to_date($dt) {
+ $dt = explode("T", $dt);
+ if (count($dt)==1) {
+ $dt = explode(" ", $dt);
+ }
+ return $dt[0];
+}
+
+function dt_to_ordinal_date($dt) {
+ return ymd_to_yd(dt_to_date($dt));
+}
+
+// -------------------------------------------------------------------
+// newcal
+
+function isleap($y) {
+ return ($y % 4 === 0 && ($y % 100 !== 0 || $y % 400 === 0));
+}
+
+function ymdp_to_d($y, $m, $d) { /// ?> <!-- ///
+ var $md; /// --> <?php ///
+ $md = array(
+ array(0,31,59,90,120,151,181,212,243,273,304,334),
+ array(0,31,60,91,121,152,182,213,244,274,305,335));
+ return $md[number(isleap($y))][$m-1] + number($d);
+}
+
+function ymd_to_d($d) {
+ if (substr($d, 4, 1)==='-') {
+ return ymdp_to_d(substr($d,0,4),substr($d,5,2),substr($d,8,2));
+ } else {
+ return ymdp_to_d(substr($d,0,4),substr($d,4,2),substr($d,6,2));
+ }
+}
+
+function ymdp_to_yd($y, $m, $d) {
+ return strcat(str_pad_left($y, 4, "0"), '-',
+ str_pad_left(ymdp_to_d($y, $m, $d), 3, "0"));
+}
+
+function ymd_to_yd($d) {
+ if (substr($d, 4, 1)==='-') {
+ return ymdp_to_yd(substr($d,0,4),substr($d,5,2),substr($d,8,2));
+ } else {
+ return ymdp_to_yd(substr($d,0,4),substr($d,4,2),substr($d,6,2));
+ }
+}
+
+function date_get_ordinal_days($d) {
+ if (js()) {
+ return ymdp_to_d($d.getFullYear(), 1+$d.getMonth(), $d.getDate());
+ } else {
+ return 1+date_format($d, 'z');
+ }
+}
+
+function bim_from_od($d) {
+ return 1+floor(($d-1)/61);
+}
+
+function date_get_bim() { /// ?> <!-- ///
+ var $args; /// --> <?php ///
+ $args = js() ? arguments : func_get_args();
+ return bim_from_od(
+ date_get_ordinal_days(
+ date_create_ymd((count($args) > 0) ? $args[0] : 0)));
+}
+
+function get_nm_str($m) { /// ?> <!-- ///
+ var $a; /// --> <?php ///
+ $a = array("New January", "New February", "New March", "New April", "New May", "New June", "New July", "New August", "New September", "New October", "New November", "New December");
+ return $a[($m-1)];
+}
+
+function nm_from_od($d) {
+ return ((($d-1) % 61) > 29) ? 2+2*(bim_from_od($d)-1) : 1+2*(bim_from_od($d)-1);
+}
+
+// date_get_ordinal_date: optional date argument
+function date_get_ordinal_date() { /// ?> <!-- ///
+ var $args, $d; /// --> <?php ///
+ $args = js() ? arguments : func_get_args();
+ $d = date_create_ymd((count($args) > 0) ? $args[0] : 0);
+ return strcat(date_get_full_year($d), '-',
+ str_pad_left(date_get_ordinal_days($d), 3, "0"));
+}
+
+// -------------------------------------------------------------------
+// begin epochdays
+
+function y_to_days($y) {
+ // convert y-01-01 to epoch days
+ return floor(
+ (date_get_timestamp(date_create_ymd(strcat($y, "-01-01"))) -
+ date_get_timestamp(date_create_ymd("1970-01-01")))/86400);
+}
+
+// convert ymd to epoch days and sexagesimal epoch days (sd)
+
+function ymd_to_days($d) {
+ return yd_to_days(ymd_to_yd($d));
+}
+
+/* old:
+function ymd_to_days($d) {
+ // fails in JS, "2013-03-10" and "2013-03-11" both return 15774
+ return floor(
+ (date_get_timestamp(date_create_ymd($d)) -
+ date_get_timestamp(date_create_ymd("1970-01-01")))/86400);
+}
+*/
+
+function ymd_to_sd($d) {
+ return num_to_sxg(ymd_to_days($d));
+}
+
+function ymd_to_sdf($d, $f) {
+ return num_to_sxgf(ymd_to_days($d), $f);
+}
+
+// ordinal date (YYYY-DDD) to ymd, epoch days, sexagesimal epoch days
+
+function ydp_to_ymd($y, $d) { /// ?> <!-- ///
+ var $md, $m; /// --> <?php ///
+ $md = array(
+ array(0,31,59,90,120,151,181,212,243,273,304,334,365),
+ array(0,31,60,91,121,152,182,213,244,274,305,335,366));
+ $d -= 1;
+ $m = trunc($d / 29);
+ if ($md[isleap($y) - 0][$m] > $d) $m -= 1;
+ $d = $d - $md[isleap($y)-0][$m] + 1;
+ $m += 1;
+ return strcat($y, '-', str_pad_left($m, 2, '0'),
+ '-', str_pad_left($d, 2, '0'));
+}
+
+function yd_to_ymd($d) {
+ return ydp_to_ymd(substr($d, 0, 4), substr($d, 5, 3));
+}
+
+function yd_to_days($d) {
+ return y_to_days(substr($d, 0, 4)) - 1 + number(substr($d, 5, 3));
+}
+
+function yd_to_sd($d) {
+ return num_to_sxg(yd_to_days($d));
+}
+
+function yd_to_sdf($d, $f) {
+ return num_to_sxgf(yd_to_days($d), $f);
+}
+
+// convert epoch days or sexagesimal epoch days (sd) to ordinal date
+function days_to_yd($d) { /// ?> <!-- ///
+ var $a, $y; /// --> <?php ///
+ $d = date_create_timestamp(
+ date_get_timestamp(
+ date_create_ymd("1970-01-01")) + $d*86400);
+ $y = date_get_full_year($d);
+ $a = date_create_ymd(strcat($y, "-01-01"));
+ return strcat($y, "-",
+ str_pad_left(
+ 1 + floor((
+ date_get_timestamp($d) - date_get_timestamp($a))/86400), 3, "0"));
+}
+
+function sd_to_yd($d) {
+ return days_to_yd(sxg_to_num($d));
+}
+
+// -------------------------------------------------------------------
+// compat as of 2011-143
+function ymdptod($y,$m,$d) { return ymdp_to_d($y,$m,$d); }
+function ymdptoyd($y,$m,$d) { return ymdp_to_yd($y,$m,$d); }
+function ymdtoyd($d) { return ymd_to_yd($d); }
+function bimfromod($d) { return bim_from_od($d); }
+function getnmstr($m) { return get_nm_str($m); }
+function nmfromod($d) { return nm_from_od($d); }
+function ymdtodays($d) { return ymd_to_days($d); }
+function ymdtosd($d) { return ymd_to_sd($d); }
+function ymdtosdf($d,$f) { return ymd_to_sdf($d, $f); }
+function ydtodays($d) { return yd_to_days($d); }
+function ydtosd($d) { return yd_to_sd($d); }
+function ydtosdf($d,$f) { return yd_to_sdf($d, $f); }
+function daystoyd($d) { return days_to_yd($d); }
+function sdtoyd($d) { return sd_to_yd($d); }
+
+// -------------------------------------------------------------------
+// HTTP
+
+function is_http_header($s) {
+ return (preg_match_1('/^[a-zA-Z][-\\w]*:/',$s)!==null);
+}
+
+// -------------------------------------------------------------------
+// webaddress
+
+function web_address_to_uri($wa, $addhttp) {
+ if (!$wa ||
+ (substr($wa, 0, 7) === 'http://') ||
+ (substr($wa, 0, 8) === 'https://') ||
+ (substr($wa, 0, 6) === 'irc://')) {
+ return $wa;
+ }
+ if ((substr($wa, 0, 7) === 'Http://') ||
+ (substr($wa, 0, 8) === 'Https://')) {
+ // handle iOS4 overcapitalization of input entries
+ return strcat('h', substr($wa, 1, strlen($wa)));
+ }
+
+ // TBI: may want to handle typos as well like:
+ // missing/extra : or / http:/ http///
+ // missing letter in protocol: ttps htps htts, ttp htp htt, ir ic rc
+ // use strtolower(substr($wa, 0, 6)); // handle capitals in URLs
+
+ if (substr($wa,0,1) === '@') {
+ return strcat('https://twitter.com/', substr($wa, 1, strlen($wa)));
+ }
+
+ if ($addhttp) {
+ $wa = strcat('http://', $wa);
+ }
+ return $wa;
+}
+
+function uri_clean($uri) {
+ $uri = web_address_to_uri($uri, false);
+ // prune the optional http:// for a neater param
+ if (substr($uri, 0, 7) === 'http://') {
+ $uri = explode('://', $uri);
+ $uri = array_slice($uri, 1);
+ $uri = implode('://', $uri);
+ }
+ // URL encode
+ return str_ireplace("%3A", ":",
+ str_ireplace("%2F", "/", rawurlencode($uri)));
+}
+
+function protocol_of_uri($uri) {
+ if (offset(':', $uri) === 0) { return ""; }
+ $uri = explode(':', $uri, 2);
+ if (!ctype_uri_scheme($uri[0])) { return ""; }
+ return strcat($uri[0], ':');
+}
+
+// returns e.g. //ttk.me/b/4DY1?seriously=yes#ud
+function relative_uri_hash($uri) {
+ if (offset(':', $uri) === 0) { return ""; }
+ $uri = explode(':', $uri, 2);
+ if (!ctype_uri_scheme($uri[0])) { return ""; }
+ return $uri[1];
+}
+
+// returns e.g. ttk.me
+function hostname_of_uri($uri) {
+ $uri = explode('/', $uri, 4);
+ if (count($uri) > 2) {
+ $uri = $uri[2];
+ if (offset(':', $uri) !== 0) {
+ $uri = explode(':', $uri, 2);
+ $uri = $uri[0];
+ }
+ return $uri;
+ }
+ return '';
+}
+
+function sld_of_uri($uri) {
+ $uri = hostname_of_uri($uri);
+ $uri = explode('.', $uri);
+ if (count($uri) > 1) {
+ return $uri[count($uri) - 2];
+ }
+ return "";
+}
+
+function path_of_uri($uri) {
+ $uri = explode('/', $uri, 4);
+ if (count($uri) > 3) {
+ $uri = array_slice($uri, 3);
+ $uri = strcat('/', implode('/', $uri));
+ if (offset('?', $uri) !== 0) {
+ $uri = explode('?', $uri, 2);
+ $uri = $uri[0];
+ }
+ if (offset('#', $uri) !== 0) {
+ $uri = explode('#', $uri, 2);
+ $uri = $uri[0];
+ }
+ return $uri;
+ }
+ return '/';
+}
+
+function is_http_uri($uri) {
+ $uri = explode(":", $uri, 2);
+ return !!strncmp($uri[0], "http", 4);
+}
+
+// -------------------------------------------------------------------
+// compat as of 2011-149
+function webaddresstouri($wa, $addhttp) {
+ return web_address_to_uri($wa, $addhttp);
+}
+function uriclean($uri) { return uri_clean($uri); }
+
+
+// -------------------------------------------------------------------
+// hexatridecimal
+
+function num_to_hxt($n) { /// ?> <!-- ///
+ var $d, $m, $s; /// --> <?php ///
+ $m = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ $s = "";
+ if ($n === undefined || $n === 0) { return "0"; }
+ while ($n>0) {
+ $d = $n % 36;
+ $s = strcat($m[$d], $s);
+ $n = ($n-$d)/36;
+ }
+ return $s;
+}
+
+function num_to_hxtf($n, $f) {
+ if ($f === undefined) { $f=1; }
+ return str_pad_left(num_to_hxt($n), $f, "0");
+}
+
+function hxt_to_num($h) { /// ?> <!-- ///
+ var $c, $i, $j, $n; /// --> <?php ///
+ $j = strlen($h);
+ $n = 0;
+ for ($i=0; $i<$j; $i+=1) { // iterate from first to last char of $h
+ $c = ord($h[$i]); // put current ASCII of char into $c
+ if ($c>=48 && $c<=57) { $c=$c-48; } // 0-9
+ else if ($c>=65 && $c<=90) { $c-=55; } // A-Z
+ else if ($c>=97 && $c<=122) { $c-=87; } // a-z treat as A-Z
+ else { $c = 0; } // treat all other noise as 0
+ $n = 36*$n + $c;
+ }
+ return $n;
+}
+
+// -------------------------------------------------------------------
+// compat as of 2011-149
+function numtohxt($n) { return num_to_hxt($n); }
+function numtohxtf($n,$f) { return num_to_hxtf($n, $f); }
+function hxttonum($h) { return hxt_to_num($h); }
+
+
+// -------------------------------------------------------------------
+// ISBN-10
+
+function num_to_isbn10($n) { /// ?> <!-- ///
+ var $d, $f, $i; /// --> <?php ///
+ $n = string($n);
+ $d = 0;
+ $f = 2;
+ for ($i=strlen($n)-1; $i>=0; $i-=1) {
+ $d += $n[$i]*$f;
+ $f += 1;
+ }
+ $d = 11-($d % 11);
+ if ($d===10) {$d="X";}
+ else if ($d===11) {$d="0";}
+ else {$d=string($d);}
+ return strcat(str_pad_left($n, 9, "0"), $d);
+}
+
+// -------------------------------------------------------------------
+// compat as of 2011-149
+function numtoisbn10($n) { return num_to_isbn10($n); }
+
+
+// -------------------------------------------------------------------
+// HyperTalk
+
+function trunc($n) { // just an alias from BASIC days
+ return floor($n);
+}
+
+function offset($n, $h) {
+ $n = strpos($h, $n);
+ if ($n === false) {
+ return 0;
+ } else {
+ return $n+1;
+ }
+}
+
+function contains($h, $n) {
+// HyperTalk syntax haystack contains needle: if ("abc" contains "b")
+ return strpos($h, $n)!==false;
+}
+
+function last_character_of($s) {
+ return (strlen($s) > 0) ? $s[strlen($s)-1] : '';
+}
+
+function line_1_of($s) { /// ?> <!-- ///
+ var $r; /// --> <?php ///
+ $r = preg_match_1('/^[\\w\\W]*?[\\n\\r]+?/', $s);
+ if ($r === null) { return $s; }
+ return substr($r, 0, strlen($r)-1);
+}
+
+function delete_line_1_of($s) {
+ if (preg_match_1('/^[\\w\\W]*?[\\n\\r]+?/', $s) === null) {
+ return '';
+ }
+ return preg_replace_1('/^[\\w\\W]*?[\\n\\r]+?/', '', $s);
+}
+
+// -------------------------------------------------------------------
+// microformats
+
+// xpath expressions to extract microformats
+function xp_has_class($s) {
+ return strcat("//*[contains(concat(' ',@class,' '),' ",$s," ')]");
+}
+
+function xpr_has_class($s) {
+ return strcat(".//*[contains(concat(' ',@class,' '),' ",$s," ')]");
+}
+
+function xp_has_id($s) {
+ return strcat("//*[@id='", $s, "']");
+}
+
+function xp_attr_starts_with($a, $s) {
+ return strcat("//*[starts-with(@", $a, ",'", $s, "')]");
+}
+
+function xp_has_rel($s) {
+ return strcat("//*[contains(concat(' ',@rel,' '),' ", $s, " ')]");
+}
+
+function xpr_has_rel($s) {
+ return strcat(".//*[contains(concat(' ',@rel,' '),' ", $s, " ')]");
+}
+
+function xpr_attr_starts_with_has_rel($a, $s, $r) {
+ return strcat(".//*[contains(concat(' ',@rel,' '),' ", $r,
+ " ') and starts-with(@", $a, ",'", $s, "')]");
+}
+
+// value class pattern readable date time from ISO8601 datetime
+function vcp_dt_readable($d) { /// ?> <!-- ///
+ var $r; /// --> <?php ///
+ $d = explode("T", $d);
+ $r = "";
+ if (count($d)>1) {
+ $r = explode("-", $d[1]);
+ if (count($d)==1) {
+ $r = explode("+", $d[1]);
+ }
+ if (count($d)>1) {
+ $r = strcat('<time class="value" datetime="',$d[1],'">',
+ $r[0],'</time> on ');
+ }
+ else {
+ $r = strcat('<time class="value">',$d[1],'</time> on ');
+ }
+ }
+ return strcat($r, '<time class="value">', $d[0], '</time>');
+}
+
+
+// -------------------------------------------------------------------
+// compat as of 2011-149
+function xphasclass($s) { return xp_has_class($s); }
+function xprhasclass($s) { return xpr_has_class($s); }
+function xphasid($s) { return xp_has_id($s); }
+function xpattrstartswith($a, $s) {
+ return xp_attr_starts_with($a, $s);
+}
+function xphasrel($s) { return xp_has_rel($s); }
+function xprhasrel($s) { return xpr_has_rel($s); }
+function xprattrstartswithhasrel($a, $s, $r) {
+ return xpr_attr_starts_with_has_rel($a, $s, $r);
+}
+function vcpdtreadable($d) { return vcp_dt_readable($d); }
+
+
+// -------------------------------------------------------------------
+// whistle
+// algorithmic URL shortener core
+// YYYY/DDD/tnnn to tdddss
+// ordinal date, type, decimal #, to sexagesimal epoch days, sexagesimal #
+function whistle_short_path($p) {
+ return strcat(substr($p, 9, 1),
+ ((substr($p, 9, 1)!=='t') ? "/" : ""),
+ yd_to_sdf(substr($p, 0, 8), 3),
+ num_to_sxg(substr($p, 10, 3)));
+}
+
+// -------------------------------------------------------------------
+// falcon
+
+function html_unesc_amp_only($s) {
+ return str_ireplace('&amp;', '&', $s);
+}
+
+function html_esc_amper_once($s) {
+ return str_ireplace('&', '&amp;', html_unesc_amp_only($s));
+}
+
+function html_esc_amp_ang($s) {
+ return str_ireplace('<', '&lt;',
+ str_ireplace('>', '&gt;', html_esc_amper_once($s)));
+}
+
+function ellipsize_to_word($s, $max, $e, $min) { /// ?> <!-- ///
+ var $elen, $i, $slen; /// --> <?php ///
+ if (strlen($s)<=$max) {
+ return $s; // no need to ellipsize
+ }
+
+ $elen = strlen($e);
+ $slen = $max-$elen;
+
+ // if last characters before $max+1 are ': ', truncate w/o ellipsis.
+ // no need to take length of ellipsis into account
+ if ($e==='...') {
+ for ($i=1; $i<=$elen+1; $i+=1) {
+ if (substr($s, $max-$i, 2)===': ') {
+ return substr($s, 0, $max-$i+1);
+ }
+ }
+ }
+
+ if ($min) {
+ // if a non-zero minimum is provided, then
+ // find previous space or word punctuation to break at.
+ // do not break at %`'"&.!?^ - reasons why to be documented.
+ while ($slen>$min &&
+ !contains('@$ -~*()_+[]{}|;,<>', $s[$slen-1])) {
+ $slen-=1;
+ }
+ }
+ // at this point we've got a min length string,
+ // only do minimum trimming necessary to avoid a punctuation error.
+
+ // trim slash after colon or slash
+ if ($s[$slen-1]==='/' && $slen > 2) {
+ if ($s[$slen-2]===':') {
+ $slen-=1;
+ }
+ if ($s[$slen-2]==='/') {
+ $slen-=2;
+ }
+ }
+
+ //if trimmed at a ":" in a URL, trim the whole thing
+ //or trimmed at "http", trim the whole URL
+ if ($s[$slen-1]===':' && $slen > 5 &&
+ substr($s, $slen-5, 5)==='http:') {
+ $slen -= 5;
+ } else if ($s[$slen-1]==='p' && $slen > 4 &&
+ substr($s, $slen-4, 4)==='http') {
+ $slen -= 4;
+ } else if ($s[$slen-1]==='t' && $slen > 4 &&
+ (substr($s, $slen-3, 4)==='http' ||
+ substr($s, $slen-3, 4)===' htt')) {
+ $slen -= 3;
+ } else if ($s[$slen-1]==='h' && $slen > 4 &&
+ substr($s, $slen-1, 4)==='http') {
+ $slen -= 1;
+ }
+
+ //if char immediately before ellipsis would be @$ then trim it
+ if ($slen > 0 && contains('@$', $s[$slen-1])) {
+ $slen-=1;
+ }
+
+ //if char before ellipsis would be sentence terminator, trim 2 more
+ while ($slen > 1 && contains('.!?', $s[$slen-1])) {
+ $slen-=2;
+ }
+
+ // trim extra whitespace before ellipsis down to one space
+ if ($slen > 2 && contains("\n\r ", $s[$slen-1])) {
+ while (contains("\n\r ", $s[$slen-2]) && $slen > 2) {
+ --$slen;
+ }
+ }
+
+ if ($slen < 1) { // somehow shortened too much
+ return $e; // or ellipsis by itself exceeded max, return ellipsis.
+ }
+
+ // if last two chars are ': ', omit ellipsis.
+ if ($e==='...' && substr($s, $slen-2, 2)===': ') {
+ return substr($s, 0, $slen);
+ }
+
+ return strcat(substr($s, 0, $slen), $e);
+}
+
+function trim_leading_urls($s) {
+ // deliberately trim URLs with explicit http: / https: from start
+ // keep schemeless URLs, @-names as expected user-visible text
+ // if empty or just space after trimming, just return original
+ $r = trim($s);
+ while (substr($r, 0, 5) == 'http:' || substr($r, 0, 6) == 'https:')
+ {
+ $ws = offset(' ', $r);
+ $rs = offset("\r", $r);
+ if ($rs == 0) { $rs = offset("\n", $r); }
+ if ($rs != 0 && $rs < $ws) { $ws = $rs; }
+ if ($ws == 0) { return $s; }
+ $r = substr($r, $ws, strlen($r)-$ws);
+ }
+ $r = trim($r);
+ return ((strlen($r) > 0) ? $r : $s);
+}
+
+function auto_space($s) {
+// replace linebreaks with <br class="auto-break"/>
+// and one leading space with &nbsp;
+// replace " " with " &nbsp;"
+// replace leading spaces (on a line or before spaces) with nbsp;
+ if ($s[0] === ' ') {
+ $s = strcat('&#xA0;', substr($s, 1, strlen($s)-1));
+ }
+ return str_ireplace(array("\r\n", "\r", "\n ", "\n", " "),
+ array("\n", "\n", '<br class="auto-break"/>&#xA0;',
+ '<br class="auto-break"/>',
+ ' &#xA0;'),
+ $s);
+}
+
+function auto_link_re() {
+ return '/(?:\\@[_a-zA-Z0-9]{1,17})|(?:(?:(?:(?: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|b[abdefghijmnorstvwyz])|(?:cat|com|coop|c[acdfghiklmnoruvxyz])|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|r[eouw]|(?:space|s[abcdeghijklmnortuvyz])|(?: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|$)/';
+ // ccTLD compressed regular expression clauses (re)created.
+ // .mobi .jobs deliberately excluded to discourage layer violations.
+ // see http://flic.kr/p/2kmuSL for more on the problematic new gTLDs
+ // part of $re derived from Android Open Source Project, Apache 2.0
+ // with a bunch of subsequent fixes/improvements (e.g. ttk.me/t44H2)
+ // thus auto_link_re is also Apache 2.0 licensed
+ // http://www.apache.org/licenses/LICENSE-2.0
+ // - Tantek 2010-046 (moved to auto_link_re 2012-062)
+}
+
+// auto_link: param 1: text;
+// optional: param 2: do embeds or not (false),
+// param 3: do auto_links or not (true)
+// auto_link is idempotent, works on plain text or typical markup.
+function auto_link() {
+ /// ?> <!-- ///
+ var $args, $afterchar, $afterlink, $do_embed, $fe, $i, $isjs,
+ $mi, $mlen, $ms, $re,
+ $sp, $spe, $spliti, $t, $wmi, $yvid;
+ /// --> <?php ///
+
+ $isjs = js();
+ $args = $isjs ? arguments : func_get_args();
+ if (count($args) === 0) {
+ return '';
+ }
+ $t = $args[0];
+ $do_embed = (count($args) > 1) && ($args[1]!==false);
+ $do_link = (count($args) < 3) || ($args[2]!==false);
+
+ $re = auto_link_re();
+ $ms = preg_matches($re, $t);
+ if (!$ms) {
+ return $t;
+ }
+
+ $mlen = count($ms);
+ $sp = preg_split($re, $t);
+ $t = "";
+ $sp[0] = string($sp[0]); // force undefined to ""
+ for ($i=0; $i<$mlen; $i+=1) {
+ $mi = $ms[$i];
+ $spliti = $sp[$i];
+ $t = strcat($t, $spliti);
+ $sp[$i+1] = string($sp[$i+1]); // force undefined to ""
+ if (substr($sp[$i+1], 0, 1)==='/') { //regex omits end/ before </a
+ $sp[$i+1] = substr($sp[$i+1], 1, strlen($sp[$i+1])-1);
+ $mi = strcat($mi, '/'); // include / in the match
+ }
+ $spe = substr($spliti, -2, 2);
+ // avoid 2x-linking, don't link CSS @-rules, attr values, asciibet
+ if ((!$spe || !preg_match('/(?:\\=[\\"\\\']?|t;)/', $spe)) &&
+ substr(trim($sp[$i+1]), 0, 3)!=='</a' &&
+ (!contains(
+'@charset@font@font-face@import@media@namespace@page@ABCDEFGHIJKLMNOPQ@',
+ strcat($mi, '@')))) {
+ $afterlink = '';
+ $afterchar = substr($mi, -1, 1);
+ while (contains('.!?,;"\')]}', $afterchar) && // trim punc from
+ ($afterchar!==')' || !contains($mi,'('))) { // 1 () pair
+ $afterlink = strcat($afterchar, $afterlink);
+ $mi = substr($mi, 0, -1);
+ $afterchar = substr($mi, -1, 1);
+ }
+
+ $fe = 0;
+ if ($do_embed) {
+ $fe = strtolower(
+ (substr($mi, -4, 1)==='.') ? substr($mi, -4, 4)
+ : substr($mi, -5, 5));
+ }
+ $wmi = web_address_to_uri($mi, true);
+ $prot = protocol_of_uri($wmi);
+ $hn = hostname_of_uri($wmi);
+ $pa = path_of_uri($wmi);
+ $ih = is_http_uri($wmi);
+
+ $ahref = '<span class="figure" style="text-align:left">';
+ $enda = '</span>';
+ if ($do_link) {
+ $ahref = strcat('<a class="auto-link figure" href="',
+ $wmi, '">');
+ $enda = '</a>';
+ }
+
+ if ($fe &&
+ ($fe === '.jpeg' || $fe === '.jpg' || $fe === '.png' ||
+ $fe === '.gif' || $fe === '.svg')) {
+ $alt = strcat('a ',
+ (offset('photo', $mi) != 0) ? 'photo'
+ : substr($fe, 1));
+ $t = strcat($t, $ahref, '<img class="auto-embed" alt="',
+ $alt, '" src="', $wmi, '"/>', $enda, $afterlink);
+ } else if ($fe &&
+ ($fe === '.mp4' || $fe === '.mov' ||
+ $fe === '.ogv' || $fe === '.webm'))
+ {
+ $t = strcat($t, $ahref, '<video class="auto-embed" ',
+ 'controls="controls" src="', $wmi, '"></video>',
+ $enda, $afterlink);
+ } else if ($hn === 'vimeo.com'
+ && ctype_digit(substr($pa, 1))) {
+ if ($do_link) {
+ $t = strcat($t, '<a class="auto-link" href="',
+ 'https:', relative_uri_hash($wmi),
+ '">', $mi, '</a> ');
+ }
+
+ $t = strcat($t, '<iframe class="vimeo-player auto-embed figure" width="480" height="385" style="border:0" src="', 'https://player.vimeo.com/video/',
+ substr($pa, 1), '"></iframe>',
+ $afterlink);
+ } else if ($hn === 'youtu.be' ||
+ (($hn === 'youtube.com' || $hn === 'www.youtube.com')
+ && ($yvid = offset('watch?v=', $mi)) !== 0)) {
+ if ($hn === 'youtu.be') {
+ $yvid = substr($pa, 1);
+ } else {
+ $yvid = explode('&', substr($mi, $yvid+7));
+ $yvid = $yvid[0];
+ }
+ if ($do_link) {
+ $t = strcat($t, '<a class="auto-link" href="',
+ 'https:', relative_uri_hash($wmi),
+ '">', $mi, '</a> ');
+ }
+ $t = strcat($t, '<iframe class="youtube-player auto-embed figure" width="480" height="385" style="border:0" src="', 'https://www.youtube.com/embed/',
+ $yvid, '"></iframe>',
+ $afterlink);
+ } else if ($mi[0] === '@' && $do_link) {
+ if ($sp[$i+1][0] === '.' &&
+ $spliti != '' &&
+ ctype_email_local(substr($spliti, -1, 1))) {
+ // if email address, simply append info, no linking
+ $t = strcat($t, $mi, $afterlink);
+ }
+ else {
+ // treat it as a Twitter @-username reference and link it
+ $t = strcat($t, '<a class="auto-link h-x-username" href="',
+ $wmi, '">', $mi, '</a>',
+ $afterlink);
+ }
+ } else if ($do_link) {
+ $t = strcat($t, '<a class="auto-link" href="',
+ $wmi, '">', $mi, '</a>',
+ $afterlink);
+ } else {
+ $t = strcat($t, $mi, $afterlink);
+ }
+ } else {
+ $t = strcat($t, $mi);
+ }
+ }
+ return strcat($t, $sp[$mlen]);
+}
+
+
+// returns array of URLs after literal "in-reply-to:" in text
+function get_in_reply_to_urls($s) {
+ /// ?> <!-- ///
+ var $irtn, $r, $re, $i, $j,
+ $ms, $sp, $afterlink, $ac;
+ /// --> <?php ///
+
+ $s = explode('in-reply-to: ', $s);
+ $irtn = count($s);
+ if ($irtn < 2) { return array(); }
+ $r = array();
+ $re = auto_link_re();
+ for ($i=1; $i<$irtn; $i++) {
+ // iterate through all strings after an 'in-reply-to: ' for URLs
+ $ms = preg_matches($re, $s[$i]);
+ $msn = count($ms);
+ if ($ms) {
+ $sp = preg_split($re, $s[$i]);
+ $j = 0;
+ $afterlink = '';
+ while ($j<$msn &&
+ $afterlink == '' &&
+ ($sp[$j] == '' || ctype_space($sp[$j]))) {
+ // iterate through space separated URLs and add them to $r
+ $m = $ms[$j];
+ if ($m[0] != '@') { // skip @-references
+ $ac = substr($m, -1, 1);
+ while (contains('.!?,;"\')]}', $ac) && // trim punc @ end
+ ($ac != ')' || !contains($m, '('))) {
+ // allow one paren pair
+ // *** not sure twitter is this smart
+ $afterlink = strcat($ac, $afterlink);
+ $m = substr($m, 0, -1);
+ $ac = substr($m, -1, 1);
+ }
+ if (substr($m, 0, 6) === 'irc://') {
+ // skip it. no known use of in-reply-to an IRC URL
+ } else {
+ $r[count($r)] = webaddresstouri($m, true);
+ }
+ }
+ $j++;
+ }
+ }
+ }
+ return $r;
+}
+
+
+// Twitter POSSE support
+
+// replace URLs with https://j.mp/0011235813 to mimic Twitter's t.co
+function tw_text_proxy() {
+ /// ?> <!-- ///
+ var $args, $afterchar, $afterlink, $i, $isjs,
+ $mlen, $ms, $re,
+ $sp, $spe, $spliti, $prot, $proxy_url;
+ /// --> <?php ///
+
+ $isjs = js();
+ $args = $isjs ? arguments : func_get_args();
+ if (count($args) === 0) {
+ return '';
+ }
+ $t = $args[0];
+
+ $re = auto_link_re();
+ $ms = preg_matches($re, $t);
+ if (!$ms) {
+ return $t;
+ }
+
+ $mlen = count($ms);
+ $sp = preg_split($re, $t);
+ $t = "";
+ $sp[0] = string($sp[0]); // force undefined to ""
+ for ($i=0; $i<$mlen; $i++) {
+ $mi = $ms[$i];
+ $spliti = $sp[$i];
+ $t = strcat($t, $spliti);
+ $sp[$i+1] = string($sp[$i+1]); // force undefined to ""
+ if (substr($sp[$i+1], 0, 1)=='/') { // regex omits '/' before </a
+ $sp[$i+1] = substr($sp[$i+1], 1, strlen($sp[$i+1])-1);
+ $mi = strcat($mi, '/'); // explicitly include in match
+ }
+ $spe = substr($spliti, -2, 2);
+ // don't proxy @-names, plain ccTLDs
+ if ($mi[0]!='@' &&
+ (substr($mi, -3, 1) !== '.' || substr_count($mi, '.') > 1)) {
+ $afterlink = '';
+ $afterchar = substr($mi, -1, 1);
+ while (contains('.!?,;"\')]}', $afterchar) && // trim punc @ end
+ ($afterchar !== ')' || !contains($mi, '('))) {
+ // allow one paren pair
+ // *** not sure twitter is this smart
+ $afterlink = strcat($afterchar, $afterlink);
+ $mi = substr($mi, 0, -1);
+ $afterchar = substr($mi, -1, 1);
+ }
+
+ $prot = protocol_of_uri($mi);
+ $proxy_url = '';
+ if ($prot === 'irc:') {
+ $proxy_url = $mi; // Twitter doesn't tco irc: URLs
+ } else { /* 'https:', 'http:' or presumed for schemeless URLs */
+ $proxy_url = 'https://j.mp/0011235813';
+ }
+ $t = strcat($t, $proxy_url, $afterlink);
+ }
+ else {
+ $t = strcat($t, $mi);
+ }
+ }
+ return strcat($t, $sp[$mlen]);
+}
+
+
+// note_length_check:
+// checks if $note fits in $maxlen characters.
+// if $username is non-empty, checks if RT'd $note fits in $maxlen
+// 0 - bad params or other precondition failure error
+// 200 - exactly fits max characters with RT if username provided
+// 206 - less than max chars with RT if username provided
+// 207 - more than RT safe length, but less than tweet max
+// 208 - tweet max length but with RT would be over
+// 413 - (entity too large) over max tweet length
+// strlen('RT @: ') === 6.
+function note_length_check($note, $maxlen, $username) {
+ /// ?> <!-- ///
+ var $note_size_chk_u, $note_size_chk_n;
+ /// --> <?php ///
+
+ if ($maxlen < 1) { return 0; }
+
+ $note_size_chk_u = $username ? 6 + strlen(string($username)) : 0;
+ $note_size_chk_n = strlen(string($note)) + $note_size_chk_u;
+
+ if ($note_size_chk_n === $maxlen) { return 200; }
+ if ($note_size_chk_n < $maxlen) { return 206; }
+ if ($note_size_chk_n - $note_size_chk_u < $maxlen) { return 207; }
+ if ($note_size_chk_n - $note_size_chk_u === $maxlen) { return 208; }
+ return 413;
+}
+
+function tw_length_check($t, $maxlen, $username) {
+ return note_length_check(tw_text_proxy($t),
+ $maxlen, $username);
+}
+
+function tw_url_to_status_id($u) {
+// $u - tweet permalink url
+// returns tweet status id string; 0 if not a tweet permalink.
+ if (!$u) return 0;
+ $u = explode("/", string($u)); // https:,,twitter.com,t,status,nnn
+ if ($u[2] != "twitter.com" ||
+ $u[4] != "status" ||
+ !ctype_digit($u[5])) {
+ return 0;
+ }
+ return $u[5];
+}
+
+function tw_url_to_username($u) {
+// $u - tweet permalink url
+// returns twitter username; 0 if not a tweet permalink.
+ if (!$u) return 0;
+ $u = explode("/", string($u)); // https:,,twitter.com,t,status,nnn
+ if ($u[2] != "twitter.com" ||
+ $u[4] != "status" ||
+ !ctype_digit($u[5])) {
+ return 0;
+ }
+ return $u[3];
+}
+
+function fb_url_to_event_id($u) {
+// $u - fb event permalink url
+// returns fb event id string; 0 if not a fb event permalink.
+ if (!$u) return 0;
+ $u = explode("/", string($u)); // https:,,fb.com,events,nnn
+ if (($u[2] != "fb.com" && $u[2] != "facebook.com" &&
+ $u[2] != "www.facebook.com") ||
+ $u[3] != "events" ||
+ !ctype_digit($u[4])) {
+ return 0;
+ }
+ return $u[4];
+}
+
+// ===================================================================
+// end CASSIS v0.1, cassis.js
+// ?> --> \ No newline at end of file
diff --git a/views/layout.php b/views/layout.php
index f783406..f853edd 100644
--- a/views/layout.php
+++ b/views/layout.php
@@ -30,6 +30,7 @@
<script src="/js/jquery-1.7.1.min.js"></script>
<script src="/js/script.js"></script>
<script src="/js/date.js"></script>
+ <script src="/js/cassis.js"></script>
</head>
<body role="document">
diff --git a/views/new-post.php b/views/new-post.php
index 4453e15..532a5ca 100644
--- a/views/new-post.php
+++ b/views/new-post.php
@@ -4,6 +4,7 @@
<form role="form" style="margin-top: 20px;" id="note_form">
<div class="form-group">
+ <div id="note_content_remaining" class="pcheck206"><img src="/images/twitter.ico"> <span>140</span></div>
<label for="note_content"><code>content</code></label>
<textarea id="note_content" value="" class="form-control" style="height: 4em;"></textarea>
</div>
@@ -103,9 +104,34 @@
</div>
</div>
+<style type="text/css">
+#note_content_remaining {
+ float: right;
+ font-size: 0.8em;
+ font-weight: bold;
+}
+
+
+.pcheck206 { color: #6ba15c; } /* tweet fits within the limit even after adding RT @username */
+.pcheck207 { color: #c4b404; } /* danger zone, tweet will overflow when RT @username is added */
+.pcheck200,.pcheck208 { color: #59cb3a; } /* exactly fits 140 chars, both with or without RT */
+.pcheck413 { color: #a73b3b; } /* over max tweet length */
+
+</style>
+
<script>
$(function(){
+ $("#note_content").on('change keyup', function(e){
+ var text = $("#note_content").val();
+ var tweet_length = tw_text_proxy(text).length;
+ var tweet_check = tw_length_check(text, 140, "<?= $this->user->twitter_username ?>");
+ var remaining = 140 - tweet_length;
+ $("#note_content_remaining span").html(remaining);
+ $("#note_content_remaining").removeClass("pcheck200 pcheck206 pcheck207 pcheck208 pcheck413");
+ $("#note_content_remaining").addClass("pcheck"+tweet_check);
+ });
+
// ctrl-s to save
$(window).on('keydown', function(e){
if(e.keyCode == 83 && e.ctrlKey){