jTimers

by Андрей Иванов

HTML

<!--Задача:
Создать кнопку, при нажатии которой будет создан новый блок таймера, реализующий функции запуска, паузы и остановки. Максимальное количество блоков таймера 20.
Реализованный код не является образцовым, и содержит некоторые фрагменты, применение которых оправдано только желанием потренироваться и не соответствует принятой практике. Напр. (!count) ? (self.trigger('timerStart')):{}; вместо if/else.
Программный таймер один, синхронно изменяющий значения всех таймерных блоков (вместо создания уникального программного таймера для каждого блока в отдельности, для снижения нагрузки; таким образом погрешность каждого блока лежит на отрезке [0;1]сек - в зависимости от того, через сколько миллисекунд после очередного такта таймера будет нажата кнопка "Старт").-->

<div class="wrapper ui-widget-content" id="timer0" data-active="false">
    <div class="title">
        <span class="title-string">jTimer</span>
        <span class="close-button"><img src="https://pp.vk.me/c608827/v608827788/92f7/4hUTBJuWrYs.jpg" alt="X" /></span>
    </div>
    <div class="window">
        <span class="minutes">00</span>:<span class="seconds">00</span>
    </div>
    <div class="controls">
        <a href="#" class="start-link">start</a>/<a href="#" class="pause-link">pause</a>/<a href="#" class="stop-link">stop</a>
    </div>    
</div>

<button id="waiter">Create new timer</button>

CSS

.wrapper {
    position: fixed;
    width: 130px;
    height: 85px;
    border: 1px solid #969696;
    display: none;
    -ms-filter:"progid:DXImageTransform.Microsoft.alpha(Opacity=85)";
    filter: alpha(opacity=85);
    -moz-opacity:0.85;
    -khtml-opacity: 0.85;
    opacity: 0.85;
}
.title-string {
    padding-left: 10px;
    padding-top: 0px;
    padding-bottom: 0px;
    vertical-align: middle;
}
.close-button {
    position: absolute;
    right: 0px;
}
.controls {
    background-color: #4F4F4F;
    font-size: 70%; color: #969696;
    text-align: center;
}
.controls a {
    color: #969696;
}
.title {
    width: 130px; height: 20px;
    color: #A5D19C;
    font-size: 80%; font-weight: bold;
    background: #4c4c4c; /* Old browsers */
background: -moz-linear-gradient(top, #4c4c4c 0%, #595959 12%, #666666 32%, #474747 55%, #2c2c2c 67%, #111111 80%, #1c1c1c 91%, #2b2b2b 97%, #131313 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(12%,#595959), color-stop(32%,#666666), color-stop(55%,#474747), color-stop(67%,#2c2c2c), color-stop(80%,#111111), color-stop(91%,#1c1c1c), color-stop(97%,#2b2b2b), color-stop(100%,#131313)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #4c4c4c 0%,#595959 12%,#666666 32%,#474747 55%,#2c2c2c 67%,#111111 80%,#1c1c1c 91%,#2b2b2b 97%,#131313 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #4c4c4c 0%,#595959 12%,#666666 32%,#474747 55%,#2c2c2c 67%,#111111 80%,#1c1c1c 91%,#2b2b2b 97%,#131313 100%); /* Opera 11.10+ */
background: -ms-linear-gradient(top, #4c4c4c 0%,#595959 12%,#666666 32%,#474747 55%,#2c2c2c 67%,#111111 80%,#1c1c1c 91%,#2b2b2b 97%,#131313 100%); /* IE10+ */
background: linear-gradient(to bottom, #4c4c4c 0%,#595959 12%,#666666 32%,#474747 55%,#2c2c2c 67%,#111111 80%,#1c1c1c 91%,#2b2b2b 97%,#131313 100%); /* W3C */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c',...

JavaScript

$(function() {
    var count = 0, MAX_TIMERS = 20, sec, min, coreTimer;
    self = $(this);
    
    $( "#waiter" ).on("click", function(){
        if (count <= MAX_TIMERS) {
            (!count) ? (self.trigger('timerStart')):{};
            var last = $( "#timer0").clone().attr("id","timer" + ++count).css("display","block").draggable().insertAfter( "#waiter" );
            last.find(".close-button").on("click", function(){
                $.when( last.fadeOut() ).then(function(){
                    last.remove(); count--;
                    if (!count) clearTimeout(coreTimer);
                })
            });
            last.find(".start-link").on("click", function(){
                last.attr("data-active","true");
                return false;
            });
            last.find(".pause-link").on("click", function() {
                last.attr("data-active","false");
                return false;
            });
            last.find(".stop-link").on("click", function() {
                last.attr("data-active","false");
                last.find(".minutes").text("00");
                                    last.find(".seconds").text("00");
                return false;
            });
        }
    });
    //end of button event
    //begin of timer event
    
    $(this).on('timerStart', function(){
    coreTimer = setInterval(function(){
        $(".wrapper").each(function(){
            if ($(this).attr("data-active") == "true") {
                secStr = $(this).find(".seconds");
                sec = parseInt(secStr.text());
                if (sec < 59) secStr.text(sec<9 ? "0"+ ++sec : ++sec)
                else {
                    secStr.text("00");
                    minStr = $(this).find(".minutes");
                    min = parseInt(minStr.text());
                    if (min < 59) minStr.text(min<9 ? "0"+ ++min : ++min)
                    else {
                        $(this).attr("data-active","false");
                    }
          ...