Disabling Ctrl+Alt+Del in Debian

В консольке vSphere ткнули ктрл-альт-дел (предполагали что винда), а там Debian Stretch и система ребутнулась.
Дабы избежать такой неожиданности решил отключить, погуглил, первое что попалось

nano /etc/inittab
*** find the line with 'ctrlaltdel' (Ctrl-W for search)
*** comment out by adding the hash '#' at beginning of line
*** save file (Ctrl-O) and exit (Ctrl-X)
init q
Не помогло, как оказалось после Wheezy, нужно такое решение

rm /lib/systemd/system/ctrl-alt-del.target
ln -s /dev/null /lib/systemd/system/ctrl-alt-del.target
systemctl daemon-reload
Работает

А для CentOS 7


[root@localhost ~]# systemctl mask ctrl-alt-del.target
Created symlink from /etc/systemd/system/ctrl-alt-del.target to /dev/null.

How remove Memory Optimized File

Моя история избавления от In-Memory OLTP, база на продакшене, так что просто грохнуть ее и забыть, никак.

А началось все с этого: «Msg 41385, Level 16, State 1, Procedure sp_cdc_enable_db, Line 31 [Batch Start Line 2] A database cannot be enabled for both Change Data Capture (CDC) and MEMORY_OPTIMIZED_DATA storage.»

В базе все таблицы в оптимизированные на использование памяти заменил на табличные переменные.
Попытался удалить группу, «The file ‘MOD’ cannot be removed because it is not empty».
Можно пытаться сделать файл пустым и тд и тп, но все будет безрезультатно, потому как читать нужно документацию, и вот что сказал майкрософт, » Once you create a memory-optimized filegroup, you can only remove it by dropping the database. SQL Server does not allow you to remove an In-Memory OLTP filegroup from the database even after you drop all memory-optimized tables.», в общем искоренить MEMORY_OPTIMIZED_DATA storage можно только одним способом дропнув БД.

В моем случае в базе присутствуют таблицы с ограничениями по внешним ключам, а так же таблицы содержащие null-ы при заданном значении по умолчанию для столбца, задействован полнотекстовый поиск.
Ну да ладно, приступим, воспользуемся SQL Server Management Studio (SSMS) и утилитой DTExecUI.exe

1) Создаем скрипт базы. Безымянный1 Безымянный2 Безымянный3 Безымянный4 Безымянный5 Безымянный6

2) Правим полученный файла, удаляем файловую группу, ну и все это во временную БД, для сверки. Безымянный7 Безымянный8
3) Импорт данных («Import data…», курсор на скрине не на том пункте), готовим пакет для импорта, указываем источник, назначение, выбираем таблицы, указываем на необходимость вставки идентификаторов, сохраняем пакет в файл, затем его подправим и отправим на выполнение. Безымянный9 Безымянный10 Безымянный11 Безымянный12 Безымянный13 Безымянный14 Безымянный15 Безымянный16 Безымянный17 Безымянный18
Отключаем контроль ограничений, иначе будет плохо. Безымянный19 Безымянный20
Вот так, плохо. Безымянный20_1
Меняем keepnulls на true, иначе нулы заменятся на значения по умолчанию. Безымянный21 Безымянный22
И отправляем пакет на выполнение. Безымянный23 Безымянный24 Безымянный25

Готово. По этим же шагам можно и понижение версии сделать.


MariaDB(MySQL) recursive CTE and Window Functions

Ну да, конечно, в MySQL, нет ничего подобного и уже наверное не будет, иначе Oracle сделал бы этот функционал много лет назад. Ставим вместо MySQL MariaDB и получаем обобщенные выражения и рекурсивные в том числе, оконные функции, обработку строки регекспом и тд. PHP и не заметит подмены. Для примера, решаем задачу, генерация «чистая», никаких лишних строк.

/*
* календарь на месяц, по умолчанию текущий - "select 0 monthShift from dual"
* начало недели с понедельника
* с отображением дней предыдущего и следующего месяцев в неполных неделях
*/
with recursive gen50 as (
 select -10 rn 
 union all
 select rn+1 from gen50 where rn<40 /* генерируем 50 строк */
), calend as (
 select *, substr(dds, -2) ddH from (
 select *, min(ttrimTop) over () trimTop, max(ttrimBottom) over () trimBottom from (
 select *,  first_value(rn) over (partition by curMonth order by weekdayIdx asc, rn desc) ttrimTop, 
            first_value(rn) over (partition by curMonth order by weekdayIdx desc, rn asc) ttrimBottom,
            min(case when ld = dds and weekdayIdx = 6 then rn else 99 end) over() ttrimBottom2, 
            max(case when fd = dds and weekdayIdx = 0 then rn else -99 end) over() ttrimTop2
 from (
 select *, case when dds < fd then -1 when dds > ld then 0 else 1 end curMonth, dds = current_date curDay, weekday(dds) weekdayIdx from ( 
 select *, dd.fd + interval (gen50.rn) day as dds from gen50 
  cross join (select last_day(now() + interval monthShift month ) ld, last_day(now() + interval monthShift month  ) + interval 1 day - interval 1 month as fd from (select 0 monthShift from dual) dd) dd
  ) tt ) tt ) tt ) tt
  where rn between trimTop and trimBottom /* обрезать по начальной и конечной неделе */
   and rn between ttrimTop2 and ttrimBottom2 /* если число месяца на границе недели, то нужно "отрезать" раньше */
)
select * from calend
 order by rn;

+----+------------+------------+------------+----------+--------+------------+----------+-------------+--------------+-----------+---------+------------+------+
| rn | ld         | fd         | dds        | curMonth | curDay | weekdayIdx | ttrimTop | ttrimBottom | ttrimBottom2 | ttrimTop2 | trimTop | trimBottom | ddH  |
+----+------------+------------+------------+----------+--------+------------+----------+-------------+--------------+-----------+---------+------------+------+
| -6 | 2017-10-31 | 2017-10-01 | 2017-09-25 |       -1 |      0 |          0 |       -6 |          -7 |           99 |       -99 |      -6 |         35 | 25   |
| -5 | 2017-10-31 | 2017-10-01 | 2017-09-26 |       -1 |      0 |          1 |       -6 |          -7 |           99 |       -99 |      -6 |         35 | 26   |
| -4 | 2017-10-31 | 2017-10-01 | 2017-09-27 |       -1 |      0 |          2 |       -6 |          -7 |           99 |       -99 |      -6 |         35 | 27   |
| -3 | 2017-10-31 | 2017-10-01 | 2017-09-28 |       -1 |      0 |          3 |       -6 |          -7 |           99 |       -99 |      -6 |         35 | 28   |
| -2 | 2017-10-31 | 2017-10-01 | 2017-09-29 |       -1 |      0 |          4 |       -6 |          -7 |           99 |       -99 |      -6 |         35 | 29   |
| -1 | 2017-10-31 | 2017-10-01 | 2017-09-30 |       -1 |      0 |          5 |       -6 |          -7 |           99 |       -99 |      -6 |         35 | 30   |
|  0 | 2017-10-31 | 2017-10-01 | 2017-10-01 |        1 |      0 |          6 |       29 |           0 |           99 |       -99 |      -6 |         35 | 01   |
|  1 | 2017-10-31 | 2017-10-01 | 2017-10-02 |        1 |      0 |          0 |       29 |           0 |           99 |       -99 |      -6 |         35 | 02   |
|  2 | 2017-10-31 | 2017-10-01 | 2017-10-03 |        1 |      0 |          1 |       29 |           0 |           99 |       -99 |      -6 |         35 | 03   |
|  3 | 2017-10-31 | 2017-10-01 | 2017-10-04 |        1 |      0 |          2 |       29 |           0 |           99 |       -99 |      -6 |         35 | 04   |
|  4 | 2017-10-31 | 2017-10-01 | 2017-10-05 |        1 |      0 |          3 |       29 |           0 |           99 |       -99 |      -6 |         35 | 05   |
|  5 | 2017-10-31 | 2017-10-01 | 2017-10-06 |        1 |      0 |          4 |       29 |           0 |           99 |       -99 |      -6 |         35 | 06   |
|  6 | 2017-10-31 | 2017-10-01 | 2017-10-07 |        1 |      0 |          5 |       29 |           0 |           99 |       -99 |      -6 |         35 | 07   |
|  7 | 2017-10-31 | 2017-10-01 | 2017-10-08 |        1 |      0 |          6 |       29 |           0 |           99 |       -99 |      -6 |         35 | 08   |
|  8 | 2017-10-31 | 2017-10-01 | 2017-10-09 |        1 |      1 |          0 |       29 |           0 |           99 |       -99 |      -6 |         35 | 09   |
|  9 | 2017-10-31 | 2017-10-01 | 2017-10-10 |        1 |      0 |          1 |       29 |           0 |           99 |       -99 |      -6 |         35 | 10   |
| 10 | 2017-10-31 | 2017-10-01 | 2017-10-11 |        1 |      0 |          2 |       29 |           0 |           99 |       -99 |      -6 |         35 | 11   |
| 11 | 2017-10-31 | 2017-10-01 | 2017-10-12 |        1 |      0 |          3 |       29 |           0 |           99 |       -99 |      -6 |         35 | 12   |
| 12 | 2017-10-31 | 2017-10-01 | 2017-10-13 |        1 |      0 |          4 |       29 |           0 |           99 |       -99 |      -6 |         35 | 13   |
| 13 | 2017-10-31 | 2017-10-01 | 2017-10-14 |        1 |      0 |          5 |       29 |           0 |           99 |       -99 |      -6 |         35 | 14   |
| 14 | 2017-10-31 | 2017-10-01 | 2017-10-15 |        1 |      0 |          6 |       29 |           0 |           99 |       -99 |      -6 |         35 | 15   |
| 15 | 2017-10-31 | 2017-10-01 | 2017-10-16 |        1 |      0 |          0 |       29 |           0 |           99 |       -99 |      -6 |         35 | 16   |
| 16 | 2017-10-31 | 2017-10-01 | 2017-10-17 |        1 |      0 |          1 |       29 |           0 |           99 |       -99 |      -6 |         35 | 17   |
| 17 | 2017-10-31 | 2017-10-01 | 2017-10-18 |        1 |      0 |          2 |       29 |           0 |           99 |       -99 |      -6 |         35 | 18   |
| 18 | 2017-10-31 | 2017-10-01 | 2017-10-19 |        1 |      0 |          3 |       29 |           0 |           99 |       -99 |      -6 |         35 | 19   |
| 19 | 2017-10-31 | 2017-10-01 | 2017-10-20 |        1 |      0 |          4 |       29 |           0 |           99 |       -99 |      -6 |         35 | 20   |
| 20 | 2017-10-31 | 2017-10-01 | 2017-10-21 |        1 |      0 |          5 |       29 |           0 |           99 |       -99 |      -6 |         35 | 21   |
| 21 | 2017-10-31 | 2017-10-01 | 2017-10-22 |        1 |      0 |          6 |       29 |           0 |           99 |       -99 |      -6 |         35 | 22   |
| 22 | 2017-10-31 | 2017-10-01 | 2017-10-23 |        1 |      0 |          0 |       29 |           0 |           99 |       -99 |      -6 |         35 | 23   |
| 23 | 2017-10-31 | 2017-10-01 | 2017-10-24 |        1 |      0 |          1 |       29 |           0 |           99 |       -99 |      -6 |         35 | 24   |
| 24 | 2017-10-31 | 2017-10-01 | 2017-10-25 |        1 |      0 |          2 |       29 |           0 |           99 |       -99 |      -6 |         35 | 25   |
| 25 | 2017-10-31 | 2017-10-01 | 2017-10-26 |        1 |      0 |          3 |       29 |           0 |           99 |       -99 |      -6 |         35 | 26   |
| 26 | 2017-10-31 | 2017-10-01 | 2017-10-27 |        1 |      0 |          4 |       29 |           0 |           99 |       -99 |      -6 |         35 | 27   |
| 27 | 2017-10-31 | 2017-10-01 | 2017-10-28 |        1 |      0 |          5 |       29 |           0 |           99 |       -99 |      -6 |         35 | 28   |
| 28 | 2017-10-31 | 2017-10-01 | 2017-10-29 |        1 |      0 |          6 |       29 |           0 |           99 |       -99 |      -6 |         35 | 29   |
| 29 | 2017-10-31 | 2017-10-01 | 2017-10-30 |        1 |      0 |          0 |       29 |           0 |           99 |       -99 |      -6 |         35 | 30   |
| 30 | 2017-10-31 | 2017-10-01 | 2017-10-31 |        1 |      0 |          1 |       29 |           0 |           99 |       -99 |      -6 |         35 | 31   |
| 31 | 2017-10-31 | 2017-10-01 | 2017-11-01 |        0 |      0 |          2 |       36 |          35 |           99 |       -99 |      -6 |         35 | 01   |
| 32 | 2017-10-31 | 2017-10-01 | 2017-11-02 |        0 |      0 |          3 |       36 |          35 |           99 |       -99 |      -6 |         35 | 02   |
| 33 | 2017-10-31 | 2017-10-01 | 2017-11-03 |        0 |      0 |          4 |       36 |          35 |           99 |       -99 |      -6 |         35 | 03   |
| 34 | 2017-10-31 | 2017-10-01 | 2017-11-04 |        0 |      0 |          5 |       36 |          35 |           99 |       -99 |      -6 |         35 | 04   |
| 35 | 2017-10-31 | 2017-10-01 | 2017-11-05 |        0 |      0 |          6 |       36 |          35 |           99 |       -99 |      -6 |         35 | 05   |
+----+------------+------------+------------+----------+--------+------------+----------+-------------+--------------+-----------+---------+------------+------+
42 rows in set (0.00 sec)
Добавим к CTE еще пару запросов, что-то вроде этого,

 , aa as (
 SELECT dd.dds dds2, GROUP_CONCAT(CONCAT_WS(CHAR(9), a.id, a.publish_date IS NOT NULL, a.publish_date IS NULL AND (a.for_date < now()), a.publish_date IS NULL AND (a.for_date > now()) ) SEPARATOR '\n ') aas
    FROM dd 
     JOIN articles a WHERE a.deleted IS NULL
     AND a.for_date BETWEEN dds AND dds + INTERVAL 1 DAY - INTERVAL 1 SECOND
      AND (a.responsible = $user OR $user = -1)
     GROUP BY dd.dds
 )
и в итоге, будет как-то так. calend

Split a string by whitespace, keeping quoted segments

Разделить строку по пробелам, с возможностью использовать пробелы в кавычках.

	// вместо простого сплита
	'58 "5 A"'.split(/\s+/);
	// используем матч
	'58 "5 A"'.match(/(?:[^\s"]+|"[^"]*")+/g);
Живой пример использования в фильтре.

	$('#lt').on('keyup paste input', '.filter input', function() {
	    var vals = $(this).val().trim().match(/(?:[^\s"]+|"[^"]*")+/g) || [];
	    // поиск по кол-ву вхождений набора входных значений (разделены пробелом, пробел тоже можно передать в поиск, пример, 100 "5 А")
	    var checkAll = function(val) {
		var finded = 0;
		$.each(vals, function(i,o){ finded += (val.toUpperCase().indexOf(o.replace(/\"/g,'').toUpperCase()) > -1 ? 1 : 0); });
		return vals.length != finded;
	    };
	    $(this).closest('.row').find('.prop-val,.mod').removeClass('hidden').filter(function(){ return vals.length != 0 && checkAll($(this).text()); }).addClass('hidden');
	});

PHP extracting zip archives with cyrillic filenames problem

Наткнулись на проблему, при распаковке zip архива (php7), если в именах русские символы получаем каракули (в нашем случае только они и присутствуют), странным способом перекодированные. Создаем архив используя ДОСовскую кодировку 866 с ней нет проблем со встроенными в винду зип-архиватором, если использовать 1251 в винде до 8-ки опять же каракули.

	$zip->addFile($pathFrom . '/' . $doc->hashedName,  mb_convert_encoding($doc->originalName, 'CP866', 'UTF-8'));
Пытаемся распаковать из ПХП

$Zip = new ZipArchive();
$open = $Zip->open('in_01122016000000.zip');
$length = $Zip->numFiles;
for($i = 0; $i < $length; $i++)
{
  echo $Zip->getNameIndex($i) . ' ' . mb_convert_encoding($Zip->getNameIndex($i), 'UTF-8', 'UTF-8, CP866') . '<br>';
}
Заглянув в исходники zip расширения, находим, что начиная с php 5.6 при получении имени файла вызывается _zip_string_get, где используется логика угадывания кодировки (/* start guessing */) и для получения не измененной строку нужно указать флаг ZIP_FL_ENC_RAW. Пока в официальной документации о такой возможности умалчивают, речь только о флаге ZipArchive::FL_UNCHANGED — до изменений.

  //zip.h
  //#define ZIP_FL_ENC_RAW         64u /* get unmodified string */
  echo mb_convert_encoding($Zip->getNameIndex($i, 64), 'UTF-8', 'UTF-8, CP866') . '<br>';
или так

  $stat = $Zip->statIndex($i, 64);
  echo mb_convert_encoding($stat['name'], 'UTF-8', 'UTF-8, CP866') . '<br>';
ZipArchive reads filenames with UTF-8 characters wrong

Phalcon: calling getRender() multiple times

При вызове метода getRender() несколько раз подряд, данные будут только при первом вызове.

$html1 = $view->getRender($section, $template, $data);
$html2 = $view->getRender($section, $template, $data);

var_dump($html1); // Valid string
var_dump($html2); // Empty string
Дабы исправить ситуацию, регистрируем сервис вольта немного иначе

	$di->set('view', function() use ($config) {

		$view = new \Phalcon\Mvc\View();

		$view->setViewsDir(__DIR__ . $config->application->viewsDir);
// было
//		$view->registerEngines(array(
//			".volt" => 'volt'
//		));

// стало
		$view->registerEngines(array(
		        ".volt" => function($view, $di) {
				$volt = new \Phalcon\Mvc\View\Engine\Volt($view, $di);
					$volt->setOptions(array(
						"compiledPath" => "../cache/volt/",
					));
				return $volt;
		        }
		));


		return $view;
	});

Select2 изменение алгоритма фильтра


    $(".select2foreqtype").select2(
	{ placeholder: 'Укажите тип', 
	  allowClear: true,
	  language: 'ru',
	  minimumInputLength: 1,
	  templateResult: function (data) { // в выпарающем списке отображаем номер
		  if (!data.id) { return data.text; }
		  return $('<div class="clearfix"><span class="pull-left">' + data.text + '</span><span class="pull-right">' + $(data.element).data('srn') + '</span></div>');
	  },
	  matcher: function(params, data) { // поиск не только по наименованию, но и по номеру, причем совместно, разделитель пробел, и без учета порядка
	    var terms = ((params.term || '').trim().toUpperCase()).split(/\s/);
	    var matchCount = 0;
	    var fullText = data.text.toUpperCase() + $(data.element).data('srn');
	    $.each(terms, function(i,term){
		if (fullText.indexOf(term) >= 0) {
			matchCount++;
		}
	    });
	    if (terms.length == matchCount) {
	        return data;
	    }
	    return false;
	  },
	}).on('select2:select', function(e){
console.log(e);
	});

MS SQL OFFSET FETCH

Воспользовавшись предложением offset fetch (аналог mysql-го limit-a) для пагинации, наткнулся на проблемку, при переходе между страницами запрос выдавал тот же результат. В запросе всегда присутствует сортировка по одному из столбцов.
select rr.[rank] r, * v.*, otfile
, try_convert(date, col12, 104) col12D, convert(varchar(10), try_convert(date, col12, 104), 104) col12DH, ceiling((count(*) over())/10.0) pagesTotal from m_dev v
 inner join (select * from (values('4370'),('4371'),('4372'),('4374'),('3461'),('3462'),('3464'),('3465'))t(code)) codes on (v.col10 like codes.code + '%')
 inner join containstable (m_dev, col99, '("трансформатор" or "трансформатор*") and ("тока" or "тока*") and ("италия" or "италия*")', language 1049) rr on (v.id = rr.[key])
 where v.deleted is null 
 order by r
 offset 20 rows fetch next 10 rows only
Грешил на использование полнотекстового поиска, и объединение с результатами функции CONTAINSTABLE, провел эксперименты над данными попроще.
select t1.id1 id1, t1.id2+t2.id2-10 id2 from 
(select * from (values(1,10),(1,20),(1,30),(1,40),(1,50),(1,60),(1,70),(1,80),(1,90)) t (id1, id2)) t1
 cross join (select * from (values(1,1),(1,2),(1,3),(1,4),(1,5),(1,6),(1,7),(1,8),(1,9)) t (id1, id2)) t2
 order by id1
 offset 0 rows fetch next 10 rows only
;

select t1.id1 id1, t1.id2+t2.id2-10 id2 from 
(select * from (values(1,10),(1,20),(1,30),(1,40),(1,50),(1,60),(1,70),(1,80),(1,90)) t (id1, id2)) t1
 cross join (select * from (values(1,1),(1,2),(1,3),(1,4),(1,5),(1,6),(1,7),(1,8),(1,9)) t (id1, id2)) t2
 order by id1
 offset 10 rows fetch next 10 rows only
;

select t1.id1+t2.id1-1 id1, t1.id2+t2.id2-10 id2 from 
(select * from (values(1,10),(1,20),(1,30),(1,40),(1,50),(1,60),(1,70),(1,80),(1,90)) t (id1, id2)) t1
 cross join (select * from (values(1,1),(1,2),(1,3),(1,4),(1,5),(1,6),(1,7),(1,8),(1,9)) t (id1, id2)) t2
 order by id1
 offset 0 rows fetch next 10 rows only
;

select t1.id1+t2.id1-1 id1, t1.id2+t2.id2-10 id2 from 
(select * from (values(1,10),(1,20),(1,30),(1,40),(1,50),(1,60),(1,70),(1,80),(1,90)) t (id1, id2)) t1
 cross join (select * from (values(1,1),(1,2),(1,3),(1,4),(1,5),(1,6),(1,7),(1,8),(1,9)) t (id1, id2)) t2
 order by id1
 offset 10 rows fetch next 10 rows only
;
Получил результаты, соответственно для каждого запроса, появились косяки.
id1	id2		id1	id2		id1	id2		id1	id2
1	1		1	12		1	12		1	12
1	2		1	13		1	11		1	11
1	3		1	14		1	9		1	9
1	4		1	15		1	8		1	8
1	5		1	16		1	7		1	7
1	6		1	17		1	6		1	6
1	7		1	18		1	5		1	5
1	8		1	19		1	4		1	4
1	9		1	21		1	3		1	3
1	11		1	22		1	2		1	2
В плане выполнения появилась сортировка. План выполнения Итого, решаем проблему всегда добавляя последней сортировку по уникальному столбцу. Если его нет, генерим, как в примере. Если есть, как в моем рабочем запросе, в нем я просто добавил
order by r, v.id
select t1.id1+t2.id1-1 id1, t1.id2+t2.id2-10 id2, row_number() over (order by (select 1)) rn from 
(select * from (values(1,10),(1,20),(1,30),(1,40),(1,50),(1,60),(1,70),(1,80),(1,90)) t (id1, id2)) t1
 cross join (select * from (values(1,1),(1,2),(1,3),(1,4),(1,5),(1,6),(1,7),(1,8),(1,9)) t (id1, id2)) t2
 order by id1, rn
 offset 0 rows fetch next 10 rows only
;

select t1.id1+t2.id1-1 id1, t1.id2+t2.id2-10 id2, row_number() over (order by (select 1)) rn from 
(select * from (values(1,10),(1,20),(1,30),(1,40),(1,50),(1,60),(1,70),(1,80),(1,90)) t (id1, id2)) t1
 cross join (select * from (values(1,1),(1,2),(1,3),(1,4),(1,5),(1,6),(1,7),(1,8),(1,9)) t (id1, id2)) t2
 order by id1, rn
 offset 10 rows fetch next 10 rows only
;

id1	id2	rn		id1	id2	rn
1	1	1		1	12	11
1	2	2		1	13	12
1	3	3		1	14	13
1	4	4		1	15	14
1	5	5		1	16	15
1	6	6		1	17	16
1	7	7		1	18	17
1	8	8		1	19	18
1	9	9		1	21	19
1	11	10		1	22	20

Excel RegExp Match

Функцию в модуль
Public Function regMatch(ByVal inStr2 As String, ByVal patternStr As String, Optional ByVal idx As Integer = -1) As String
Dim regEx As New RegExp
Dim regMatches As Object
    With regEx
            .Global = True
            .MultiLine = True
            .IgnoreCase = False
            .Pattern = patternStr
            
            If .Test(inStr2) Then
                If (idx >= 0) Then
                    If .Execute(inStr2)(0).SubMatches.Count() > idx Then
                    regMatch = .Execute(inStr2)(0).SubMatches(idx)
                    Else
                    regMatch = ""
                    End If
                Else
                    regMatch = .Execute(inStr2)(0)
                End If
            Else
                regMatch = ""
            End If
        
    End With
End Function
Пример формулы в ячейке
=regMatch(H3;"(\(|\{)\s*([0-9A-Z]{8}\s*\-\s*[0-9A-Z]{8})\s*(\}|\))";1)
Последний параметр необязательный
=regMatch(H4;"(\(|\{)\s*([0-9A-Z]{8}\s*\-\s*[0-9A-Z]{8})\s*(\}|\))")

Fly to cart

«Полет в корзину», если у нас нужно «отправлять в полет» только картинку, то все очень легко, пример с перемещением всей карточки и текст описания и картинка. Безымянный15 Свойство zoom не использую, ибо при его использовании расчет координат отличается от браузера к браузеру, использую transform: scale, если не поддерживается, просто «сворачиваю» по высоте.
Ниже код того что на картинке. <style> .card { font-size: 60px; color: #444; } .card .panel-body { font-size: 60px; } .card .fa-cart-plus { font-size: 26px; } .cart { margin: 60px 100px; } .cart i { font-size: 46px; color: #aeb733; } .card-body { position: relative; margin-bottom: 6px; } .card-body-copy { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1100; zoom: 1; } </style> <div id="cart" class="cart clearfix"> <button type="button" class="btn btn-default btn-lg pull-right"><i class="fa fa-shopping-cart" aria-hidden="true"></i></button> </div> <div class="row"> <div class="col-xs-3"> <div class="card panel panel-default"> <div class="panel-heading clearfix"> <div class="pull-right"><h4>Card 1</h4></div> </div> <div class="panel-body"> <div class="card-body"> <img src="/public/img/export_excel2.png"> 111<i style="color: #ee0;" class="fa fa-bitcoin" aria-hidden="true"></i> </div> <div class="clearfix"> <div class="pull-right"><button class="btn"><i class="fa fa-cart-plus" aria-hidden="true"></i></button></div> </div> </div> </div> </div> <div class="col-xs-3"> <div class="card panel panel-default"> <div class="panel-heading clearfix"> <div class="pull-right"><h4>Card 2</h4></div> </div> <div class="panel-body"> <div class="card-body"> <img src="/public/img/export_excel2.png"> 222<i style="color: #c0e;" class="fa fa-eur" aria-hidden="true"></i> </div> <div class="clearfix"> <div class="pull-right"><button class="btn"><i class="fa fa-cart-plus" aria-hidden="true"></i></button></div> </div> </div> </div> </div> <div class="col-xs-3"> <div class="card panel panel-default"> <div class="panel-heading clearfix"> <div class="pull-right"><h4>Card 3</h4></div> </div> <div class="panel-body"> <div class="card-body"> <img src="/public/img/export_excel2.png"> 333<i style="color: #0f0;" class="fa fa-dollar" aria-hidden="true"></i> </div> <div class="clearfix"> <div class="pull-right"><button class="btn"><i class="fa fa-cart-plus" aria-hidden="true"></i></button></div> </div> </div> </div> </div> </div> <script type="text/javascript"> (function($) { $('body').on('click', '.btn', function(e) { var card = $(this).closest('.panel-body').find('.card-body'); if (!card.size()) return; var cardCopy = $(this).closest('.panel-body').find('.card-body-copy'); if (!cardCopy.size()) $('<div/>').addClass('card-body-copy').html( card.html() ).appendTo(card); var cart = $('#cart .fa-shopping-cart'); var cardCopy = $(this).closest('.panel-body').find('.card-body-copy'); if (!cardCopy.size() || !cart.size()) return; var cardCoord = cardCopy.offset(); var cartCoord = cart.offset(); var lenght = Math.sqrt(Math.pow((cardCoord.top-cartCoord.top),2) + Math.pow((cardCoord.left-cartCoord.left),2)); var zoom = 0.5; var speed = 1/4; // pixel in msec var maybeTransform = (card.get(0).style['transform'] !== undefined); var l = (cartCoord.left - cardCoord.left); var t = (cartCoord.top - cardCoord.top); $(cardCopy).animate($.extend({ opacity: 0.4, left: (l > 0 ? '+' : '') + l.toString(), top: (t > 0 ? '+' : '') + t.toString(), }, maybeTransform ? {} : {height: 'toggle'}), { duration: lenght/speed, step:function(now, fx){ if (fx.prop == 'left') { $(this).css('transform-origin', '0 0' ); $(this).css('transform', 'scale('+ (zoom+(1-zoom)*(1-now/l)) +')' ); } }, complete:function(){ $(this).remove(); // cart.toggle( "bounce", { times: 3 }, "slow" ); } }); }); })(jQuery); </script>