четверг, 19 сентября 2013 г.

Всплывающий календарь с указанием даты и времени


Файл index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <link href="style/default/index.css" rel="stylesheet" type="text/css">
    <script data-main="js/index.js" src="js/lib/require/require.js"></script>
<title>Календарь</title>
</head>
<body>
<div id="grid">

    <div id="sessions-start-time" class="calendar">
        <input class="calendar-input" type="text" size="19" name="session-time" value="12/02/2012 23:12:30" data-date="12.02.2012.23.12.30"><span class="calendar-button"><i class="calendar-icon"></i></span>
        <div class="calendar-datepicker hidden">
            <div class="calendar-datepicker-border-arrow"></div>
            <table class="period-selector">
                <tr>
                    <td class="prev-month"><div class="arrow left"></div></td>
                    <td class="select-month"><select>
                                                                        <option value="1" selected="selected">Январь</option>
                                                                        <option value="2">Февраль</option>
                                                                        <option value="3">Март</option>
                                                                        <option value="4">Апрель</option>
                                                                        <option value="5">Май</option>
                                                                        <option value="6">Июнь</option>
                                                                        <option value="7">Июль</option>
                                                                        <option value="8">Август</option>
                                                                        <option value="9">Сентябрь</option>
                                                                        <option value="10">Октябрь</option>
                                                                        <option value="11">Ноябрь</option>
                                                                        <option value="12">Декабрь</option>
                                                                    </select></th>
                    <td class="next-month"><div class="arrow right"></div></td>
                    <td class="prev-year"><div class="arrow left"></div></td>
                    <td class="select-year"><select>
                                                    <option value="2014">2014</option>
                                                    <option value="2013">2013</option>
                                                    <option value="2012" selected="selected">2012</option>
                                                    <option value="2011">2011</option>
                                                    <option value="2010">2010</option>
                                                    <option value="1970">1970</option>
                                                </select></th>
                    <td class="next-year"><div class="arrow right"></div></td>
                </tr>
            </table>
            <table class="day-selector">
                <thead>
                    <tr>
                        <th class="dow">Пн</th>
                        <th class="dow">Вт</th>
                        <th class="dow">Ср</th>
                        <th class="dow">Чт</th>
                        <th class="dow">Пт</th>
                        <th class="dow">Сб</th>
                        <th class="dow">Вс</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td class="day old">29</td>
                        <td class="day old">30</td>
                        <td class="day old">31</td>
                        <td class="day">1</td>
                        <td class="day">2</td>
                        <td class="day">3</td>
                        <td class="day">4</td>
                    </tr>
                    <tr>
                        <td class="day">5</td>
                        <td class="day">6</td>
                        <td class="day">7</td>
                        <td class="day">8</td>
                        <td class="day">9</td>
                        <td class="day">10</td>
                        <td class="day">11</td>
                    </tr>
                    <tr>
                        <td class="day active">12</td>
                        <td class="day">13</td>
                        <td class="day">14</td>
                        <td class="day">15</td>
                        <td class="day">16</td>
                        <td class="day">17</td>
                        <td class="day">18</td>
                    </tr>
                    <tr>
                        <td class="day">19</td>
                        <td class="day">20</td>
                        <td class="day">21</td>
                        <td class="day">22</td>
                        <td class="day">23</td>
                        <td class="day">24</td>
                        <td class="day">25</td>
                    </tr>
                    <tr>
                        <td class="day">26</td>
                        <td class="day">27</td>
                        <td class="day">28</td>
                        <td class="day">29</td>
                        <td class="day new">1</td>
                        <td class="day new">2</td>
                        <td class="day new">3</td>
                    </tr>
                    <tr>
                        <td class="day new">4</td>
                        <td class="day new">5</td>
                        <td class="day new">6</td>
                        <td class="day new">7</td>
                        <td class="day new">8</td>
                        <td class="day new">9</td>
                        <td class="day new">10</td>
                    </tr>
                </tbody>
            </table>
            <table class="time-selector">
                <tr>
                    <td>&nbsp;</td>
                    <td class="next-hour"><div class="arrow up"></div></td>
                    <td>&nbsp;</td>
                    <td class="next-minute"><div class="arrow up"></div></td>
                    <td>&nbsp;</td>
                    <td class="next-second"><div class="arrow up"></div></td>
                    <td>&nbsp;</td>
                </tr>
                <tr>
                    <td><div class="clock"></div></td>
                    <td class="select-hour"><select>
                                <option value="0" selected="selected">00</option>
                                <option value="1">01</option>
                                <option value="2">02</option>
                                <option value="3">03</option>
                                <option value="4">04</option>
                                <option value="5">05</option>
                                <option value="6">06</option>
                                <option value="7">07</option>
                                <option value="8">08</option>
                                <option value="9">09</option>
                                <option value="10">10</option>
                                <option value="11">11</option>
                                <option value="12">12</option>
                                <option value="13">13</option>
                                <option value="14">14</option>
                                <option value="15">15</option>
                                <option value="16">16</option>
                                <option value="17">17</option>
                                <option value="18">18</option>
                                <option value="19">19</option>
                                <option value="20">20</option>
                                <option value="21">21</option>
                                <option value="22">22</option>
                                <option value="23">23</option>
                            </select></td>
                    <td>ч</td>
                    <td class="select-minute"><select>
                        <option value="0" selected="selected">00</option>
                        <option value="1">01</option>
                        <option value="2">02</option>
                        <option value="3">03</option>
                        <option value="4">04</option>
                        <option value="5">05</option>
                        <option value="6">06</option>
                        <option value="7">07</option>
                        <option value="8">08</option>
                        <option value="9">09</option>
                        <option value="10">10</option>
                        <option value="11">11</option>
                        <option value="12">12</option>
                        <option value="13">13</option>
                        <option value="14">14</option>
                        <option value="15">15</option>
                        <option value="16">16</option>
                        <option value="17">17</option>
                        <option value="18">18</option>
                        <option value="19">19</option>
                        <option value="20">20</option>
                        <option value="21">21</option>
                        <option value="22">22</option>
                        <option value="23">23</option>
                        <option value="24">24</option>
                        <option value="25">25</option>
                        <option value="26">26</option>
                        <option value="27">27</option>
                        <option value="28">28</option>
                        <option value="29">29</option>
                        <option value="30">30</option>
                        <option value="31">31</option>
                        <option value="32">32</option>
                        <option value="33">33</option>
                        <option value="34">34</option>
                        <option value="35">35</option>
                        <option value="36">36</option>
                        <option value="37">37</option>
                        <option value="38">38</option>
                        <option value="39">39</option>
                        <option value="40">40</option>
                        <option value="41">41</option>
                        <option value="42">42</option>
                        <option value="43">43</option>
                        <option value="44">44</option>
                        <option value="45">45</option>
                        <option value="46">46</option>
                        <option value="47">47</option>
                        <option value="48">48</option>
                        <option value="49">49</option>
                        <option value="50">50</option>
                        <option value="51">51</option>
                        <option value="52">52</option>
                        <option value="53">53</option>
                        <option value="54">54</option>
                        <option value="55">55</option>
                        <option value="56">56</option>
                        <option value="57">57</option>
                        <option value="58">58</option>
                        <option value="59">59</option>
                    </select></td>
                    <td>м</td>
                    <td class="select-second"><select>
                        <option value="0" selected="selected">00</option>
                        <option value="1">01</option>
                        <option value="2">02</option>
                        <option value="3">03</option>
                        <option value="4">04</option>
                        <option value="5">05</option>
                        <option value="6">06</option>
                        <option value="7">07</option>
                        <option value="8">08</option>
                        <option value="9">09</option>
                        <option value="10">10</option>
                        <option value="11">11</option>
                        <option value="12">12</option>
                        <option value="13">13</option>
                        <option value="14">14</option>
                        <option value="15">15</option>
                        <option value="16">16</option>
                        <option value="17">17</option>
                        <option value="18">18</option>
                        <option value="19">19</option>
                        <option value="20">20</option>
                        <option value="21">21</option>
                        <option value="22">22</option>
                        <option value="23">23</option>
                        <option value="24">24</option>
                        <option value="25">25</option>
                        <option value="26">26</option>
                        <option value="27">27</option>
                        <option value="28">28</option>
                        <option value="29">29</option>
                        <option value="30">30</option>
                        <option value="31">31</option>
                        <option value="32">32</option>
                        <option value="33">33</option>
                        <option value="34">34</option>
                        <option value="35">35</option>
                        <option value="36">36</option>
                        <option value="37">37</option>
                        <option value="38">38</option>
                        <option value="39">39</option>
                        <option value="40">40</option>
                        <option value="41">41</option>
                        <option value="42">42</option>
                        <option value="43">43</option>
                        <option value="44">44</option>
                        <option value="45">45</option>
                        <option value="46">46</option>
                        <option value="47">47</option>
                        <option value="48">48</option>
                        <option value="49">49</option>
                        <option value="50">50</option>
                        <option value="51">51</option>
                        <option value="52">52</option>
                        <option value="53">53</option>
                        <option value="54">54</option>
                        <option value="55">55</option>
                        <option value="56">56</option>
                        <option value="57">57</option>
                        <option value="58">58</option>
                        <option value="59">59</option>
                    </select></td>
                    <td>с</td>
                </tr>
                <tr>
                    <td>&nbsp;</td>
                    <td class="prev-hour"><div class="arrow down"></div></td>
                    <td>&nbsp;</td>
                    <td class="prev-minute"><div class="arrow down"></div></td>
                    <td>&nbsp;</td>
                    <td class="prev-second"><div class="arrow down"></div></td>
                    <td>&nbsp;</td>
                </tr>
                <tr>
                    <td class="now" colspan="7">Сейчас</td>
                </tr>
            </table>
        </div>
    </div>
   
</div>
</body>
</html>

Файл index.css

/******************************************************************\
  Reset CSS
\******************************************************************/

html {
    font-size: 100.01%;  /* Bug fix for Opera */
}

body {
    width: 1000px;
    position: relative; /* Bug fix for IE 6 */
    margin: 0;
    padding: 0;
    margin-left: auto;
    margin-right: auto;
    text-align: center;
    font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif;
    font-size: 62.5%;
    background-color: #ffffff;
    color: #333333;
}

div,h1,h2,h3,h4,h5,h6,p,img,ol,ul,li,table,th,td,form,select,fieldset {
    margin: 0;
    padding: 0;
    text-align: left;
}

article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {
    display: block;
}

ol,ul {
    list-style: none;
}

table {
    border: 0px; /* border="0" */
    border-collapse: collapse; /* cellspacing="0" */
}

table tr th {
    padding: 0px; /* cellpadding="0" */
}

table tr td {
padding: 0px; /* cellpadding="0" */
}

input,textarea,select {
    font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif;
    font-size: 1.0em;
    color: #333333;
}

textarea {
    resize: none;
}

a img {
    border: none;
}

a {
    color: #333333;
    text-decoration: underline;
}

a:link {
    color: #333333;
    text-decoration: underline;
}

a:visited {
    color: #333333;
    text-decoration: underline;
}

a:hover {
    color: #333333;
    text-decoration: none;
}

a:active {
    color: #333333;
    text-decoration: none;
}

a:active,a:focus,img {
    outline: 0; /* Firefox anchor border remove */
}

/*****************************************************************\
  Common classes
\*****************************************************************/

.hidden {
    display: none !important;
}

/******************************************************************\
  Grid
\******************************************************************/

 div#grid {
     font-size: 1.2em;
 }

/******************************************************************\
  Selectbox
\******************************************************************/

div.selectbox {
    position: relative;
    zoom: 1;
    display: inline-block;
    *display: inline;
    _display: inline;
    width: 100%;
    height: 20px;
    line-height: 20px;
    background-color: #ffffff;
    border: 1px solid #bbbbbb;
    vertical-align: top;
    cursor: default;
}

div.selectbox div.selectbox-label {
    overflow: hidden;
    height: 20px;
    margin-right: 20px;
    text-indent: 5px;
}

div.selectbox div.selectbox-button {
    position: absolute;
    width: 20px;
    height: 20px;
    top: 0;
    right: 0;
    background: url(img/icons/selectbox.png) no-repeat 0 0;
}

div.selectbox ul.selectbox-dropbox {
    display: none;
    overflow: hidden;
    overflow-y: auto;
    position: absolute;
    z-index: 100;
    width: 100%;
    max-height: 150px;
    _height: expression(this.scrollHeight > 150 ? '150px' : 'auto');
    line-height: 1.5em;
    margin-left: -1px;
    background-color: #ffffff;
    border: 1px solid #bbbbbb;
}

div.selectbox ul.selectbox-dropbox li.selectbox-option {
    position: relative;
    display: block;
    padding: 1px 5px;
}

div.selectbox ul.selectbox-dropbox li.hover {
    background: #40c0c0;
    color: #ffffff;
}

/******************************************************************\
  Calendar
\******************************************************************/

div.calendar {
    position: relative;
    display: inline-block;
    *display: inline;
    _display: inline;
}

div.calendar input.calendar-input {
    display: inline-block;
    *display: inline;
    _display: inline;
    width: 150px;
    height: 20px;
    line-height: 20px;
    padding: 4px 6px;
    border: 1px solid #cccccc;
    font-size: 1.2em;
    vertical-align: top;
}

div.calendar span.calendar-button {
    display: inline-block;
    *display: inline;
    _display: inline;
    height: 20px;
    line-height: 20px;
    width: 16px;
    margin-left: -1px;
    padding: 4px 5px;
    background-color: #eeeeee;
    border: 1px solid #cccccc;
    vertical-align: top;
    font-size: 1.2em;
    text-align: center;
    cursor: pointer;
}

div.calendar span.calendar-button i.calendar-icon {
    display: block;
    width: 11px;
    height: 12px;
    margin-top: 3px;
    margin-left: auto;
    margin-right: auto;
    background: url(img/icons/calendar.gif) no-repeat 0 0;
}

div.calendar div.calendar-datepicker {
    position: absolute;
    margin-top: 6px;
    padding: 4px;
    background-color: #ffffff;
    border: 1px solid #cccccc;
    z-index: 1000;
}

div.calendar div.calendar-datepicker-border-arrow {
    width: 14px;
    height: 7px;
    margin-top: -11px;
    margin-left: auto;
    margin-right: auto;
    background: url(img/icons/border-arrow.gif) no-repeat 0 0;
}

div.calendar div.calendar-datepicker table {
    margin-top: 4px;
}

div.calendar div.calendar-datepicker table tr td select  {
    height: 20px;
    line-height: 20px;
    border: 1px solid #cccccc;
}

div.calendar div.selectbox {
    height: 18px;
    line-height: 18px;
}

div.calendar div.selectbox div.selectbox-label {
    height: 18px;
    margin-right: 5px;
    text-indent: 5px;
}

div.calendar div.selectbox div.selectbox-button {
    width: 17px;
    height: 18px;
    background: url(img/icons/selectbox-small.png) no-repeat 0 0;
}

div.calendar div.selectbox ul.selectbox-dropbox li.selectbox-option {
    padding: 1px 5px;
}

div.calendar div.calendar-datepicker table tr td.select-month select {
    width: 80px;
}

div.calendar div.calendar-datepicker table tr td.select-month div.selectbox {
    width: 80px;
}

div.calendar div.calendar-datepicker table tr td.select-month div.selectbox ul.selectbox-dropbox {
    width: 80px;
}

div.calendar div.calendar-datepicker table tr td.select-year select {
    width: 55px;
}

div.calendar div.calendar-datepicker table tr td.select-year div.selectbox {
    width: 55px;
}

div.calendar div.calendar-datepicker table tr td.select-year div.selectbox ul.selectbox-dropbox {
    width: 55px;
}

div.calendar div.calendar-datepicker table tr td.select-hour select,
div.calendar div.calendar-datepicker table tr td.select-minute select,
div.calendar div.calendar-datepicker table tr td.select-second select {
    width: 40px;
}

div.calendar div.calendar-datepicker table tr td.select-hour div.selectbox,
div.calendar div.calendar-datepicker table tr td.select-minute div.selectbox,
div.calendar div.calendar-datepicker table tr td.select-second div.selectbox {
    width: 40px;
}

div.calendar div.calendar-datepicker table tr td.select-hour div.selectbox ul.selectbox-dropbox,
div.calendar div.calendar-datepicker table tr td.select-minute div.selectbox ul.selectbox-dropbox,
div.calendar div.calendar-datepicker table tr td.select-second div.selectbox ul.selectbox-dropbox {
    width: 40px;
}

div.calendar div.calendar-datepicker table tr td div.arrow {
    width: 10px;
    height: 10px;
    background: url(img/icons/arrows.gif) no-repeat;
}

div.calendar div.calendar-datepicker table tr td div.left {
    background-position: 0px 0px;
}

div.calendar div.calendar-datepicker table tr td div.right {
    background-position: -10px 0px;
}

div.calendar div.calendar-datepicker table tr td div.up {
    background-position: -20px 0px;
}

div.calendar div.calendar-datepicker table tr td div.down {
    background-position: -30px 0px;
}

div.calendar div.calendar-datepicker table tr td div.clock {
    width: 12px;
    height: 12px;
    background: url(img/icons/clock.gif) no-repeat 0px 0px;
}

div.calendar div.calendar-datepicker table.period-selector tr td {
    width: 20px;
    height: 20px;
    padding: 4px 5px;
    text-align: center;
}

div.calendar div.calendar-datepicker table.period-selector tr td.prev-month,
div.calendar div.calendar-datepicker table.period-selector tr td.next-month,
div.calendar div.calendar-datepicker table.period-selector tr td.prev-year,
div.calendar div.calendar-datepicker table.period-selector tr td.next-year {
    cursor: pointer;
}

div.calendar div.calendar-datepicker table.period-selector tr td.prev-month:hover,
div.calendar div.calendar-datepicker table.period-selector tr td.next-month:hover,
div.calendar div.calendar-datepicker table.period-selector tr td.prev-year:hover,
div.calendar div.calendar-datepicker table.period-selector tr td.next-year:hover {
    background-color: #eeeeee;
}

div.calendar div.calendar-datepicker table.day-selector {
    margin-left: auto;
    margin-right: auto;
}

div.calendar div.calendar-datepicker table.day-selector tr th,
div.calendar div.calendar-datepicker table.day-selector tr td {
    width: 24px;
    height: 24px;
    padding: 4px 5px;
    text-align: center;
}

div.calendar div.calendar-datepicker table.day-selector tr td.day {
    cursor: pointer;
}

div.calendar div.calendar-datepicker table.day-selector tr td.day:hover {
    background-color: #eeeeee;
}

div.calendar div.calendar-datepicker table.day-selector tr td.old,
div.calendar div.calendar-datepicker table.day-selector tr td.new {
    color: #999999;
}

div.calendar div.calendar-datepicker table.day-selector tr td.active,
div.calendar div.calendar-datepicker table.day-selector tr td.active:hover {
    background-color: #40c0c0;
    color: #ffffff;
}

div.calendar div.calendar-datepicker table.time-selector tr td {
    width: 24px;
    height: 24px;
    padding: 4px 5px;
    text-align: center;
    font-weight: bold;
}

div.calendar div.calendar-datepicker table tr td.select-hour,
div.calendar div.calendar-datepicker table tr td.select-minute,
div.calendar div.calendar-datepicker table tr td.select-second {
    font-weight: normal;
}

div.calendar div.calendar-datepicker table.time-selector tr td.next-hour,
div.calendar div.calendar-datepicker table.time-selector tr td.prev-hour,
div.calendar div.calendar-datepicker table.time-selector tr td.next-minute,
div.calendar div.calendar-datepicker table.time-selector tr td.prev-minute,
div.calendar div.calendar-datepicker table.time-selector tr td.next-second,
div.calendar div.calendar-datepicker table.time-selector tr td.prev-second,
div.calendar div.calendar-datepicker table.time-selector tr td.now {
    cursor: pointer;
}

div.calendar div.calendar-datepicker table.time-selector tr td.next-hour:hover,
div.calendar div.calendar-datepicker table.time-selector tr td.prev-hour:hover,
div.calendar div.calendar-datepicker table.time-selector tr td.next-minute:hover,
div.calendar div.calendar-datepicker table.time-selector tr td.prev-minute:hover,
div.calendar div.calendar-datepicker table.time-selector tr td.next-second:hover,
div.calendar div.calendar-datepicker table.time-selector tr td.prev-second:hover,
div.calendar div.calendar-datepicker table.time-selector tr td.now:hover {
    background-color: #eeeeee;
}

div.calendar div.calendar-datepicker table.time-selector tr td div {
    margin-left: auto;
    margin-right: auto;
}

Файл index.js

require.config({
    paths: {
        jquery: 'lib/jquery/jquery'
    }
});

require(
    [
        'jquery'
    ]
    , function(
        $
    ) {
   
        // Всплывающий календарь
        function createCalendar (calendarId) {
       
            // Вспомогательные функции
            function isLeapYear (year) {
                return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0));
            }
           
            function getDaysInMonth (year, month) {
                return [31, (isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month - 1];
            }
           
            function addLeadingZero (value) {
                return value < 10 ? '0' + value : value;
            }
           
            function removeLeadingZero (value) {
                return parseInt(value, 10);
            }
           
            function normalizeYear (year) {
                return year < 1000 ? year += 1900 : year;
            }
           
            function normalizeMonth (month) {
                return month + 1;
            }
           
            function getCurrentDate () {
                return new Date();
            }

            function getCurrentYear () {
                return normalizeYear(getCurrentDate().getYear());
            }

            function getCurrentMonth () {
                return normalizeMonth(getCurrentDate().getMonth());
            }

            function getCurrentDay () {
                return getCurrentDate().getDate();
            }
           
            function getCurrentHour () {
                return getCurrentDate().getHours();
            }
           
            function getCurrentMinute () {
                return getCurrentDate().getMinutes();
            }
           
            function getCurrentSecond () {
                return getCurrentDate().getSeconds();
            }

            function getNumberOfDayOfTheWeek (year, month, day) {
                var dayNumber = new Date(year, month - 1, day).getDay();
                return dayNumber === 0 ? dayNumber = 7 : dayNumber;
            }
           
            function createYearSelect (name, startYear, endYear) {
                var select = '<select name="' + name + '">'
                    , i;
                for (i = endYear; i >= startYear; i--) {
                    select += '<option value="' + i + '">' + i + '</option>';
                }
                select += '</select>'
                return select;
            }
           
            function createMonthSelect (name) {
                var select = '<select name="' + name + '">'
                    , months = [
                          'Январь', 'Февраль', 'Март'
                        , 'Апрель', 'Май', 'Июнь'
                        , 'Июль', 'Август', 'Сентябрь'
                        , 'Октябрь', 'Ноябрь', 'Декабрь'
                      ]
                    , len = months.length
                    , i;
                for (i = 0; i < len; i++) {
                    select += '<option value="' + (i+1) + '">' + months[i] + '</option>';
                }
                select += '</select>'
                return select;
            }

            function createTimeSelect (name, startTime, endTime) {
                var len = endTime - startTime
                    , select = '<select name="' + name + '">'
                    , i;
                for (i = 0; i <= len; i++) {
                    select += '<option value="' + i + '">' + addLeadingZero (i) + '</option>';
                }
                select += '</select>'
                return select;
            }
           
            // Изменение дизайна элемента <select>  
            function replaceStandartSelect (select) {

                var selectOptions = select.find('option')
                    , selectOptionsLength = selectOptions.length
                    , selectedOptionText = ''
                    , selectbox  = ''
                    , selectboxOptions = ''
                    , i;

                selectOptions.eq(0).prop('selected', true);

                for (i = 0; i < selectOptionsLength; i++) {
                    if (selectOptions.eq(i).is(':selected')) {
                        selectedOptionText = selectOptions.eq(i).text();
                    }
                    selectboxOptions += '<li class="selectbox-option">' + selectOptions.eq(i).text() + '</li>';
                }

                selectbox = '<div class="selectbox">'
                              + '<div class="selectbox-label">' + selectedOptionText + '</div>'
                              + '<div class="selectbox-button"></div>'
                              + '<ul class="selectbox-dropbox">'
                              + selectboxOptions
                              + '</ul>'
                              + '</div>';

                select.hide();

                selectbox = $(selectbox);
               
                selectbox.insertAfter(select);

                selectbox.find('.selectbox-label, .selectbox-button').click(function(event){
                    event.stopPropagation();
                    if (selectbox.find('.selectbox-dropbox').is(':visible')) {
                        selectbox.find('.selectbox-dropbox').hide();
                    } else {
                        selectbox.find('.selectbox-dropbox').show();
                    }
                });

                selectbox.find('.selectbox-option').hover(function(){
                    selectbox.find('.selectbox-option').removeClass('hover');
                    $(this).addClass('hover');
                });

                selectbox.find('.selectbox-dropbox .selectbox-option').click(function(event){
                    event.stopPropagation();
                    selectOptions.removeAttr('selected');
                    selectOptions.eq($(this).index()).attr('selected', true);
                    selectbox.find('.selectbox-label').text($(this).text());
                    selectbox.find('.selectbox-dropbox').hide();
                    // Установить дату и время
                    if (selectbox.parent().hasClass('select-month')) {
                        selectedMonth = $(this).index() + 1;
                        $('#' + calendarId + ' .day-selector tr td').removeClass('active');
                        selectedDay = 1;
                        renderDays();
                        $('#' + calendarId + ' .day-selector tr td.day').not('.old').not('.new').eq(0).addClass('active');
                        updateInputValue();
                    } else if (selectbox.parent().hasClass('select-year')) {
                        selectedYear = parseInt($(this).text(), 10);
                        $('#' + calendarId + ' .day-selector tr td').removeClass('active');
                        selectedDay = 1;
                        renderDays();
                        $('#' + calendarId + ' .day-selector tr td.day').not('.old').not('.new').eq(0).addClass('active');
                        updateInputValue();
                    } else if (selectbox.parent().hasClass('select-hour')) {
                        selectedHour = parseInt($(this).text(), 10);
                        updateInputValue();
                    } else if (selectbox.parent().hasClass('select-minute')) {
                        selectedMinute = parseInt($(this).text(), 10);
                        updateInputValue();
                    } else if (selectbox.parent().hasClass('select-second')) {
                        selectedSecond = parseInt($(this).text(), 10);
                        updateInputValue();
                    }
                });

            }

            // Переменные, хранящие текущую выбранную дату и время
            var selectedYear = getCurrentYear()
                , selectedMonth = getCurrentMonth()
                , selectedDay = getCurrentDay()
                , selectedHour = getCurrentHour()
                , selectedMinute = getCurrentMinute()
                , selectedSecond = getCurrentSecond();
               
            // Одноразовая запись первоначальных значений текущей даты и времени после загрузки страницы
            updateInputValue();
           
            // Функция обновления данных в input и data-date          
            function updateInputValue () {
                var value = addLeadingZero(selectedDay) + '/'
                              + addLeadingZero(selectedMonth) + '/'
                                                     + selectedYear + ' '
                              + addLeadingZero(selectedHour) + ':'
                              + addLeadingZero(selectedMinute) + ':'
                              + addLeadingZero(selectedSecond)
                             
                    , date = addLeadingZero(selectedDay) + '.'
                             + addLeadingZero(selectedMonth) + '.'
                                                    + selectedYear + '.'
                             + addLeadingZero(selectedHour) + '.'
                             + addLeadingZero(selectedMinute) + '.'
                             + addLeadingZero(selectedSecond);
                   
                $('#' + calendarId + ' .calendar-input').val(value).data('date', date);
            }
           
            // Функция отрисовки всего календаря
            function renderCalendar () {
           
                var date = $('#' + calendarId + ' .calendar-input').data('date').split('.');
               
                selectedYear = removeLeadingZero(date[2]);
                selectedMonth = removeLeadingZero(date[1]);
                selectedDay = removeLeadingZero(date[0]);
                selectedHour = removeLeadingZero(date[3]);
                selectedMinute = removeLeadingZero(date[4]);
                selectedSecond = removeLeadingZero(date[5]);
               
                var calendar = '<div class="calendar-datepicker">'
                                  + '<div class="calendar-datepicker-border-arrow"></div>'
                                  + '<table class="period-selector">'
                                  + '<tr>'
                                  + '<td class="prev-month"><div class="arrow left"></div></td>'
                                  + '<td class="select-month">' + createMonthSelect ('months') + '</th>'
                                  + '<td class="next-month"><div class="arrow right"></div></td>'
                                  + '<td class="prev-year"><div class="arrow left"></div></td>'
                                  + '<td class="select-year">' + createYearSelect ('years', 1970, getCurrentYear() + 1) + '</th>'
                                  + '<td class="next-year"><div class="arrow right"></div></td>'
                                  + '</tr>'
                                  + '</table>'
                                  + '<table class="day-selector">'
                                  + '<thead>'
                                  + '<tr>'
                                  + '<th class="dow">Пн</th>'
                                  + '<th class="dow">Вт</th>'
                                  + '<th class="dow">Ср</th>'
                                  + '<th class="dow">Чт</th>'
                                  + '<th class="dow">Пт</th>'
                                  + '<th class="dow">Сб</th>'
                                  + '<th class="dow">Вс</th>'
                                  + '</tr>'
                                  + '</thead>'
                                  + '<tbody>'
                                  + '</tbody>'
                                  + '</table>'
                                  + '<table class="time-selector">'
                                  + '<tr>'
                                  + '<td>&nbsp;</td>'
                                  + '<td class="next-hour"><div class="arrow up"></div></td>'
                                  + '<td>&nbsp;</td>'
                                  + '<td class="next-minute"><div class="arrow up"></div></td>'
                                  + '<td>&nbsp;</td>'
                                  + '<td class="next-second"><div class="arrow up"></div></td>'
                                  + '<td>&nbsp;</td>'
                                  + '</tr>'
                                  + '<tr>'
                                  + '<td><div class="clock"></div></td>'
                                  + '<td class="select-hour">' + createTimeSelect ('hours', 0, 23) + '</td>'
                                  + '<td>ч</td>'
                                  + '<td class="select-minute">' + createTimeSelect ('minutes', 0, 59) + '</td>'
                                  + '<td>м</td>'
                                  + '<td class="select-second">' + createTimeSelect ('seconds', 0, 59) + '</td>'
                                  + '<td>с</td>'
                                  + '</tr>'
                                  + '<tr>'
                                  + '<td>&nbsp;</td>'
                                  + '<td class="prev-hour"><div class="arrow down"></div></td>'
                                  + '<td>&nbsp;</td>'
                                  + '<td class="prev-minute"><div class="arrow down"></div></td>'
                                  + '<td>&nbsp;</td>'
                                  + '<td class="prev-second"><div class="arrow down"></div></td>'
                                  + '<td>&nbsp;</td>'
                                  + '</tr>'
                                  + '<tr>'
                                  + '<td class="now" colspan="7">Сейчас</td>'
                                  + '</tr>'
                                  + '</table>'
                                  + '</div>';

                $('#' + calendarId + ' .calendar-datepicker').replaceWith(calendar);
               
                renderDays();
                $('#' + calendarId + ' .day-selector tr td.day').not('.old').not('.new').eq(selectedDay - 1).addClass('active');
               
                $('#' + calendarId + ' .select-month select option').eq(selectedMonth - 1).prop('selected', true);
                $('#' + calendarId + ' .select-year select option[value="' + selectedYear + '"]').prop('selected', true);
                $('#' + calendarId + ' .select-hour select option').eq(selectedHour).prop('selected', true);
                $('#' + calendarId + ' .select-minute select option').eq(selectedMinute).prop('selected', true);
                $('#' + calendarId + ' .select-second select option').eq(selectedSecond).prop('selected', true);
               
                replaceStandartSelect($('#' + calendarId + ' .select-month select'));
                replaceStandartSelect($('#' + calendarId + ' .select-year select'));
                replaceStandartSelect($('#' + calendarId + ' .select-hour select'));
                replaceStandartSelect($('#' + calendarId + ' .select-minute select'));
                replaceStandartSelect($('#' + calendarId + ' .select-second select'));
               
                $('#' + calendarId + ' .select-month .selectbox-option').removeClass('hover');
                $('#' + calendarId + ' .select-year .selectbox-option').removeClass('hover');
                $('#' + calendarId + ' .select-hour .selectbox-option').removeClass('hover');
                $('#' + calendarId + ' .select-minute .selectbox-option').removeClass('hover');
                $('#' + calendarId + ' .select-second .selectbox-option').removeClass('hover');
               
                $('#' + calendarId + ' .select-month .selectbox-option').eq(selectedMonth - 1).addClass('hover');
                $('#' + calendarId + ' .select-year .selectbox-option:contains(' + selectedYear + ')').addClass('hover');
                $('#' + calendarId + ' .select-hour .selectbox-option').eq(selectedHour).addClass('hover');
                $('#' + calendarId + ' .select-minute .selectbox-option').eq(selectedMinute).addClass('hover');
                $('#' + calendarId + ' .select-second .selectbox-option').eq(selectedSecond).addClass('hover');
               
                $('#' + calendarId + ' .select-month .selectbox-label').text($('#' + calendarId + ' .select-month .selectbox-option').eq(selectedMonth - 1).text());
                $('#' + calendarId + ' .select-year .selectbox-label').text(selectedYear);
                $('#' + calendarId + ' .select-hour .selectbox-label').text(addLeadingZero(selectedHour));
                $('#' + calendarId + ' .select-minute .selectbox-label').text(addLeadingZero(selectedMinute));
                $('#' + calendarId + ' .select-second .selectbox-label').text(addLeadingZero(selectedSecond));
               
                // Скрытие селектов при клике по календарю и блокирование закрытия календаря при клике по нему
                $('#' + calendarId + ' .calendar-datepicker').click(function(e){
                    $('#' + calendarId + ' .selectbox-dropbox').hide();
                    e.stopPropagation();
                });
               
                // Функции кликов по стрелкам календаря
                $('#' + calendarId + ' .prev-month').click(function(){
                    if (selectedMonth === 1) {
                        selectedMonth = 12;
                        selectedYear -= 1;
                    } else {
                        selectedMonth -= 1;
                    }
                    selectedDay = 1;
                    renderDays();
                    $('#' + calendarId + ' .day-selector tr td.day').not('.old').not('.new').eq(0).addClass('active');
                    $('#' + calendarId + ' .select-month select option').eq(selectedMonth - 1).prop('selected', true);
                    $('#' + calendarId + ' .select-month .selectbox-option').removeClass('hover');
                    $('#' + calendarId + ' .select-month .selectbox-option').eq(selectedMonth - 1).addClass('hover');
                    $('#' + calendarId + ' .select-month .selectbox-label').text($('#' + calendarId + ' .select-month .selectbox-option').eq(selectedMonth - 1).text());
                    updateInputValue();
                });
               
                $('#' + calendarId + ' .next-month').click(function(){
                    $('#' + calendarId + ' .day-selector tr td').removeClass('active');
                    if (selectedMonth === 12) {
                        selectedMonth = 1;
                        selectedYear += 1;
                    } else {
                        selectedMonth += 1;
                    }
                    selectedDay = 1;
                    renderDays();
                    $('#' + calendarId + ' .day-selector tr td.day').not('.old').not('.new').eq(0).addClass('active');
                    $('#' + calendarId + ' .select-month select option').eq(selectedMonth - 1).prop('selected', true);
                    $('#' + calendarId + ' .select-month .selectbox-option').removeClass('hover');
                    $('#' + calendarId + ' .select-month .selectbox-option').eq(selectedMonth - 1).addClass('hover');
                    $('#' + calendarId + ' .select-month .selectbox-label').text($('#' + calendarId + ' .select-month .selectbox-option').eq(selectedMonth - 1).text());
                    updateInputValue();
                });
               
                $('#' + calendarId + ' .prev-year').click(function(){
                    if (selectedYear === 1970) {
                        selectedYear = 1970;
                    } else {
                        selectedYear -= 1;
                    }
                    selectedDay = 1;
                    renderDays();
                    $('#' + calendarId + ' .day-selector tr td.day').not('.old').not('.new').eq(0).addClass('active');
                    $('#' + calendarId + ' .select-year select option[value="' + selectedYear + '"]').prop('selected', true);
                    $('#' + calendarId + ' .select-year .selectbox-option').removeClass('hover');
                    $('#' + calendarId + ' .select-year .selectbox-option:contains(' + selectedYear + ')').addClass('hover');
                    $('#' + calendarId + ' .select-year .selectbox-label').text(selectedYear);
                    updateInputValue();
                });
               
                $('#' + calendarId + ' .next-year').click(function(){
                    if (selectedYear === getCurrentYear() + 1) {
                        selectedYear === getCurrentYear() + 1;
                    } else {
                        selectedYear += 1;
                    }
                    selectedDay = 1;
                    renderDays();
                    $('#' + calendarId + ' .day-selector tr td.day').not('.old').not('.new').eq(0).addClass('active');
                    $('#' + calendarId + ' .select-year select option[value="' + selectedYear + '"]').prop('selected', true);
                    $('#' + calendarId + ' .select-year .selectbox-option').removeClass('hover');
                    $('#' + calendarId + ' .select-year .selectbox-option:contains(' + selectedYear + ')').addClass('hover');
                    $('#' + calendarId + ' .select-year .selectbox-label').text(selectedYear);
                    updateInputValue();
                });
               
                $('#' + calendarId + ' .prev-hour').click(function(){
                    if (selectedHour === 0) {
                        selectedHour = 23;
                    } else {
                        selectedHour -= 1;
                    }
                    $('#' + calendarId + ' .select-hour select option').eq(selectedHour).prop('selected', true);
                    $('#' + calendarId + ' .select-hour .selectbox-option').removeClass('hover');
                    $('#' + calendarId + ' .select-hour .selectbox-option').eq(selectedHour).addClass('hover');
                    $('#' + calendarId + ' .select-hour .selectbox-label').text(addLeadingZero(selectedHour));
                    updateInputValue();
                });
               
                $('#' + calendarId + ' .next-hour').click(function(){
                    if (selectedHour === 23) {
                        selectedHour = 0;
                    } else {
                        selectedHour += 1;
                    }
                    $('#' + calendarId + ' .select-hour select option').eq(selectedHour).prop('selected', true);
                    $('#' + calendarId + ' .select-hour .selectbox-option').removeClass('hover');
                    $('#' + calendarId + ' .select-hour .selectbox-option').eq(selectedHour).addClass('hover');
                    $('#' + calendarId + ' .select-hour .selectbox-label').text(addLeadingZero(selectedHour));
                    updateInputValue();
                });
               
                $('#' + calendarId + ' .prev-minute').click(function(){
                    if (selectedMinute === 0) {
                        selectedMinute = 59;
                    } else {
                        selectedMinute -= 1;
                    }
                    $('#' + calendarId + ' .select-minute select option').eq(selectedMinute).prop('selected', true);
                    $('#' + calendarId + ' .select-minute .selectbox-option').removeClass('hover');
                    $('#' + calendarId + ' .select-minute .selectbox-option').eq(selectedMinute).addClass('hover');
                    $('#' + calendarId + ' .select-minute .selectbox-label').text(addLeadingZero(selectedMinute));
                    updateInputValue();
                });
               
                $('#' + calendarId + ' .next-minute').click(function(){
                    if (selectedMinute === 59) {
                        selectedMinute = 0;
                    } else {
                        selectedMinute += 1;
                    }
                    $('#' + calendarId + ' .select-minute select option').eq(selectedMinute).prop('selected', true);
                    $('#' + calendarId + ' .select-minute .selectbox-option').removeClass('hover');
                    $('#' + calendarId + ' .select-minute .selectbox-option').eq(selectedMinute).addClass('hover');
                    $('#' + calendarId + ' .select-minute .selectbox-label').text(addLeadingZero(selectedMinute));
                    updateInputValue();
                });
               
                $('#' + calendarId + ' .prev-second').click(function(){
                    if (selectedSecond === 0) {
                        selectedSecond = 59;
                    } else {
                        selectedSecond -= 1;
                    }
                    $('#' + calendarId + ' .select-second select option').eq(selectedSecond).prop('selected', true);
                    $('#' + calendarId + ' .select-second .selectbox-option').removeClass('hover');
                    $('#' + calendarId + ' .select-second .selectbox-option').eq(selectedSecond).addClass('hover');
                    $('#' + calendarId + ' .select-second .selectbox-label').text(addLeadingZero(selectedSecond));
                    updateInputValue();
                });
               
                $('#' + calendarId + ' .next-second').click(function(){
                    if (selectedSecond === 59) {
                        selectedSecond = 0;
                    } else {
                        selectedSecond += 1;
                    }
                    $('#' + calendarId + ' .select-second select option').eq(selectedSecond).prop('selected', true);
                    $('#' + calendarId + ' .select-second .selectbox-option').removeClass('hover');
                    $('#' + calendarId + ' .select-second .selectbox-option').eq(selectedSecond).addClass('hover');
                    $('#' + calendarId + ' .select-second .selectbox-label').text(addLeadingZero(selectedSecond));
                    updateInputValue();
                });
               
                $('#' + calendarId + ' .now').click(function(){
                    selectedYear = getCurrentYear();
                    selectedMonth = getCurrentMonth();
                    selectedDay = getCurrentDay();
                    selectedHour = getCurrentHour();
                    selectedMinute = getCurrentMinute();
                    selectedSecond = getCurrentSecond();
                    renderDays();
                    $('#' + calendarId + ' .day-selector tr td.day').not('.old').not('.new').eq(selectedDay - 1).addClass('active');
                    $('#' + calendarId + ' .select-month select option').eq(selectedMonth - 1).prop('selected', true);
                    $('#' + calendarId + ' .select-year select option[value="' + selectedYear + '"]').prop('selected', true);
                    $('#' + calendarId + ' .select-hour select option').eq(selectedHour).prop('selected', true);
                    $('#' + calendarId + ' .select-minute select option').eq(selectedMinute).prop('selected', true);
                    $('#' + calendarId + ' .select-second select option').eq(selectedSecond).prop('selected', true);

                    $('#' + calendarId + ' .select-month .selectbox-option').removeClass('hover');
                    $('#' + calendarId + ' .select-year .selectbox-option').removeClass('hover');
                    $('#' + calendarId + ' .select-hour .selectbox-option').removeClass('hover');
                    $('#' + calendarId + ' .select-minute .selectbox-option').removeClass('hover');
                    $('#' + calendarId + ' .select-second .selectbox-option').removeClass('hover');
                   
                    $('#' + calendarId + ' .select-month .selectbox-option').eq(selectedMonth - 1).addClass('hover');
                    $('#' + calendarId + ' .select-year .selectbox-option:contains(' + selectedYear + ')').addClass('hover');
                    $('#' + calendarId + ' .select-hour .selectbox-option').eq(selectedHour).addClass('hover');
                    $('#' + calendarId + ' .select-minute .selectbox-option').eq(selectedMinute).addClass('hover');
                    $('#' + calendarId + ' .select-second .selectbox-option').eq(selectedSecond).addClass('hover');
                   
                    $('#' + calendarId + ' .select-month .selectbox-label').text($('#' + calendarId + ' .select-month .selectbox-option').eq(selectedMonth - 1).text());
                    $('#' + calendarId + ' .select-year .selectbox-label').text(selectedYear);
                    $('#' + calendarId + ' .select-hour .selectbox-label').text(addLeadingZero(selectedHour));
                    $('#' + calendarId + ' .select-minute .selectbox-label').text(addLeadingZero(selectedMinute));
                    $('#' + calendarId + ' .select-second .selectbox-label').text(addLeadingZero(selectedSecond));

                    updateInputValue();
                });
               
                // Функции изменения значений селектов календаря
                $('#' + calendarId + ' .select-month select').change(function(){
                    selectedMonth = parseInt($(this).find('option:selected').val(), 10);
                    $('#' + calendarId + ' .day-selector tr td').removeClass('active');
                    selectedDay = 1;
                    renderDays();
                    $('#' + calendarId + ' .day-selector tr td.day').not('.old').not('.new').eq(0).addClass('active');
                    updateInputValue();
                });
               
                $('#' + calendarId + ' .select-year select').change(function(){
                    selectedYear = parseInt($(this).find('option:selected').val(), 10);
                    $('#' + calendarId + ' .day-selector tr td').removeClass('active');
                    selectedDay = 1;
                    renderDays();
                    $('#' + calendarId + ' .day-selector tr td.day').not('.old').not('.new').eq(0).addClass('active');
                    updateInputValue();
                });
               
                $('#' + calendarId + ' .select-hour select').change(function(){
                    selectedHour = parseInt($(this).find('option:selected').val(), 10);
                    updateInputValue();
                });
               
                $('#' + calendarId + ' .select-minute select').change(function(){
                    selectedMinute = parseInt($(this).find('option:selected').val(), 10);
                    updateInputValue();
                });
               
                $('#' + calendarId + ' .select-second select').change(function(){
                    selectedSecond = parseInt($(this).find('option:selected').val(), 10);
                    updateInputValue();
                });
               
            }
           
            // Одноразовая отрисовка и скрытие календаря после загрузки страницы
            renderCalendar();
            $('#' + calendarId + ' .calendar-datepicker').addClass('hidden');
           
            // Функция отрисовки дней календаря
            function renderDays () {
           
                // Получение значений предыдущего, следующего месяца и года относительно текущей выбранной даты
                var previousMonth = selectedMonth
                    , nextMonth = selectedMonth
                    , previousMonthYear = selectedYear
                    , nextMonthYear = selectedYear;
               
                if (selectedMonth === 12) {
                    previousMonth -= 1;
                    nextMonth = 1;
                    nextMonthYear += 1;
                } else if (selectedMonth === 1) {
                    previousMonth = 12;
                    nextMonth += 1;
                    previousMonthYear -= 1;
                } else {
                    previousMonth -= 1;
                    nextMonth += 1;
                }
               
                // Получение числа дней в предыдущем и текущем месяце
                var daysInPreviousMonth = getDaysInMonth(previousMonthYear, previousMonth)
                    , daysInSelectedMonth = getDaysInMonth(selectedYear, selectedMonth);
               
                // Общее число ячеек в таблице дней календаря и общее число дней в неделе
                var totalDayCells = 42
                    , daysInTheWeek = 7;

                // Определение числа дней в календаре, стоящих перед днями текущего месяца, по порядковому номеру последнего дня в последней неделе
                var daysNumberForPreviousMonth = getNumberOfDayOfTheWeek(previousMonthYear, previousMonth, daysInPreviousMonth);
               
                // Определение числа дней в календаре, стоящих после дней текущего месяца
                var daysNumberForNextMonth = totalDayCells - daysInSelectedMonth - daysNumberForPreviousMonth;

                // Создание таблицы дней для календаря
                var i
                    , daysArray = [];

                for (i = daysNumberForPreviousMonth; i > 0; i--, daysInPreviousMonth--) {
                    daysArray.push('<td class="day old">' + daysInPreviousMonth + '</td>');
                }
               
                daysArray.reverse();
               
                for (i = 1; i <= daysInSelectedMonth; i++) {
                    daysArray.push('<td class="day">' + i + '</td>');
                }
               
                for (i = 1; i <= daysNumberForNextMonth; i++) {
                    daysArray.push('<td class="day new">' + i + '</td>');
                }
               
                var days = '<tbody>'
               
                var tableRowsNumber = Math.round(totalDayCells / daysInTheWeek);

                var j
                    , cellCounterStart = 0
                    , cellCounterStop = daysInTheWeek
                    , row = '';

                for (i = 0; i < tableRowsNumber; i++) {
                    for (j = cellCounterStart; j < cellCounterStop; j++) {
                        row += daysArray[j];
                    }
                    days += '<tr>' + row + '</tr>';
                    row = '';
                    cellCounterStart = j;
                    cellCounterStop = j + daysInTheWeek;
                }
               
                days += '</tbody>';
               
                // Вставка дней в календарь
                $('#' + calendarId + ' .day-selector tbody').replaceWith(days);
               
                // Навешивание функции кликов по дням календаря. Функция определена ниже
                $('#' + calendarId + ' .day').click(dayClick);
             
            }
           
            // Функция кликов по дням календаря. Вызывается выше во время отрисовки дней календаря
            // Выделено в отдельную функцию, чтобы срабатывали клики на днях при использовании кода $(document).on('click', function(){...});
            // написанного ниже
            function dayClick () {
                var day = $(this);
                selectedDay = parseInt(day.text(), 10);
                if (day.hasClass('old')) {
                    if (selectedMonth === 1) {
                        selectedMonth = 12;
                        if (selectedYear === 1970) {
                            selectedYear = 1970;
                        } else {
                            selectedYear -= 1;
                        }
                    } else {
                        selectedMonth -= 1;
                    }
                    renderDays();
                    $('#' + calendarId + ' .day-selector tr td.day').not('.old').not('.new').eq(selectedDay - 1).addClass('active');
                } else if (day.hasClass('new')) {
                    if (selectedMonth === 12) {
                        selectedMonth = 1;
                        if (selectedYear === getCurrentYear() + 1) {
                            selectedYear === getCurrentYear() + 1;
                        } else {
                            selectedYear += 1;
                        }
                    } else {
                        selectedMonth += 1;
                    }
                    renderDays();
                    $('#' + calendarId + ' .day-selector tr td.day').not('.old').not('.new').eq(selectedDay - 1).addClass('active');
                } else {
                    $('#' + calendarId + ' .day-selector tr td').removeClass('active');
                    day.addClass('active');
                }
                $('#' + calendarId + ' .select-month select option').eq(selectedMonth - 1).prop('selected', true);
                $('#' + calendarId + ' .select-year select option[value="' + selectedYear + '"]').prop('selected', true);
               
                $('#' + calendarId + ' .select-month .selectbox-option').removeClass('hover');
                $('#' + calendarId + ' .select-month .selectbox-option').eq(selectedMonth - 1).addClass('hover');
                $('#' + calendarId + ' .select-month .selectbox-label').text($('#' + calendarId + ' .select-month .selectbox-option').eq(selectedMonth - 1).text());

                $('#' + calendarId + ' .select-year .selectbox-option').removeClass('hover');
                $('#' + calendarId + ' .select-year .selectbox-option:contains(' + selectedYear + ')').addClass('hover');
                $('#' + calendarId + ' .select-year .selectbox-label').text(selectedYear);
               
                updateInputValue();
            }

            // Функции показа и скрытия календаря
            $('#' + calendarId + ' .calendar-button').click(function(e){
                var calendar = $(this).next();
                if (calendar.hasClass('hidden')) {
                    calendar.removeClass('hidden');
                    renderCalendar();
                } else {
                    calendar.addClass('hidden');
                }
                $('#' + calendarId + ' .selectbox-dropbox').hide();
                e.stopPropagation();
            });

            $(document).on('click', function(){
                var calendar = $('#' + calendarId + ' .calendar-datepicker');
                if (!calendar.hasClass('hidden')) {
                    calendar.addClass('hidden');
                }
                $('#' + calendarId + ' .selectbox-dropbox').hide();
            });
           
            // Функции удаления символов, недопустимых при вводе даты в поле input календаря
           
            // Удаление всех символов из времени начала сессии кроме цифр, пробела, символов "/", ":"
            // и замена значения даты, если данные введены правильно
            $('#' + calendarId + ' .calendar-input').keyup(function(){
                deleteNotDateSymbols(this);
                replaceDataDate(this);
            }).change(function(){
                deleteNotDateSymbols(this);
                replaceDataDate(this);
            });

            // Удаление всех символов, кроме цифр, пробела, символов / и :
            function deleteNotDateSymbols (input) {
                var value = $(input).val();
                value = value.replace(/[^0-9\s\/:]/gi, '');
                value = $.trim(value);
                $(input).val(value);
            }

            // Замена значения даты, если данные введены в правильном формате
            function replaceDataDate (input) {
                var value = $(input).val()
                    , date
                    , day
                    , month
                    , year
                    , hour
                    , minute
                    , second;
               
                if ( value.length === 19 && (/(\d{2}\/\d{2}\/\d{4}\s\d{2}:\d{2}:\d{2}){1}/gi).test(value) ) {
                    value = value.replace(/[\s\/:]/gi, '.');
                    date = value.split('.');
                    day = removeLeadingZero(date[0]);
                    month = removeLeadingZero(date[1]);
                    year = removeLeadingZero(date[2]);
                    hour = removeLeadingZero(date[3]);
                    minute = removeLeadingZero(date[4]);
                    second = removeLeadingZero(date[5]);
                    if (
                              (day > 0 && day < getDaysInMonth(year, month) + 1)
                        && (month > 0 && month < 13)
                        && (year > 1969 && year < getCurrentYear() + 2)
                        && (hour > -1 && hour < 24)
                        && (minute > -1 && minute < 60)
                        && (second > -1 && second < 60)
                    ) {
                        $(input).data('date', value);
                    }
                }
            }
           
        }
           
        // Создание календаря для времени начала сессии
        createCalendar('sessions-start-time');    
       
    }

);

понедельник, 16 сентября 2013 г.

Обфускация JavaScript

В статье собраны всем известные методы и предельно извращенные. 


Первый способ


Он всем известен — обфускация минимизаторами такими как JS Packer, JSmin, YUI Compressor, Closure compiler или можно просто пугуглить «JavaScript Obfuscator» и найдется ещё сто штук разных обфускаторов.
Они превращают существующий код
function MyClass(){
    this.foo = function(argument1, argument2){
        var addedArgs = parseInt(argument1)+parseInt(argument2);
        return addedArgs;
    }
    var anonymousInnerFunction = function(){
        // do stuff here!
    }
}

В какой-то такой вид:
function MyClass(){this.foo=function(c,b){var d=parseInt(c)+parseInt(b);return d};var a=function(){}};

Или такой:
var _0xd799=["\x66\x6F\x6F"];function MyClass(){this[_0xd799[0]]=function (_0xefcax2,_0xefcax3){var _0xefcax4=parseInt(_0xefcax2)+parseInt(_0xefcax3);return _0xefcax4;} ;var _0xefcax5=function (){} ;} ;

Или вот такой:
eval(function(p,a,c,k,e,d){e=function(c){return c};if(!''.replace(/^/,String)){while(c--){d[c]=k[c]||c}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('4 0="3 5!";9 2(1){6(1+"\\7"+0)}2("8");',10,10,'a|msg|MsgBox|Hello|var|World|alert|n|OK|function'.split('|'),0,{}))

Но ничего не стоит его восстановить с помощью jsbeautifier.org либо просто убрать eval и получить исходный код, многое потеряем, но смысл кода восстановим. Ну и с первого взгляда мы видим, что перед нами JavaScript.

Все это были цветочки под катом жесткие методы обфускации.

Способ второй


Изменение кода до неузнаваемости, который превратит наш мизерный скрипт:
alert(0)

В такой Braifuck-подобный вид:
([][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()[(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]])(+[])

Или вот в такой:
゚ω゚ノ= /`m´)ノ ~┻━┻ //*´∇`*/ ['_']; o=(゚ー゚) =_=3; c=(゚Θ゚) =(゚ー゚)-(゚ー゚); (゚Д゚) =(゚Θ゚)= (o^_^o)/ (o^_^o);(゚Д゚)={゚Θ゚: '_' ,゚ω゚ノ : ((゚ω゚ノ==3) +'_') [゚Θ゚] ,゚ー゚ノ :(゚ω゚ノ+ '_')[o^_^o -(゚Θ゚)] ,゚Д゚ノ:((゚ー゚==3) +'_')[゚ー゚] }; (゚Д゚) [゚Θ゚] =((゚ω゚ノ==3) +'_') [c^_^o];(゚Д゚) ['c'] = ((゚Д゚)+'_') [ (゚ー゚)+(゚ー゚)-(゚Θ゚) ];(゚Д゚) ['o'] = ((゚Д゚)+'_') [゚Θ゚];(゚o゚)=(゚Д゚) ['c']+(゚Д゚) ['o']+(゚ω゚ノ +'_')[゚Θ゚]+ ((゚ω゚ノ==3) +'_') [゚ー゚] + ((゚Д゚) +'_') [(゚ー゚)+(゚ー゚)]+ ((゚ー゚==3) +'_') [゚Θ゚]+((゚ー゚==3) +'_') [(゚ー゚) - (゚Θ゚)]+(゚Д゚) ['c']+((゚Д゚)+'_') [(゚ー゚)+(゚ー゚)]+ (゚Д゚) ['o']+((゚ー゚==3) +'_') [゚Θ゚];(゚Д゚) ['_'] =(o^_^o) [゚o゚] [゚o゚];(゚ε゚)=((゚ー゚==3) +'_') [゚Θ゚]+ (゚Д゚) .゚Д゚ノ+((゚Д゚)+'_') [(゚ー゚) + (゚ー゚)]+((゚ー゚==3) +'_') [o^_^o -゚Θ゚]+((゚ー゚==3) +'_') [゚Θ゚]+ (゚ω゚ノ +'_') [゚Θ゚]; (゚ー゚)+=(゚Θ゚); (゚Д゚)[゚ε゚]='\\'; (゚Д゚).゚Θ゚ノ=(゚Д゚+ ゚ー゚)[o^_^o -(゚Θ゚)];(o゚ー゚o)=(゚ω゚ノ +'_')[c^_^o];(゚Д゚) [゚o゚]='\"';(゚Д゚) ['_'] ( (゚Д゚) ['_'] (゚ε゚+(゚Д゚)[゚o゚]+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (c^_^o)+ (゚Д゚)[゚ε゚]+((o^_^o) +(o^_^o))+ (c^_^o)+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (゚Θ゚)+ (゚Д゚)[゚o゚]) (゚Θ゚)) ('_');

Тут с первого раза и не скажешь, что перед нами работающий JavaScript.
Тулза делающая код вида один, код вида два и jjencode

Объяснение некоторых моментов способа два

Пример:
($=[$=[]][(__=!$+$)[_=-~-~-~$]+({}+$)[_/_]+
($$=($_=!''+$)[_/_]+$_[+$])])()[__[_/_]+__
[_+~$]+$_[_]+$$](_/_)

Имеем какое-то месево символов, но на самом деле в нем скрывается alert(1), но ведь там нет ни одной строки и ни одного символа, откуда это?!

Будем разбираться:
$=[] это пустой массив
$=[$=[]] это массив с ссылкой на массив
В переменной $ будет 0.
Теперь у нас есть 0 и мы можем ссылаться. Делаем __ = "false" через (__ = !$ + $ )
Далее _ = -~-~-~$
Оператор ~ в JavaScript означает -(N+1) поэтому -~ = +1
Если $ = 0 тогда -~-~-~$ = 3
Получаем: _ = 3

Таким образом _/_ = 3/3 = 1

(__ = !$ + $ )[ _ = -~-~-~$]
("false")[_]
("false")[3]
"false"[3] = s


({} + $)[_/_]
(" object")[_/_]
(" object")[1]
" object"[1] = o


$$ = ( $_ = !'' + $)[_/_]
$$ = ( "true")[1]
"true"[1] = r

$_[+$] = "true"[0] = t

$_ = "true"null
$$ = rt 


($$ = ( $_ = !'' + $)[_/_] + $_[+$] ))

!'' = "true"
$_ = (true)
$_[1] = r
$_[0] = t
$$ = rt


Таким образом первая строка станет ($ = [] ["s" + "o"+ "r"+ "t" ] )()
($=[]["sort"])()

Идем дельше
[__[_/_]+__[_+~$]+$_[_]+$$](_/_)

$ = 0
_ = 3
__ = "false"
$_ = "true"
$$ = "rt"


Строка превращается…
[__[_/_]+__[_+~$]+$_[_]+$$](_/_)

Превращается…
[__[1] + __[3 + -1] + $_[3] + $$)(1);

Превращается…
["false"[1] + "false"[3 + -1 ] + "true"[3] + "rt"] (1)

Превращается в
[ "a" + "l" + "e" + "r" + "t" ](1)

В конце мы получаем ($=[]["sort"])()["alert"](1)

Разобьём по частям
a = [] // Создает массив
b = a["sort"] // Получает ссылку на метод sort
c = b() // Выполяет метод вне контекста массива для получения ссылки на window
d = c["alert"] // Получает ссылку на window.alert
d(1) // Выполняет window.alert с аргументом 1


Итог: window["alert"](1)

Это, конечно, искусственный пример и никакой из обфускаторов так не сможет.

Способ третий


Первый способ делал код похожим на JavaScript, второй совершенно не похожим, а третий сделает код вообще невидимым.

Готовых решений не видел, концепт, подсмотренный с одного из видео JS конференций.
Код будет состоять из двух частей: видимая часть — можно использовать что-нибудь описанное выше для её обфускации и невидимая часть.
Если с видимой все ясно, то секрет невидимой вот в чем: Имеющийся «плохой код» (иначе зачем его прятать) мы пропускаем через наш обфускатор-изчезатор, который превращает видимый скрипт в невидимый, то есть в строку состоящую из знаков табуляции (бит 1) и пробелов (бит 0).
В итоге мы получим в 8 раз больше кода чем имели. Видимая часть будет декодировать невидимую часть и её выполнять: декодирует биты в число, а число переделает в символ String.fromCharCode() ну а дальше eval

В конце получается что-то такое (невидимую часть не обязательно скрывать в элементе)
decodeAndEval(document.getElementById("evilCode").innerHTML);
<div id="evilCode">

</div>

обфускатор-изчезатор

По мотивам:

Очень оригинальный скрипт на JavaScript

゚ω゚ノ= /`m´)ノ ~┻━┻ //*´∇`*/ ['_']; o=(゚ー゚) =_=3; c=(゚Θ゚) =(゚ー゚)-(゚ー゚); (゚Д゚) =(゚Θ゚)= (o^_^o)/ (o^_^o);(゚Д゚)={゚Θ゚: '_' ,゚ω゚ノ : ((゚ω゚ノ==3) +'_') [゚Θ゚] ,゚ー゚ノ :(゚ω゚ノ+ '_')[o^_^o -(゚Θ゚)] ,゚Д゚ノ:((゚ー゚==3) +'_')[゚ー゚] }; (゚Д゚) [゚Θ゚] =((゚ω゚ノ==3) +'_') [c^_^o];(゚Д゚) ['c'] = ((゚Д゚)+'_') [ (゚ー゚)+(゚ー゚)-(゚Θ゚) ];(゚Д゚) ['o'] = ((゚Д゚)+'_') [゚Θ゚];(゚o゚)=(゚Д゚) ['c']+(゚Д゚) ['o']+(゚ω゚ノ +'_')[゚Θ゚]+ ((゚ω゚ノ==3) +'_') [゚ー゚] + ((゚Д゚) +'_') [(゚ー゚)+(゚ー゚)]+ ((゚ー゚==3) +'_') [゚Θ゚]+((゚ー゚==3) +'_') [(゚ー゚) - (゚Θ゚)]+(゚Д゚) ['c']+((゚Д゚)+'_') [(゚ー゚)+(゚ー゚)]+ (゚Д゚) ['o']+((゚ー゚==3) +'_') [゚Θ゚];(゚Д゚) ['_'] =(o^_^o) [゚o゚] [゚o゚];(゚ε゚)=((゚ー゚==3) +'_') [゚Θ゚]+ (゚Д゚) .゚Д゚ノ+((゚Д゚)+'_') [(゚ー゚) + (゚ー゚)]+((゚ー゚==3) +'_') [o^_^o -゚Θ゚]+((゚ー゚==3) +'_') [゚Θ゚]+ (゚ω゚ノ +'_') [゚Θ゚]; (゚ー゚)+=(゚Θ゚); (゚Д゚)[゚ε゚]='\\'; (゚Д゚).゚Θ゚ノ=(゚Д゚+ ゚ー゚)[o^_^o -(゚Θ゚)];(o゚ー゚o)=(゚ω゚ノ +'_')[c^_^o];(゚Д゚) [゚o゚]='\"';(゚Д゚) ['_'] ( (゚Д゚) ['_'] (゚ε゚+(゚Д゚)[゚o゚]+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚ー゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (o^_^o)+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚Θ゚)+ ((゚ー゚) + (o^_^o))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) - (゚Θ゚))+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚ー゚)+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (c^_^o)+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚Θ゚)+ ((゚ー゚) + (o^_^o))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚Θ゚)+ ((o^_^o) +(o^_^o))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) - (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚ー゚)+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) - (゚Θ゚))+ (o^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (c^_^o)+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (o^_^o)+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+((゚ー゚) + (o^_^o))+ ((゚ー゚) + (o^_^o))+ (゚Д゚)[゚ε゚]+(゚ー゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (゚Θ゚)+ (゚Д゚)[゚o゚]) (゚Θ゚)) ('_');

вторник, 10 сентября 2013 г.

Random Number - Формирование случайного числа из диапазона

function randomNumber (m,n) {
    m = parseInt(m);
    n = parseInt(n);
    return Math.floor( Math.random() * (n - m + 1) ) + m;
}
var random_number = randomNumber (1,999);

Raphael Pie Chart Library

Файл index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <style type="text/css">
        #canvas {
            width: 640px;
            height: 480px;
            margin: 0 auto;
            text-align: left;
            border: 1px solid #000000;
        }
    </style>
    <script data-main="js/index.js" src="js/lib/require/require.js"></script>
<title>Chart Lib</title>
</head>
<body>
    <div id="canvas"></div>
</body>
</html>

Файл index.js

require.config({
      paths: {
          jquery: 'lib/jquery/jquery'
        , raphael: 'lib/raphael/raphael'
        , raphaelPieChart: 'lib/raphael/raphael.piechart'
      }
    , shim: {
        raphaelPieChart: {
            deps: ['raphael']
        }
      }
});

require(
    [
          'jquery'
        , 'raphael'
        , 'raphaelPieChart'
    ]
    , function(
          $
        , Raphael
    ) {
        // Тестируем работу библиотеки Pie Chart.
        var paper = Raphael("canvas", 640, 480);
        paper.piechart(
              320, 240, 100
            , [25, 25, 25, 25]
            , {
                  colors: ['#cc0000', '#00ff00', '#000000', '#aa00aa']
                , labels: ['one', 'two', 'three', 'four']
                , labelsTextColor: '#ff0000'
              }
        );
    }
);

Файл raphael.piechart.js

// Библиотека Pie Chart.

// Параметры функции piechart(cx, cy, r, values, options):
// cx (number) - координата x центра круговой диаграммы.
// cy (number) - координата y вцентра круговой диаграммы.
// r (number) - радиус круговой диаграммы.
// values (array) - массив значений, по которым строится круговая диаграмма. Пример: [55, 20, 13, 32, 5, 1, 2].
// options (object) - список опций, использующихся для модификации круговой диаграммы.

// Возможные параметры options:
// options = {
//     stroke (string) - цвет линни окружности круговой диаграммы в формате "#ffffff".
//     strokeWidth (number) (по умолчанию равно 1) - толщина линии окружности круговой диаграммы.
//     colors (array) - массив цветов в формате "#ffffff", каждый из которых будет использоваться для закрашивания секторов круговой диаграмме.
//     labels (array) - массив, содержащий строки, которые будут использованы в качестве подписей к секторам круговой диаграммы.
// Ниже следующие опции будут работать, если опция labels была определена.
//     labelsTextColor (string) (по умолчанию равно "#000000") - цвет текста подписи в формате "#ffffff".
//     labelsMark (string) (по умолчанию равно "circle") - символ, используемый в качестве маркера слева от текста подписи. Данный символ имеет  тот же цвет, что и соответствующий сектор на круговой диаграмме.
//     labelsPosition (string) (по умолчанию равно "east") - место положения подписей рядом с круговой диаграммой. Для данной опции возможны следующие значения: "north", "south", "east", "west".
// }

// Пример использования библиотеки:
// paper.piechart(
//       320, 240, 100
//     , [25, 25, 25, 25]
//     , {
//            colors: ['#cc0000', '#00ff00', '#000000', '#aa00aa']
//          , labels: ['one', 'two', 'three', 'four']
//          , labelsTextColor: '#ff0000'
//       }
// );

;(function(){

    // Главная функция библиотеки.

    function PieChart (paper, cx, cy, r, values, options) {

        // Опции библиотеки.

        options = options || {};

        // Опции задающие цвета секторов и толщину разделительных линний на круговой диаграмме.
       
        var   stroke = options.stroke || "#ffffff"
            , strokeWidth = options.strokeWidth || 1
            , colors = options.colors || (function () {
                var   hues = [0.6, 0.2, 0.05, 0.1333, 0.75, 0]
                    , colors = []
                    , i;
                // Допустимо только 10 цветов. Для большего числа цветов необходимо изменить 10 и массив hues.
                for (i = 0; i < 10; i++) {
                    if (i < hues.length) {
                        colors.push('hsb(' + hues[i] + ', 0.75, 0.75)');
                    } else {
                        colors.push('hsb(' + hues[i - hues.length] + ', 1, 0.5)');
                    }
                }
                return colors;
              })();

        // Опции для отрисовки секторов круговой диаграммы

        var   len = values.length
            , total = 0
            , mangle
            , angle = 0
            , sectorPath
            , i;

        for (i = 0; i < len; i++) {
            total += values[i];
        }

        // Функция вычисления пути для отрисовки каждого сектора на круговой диаграмме.

        function sector(cx, cy, r, startAngle, endAngle) {
            var   rad = Math.PI / 180
                , x1 = cx + r * Math.cos(-startAngle * rad)
                , x2 = cx + r * Math.cos(-endAngle * rad)
                , y1 = cy + r * Math.sin(-startAngle * rad)
                , y2 = cy + r * Math.sin(-endAngle * rad)
                , path = [
                      'M', cx, cy // MoveTo: cx, cy
                    , 'L', x1, y1 // LineTo: x1, y1
                    , 'A', r, r, 0, +(Math.abs(endAngle - startAngle) > 180), 1, x2, y2 // Elliptical Arc: rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y
                    , 'z' // Close path
                ];
            return path.join();
        }

        // Цикл отрисовки секторов на круговой диаграмме.
       
        for (i = 0; i < len; i++) {
            mangle = angle - 360 * values[i] / total / 2;
            if (!i) {
                angle = 90 - mangle;
                mangle = angle - 360 * values[i] / total / 2;
            }
            sectorPath = sector(cx, cy, r, angle, angle -= 360 * values[i] / total);
            paper.path(sectorPath).attr({
                  'fill': colors[i]
                , 'stroke': stroke
                , 'stroke-width': strokeWidth
                , 'stroke-linejoin': 'round'
            });
        }

        // Опции задающие цвета, метки и положение подписей рядом с кгруговой диаграммой.

        var   labels = options.labels || values
            , labelsTextColor = options.labelsTextColor || '#000000'
            , labelsMark = options.labelsMark || 'circle'
            , labelsPosition = options.labelsPosition || 'east'
            , labelsSet = paper.set();

        labelsMark = labelsMark.toLowerCase();
        labelsPosition = labelsPosition.toLowerCase();

        // Цикл отрисовки подписей рядом с круговой диаграммой.
       
        var   x = cx + r + r / 5
            , y = cy
            , h = y + 10
            , labelMark
            , labelText;

        for (i = 0; i < len; i++) {
            labelMark = paper[labelsMark](x + 5, h, 5).attr({fill: colors[i], stroke: 'none'});
            labelText = paper.text(x + 20, h, labels[i]).attr({fill: labelsTextColor, 'text-anchor': 'start'});
            h += labelText.getBBox().height * 1.2;
            labelsSet.push(paper.set());
            labelsSet[i].push(labelMark).push(labelText);
        }

        var   labelsBoundingBox = labelsSet.getBBox()
            , translateToPosition = {
                  east: [0, -labelsBoundingBox.height / 2]
                , west: [-labelsBoundingBox.width - 2 * r - 20, -labelsBoundingBox.height / 2]
                , north: [-r - labelsBoundingBox.width / 2, -r - labelsBoundingBox.height - 10]
                , south: [-r - labelsBoundingBox.width / 2, r + 10]
              }[labelsPosition];
               
        labelsSet.translate.apply(labelsSet, translateToPosition);

    }

    // Добавляем библиотеку Pie Chart в состав библиотеки Raphael JS.

    Raphael.fn.piechart = function (cx, cy, r, values, options) {
        return new PieChart(this, cx, cy, r, values, options);
    };

})();

понедельник, 2 сентября 2013 г.

jQuery Firefox offsetX offestY undefined

В Firefox значения offestX и offsetY равны undefined.
Для устранения этого недостатка нужно написать следующий код.

$('div').click(function(event){
    var offestX = event.pageX - $(event.target).offset().left;
    var offsetY = event.pageY - $(event.target).offset().top;
    console.log(offsetX);
    console.log(offsetY);
});