Advertisement
Guest User

Untitled

a guest
Dec 3rd, 2019
119
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 25.98 KB | None | 0 0
  1. // ==UserScript==
  2. // @name Chaturbate Easy Tipping Fixed Version
  3. // @namespace madTipper
  4. // @version 0.11
  5. // @author omgmikey - fixed on 1/21/2018 by tblopgreg
  6. // @match https://*.chaturbate.com/*
  7. // @grant GM_getValue
  8. // @grant GM_setValue
  9. // @license MIT
  10. // @run-at document-idle
  11. // @description Adds a new tipping popup and modifies the existing one
  12. // ==/UserScript==
  13.  
  14. var CB2_TipButtonText = "TRINKGELD SENDEN";
  15. var CB2_TipPopupHeaderText = "Trinkgeld senden"
  16. var CSS_GREY = {'color': 'rgb(88,141,61)'};
  17. var CSS_WHITE = {'color': '#FFFFFF'};
  18. var CSS_BLACK = {'color': '#000000'};
  19.  
  20. var ID_PREFIX = 'madTipper'
  21. var CLASS_PREFIX = 'madTipper'
  22. var CLASS_INPUT = CLASS_PREFIX + '_input';
  23. var BROADCASTER = "";
  24. var TippingUrl;
  25. var isLegacy;
  26.  
  27. var HTML_IDS = {
  28. 'BUTTON': 'button',
  29. 'POPUP': 'popup',
  30. 'AMOUNT': 'amount',
  31. 'COUNT': 'count',
  32. 'INTERVAL': 'interval',
  33. 'VARIANCE_LOWER': 'variance_lower',
  34. 'VARIANCE_UPPER': 'variance_upper',
  35.  
  36. 'START': 'start',
  37. 'STOP': 'stop',
  38. 'TOTAL': 'total',
  39. 'ETA': 'eta'
  40. };
  41.  
  42. for (var key in HTML_IDS) {
  43. HTML_IDS[key] = ID_PREFIX + '_' + HTML_IDS[key];
  44. }
  45.  
  46. function dragElement(elmnt, grabElement) {
  47. var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
  48. if (grabElement) {
  49. // if present, the header is where you move the DIV from:
  50. grabElement.onmousedown = dragMouseDown;
  51. } else {
  52. // otherwise, move the DIV from anywhere inside the DIV:
  53. elmnt.onmousedown = dragMouseDown;
  54. }
  55.  
  56. function dragMouseDown(e) {
  57. e = e || window.event;
  58. e.preventDefault();
  59. // get the mouse cursor position at startup:
  60. pos3 = e.clientX;
  61. pos4 = e.clientY;
  62. document.onmouseup = closeDragElement;
  63. // call a function whenever the cursor moves:
  64. document.onmousemove = elementDrag;
  65. }
  66.  
  67. function elementDrag(e) {
  68. e = e || window.event;
  69. e.preventDefault();
  70. // calculate the new cursor position:
  71. pos1 = pos3 - e.clientX;
  72. pos2 = pos4 - e.clientY;
  73. pos3 = e.clientX;
  74. pos4 = e.clientY;
  75. // set the element's new position:
  76. elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
  77. elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
  78. }
  79.  
  80. function closeDragElement() {
  81. // stop moving when mouse button is released:
  82. document.onmouseup = null;
  83. document.onmousemove = null;
  84. }
  85. }
  86.  
  87.  
  88. function getCookie(cname) {
  89. var name = cname + "=";
  90. var decodedCookie = decodeURIComponent(document.cookie);
  91. var ca = decodedCookie.split(';');
  92. for(var i = 0; i <ca.length; i++) {
  93. var c = ca[i];
  94. while (c.charAt(0) == ' ') {
  95. c = c.substring(1);
  96. }
  97. if (c.indexOf(name) == 0) {
  98. return c.substring(name.length, c.length);
  99. }
  100. }
  101. return "";
  102. }
  103.  
  104.  
  105. function setCookie(cname, cvalue, exdays) {
  106. var d = new Date();
  107. d.setTime(d.getTime() + (exdays*24*60*60*1000));
  108. var expires = "expires="+ d.toUTCString();
  109. document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
  110. }
  111.  
  112. function applyCSSobjectToElement(element, cssObject) {
  113. for (let key in cssObject) {
  114. element.style[key] = cssObject[key];
  115. }
  116. }
  117.  
  118.  
  119. var tipPopup = "";
  120. var tipButton = "";
  121. var buttonContainer = "nix";
  122. var tipsLeft = 0;
  123. var tipFunctionTimeout = null;
  124. var juration = loadJuration();
  125.  
  126. setTimeout(function initialize() {
  127.  
  128. isLegacy = getCookie("cb_legacy") === "1"; // new Beta website of CB, with interactive fullscreen and that jazz
  129. if (isLegacy) {
  130. tipButton = document.querySelector("a.tip_button").parentElement;
  131. buttonContainer = document.querySelector("div.tip_shell");
  132. TippingUrl = document.querySelector('.tip_popup form').action
  133. tipPopup = document.querySelector('.overlay_popup.tip_popup');
  134.  
  135.  
  136. tipPopupHeader = document.querySelector("div.tip_popup div.title")
  137. }
  138. else {
  139. tipButton = document.querySelector(`[title="${CB2_TipButtonText}"]`);
  140. buttonContainer = tipButton.parentElement;
  141. buttonContainer.parentElement.style.width = "430px";
  142. var broadcaster = JSON.parse(initialRoomDossier).broadcaster_username;
  143. TippingUrl = "/tipping/send_tip/" + broadcaster + "/";
  144.  
  145.  
  146. var divTags = document.getElementsByTagName("div");
  147. var tipPopupHeader;
  148.  
  149. for (var i = 0; i < divTags.length; i++) {
  150. if (divTags[i].textContent == CB2_TipPopupHeaderText) {
  151. tipPopupHeader = divTags[i];
  152. break;
  153. }
  154. }
  155.  
  156. if (tipPopupHeader) {
  157. tipPopup = tipPopupHeader.parentElement
  158. }
  159.  
  160. }
  161.  
  162.  
  163.  
  164. dragElement(tipPopup, tipPopupHeader);
  165.  
  166.  
  167.  
  168. createTipperButton();
  169. createTipperPopup();
  170. injectCSS();
  171.  
  172. loadPreviousSettings();
  173. initializeButtonCallbacks();
  174. updateTipperButton();
  175. }, 400);
  176.  
  177. function createTipperButton() {
  178. buttonContainer.insertAdjacentHTML("beforeend", '<span id="madTipper_button" >MADTIPPER</span>');
  179. }
  180.  
  181. function updateTipperButton() {
  182.  
  183. if (tipsLeft == 0) {
  184. document.getElementById(HTML_IDS['BUTTON']).innerHTML = 'MAD TIPPER';
  185. document.getElementById(HTML_IDS['BUTTON']).style.width = "80px";
  186. }
  187. else {
  188. document.getElementById(HTML_IDS['BUTTON']).innerHTML = 'MAD TIPPER (' + tipsLeft + ')' ;
  189. document.getElementById(HTML_IDS['BUTTON']).style.width = "120px";
  190. }
  191. }
  192.  
  193. function createTipperPopup() {
  194.  
  195. buttonContainer.insertAdjacentHTML("beforeend",
  196. '<div class="overlay_popup" id="madTipper_popup">' +
  197. '<div id="madTipper_popup_header" style="color: rgb(11, 93, 129); background-color: rgb(224, 224, 224); font-size: 15px; font-family: UbuntuBold, Helvetica, Arial, sans-serif; padding: 6px;">Mad Tipper</div>'+
  198. '<table width="100%" border="0" cellspacing="0" cellpadding="0">' +
  199. '<tbody>' +
  200. '<tr>' +
  201. '<div class="body">' +
  202. '<form>' +
  203. '<label>Amount per tip:</label><br >' +
  204. '<input type="text" id="madTipper_amount" class="madTipper_input">' +
  205. '<br />' +
  206.  
  207. '<label>Number of tips:</label><br >' +
  208. '<input type="text" id="madTipper_count" class="madTipper_input">' +
  209. '<br /><hr />' +
  210.  
  211. '<label>Interval:</label><br >' +
  212. '<input type="text" id="madTipper_interval" class="madTipper_input">' +
  213. '<br />' +
  214.  
  215. '<label>Interval variance lower (optional):</label><br >' +
  216. '<input type="text" id="madTipper_variance_lower" class="madTipper_input">' +
  217. '<br />' +
  218.  
  219. '<label>Interval variance upper (optional):</label><br >' +
  220. '<input type="text" id="madTipper_variance_upper" class="madTipper_input">' +
  221. '<br /><hr />' +
  222.  
  223. 'Total tip: ' + '<a id="madTipper_total"></a>' +
  224. '<br />' +
  225. 'Estimated duration: ' + '<a id="madTipper_eta"></a>' +
  226. '</form>' +
  227. '<hr />' +
  228. '<button id="madTipper_start">Start</button>' +
  229. '<button id="madTipper_stop" disabled="disabled">Stop</button>' +
  230. '</div>' +
  231. '</td>' +
  232. '</tr>' +
  233. '</tbody>' +
  234. '</table>' +
  235. '</div>'
  236. );
  237. }
  238.  
  239. function injectCSS() {
  240.  
  241. var buttonBackgroundUrl =
  242. 'url("https://ssl-ccstatic.highwebmedia.com/images/btn-sprites2.gif?ac5eba7d5cf3") no-repeat right';
  243.  
  244.  
  245. var buttonFontFamily =
  246. 'UbuntuMedium,Arial,Helvetica,sans-serif';
  247.  
  248. var genericButtonCSS = {
  249. 'height':'21px',
  250. 'width':'100px',
  251. 'padding-left':'10px',
  252. 'marginRight':'10px',
  253. 'fontSize':'12px',
  254. 'textShadow':'1px 1px 0 #588d3d',
  255. 'color': '#FFFFFF'
  256. };
  257. genericButtonCSS['font-family'] = buttonFontFamily;
  258. genericButtonCSS['background'] = buttonBackgroundUrl + ' -84px';
  259.  
  260. var mainButtonCSS = {
  261. position: "absolute",
  262. marginRight: "10px",
  263. fontSize: "12px",
  264. textShadow: "rgb(88, 141, 61) 1px 1px 0px",
  265. color: "rgb(255, 255, 255)",
  266. }
  267.  
  268. for (let key in genericButtonCSS) {
  269. if (mainButtonCSS[key] === undefined) {
  270. mainButtonCSS[key] = genericButtonCSS[key];
  271. }
  272. }
  273.  
  274. console.log("Hallo");
  275.  
  276.  
  277. if (isLegacy) {
  278. document.getElementById(HTML_IDS['BUTTON']).classList.add(tipButton.classList[0])
  279. document.getElementById(HTML_IDS['BUTTON']).style.left = parseInt(getComputedStyle(tipButton).left, 10) + parseInt(getComputedStyle(tipButton).width, 10) + "px";
  280. document.getElementById(HTML_IDS['BUTTON']).style.lineHeight = 1.7;
  281. document.getElementById(HTML_IDS['BUTTON']).style.color = "white";
  282. }
  283. else {
  284. document.getElementById(HTML_IDS['BUTTON']).style.cssText = tipButton.style.cssText
  285. }
  286.  
  287. console.log("blablabla");
  288.  
  289.  
  290. for (const element of document.getElementsByClassName(CLASS_INPUT)) {
  291. element.style.width = "auto";
  292. element.style.marginBottom = "10px" ;
  293. };
  294.  
  295.  
  296. document.getElementById(HTML_IDS['POPUP']).style.position = "absolute" ;
  297. document.getElementById(HTML_IDS['POPUP']).style.zIndex = "auto" ;
  298. document.getElementById(HTML_IDS['POPUP']).style.width = "280px" ;
  299. document.getElementById(HTML_IDS['POPUP']).style.height = "367px";
  300. document.getElementById(HTML_IDS['POPUP']).style.backgroundColor = "rgb(255, 255, 255)";
  301. document.getElementById(HTML_IDS['POPUP']).style.border = "2px solid rgb(11, 93, 129)";
  302. document.getElementById(HTML_IDS['POPUP']).style.borderRadius = "4px";
  303. document.getElementById(HTML_IDS['POPUP']).style.color = "rgb(73, 73, 73)";
  304. document.getElementById(HTML_IDS['POPUP']).style.top = "-340" ;
  305. document.getElementById(HTML_IDS['POPUP']).style.left = "170" ;
  306. document.getElementById(HTML_IDS['POPUP']).style.display = "none" ;
  307. document.getElementById(HTML_IDS['POPUP']).style.fontFamily = "UbuntuRegular, Helvetica, Arial, sans-serif";
  308.  
  309.  
  310.  
  311.  
  312. dragElement(document.getElementById(HTML_IDS['POPUP']), document.getElementById("madTipper_popup_header"))
  313.  
  314.  
  315. applyCSSobjectToElement(document.getElementById(HTML_IDS['START']), genericButtonCSS)
  316.  
  317. genericButtonCSS['background'] = buttonBackgroundUrl + ' -42px';
  318.  
  319. delete genericButtonCSS['color'];
  320.  
  321. applyCSSobjectToElement(document.getElementById(HTML_IDS['STOP']), genericButtonCSS)
  322.  
  323. }
  324.  
  325.  
  326.  
  327. function startTipping() {
  328.  
  329. var err = verifyTipperFields();
  330.  
  331. if (err) {
  332. alert(err);
  333. stopTipping();
  334. return;
  335. }
  336.  
  337. saveCurrentSettings();
  338.  
  339. document.getElementById(HTML_IDS['START']).disabled = true;
  340. applyCSSobjectToElement(document.getElementById(HTML_IDS['START']), CSS_GREY) ;
  341.  
  342. document.getElementById(HTML_IDS['STOP']).disabled = false
  343. applyCSSobjectToElement(document.getElementById(HTML_IDS['STOP']), CSS_WHITE) ;
  344.  
  345.  
  346. document.getElementById(HTML_IDS['STOP']);
  347.  
  348.  
  349. for (const element of document.getElementsByClassName(CLASS_INPUT)) {
  350. element.disabled = true;
  351. applyCSSobjectToElement(element, CSS_GREY);
  352. };
  353.  
  354.  
  355. tipsLeft = getTipCount();
  356.  
  357. /* we really want to send the first one immediately */
  358. sendTip();
  359.  
  360. if (tipsLeft > 0) {
  361. chainQueueTips();
  362. }
  363. }
  364.  
  365. function verifyTipperFields() {
  366.  
  367. function isInt(value) {
  368. var regex = /^[0-9]+$/;
  369. return regex.test(String(value));
  370. }
  371.  
  372. function isDuration(value) {
  373. try {
  374. juration.parse(value);
  375. return true;
  376. }
  377. catch(ex) {
  378. return false;
  379. }
  380. }
  381.  
  382. function isDurationOrEmpty(value) {
  383. return value === '' || isDuration(value);
  384. }
  385.  
  386. if (!isInt(getTipAmountRaw()) || getTipAmount() <= 0) {
  387. return 'Tip amount field should be a positive integer.';
  388. }
  389.  
  390. if (!isInt(getTipCountRaw()) || getTipCount() <= 0) {
  391. return 'Tip count field should be a positive integer.';
  392. }
  393.  
  394. if (!isDuration(getTipInterval())) {
  395. return 'Tip interval should contain a duration. E.g.: "2.5s", "1", "2min"';
  396. }
  397.  
  398. if (!isDurationOrEmpty(getVarianceLowerRaw()) || !isDurationOrEmpty(getVarianceUpperRaw())) {
  399. return 'Variance fields should contain durations, or be left blank. E.g.: "", "2.5s"';
  400. }
  401. }
  402.  
  403. function getSleepInterval() {
  404.  
  405. var interval = juration.parse(getTipInterval());
  406. var lower_bound = interval - getVarianceLower();
  407. var upper_bound = interval + getVarianceUpper();
  408.  
  409. return getRandomNumber(lower_bound, upper_bound) * 1000;
  410. }
  411.  
  412. function getRandomNumber(min, max) {
  413.  
  414. return Math.random() * (max - min) + min;
  415. }
  416.  
  417. function chainQueueTips() {
  418.  
  419. var sleepTime = getSleepInterval();
  420.  
  421. tipFunctionTimeout = setTimeout(function() {
  422. sendTip(chainQueueTips);
  423. }, sleepTime);
  424. }
  425.  
  426. function sendTip(queueNextTipFn) {
  427. var queryParams = {
  428. 'csrfmiddlewaretoken': getCookie('csrftoken'),
  429. 'tip_amount': getTipAmount(),
  430. 'source': 'theater',
  431. 'message': '',
  432. 'tip_room_type': 'public' // TODO: normally not static, used before was: document.getElementById('id_tip_room_type').value
  433. };
  434.  
  435.  
  436.  
  437.  
  438. fetch(TippingUrl,
  439. {
  440. method: 'POST',
  441. headers: {
  442. 'Content-Type': 'application/x-www-form-urlencoded',
  443. 'X-Requested-With': 'XMLHttpRequest'
  444. },
  445. body: Object.keys(queryParams).map(function(k) {
  446. return encodeURIComponent(k) + '=' + encodeURIComponent(queryParams[k])
  447. }).join('&')
  448. })
  449. .then(function (response) {
  450. if (response.error) {
  451. alert(response.error);
  452. stopTipping();
  453. }
  454. })
  455. .catch(function (error) {
  456.  
  457. alert(response.error);
  458. stopTipping();
  459.  
  460. });
  461.  
  462. updateTipsLeft();
  463.  
  464. if (tipsLeft === 0) {
  465. stopTipping();
  466. }
  467. else if (queueNextTipFn) {
  468. queueNextTipFn();
  469. }
  470. }
  471.  
  472. function updateTipsLeft() {
  473.  
  474. tipsLeft--;
  475. updateTipperButton();
  476. }
  477.  
  478. function stopTipping() {
  479.  
  480. clearTimeout(tipFunctionTimeout);
  481. tipFunctionTimeout = null;
  482.  
  483. tipsLeft = 0;
  484. updateTipperButton();
  485.  
  486. document.getElementById(HTML_IDS['STOP']).disabled = true;
  487. applyCSSobjectToElement(document.getElementById(HTML_IDS['STOP']), CSS_GREY);
  488.  
  489. document.getElementById(HTML_IDS['START']).disabled = false;
  490. applyCSSobjectToElement(document.getElementById(HTML_IDS['STOP']), CSS_WHITE);
  491.  
  492.  
  493. for (const element of document.getElementsByClassName(CLASS_INPUT)) {
  494. element.disabled = false;;
  495. applyCSSobjectToElement(element, CSS_BLACK);
  496. };
  497.  
  498.  
  499. }
  500.  
  501. function initializeButtonCallbacks() {
  502.  
  503. var popup = document.getElementById(HTML_IDS['POPUP'])
  504. var button = document.getElementById(HTML_IDS['BUTTON'])
  505.  
  506. button.onclick = function(ev) {
  507. if (popup.style.display !=="none") {
  508. popup.style.display = "none";
  509. }
  510. else {
  511. popup.style.display = "block";
  512. }
  513. };
  514.  
  515. popup.onclick = function(ev) {
  516. ev.stopPropagation();
  517. };
  518.  
  519. document.getElementById(HTML_IDS['START']).onclick =function() {
  520. startTipping();
  521. document.getElementById(HTML_IDS['POPUP']).style.display = "none";
  522. };
  523.  
  524. document.getElementById(HTML_IDS['STOP']).onclick = function() {
  525. stopTipping();
  526. };
  527.  
  528. document.body.addEventListener("click", function(ev) {
  529. if (ev.target.id != button.id) {
  530. document.getElementById(HTML_IDS['POPUP']).style.display = "none";
  531. }
  532. });
  533.  
  534. for (const element of document.getElementsByClassName(CLASS_INPUT)) {
  535. element.onChange = ()=> {
  536. calculateAndSetTotalTip();
  537. calculateAndSetETA();
  538. }
  539.  
  540. };
  541. }
  542.  
  543. function calculateAndSetTotalTip() {
  544.  
  545. var value = document.getElementById(HTML_IDS['AMOUNT']).value * document.getElementById(HTML_IDS['COUNT']).value;
  546. document.getElementById(HTML_IDS['TOTAL']).innerHTML = value + ' tokens';
  547. }
  548.  
  549. function calculateAndSetETA() {
  550.  
  551. var interval = juration.parse(document.getElementById(HTML_IDS['INTERVAL']).value);
  552.  
  553. /* we're not counting the first tip */
  554. var count = getTipCount() - 1;
  555.  
  556. var variance_lower = getVarianceLower();
  557. var variance_upper = getVarianceUpper();
  558.  
  559. var eta = (interval + variance_upper - variance_lower) * count;
  560. document.getElementById(HTML_IDS['ETA']).innerHTML = juration.stringify(eta, {'format': 'long', 'units': 2});
  561. }
  562.  
  563. function getTipAmount() {
  564.  
  565. return parseInt(getTipAmountRaw());
  566. }
  567.  
  568. function getTipAmountRaw() {
  569.  
  570. return document.getElementById(HTML_IDS['AMOUNT']).value;
  571. }
  572.  
  573. function getTipInterval() {
  574.  
  575. return document.getElementById(HTML_IDS['INTERVAL']).value;
  576. }
  577.  
  578. function getTipCount() {
  579.  
  580. return parseInt(getTipCountRaw());
  581. }
  582.  
  583. function getTipCountRaw() {
  584.  
  585. return document.getElementById(HTML_IDS['COUNT']).value;
  586. }
  587.  
  588. function getVarianceLower() {
  589.  
  590. return parseVariance(getVarianceLowerRaw());
  591. }
  592.  
  593. function getVarianceLowerRaw() {
  594.  
  595. return document.getElementById(HTML_IDS['VARIANCE_LOWER']).value;
  596. }
  597.  
  598.  
  599. function getVarianceUpper() {
  600.  
  601. return parseVariance(getVarianceUpperRaw());
  602. }
  603.  
  604. function getVarianceUpperRaw() {
  605.  
  606. return document.getElementById(HTML_IDS['VARIANCE_UPPER']).value;
  607. }
  608.  
  609. function parseVariance(variance) {
  610.  
  611. if (variance == '0') {
  612. variance = 0;
  613. }
  614.  
  615. variance = variance || 0;
  616.  
  617. if (variance != 0) {
  618. variance = juration.parse(variance);
  619. }
  620.  
  621. return variance;
  622. }
  623.  
  624. function saveCurrentSettings() {
  625.  
  626. GM_setValue('amount', getTipAmount());
  627. GM_setValue('interval', getTipInterval());
  628. GM_setValue('count', getTipCount());
  629. GM_setValue('variance_lower', getVarianceLower());
  630. GM_setValue('variance_upper', getVarianceUpper());
  631. }
  632.  
  633. function loadPreviousSettings() {
  634.  
  635. var amount = GM_getValue('amount', 1);
  636. document.getElementById(HTML_IDS['AMOUNT']).value = amount;
  637.  
  638. var count = GM_getValue('count', 10);
  639. document.getElementById(HTML_IDS['COUNT']).value = count;
  640.  
  641. var interval = GM_getValue('interval', '1s');
  642. document.getElementById(HTML_IDS['INTERVAL']).value = interval;
  643.  
  644. var variance_lower = GM_getValue('variance_lower', '');
  645. document.getElementById(HTML_IDS['VARIANCE_LOWER']).value = variance_lower;
  646.  
  647. var variance_upper = GM_getValue('variance_upper', '');
  648. document.getElementById(HTML_IDS['VARIANCE_UPPER']).value = variance_upper;
  649.  
  650. calculateAndSetTotalTip();
  651. calculateAndSetETA();
  652. }
  653.  
  654.  
  655. // Script ends here
  656. // Libs included because they're not on a popular cdn
  657.  
  658.  
  659. /*
  660. * juration - a natural language duration parser
  661. * https://github.com/domchristie/juration
  662. *
  663. * Copyright 2011, Dom Christie
  664. * Licenced under the MIT licence
  665. *
  666. */
  667.  
  668. function loadJuration() {
  669.  
  670. var UNITS = {
  671. seconds: {
  672. patterns: ['second', 'sec', 's'],
  673. value: 1,
  674. formats: {
  675. 'chrono': '',
  676. 'micro': 's',
  677. 'short': 'sec',
  678. 'long': 'second'
  679. }
  680. },
  681. minutes: {
  682. patterns: ['minute', 'min', 'm(?!s)'],
  683. value: 60,
  684. formats: {
  685. 'chrono': ':',
  686. 'micro': 'm',
  687. 'short': 'min',
  688. 'long': 'minute'
  689. }
  690. },
  691. hours: {
  692. patterns: ['hour', 'hr', 'h'],
  693. value: 3600,
  694. formats: {
  695. 'chrono': ':',
  696. 'micro': 'h',
  697. 'short': 'hr',
  698. 'long': 'hour'
  699. }
  700. },
  701. days: {
  702. patterns: ['day', 'dy', 'd'],
  703. value: 86400,
  704. formats: {
  705. 'chrono': ':',
  706. 'micro': 'd',
  707. 'short': 'day',
  708. 'long': 'day'
  709. }
  710. },
  711. weeks: {
  712. patterns: ['week', 'wk', 'w'],
  713. value: 604800,
  714. formats: {
  715. 'chrono': ':',
  716. 'micro': 'w',
  717. 'short': 'wk',
  718. 'long': 'week'
  719. }
  720. },
  721. months: {
  722. patterns: ['month', 'mon', 'mo', 'mth'],
  723. value: 2628000,
  724. formats: {
  725. 'chrono': ':',
  726. 'micro': 'm',
  727. 'short': 'mth',
  728. 'long': 'month'
  729. }
  730. },
  731. years: {
  732. patterns: ['year', 'yr', 'y'],
  733. value: 31536000,
  734. formats: {
  735. 'chrono': ':',
  736. 'micro': 'y',
  737. 'short': 'yr',
  738. 'long': 'year'
  739. }
  740. }
  741. };
  742.  
  743. var stringify = function(seconds, options) {
  744.  
  745. if(!_isNumeric(seconds)) {
  746. throw "juration.stringify(): Unable to stringify a non-numeric value";
  747. }
  748.  
  749. if((typeof options === 'object' && options.format !== undefined) && (options.format !== 'micro' && options.format !== 'short' && options.format !== 'long' && options.format !== 'chrono')) {
  750. throw "juration.stringify(): format cannot be '" + options.format + "', and must be either 'micro', 'short', or 'long'";
  751. }
  752.  
  753. var defaults = {
  754. format: 'short',
  755. units: undefined
  756. };
  757.  
  758. var opts = _extend(defaults, options);
  759.  
  760. var units = ['years', 'months', 'days', 'hours', 'minutes', 'seconds'], values = [];
  761. var remaining = seconds;
  762. var activeUnits = 0;
  763. for(var i = 0, len = units.length;
  764. i < len && (opts.units == undefined || activeUnits < opts.units);
  765. i++) {
  766. var unit = UNITS[units[i]];
  767. values[i] = Math.floor(remaining / unit.value);
  768. if (values[i] > 0 || activeUnits > 0)
  769. activeUnits++;
  770.  
  771. if(opts.format === 'micro' || opts.format === 'chrono') {
  772. values[i] += unit.formats[opts.format];
  773. }
  774. else {
  775. values[i] += ' ' + _pluralize(values[i], unit.formats[opts.format]);
  776. }
  777. remaining = remaining % unit.value;
  778. }
  779. var output = '';
  780. for(i = 0, len = values.length; i < len; i++) {
  781. if(values[i].charAt(0) !== "0" && opts.format != 'chrono') {
  782. output += values[i] + ' ';
  783. }
  784. else if (opts.format == 'chrono') {
  785. output += _padLeft(values[i]+'', '0', i==values.length-1 ? 2 : 3);
  786. }
  787. }
  788. return output.replace(/\s+$/, '').replace(/^(00:)+/g, '').replace(/^0/, '');
  789. };
  790.  
  791. var parse = function(string) {
  792.  
  793. // returns calculated values separated by spaces
  794. for(var unit in UNITS) {
  795. for(var i = 0, mLen = UNITS[unit].patterns.length; i < mLen; i++) {
  796. var regex = new RegExp("((?:\\d+\\.\\d+)|\\d+)\\s?(" + UNITS[unit].patterns[i] + "s?(?=\\s|\\d|\\b))", 'gi');
  797. string = string.replace(regex, function(str, p1, p2) {
  798. return " " + (p1 * UNITS[unit].value).toString() + " ";
  799. });
  800. }
  801. }
  802.  
  803. var sum = 0,
  804. numbers = string
  805. .replace(/(?!\.)\W+/g, ' ') // replaces non-word chars (excluding '.') with whitespace
  806. .replace(/^\s+|\s+$|(?:and|plus|with)\s?/g, '') // trim L/R whitespace, replace known join words with ''
  807. .split(' ');
  808.  
  809. for(var j = 0, nLen = numbers.length; j < nLen; j++) {
  810. if(numbers[j] && isFinite(numbers[j])) {
  811. sum += parseFloat(numbers[j]);
  812. } else if(!numbers[j]) {
  813. throw "juration.parse(): Unable to parse: a falsey value";
  814. } else {
  815. // throw an exception if it's not a valid word/unit
  816. throw "juration.parse(): Unable to parse: " + numbers[j].replace(/^\d+/g, '');
  817. }
  818. }
  819. return sum;
  820. };
  821.  
  822. // _padLeft('5', '0', 2); // 05
  823. var _padLeft = function(s, c, n) {
  824. if (! s || ! c || s.length >= n) {
  825. return s;
  826. }
  827.  
  828. var max = (n - s.length)/c.length;
  829. for (var i = 0; i < max; i++) {
  830. s = c + s;
  831. }
  832.  
  833. return s;
  834. };
  835.  
  836. var _pluralize = function(count, singular) {
  837. return count == 1 ? singular : singular + "s";
  838. };
  839.  
  840. var _isNumeric = function(n) {
  841. return !isNaN(parseFloat(n)) && isFinite(n);
  842. };
  843.  
  844. var _extend = function(obj, extObj) {
  845. for (var i in extObj) {
  846. if(extObj[i] !== undefined) {
  847. obj[i] = extObj[i];
  848. }
  849. }
  850. return obj;
  851. };
  852.  
  853. var juration = {
  854. parse: parse,
  855. stringify: stringify,
  856. humanize: stringify
  857. };
  858.  
  859. return juration;
  860. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement