diff --git a/Gruntfile.js b/Gruntfile.js index e1a15cb..688e29e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -4,20 +4,20 @@ module.exports = function(grunt) { pkg: grunt.file.readJSON('package.json'), jscs: { - src: ['Gruntfile.js', 'src/*.js', 'test/*.js'] + src: ['Gruntfile.js', 'src/*.js', 'test/utils-test.js', 'test/basicTimerSpec', 'test/timedFuncSpec.js'] }, jshint: { - all: ['Gruntfile.js', 'src/*.js', 'test/*.js'] + all: ['Gruntfile.js', 'src/*.js', 'test/utils-test.js', 'test/basicTimerSpec', 'test/timedFuncSpec.js'] }, concat: { options: { banner: [ '/*! <%= pkg.name %> <%= pkg.version %> <%=grunt.template.today("yyyy-mm-dd")%>*/\n', - '(function() {\n' + '(function($) {\n' ].join(''), - footer: '} ());' + footer: '} (jQuery));' }, dist: { src: [ diff --git a/README.md b/README.md index 162b3aa..86721b8 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ [Demo & Instructions][demo] | [Download][min] -[demo]: http://jquerytimer.com/ +[demo]: https://walmik.github.io/timer.jquery/ [min]: https://raw.githubusercontent.com/walmik/timer.jquery/master/dist/timer.jquery.min.js ## Getting started @@ -54,7 +54,9 @@ $("#div-id").timer({ duration: {String}, // The time to countdown from. `seconds` and `duration` are mutually exclusive callback: {Function}, // If duration is set, this function is called after `duration` has elapsed repeat: {Bool}, // If duration is set, `callback` will be called repeatedly - format: {String} // Format to show time in + format: {String}, // Format to show time in + editable: {Bool} // If click and edit time is enabled + hidden; {Bool} // If true, the timer is not displayed in the selected item. }); ``` @@ -121,7 +123,7 @@ $('#div-id').timer({ You can get the current state of timer by querying the `state` property on it's data object ```javascript -$('#div-id').data('state'); // running | paused | stopped +$('#div-id').data('state'); // running | paused | stopped | removed ``` ##### Duration Syntax diff --git a/dist/timer.jquery.js b/dist/timer.jquery.js index 61c52d3..efdb776 100644 --- a/dist/timer.jquery.js +++ b/dist/timer.jquery.js @@ -1,12 +1,14 @@ -/*! timer.jquery 0.7.0 2017-04-15*/ -(function() { +/*! timer.jquery 0.9.2 2024-12-13*/ +(function($) { var Constants = { PLUGIN_NAME: 'timer', TIMER_RUNNING: 'running', TIMER_PAUSED: 'paused', + TIMER_STOPPED: 'stopped', + TIMER_REMOVED: 'removed', DAYINSECONDS: 86400 }; - + /* global Constants:true */ /** * Private @@ -237,27 +239,30 @@ function intervalHandler(timerInstance) { if (timerInstance.config.countdown) { timerInstance.totalSeconds = timerInstance.config.duration - timerInstance.totalSeconds; + timerInstance.render(); + if (timerInstance.totalSeconds === 0) { clearInterval(timerInstance.intervalId); setState(timerInstance, Constants.TIMER_STOPPED); timerInstance.config.callback(); $(timerInstance.element).data('seconds'); } - - timerInstance.render(); return; } timerInstance.render(); + if (!timerInstance.config.duration) { return; } // If the timer was called with a duration parameter, - // run the callback if duration is complete + // run the callback if duration is complete or total seconds is more than duration // and remove the duration if `repeat` is not requested + if (timerInstance.totalSeconds > 0 && - timerInstance.totalSeconds % timerInstance.config.duration === 0) { + (timerInstance.totalSeconds % timerInstance.config.duration === 0 || + timerInstance.totalSeconds > timerInstance.config.duration )) { if (timerInstance.config.callback) { timerInstance.config.callback(); } @@ -281,7 +286,7 @@ var utils = { makeEditable: makeEditable, intervalHandler: intervalHandler }; - + /** * Timer function to initiate a timer on the provided element with the given config. * @param {Object} element HTML node as passed by jQuery @@ -292,6 +297,7 @@ function Timer(element, config) { this.originalConfig = $.extend({}, config); this.totalSeconds = 0; this.intervalId = null; + // A HTML element will have the html() method in jQuery to inject content, this.html = 'html'; if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') { @@ -343,7 +349,9 @@ function Timer(element, config) { Timer.prototype.start = function() { if (this.state !== Constants.TIMER_RUNNING) { utils.setState(this, Constants.TIMER_RUNNING); - this.render(); + if (this.config.hidden !== true) { + this.render(); + } this.intervalId = setInterval(utils.intervalHandler.bind(null, this), this.config.updateFrequency); } }; @@ -369,6 +377,7 @@ Timer.prototype.resume = function() { Timer.prototype.remove = function() { clearInterval(this.intervalId); + utils.setState(this, Constants.TIMER_REMOVED); $(this.element).data(Constants.PLUGIN_NAME, null); $(this.element).data('seconds', null); }; @@ -387,7 +396,7 @@ Timer.prototype.render = function() { } $(this.element).data('seconds', this.totalSeconds); }; - + /* global $:true */ $.fn.timer = function(options) { options = options || 'start'; @@ -421,4 +430,4 @@ $.fn.timer = function(options) { } }); }; -} ()); \ No newline at end of file +} (jQuery)); \ No newline at end of file diff --git a/dist/timer.jquery.min.js b/dist/timer.jquery.min.js index 63dc6cc..37bc970 100644 --- a/dist/timer.jquery.min.js +++ b/dist/timer.jquery.min.js @@ -1 +1 @@ -!function(){function a(a){var b;return a=a||0,b=Math.floor(a/60),{days:a>=m.DAYINSECONDS?Math.floor(a/m.DAYINSECONDS):0,hours:a>=3600?Math.floor(a%m.DAYINSECONDS/3600):0,totalMinutes:b,minutes:a>=60?Math.floor(a%3600/60):b,seconds:a%60,totalSeconds:a}}function b(a){return((a=parseInt(a,10))<10&&"0")+a}function c(){return{seconds:0,editable:!1,duration:null,callback:function(){console.log("Time up!")},repeat:!1,countdown:!1,format:null,updateFrequency:500}}function d(){return Math.round((Date.now?Date.now():(new Date).getTime())/1e3)}function e(c){var d=a(c);if(d.days)return d.days+":"+b(d.hours)+":"+b(d.minutes)+":"+b(d.seconds);if(d.hours)return d.hours+":"+b(d.minutes)+":"+b(d.seconds);return d.minutes?d.minutes+":"+b(d.seconds)+" min":d.seconds+" sec"}function f(c,d){for(var e=a(c),f=[{identifier:"%d",value:e.days},{identifier:"%h",value:e.hours},{identifier:"%m",value:e.minutes},{identifier:"%s",value:e.seconds},{identifier:"%g",value:e.totalMinutes},{identifier:"%t",value:e.totalSeconds},{identifier:"%D",value:b(e.days)},{identifier:"%H",value:b(e.hours)},{identifier:"%M",value:b(e.minutes)},{identifier:"%S",value:b(e.seconds)},{identifier:"%G",value:b(e.totalMinutes)},{identifier:"%T",value:b(e.totalSeconds)}],g=0;g0?c=Number(a.replace(/\ssec/g,"")):a.indexOf("min")>0?(a=a.replace(/\smin/g,""),b=a.split(":"),c=Number(60*b[0])+Number(b[1])):a.match(/\d{1,2}:\d{2}:\d{2}:\d{2}/)?(b=a.split(":"),c=Number(b[0]*m.DAYINSECONDS)+Number(3600*b[1])+Number(60*b[2])+Number(b[3])):a.match(/\d{1,2}:\d{2}:\d{2}/)&&(b=a.split(":"),c=Number(3600*b[0])+Number(60*b[1])+Number(b[2])),c}function i(a,b){a.state=b,$(a.element).data("state",b)}function j(a){$(a.element).on("focus",function(){a.pause()}),$(a.element).on("blur",function(){a.totalSeconds=h($(a.element)[a.html]()),a.resume()})}function k(a){if(a.totalSeconds=d()-a.startTime,a.config.countdown)return a.totalSeconds=a.config.duration-a.totalSeconds,0===a.totalSeconds&&(clearInterval(a.intervalId),i(a,m.TIMER_STOPPED),a.config.callback(),$(a.element).data("seconds")),void a.render();a.render(),a.config.duration&&a.totalSeconds>0&&a.totalSeconds%a.config.duration==0&&(a.config.callback&&a.config.callback(),a.config.repeat||(clearInterval(a.intervalId),i(a,m.TIMER_STOPPED),a.config.duration=null))}function l(a,b){if(this.element=a,this.originalConfig=$.extend({},b),this.totalSeconds=0,this.intervalId=null,this.html="html","INPUT"!==a.tagName&&"TEXTAREA"!==a.tagName||(this.html="val"),this.config=n.getDefaultConfig(),b.duration&&(b.duration=n.durationTimeToSeconds(b.duration)),"string"!=typeof b&&(this.config=$.extend(this.config,b)),this.config.seconds&&(this.totalSeconds=this.config.seconds),this.config.editable&&n.makeEditable(this),this.startTime=n.unixSeconds()-this.totalSeconds,this.config.duration&&this.config.repeat&&this.config.updateFrequency<1e3&&(this.config.updateFrequency=1e3),this.config.countdown){if(!this.config.duration)throw new Error("Countdown option set without duration!");if(this.config.editable)throw new Error("Cannot set editable on a countdown timer!");this.config.startTime=n.unixSeconds()-this.config.duration,this.totalSeconds=this.config.duration}}var m={PLUGIN_NAME:"timer",TIMER_RUNNING:"running",TIMER_PAUSED:"paused",DAYINSECONDS:86400},n={getDefaultConfig:c,unixSeconds:d,secondsToPrettyTime:e,secondsToFormattedTime:f,durationTimeToSeconds:g,prettyTimeToSeconds:h,setState:i,makeEditable:j,intervalHandler:k};l.prototype.start=function(){this.state!==m.TIMER_RUNNING&&(n.setState(this,m.TIMER_RUNNING),this.render(),this.intervalId=setInterval(n.intervalHandler.bind(null,this),this.config.updateFrequency))},l.prototype.pause=function(){this.state===m.TIMER_RUNNING&&(n.setState(this,m.TIMER_PAUSED),clearInterval(this.intervalId))},l.prototype.resume=function(){this.state===m.TIMER_PAUSED&&(n.setState(this,m.TIMER_RUNNING),this.config.countdown?this.startTime=n.unixSeconds()-this.config.duration+this.totalSeconds:this.startTime=n.unixSeconds()-this.totalSeconds,this.intervalId=setInterval(n.intervalHandler.bind(null,this),this.config.updateFrequency))},l.prototype.remove=function(){clearInterval(this.intervalId),$(this.element).data(m.PLUGIN_NAME,null),$(this.element).data("seconds",null)},l.prototype.reset=function(){var a=this.originalConfig;this.remove(),$(this.element).timer(a)},l.prototype.render=function(){this.config.format?$(this.element)[this.html](n.secondsToFormattedTime(this.totalSeconds,this.config.format)):$(this.element)[this.html](n.secondsToPrettyTime(this.totalSeconds)),$(this.element).data("seconds",this.totalSeconds)},$.fn.timer=function(a){return a=a||"start",this.each(function(){$.data(this,m.PLUGIN_NAME)instanceof l||$.data(this,m.PLUGIN_NAME,new l(this,a));var b=$.data(this,m.PLUGIN_NAME);"string"==typeof a?"function"==typeof b[a]&&b[a]():b.start()})}}(); \ No newline at end of file +!function(a){function b(a){var b;return a=a||0,b=Math.floor(a/60),{days:a>=n.DAYINSECONDS?Math.floor(a/n.DAYINSECONDS):0,hours:a>=3600?Math.floor(a%n.DAYINSECONDS/3600):0,totalMinutes:b,minutes:a>=60?Math.floor(a%3600/60):b,seconds:a%60,totalSeconds:a}}function c(a){return((a=parseInt(a,10))<10&&"0")+a}function d(){return{seconds:0,editable:!1,duration:null,callback:function(){console.log("Time up!")},repeat:!1,countdown:!1,format:null,updateFrequency:500}}function e(){return Math.round((Date.now?Date.now():(new Date).getTime())/1e3)}function f(a){var d=b(a);if(d.days)return d.days+":"+c(d.hours)+":"+c(d.minutes)+":"+c(d.seconds);if(d.hours)return d.hours+":"+c(d.minutes)+":"+c(d.seconds);return d.minutes?d.minutes+":"+c(d.seconds)+" min":d.seconds+" sec"}function g(a,d){for(var e=b(a),f=[{identifier:"%d",value:e.days},{identifier:"%h",value:e.hours},{identifier:"%m",value:e.minutes},{identifier:"%s",value:e.seconds},{identifier:"%g",value:e.totalMinutes},{identifier:"%t",value:e.totalSeconds},{identifier:"%D",value:c(e.days)},{identifier:"%H",value:c(e.hours)},{identifier:"%M",value:c(e.minutes)},{identifier:"%S",value:c(e.seconds)},{identifier:"%G",value:c(e.totalMinutes)},{identifier:"%T",value:c(e.totalSeconds)}],g=0;g0?c=Number(a.replace(/\ssec/g,"")):a.indexOf("min")>0?(a=a.replace(/\smin/g,""),b=a.split(":"),c=Number(60*b[0])+Number(b[1])):a.match(/\d{1,2}:\d{2}:\d{2}:\d{2}/)?(b=a.split(":"),c=Number(b[0]*n.DAYINSECONDS)+Number(3600*b[1])+Number(60*b[2])+Number(b[3])):a.match(/\d{1,2}:\d{2}:\d{2}/)&&(b=a.split(":"),c=Number(3600*b[0])+Number(60*b[1])+Number(b[2])),c}function j(b,c){b.state=c,a(b.element).data("state",c)}function k(b){a(b.element).on("focus",function(){b.pause()}),a(b.element).on("blur",function(){b.totalSeconds=i(a(b.element)[b.html]()),b.resume()})}function l(b){if(b.totalSeconds=e()-b.startTime,b.config.countdown)return b.totalSeconds=b.config.duration-b.totalSeconds,b.render(),void(0===b.totalSeconds&&(clearInterval(b.intervalId),j(b,n.TIMER_STOPPED),b.config.callback(),a(b.element).data("seconds")));b.render(),b.config.duration&&b.totalSeconds>0&&(b.totalSeconds%b.config.duration==0||b.totalSeconds>b.config.duration)&&(b.config.callback&&b.config.callback(),b.config.repeat||(clearInterval(b.intervalId),j(b,n.TIMER_STOPPED),b.config.duration=null))}function m(b,c){if(this.element=b,this.originalConfig=a.extend({},c),this.totalSeconds=0,this.intervalId=null,this.html="html","INPUT"!==b.tagName&&"TEXTAREA"!==b.tagName||(this.html="val"),this.config=o.getDefaultConfig(),c.duration&&(c.duration=o.durationTimeToSeconds(c.duration)),"string"!=typeof c&&(this.config=a.extend(this.config,c)),this.config.seconds&&(this.totalSeconds=this.config.seconds),this.config.editable&&o.makeEditable(this),this.startTime=o.unixSeconds()-this.totalSeconds,this.config.duration&&this.config.repeat&&this.config.updateFrequency<1e3&&(this.config.updateFrequency=1e3),this.config.countdown){if(!this.config.duration)throw new Error("Countdown option set without duration!");if(this.config.editable)throw new Error("Cannot set editable on a countdown timer!");this.config.startTime=o.unixSeconds()-this.config.duration,this.totalSeconds=this.config.duration}}var n={PLUGIN_NAME:"timer",TIMER_RUNNING:"running",TIMER_PAUSED:"paused",TIMER_STOPPED:"stopped",TIMER_REMOVED:"removed",DAYINSECONDS:86400},o={getDefaultConfig:d,unixSeconds:e,secondsToPrettyTime:f,secondsToFormattedTime:g,durationTimeToSeconds:h,prettyTimeToSeconds:i,setState:j,makeEditable:k,intervalHandler:l};m.prototype.start=function(){this.state!==n.TIMER_RUNNING&&(o.setState(this,n.TIMER_RUNNING),!0!==this.config.hidden&&this.render(),this.intervalId=setInterval(o.intervalHandler.bind(null,this),this.config.updateFrequency))},m.prototype.pause=function(){this.state===n.TIMER_RUNNING&&(o.setState(this,n.TIMER_PAUSED),clearInterval(this.intervalId))},m.prototype.resume=function(){this.state===n.TIMER_PAUSED&&(o.setState(this,n.TIMER_RUNNING),this.config.countdown?this.startTime=o.unixSeconds()-this.config.duration+this.totalSeconds:this.startTime=o.unixSeconds()-this.totalSeconds,this.intervalId=setInterval(o.intervalHandler.bind(null,this),this.config.updateFrequency))},m.prototype.remove=function(){clearInterval(this.intervalId),o.setState(this,n.TIMER_REMOVED),a(this.element).data(n.PLUGIN_NAME,null),a(this.element).data("seconds",null)},m.prototype.reset=function(){var b=this.originalConfig;this.remove(),a(this.element).timer(b)},m.prototype.render=function(){this.config.format?a(this.element)[this.html](o.secondsToFormattedTime(this.totalSeconds,this.config.format)):a(this.element)[this.html](o.secondsToPrettyTime(this.totalSeconds)),a(this.element).data("seconds",this.totalSeconds)},a.fn.timer=function(b){return b=b||"start",this.each(function(){a.data(this,n.PLUGIN_NAME)instanceof m||a.data(this,n.PLUGIN_NAME,new m(this,b));var c=a.data(this,n.PLUGIN_NAME);"string"==typeof b?"function"==typeof c[b]&&c[b]():c.start()})}}(jQuery); \ No newline at end of file diff --git a/package.json b/package.json index b0e310a..685d83d 100644 --- a/package.json +++ b/package.json @@ -2,16 +2,16 @@ "name": "timer.jquery", "description": "Start/Stop/Resume/Remove a pretty timer inside any HTML element.", "author": "Walmik Deshpande", - "version": "0.7.0", + "version": "0.9.2", "repository": { "type": "git", "url": "git://github.com/walmik/timer.jquery.git" }, "scripts": { - "test": "./node_modules/.bin/tape ./test/utils-test.js", - "build": "./node_modules/.bin/grunt", - "watch": "./node_modules/.bin/grunt watch", - "karma": "./node_modules/.bin/karma start" + "test": "npx tape ./test/utils-test.js", + "build": "npx grunt", + "watch": "npx grunt watch", + "karma": "npx karma start" }, "engines": { "node": "> 0.10.0" diff --git a/src/Timer.js b/src/Timer.js index 76166be..20418c6 100644 --- a/src/Timer.js +++ b/src/Timer.js @@ -8,6 +8,7 @@ function Timer(element, config) { this.originalConfig = $.extend({}, config); this.totalSeconds = 0; this.intervalId = null; + // A HTML element will have the html() method in jQuery to inject content, this.html = 'html'; if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') { @@ -59,7 +60,9 @@ function Timer(element, config) { Timer.prototype.start = function() { if (this.state !== Constants.TIMER_RUNNING) { utils.setState(this, Constants.TIMER_RUNNING); - this.render(); + if (this.config.hidden !== true) { + this.render(); + } this.intervalId = setInterval(utils.intervalHandler.bind(null, this), this.config.updateFrequency); } }; @@ -85,6 +88,7 @@ Timer.prototype.resume = function() { Timer.prototype.remove = function() { clearInterval(this.intervalId); + utils.setState(this, Constants.TIMER_REMOVED); $(this.element).data(Constants.PLUGIN_NAME, null); $(this.element).data('seconds', null); }; diff --git a/src/constants.js b/src/constants.js index 31a63fd..c94f6de 100644 --- a/src/constants.js +++ b/src/constants.js @@ -2,5 +2,7 @@ var Constants = { PLUGIN_NAME: 'timer', TIMER_RUNNING: 'running', TIMER_PAUSED: 'paused', + TIMER_STOPPED: 'stopped', + TIMER_REMOVED: 'removed', DAYINSECONDS: 86400 }; diff --git a/src/utils.js b/src/utils.js index 04c64f3..9d6fbc2 100644 --- a/src/utils.js +++ b/src/utils.js @@ -228,27 +228,30 @@ function intervalHandler(timerInstance) { if (timerInstance.config.countdown) { timerInstance.totalSeconds = timerInstance.config.duration - timerInstance.totalSeconds; + timerInstance.render(); + if (timerInstance.totalSeconds === 0) { clearInterval(timerInstance.intervalId); setState(timerInstance, Constants.TIMER_STOPPED); timerInstance.config.callback(); $(timerInstance.element).data('seconds'); } - - timerInstance.render(); return; } timerInstance.render(); + if (!timerInstance.config.duration) { return; } // If the timer was called with a duration parameter, - // run the callback if duration is complete + // run the callback if duration is complete or total seconds is more than duration // and remove the duration if `repeat` is not requested + if (timerInstance.totalSeconds > 0 && - timerInstance.totalSeconds % timerInstance.config.duration === 0) { + (timerInstance.totalSeconds % timerInstance.config.duration === 0 || + timerInstance.totalSeconds > timerInstance.config.duration )) { if (timerInstance.config.callback) { timerInstance.config.callback(); } diff --git a/test/timedFuncSpec.js b/test/timedFuncSpec.js index 62bac94..d242514 100644 --- a/test/timedFuncSpec.js +++ b/test/timedFuncSpec.js @@ -1,4 +1,4 @@ -describe('Timed functions', function () { +describe('Timed functions', function() { beforeEach(function() { setFixtures('
'); }); @@ -6,38 +6,38 @@ describe('Timed functions', function () { // Start a timer in a DIV describe('Start a timer in a DIV', function() { var timeElapsed = 0; - beforeEach(function (done) { - $('#timer').timer(); + beforeEach(function(done) { + $('#timer').timer(); setTimeout(done, 1000); }); - it('Should be true if the async call has completed', function () { + it('Should be true if the async call has completed', function() { expect($('#timer').data('seconds')).toEqual(1); }); }); // Start timer in an INPUT element describe('Start a timer in a INPUT element', function() { - beforeEach(function (done) { + beforeEach(function(done) { setFixtures(''); - $('#inputTimer').timer(); + $('#inputTimer').timer(); setTimeout(done, 1000); }); - it('Should be true if the async call has completed', function () { + it('Should be true if the async call has completed', function() { expect($('#inputTimer').data('seconds')).toEqual(1); }); }); // Pause a timer describe('Pause a running timer', function() { - beforeEach(function (done) { + beforeEach(function(done) { $('#timer').timer(); - $('#timer').timer('pause'); + $('#timer').timer('pause'); setTimeout(done, 1000); }); - it('Should be true if the async call has completed', function () { + it('Should be true if the async call has completed', function() { // Even when we wait for a second, the time elapsed shouldnt change // as we had immediately paused the timer after starting it expect($('#timer').data('seconds')).toEqual(0); @@ -46,14 +46,14 @@ describe('Timed functions', function () { // Pause and Resume a timer describe('Pause and Resume a timer', function() { - beforeEach(function (done) { + beforeEach(function(done) { $('#timer').timer(); $('#timer').timer('pause'); expect($('#timer').data('state')).toBe('paused'); setTimeout(done, 1000); }); - it('Should be true if the async call has completed', function () { + it('Should be true if the async call has completed', function() { $('#timer').timer('resume'); expect($('#timer').data('state')).toBe('running'); }); @@ -62,7 +62,7 @@ describe('Timed functions', function () { // Execute a function after a set time describe('Execute a function after a set time', function() { var flag; - beforeEach(function (done) { + beforeEach(function(done) { $('#timer').timer({ callback: function() { flag = true; @@ -72,7 +72,7 @@ describe('Timed functions', function () { setTimeout(done, 1000); }); - it('Should call a function after the provided duration is complete', function () { + it('Should call a function after the provided duration is complete', function() { expect(flag).toBe(true); }); }); @@ -80,7 +80,7 @@ describe('Timed functions', function () { // Execute a function after a set time describe('Execute a function after a set time provided in pretty syntax', function() { var flag; - beforeEach(function (done) { + beforeEach(function(done) { $('#timer').timer({ callback: function() { flag = true; @@ -91,7 +91,7 @@ describe('Timed functions', function () { setTimeout(done, 1000); }); - it('Should call a function after the provided duration is complete', function () { + it('Should call a function after the provided duration is complete', function() { expect(flag).toBe(true); expect($('#timer')).toContainText('1:40 min'); });