jquery.countdown.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. /*!
  2. The Final Countdown
  3. Version: 2.2.0
  4. Plugin URL: http://hilios.github.io/jQuery.countdown/
  5. License: Copyright (c) 2016 Edson Hilios
  6. !*/
  7. (function(factory) {
  8. "use strict";
  9. if (typeof define === "function" && define.amd) {
  10. define([ "jquery" ], factory);
  11. } else {
  12. factory(jQuery);
  13. }
  14. })(function($) {
  15. "use strict";
  16. var instances = [], matchers = [], defaultOptions = {
  17. precision: 100,
  18. elapse: false,
  19. defer: false
  20. };
  21. matchers.push(/^[0-9]*$/.source);
  22. matchers.push(/([0-9]{1,2}\/){2}[0-9]{4}( [0-9]{1,2}(:[0-9]{2}){2})?/.source);
  23. matchers.push(/[0-9]{4}([\/\-][0-9]{1,2}){2}( [0-9]{1,2}(:[0-9]{2}){2})?/.source);
  24. matchers = new RegExp(matchers.join("|"));
  25. function parseDateString(dateString) {
  26. if (dateString instanceof Date) {
  27. return dateString;
  28. }
  29. if (String(dateString).match(matchers)) {
  30. if (String(dateString).match(/^[0-9]*$/)) {
  31. dateString = Number(dateString);
  32. }
  33. if (String(dateString).match(/\-/)) {
  34. dateString = String(dateString).replace(/\-/g, "/");
  35. }
  36. return new Date(dateString);
  37. } else {
  38. throw new Error("Couldn't cast `" + dateString + "` to a date object.");
  39. }
  40. }
  41. var DIRECTIVE_KEY_MAP = {
  42. Y: "years",
  43. m: "months",
  44. n: "daysToMonth",
  45. d: "daysToWeek",
  46. w: "weeks",
  47. W: "weeksToMonth",
  48. H: "hours",
  49. M: "minutes",
  50. S: "seconds",
  51. D: "totalDays",
  52. I: "totalHours",
  53. N: "totalMinutes",
  54. T: "totalSeconds"
  55. };
  56. function escapedRegExp(str) {
  57. var sanitize = str.toString().replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
  58. return new RegExp(sanitize);
  59. }
  60. function strftime(offsetObject) {
  61. return function(format) {
  62. var directives = format.match(/%(-|!)?[A-Z]{1}(:[^;]+;)?/gi);
  63. if (directives) {
  64. for (var i = 0, len = directives.length; i < len; ++i) {
  65. var directive = directives[i].match(/%(-|!)?([a-zA-Z]{1})(:[^;]+;)?/), regexp = escapedRegExp(directive[0]), modifier = directive[1] || "", plural = directive[3] || "", value = null;
  66. directive = directive[2];
  67. if (DIRECTIVE_KEY_MAP.hasOwnProperty(directive)) {
  68. value = DIRECTIVE_KEY_MAP[directive];
  69. value = Number(offsetObject[value]);
  70. }
  71. if (value !== null) {
  72. if (modifier === "!") {
  73. value = pluralize(plural, value);
  74. }
  75. if (modifier === "") {
  76. if (value < 10) {
  77. value = "0" + value.toString();
  78. }
  79. }
  80. format = format.replace(regexp, value.toString());
  81. }
  82. }
  83. }
  84. format = format.replace(/%%/, "%");
  85. return format;
  86. };
  87. }
  88. function pluralize(format, count) {
  89. var plural = "s", singular = "";
  90. if (format) {
  91. format = format.replace(/(:|;|\s)/gi, "").split(/\,/);
  92. if (format.length === 1) {
  93. plural = format[0];
  94. } else {
  95. singular = format[0];
  96. plural = format[1];
  97. }
  98. }
  99. if (Math.abs(count) > 1) {
  100. return plural;
  101. } else {
  102. return singular;
  103. }
  104. }
  105. var Countdown = function(el, finalDate, options) {
  106. this.el = el;
  107. this.$el = $(el);
  108. this.interval = null;
  109. this.offset = {};
  110. this.options = $.extend({}, defaultOptions);
  111. this.instanceNumber = instances.length;
  112. instances.push(this);
  113. this.$el.data("countdown-instance", this.instanceNumber);
  114. if (options) {
  115. if (typeof options === "function") {
  116. this.$el.on("update.countdown", options);
  117. this.$el.on("stoped.countdown", options);
  118. this.$el.on("finish.countdown", options);
  119. } else {
  120. this.options = $.extend({}, defaultOptions, options);
  121. }
  122. }
  123. this.setFinalDate(finalDate);
  124. if (this.options.defer === false) {
  125. this.start();
  126. }
  127. };
  128. $.extend(Countdown.prototype, {
  129. start: function() {
  130. if (this.interval !== null) {
  131. clearInterval(this.interval);
  132. }
  133. var self = this;
  134. this.update();
  135. this.interval = setInterval(function() {
  136. self.update.call(self);
  137. }, this.options.precision);
  138. },
  139. stop: function() {
  140. clearInterval(this.interval);
  141. this.interval = null;
  142. this.dispatchEvent("stoped");
  143. },
  144. toggle: function() {
  145. if (this.interval) {
  146. this.stop();
  147. } else {
  148. this.start();
  149. }
  150. },
  151. pause: function() {
  152. this.stop();
  153. },
  154. resume: function() {
  155. this.start();
  156. },
  157. remove: function() {
  158. this.stop.call(this);
  159. instances[this.instanceNumber] = null;
  160. delete this.$el.data().countdownInstance;
  161. },
  162. setFinalDate: function(value) {
  163. this.finalDate = parseDateString(value);
  164. },
  165. update: function() {
  166. if (this.$el.closest("html").length === 0) {
  167. this.remove();
  168. return;
  169. }
  170. var hasEventsAttached = $._data(this.el, "events") !== undefined, now = new Date(), newTotalSecsLeft;
  171. newTotalSecsLeft = this.finalDate.getTime() - now.getTime();
  172. newTotalSecsLeft = Math.ceil(newTotalSecsLeft / 1e3);
  173. newTotalSecsLeft = !this.options.elapse && newTotalSecsLeft < 0 ? 0 : Math.abs(newTotalSecsLeft);
  174. if (this.totalSecsLeft === newTotalSecsLeft || !hasEventsAttached) {
  175. return;
  176. } else {
  177. this.totalSecsLeft = newTotalSecsLeft;
  178. }
  179. this.elapsed = now >= this.finalDate;
  180. this.offset = {
  181. seconds: this.totalSecsLeft % 60,
  182. minutes: Math.floor(this.totalSecsLeft / 60) % 60,
  183. hours: Math.floor(this.totalSecsLeft / 60 / 60) % 24,
  184. days: Math.floor(this.totalSecsLeft / 60 / 60 / 24) % 7,
  185. daysToWeek: Math.floor(this.totalSecsLeft / 60 / 60 / 24) % 7,
  186. daysToMonth: Math.floor(this.totalSecsLeft / 60 / 60 / 24 % 30.4368),
  187. weeks: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 7),
  188. weeksToMonth: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 7) % 4,
  189. months: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 30.4368),
  190. years: Math.abs(this.finalDate.getFullYear() - now.getFullYear()),
  191. totalDays: Math.floor(this.totalSecsLeft / 60 / 60 / 24),
  192. totalHours: Math.floor(this.totalSecsLeft / 60 / 60),
  193. totalMinutes: Math.floor(this.totalSecsLeft / 60),
  194. totalSeconds: this.totalSecsLeft
  195. };
  196. if (!this.options.elapse && this.totalSecsLeft === 0) {
  197. this.stop();
  198. this.dispatchEvent("finish");
  199. } else {
  200. this.dispatchEvent("update");
  201. }
  202. },
  203. dispatchEvent: function(eventName) {
  204. var event = $.Event(eventName + ".countdown");
  205. event.finalDate = this.finalDate;
  206. event.elapsed = this.elapsed;
  207. event.offset = $.extend({}, this.offset);
  208. event.strftime = strftime(this.offset);
  209. this.$el.trigger(event);
  210. }
  211. });
  212. $.fn.countdown = function() {
  213. var argumentsArray = Array.prototype.slice.call(arguments, 0);
  214. return this.each(function() {
  215. var instanceNumber = $(this).data("countdown-instance");
  216. if (instanceNumber !== undefined) {
  217. var instance = instances[instanceNumber], method = argumentsArray[0];
  218. if (Countdown.prototype.hasOwnProperty(method)) {
  219. instance[method].apply(instance, argumentsArray.slice(1));
  220. } else if (String(method).match(/^[$A-Z_][0-9A-Z_$]*$/i) === null) {
  221. instance.setFinalDate.call(instance, method);
  222. instance.start();
  223. } else {
  224. $.error("Method %s does not exist on jQuery.countdown".replace(/\%s/gi, method));
  225. }
  226. } else {
  227. new Countdown(this, argumentsArray[0], argumentsArray[1]);
  228. }
  229. });
  230. };
  231. });