jquery.missofis-countdown.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. /*!
  2. * Missofis Countdown Timer
  3. *
  4. * Author: Kemal Yılmaz
  5. * Website: http://missofis.com
  6. * License: MIT
  7. *
  8. * @see http://jqueryboilerplate.com/ for this jQuery plugin boilerplate details
  9. */
  10. ;( function ( $, window, document, undefined ) {
  11. 'use strict';
  12. /**
  13. * Plugin defaults
  14. */
  15. var PLUGIN_NAME = 'countdown',
  16. defaults = {
  17. from: 180, // 3 minutes (3*60)
  18. to: 0, // stop at zero
  19. movingUnit: 1000, // 1000 for 1 second increment/decrements
  20. timerEnd: undefined,
  21. outputPattern: '$day Day $hour : $minute : $second',
  22. autostart: true
  23. },
  24. outputReplacement = new RegExp( '\\$day|\\$hour|\\$minute|\\$second', 'g' );
  25. /**
  26. * Plugin constructor
  27. *
  28. * @param element
  29. *
  30. * @param options
  31. */
  32. function Plugin( element, options ) {
  33. // set scope vars
  34. this.element = element;
  35. this.$element = $( element );
  36. // set plugin setting
  37. this.settings = $.extend( {}, defaults, options );
  38. // set name and defaults
  39. this._defaults = defaults;
  40. this._name = PLUGIN_NAME;
  41. // set counter direction (counting up/down) and timer current position
  42. // _displacement is -1 for count down +1 for counting up, NaN for equality
  43. // set current interval id memory
  44. // set counter status flag
  45. this._displacement = -1 * ( this.settings.from - this.settings.to ) / Math.abs( this.settings.from - this.settings.to );
  46. this._timerAt = this.settings.from;
  47. this._intervalId = undefined;
  48. this._isCounting = false;
  49. // call initialize
  50. this.init();
  51. }
  52. /**
  53. * Plugin prototype
  54. */
  55. Plugin.prototype = {
  56. /*
  57. ------------------------------------------------------------
  58. PLUGIN PUBLIC API
  59. ------------------------------------------------------------
  60. */
  61. /**
  62. * Plugin initialization
  63. */
  64. init: function() {
  65. // initiate timer starting text
  66. this._updateTimerText();
  67. // check autostart and start counter
  68. if( this.settings.autostart )
  69. this._start();
  70. },
  71. /**
  72. * Remove timer javascript counter and delete timer instance from element
  73. */
  74. destroy: function() {
  75. // clear timer
  76. window.clearInterval( this._intervalID );
  77. // remove plugin instance
  78. $.data( this.element, 'plugin_' + PLUGIN_NAME, null );
  79. },
  80. /**
  81. * Resumes counter
  82. */
  83. resume: function() {
  84. // check if counter is running and return
  85. if( this._isCounting )
  86. return;
  87. // start timer
  88. this._start();
  89. },
  90. /**
  91. * Pause counter
  92. */
  93. pause: function() {
  94. // check if counter is not running and return
  95. if( ! this._isCounting )
  96. return;
  97. // set counter as stopped
  98. this._isCounting = false;
  99. // clear old interval
  100. if( undefined !== this._intervalID )
  101. window.clearInterval( this._intervalID );
  102. },
  103. /*
  104. ------------------------------------------------------------
  105. PLUGIN PSEUDO-PRIVATE API by "_" NAMING CONVENTION
  106. ------------------------------------------------------------
  107. */
  108. /**
  109. * Starts counter
  110. */
  111. _start: function() {
  112. // get plugin
  113. var that = this;
  114. // clear old interval
  115. if( undefined !== this._intervalID )
  116. window.clearInterval( this._intervalID );
  117. //
  118. this._isCounting = true;
  119. // todo :: do not set an interval for from == to equality
  120. //
  121. this._intervalID = window.setInterval( function() {
  122. // update timer position (fire event?)
  123. that._timerAt += that._displacement;
  124. // update timer
  125. that._update();
  126. }, that.settings.movingUnit );
  127. },
  128. /**
  129. * Stop and destroy counter
  130. */
  131. _stop: function() {
  132. // destroy timer
  133. this.destroy();
  134. // call timerEnd callback if defined
  135. if( this.settings.timerEnd && 'function' === typeof this.settings.timerEnd ) {
  136. // set calback functions scope to jQuery element timer being created
  137. // todo :: change context with native element instead of jQuery one
  138. this.settings.timerEnd.call( this.$element );
  139. }
  140. },
  141. /**
  142. * Update counter data
  143. */
  144. _update: function() {
  145. // get plugin
  146. var that = this;
  147. //
  148. this._updateTimerText();
  149. //
  150. if( this._timerAt === this.settings.to || isNaN( this._timerAt ) ) {
  151. //
  152. this._stop();
  153. }
  154. },
  155. /**
  156. * Update timer via intervalID's
  157. */
  158. _updateTimerText: function() {
  159. // get plugin
  160. var that = this;
  161. //
  162. this.$element.text( function( index, text ) {
  163. //
  164. var _parsedStuff = that._parseSeconds( that._timerAt );
  165. //
  166. var _daysParsed = _parsedStuff.days.toString(),
  167. _hoursParsed = _parsedStuff.hours.toString(),
  168. _minutesParsed = _parsedStuff.minutes.toString(),
  169. _secondsParsed = _parsedStuff.seconds.toString();
  170. // check days/hours/minutes/second and change single digit result with 2 digit ones ( 1's to 01's )
  171. if( _parsedStuff.days < 10 )
  172. _daysParsed = '0' + _daysParsed;
  173. if( _parsedStuff.hours < 10 )
  174. _hoursParsed = '0' + _hoursParsed;
  175. if( _parsedStuff.minutes < 10 )
  176. _minutesParsed = '0' + _minutesParsed;
  177. if( _parsedStuff.seconds < 10 )
  178. _secondsParsed = '0' + _secondsParsed;
  179. // todo :: compare perfromance between length check and regexp replace
  180. // return parsed timer string
  181. return that.settings.outputPattern.replace( outputReplacement, function( match, offset, string ) {
  182. //
  183. switch( match ) {
  184. case '$day':
  185. return _daysParsed;
  186. case '$hour':
  187. return _hoursParsed;
  188. case '$minute':
  189. return _minutesParsed;
  190. case '$second':
  191. return _secondsParsed;
  192. default:
  193. return '';
  194. }
  195. } );
  196. } );
  197. },
  198. /**
  199. * Split up seconds to time components (days, hours, minutes, seconds)
  200. *
  201. * @param seconds
  202. */
  203. _parseSeconds: function( seconds ) {
  204. // handle negative counting
  205. seconds = Math.abs( seconds );
  206. //
  207. var _parsedTime = {
  208. days: 0,
  209. hours: 0,
  210. minutes: Math.floor( seconds / 60 ),
  211. seconds: 0
  212. };
  213. // get days
  214. if( seconds >= 86400 ) {
  215. _parsedTime.days = Math.floor( seconds / 86400 );
  216. _parsedTime.hours = Math.floor( seconds % 86400 / 3600 );
  217. }
  218. else if( seconds >= 3600 )
  219. _parsedTime.hours = Math.floor( seconds / 3600 );
  220. // get minutes
  221. if( seconds >= 3600 )
  222. _parsedTime.minutes = Math.floor( seconds % 3600 / 60 );
  223. // get seconds
  224. _parsedTime.seconds = seconds % 60;
  225. // return parset numeric values
  226. return _parsedTime;
  227. }
  228. };
  229. /**
  230. * Plugin wrapper
  231. */
  232. $.fn[ PLUGIN_NAME ] = function ( options ) {
  233. // instantiate plugin with chaining enabled if options is omitted or is an object
  234. if( undefined === options || 'object' === typeof options ) {
  235. return this.each( function () {
  236. // create new plugin in elements data object which plugin is executed from
  237. // skip plugin instantiation if plugin set in element's data object already
  238. if ( ! $.data( this, 'plugin_' + PLUGIN_NAME ) )
  239. $.data( this, 'plugin_' + PLUGIN_NAME, new Plugin( this, options ) );
  240. } );
  241. }
  242. // capture any public plugin method call
  243. // skip pseudo-private function via "_" skipper
  244. // skip 'init' call
  245. // @see https://github.com/jquery-boilerplate/jquery-boilerplate/wiki/Extending-jQuery-Boilerplate
  246. else if( 'string' === typeof options && options[ 0 ] !== '_' && options !== 'init' ) {
  247. return this.each( function() {
  248. // get current instance of plugin
  249. var instance = $.data( this, 'plugin_' + PLUGIN_NAME );
  250. // check if plugin instance is available and instance has target method being called
  251. if( instance instanceof Plugin && 'function' === typeof instance[ options ] ) {
  252. // call the plugin public method
  253. instance[ options ].call( instance );
  254. }
  255. } );
  256. }
  257. };
  258. } ) ( jQuery, window, document );