Страницы

Поиск по вопросам

суббота, 7 декабря 2019 г.

Предварительно выбранный автор в админке Django

#python #django


Имеется вот такая модель:
class NewsContent(models.Model):
    title = models.CharField(_('Title'), max_length=255)
    author = models.ForeignKey(User, blank=True, null=True, verbose_name = _('Author'))
    content = models.TextField(_('Content'))

Нужно, чтобы в админке django при создании нового новостного контента по умолчанию
был выбран текущий пользователь в качестве автора.    


Ответы

Ответ 1



Отвечаю на свой вопрос сам. В недрах django нашёл код куда можно внедриться без особых проблем, да и с доступом к request. Получилось очень даже красиво. class NewsContentAdmin(admin.ModelAdmin): def formfield_for_dbfield(self, db_field, **kwargs): field = super(NewsContentAdmin, self).formfield_for_dbfield(db_field, **kwargs) if db_field.name == "author": field.initial = kwargs["request"].user.id return field

Ответ 2



Создайте кастомную форму для своей модели и переопределите admin view для редактирования данной модели, в этом view словите request.user, который и передадите в форму как initial data (или передадите параметром в конструктор формы). Возможно, для такой мелкой задачи это решение тяжеловато, зато это документированная возможность (не костыль) и при переезде на более новые версии фреймворка у вас ничего "не поедет". Как создать custom admin forms/views для cвоих моделей, подробно описано в официальной документации.

Ответ 3



Поскольку ответов нет, могу предложить или сделать все это на js, или сделать так что бы пользователь мог выбрать только себя: если будет вот так author = models.ForeignKey(User, blank=False, null=False, verbose_name = _('Author')) то в admins.py class NewsContentAdmin(admin.ModelAdmin): """Ващ класс для админки для данной модели """ def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == "author": kwargs["queryset"] = Users.objects.filter(pk = request.user.pk) return super(NewsContentAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

Ответ 4



Вот здесь пишут про js решение.

Ответ 5



Переопределяем метод save_model класса ModelAdmin и добавляем там пользователя. В вашем случае будет выглядеть примерно так: class NewsContentAdmin(admin.ModelAdmin): def save_model(self, request, obj, form, change): if not change: obj.author = request.user obj.save()

Как определить координаты элемента

#javascript #html #css


Как определить координаты элемента относительно всего документа и относительно видимой
части окна в javascript? После события onscroll координаты элемента относительно видимой
части окна должны измениться. Заранее благодарен.
    


Ответы

Ответ 1



Координаты относительно видимой части документа — .getBoundingClientRect(). Этот метод также возвращает ширину и высоту элемента. document.querySelector('button').onclick = function() { var c = this.getBoundingClientRect() alert('top:' + c.top + ' left: ' + c.left +''); } button { margin: 500px 0 0; } span { display:block; }
scroll down
Координаты относительно начала документа — значение .scrollTop / .scrollLeft + .getBoundingClientRect().top / .getBoundingClientRect().left document.querySelector('button').onclick = function() { var c = this.getBoundingClientRect(), scrolltop = document.body.scrollTop + c.top, scrollleft = document.body.scrollLeft + c.left; alert('top:' + scrolltop + ' left: ' + scrollleft + ''); } button { margin: 500px 0 0 500px; } span { display: block; }
scroll down & left
Более подробно на learn.javascript.ru/coordinates MDN: .scrollTop, .getBoundingClientRect()

Ответ 2



Координаты относительно всего документа с jQuery: $('#element').offset(); Возвращает left и top относительно документа. Относительно прокрутки(скролла) - как уже писали, лидер по лаконичности getBoundingClientRect

std один namespace на всю стандартную библиотеку

#cpp


Почему std один namespace на всю стандартную библиотеку? Почему, например, не сделали
для алгоритмов отдельный std::algorithm?
    


Ответы

Ответ 1



Изначально пространства имён создавались для разрешения конфликтов. Пространство имён — это не декорация кода, не атрибут, не метка и даже не путь к файлу. Пространство имён — это в первую очередь цельное объединение кода, в котором не предполагается конфликтов. Также пространства имён используются для того, чтобы избежать засорения списка доступных сущностей в коде (важно при использовании автодополнения кода). Джависты изуродовали пространства имён, у них папки — это пространства имён, а файлы — классы. В результате библиотеки обрастают глубокими иерархиями пространств имён, и даже базовое использование библиотеки может требовать десяток пространств. PHP-шники пошли по пути Java, причём изначально возможности включения пространств имён были ограничены, да и сейчас не лучшие. Результатом всего этого является абсолютная необходимость использовать сложные IDE, иначе невозможно запомнить, где какой класс находится. Что же в C++? В C++ стандартная библиотека достаточно минималистичная, а доступные в конкретном файле классы и функции зависят от включаемых заголовочных файлов, поэтому проблемы засорения списка доступных имён нет. Кроме того, C++ создавался во времена, когда сильно умных IDE не было, поэтому приходилось помнить, что где находится. Засорять память пространствами имён в дополнение к заголовочным файлам — излишне. В целом, пока библиотека не натыкается на конфликты, и не возникает других причин использовать дополнительные пространства имён, достаточно только одного пространства. Для сравнения, в boost каждая библиотека в своём пространстве, потому что библиотеки существуют по отдельности, возможны конфликты как между библиотеками, так и со стандартной библиотекой.

Ответ 2



На самом деле внутри пространства имен std:: могут быть определены другие пространства имен, как, например, namespace std { namespace placeholders { //... } } в заголовочном файле или namespace std { namespace rel_ops { //... } } в заголовочном файле или namespace std { namespace chrono { //... } } в заголовочном файле или такие встроенные пространства имен как inline namespace literals { //... } inline namespace chrono_literals { //... } Кроме того разрешается стандартные имена языка C помещать в глобальное пространство имен. Так что если возникает какая-то коллизия стандартных имен, или другие проблемы, то стандарт C++ вводит новые пространства имен, чтобы избежать такие проблемы. Для алгоритмов не было необходимости объявлять отдельное пространства имен внутри пространства std , так как пока с этим никакой проблемы не возникало. Алгоритмы очень часто используется, поэтому заключение их в какое-то вложенное пространство имен усложняло бы написание кода. Рассмотрите, например std::algorithm::set_symmetric_difference( std::begin( a ), std::end( a ), std::begin( b ), std::end( b ), std::begin( c ) ); Получается очень длинная строка.:)

Хорошая книга по работе с памятью в C++

#cpp #memory_management


Ищу хорошую книгу или серию статей, которая описала бы различные нюансы работы с
памятью в C++ (в каких-то аспектах язык может не иметь значения, но ориентируюсь именно
на этот язык программирования). Хотелось бы знать как "под капотом" работает распределение
памяти, хранение объектов и данных в памяти.

Описание вопроса довольно расплывчатое. Привожу пример вопросов, который меня интересует:


Когда вызывается new/delete как программа/ОС хранит информацию о выделенные участках
памяти. Как виртуальное адресное пространство соотносится с реальным адресным пространством.
Что из себя представляет класс в памяти. А если класс наследуется от двух других
классов с виртуальными функциями.
Exception-ы в конструкторах/деcтрукторах и особенности для многопоточных приложений.
Как реализуется Exception на низком уровне.
Оптимизация выделения памяти. Собственный аллокатор памяти.
Создание/удаление объектов в программе с несколькими модулями.


Возможно эти вопросы будут отражены в разных книгах, буду рад любым. Насчёт специфики
ОС и разных компиляторов: интересны любые варианты.
    


Ответы

Ответ 1



Про механизмы распределения памяти, потоки и межпроцессную/межпоточную синхронизацию нужно читать книги об устройстве операционных систем. Можно порекомендовать: Таненбаум, Бос. Современные операционные системы Дейтел Х.М. Операционные системы. Том 1. Основы и принципы. Таненбаум Э.С., Вудхалл А.С. Операционные системы. Разработка и реализация Описание реализации принципов работы операционных систем можно почитать в Лав. Ядро Linux: описание процесса разработки и Labrosse. µC/OS-II: The Real-Time Kernel Об алгоритмах распределения памяти рассказывается также в Ахо, Хопкрофт, Ульман. Структуры данных и алгоритмы Написание своих распределителей памяти обсуждается в Джосаттис. Стандартная библиотека C++: справочное руководство. Также этот вопрос затронут в Керниган, Ритчи. Язык программирования C Разработке многопоточных приложений на C++11 посвящена книга Уильямс. Параллельное программирование на С++ в действии Еще есть книга о "внутреннем устройстве" C++: Lippman, Inside the C++ Object Model. Она довольно старая - возможно там описаны не все интересующие темы.

Ответ 2



Одной книги покрывающей всё это, разумеется,— нет. По первому вопросу можно почитать Structured Computer Organization и Modern Operating Systems По второму книги есть вряд ли, т.к. область весьма специфичная и подвержена изменениям, тут лучше искать статьи. У меня есть две статьи на эту тему: Размещение объектов. Часть 1: Основы и Размещение объектов. Часть 2: Виртуальность По третьему вопросу книг читать не надо: исключения в конструкторе это нормально, деструктор исключения покидать не должны. По умолчанию, с C++11, исключение, которое покинет деструктор, вызовет std::terminate и приложение будет завершено. Многопоточность не имеет отношения ни к деструктору, ни к конструктору. Они не могут быть одновременно выполнены в разных потоках(если конечно не вызвать их явно для глобального объекта, но это экзотика) По четвёртому вопросу тот же Танненбаум, вышеупомянутый, для общего развития, а потом статьи в интернете. Но Вы не хотите писать свой аллокатор, правда. Вот неплохое видео по аллокаторам, от Александреску. И вот кусок книги(Supplementary Chapter) от одного замечательного автора, я не читал это приложение к книге, но читал основную книгу — автор всё очень хорошо описывает. Правда на английском, и видео и книга. Пятый вопрос: тоже нужно искать статьи в интернете. Если вкратце: если не хотите иметь головную боль, то лучше освобождать память посредством того же модуля, что её выделял.

Как правильно внести изменения в скрипт таймера обратного отсчета времени

#javascript #jquery #дата #время #таймер


Всех приветствую! Прошу помощи. Есть скрипт таймера обратного отсчета времени до
определенной даты. Помогите, пожалуйста, внести в него изменения. Есть несколько моментов:


Необходимо добавить в скрипт таймера количество месяцев и количество лет.
Второй момент, не могу разобраться, когда таймер заканчивает отсчет, блок становится
пустым. Как сделать так, чтобы по окончании таймера, в этом блоке появлялся другой блок?
И еще один момент: скрипт ориентирован не на нашего брата, АМ и РМ, не совсем это
удобно, возможно ли это изменить, а также поменять местами в первой строке скрипта
число и месяц? Очень буду благодарен всем за любую помощь!


скрипт:

var end = new Date('12/01/2018 12:00 AM');

var _second = 1000;
var _minute = _second * 60;
var _hour = _minute * 60;
var _day = _hour * 24;
var timer;

function showRemaining() {
  var now = new Date();
  var distance = end - now;
  if (distance < 0) {
    clearInterval(timer);
    return;
  }
  var days = Math.floor(distance / _day);
  var hours = Math.floor((distance % _day) / _hour);
  var minutes = Math.floor((distance % _hour) / _minute);
  var seconds = Math.floor((distance % _minute) / _second);

  var daysLabel = pluralize(days, 'Дней');
  var hoursLabel = pluralize(hours, 'Часов');
  var minutesLabel = pluralize(minutes, 'Минут');
  var secondsLabel = pluralize(seconds, 'Секунд');

  var days = leadingZero(days);
  var hours = leadingZero(hours);
  var minutes = leadingZero(minutes);
  var seconds = leadingZero(seconds);

  function pluralize(number, text) {
    if(number === 1) {
      return text;
    } else {
      return text+''
    }
  }

  function leadingZero(number) {
    if(number < 10) {
      return '0'+number;
    } else {
      return number;
    }
  }

  $('.chart__bar--days .chart__bar-number').html(days);
  $('.chart__bar--days').css('height', ((days/360)*100)+'%');
  $('.chart__bar--days .chart__bar-label').html(daysLabel);

  $('.chart__bar--hours .chart__bar-number').html(hours);
  $('.chart__bar--hours').css('height', ((hours/24)*100)+'%');
  $('.chart__bar--hours .chart__bar-label').html(hoursLabel);

  $('.chart__bar--minutes .chart__bar-number').html(minutes);
  $('.chart__bar--minutes').css('height', ((minutes/60)*100)+'%');
  $('.chart__bar--minutes .chart__bar-label').html(minutesLabel);

  $('.chart__bar--seconds .chart__bar-number').html(seconds);
  $('.chart__bar--seconds').css('height', ((seconds/60)*100)+'%');
  $('.chart__bar--seconds .chart__bar-label').html(secondsLabel);
}

timer = setInterval(showRemaining, 1000);


html:

css: .widget { position: absolute; top: 0; bottom: 0; left: 0; right: 0; height: 100%; width: 100%; } .widget__foreground { position: absolute; top: 0px; bottom: 0px; left: 0px; right: 0px; } .widget--countdown__main { position: absolute; width: 50%; left: 50px; top: 120px; font-family: PT Serif Caption,Georgia,Times New Roman,Times,serif; color: #000000; text-shadow: #fff 1px 0px, #fff 1px 1px, #fff 0px 1px, #fff -1px 1px, #fff -1px 0px, #fff -1px -1px, #fff 0px -1px, #fff 1px -1px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px; } .widget--countdown__title { font-size: 36px; line-height: 42px; font-weight: 700; margin: 0 50px 20px 0; } .widget--countdown__supporting-text { margin-bottom: 25px; line-height: 17px; width: 50%; } .widget--countdown__chart { height: 100%; width: 50%; max-width: 470px; position: absolute; right: 50px; } .chart { position: absolute; top: 0px; bottom: 0px; left: 0px; right: 0px; display: flex; justify-content: space-between; } .chart__bar { width: 23%; position: relative; display: inline-block; align-self: flex-end; -webkit-transition: all linear .2s; -moz-transition: all linear .2s; -ms-transition: all linear .2s; -o-transition: all linear .2s; transition: all linear .2s; } .chart__bar-content { position: absolute; bottom: 20px; width: 100%; text-align: center; } .chart__bar-number { font-size: 36px; font-weight: 300; } .chart__bar-label { text-transform: uppercase; } .chart__bar--days { background-color: rgba(0, 191, 228, .90); box-shadow: 0px 0px 40px -5px rgba(0, 191, 228, .90); } .chart__bar--hours { background-color: rgba(249, 108, 207, .90); box-shadow: 0px 0px 40px -5px rgba(249, 108, 207, .90); } .chart__bar--minutes { background-color: rgba(0, 207, 167, .90); box-shadow: 0px 0px 40px -5px rgba(0, 207, 167, .90); } .chart__bar--seconds { background-color: rgba(238, 138, 56, .90); box-shadow: 0px 0px 40px -5px rgba(238, 138, 56, .90); } .chart__bar--days,.chart__bar--hours,.chart__bar--minutes,.chart__bar--seconds { background-image: url(pattern-timer.png); background-position: bottom right; background-repeat: repeat; }


Ответы

Ответ 1



upd.2 обновил ф-ю calendiff, чтобы правильно считала случаи, когда надо «отмотать» назад не один, а два месяца, для компенсации смещения в днях. Напр. чтобы компенсировать -31 день, добавить дни предыдущего одного месяца (30 дней) не достаточно, нужно заехать в пред-предыдущий месяц. upd. Как выяснилось, надо всё-таки считать точную «календарную» дистанцию между двух дат: и от 28 января до 28 февраля (31 день), и от 28 февраля до 28 марта (28 дней) – в обоих случаях ровно 1 месяц. Поэтому написал функцию для такого «человеческого» подсчёта разницы между двумя датами. Поскольку разметка повторяется, отличается лишь названием класса, вынес её генерацию в функцию. jQuery элементы достаточно найти один раз, и затем обращаться к сохранённым, вместо того, чтобы при каждом вызове заново искать в DOM-дереве. Добавил функцию для склонения существительных, в зависимости от числа (День, Дня, Дней). В стили добавил цвета для колонок года и месяца. // допустимые форматы времени: // https://tools.ietf.org/html/rfc2822#page-14 // https://www.w3.org/TR/NOTE-datetime // Напр. 1 декабря 2018, полдень по Москве: var end = new Date('2018-12-01T12:00+03:00') ,offset = (new Date().getTimezoneOffset())*6E4 ,timer ,$el ,key ,html = '' ; // generate markup html = ['years','months','days','hours','minutes','seconds'] .reduce( function(p,c){ return p += makeHtml(c);}, ''); $('.widget--countdown__chart > .chart').html(html); $el = { // найти один раз и запомнить Y: { n: $('.chart__bar--years .chart__bar-number'), l: $('.chart__bar--years .chart__bar-label'), b: $('.chart__bar--years') } ,M: { n: $('.chart__bar--months .chart__bar-number'), l: $('.chart__bar--months .chart__bar-label'), b: $('.chart__bar--months') } ,D: { n: $('.chart__bar--days .chart__bar-number'), l: $('.chart__bar--days .chart__bar-label'), b: $('.chart__bar--days') } ,h: { n: $('.chart__bar--hours .chart__bar-number'), l: $('.chart__bar--hours .chart__bar-label'), b: $('.chart__bar--hours') } ,m: { n: $('.chart__bar--minutes .chart__bar-number'), l: $('.chart__bar--minutes .chart__bar-label'), b: $('.chart__bar--minutes') } ,s: { n: $('.chart__bar--seconds .chart__bar-number'), l: $('.chart__bar--seconds .chart__bar-label'), b: $('.chart__bar--seconds') } } /** * генерирует html одного блока */ function makeHtml(key) { return [ '
', '
', '
', '
', '
', '
', ].join("\n").replace(/%KEY%/g, key); } function showRemaining() { var start = new Date(), r; if( end - start < 0) return timeHasCome(); r = calendiff( start, end); render( 'Y', r.years, ['Год','Года','Лет'], 3); // сколько максимум лет может быть? render( 'M', r.months, ['Месяц','Месяца','Месяцев'], 12); render( 'D', r.days, ['День','Дня','Дней'], 31); render( 'h', r.hours, ['Час','Часа','Часов'], 24); render( 'm', r.minutes, ['Минута','Минуты','Минут'], 60); render( 's', r.seconds, ['Секунда','Секунды','Секунд'], 60); } /** * Когда отсчёт закончился, или время уже прошло */ function timeHasCome() { if(timer) { window.clearInterval( timer); timer = undefined; } $('.widget.widget--countdown').html('
Баста, карапузики!
'); } function render( key, n, s, max) { $el[key].n.text( ('0' + n).slice(-2)); $el[key].l.text( getNumEnding(n, s)); $el[key].b.css('height', ((n / max) * 100) + '%'); } /** * Calendar difference between two dates * @param Date object dateIn start date * @param Date object dateOut end date * @return object with int properties * "years", "months", "days", "hours", "minutes" and "seconds" * * https://github.com/sergiks/calendiff.js * * @author Sergei Sokolov */ function calendiff( dateIn, dateOut) { var out = { years : 0 ,months : 0 ,days : 0 ,hours : 0 ,minutes : 0 ,seconds : 0 } ,sign = 1 ,diff = 0 ,proto ,monthsShift ,prop ; // check input proto = Object.prototype.toString.call(dateIn); if( proto !== '[object Date]') { dateIn = new Date( dateIn); if( isNaN( dateIn.getTime())) throw 'Incorrect "In" date format'; } proto = Object.prototype.toString.call(dateOut); if( proto !== '[object Date]') { dateOut = new Date( dateOut); if( isNaN( dateOut.getTime())) throw 'Incorrect "Out" date format'; } // check numeric difference diff = dateOut.getTime() - dateIn.getTime(); if( diff === 0) { return out; } else if( diff < 0) { sign = -1; dateOut = [dateIn, dateIn = dateOut][0]; // swap the dates } // calculate human-readable difference out.seconds += dateOut.getSeconds() - dateIn.getSeconds(); if( out.seconds < 0) { out.seconds += 60; out.minutes--; } out.minutes += dateOut.getMinutes() - dateIn.getMinutes(); if( out.minutes < 0) { out.minutes += 60; out.hours--; } out.hours += dateOut.getHours() - dateIn.getHours(); if( out.hours < 0) { out.hours += 24; out.days--; } // complex part: a month can have various number of days // when entering with a negative number of days, up to -31, // it might take up to two months shift back // should the preceding month only have 28, 29 or 30 days. out.days += dateOut.getDate() - dateIn.getDate(); while( out.days < 0) { monthsShift = 0; out.days += new Date( dateOut.getFullYear(), dateOut.getMonth() - monthsShift, 0).getDate(); monthsShift++; out.months--; } out.months += dateOut.getMonth() - dateIn.getMonth(); if( out.months < 0) { out.months += 12; out.years--; } out.years += dateOut.getFullYear() - dateIn.getFullYear(); // negative difference case if( sign < 0) for( prop in out) if( out[prop]) out[prop] *= -1; // avoid -0 values return out; } /** * Функция возвращает окончание для множественного числа слова на основании числа и массива окончаний * param iNumber Integer Число на основе которого нужно сформировать окончание * param aEndings Array Массив слов или окончаний для чисел (1, 4, 5), * например ['яблоко', 'яблока', 'яблок'] * return String */ function getNumEnding(iNumber, aEndings) { var sEnding, i; iNumber = iNumber % 100; if (iNumber>=11 && iNumber<=19) { sEnding=aEndings[2]; } else { i = iNumber % 10; switch (i) { case (1): sEnding = aEndings[0]; break; case (2): case (3): case (4): sEnding = aEndings[1]; break; default: sEnding = aEndings[2]; } } return sEnding; } timer = setInterval(showRemaining, 200); .widget { position: absolute; top: 0; bottom: 0; left: 0; right: 0; height: 100%; width: 100%; } .widget__foreground { position: absolute; top: 0px; bottom: 0px; left: 0px; right: 0px; } .widget--countdown__main { position: absolute; width: 50%; left: 50px; top: 120px; font-family: PT Serif Caption,Georgia,Times New Roman,Times,serif; color: #000000; text-shadow: #fff 1px 0px, #fff 1px 1px, #fff 0px 1px, #fff -1px 1px, #fff -1px 0px, #fff -1px -1px, #fff 0px -1px, #fff 1px -1px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px; } .widget--countdown__title { font-size: 36px; line-height: 42px; font-weight: 700; margin: 0 50px 20px 0; } .widget--countdown__supporting-text { margin-bottom: 25px; line-height: 17px; width: 50%; } .widget--countdown__chart { height: 100%; width: 50%; max-width: 470px; position: absolute; right: 50px; } .chart { position: absolute; top: 0px; bottom: 0px; left: 0px; right: 0px; display: flex; justify-content: space-between; } .chart__bar { width: 23%; position: relative; display: inline-block; align-self: flex-end; -webkit-transition: all linear .2s; -moz-transition: all linear .2s; -ms-transition: all linear .2s; -o-transition: all linear .2s; transition: all linear .2s; } .chart__bar-content { position: absolute; bottom: 20px; width: 100%; text-align: center; } .chart__bar-number { font-size: 36px; font-weight: 300; } .chart__bar-label { text-transform: uppercase; } .chart__bar--years { background-color: rgba(228, 191, 0, .90); box-shadow: 0px 0px 40px -5px rgba(228, 191, 0, .90); } .chart__bar--months { background-color: rgba(207, 108, 249, .90); box-shadow: 0px 0px 40px -5px rgba(207, 108, 249, .90); } .chart__bar--days { background-color: rgba(0, 191, 228, .90); box-shadow: 0px 0px 40px -5px rgba(0, 191, 228, .90); } .chart__bar--hours { background-color: rgba(249, 108, 207, .90); box-shadow: 0px 0px 40px -5px rgba(249, 108, 207, .90); } .chart__bar--minutes { background-color: rgba(0, 207, 167, .90); box-shadow: 0px 0px 40px -5px rgba(0, 207, 167, .90); } .chart__bar--seconds { background-color: rgba(238, 138, 56, .90); box-shadow: 0px 0px 40px -5px rgba(238, 138, 56, .90); } .chart__bar--days,.chart__bar--hours,.chart__bar--minutes,.chart__bar--seconds { background-image: url(pattern-timer.png); background-position: bottom right; background-repeat: repeat; }


Ответ 2



Сначала разберемся с терминологией. Терминология Полный год - двенадцать полных месяцев. Полный месяц - количество полных дней, равное количеству календарных дней этого месяца с учетом високосного года. Полный день - 24 полных часа. Полный час - 60 полных минут. Полная минута - 60 полных секунд. Полная секунда - 1000 миллисекунд. Алгоритм Рассмотрим алгоритм вычисления значений счетчика на примере. Допустим сегодня 2 января 2015 года время 00:01. Событие наступит 15 апреля 2016 года в 00:01. Мы выбрали одинаковое время для упрощения понимания алгоритма. Посчитаем количество полных месяцев. Начиная с февраля 2015 до марта 2016 включительно будет 14 полных месецев. Мы не стали учитывать январь 2015 и апрель 2016, так как эти месяцы не полные. Вычислим количество полных дней в этих месяцах. В январе 2015 29 полных дней и 1 неполный подолжительностью 23:59, в апреле 2016 - 14 полных и 1 неполный подолжительностью 00:01. В сумме за два месяца 44 полных дня 29 + 14 + 1. Это много, здесь, наверняка, есть еще один полный месяц. Но как определить сколько в нем должно быть полных дней? А столько, сколько в текущем месяце календарных дней. И это, пожалуй, самое сложное для понимания. Почему именно количество дней в текущем месяце? Дело в нашем восприятии месяца. Если сегодня 12-е, то для нас "через месяц" это 12-е следующего месяца. Если в текущем месяце N дней, то количество дней с 12-го до 12-го следующего месяца будет (N - 12) + 12, где (N - 12) количество дней до окончания текущего месяца, а 12 - количество дней до 12-го числа следующего месяца. Рассмотрим пару примеров (во всех примерах считаем что время 00:00): Если сегодня 31-е января, то до 28-го февраля осталось 28 дней. 1 день в январе, 27 дней в феврале. В текущем месяце 31 день. До 28 февраля 0 месяцев 28 дней. А если до 1-го марта? Тогда февраль является полным месяцем. Нам надо подсчитать количество дней между январем и мартом, как будто за январем сразу идет март. Получается 1 день в январе и 0 дней в марте. До 1-го марта 1 месяц и 1 день. Похоже на правду? Если же сегодня 1-е февраля (не високосный год), то до 31-го марта 28 дней в феврале и 30 в марте, итого 58 дней. Вычитаем февраль 58 - 28, получаем 1 месяц и 30 дней. Если же сегодня 1-е февраля (високосный год), то до 31-го марта 29 дней в феврале и 30 в марте, итого 59 дней. Вычитаем февраль 59 - 29, получаем 1 месяц и 30 дней. В последних примерах получается интересная ситуация, когда один месяц меньше, чем оставшееся количесвто дней. Но это нормально, так мы воспринимаем месяцы. Итак у нас 44 полных дня. Сейчас январь. В январе 31 день. Извлекаем еще один полный месяц 44 - 31, и остается 13 полных дней. Итого полных месяцев у нас 14 + 1 = 15, а это 1 полный год и 3 полных месяца. А общий результат 1 год 3 месяца 13 дней (время по нулям, так как оно одинаковое) Время Со временем есть проблема, которая называется Daylight saving time DST сложная тема, не будем ее касаться в рамках этой задачи. Попытаемся обойти DST, выдав локальное время за UTC. Тогда будет необходим механизм синхронизации времени, при котором возможны ситуации, когда счетчик при отсчете обратного времени по завершению последней секунды последней минуты часа либо не изменит час, либо уменьшит его на 2. Примечания к коду Я вставил в код комментария, призванные помочь пониманию алгоритма. Код не оптимизирован. Где-то производительность была проигнорирована в угоду ясности. Не стал заниматься падежами, чтобы не усложнять код. Создал функцию timeOver(), которая вызовется по окончанию времени. Но она пока только выводит alert, так как не понятно, что должно быть по истечении времени. Надо додумать отображение высоты счетчиков и их стили. Код JSFiddle и здесь: // Дата и время собятия var end = new Date(Date.parse("2017-02-21 22:45")); end = moveToUTC(end); var now = moveToUTC(new Date()); // Вынес поиск элементов HTML, чтобы не искать их в DOM в каждой итерации var $years = $('.chart__bar--years'), $yearsLabel = $years.find('.chart__bar-label'), $yearsNumber = $years.find('.chart__bar-number'), $months = $('.chart__bar--months'), $monthsLabel = $months.find('.chart__bar-label'), $monthsNumber = $months.find('.chart__bar-number'), $days = $('.chart__bar--days'), $daysLabel = $days.find('.chart__bar-label'), $daysNumber = $days.find('.chart__bar-number'), $hours = $('.chart__bar--hours'), $hoursLabel = $hours.find('.chart__bar-label'), $hoursNumber = $hours.find('.chart__bar-number'), $minutes = $('.chart__bar--minutes'), $minutesLabel = $minutes.find('.chart__bar-label'), $minutesNumber = $minutes.find('.chart__bar-number'), $seconds = $('.chart__bar--seconds'), $secondsLabel = $seconds.find('.chart__bar-label'), $secondsNumber = $seconds.find('.chart__bar-number'); var timer; function showRemaining() { if (now.getTime() >= end.getTime()) { clearInterval(timer); timeOver(); return; } now.setSeconds(now.getUTCSeconds() + 1); if (now.getUTCSeconds() % (60 * 60)) { // каждый час синхронизация времени now = moveToUTC(new Date()) } var counters = calcDistance(end, now); var years = counters['years']; var months = counters['months']; var days = counters['days']; var hours = counters['hours']; var minutes = counters['minutes']; var seconds = counters['seconds']; var yearsLabel = pluralize(years, 'Лет'); var monthsLabel = pluralize(days, 'Месяцев'); var daysLabel = pluralize(days, 'Дней'); var hoursLabel = pluralize(hours, 'Часов'); var minutesLabel = pluralize(minutes, 'Минут'); var secondsLabel = pluralize(seconds, 'Секунд'); var months = leadingZero(months); var days = leadingZero(days); var hours = leadingZero(hours); var minutes = leadingZero(minutes); var seconds = leadingZero(seconds); function pluralize(number, text) { if (number === 1) { return text; } else { return text + '' } } function leadingZero(number) { if (number < 10) { return '0' + number; } else { return number; } } $yearsNumber.html(years); //$years.css('height', ((years / ???) * 100) + '%'); // не понятно на что делить $years.css('height', 0); // пусть будет 0 $yearsLabel.html(yearsLabel); $monthsNumber.html(months); $months.css('height', ((months / 12) * 100) + '%'); $monthsLabel.html(monthsLabel); $daysNumber.html(days); $days.css('height', ((days / 30) * 100) + '%'); // примерно 30, хотя и не правильно $daysLabel.html(daysLabel); $hoursNumber.html(hours); $hours.css('height', ((hours / 24) * 100) + '%'); $hoursLabel.html(hoursLabel); $minutesNumber.html(minutes); $minutes.css('height', ((minutes / 60) * 100) + '%'); $minutesLabel.html(minutesLabel); $secondsNumber.html(seconds); $seconds.css('height', ((seconds / 60) * 100) + '%'); $secondsLabel.html(secondsLabel); } function timeOver() { alert('Время вышло'); } // Вернет количество дней в казанном месяце указанного года function daysInMonth(date) { return new Date(date.getUTCFullYear(), date.getUTCMonth(), 0).getUTCDate(); } // Вернет количество секунд прошедших с начала дня function secondsFromDayStart(date) { return date.getUTCSeconds() + 60 * date.getUTCMinutes() + 60 * 60 * date.getUTCHours(); } // Возвращает дату, смещенную в UTC // Например, на входе 2015-02-15 00:00 в локальном времени, на выходе 2015-02-15 00:00 в UTC function moveToUTC(date) { return new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds())); } function calcDistance(end, now) { var secondsInDay = 60 * 60 * 24; // 60 секунд в 60 минутах в 24 часах var yearsCount = 0, monthsCount = 0, daysCount = 0, // количество полных дней hoursCount = 0, minutesCount = 0, secondsCount = 0; // Сначала разберемся со временем // Вычисляем общее количиство секунд до назначенной даты. secondsCount = secondsFromDayStart(end) - secondsFromDayStart(now); // (1) secondsFromDayStart(end) - количество секунд с начала дня события до наступления события // (2) secondsFromDayStart(now) - количество секунд, прошедших сначала сегодняшнего дня // Если secondsCount имеет отрицательное значение, // то это тот случай, когда сегодня мы уже завершили полный день и перешли к отсчету очередного дня if (secondsCount < 0) { // тогда нам нужно забрать один день, // так как дальнейшая логика основывается на том, что (1) больше либо равно (2) daysCount -= 1; // и добавить количество секунд в дне // (так как secondsCount имеет отрицательное значение, то secondsCount + secondsInDay будет всегда меньше полного дня) secondsCount += secondsInDay; } // В secondsCount сейчас находится общее количество секунд. // Разабьем это значение на компоненты: hoursCount = secondsCount / 3600 >> 0; minutesCount = (secondsCount / 60 >> 0) % 60; secondsCount = secondsCount % 60 // Займемся годом // Определим количество полных лет, в которых мы точно будем уверены yearsCount = end.getUTCFullYear() - now.getUTCFullYear() - 1; // Если окончание в 2017-м, а сегодня 2015-й, то между датами точно есть один полный год. // Если окончание в 2016-м, а сегодня 2015-й, то между датами может и не быть полного года. // Если года одинаковые, то yearsCount будет равен -1. Мы восстановим справедливость позже. // yearsCount посчитан, но не до конца // Определим количество полных месяцев monthCount = 11 - now.getUTCMonth() + end.getUTCMonth(); // Так как в js месяцы начинаются с нуля (январь = 0, февраль = 1, ...), // то end.getMonth() вернет количество прошедших полных месяцев с начала года, // а выражение 11 - now.getMonth() вернет количество полных месяцев до завершения года. // monthCount посчитан, но тоже не до конца // Займемся количеством дней // Запомним в переменной количество дней в текущем месяце, оно пригодится несколько раз var daysInCurrentMonth = daysInMonth(now); daysCount += daysInCurrentMonth - now.getUTCDate() + end.getUTCDate(); // daysInCurrentMonth - now.getDate() - количество дней до конца текущего месяца // end.getDate() - количество дней с начала месяца до дня события // Минимальное значение daysCount равняется 0, если сегодня 31-е, дата события 1-е число, // и при вычислении времени мы одолжили один день // Максимальное значени daysCount равняется 61, // в случае если сегодня 1-е число, в этом месяце 31 день, дата события 31-е число, // и при вычислении времени мы не брали один день // Извлекаем еще один полный месяц, если он есть monthCount += daysCount / daysInCurrentMonth >> 0; daysCount = daysCount % daysInCurrentMonth; // Извлекаем еще один полный год, если он есть yearsCount += monthCount / 12 >> 0; monthCount = monthCount % 12; return { years: yearsCount, months: monthCount, days: daysCount, hours: hoursCount, minutes: minutesCount, seconds: secondsCount }; } timer = setInterval(showRemaining, 1000); .widget { position: absolute; top: 0; bottom: 0; left: 0; right: 0; height: 100%; width: 100%; } .widget__foreground { position: absolute; top: 0px; bottom: 0px; left: 0px; right: 0px; } .widget--countdown__main { position: absolute; width: 50%; left: 50px; top: 120px; font-family: PT Serif Caption, Georgia, Times New Roman, Times, serif; color: #000000; text-shadow: #fff 1px 0px, #fff 1px 1px, #fff 0px 1px, #fff -1px 1px, #fff -1px 0px, #fff -1px -1px, #fff 0px -1px, #fff 1px -1px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px; } .widget--countdown__title { font-size: 36px; line-height: 42px; font-weight: 700; margin: 0 50px 20px 0; } .widget--countdown__supporting-text { margin-bottom: 25px; line-height: 17px; width: 50%; } .widget--countdown__chart { height: 100%; width: 50%; max-width: 470px; position: absolute; right: 50px; } .chart { position: absolute; top: 0px; bottom: 0px; left: 0px; right: 0px; display: flex; justify-content: space-between; } .chart__bar { width: 23%; position: relative; display: inline-block; align-self: flex-end; -webkit-transition: all linear .2s; -moz-transition: all linear .2s; -ms-transition: all linear .2s; -o-transition: all linear .2s; transition: all linear .2s; } .chart__bar-content { position: absolute; bottom: 20px; width: 100%; text-align: center; } .chart__bar-number { font-size: 36px; font-weight: 300; } .chart__bar-label { text-transform: uppercase; } .chart__bar--months { background-color: red; box-shadow: 0px 0px 40px -5px rgba(0, 191, 228, .90); } .chart__bar--days { background-color: rgba(0, 191, 228, .90); box-shadow: 0px 0px 40px -5px rgba(0, 191, 228, .90); } .chart__bar--hours { background-color: rgba(249, 108, 207, .90); box-shadow: 0px 0px 40px -5px rgba(249, 108, 207, .90); } .chart__bar--minutes { background-color: rgba(0, 207, 167, .90); box-shadow: 0px 0px 40px -5px rgba(0, 207, 167, .90); } .chart__bar--seconds { background-color: rgba(238, 138, 56, .90); box-shadow: 0px 0px 40px -5px rgba(238, 138, 56, .90); } .chart__bar--days, .chart__bar--hours, .chart__bar--minutes, .chart__bar--seconds { background-image: url(pattern-timer.png); background-position: bottom right; background-repeat: repeat; }


Ответ 3



Описал в комментариях как работает скрипт и дополнил его. //Устанавливаем дату окончания var end = new Date('12/01/2018 12:00 AM'); //Задаём константы var _second = 1000; var _minute = _second * 60; var _hour = _minute * 60; var _day = _hour * 24; var _month = _day * 30; var _year = _month * 12; //Для того что бы вывести диаграмму остатка количества лет нужно общее количество — в данно случае ставлю 2 var _year_all = 2; var timer; //Функция которая вызывается при окончании отсчёта function eventStopTimer(){ return false; } function showRemaining() { var now = new Date(); var distance = end - now; if (distance < 0) { eventStopTimer(); clearInterval(timer); return; } // Находим количество var years = Math.floor(distance / _year); var months = Math.floor((distance % _year) / _month); var days = Math.floor((distance % _months) / _day); var hours = Math.floor((distance % _day) / _hour); var minutes = Math.floor((distance % _hour) / _minute); var seconds = Math.floor((distance % _minute) / _second); // В английской версии тут склоняли добавлением «S» var yearsLabel = pluralize(years, 'Дней'); var monthsLabel = pluralize(months, 'Часов'); var daysLabel = pluralize(days, 'Дней'); var hoursLabel = pluralize(hours, 'Часов'); var minutesLabel = pluralize(minutes, 'Минут'); var secondsLabel = pluralize(seconds, 'Секунд'); //Добавляем лидирующий ноль var days = leadingZero(days); var hours = leadingZero(hours); var minutes = leadingZero(minutes); var seconds = leadingZero(seconds); //Функция для склонения (неправильная) function pluralize(number, text) { if(number === 1) { return text; } else { return text+'' } } //Функция добавления лидирующего нуля function leadingZero(number) { if(number < 10) { return '0'+number; } else { return number; } } $('.chart__bar--years .chart__bar-number').html(years); $('.chart__bar--years').css('height', ((years/_year_all)*100)+'%'); $('.chart__bar--years .chart__bar-label').html(yearsLabel); $('.chart__bar--months .chart__bar-number').html(months); $('.chart__bar--months').css('height', ((months/12)*100)+'%'); $('.chart__bar--months .chart__bar-label').html(monthsLabel); $('.chart__bar--days .chart__bar-number').html(days); $('.chart__bar--days').css('height', ((days/360)*100)+'%'); $('.chart__bar--days .chart__bar-label').html(daysLabel); $('.chart__bar--hours .chart__bar-number').html(hours); $('.chart__bar--hours').css('height', ((hours/24)*100)+'%'); $('.chart__bar--hours .chart__bar-label').html(hoursLabel); $('.chart__bar--minutes .chart__bar-number').html(minutes); $('.chart__bar--minutes').css('height', ((minutes/60)*100)+'%'); $('.chart__bar--minutes .chart__bar-label').html(minutesLabel); $('.chart__bar--seconds .chart__bar-number').html(seconds); $('.chart__bar--seconds').css('height', ((seconds/60)*100)+'%'); $('.chart__bar--seconds .chart__bar-label').html(secondsLabel); } timer = setInterval(showRemaining, 1000); В html нужно добавить блоки


Произвольное появление букв

#javascript #css


Есть задача. Появления текста в блоке, в произвольной последовательности, по буквам
(Например для слова Hello, чтоб буквы появлялись в подобной последовательности eHlol).
Тестовый блок

Hello,
Javascript is an awesome language
Единственное что пришло в голову, это обернуть каждую букву текста в и потом показывать его с рандомный таймаутом. Но сложность обстоит с тэгами. Что с ними можно сделать? Убирать, оборачивать потом снова добавлять? Может быть это реализуется без JS с помощью СSS?


Ответы

Ответ 1



Пока другого не придумал. Каждую букву обернуть в span с классом, который ставит visibility: hidden, одновременно запихнув все созданные span'ы в массив. Массив случайно перемешать. И затем по очереди у каждого из массива снимать прячущий класс: var spans = [] ,allTextNodes = getTextNodes( document.getElementById('b-test')) ,i ,timer ; /** * recursively get all text nodes as an array for a given element */ function getTextNodes(node) { var childTextNodes = []; if (!node.hasChildNodes()) return; var childNodes = node.childNodes; for (var i = 0; i < childNodes.length; i++) { if (childNodes[i].nodeType == Node.TEXT_NODE) { childTextNodes.push(childNodes[i]); } else if (childNodes[i].nodeType == Node.ELEMENT_NODE) { Array.prototype.push.apply(childTextNodes, getTextNodes(childNodes[i])); } } return childTextNodes; } /** * given a text node, wrap each character in the * given tag. */ function wrapEachCharacter(textNode, tag, className = '') { var text = textNode.nodeValue; var parent = textNode.parentNode; var characters = text.split(''); var elements = []; characters.forEach(function(character) { var element = document.createElement(tag); element.className = className; var characterNode = document.createTextNode(character); element.appendChild(characterNode); parent.insertBefore(element, textNode); spans.push(element); }); parent.removeChild(textNode); } allTextNodes.forEach(function(textNode) { wrapEachCharacter(textNode, 'span', 'h'); }); /** * Shuffles array in place. * @param {Array} a items The array containing the items. */ function shuffle(a) { var j, x, i; for (i = a.length; i; i--) { j = Math.floor(Math.random() * i); x = a[i - 1]; a[i - 1] = a[j]; a[j] = x; } } shuffle(spans); i = spans.length - 1; function showOne() { spans[i--].className = ""; if (i < 0) clearInterval(timer); } timer = window.setInterval(showOne.bind(this), 100); span.h{visibility: hidden}
Hello,
Javascript is an awesome language
Что улучшить: пробелы не надо оборачивать и прятать – только видимые буквы, иначе нарушается видимый «ритм» появления символов. Ещё недостаток: слова перестают быть целыми словами и могут рваться переносом строки. Выход: оборачивать каждое слово в .

Ответ 2



Спасибо! Пока ждал ответа, сам набросал, но ваш вариант мне нравится больше) В процессе столкнулся еще с одним моментом. Слова больше не являются словами и переносы строк могут сыграть злую шутку. Думаю правильно будет оборачивать слова перед обработкой в отдельные контейнеры которые будут виртуальными словами. $(function() { $('.random-fading').each(function() { var input = $(this).html(); var charRegexp = /<[^>]*>/g; var wordRegexp = /\S+/g; var match; var offset = 1; var replacer = []; var wordReplacer = []; var output = '
'; while ((match = charRegexp.exec(input)) != null) { replacer[match.index - offset] = match[0]; offset += match[0].length; } input = input.replace(charRegexp, ''); offset = 1; while ((match = wordRegexp.exec(input)) != null) { wordReplacer[match.index - offset] = true; offset += 14; //console.log(match[0]); } input.split('').forEach(function(value, key) { if (/^\S$/.exec(value)) { output += '' + value + ''; } else { //пустые символы не оборачиваем output += value; } if (wordReplacer[key] && key != 0) { output += ''; } if (replacer[key]) { //добавить тег output += replacer[key]; } }); //console.log(output); output += ''; $(this).html(output); }); $('.random-fading span.h').each(function() { var self = $(this); setTimeout(function() { self.removeClass('h'); }, Math.floor((Math.random() * 300) + 100)); }); }); .random-fading span.h { opacity: 0; transition: 1s linear all; }
Hello,
Javascript is an awesome language
ОченьДлинныйТекст КоторыйДолженПереноситсяПоСловам, ПростаяПроверкаНаВсякийСлучайАТоМалоЛиЧтоТоПойдётНеТак:-(((((


четверг, 5 декабря 2019 г.

Завершение работы приложения

#java #android #android_sdk #activity


Скажите, есть ли возможность командой полностью закрыть приложение, чтобы даже при
долгом нажатии кнопки "домой", оно не высвечивалось в списке приложений? Командой finish()
проблема не решается. Если нет, то как сделать так, чтобы при нажатии на приложение
в списке оно перенаправляло на MainActivity, а не на ту, в которой сработала команда
finish()?
    


Ответы

Ответ 1



В стандартной ситуации достаточно вызвать finish() - активити завершится. Описанный ниже случай нужно использовать только в крайних ситуациях. В мире андроида не принято вручную завершать процессы - всё это должна делать система по своему разумению. //эмулируем нажатие на HOME, сворачивая приложение Intent i = new Intent(Intent.ACTION_MAIN); i.addCategory(Intent.CATEGORY_HOME); i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(i); //в зависимости от версии оси намертво убиваем приложение if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { finishAndRemoveTask(); } else { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { finishAffinity(); } else { finish(); } } //и контрольный в голову Process.killProcess(Process.myPid());