Страницы

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

вторник, 19 февраля 2019 г.

Изменить цвет кнопки если внесены изменения

Есть Button1 который сохраняет изменения в RichTextBox1. Как сделать, так чтобы когда пользователь что-то напишет в RichTextBox1 то Button1 поменяет свой цвет на зеленый, а если пользователь сохранит файл нажав на этот Button1, то Button1 примет свой стандартный цвет (например черный)? В случае если пользователь что-то опять допишет, то Button1 станет опять зеленым.


Ответ

Нужно подписаться на событие richTextBox1_TextChanged
private void richTextBox1_TextChanged(object sender, EventArgs e) { button1.BackColor = System.Drawing.Color.Green; }
private void button1_Click(object sender, EventArgs e) { //Сохранение файла button1.BackColor = System.Drawing.Color.Empty; }

Как лучше добавлять класс в div на чистом JavaScript?

Как лучше добавлять класс в div на чистом JavaScript (без jQuery)?


Ответ

Element.classList
Синтаксис
var elementClasses = elem.classList;
Методы
add( String [,String] ) Добавляет элементу указанные классы remove( String [,String] ) Удаляет у элемента указанные классы item ( Number ) Результат аналогичен вызову сlassList[Number] toggle ( String [, Boolean]) Если класс у элемента отсутствует - добавляет, иначе - убирает. Когда вторым параметром передано false - удаляет указанный класс, а если true - добавляет. Если вторым параметром передан undefined или переменная с typeof == 'undefined', поведение будет аналогичным передаче только первого параметра при вызове toggle. contains ( String ) Проверяет, есть ли данный класс у элемента (вернет true или false)
конечно же, у ClassList есть заветное свойство length, которое возвращает количество классов у элемента.
const elem = document.querySelector("#clock") //Выведем классы console.log(elem.classList); //DOMTokenList ["example", "for", "you"] //Добавим классы elem.classList.add("ok", "understand"); console.log(elem.classList); //DOMTokenList ["example", "for", "you", "ok", "understand"] //Переключим классы elem.classList.toggle("you"); elem.classList.toggle("he"); console.log(elem.classList); //DOMTokenList ["example", "for", "ok", "understand", "he"] //Проверим класс console.log(elem.classList.contains("example")); //true console.log(elem.classList.contains("lol")); //false //И удалим классы elem.classList.remove("example", "for", "understand", "he"); console.log(elem.classList); //DOMTokenList ["ok"]


*все взято из MDN кому не лень можно и здесь смотреть

Помогите составить сложный sql запрос

Есть таблица, одним запросом хочу получить, к примеру, 2 значения - сумма значений value и сумма значений points, но не могу понять, как составить условия для 2х значений одновременно, возможно ли это? Вот например сумма values:
`SELECT SUM(value) AS balance FROM db WHERE id>30 AND value>1000`;
Тоже самое для points
`SELECT SUM(value) AS balance FROM db WHERE xxx=2 OR yyy=3`;
А можно ли как то объединить эти 2 запроса в один? SELECT SUM(value) AS balance, SUM(point) AS points FROM.... WHERE ??
Всё это при условии, что данные перекликаются и не получится просто соединить эти 2 условия в WHERE


Ответ

Да, можно. Запихните условия where в агрегирующую функцию sum
select id, sum(if(xxx = 2 or yyy = 3, value, 0)) as balance_1, sum(if(id > 30 AND value > 1000, value, 0)) as balance_2 from db group by id

Конвертировать строку в число SCSS

Есть строка "14рх", как привести ее к числу в SCSS? К примеру для строкового представления значения есть функция inspect($value), для того, что бы узнать единицы связанные с числом есть unit($number). Но как быть если type-of(14рх) возвращает string, а нужно namber Поясняю: есть такой код, который смотрит есть ли у значения списка единицы измерения и если нет, то дописывает
@function chekList($list) { $tmp: (); @each $key in $list { @if(unitless($key)) { $key: inspect($key); $key: str-insert($key, "px", str-length($key) + 1); } $tmp: append($tmp, $key); } @return $tmp; }
Теперь бы еще конвертнуть полученные строковые значения в числовые.... Или посоветуйте как дописать единицы к числу, если их нет?
P.S. Ниже я дал ответ как можно добавить единицы измерения, если их нет. Но все же интересует как можно конвертировать строку в число. Допустим 10 + "px" на выходе даст строку "10рх", а нужно, что бы это было число.


Ответ

Прочитав статью, ссылку на которую дал soledar10 я не стал дублировать полностью функцию предоставленную там, для моих целей она нужна не полностью, а лишь та часть, где к числу добавляются единицы измерения. Немного модифицировав код из предыдущего ответа получилось вот что:
@function chekUnit($list) { $strings: 'px' 'cm' 'mm' '%' 'ch' 'pica' 'in' 'em' 'rem' 'pt' 'pc' 'ex' 'vw' 'vh' 'vmin' 'vmax'; $units: 1px 1cm 1mm 1% 1ch 1pica 1in 1em 1rem 1pt 1pc 1ex 1vw 1vh 1vmin 1vmax; $tmp: (); @each $key in $list { @if(unitless($key)) { $index: index($strings, $list-unit); @if not $index { @error "Unknown unit `#{$list-unit}`."; } $key: $key * nth($units, $index); } $tmp: append($tmp, $key); } @return $tmp; }
Где $list-unit - это переменная с задаваемыми единицами в настройках проекта.

Игнорировать все файлы кроме тех, что имеют определённые расширения

У меня есть файл .gitignore следующего содержания:
* !/.gitignore !CMakeLists.txt !*.cpp !*.hpp
Согласно задумке он должен разрешать добавление:
самого .gitignore в корне репозитория, файлов CMakeLists.txt в любых папках и подпапках, файлов формата .cpp и .hpp в любых папках и подпапках,
а всё остальное запрещать.
Однако разрешающие правила действуют только на файлы, расположенные в корне репозитория. И это, в принципе, логично
ФОРМАТ ПРАВИЛ Необязательный префикс «!», инвертирующий действие правила; любой подпадающий по него файл, исключённый правилами выше, будет включён обратно. Невозможно включить файл, если он находится в уже исключённой папке. Git не просматривает содержимое исключённых папок в целях оптимизации, а потому правила не распространяются на их содержимое. PATTERN FORMAT An optional prefix “!” which negates the pattern; any matching file excluded by a previous pattern will become included again. It is not possible to re-include a file if a parent directory of that file is excluded. Git doesn’t list excluded directories for performance reasons, so any patterns on contained files have no effect, no matter where they are defined.
То есть первое правило, исключающее все подпапки, практически аннулирует действие разрешающих правил ниже.
Вопрос: как надо правильно включать только файлы с определёнными расширениями вне зависимости от их расположения и уровня вложенности?


Ответ

Можно сделать так:
* !*/ !.gitignore !.gitattributes !.gitmodules !CMakeLists.txt !*.cpp !*.hpp
Добавил ещё пару часто встречающихся файлов конфигурации git.

JavaScript и длинная регулярка

Доброго времени суток! У меня есть регулярка, предназначенная для поиска форм одного и того же слова в строке. Эти регулярки автоматически генерируются для каждого слова и получаются довольно длинными, но создаются по одному шаблону. Например нужно создать регулярку для поиска форм слова "кот". Берём "базовую" часть регулярки:
(^|[^0-9a-zа-я])(кот)([^0-9a-zа-я]|$)
и создаём на её основе полную регулярку для всех форм, склеивая из отдельных частей для каждой формы:
((^|[^0-9a-zа-я])(кот)([^0-9a-zа-я]|$)|(^|[^0-9a-zа-я])(котов)([^0-9a-zа-я]|$)|(^|[^0-9a-zа-я])(котам)([^0-9a-zа-я]|$)|(^|[^0-9a-zа-я])(котами)([^0-9a-zа-я]|$)|(^|[^0-9a-zа-я])(котах)([^0-9a-zа-я]|$)|(^|[^0-9a-zа-я])(коту)([^0-9a-zа-я]|$)|(^|[^0-9a-zа-я])(котом)([^0-9a-zа-я]|$)|(^|[^0-9a-zа-я])(коте)([^0-9a-zа-я]|$)|(^|[^0-9a-zа-я])(кота)([^0-9a-zа-я]|$)|(^|[^0-9a-zа-я])(коты)([^0-9a-zа-я]|$))
Получается такая длинная абракадабра. Возьмём некий текст, в котором встречаются формы слова "кот":
бла бла кот бла бла "кота" бла коты скоты
Мне нужно заменить все вхождения форм слова "кот", ограниченные не словарными символами (пробелами, знаками препинания и тд) на ту же самую форму, обрамлённую угловыми скобками. То есть из исходной строки мне надо получить эту:
бла бла <кот> бла бла "<кота>" бла <коты> скоты
Я это делаю так:
'бла бла кот бла бла "кота" бла коты скоты'.replace(/моя большая регулярка/gi, '$1<$2>$3')
Получается вот что:
"бла бла кот <>бла бла "кота"<> бла коты <>скоты"
Как можно видеть, мой способ не работает - в строке очень много групп (и в каждой строке их разное количество), и поэтому я не могу пользоваться номерами групп при замене - каждый раз мне будут нужны разные номера.
Пробовал использовать такую замену:
'бла бла кот бла бла "кота" бла коты скоты'.replace(/моя большая регулярка/gi, function(match) { return "<" + match + ">"; })
Этот вариант почти работает, выдавая такой результат:
"бла бла< кот >бла бла <"кота"> бла< коты >скоты"
Но как можно видеть, он "захватывает" внутрь скобок кавычки и пробелы. Подскажите, как в данной ситуации организовать корректную замену? Заранее спасибо!
Несколько важных замечаний:
Я не могу использовать модификатор \b так как в движке регулярных выражений в JavaScript этот модификатор корректно работает только с латинскими буквами (у меня как правило кириллические тексты) Примерно по той же причине я не могу пользоваться именованными группами - в Js-регулярках они не поддерживаются Сами регулярки несколько упрощены в демонстрационных целях, например, в них нет буквы "ё" и прописных букв. Возможно тут указаны не все формы слова "кот", в данном примере это не столь важно


Ответ

Всех своих котов сложите во вторую группу захвата
console.log('бла бла кот бла бла "кота" бла коты скоты'.replace(/(^|[^0-9a-zа-яё])(кот|котов|котам|котами|котах|коту|коте|котом|кота|коты)([^0-9a-zа-яё]|$)/gi, '$1<$2>$3'));
UPD для работы с котами идущими через пробел (1 символ):
console.log('котам бла бла кот кот бла бла "кота"кот бла коты скоты котом'.replace(/(^|[^0-9a-zа-яё])(кот|котов|котам|котами|котах|коту|коте|котом|кота|коты)(?![0-9a-zа-яё])/gi, '$1<$2>'));

Дерево сценариев в telegram-боте (строгая последовательность вопросов/ответов)

Делаю чат-бота для телеграм. На данный момент есть многомерное меню, которое работает на callback-ах инлайнкнопок. Все бы было хорошо, но есть проблема: к примеру, человек нажимает заказать, затем выбирает товар. Затем ему нужно ввести адрес и время доставки, а также телефон и тд.
Скажите, пожалуйста, каким образом это можно сделать? Т.е. если человек просто введет адрес доставки, то ему будет выдаваться "не выбран товар" или "ошибка", а если он выберет товар, то он пойдет по этому сценарию и так далее.
Вопрос - как сделать сценарий?

Мой код (на примере php, библиотека telegram-bot-sdk):
$telegram = new Api('api token here'); //Устанавливаем токен, полученный у BotFather
$result = $telegram -> getWebhookUpdates(); //Передаем в переменную $result полную информацию о сообщении пользователя
$keyboard = Keyboard::make() ->inline() ->row( Keyboard::inlineButton(['text' => 'Показать меню', 'callback_data' => '/show_menu']), Keyboard::inlineButton(['text' => 'Купить цветы', 'callback_data' => '/buyflowers']) ); $keyboard_flowers = Keyboard::make() ->inline() ->row( Keyboard::inlineButton(['text' => 'Розы', 'callback_data' => '/buyflowers_roses']) ) ->row( Keyboard::inlineButton(['text' => 'Пионы', 'callback_data' => '/buyflowers_piony']) ) ->row( Keyboard::inlineButton(['text' => 'Шляпные коробки', 'callback_data' => '/buyflowers_hatbox']) ) ->row( Keyboard::inlineButton(['text' => 'Коробочки со сладостями', 'callback_data' => '/buyflowers_sweetbox']) ) ->row( Keyboard::inlineButton(['text' => 'Акции и спецпредложения', 'callback_data' => '/buyflowers_sales']) ); $keyboard_menu = Keyboard::make() ->inline() ->row( Keyboard::inlineButton(['text' => 'Связаться с нами', 'callback_data' => '/contact_us']) ) ->row( Keyboard::inlineButton(['text' => 'Купить цветы', 'callback_data' => '/buyflowers']) ) ->row( Keyboard::inlineButton(['text' => 'Стать партнером', 'callback_data' => '/partner']) ) ->row( Keyboard::inlineButton(['text' => 'Перейти на сайт', 'url' => 'https://kands.spb.ru/']) ) ->row( Keyboard::inlineButton(['text' => 'Акции и спецпредложения', 'callback_data' => '/buyflowers_sales']) ); $contact = $result['message']['contact']; $text = $result["message"]["text"]; //Текст сообщения $chat_id = $result["message"]["chat"]["id"]; $name = $result["message"]["from"]["username"]; //Юзернейм пользователя if ($result->isType('callback_query')) { $data = $result['callback_query']['data']; $chat = $result['callback_query']['message']['chat']['id']; switch ($data) { case '/buyflowers': $telegram->sendMessage([ 'chat_id' => $chat, 'text' => 'Выберите категорию:', 'reply_markup' => $keyboard_flowers ]); break; case '/show_menu': $telegram->sendMessage([ 'chat_id' => $chat, 'text' => 'Меню: ', 'reply_markup' => $keyboard_menu ]); break;
default: # code... break; } }
$telegram->sendMessage([ 'chat_id' => $chat_id, 'text' => 'Стартовая страница. Выберите дальнейшие действия.', 'reply_markup' => $keyboard ]);

?> `


Ответ

Есть как минимум три варианта (варианты можно совмещать):
Используйте коллекцию типа ключ-значение, где в качестве ключа используется chat_id, а в качестве значения - произвольный идентификатор предыдущего/следующего шага сценария (концептуально, это могут вообще абсолютно любые необходимые данные). Получив очередное сообщение от пользователя, сверяйте статус сценария со значением элемента коллекции для данного чата Используйте ForseReply при отправке сообщений с запросами адреса и пр., а затем проверяйте входящие сообщения на наличие непустого поля reply_to_message и смотрите в текст этого поля. В зависимости от его содержимого можно будет понять, какой именно шаг сценария выполняется Используйте встроенные кнопки (Inline buttons). Inline button отличается от обычной кнопки тем, что не отправляет текст в чат. Вместо этого отправляется callback, заданный программно и невидимый пользователю. Соответственно, в качестве callback_data можно использовать любой текст (типа step1 или this_is_unexpected_step), ожидая затем нужный коллбэк при получении обновлений

Пример для варианта с ForseReply (псевдокод):
// Производится запрос адреса пользователя Bot.SendChatAction(update.Message.Chat.Id, ChatAction.Typing); Bot.SendTextMessage(update.Message.Chat.Id, "Укажите адрес доставки", replyMarkup: force_reply); ... // Проверка полученных сообщений от пользователя if (update.Message.ReplyToMessage.Text.Contains("адрес доставки")) { if (!IsValidAddress(update.Message.ReplyToMessage.Text)) return; SaveAddressToDb(update.Message.Chat.Id, update.Message.ReplyToMessage.Text); Bot.SendChatAction(update.Message.Chat.Id, ChatAction.Typing); Bot.SendTextMessage(update.Message.Chat.Id, "Адрес успешно сохранён, ожидайте поступления заказа"); } else { ... }

Похожий вопрос на en SO

Ограничить время жизни потока

Есть список задач, каждую задачу нужно ограничить по времени. Если задача не успевает выполниться, то пусть возвращается какое-то дефолтное значение.
class Program { static void Main(string[] args) { var sw = new Stopwatch(); sw.Start();
foreach (var i in GetInts()) { Console.WriteLine("result: " + i); }
sw.Stop(); Console.WriteLine("done " + sw.Elapsed.TotalMilliseconds.ToString("F2")); Console.ReadKey(); }
static List GetInts() { using (var cts = new CancellationTokenSource()) { var tasks = new Task[3];
tasks[1] = Task.Run(() => { int i = 5000; Thread.Sleep(i); Console.WriteLine(i); return i; }, cts.Token);
tasks[0] = Task.Run(() => { int i = 3000; Thread.Sleep(i); Console.WriteLine(i); return i; }, cts.Token);
tasks[2] = Task.Run(() => { int i = 2000; Thread.Sleep(i); Console.WriteLine(i); return i; }, cts.Token);
var delay = 1000;
return tasks.Select(x => x.TimeoutAfter(delay, cts)).Select(x => x.Result).ToList(); } } }
public static class TaskExtensions { public static async Task TimeoutAfter(this Task task, int delay, CancellationTokenSource cts) {
using (var timeoutCancellationTokenSource = new CancellationTokenSource()) {
var completedTask = await Task.WhenAny(task, Task.Delay(delay, timeoutCancellationTokenSource.Token)); if (completedTask == task) { timeoutCancellationTokenSource.Cancel(); return await task; } else { cts.Cancel(); return default(TResult); } } } }
Хотя в коде прописано ограничение в 1000мс, данный код выводит следующий результат:
2000 result: 0 result: 0 result: 2000 done 2010,83 3000 5000
Почему задача с 2с попадает в result? А если выставить delay = 3000, то выведутся все задачи (хотя 5с не должна):
2000 3000 5000 result: 3000 result: 5000 result: 2000 done 5008,06


Ответ

Смотрите. Вы не можете завершить бегущую задачу насильно, точно так же как вы не можете завершить насильно любой другой код. Для этого задача должна сотрудничать.
Идея с использованием CancellationToken'а правильная, но не доведена до конца, отсюда и проблема. CancellationToken в Task.Run относится только к процессу запуска таска*. Когда таск запущен, он не сможет магическим образом узнать о том, что токен сработал, ему нужно самому проверять отмену токена.
Например, так:
var ct = cts.Token; tasks[0] = Task.Run(async () => // добавил async { int i = 3000; await Task.Delay(i, ct); // проверяем токен Console.WriteLine(i); return i; }, ct);
Ещё по теме: Как работает CancellationToken в TaskFactory.StartNew Method (Action, CancellationToken)?

*Почти. Ещё он ассоциируется с таском, и при выбросе исключения этим токеном таск переходит в состояние Cancelled, а не Faulted

Правильное написание плагина на чистом JavaScript

Возник вопрос о правильном подходе создания плагина на js.
Пример работы на Plunker, а так же код на github
Суть работы плагина:
на странице есть изображения, при клике на которые открывается модальное окно; в этом модальном окне есть Слайдер и Превьюшки с этими изображениями, которые мы можем листать, переключить и т.д.; в слайдер можем передать параметры отображение кол-ва превью и возможность их отключить; на одной странице можем использовать плагин несколько раз.
Проблема: когда используется два и более раз, то плагин ломается и отображет только последнюю галерею.
(function() { let gallery_1 = new SkySlider('.first-gallery');
let gallery_2 = new SkySlider('#second-gallery', { showThumbnails: true, thumbnailsItemCount: 6, }); })();


Ответ

Вместо хранения this в замыкании, как это сделано у вас
(function() { // for `this` let _;
function SkySlider(selector = null, options = null) { _ = this; }
// остальные функции использующие _ в качестве this })()
Можно все вспомогательные функции поместить в прототип функции SkySlider
(function() { function SkySlider(selector = null, options = null) {}
SkySlider.prototype.init = function () {} SkySlider.prototype.otherMethod = function () {}
// либо по другому делаем функцию доступной из вне window.SkySlider = SkySlider })()
В таких методах this будет ссылать на текущий экземпляр класса и не перезаписывать какую-то глобальную переменную