jquery.epiclock.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137
  1. /*!
  2. * epiClock 3.0
  3. *
  4. * Copyright (c) 2008 Eric Garside
  5. * Dual licensed under:
  6. * MIT: http://www.opensource.org/licenses/mit-license.php
  7. * GPLv3: http://www.opensource.org/licenses/gpl-3.0.html
  8. */
  9. "use strict";
  10. /*global Class, window, jQuery */
  11. /*jslint white: true, browser: true, onevar: true, undef: true, eqeqeq: true, bitwise: true, regexp: true, strict: true, newcap: true, immed: true, maxerr: 50, indent: 4 */
  12. /**
  13. * This plugin requires the jquery.dateformat plugin, which is included alongside this
  14. * script, and is packed into the minified version.
  15. */
  16. (function ($) {
  17. //------------------------------
  18. //
  19. // Constants
  20. //
  21. //------------------------------
  22. /**
  23. * Clock modes for epiclock. As of epiclock 3.0, users can add: "epiclock epiclock-<mode>" classes
  24. * to an element to automate the setting of a clock on the page.
  25. */
  26. var mode =
  27. {
  28. /**
  29. * Default clock type. Renders a clock which displays the current time, based
  30. * on the client's clock.
  31. */
  32. clock: 'clock',
  33. /**
  34. * Renders a clock which displays the current time, based on the datetime value
  35. * passed to the clock. Allows for some server to explicitly set the starting value
  36. * of the clock (to show the proper time, regardless of the value of the client's clock).
  37. */
  38. explicit: 'explicit',
  39. /**
  40. * Countdown clock, displaying the time remaining until a certain datetime. Expires
  41. * when the countdown finishes.
  42. */
  43. countdown: 'countdown',
  44. /**
  45. * Countup clock, displaying the time since a certain datetime. If the datetime has not
  46. * yet arrived, the clock will display negative numbers.
  47. */
  48. countup: 'countup',
  49. /**
  50. * Exactly like the "countdown" clock, except on expiry, the clock will become a
  51. * "countup" clock.
  52. */
  53. rollover: 'rollover',
  54. /**
  55. * A clock which displays the number of remaining seconds until the expiry.
  56. */
  57. expire: 'expire',
  58. /**
  59. * Similar to the "expire" clock, only it will refresh the duration when the expiry hits. Also, this clock
  60. * tares on pause.
  61. */
  62. loop: 'loop',
  63. /**
  64. * A counter which displays the number of seconds since the clock was started. Tares any
  65. * time when paused. (if the clock is at 10 and is paused for 3 seconds, when resumed, it will
  66. * be at 10 seconds).
  67. */
  68. stopwatch: 'stopwatch',
  69. /**
  70. * A countup clock, displaying the time since a certain datetime. If the datetime has not
  71. * yet arrived, the clock will display all zeroes.
  72. */
  73. holdup: 'holdup',
  74. /**
  75. * A countdown clock displaying the remaining time on a timer. Similar to the expire counter, except it
  76. * tares on pause, like the stopwatch clock.
  77. */
  78. timer: 'timer'
  79. },
  80. //------------------------------
  81. //
  82. // Property Declaration
  83. //
  84. //------------------------------
  85. /**
  86. * Create a placeholder object for the epiclock manager.
  87. *
  88. * @param selector The selector to grab an epiclock object for.
  89. *
  90. * @return The epiclock instance for the given selector, or null.
  91. */
  92. _ = function (selector)
  93. {
  94. return $(selector).data('epiclock');
  95. },
  96. /**
  97. * A collection of listeners for clock events.
  98. *
  99. * Signature:
  100. * {
  101. * <clockInstance>:
  102. * {
  103. * <eventType>: [listener, ...],
  104. *
  105. * ...,
  106. * },
  107. *
  108. * ...
  109. * }
  110. */
  111. events = {},
  112. /**
  113. * A collection of the clock instances created. By default, Clocks render each formatting
  114. * character and label in their own HTML element. These elements are indexed by their
  115. * formatting character and stored as elements of the "frame" object in this object.
  116. *
  117. * Non-formatting characters (such as labels) are still contained within their own HTML
  118. * elements, but are not tracked in the frame object, as they are not intended to change.
  119. *
  120. * Signature:
  121. * {
  122. * <clockInstance>:
  123. * {
  124. * clock: <clockObject>,
  125. *
  126. * frame:
  127. * {
  128. * <formattingCharacter>: <jQueryObject>,
  129. *
  130. * ...
  131. * }
  132. * },
  133. *
  134. * ...
  135. * }
  136. */
  137. instances = {},
  138. /**
  139. * A unique identifier for each created clock.
  140. */
  141. uid = 0,
  142. /**
  143. * Whenever a tick process begins, this variable will contain the current datetime value.
  144. */
  145. now,
  146. /**
  147. * A "zero" date for clocks.
  148. */
  149. zero = new Date(0),
  150. /**
  151. * The interval for tracking the clock timeout.
  152. */
  153. intervalID,
  154. /**
  155. * Create the Clock class, defined below.
  156. */
  157. Clock = function ()
  158. {
  159. this.__uid = uid++;
  160. instances[this.__uid] = {clock: this, frame: {}};
  161. };
  162. //------------------------------
  163. //
  164. // Internal Methods
  165. //
  166. //------------------------------
  167. /**
  168. * Trigger an event for a clock instance.
  169. *
  170. * @param uid The unique id of the clock instance.
  171. *
  172. * @param event The event being triggered.
  173. *
  174. * @param params The parameters for the event listener.
  175. */
  176. function triggerEvent(uid, event, params)
  177. {
  178. instances[uid].clock.container.triggerHandler(event, params);
  179. if (events[uid] === undefined || events[uid][event] === undefined)
  180. {
  181. return;
  182. }
  183. $.each(events[uid][event], function (index, value)
  184. {
  185. if ($.isFunction(value))
  186. {
  187. value.apply(instances[uid].clock, params || []);
  188. }
  189. });
  190. }
  191. /**
  192. * Terminate a counter clock.
  193. *
  194. * @param clock The clock to terminate.
  195. *
  196. * @param current The current time.
  197. *
  198. * @return The time to render.
  199. */
  200. function terminate(clock, current)
  201. {
  202. triggerEvent(clock.__uid, 'timer');
  203. switch (clock.mode)
  204. {
  205. case mode.holdup:
  206. case mode.rollover:
  207. clock.mode = mode.countup;
  208. clock.restart(0);
  209. return zero;
  210. case mode.expire:
  211. case mode.countdown:
  212. case mode.timer:
  213. clock.destroy();
  214. return zero;
  215. case mode.loop:
  216. clock.restart();
  217. return zero;
  218. }
  219. }
  220. /**
  221. * Given the settings for a clock, determine what the state would be if the clock ticked forward.
  222. *
  223. * @param clock The clock to tick foward using the current value of "now".
  224. *
  225. * @return False to indicate rendering should be skipped, or the date to render for the clock.
  226. */
  227. function tick(clock)
  228. {
  229. if (clock.__paused !== undefined && clock.mode)
  230. {
  231. return false;
  232. }
  233. var current = now + clock.__displacement,
  234. days;
  235. switch (clock.mode)
  236. {
  237. case mode.holdup:
  238. current -= clock.time;
  239. if (current > 0 || current > -1e3)
  240. {
  241. return terminate(clock, current);
  242. }
  243. return zero;
  244. case mode.countup:
  245. case mode.stopwatch:
  246. current -= clock.time;
  247. break;
  248. case mode.explicit:
  249. current += clock.time;
  250. break;
  251. case mode.rollover:
  252. case mode.loop:
  253. case mode.expire:
  254. case mode.countdown:
  255. case mode.timer:
  256. current = clock.time - current;
  257. if (current < 1e3)
  258. {
  259. return terminate(clock, current);
  260. }
  261. break;
  262. }
  263. if (clock.displayOffset !== undefined)
  264. {
  265. days = parseInt($.dateformat.get('V', current), 10);
  266. if (days > clock.__days_added)
  267. {
  268. clock.__days_added = days;
  269. clock.displayOffset.days += days;
  270. if (clock.displayOffset.days >= 365)
  271. {
  272. clock.displayOffset.years += Math.floor(clock.displayOffset.days / 365.4 % 365.4);
  273. clock.displayOffset.days = Math.floor(clock.displayOffset.days % 365.4);
  274. }
  275. }
  276. }
  277. return new Date(current);
  278. }
  279. /**
  280. * Compute the value for a formatting symbol for a given clock using the provided current time.
  281. *
  282. * @param clock The clock object.
  283. *
  284. * @param symbol The formatting symbol.
  285. *
  286. * @param current The current time.
  287. */
  288. function evaluate(clock, symbol, current)
  289. {
  290. switch (symbol)
  291. {
  292. case 'Q':
  293. return clock.displayOffset.years;
  294. case 'E':
  295. return clock.displayOffset.days;
  296. case 'e':
  297. return $.dateformat.pad(clock.displayOffset.days, 0);
  298. default:
  299. return $.dateformat.get(symbol, current);
  300. }
  301. }
  302. /**
  303. * The default operation for rendering an epiclock.
  304. *
  305. * @param frame The frame getting updated.
  306. *
  307. * @param value The current value for the frame.
  308. */
  309. function defaultRenderer(frame, value)
  310. {
  311. frame.text(value);
  312. }
  313. /**
  314. * Render a clock.
  315. *
  316. * @param clock The clock to render.
  317. */
  318. function render(clock)
  319. {
  320. var time = tick(clock);
  321. if (time === false)
  322. {
  323. return false;
  324. }
  325. $.each(instances[clock.__uid].frame, function (symbol, frame)
  326. {
  327. var value = evaluate(clock, symbol, time) + '';
  328. if (frame.data('epiclock-last') !== value)
  329. {
  330. (clock.__render || defaultRenderer)(frame, value);
  331. }
  332. frame.data('epiclock-last', value);
  333. });
  334. triggerEvent(clock.__uid, 'rendered');
  335. if (clock.container.hasClass('epiclock-wait-for-render'))
  336. {
  337. clock.container.removeClass('epiclock-wait-for-render');
  338. }
  339. }
  340. /**
  341. * Process all the clocks currently available.
  342. */
  343. function cycleClocks()
  344. {
  345. now = new Date().valueOf();
  346. $.each(instances, function ()
  347. {
  348. render(this.clock);
  349. if (this.clock.__destroy)
  350. {
  351. this.clock.container.removeData('epiclock');
  352. delete instances[this.clock.__uid];
  353. }
  354. });
  355. }
  356. /**
  357. * Start the epiclock manager.
  358. */
  359. function startManager()
  360. {
  361. if (intervalID !== undefined)
  362. {
  363. return;
  364. }
  365. $.each(instances, function ()
  366. {
  367. this.clock.resume();
  368. });
  369. intervalID = setInterval(cycleClocks, _.precision);
  370. }
  371. /**
  372. * Halt the epiclock manager.
  373. */
  374. function haltManager()
  375. {
  376. clearInterval(intervalID);
  377. intervalID = undefined;
  378. $.each(instances, function ()
  379. {
  380. this.clock.pause();
  381. });
  382. }
  383. /**
  384. * The the default formatting string for a clock.
  385. *
  386. * @param format The format of the clock.
  387. *
  388. * @return The default dateformat string to use.
  389. */
  390. function defaultFormat(format)
  391. {
  392. switch (format)
  393. {
  394. case mode.clock:
  395. case mode.explicit:
  396. return 'F j, Y g:i:s a';
  397. case mode.countdown:
  398. return 'V{d} x{h} i{m} s{s}';
  399. case mode.countup:
  400. return 'Q{y} K{d} x{h} i{m} s{s}';
  401. case mode.rollover:
  402. return 'V{d} x{h} i{m} s{s}';
  403. case mode.expire:
  404. case mode.timer:
  405. return 'x{h} i{m} s{s}';
  406. case mode.loop:
  407. return 'i{m} s{s}';
  408. case mode.stopwatch:
  409. return 'x{h} C{m} s{s}';
  410. case mode.holdup:
  411. return 'Q{y} K{d} x{h} i{m} s{s}';
  412. }
  413. }
  414. /**
  415. * Given a properties object, configure a clock instance.
  416. *
  417. * @param properties The configuration for the clock.
  418. *
  419. * @return Clock instance.
  420. */
  421. function configureInstance(properties)
  422. {
  423. var clock = new Clock(),
  424. modifier = 1;
  425. clock.mode = properties.mode || mode.clock;
  426. if (properties.offset !== undefined)
  427. {
  428. clock.__offset = (
  429. (properties.offset.years || 0) * 3157056e4 +
  430. (properties.offset.days || 0) * 864e5 +
  431. (properties.offset.hours || 0) * 36e5 +
  432. (properties.offset.minutes || 0) * 6e4 +
  433. (properties.offset.seconds || 0) * 1e3
  434. );
  435. }
  436. if (properties.startpaused)
  437. {
  438. clock.__paused = new Date().valueOf();
  439. }
  440. clock.__displacement = properties.utc === true || properties.gmt === true ? new Date().getTimezoneOffset() * 6e4 : 0;
  441. clock.__render = $.isFunction(properties.renderer) ? properties.renderer : _.renderers[properties.renderer];
  442. clock.__days_added = 0;
  443. clock.__tare = properties.tare || false;
  444. clock.format = properties.format || defaultFormat(clock.mode);
  445. clock.time = (properties.time || properties.target ? new Date(properties.time || properties.target) : new Date()).valueOf();
  446. clock.displayOffset = $.extend({years: 0, days: 0}, properties.displayOffset);
  447. switch (clock.mode)
  448. {
  449. case mode.clock:
  450. case mode.countup:
  451. break;
  452. case mode.explicit:
  453. clock.__displacement -= new Date().valueOf();
  454. break;
  455. case mode.timer:
  456. case mode.loop:
  457. modifier = -1;
  458. clock.__tare = true;
  459. clock.__displacement -= 1e3;
  460. break;
  461. case mode.expire:
  462. case mode.countdown:
  463. case mode.rollover:
  464. modifier = -1;
  465. clock.__displacement -= 1e3;
  466. break;
  467. case mode.holdup:
  468. if (clock.time < new Date().valueOf())
  469. {
  470. clock.mode = mode.countup;
  471. }
  472. else
  473. {
  474. clock.__displacement -= 1e3;
  475. }
  476. break;
  477. case mode.stopwatch:
  478. clock.__tare = true;
  479. break;
  480. default:
  481. throw 'EPICLOCK_INVALID_MODE';
  482. }
  483. clock.__displacement += modifier * clock.__offset;
  484. return clock;
  485. }
  486. /**
  487. * Perform a tick for printing on the given clock.
  488. *
  489. * @param clock The clock to tick for printing.
  490. *
  491. * @return The current time.
  492. */
  493. function printTick(clock)
  494. {
  495. now = new Date().valueOf();
  496. return tick(clock);
  497. }
  498. //------------------------------
  499. //
  500. // Plugin Definition
  501. //
  502. //------------------------------
  503. //------------------------------
  504. // Error Declaration
  505. //------------------------------
  506. //------------------------------
  507. // Plugin Creation
  508. //------------------------------
  509. $.extend(_, {
  510. //------------------------------
  511. // Properties
  512. //------------------------------
  513. /**
  514. * The precision for timing out the clocks.
  515. */
  516. precision: 500,
  517. /**
  518. * The modes for epiclocks.
  519. */
  520. modes: mode,
  521. /**
  522. * Custom renderers for epiclock.
  523. *
  524. * Signature:
  525. * {
  526. * <renderer>: <renderingFunction>,
  527. *
  528. * ...
  529. * }
  530. */
  531. renderers: {},
  532. //------------------------------
  533. // Methods
  534. //------------------------------
  535. /**
  536. * Add a renderer.
  537. *
  538. * @param key The key for this rendering function.
  539. *
  540. * @param renderer The rendering function.
  541. *
  542. * @param setup The setup function for the renderer.
  543. */
  544. addRenderer: function (key, renderer, setup)
  545. {
  546. _.renderers[key] = renderer;
  547. if ($.isFunction(setup))
  548. {
  549. _.renderers[key].setup = setup;
  550. }
  551. return _;
  552. },
  553. /**
  554. * Make a clock inside the given container using the given template html with the provided properties.
  555. *
  556. * @param properties The properties of the clock.
  557. *
  558. * @param container The container the clock will exist within.
  559. *
  560. * @param template The element template.
  561. */
  562. make: function (properties, container, template)
  563. {
  564. var clock = configureInstance(properties),
  565. output = '',
  566. /**
  567. * The characters '{' and '}' are the start and end characters of an escape. Anything between these
  568. * characters will not be treated as a formatting commands, and will merely be appended to the output
  569. * string. When the buffering property here is true, we are in the midst of appending escaped characters
  570. * to the output, and the formatting check should therefore be skipped.
  571. */
  572. buffering = false,
  573. char = '',
  574. index = 0,
  575. format = clock.format.split(''),
  576. containerClass = typeof properties.renderer === "string" ? ' epiclock-' + properties.renderer : '';
  577. container = $(container).data('epiclock', clock).addClass('epiclock-container epiclock-wait-for-render' + containerClass);
  578. template = $(template || '<span></span>');
  579. for (; index < format.length; index++)
  580. {
  581. char = format[index] + '';
  582. switch (char)
  583. {
  584. case ' ':
  585. if (buffering)
  586. {
  587. output += char;
  588. }
  589. else
  590. {
  591. template.clone(true).addClass('epiclock epiclock-spacer').appendTo(container);
  592. }
  593. break;
  594. case '{':
  595. case '}':
  596. buffering = char === '{';
  597. if (!buffering)
  598. {
  599. template.clone(true).addClass('epiclock').html(output).appendTo(container);
  600. output = '';
  601. }
  602. break;
  603. default:
  604. if (!buffering && $.dateformat.hasRule(char))
  605. {
  606. instances[clock.__uid].frame[char] = template
  607. .clone(true).addClass('epiclock epiclock-digit').data('epiclock-encoding', char).appendTo(container);
  608. }
  609. else if (!buffering)
  610. {
  611. template.clone(true).addClass('epiclock epiclock-separator').html(char).appendTo(container);
  612. }
  613. else
  614. {
  615. output += char;
  616. }
  617. break;
  618. }
  619. }
  620. clock.container = container;
  621. if (clock.__render !== undefined && $.isFunction(clock.__render.setup))
  622. {
  623. clock.__render.setup.apply(clock, [container]);
  624. }
  625. startManager();
  626. return clock;
  627. },
  628. /**
  629. * Bounce the manager.
  630. *
  631. * @param precision The precision to set in the manager upon restart.
  632. */
  633. bounce: function (precision)
  634. {
  635. if (precision !== undefined)
  636. {
  637. _.precision = precision;
  638. }
  639. _.pause().resume();
  640. },
  641. /**
  642. * Pause all clocks.
  643. */
  644. pause: function ()
  645. {
  646. haltManager();
  647. return _;
  648. },
  649. /**
  650. * Start all clocks.
  651. */
  652. resume: function ()
  653. {
  654. startManager();
  655. return _;
  656. },
  657. /**
  658. * Pause the manager and destroy all clocks.
  659. */
  660. destroy: function ()
  661. {
  662. clearInterval(intervalID);
  663. $.each(instances, function ()
  664. {
  665. this.clock.destroy();
  666. });
  667. return _;
  668. },
  669. /**
  670. * Restart all clocks.
  671. */
  672. restart: function ()
  673. {
  674. $.each(instances, function ()
  675. {
  676. this.clock.restart();
  677. });
  678. return _;
  679. }
  680. });
  681. //------------------------------
  682. // Core Extension
  683. //------------------------------
  684. $.dateformat.rules({
  685. //------------------------------
  686. // Custom Date Rules
  687. //------------------------------
  688. Q: function ()
  689. {
  690. return '%displayOffset-years%';
  691. },
  692. E: function ()
  693. {
  694. return '%displayOffset-days%';
  695. },
  696. e: function ()
  697. {
  698. return '%displayOffset-days-pad%';
  699. }
  700. });
  701. //------------------------------
  702. //
  703. // Class Definition
  704. //
  705. //------------------------------
  706. $.extend(Clock.prototype, {
  707. //------------------------------
  708. // Internal Properties
  709. //------------------------------
  710. /**
  711. * A unique identifier for this clock instance.
  712. */
  713. __uid: undefined,
  714. /**
  715. * The rendering function used by this clock instance.
  716. */
  717. __render: undefined,
  718. /**
  719. * The displacement of a clock is some number of milliseconds
  720. * which is added to the computed value of the clock before rendering.
  721. *
  722. * This property allows for timers to use the default "tick" mechanics
  723. * which drive all clocks. By setting a negative displacement equal to
  724. * the number of seconds since the epoch when the clock was started, we
  725. * basically zero out the date object, and can then compute timers from
  726. * zero seconds.
  727. *
  728. * This property is generated automatically based on whatever values are
  729. * passed into the "offset" hash, depending on what mode of clock this is.
  730. */
  731. __displacement: undefined,
  732. /**
  733. * When dealing with display offset days, the clock adds the actual number of days
  734. * since the start time to the display offset days, to update them whenever the day
  735. * actually rolls over. We need to keep track of how many days have been added to the
  736. * display offset, so we can keep computation and display separate, while still letting
  737. * computation impact display.
  738. */
  739. __days_added: undefined,
  740. /**
  741. * Whenever a clock is paused, this variable tracks the datetime when the pause first
  742. * started. Whenever the clock is resumed, this value is marked as undefined. By the nature
  743. * of this interaction, it should be considered an implicit flag. If it is undefined, the clock
  744. * is not paused. If it is any value other than undefined, the clock is paused.
  745. */
  746. __paused: undefined,
  747. /**
  748. * Offset to the current time. These values are applied to the
  749. * computed time when running, and are used to add some displacement
  750. * to the clock value. These values directly impact the computed time.
  751. */
  752. __offset: 0,
  753. /**
  754. * If true, the clock will tare on pause, to reset the start time on the clock to represent a change
  755. * in the start time of the clock, to allow for pauses not to count against the rendered time of a
  756. * clock.
  757. */
  758. __tare: false,
  759. /**
  760. * When true, the clock will be destroyed after the next render action occurs.
  761. */
  762. __destroy: false,
  763. //------------------------------
  764. // Properties
  765. //------------------------------
  766. /**
  767. * Tracks extra offsets for days and years for counter clocks. Unlike the
  768. * offset object, displayOffset has no impact on computation, and is merely
  769. * used to add untrackable date support (pre 1970) to the clock.
  770. *
  771. * Signature:
  772. * {
  773. * days: <dayDisplayOffset>,
  774. *
  775. * years: <yearDisplayOffset>
  776. * }
  777. */
  778. displayOffset: undefined,
  779. /**
  780. * Date object representing the base time used for computation in the clock.
  781. */
  782. time: undefined,
  783. /**
  784. * String describing the format this clock should be rendered with.
  785. *
  786. * See the jquery.dateformat plugin for formatting characters.
  787. */
  788. format: undefined,
  789. /**
  790. * The mode this clock is in.
  791. */
  792. mode: undefined,
  793. /**
  794. * The element representing the clocks container in the DOM.
  795. */
  796. container: undefined,
  797. //------------------------------
  798. // Methods
  799. //------------------------------
  800. /**
  801. * Bind an event listener to this clock.
  802. *
  803. * @param event The event to bind on.
  804. *
  805. * @param listener The listener function to notify when the event occurs.
  806. */
  807. bind: function (event, listener)
  808. {
  809. if (events[this.__uid] === undefined)
  810. {
  811. events[this.__uid] = {};
  812. }
  813. if (events[this.__uid][event] === undefined)
  814. {
  815. events[this.__uid][event] = [listener];
  816. }
  817. else
  818. {
  819. events[this.__uid][event].push(listener);
  820. }
  821. return this;
  822. },
  823. /**
  824. * Pause the clock.
  825. */
  826. pause: function ()
  827. {
  828. if (this.__paused === undefined)
  829. {
  830. this.__paused = new Date().valueOf();
  831. }
  832. triggerEvent(this.__uid, 'pause');
  833. },
  834. /**
  835. * Resume the clock.
  836. */
  837. resume: function ()
  838. {
  839. if (this.__paused === undefined)
  840. {
  841. return;
  842. }
  843. triggerEvent(this.__uid, 'resume');
  844. if (this.__tare)
  845. {
  846. this.__displacement += (this.__paused - new Date().valueOf());
  847. }
  848. this.__paused = undefined;
  849. },
  850. /**
  851. * Toggle the pause status of the clock.
  852. */
  853. toggle: function ()
  854. {
  855. if (this.__paused === undefined)
  856. {
  857. this.pause();
  858. }
  859. else
  860. {
  861. this.resume();
  862. }
  863. },
  864. /**
  865. * Destroy the clock.
  866. */
  867. destroy: function ()
  868. {
  869. this.__destroy = true;
  870. triggerEvent(this.__uid, 'destroy');
  871. },
  872. /**
  873. * Reset the clock to use the current time as the start time for the clock.
  874. *
  875. * @param displacement The new value for the displacement.
  876. */
  877. restart: function (displacement)
  878. {
  879. if (displacement !== undefined)
  880. {
  881. this.__displacement = displacement;
  882. }
  883. this.time = now.valueOf();
  884. },
  885. /**
  886. * Print a string version of the clock's current time.
  887. *
  888. * @param format If a special format other than the default is to be used.
  889. *
  890. * @return The formatted string.
  891. */
  892. print: function (format)
  893. {
  894. var value = $.dateformat.format(this.format, printTick(this));
  895. if (this.displayOffset !== undefined)
  896. {
  897. return value
  898. .replace('%displayOffset-days%', this.displayOffset.years)
  899. .replace('%displayOffset-days%', this.displayOffset.days)
  900. .replace('%displayOffset-days-pad%', $.dateformat.pad(this.displayOffset.days, 0));
  901. }
  902. return value;
  903. }
  904. });
  905. //------------------------------
  906. //
  907. // jQuery Hooks
  908. //
  909. //------------------------------
  910. $.epiclock = _;
  911. /**
  912. * Element builder for epiclock.
  913. *
  914. * @param properties The properties for building the epiclock.
  915. */
  916. $.fn.epiclock = function (properties)
  917. {
  918. return this.each(function ()
  919. {
  920. var container = $(this),
  921. template;
  922. properties = properties || {};
  923. if (properties.template !== undefined)
  924. {
  925. template = properties.template;
  926. delete properties.template;
  927. }
  928. else if (container.children().length > 0)
  929. {
  930. template = container.children().remove().eq(0);
  931. }
  932. $.epiclock.make(properties, container, template);
  933. });
  934. };
  935. //------------------------------
  936. //
  937. // Event Bindings
  938. //
  939. //------------------------------
  940. //------------------------------
  941. //
  942. // Startup Code
  943. //
  944. //------------------------------
  945. }(jQuery));