You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1583 lines
44 KiB

  1. /* <!--
  2. cassis.js Copyright 2008-2015 Tantek Çelik http://tantek.com
  3. http://cassisproject.com conceived:2008-254; created:2009-299;
  4. license:http://creativecommons.org/licenses/by-sa/3.0/ -->
  5. 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.:
  6. ob_start();
  7. include 'cassis.js';
  8. // your code that calls CASSIS functions goes here
  9. ob_end_clean();
  10. /* <!-- <?php // CASSIS v0.1 start -->
  11. // ===================================================================
  12. // PHP-only block. Processed only by PHP. Use only // comments here.
  13. // -------------------------------------------------------------------
  14. function js() {
  15. return false;
  16. }
  17. // global configuration
  18. if (php_min_version("5.1.0")) {
  19. date_default_timezone_set("UTC");
  20. }
  21. function php_min_version($s) {
  22. $s = explode(".", $s);
  23. $phpv = explode(".", phpversion());
  24. for ($i=0; $i < count($s); $i+=1) {
  25. if ($s[$i] > $phpv[$i]) {
  26. return false;
  27. }
  28. }
  29. return true;
  30. }
  31. // -------------------------------------------------------------------
  32. // date time functions
  33. function date_get_full_year($d = "") {
  34. if ($d == "") {
  35. $d = new DateTime();
  36. }
  37. return $d->format('Y');
  38. }
  39. function date_get_timestamp($d) {
  40. return $d->format('U'); // $d->getTimestamp(); // in PHP 5.3+
  41. }
  42. // mixed-case function names are bad for PHP vs JS. Don't do it.
  43. //function Number($n) {
  44. // return $n-0;
  45. //}
  46. // -------------------------------------------------------------------
  47. // old wrappers. transition code away from these
  48. // ** do not use these in new code. **
  49. function getFullYear($d = "") {
  50. // 2010-020 obsoleted. Use date_get_full_year instead
  51. return date_get_full_year($d);
  52. }
  53. // ===================================================================
  54. /*/ // This comment inverter switches from PHP only to JS only.
  55. // JS-only block. Processed only by JS. Use only // comments here.
  56. // -------------------------------------------------------------------
  57. function js() {
  58. return true;
  59. }
  60. // array functions
  61. function array() { // makes an array from arbitrary parameter list.
  62. return Array.prototype.slice.call(arguments);
  63. }
  64. function is_array(a) {
  65. return (typeof(a) === "object") && (a instanceof Array);
  66. }
  67. function count(a) {
  68. return a.length;
  69. }
  70. function array_slice(a, b, e) { // slice an array, begin, optional end
  71. if (a === undefined) { return array(); }
  72. if (b === undefined) { return a; }
  73. if (e === undefined) { return a.slice(b); }
  74. return a.slice(b, e);
  75. }
  76. // -------------------------------------------------------------------
  77. // math and numerical functions
  78. function floor(n) {
  79. return Math.floor(n);
  80. }
  81. function intval(n) {
  82. return parseInt(n, 10);
  83. }
  84. Array.min = function(a) {
  85. // from http://ejohn.org/blog/fast-javascript-maxmin/
  86. return Math.min.apply(Math, a);
  87. };
  88. function min() {
  89. var m = arguments;
  90. if (m.length < 1) {
  91. return false;
  92. }
  93. if (m.length === 1) {
  94. m = m[0];
  95. if (!is_array(m)) {
  96. return m;
  97. }
  98. }
  99. return Array.min(m);
  100. }
  101. function ctype_digit(s) {
  102. return (/^[0-9]+$/).test(s);
  103. }
  104. function ctype_space(s) {
  105. return /\s/.test(s);
  106. }
  107. // -------------------------------------------------------------------
  108. // date time functions
  109. function date_create(s) {
  110. var d = new Date();
  111. d.parse(s);
  112. return d;
  113. }
  114. function date_get_full_year(d) {
  115. if (arguments.length < 1) {
  116. d = new Date();
  117. }
  118. return d.getFullYear();
  119. }
  120. function date_get_timestamp(d) {
  121. return floor(d.getTime() / 1000);
  122. }
  123. // -------------------------------------------------------------------
  124. // character and string functions
  125. function ord(s) {
  126. return s.charCodeAt(0);
  127. }
  128. function strlen(s) {
  129. return s.length;
  130. }
  131. function substr(s, o, n) {
  132. var m = strlen(s);
  133. if ((o < 0 ? -1-o : o) >= m) { return false; }
  134. if (o < 0) { o = m + o; }
  135. if (n < 0) { n = m - o + n; }
  136. if (n === undefined) { n = m - o; }
  137. return s.substring(o, o + n);
  138. }
  139. function substr_count(s, n) {
  140. return s.split(n).length - 1;
  141. }
  142. function strpos(h, n, o) {
  143. // clients must triple-equal test return for === false for no match!
  144. // or use offset(n, h) instead (0 = not found, else 1-based index)
  145. if (arguments.length === 2) {
  146. o = 0;
  147. }
  148. o = h.indexOf(n, o);
  149. if (o === -1) { return false; }
  150. else { return o; }
  151. }
  152. function strncmp(s1, s2, n) {
  153. s1 = substr(String(s1), 0, n);
  154. s2 = substr(String(s2), 0, n);
  155. return (s1 === s2) ? 0 :
  156. ((s1 < s2) ? -1 : 1);
  157. }
  158. function explode(d, s, n) {
  159. if (arguments.length === 2) {
  160. return s.split(d);
  161. }
  162. return s.split(d, n);
  163. }
  164. function implode(d, a) {
  165. return a.join(d);
  166. }
  167. function rawurlencode(s) {
  168. return encodeURIComponent(s);
  169. }
  170. function htmlspecialchars(s) {
  171. var c, i;
  172. c = [["&","&amp;"], ["<","&lt;"], [">","&gt;"],
  173. ["'","&#039;"], ['"',"&quot;"]];
  174. for (i = 0; i < c.length; i+=1) {
  175. s = s.replace(new RegExp(c[i][0], "g"), c[i][1]);
  176. }
  177. return s;
  178. }
  179. function str_ireplace(a, b, s) {
  180. var i;
  181. if (!is_array(a)) {
  182. return s.replace(new RegExp(a, "gi"), is_array(b) ? b[0] : b);
  183. }
  184. else {
  185. for (i=0; i<a.length; i++) {
  186. s = s.replace(new RegExp(a[i], "gi"), is_array(b) ? b[i] : b);
  187. }
  188. return s;
  189. }
  190. }
  191. function trim() {
  192. var c, i, j, m, s;
  193. m = arguments;
  194. s = m[0];
  195. c = count(m) > 1 ? m[1] : " \t\n\r\f\x00\x0b\xa0";
  196. i = 0;
  197. j = strlen(s);
  198. while (strpos(c,s[i])!==false && i<j) {
  199. i+=1;
  200. }
  201. j-=1;
  202. while (j>i && strpos(c,s[j])!==false) {
  203. j-=1;
  204. }
  205. j+=1;
  206. if (j>i) {
  207. return substr(s,i,j-i);
  208. }
  209. else {
  210. return '';
  211. }
  212. }
  213. function rtrim() {
  214. var c,j,m,s;
  215. m = arguments;
  216. s = m[0];
  217. c = count(m)>1 ? m[1] : " \t\n\r\f\x00\x0b\xa0";
  218. j = strlen(s)-1;
  219. while (j>=0 && strpos(c,s[j])!==false) {
  220. j-=1;
  221. }
  222. if (j>=0) {
  223. return substr(s,0,j+1);
  224. }
  225. else {
  226. return '';
  227. }
  228. }
  229. function strtolower(s) {
  230. return s.toLowerCase();
  231. }
  232. function ucfirst(s) {
  233. return s.charAt(0).toUpperCase() + substr(s, 1);
  234. }
  235. // -------------------------------------------------------------------
  236. // more javascript-only php-equivalent functions here
  237. // -------------------------------------------------------------------
  238. // pacify jslint/jshint
  239. // -- define functions and variables only used in PHP flow.
  240. function func_get_args() { }
  241. var FALSE = false;
  242. var PREG_PATTERN_ORDER;
  243. var STR_PAD_LEFT;
  244. // -- may eventually define these for JS.
  245. function date_format() { }
  246. function preg_match_all() { }
  247. function str_pad() { }
  248. function DateTime() { }
  249. // ===================================================================
  250. /**/ // unconditional comment closer exits PHP comment block.
  251. // JS+PHP block. Processed by both JS and PHP. /*...*/ comments ok.
  252. // -------------------------------------------------------------------
  253. /* original js/php test - doesn't pass jslint/jshint.
  254. function js() {
  255. return "00"==false;
  256. }
  257. */
  258. /*global document: false, window: false */
  259. /// ?> <!-- ///
  260. // -------------------------------------------------------------------
  261. // javascript-only framework functions
  262. function doevent(el, evt) {
  263. if (evt==="click" && el.tagName==='A') {
  264. // dispatch/fireEvent fails FF3.5+/IE8+ on [a href] w "click" event
  265. window.location = el.href; // workaround
  266. return true;
  267. }
  268. if (document.createEvent) {
  269. var eo = document.createEvent("HTMLEvents");
  270. eo.initEvent(evt, true, true);
  271. return !el.dispatchEvent(eo);
  272. }
  273. else if (document.createEventObject) {
  274. return el.fireEvent("on"+evt);
  275. }
  276. }
  277. function targetelement(e) {
  278. var t;
  279. e = e ? e : window.event;
  280. t = e.target ? e.target : e.srcElement;
  281. t = (t.nodeType == 3) ? t.parentNode : t; // Safari workaround
  282. return t;
  283. }
  284. // CommonJS (node, browserify, npm) Exports
  285. if (typeof exports !== 'undefined' &&
  286. typeof module !== 'undefined' && module.exports) {
  287. exports.auto_link = auto_link;
  288. exports.num_to_sxg = num_to_sxg;
  289. exports.sxg_to_num = sxg_to_num;
  290. }
  291. /// --> <?php ///
  292. // -------------------------------------------------------------------
  293. // character and string functions
  294. // strcat: takes as many strings as you want to give it.
  295. function strcat() { /// ?> <!-- ///
  296. var $args, $i, $isjs, $r; /// --> <?php ///
  297. $r = "";
  298. $isjs = js();
  299. $args = $isjs ? arguments : func_get_args();
  300. for ($i=count($args)-1; $i>=0; $i-=1) {
  301. $r = $isjs ? $args[$i] + $r : $args[$i] . $r;
  302. }
  303. return $r;
  304. }
  305. function number($s) {
  306. return $s - 0;
  307. }
  308. function string($n) {
  309. if (js()) {
  310. if (typeof($n)==="number") {
  311. return Number($n).toString();
  312. } else if (typeof($n)==="undefined") {
  313. return "";
  314. } else {
  315. return $n.toString();
  316. }
  317. }
  318. else {
  319. return "" . $n;
  320. }
  321. }
  322. function str_pad_left($s1, $n, $s2) {
  323. $s1 = string($s1);
  324. $s2 = string($s2);
  325. if (js()) {
  326. $n -= strlen($s1);
  327. while ($n >= strlen($s2)) {
  328. $s1 = strcat($s2, $s1);
  329. $n -= strlen($s2);
  330. }
  331. if ($n > 0) {
  332. $s1 = strcat(substr($s2, 0, $n), $s1);
  333. }
  334. return $s1;
  335. } else {
  336. return str_pad($s1, $n, $s2, STR_PAD_LEFT);
  337. }
  338. }
  339. function trim_slashes($s) {
  340. if ($s[0]==="/") { // strip unnecessary / delim PHP regex funcs want
  341. return substr($s, 1, strlen($s)-2);
  342. }
  343. return $s;
  344. }
  345. // define a few JS functions that PHP already has, using CASSIS funcs
  346. /// ?> <!-- ///
  347. function preg_match(p, s) {
  348. return (s.match(trim_slashes(p)) ? 1 : 0);
  349. }
  350. function preg_split(p, s) {
  351. return s.split(new RegExp(trim_slashes(p), "gi"));
  352. }
  353. /// --> <?php ///
  354. function preg_match_1($p, $s) { /// ?> <!-- ///
  355. var $m; /// --> <?php ///
  356. if (js()) {
  357. return $s.match(new RegExp(trim_slashes($p), "i"));
  358. } else {
  359. $m = array();
  360. if (preg_match($p, $s, $m) !== FALSE) {
  361. return $m[0];
  362. } else {
  363. return null; //array();
  364. }
  365. }
  366. }
  367. function preg_replace_1($p, $r, $s) {
  368. if (js()) {
  369. return $s.replace(new RegExp(trim_slashes($p), "i"), $r);
  370. }
  371. else {
  372. $r = preg_replace($p, $r, $s, 1);
  373. if ($r !== null) { return $r; }
  374. else { return $s; }
  375. }
  376. }
  377. function preg_matches($p, $s) { /// ?> <!-- ///
  378. var $m; /// --> <?php ///
  379. if (js()) {
  380. return $s.match(new RegExp(trim_slashes($p), "gi"));
  381. } else {
  382. $m = array();
  383. if (preg_match_all($p, $s, $m, PREG_PATTERN_ORDER) !== FALSE) {
  384. return $m[0];
  385. } else {
  386. return null; //array();
  387. }
  388. }
  389. }
  390. function ctype_email_local($s) {
  391. // close enough. no '.' because this is used for last char of.
  392. return (preg_match("/^[a-zA-Z0-9_%+-]+$/", $s));
  393. }
  394. function ctype_uri_scheme($s) {
  395. return (preg_match("/^[a-zA-Z][a-zA-Z0-9+.-]*$/", $s));
  396. }
  397. // -------------------------------------------------------------------
  398. // newbase60
  399. function num_to_sxg($n) { /// ?> <!-- ///
  400. var $d, $m, $p, $s; /// --> <?php ///
  401. $m = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ_abcdefghijkmnopqrstuvwxyz";
  402. $p = "";
  403. $s = "";
  404. if ($n==="" || $n===0) { return "0"; }
  405. if ($n<0) {
  406. $n = -$n;
  407. $p = "-";
  408. }
  409. while ($n>0) {
  410. if(!js() && function_exists('bcmod')) {
  411. $d = bcmod($n, 60);
  412. $s = $m[$d] . $s;
  413. $n = bcdiv(bcsub($n, $d), 60);
  414. } else {
  415. $d = $n % 60;
  416. $s = strcat($m[$d], $s);
  417. $n = ($n-$d)/60;
  418. }
  419. }
  420. return strcat($p, $s);
  421. }
  422. function num_to_sxgf($n, $f) {
  423. if (!$f) { $f=1; }
  424. return str_pad_left(num_to_sxg($n), $f, "0");
  425. }
  426. function sxg_to_num($s) { /// ?> <!-- ///
  427. var $c, $i, $j, $m, $n; /// --> <?php ///
  428. $j = strlen($s);
  429. $m = 1;
  430. $n = 0;
  431. if ($s[0]==="-") {
  432. $m = -1;
  433. $j-=1;
  434. $s = substr($s, 1, $j);
  435. }
  436. for ($i=0; $i<$j; $i+=1) { // iterate from first to last char of $s
  437. $c = ord($s[$i]); // put current ASCII of char into $c
  438. if ($c>=48 && $c<=57) { $c=$c-48; }
  439. else if ($c>=65 && $c<=72) { $c-=55; }
  440. else if ($c===73 || $c===108) { $c=1; } // typo cap I lower l to 1
  441. else if ($c>=74 && $c<=78) { $c-=56; }
  442. else if ($c===79) { $c=0; } // error correct typo capital O to 0
  443. else if ($c>=80 && $c<=90) { $c-=57; }
  444. else if ($c===95 || $c===45) { $c=34; } // _ and dash - to _
  445. else if ($c>=97 && $c<=107) { $c-=62; }
  446. else if ($c>=109 && $c<=122) { $c-=63; }
  447. else { break; } // treat all other noise as end of number
  448. if(!js() && function_exists('bcadd')) {
  449. $n = bcadd(bcmul(60, $n), $c);
  450. } else {
  451. $n = 60*$n + $c;
  452. }
  453. }
  454. return $n*$m;
  455. }
  456. function sxg_to_numf($s, $f) {
  457. if ($f===undefined) { $f=1; }
  458. return str_pad_left(sxg_to_num($s), $f, "0");
  459. }
  460. // -------------------------------------------------------------------
  461. // == newbase60 compat functions only == (before 2011-149)
  462. function numtosxg($n) {
  463. return num_to_sxg($n);
  464. }
  465. function numtosxgf($n, $f) {
  466. return num_to_sxgf($n, $f);
  467. }
  468. function sxgtonum($s) {
  469. return sxg_to_num($s);
  470. }
  471. function sxgtonumf($s, $f) {
  472. return sxg_to_numf($s, $f);
  473. }
  474. // -------------------------------------------------------------------
  475. // date and time
  476. function date_create_ymd($s) { /// ?> <!-- ///
  477. var $d; /// --> <?php ///
  478. if (!$s) {
  479. return (js() ? new Date() : new DateTime());
  480. }
  481. if (js()) {
  482. if (substr($s,4,1)==='-') {
  483. $s=strcat(substr($s,0,4), substr($s,5,2), substr($s,8,2));
  484. }
  485. $d = new Date(substr($s,0,4),substr($s,4,2)-1,substr($s,6,2));
  486. $d.setHours(0); // was setUTCHours, avoiding bc JS no default tz
  487. return $d;
  488. } else {
  489. return date_create(strcat($s, " 00:00:00"));
  490. }
  491. }
  492. function date_create_timestamp($s) {
  493. if (js()) {
  494. return new Date(1000*$s);
  495. } else {
  496. return new DateTime(strcat("@", string($s)));
  497. }
  498. }
  499. // function date_get_timestamp($d) // in PHP/JS specific code above.
  500. function date_get_rfc3339($d) {
  501. if (js()) {
  502. return strcat($d.getFullYear(), '-',
  503. str_pad_left(1+$d.getUTCMonth(), 2, "0"), '-',
  504. str_pad_left($d.getDate(), 2, "0"), 'T',
  505. str_pad_left($d.getUTCHours(), 2, "0"), ':',
  506. str_pad_left($d.getUTCMinutes(), 2, "0"), ':',
  507. str_pad_left($d.getUTCSeconds(), 2, "0"), 'Z');
  508. } else {
  509. return date_format($d, 'c');
  510. }
  511. }
  512. function dt_to_time($dt) {
  513. $dt = explode("T", $dt);
  514. if (count($dt)==1) {
  515. $dt = explode(" ", $dt);
  516. }
  517. return (count($dt)>1) ? $dt[1] : "0:00";
  518. }
  519. function dt_to_date($dt) {
  520. $dt = explode("T", $dt);
  521. if (count($dt)==1) {
  522. $dt = explode(" ", $dt);
  523. }
  524. return $dt[0];
  525. }
  526. function dt_to_ordinal_date($dt) {
  527. return ymd_to_yd(dt_to_date($dt));
  528. }
  529. // -------------------------------------------------------------------
  530. // newcal
  531. function isleap($y) {
  532. return ($y % 4 === 0 && ($y % 100 !== 0 || $y % 400 === 0));
  533. }
  534. function ymdp_to_d($y, $m, $d) { /// ?> <!-- ///
  535. var $md; /// --> <?php ///
  536. $md = array(
  537. array(0,31,59,90,120,151,181,212,243,273,304,334),
  538. array(0,31,60,91,121,152,182,213,244,274,305,335));
  539. return $md[number(isleap($y))][$m-1] + number($d);
  540. }
  541. function ymd_to_d($d) {
  542. if (substr($d, 4, 1)==='-') {
  543. return ymdp_to_d(substr($d,0,4),substr($d,5,2),substr($d,8,2));
  544. } else {
  545. return ymdp_to_d(substr($d,0,4),substr($d,4,2),substr($d,6,2));
  546. }
  547. }
  548. function ymdp_to_yd($y, $m, $d) {
  549. return strcat(str_pad_left($y, 4, "0"), '-',
  550. str_pad_left(ymdp_to_d($y, $m, $d), 3, "0"));
  551. }
  552. function ymd_to_yd($d) {
  553. if (substr($d, 4, 1)==='-') {
  554. return ymdp_to_yd(substr($d,0,4),substr($d,5,2),substr($d,8,2));
  555. } else {
  556. return ymdp_to_yd(substr($d,0,4),substr($d,4,2),substr($d,6,2));
  557. }
  558. }
  559. function date_get_ordinal_days($d) {
  560. if (js()) {
  561. return ymdp_to_d($d.getFullYear(), 1+$d.getMonth(), $d.getDate());
  562. } else {
  563. return 1+date_format($d, 'z');
  564. }
  565. }
  566. function bim_from_od($d) {
  567. return 1+floor(($d-1)/61);
  568. }
  569. function date_get_bim() { /// ?> <!-- ///
  570. var $args; /// --> <?php ///
  571. $args = js() ? arguments : func_get_args();
  572. return bim_from_od(
  573. date_get_ordinal_days(
  574. date_create_ymd((count($args) > 0) ? $args[0] : 0)));
  575. }
  576. function get_nm_str($m) { /// ?> <!-- ///
  577. var $a; /// --> <?php ///
  578. $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");
  579. return $a[($m-1)];
  580. }
  581. function nm_from_od($d) {
  582. return ((($d-1) % 61) > 29) ? 2+2*(bim_from_od($d)-1) : 1+2*(bim_from_od($d)-1);
  583. }
  584. // date_get_ordinal_date: optional date argument
  585. function date_get_ordinal_date() { /// ?> <!-- ///
  586. var $args, $d; /// --> <?php ///
  587. $args = js() ? arguments : func_get_args();
  588. $d = date_create_ymd((count($args) > 0) ? $args[0] : 0);
  589. return strcat(date_get_full_year($d), '-',
  590. str_pad_left(date_get_ordinal_days($d), 3, "0"));
  591. }
  592. // -------------------------------------------------------------------
  593. // begin epochdays
  594. function y_to_days($y) {
  595. // convert y-01-01 to epoch days
  596. return floor(
  597. (date_get_timestamp(date_create_ymd(strcat($y, "-01-01"))) -
  598. date_get_timestamp(date_create_ymd("1970-01-01")))/86400);
  599. }
  600. // convert ymd to epoch days and sexagesimal epoch days (sd)
  601. function ymd_to_days($d) {
  602. return yd_to_days(ymd_to_yd($d));
  603. }
  604. /* old:
  605. function ymd_to_days($d) {
  606. // fails in JS, "2013-03-10" and "2013-03-11" both return 15774
  607. return floor(
  608. (date_get_timestamp(date_create_ymd($d)) -
  609. date_get_timestamp(date_create_ymd("1970-01-01")))/86400);
  610. }
  611. */
  612. function ymd_to_sd($d) {
  613. return num_to_sxg(ymd_to_days($d));
  614. }
  615. function ymd_to_sdf($d, $f) {
  616. return num_to_sxgf(ymd_to_days($d), $f);
  617. }
  618. // ordinal date (YYYY-DDD) to ymd, epoch days, sexagesimal epoch days
  619. function ydp_to_ymd($y, $d) { /// ?> <!-- ///
  620. var $md, $m; /// --> <?php ///
  621. $md = array(
  622. array(0,31,59,90,120,151,181,212,243,273,304,334,365),
  623. array(0,31,60,91,121,152,182,213,244,274,305,335,366));
  624. $d -= 1;
  625. $m = trunc($d / 29);
  626. if ($md[isleap($y) - 0][$m] > $d) $m -= 1;
  627. $d = $d - $md[isleap($y)-0][$m] + 1;
  628. $m += 1;
  629. return strcat($y, '-', str_pad_left($m, 2, '0'),
  630. '-', str_pad_left($d, 2, '0'));
  631. }
  632. function yd_to_ymd($d) {
  633. return ydp_to_ymd(substr($d, 0, 4), substr($d, 5, 3));
  634. }
  635. function yd_to_days($d) {
  636. return y_to_days(substr($d, 0, 4)) - 1 + number(substr($d, 5, 3));
  637. }
  638. function yd_to_sd($d) {
  639. return num_to_sxg(yd_to_days($d));
  640. }
  641. function yd_to_sdf($d, $f) {
  642. return num_to_sxgf(yd_to_days($d), $f);
  643. }
  644. // convert epoch days or sexagesimal epoch days (sd) to ordinal date
  645. function days_to_yd($d) { /// ?> <!-- ///
  646. var $a, $y; /// --> <?php ///
  647. $d = date_create_timestamp(
  648. date_get_timestamp(
  649. date_create_ymd("1970-01-01")) + $d*86400);
  650. $y = date_get_full_year($d);
  651. $a = date_create_ymd(strcat($y, "-01-01"));
  652. return strcat($y, "-",
  653. str_pad_left(
  654. 1 + floor((
  655. date_get_timestamp($d) - date_get_timestamp($a))/86400), 3, "0"));
  656. }
  657. function sd_to_yd($d) {
  658. return days_to_yd(sxg_to_num($d));
  659. }
  660. // -------------------------------------------------------------------
  661. // compat as of 2011-143
  662. function ymdptod($y,$m,$d) { return ymdp_to_d($y,$m,$d); }
  663. function ymdptoyd($y,$m,$d) { return ymdp_to_yd($y,$m,$d); }
  664. function ymdtoyd($d) { return ymd_to_yd($d); }
  665. function bimfromod($d) { return bim_from_od($d); }
  666. function getnmstr($m) { return get_nm_str($m); }
  667. function nmfromod($d) { return nm_from_od($d); }
  668. function ymdtodays($d) { return ymd_to_days($d); }
  669. function ymdtosd($d) { return ymd_to_sd($d); }
  670. function ymdtosdf($d,$f) { return ymd_to_sdf($d, $f); }
  671. function ydtodays($d) { return yd_to_days($d); }
  672. function ydtosd($d) { return yd_to_sd($d); }
  673. function ydtosdf($d,$f) { return yd_to_sdf($d, $f); }
  674. function daystoyd($d) { return days_to_yd($d); }
  675. function sdtoyd($d) { return sd_to_yd($d); }
  676. // -------------------------------------------------------------------
  677. // HTTP
  678. function is_http_header($s) {
  679. return (preg_match_1('/^[a-zA-Z][-\\w]*:/',$s)!==null);
  680. }
  681. // -------------------------------------------------------------------
  682. // webaddress
  683. function web_address_to_uri($wa, $addhttp) {
  684. if (!$wa ||
  685. (substr($wa, 0, 7) === 'http://') ||
  686. (substr($wa, 0, 8) === 'https://') ||
  687. (substr($wa, 0, 6) === 'irc://')) {
  688. return $wa;
  689. }
  690. if ((substr($wa, 0, 7) === 'Http://') ||
  691. (substr($wa, 0, 8) === 'Https://')) {
  692. // handle iOS4 overcapitalization of input entries
  693. return strcat('h', substr($wa, 1, strlen($wa)));
  694. }
  695. // TBI: may want to handle typos as well like:
  696. // missing/extra : or / http:/ http///
  697. // missing letter in protocol: ttps htps htts, ttp htp htt, ir ic rc
  698. // use strtolower(substr($wa, 0, 6)); // handle capitals in URLs
  699. if (substr($wa,0,1) === '@') {
  700. return strcat('https://twitter.com/', substr($wa, 1, strlen($wa)));
  701. }
  702. if ($addhttp) {
  703. $wa = strcat('http://', $wa);
  704. }
  705. return $wa;
  706. }
  707. function uri_clean($uri) {
  708. $uri = web_address_to_uri($uri, false);
  709. // prune the optional http:// for a neater param
  710. if (substr($uri, 0, 7) === 'http://') {
  711. $uri = explode('://', $uri);
  712. $uri = array_slice($uri, 1);
  713. $uri = implode('://', $uri);
  714. }
  715. // URL encode
  716. return str_ireplace("%3A", ":",
  717. str_ireplace("%2F", "/", rawurlencode($uri)));
  718. }
  719. function protocol_of_uri($uri) {
  720. if (offset(':', $uri) === 0) { return ""; }
  721. $uri = explode(':', $uri, 2);
  722. if (!ctype_uri_scheme($uri[0])) { return ""; }
  723. return strcat($uri[0], ':');
  724. }
  725. // returns e.g. //ttk.me/b/4DY1?seriously=yes#ud
  726. function relative_uri_hash($uri) {
  727. if (offset(':', $uri) === 0) { return ""; }
  728. $uri = explode(':', $uri, 2);
  729. if (!ctype_uri_scheme($uri[0])) { return ""; }
  730. return $uri[1];
  731. }
  732. // returns e.g. ttk.me
  733. function hostname_of_uri($uri) {
  734. $uri = explode('/', $uri, 4);
  735. if (count($uri) > 2) {
  736. $uri = $uri[2];
  737. if (offset(':', $uri) !== 0) {
  738. $uri = explode(':', $uri, 2);
  739. $uri = $uri[0];
  740. }
  741. return $uri;
  742. }
  743. return '';
  744. }
  745. function sld_of_uri($uri) {
  746. $uri = hostname_of_uri($uri);
  747. $uri = explode('.', $uri);
  748. if (count($uri) > 1) {
  749. return $uri[count($uri) - 2];
  750. }
  751. return "";
  752. }
  753. function path_of_uri($uri) {
  754. $uri = explode('/', $uri, 4);
  755. if (count($uri) > 3) {
  756. $uri = array_slice($uri, 3);
  757. $uri = strcat('/', implode('/', $uri));
  758. if (offset('?', $uri) !== 0) {
  759. $uri = explode('?', $uri, 2);
  760. $uri = $uri[0];
  761. }
  762. if (offset('#', $uri) !== 0) {
  763. $uri = explode('#', $uri, 2);
  764. $uri = $uri[0];
  765. }
  766. return $uri;
  767. }
  768. return '/';
  769. }
  770. function is_http_uri($uri) {
  771. $uri = explode(":", $uri, 2);
  772. return !!strncmp($uri[0], "http", 4);
  773. }
  774. // -------------------------------------------------------------------
  775. // compat as of 2011-149
  776. function webaddresstouri($wa, $addhttp) {
  777. return web_address_to_uri($wa, $addhttp);
  778. }
  779. function uriclean($uri) { return uri_clean($uri); }
  780. // -------------------------------------------------------------------
  781. // hexatridecimal
  782. function num_to_hxt($n) { /// ?> <!-- ///
  783. var $d, $m, $s; /// --> <?php ///
  784. $m = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  785. $s = "";
  786. if ($n === undefined || $n === 0) { return "0"; }
  787. while ($n>0) {
  788. $d = $n % 36;
  789. $s = strcat($m[$d], $s);
  790. $n = ($n-$d)/36;
  791. }
  792. return $s;
  793. }
  794. function num_to_hxtf($n, $f) {
  795. if ($f === undefined) { $f=1; }
  796. return str_pad_left(num_to_hxt($n), $f, "0");
  797. }
  798. function hxt_to_num($h) { /// ?> <!-- ///
  799. var $c, $i, $j, $n; /// --> <?php ///
  800. $j = strlen($h);
  801. $n = 0;
  802. for ($i=0; $i<$j; $i+=1) { // iterate from first to last char of $h
  803. $c = ord($h[$i]); // put current ASCII of char into $c
  804. if ($c>=48 && $c<=57) { $c=$c-48; } // 0-9
  805. else if ($c>=65 && $c<=90) { $c-=55; } // A-Z
  806. else if ($c>=97 && $c<=122) { $c-=87; } // a-z treat as A-Z
  807. else { $c = 0; } // treat all other noise as 0
  808. $n = 36*$n + $c;
  809. }
  810. return $n;
  811. }
  812. // -------------------------------------------------------------------
  813. // compat as of 2011-149
  814. function numtohxt($n) { return num_to_hxt($n); }
  815. function numtohxtf($n,$f) { return num_to_hxtf($n, $f); }
  816. function hxttonum($h) { return hxt_to_num($h); }
  817. // -------------------------------------------------------------------
  818. // ISBN-10
  819. function num_to_isbn10($n) { /// ?> <!-- ///
  820. var $d, $f, $i; /// --> <?php ///
  821. $n = string($n);
  822. $d = 0;
  823. $f = 2;
  824. for ($i=strlen($n)-1; $i>=0; $i-=1) {
  825. $d += $n[$i]*$f;
  826. $f += 1;
  827. }
  828. $d = 11-($d % 11);
  829. if ($d===10) {$d="X";}
  830. else if ($d===11) {$d="0";}
  831. else {$d=string($d);}
  832. return strcat(str_pad_left($n, 9, "0"), $d);
  833. }
  834. // -------------------------------------------------------------------
  835. // compat as of 2011-149
  836. function numtoisbn10($n) { return num_to_isbn10($n); }
  837. // -------------------------------------------------------------------
  838. // HyperTalk
  839. function trunc($n) { // just an alias from BASIC days
  840. return floor($n);
  841. }
  842. function offset($n, $h) {
  843. $n = strpos($h, $n);
  844. if ($n === false) {
  845. return 0;
  846. } else {
  847. return $n+1;
  848. }
  849. }
  850. function contains($h, $n) {
  851. // HyperTalk syntax haystack contains needle: if ("abc" contains "b")
  852. return strpos($h, $n)!==false;
  853. }
  854. function last_character_of($s) {
  855. return (strlen($s) > 0) ? $s[strlen($s)-1] : '';
  856. }
  857. function line_1_of($s) { /// ?> <!-- ///
  858. var $r; /// --> <?php ///
  859. $r = preg_match_1('/^[\\w\\W]*?[\\n\\r]+?/', $s);
  860. if ($r === null) { return $s; }
  861. return substr($r, 0, strlen($r)-1);
  862. }
  863. function delete_line_1_of($s) {
  864. if (preg_match_1('/^[\\w\\W]*?[\\n\\r]+?/', $s) === null) {
  865. return '';
  866. }
  867. return preg_replace_1('/^[\\w\\W]*?[\\n\\r]+?/', '', $s);
  868. }
  869. // -------------------------------------------------------------------
  870. // microformats
  871. // xpath expressions to extract microformats
  872. function xp_has_class($s) {
  873. return strcat("//*[contains(concat(' ',@class,' '),' ",$s," ')]");
  874. }
  875. function xpr_has_class($s) {
  876. return strcat(".//*[contains(concat(' ',@class,' '),' ",$s," ')]");
  877. }
  878. function xp_has_id($s) {
  879. return strcat("//*[@id='", $s, "']");
  880. }
  881. function xp_attr_starts_with($a, $s) {
  882. return strcat("//*[starts-with(@", $a, ",'", $s, "')]");
  883. }
  884. function xp_has_rel($s) {
  885. return strcat("//*[contains(concat(' ',@rel,' '),' ", $s, " ')]");
  886. }
  887. function xpr_has_rel($s) {
  888. return strcat(".//*[contains(concat(' ',@rel,' '),' ", $s, " ')]");
  889. }
  890. function xpr_attr_starts_with_has_rel($a, $s, $r) {
  891. return strcat(".//*[contains(concat(' ',@rel,' '),' ", $r,
  892. " ') and starts-with(@", $a, ",'", $s, "')]");
  893. }
  894. // value class pattern readable date time from ISO8601 datetime
  895. function vcp_dt_readable($d) { /// ?> <!-- ///
  896. var $r; /// --> <?php ///
  897. $d = explode("T", $d);
  898. $r = "";
  899. if (count($d)>1) {
  900. $r = explode("-", $d[1]);
  901. if (count($d)==1) {
  902. $r = explode("+", $d[1]);
  903. }
  904. if (count($d)>1) {
  905. $r = strcat('<time class="value" datetime="',$d[1],'">',
  906. $r[0],'</time> on ');
  907. }
  908. else {
  909. $r = strcat('<time class="value">',$d[1],'</time> on ');
  910. }
  911. }
  912. return strcat($r, '<time class="value">', $d[0], '</time>');
  913. }
  914. // -------------------------------------------------------------------
  915. // compat as of 2011-149
  916. function xphasclass($s) { return xp_has_class($s); }
  917. function xprhasclass($s) { return xpr_has_class($s); }
  918. function xphasid($s) { return xp_has_id($s); }
  919. function xpattrstartswith($a, $s) {
  920. return xp_attr_starts_with($a, $s);
  921. }
  922. function xphasrel($s) { return xp_has_rel($s); }
  923. function xprhasrel($s) { return xpr_has_rel($s); }
  924. function xprattrstartswithhasrel($a, $s, $r) {
  925. return xpr_attr_starts_with_has_rel($a, $s, $r);
  926. }
  927. function vcpdtreadable($d) { return vcp_dt_readable($d); }
  928. // -------------------------------------------------------------------
  929. // whistle
  930. // algorithmic URL shortener core
  931. // YYYY/DDD/tnnn to tdddss
  932. // ordinal date, type, decimal #, to sexagesimal epoch days, sexagesimal #
  933. function whistle_short_path($p) {
  934. return strcat(substr($p, 9, 1),
  935. ((substr($p, 9, 1)!=='t') ? "/" : ""),
  936. yd_to_sdf(substr($p, 0, 8), 3),
  937. num_to_sxg(substr($p, 10, 3)));
  938. }
  939. // -------------------------------------------------------------------
  940. // falcon
  941. function html_unesc_amp_only($s) {
  942. return str_ireplace('&amp;', '&', $s);
  943. }
  944. function html_esc_amper_once($s) {
  945. return str_ireplace('&', '&amp;', html_unesc_amp_only($s));
  946. }
  947. function html_esc_amp_ang($s) {
  948. return str_ireplace('<', '&lt;',
  949. str_ireplace('>', '&gt;', html_esc_amper_once($s)));
  950. }
  951. function ellipsize_to_word($s, $max, $e, $min) { /// ?> <!-- ///
  952. var $elen, $i, $slen; /// --> <?php ///
  953. if (strlen($s)<=$max) {
  954. return $s; // no need to ellipsize
  955. }
  956. $elen = strlen($e);
  957. $slen = $max-$elen;
  958. // if last characters before $max+1 are ': ', truncate w/o ellipsis.
  959. // no need to take length of ellipsis into account
  960. if ($e==='...') {
  961. for ($i=1; $i<=$elen+1; $i+=1) {
  962. if (substr($s, $max-$i, 2)===': ') {
  963. return substr($s, 0, $max-$i+1);
  964. }
  965. }
  966. }
  967. if ($min) {
  968. // if a non-zero minimum is provided, then
  969. // find previous space or word punctuation to break at.
  970. // do not break at %`'"&.!?^ - reasons why to be documented.
  971. while ($slen>$min &&
  972. !contains('@$ -~*()_+[]{}|;,<>', $s[$slen-1])) {
  973. $slen-=1;
  974. }
  975. }
  976. // at this point we've got a min length string,
  977. // only do minimum trimming necessary to avoid a punctuation error.
  978. // trim slash after colon or slash
  979. if ($s[$slen-1]==='/' && $slen > 2) {
  980. if ($s[$slen-2]===':') {
  981. $slen-=1;
  982. }
  983. if ($s[$slen-2]==='/') {
  984. $slen-=2;
  985. }
  986. }
  987. //if trimmed at a ":" in a URL, trim the whole thing
  988. //or trimmed at "http", trim the whole URL
  989. if ($s[$slen-1]===':' && $slen > 5 &&
  990. substr($s, $slen-5, 5)==='http:') {
  991. $slen -= 5;
  992. } else if ($s[$slen-1]==='p' && $slen > 4 &&
  993. substr($s, $slen-4, 4)==='http') {
  994. $slen -= 4;
  995. } else if ($s[$slen-1]==='t' && $slen > 4 &&
  996. (substr($s, $slen-3, 4)==='http' ||
  997. substr($s, $slen-3, 4)===' htt')) {
  998. $slen -= 3;
  999. } else if ($s[$slen-1]==='h' && $slen > 4 &&
  1000. substr($s, $slen-1, 4)==='http') {
  1001. $slen -= 1;
  1002. }
  1003. //if char immediately before ellipsis would be @$ then trim it
  1004. if ($slen > 0 && contains('@$', $s[$slen-1])) {
  1005. $slen-=1;
  1006. }
  1007. //if char before ellipsis would be sentence terminator, trim 2 more
  1008. while ($slen > 1 && contains('.!?', $s[$slen-1])) {
  1009. $slen-=2;
  1010. }
  1011. // trim extra whitespace before ellipsis down to one space
  1012. if ($slen > 2 && contains("\n\r ", $s[$slen-1])) {
  1013. while (contains("\n\r ", $s[$slen-2]) && $slen > 2) {
  1014. --$slen;
  1015. }
  1016. }
  1017. if ($slen < 1) { // somehow shortened too much
  1018. return $e; // or ellipsis by itself exceeded max, return ellipsis.
  1019. }
  1020. // if last two chars are ': ', omit ellipsis.
  1021. if ($e==='...' && substr($s, $slen-2, 2)===': ') {
  1022. return substr($s, 0, $slen);
  1023. }
  1024. return strcat(substr($s, 0, $slen), $e);
  1025. }
  1026. function trim_leading_urls($s) {
  1027. // deliberately trim URLs with explicit http: / https: from start
  1028. // keep schemeless URLs, @-names as expected user-visible text
  1029. // if empty or just space after trimming, just return original
  1030. $r = trim($s);
  1031. while (substr($r, 0, 5) == 'http:' || substr($r, 0, 6) == 'https:')
  1032. {
  1033. $ws = offset(' ', $r);
  1034. $rs = offset("\r", $r);
  1035. if ($rs == 0) { $rs = offset("\n", $r); }
  1036. if ($rs != 0 && $rs < $ws) { $ws = $rs; }
  1037. if ($ws == 0) { return $s; }
  1038. $r = substr($r, $ws, strlen($r)-$ws);
  1039. }
  1040. $r = trim($r);
  1041. return ((strlen($r) > 0) ? $r : $s);
  1042. }
  1043. function auto_space($s) {
  1044. // replace linebreaks with <br class="auto-break"/>
  1045. // and one leading space with &nbsp;
  1046. // replace " " with " &nbsp;"
  1047. // replace leading spaces (on a line or before spaces) with nbsp;
  1048. if ($s[0] === ' ') {
  1049. $s = strcat('&#xA0;', substr($s, 1, strlen($s)-1));
  1050. }
  1051. return str_ireplace(array("\r\n", "\r", "\n ", "\n", " "),
  1052. array("\n", "\n", '<br class="auto-break"/>&#xA0;',
  1053. '<br class="auto-break"/>',
  1054. ' &#xA0;'),
  1055. $s);
  1056. }
  1057. function auto_link_re() {
  1058. 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|$)/';
  1059. // ccTLD compressed regular expression clauses (re)created.
  1060. // .mobi .jobs deliberately excluded to discourage layer violations.
  1061. // see http://flic.kr/p/2kmuSL for more on the problematic new gTLDs
  1062. // part of $re derived from Android Open Source Project, Apache 2.0
  1063. // with a bunch of subsequent fixes/improvements (e.g. ttk.me/t44H2)
  1064. // thus auto_link_re is also Apache 2.0 licensed
  1065. // http://www.apache.org/licenses/LICENSE-2.0
  1066. // - Tantek 2010-046 (moved to auto_link_re 2012-062)
  1067. }
  1068. // auto_link: param 1: text;
  1069. // optional: param 2: do embeds or not (false),
  1070. // param 3: do auto_links or not (true)
  1071. // auto_link is idempotent, works on plain text or typical markup.
  1072. function auto_link() {
  1073. /// ?> <!-- ///
  1074. var $args, $afterchar, $afterlink, $do_embed, $fe, $i, $isjs,
  1075. $mi, $mlen, $ms, $re,
  1076. $sp, $spe, $spliti, $t, $wmi, $yvid;
  1077. /// --> <?php ///
  1078. $isjs = js();
  1079. $args = $isjs ? arguments : func_get_args();
  1080. if (count($args) === 0) {
  1081. return '';
  1082. }
  1083. $t = $args[0];
  1084. $do_embed = (count($args) > 1) && ($args[1]!==false);
  1085. $do_link = (count($args) < 3) || ($args[2]!==false);
  1086. $re = auto_link_re();
  1087. $ms = preg_matches($re, $t);
  1088. if (!$ms) {
  1089. return $t;
  1090. }
  1091. $mlen = count($ms);
  1092. $sp = preg_split($re, $t);
  1093. $t = "";
  1094. $sp[0] = string($sp[0]); // force undefined to ""
  1095. for ($i=0; $i<$mlen; $i+=1) {
  1096. $mi = $ms[$i];
  1097. $spliti = $sp[$i];
  1098. $t = strcat($t, $spliti);
  1099. $sp[$i+1] = string($sp[$i+1]); // force undefined to ""
  1100. if (substr($sp[$i+1], 0, 1)==='/') { //regex omits end/ before </a
  1101. $sp[$i+1] = substr($sp[$i+1], 1, strlen($sp[$i+1])-1);
  1102. $mi = strcat($mi, '/'); // include / in the match
  1103. }
  1104. $spe = substr($spliti, -2, 2);
  1105. // avoid 2x-linking, don't link CSS @-rules, attr values, asciibet
  1106. if ((!$spe || !preg_match('/(?:\\=[\\"\\\']?|t;)/', $spe)) &&
  1107. substr(trim($sp[$i+1]), 0, 3)!=='</a' &&
  1108. (!contains(
  1109. '@charset@font@font-face@import@media@namespace@page@ABCDEFGHIJKLMNOPQ@',
  1110. strcat($mi, '@')))) {
  1111. $afterlink = '';
  1112. $afterchar = substr($mi, -1, 1);
  1113. while (contains('.!?,;"\')]}', $afterchar) && // trim punc from
  1114. ($afterchar!==')' || !contains($mi,'('))) { // 1 () pair
  1115. $afterlink = strcat($afterchar, $afterlink);
  1116. $mi = substr($mi, 0, -1);
  1117. $afterchar = substr($mi, -1, 1);
  1118. }
  1119. $fe = 0;
  1120. if ($do_embed) {
  1121. $fe = strtolower(
  1122. (substr($mi, -4, 1)==='.') ? substr($mi, -4, 4)
  1123. : substr($mi, -5, 5));
  1124. }
  1125. $wmi = web_address_to_uri($mi, true);
  1126. $prot = protocol_of_uri($wmi);
  1127. $hn = hostname_of_uri($wmi);
  1128. $pa = path_of_uri($wmi);
  1129. $ih = is_http_uri($wmi);
  1130. $ahref = '<span class="figure" style="text-align:left">';
  1131. $enda = '</span>';
  1132. if ($do_link) {
  1133. $ahref = strcat('<a class="auto-link figure" href="',
  1134. $wmi, '">');
  1135. $enda = '</a>';
  1136. }
  1137. if ($fe &&
  1138. ($fe === '.jpeg' || $fe === '.jpg' || $fe === '.png' ||
  1139. $fe === '.gif' || $fe === '.svg')) {
  1140. $alt = strcat('a ',
  1141. (offset('photo', $mi) != 0) ? 'photo'
  1142. : substr($fe, 1));
  1143. $t = strcat($t, $ahref, '<img class="auto-embed" alt="',
  1144. $alt, '" src="', $wmi, '"/>', $enda, $afterlink);
  1145. } else if ($fe &&
  1146. ($fe === '.mp4' || $fe === '.mov' ||
  1147. $fe === '.ogv' || $fe === '.webm'))
  1148. {
  1149. $t = strcat($t, $ahref, '<video class="auto-embed" ',
  1150. 'controls="controls" src="', $wmi, '"></video>',
  1151. $enda, $afterlink);
  1152. } else if ($hn === 'vimeo.com'
  1153. && ctype_digit(substr($pa, 1))) {
  1154. if ($do_link) {
  1155. $t = strcat($t, '<a class="auto-link" href="',
  1156. 'https:', relative_uri_hash($wmi),
  1157. '">', $mi, '</a> ');
  1158. }
  1159. $t = strcat($t, '<iframe class="vimeo-player auto-embed figure" width="480" height="385" style="border:0" src="', 'https://player.vimeo.com/video/',
  1160. substr($pa, 1), '"></iframe>',
  1161. $afterlink);
  1162. } else if ($hn === 'youtu.be' ||
  1163. (($hn === 'youtube.com' || $hn === 'www.youtube.com')
  1164. && ($yvid = offset('watch?v=', $mi)) !== 0)) {
  1165. if ($hn === 'youtu.be') {
  1166. $yvid = substr($pa, 1);
  1167. } else {
  1168. $yvid = explode('&', substr($mi, $yvid+7));
  1169. $yvid = $yvid[0];
  1170. }
  1171. if ($do_link) {
  1172. $t = strcat($t, '<a class="auto-link" href="',
  1173. 'https:', relative_uri_hash($wmi),
  1174. '">', $mi, '</a> ');
  1175. }
  1176. $t = strcat($t, '<iframe class="youtube-player auto-embed figure" width="480" height="385" style="border:0" src="', 'https://www.youtube.com/embed/',
  1177. $yvid, '"></iframe>',
  1178. $afterlink);
  1179. } else if ($mi[0] === '@' && $do_link) {
  1180. if ($sp[$i+1][0] === '.' &&
  1181. $spliti != '' &&
  1182. ctype_email_local(substr($spliti, -1, 1))) {
  1183. // if email address, simply append info, no linking
  1184. $t = strcat($t, $mi, $afterlink);
  1185. }
  1186. else {
  1187. // treat it as a Twitter @-username reference and link it
  1188. $t = strcat($t, '<a class="auto-link h-x-username" href="',
  1189. $wmi, '">', $mi, '</a>',
  1190. $afterlink);
  1191. }
  1192. } else if ($do_link) {
  1193. $t = strcat($t, '<a class="auto-link" href="',
  1194. $wmi, '">', $mi, '</a>',
  1195. $afterlink);
  1196. } else {
  1197. $t = strcat($t, $mi, $afterlink);
  1198. }
  1199. } else {
  1200. $t = strcat($t, $mi);
  1201. }
  1202. }
  1203. return strcat($t, $sp[$mlen]);
  1204. }
  1205. // returns array of URLs after literal "in-reply-to:" in text
  1206. function get_in_reply_to_urls($s) {
  1207. /// ?> <!-- ///
  1208. var $irtn, $r, $re, $i, $j,
  1209. $ms, $sp, $afterlink, $ac;
  1210. /// --> <?php ///
  1211. $s = explode('in-reply-to: ', $s);
  1212. $irtn = count($s);
  1213. if ($irtn < 2) { return array(); }
  1214. $r = array();
  1215. $re = auto_link_re();
  1216. for ($i=1; $i<$irtn; $i++) {
  1217. // iterate through all strings after an 'in-reply-to: ' for URLs
  1218. $ms = preg_matches($re, $s[$i]);
  1219. $msn = count($ms);
  1220. if ($ms) {
  1221. $sp = preg_split($re, $s[$i]);
  1222. $j = 0;
  1223. $afterlink = '';
  1224. while ($j<$msn &&
  1225. $afterlink == '' &&
  1226. ($sp[$j] == '' || ctype_space($sp[$j]))) {
  1227. // iterate through space separated URLs and add them to $r
  1228. $m = $ms[$j];
  1229. if ($m[0] != '@') { // skip @-references
  1230. $ac = substr($m, -1, 1);
  1231. while (contains('.!?,;"\')]}', $ac) && // trim punc @ end
  1232. ($ac != ')' || !contains($m, '('))) {
  1233. // allow one paren pair
  1234. // *** not sure twitter is this smart
  1235. $afterlink = strcat($ac, $afterlink);
  1236. $m = substr($m, 0, -1);
  1237. $ac = substr($m, -1, 1);
  1238. }
  1239. if (substr($m, 0, 6) === 'irc://') {
  1240. // skip it. no known use of in-reply-to an IRC URL
  1241. } else {
  1242. $r[count($r)] = webaddresstouri($m, true);
  1243. }
  1244. }
  1245. $j++;
  1246. }
  1247. }
  1248. }
  1249. return $r;
  1250. }
  1251. // Twitter POSSE support
  1252. // replace URLs with https://j.mp/0011235813 to mimic Twitter's t.co
  1253. function tw_text_proxy() {
  1254. /// ?> <!-- ///
  1255. var $args, $afterchar, $afterlink, $i, $isjs,
  1256. $mlen, $ms, $re,
  1257. $sp, $spe, $spliti, $prot, $proxy_url;
  1258. /// --> <?php ///
  1259. $isjs = js();
  1260. $args = $isjs ? arguments : func_get_args();
  1261. if (count($args) === 0) {
  1262. return '';
  1263. }
  1264. $t = $args[0];
  1265. $re = auto_link_re();
  1266. $ms = preg_matches($re, $t);
  1267. if (!$ms) {
  1268. return $t;
  1269. }
  1270. $mlen = count($ms);
  1271. $sp = preg_split($re, $t);
  1272. $t = "";
  1273. $sp[0] = string($sp[0]); // force undefined to ""
  1274. for ($i=0; $i<$mlen; $i++) {
  1275. $mi = $ms[$i];
  1276. $spliti = $sp[$i];
  1277. $t = strcat($t, $spliti);
  1278. $sp[$i+1] = string($sp[$i+1]); // force undefined to ""
  1279. if (substr($sp[$i+1], 0, 1)=='/') { // regex omits '/' before </a
  1280. $sp[$i+1] = substr($sp[$i+1], 1, strlen($sp[$i+1])-1);
  1281. $mi = strcat($mi, '/'); // explicitly include in match
  1282. }
  1283. $spe = substr($spliti, -2, 2);
  1284. // don't proxy @-names, plain ccTLDs
  1285. if ($mi[0]!='@' &&
  1286. (substr($mi, -3, 1) !== '.' || substr_count($mi, '.') > 1)) {
  1287. $afterlink = '';
  1288. $afterchar = substr($mi, -1, 1);
  1289. while (contains('.!?,;"\')]}', $afterchar) && // trim punc @ end
  1290. ($afterchar !== ')' || !contains($mi, '('))) {
  1291. // allow one paren pair
  1292. // *** not sure twitter is this smart
  1293. $afterlink = strcat($afterchar, $afterlink);
  1294. $mi = substr($mi, 0, -1);
  1295. $afterchar = substr($mi, -1, 1);
  1296. }
  1297. $prot = protocol_of_uri($mi);
  1298. $proxy_url = '';
  1299. if ($prot === 'irc:') {
  1300. $proxy_url = $mi; // Twitter doesn't tco irc: URLs
  1301. } else { /* 'https:', 'http:' or presumed for schemeless URLs */
  1302. $proxy_url = 'https://j.mp/0011235813';
  1303. }
  1304. $t = strcat($t, $proxy_url, $afterlink);
  1305. }
  1306. else {
  1307. $t = strcat($t, $mi);
  1308. }
  1309. }
  1310. return strcat($t, $sp[$mlen]);
  1311. }
  1312. // note_length_check:
  1313. // checks if $note fits in $maxlen characters.
  1314. // if $username is non-empty, checks if RT'd $note fits in $maxlen
  1315. // 0 - bad params or other precondition failure error
  1316. // 200 - exactly fits max characters with RT if username provided
  1317. // 206 - less than max chars with RT if username provided
  1318. // 207 - more than RT safe length, but less than tweet max
  1319. // 208 - tweet max length but with RT would be over
  1320. // 413 - (entity too large) over max tweet length
  1321. // strlen('RT @: ') === 6.
  1322. function note_length_check($note, $maxlen, $username) {
  1323. /// ?> <!-- ///
  1324. var $note_size_chk_u, $note_size_chk_n;
  1325. /// --> <?php ///
  1326. if ($maxlen < 1) { return 0; }
  1327. $note_size_chk_u = $username ? 6 + strlen(string($username)) : 0;
  1328. $note_size_chk_n = strlen(string($note)) + $note_size_chk_u;
  1329. if ($note_size_chk_n === $maxlen) { return 200; }
  1330. if ($note_size_chk_n < $maxlen) { return 206; }
  1331. if ($note_size_chk_n - $note_size_chk_u < $maxlen) { return 207; }
  1332. if ($note_size_chk_n - $note_size_chk_u === $maxlen) { return 208; }
  1333. return 413;
  1334. }
  1335. function tw_length_check($t, $maxlen, $username) {
  1336. return note_length_check(tw_text_proxy($t),
  1337. $maxlen, $username);
  1338. }
  1339. function tw_url_to_status_id($u) {
  1340. // $u - tweet permalink url
  1341. // returns tweet status id string; 0 if not a tweet permalink.
  1342. if (!$u) return 0;
  1343. $u = explode("/", string($u)); // https:,,twitter.com,t,status,nnn
  1344. if ($u[2] != "twitter.com" ||
  1345. $u[4] != "status" ||
  1346. !ctype_digit($u[5])) {
  1347. return 0;
  1348. }
  1349. return $u[5];
  1350. }
  1351. function tw_url_to_username($u) {
  1352. // $u - tweet permalink url
  1353. // returns twitter username; 0 if not a tweet permalink.
  1354. if (!$u) return 0;
  1355. $u = explode("/", string($u)); // https:,,twitter.com,t,status,nnn
  1356. if ($u[2] != "twitter.com" ||
  1357. $u[4] != "status" ||
  1358. !ctype_digit($u[5])) {
  1359. return 0;
  1360. }
  1361. return $u[3];
  1362. }
  1363. function fb_url_to_event_id($u) {
  1364. // $u - fb event permalink url
  1365. // returns fb event id string; 0 if not a fb event permalink.
  1366. if (!$u) return 0;
  1367. $u = explode("/", string($u)); // https:,,fb.com,events,nnn
  1368. if (($u[2] != "fb.com" && $u[2] != "facebook.com" &&
  1369. $u[2] != "www.facebook.com") ||
  1370. $u[3] != "events" ||
  1371. !ctype_digit($u[4])) {
  1372. return 0;
  1373. }
  1374. return $u[4];
  1375. }
  1376. // ===================================================================
  1377. // end CASSIS v0.1, cassis.js
  1378. // ?> -->