23 декабря 2011 г.

Падающие снежинки на css3


В канун нового года на своем сайте хочется навести красоту и украсить "по новогоднему" некоторые страницы падающими снежинками.

Выбор инструментов реализации пал на css3 анимацию. И как не трудно догадаться у такого решения есть и положительные и отрицательные стороны.
К положительным сторонам можно отнести:
  • относительную простоту реализации;
  • и отсутствие необходимости в сторонних компонентов.
К минусам:
  • Поддерживает только FireFox и Chrome(Webkit) (Opera 11.5x не поддерживает css3 анимацию).
В интернете я нашел уже готовый демонстрационный пример http://natbat.net/code/clientside/css/snowflakes/, который я взял за основу.
Переделать мне его пришлось дважды:
  • Первый пример использует в качестве снежинок символы юникод.
  • Второй пример где используются спрайтовые картинки для снежинок.
Как показало тестирование на разных компьютерах, символы юникода в качестве снежинок - не самый лучший выбор. Так как на некоторых компьютерах вместо снежинок падали квадратики или иероглифы.
Пересказывать как сделаны падающие снежинки на css3 довольно скучно, поэтому ограничусь демонстрацией исходников:
Первый пример - Рабочий пример на jsfiddle
Второй пример - исходники на Googlcode

20 декабря 2011 г.

Интересные моменты настройки почтовой рассылки Битрикс

... Или как добавить ссылку на отписку от рассылки в письме

Если посмотреть шире на проблему, то можно задаться вопросом, как можно добавить параметры вида #param# в отсылаемые подписчикам почтовые сообщения. Так же как это делается в шаблонах почтовых сообщениях. Таким образом, в отсылаемое письмо мы сможем разместить не только ссылку на отписку от рассылки, которая для каждого подписчика должна быть уникальной, но и добавить другие параметры:
  • Имя, фамилию, отчество;
  • Код подтверждения подписчика;
  • Id подписчика;
  • и др. параметры.
В документации Битрикса я нашел страницу c описанием необходимого функционала http://dev.1c-bitrix.ru/user_help/service/subscribe/rubric_edit.php?print=Y .

В принципе в нем описан все необходимые действия, но я приведу свой пример реализации.
В файле /bitrix/php_interface/init.php храняться обработччики выполняющихся событий.
В этот файл необходимо добавить следующий код:
Copy Source | Copy HTML
  1. // регистрируем обработчик 
  2. AddEventHandler("subscribe", "BeforePostingSendMail", Array("CatchSubscriter", "beforePostingSendMailHandler"));
  3. class CatchSubscriter{
  4.     // создаем обработчик события "BeforePostingSendMail" 
  5.     function beforePostingSendMailHandler(&$arFields){
  6.         //$USER_NAME = "Подписчик"; 
  7.         //Попробуем найти подписчика. 
  8.         $rs = CSubscription::GetByEmail($arFields["EMAIL"]);
  9.         if($ar = $rs->Fetch()){
  10.             //echo "<p>[</p>";
  11.             //print_r($ar);
  12.             //echo "<p>]</p>";
  13.             $arFields["BODY"] = str_replace("#CONFIRM_CODE#", $ar["CONFIRM_CODE"], $arFields["BODY"]);
  14.             $arFields["BODY"] = str_replace("#ID#", $ar["ID"], $arFields["BODY"]);
  15.             $arFields["BODY"] = str_replace("#EMAIL#", $ar["EMAIL"], $arFields["BODY"]);
  16.         }
  17.         //$arFields["BODY"] = str_replace("#NAME#", $USER_NAME, $arFields["BODY"]); 
  18.         return $arFields;
  19.     }
  20. }
Здесь в функции AddEventHandler("subscribe", "BeforePostingSendMail", Array("CatchSubscriter", "beforePostingSendMailHandler"));
Первый аргумент — название  используемого модуля подписки и рассылок.
Второй аргумент — название события.
Третий аргумент — массив с параметрами обработчика события.

Так при возникновении события BeforePostingSendMail т. е перед самой отправкой письма извлекаются почтовый адрес и текст письма из переменной $arFields. Зная email на который отправляется письмо — устанавливаем личность получателя, его ID и т. п. Текст письма проверяем на наличие ключей заданного формата (#EMAIL#, #ID#), и тут же их подменяем на нужные значения. Не трудно догадаться, что стандартный письмо рассылки можно расширить и другими парметрами.

Чтобы проверить работу можно отправить рассылку с следующим содержанием:
Copy Source | Copy HTML
  1. <p>email: #EMAIL#</p>
  2. <p>id: #ID#</p>
  3. <p>confirm code: #CONFIRM_CODE#"</p>
  4. <a href="http://www.tetrapolis.ru/service/edit_subscribe.php?ID=#ID#&CONFIRM_CODE=#CONFIRM_CODE#">Сменить адрес подписки</a>
  5. <a href="http://www.tetrapolis.ru/service/edit_subscribe.php?ID=#ID#&CONFIRM_CODE=#CONFIRM_CODE#&action=unsubscribe">Отписаться</a>
Ссылки должны вести на страницу с компонентом редактирования рассылки.
Если Вас заинтересовал метод Csubscription::GetByEmail() то у меня есть пример на работу с ним:
Copy Source | Copy HTML
  1. <?
  2. if(CModule::IncludeModule("subscribe")){
  3.     class CatchSubscriter {
  4.         // создаем обработчик события "BeforePostingSendMail" 
  5.         function BeforePostingSendMailHandler(&$arFields){
  6.             //Попробуем найти подписчика. 
  7.             $rs = CSubscription::GetByEmail($arFields["EMAIL"]);
  8.             if($ar = $rs->Fetch()){
  9.                 echo "<p>[</p>";
  10.                 print_r($ar);// посмотрим какие еще параметры можно получить
  11.                 echo "<p>]</p>";
  12.                 $arFields["BODY"] = str_replace("#CONFIRM_CODE#", $ar["CONFIRM_CODE"], $arFields["BODY"]);
  13.                 $arFields["BODY"] = str_replace("#ID#", $ar["ID"], $arFields["BODY"]);
  14.                 $arFields["BODY"] = str_replace("#EMAIL#", $ar["EMAIL"], $arFields["BODY"]);
  15.             }
  16.             return $arFields;
  17.         }
  18.     }
  19.     $cs1= new CatchSubscriter();
  20.     $ar=array("EMAIL" => "vklad@tetrapolis.spb.ru", "BODY" => " body ");
  21.     $rez=$cs1->BeforePostingSendMailHandler($ar);
  22.     print_r($rez);
  23. }else{
  24.     echo "nothing to load";
  25. }
  26. ?>

6 ноября 2011 г.

"Unexpected token u" в Chrome


Столкнулся в Chrome (v14) с таким сообщением ошибки:
"Uncaught SyntaxError: Unexpected token u"
Проблема проста особенно для языков с динамической типизацией, на вход метода JSON.parse попала undefined.
Обычно такое сообщение возникает, когда в исходном тексте попадаются лишние символы. Но в данном примере стандартный метод просто решил выкинуть ошибку с невнятным описанием, вместо того, чтобы вернуть undefined. Баг в Chrome?

Ошибку можно с имитировать:
var demo1={field1:"a",field2:"b"};
var str=JSON.stringify(demo1);
console.log(str);
console.dir(JSON.parse(str)); // Ok
console.dir(JSON.parse(undefined)); // Exception

4 ноября 2011 г.

События keydown и keypress в Javascript



По работе в плотную встретился с двумя этими событиями, и хочу поделиться своим опытом работы с ними. Заодно приведу пример использования этих событий.
Дело осложнаятся тем, что нету стандарта для событий нажатий клавиш, и поэтому имеются рассхождения в работе в разных браузерах. События keydown и keypress в объекте события могут содержать несколько свойств, характеризующих нажатую клавишу:
  • charCode - это unicode значение символа связанного с нажатой клавишей;
  • keyCode - код клавиш;
  • which - идентичен charCode.
Для пояснения приведу пример - вводимые символы 'a' и 'A' будут иметь одинаковый keyCode, но разыный charCode.
Событие keyPress служит для обработки печатных клавиш (printable characters), т.е. те которые имеют печатные символы: A-Z, 0-9, Enter, Backspace.
Но есть один неприятный момент с этим событием - по своему назначению работет только в IE и Chrome(Webkit браузерах). В FireFox и Opera объект события возвращает коды  и клавиш с непечатными символами (Non printable characters) - клавиша Del, стрелочки. Причем коды для печатных и непечатных символов могут совпадать.
Событие keyDown - объект события "отлавливает" как коды печатных символов, так и не печатных символов. При этом код каждой клавиши будет уникальным.
Подробнее про эти два события можно почитать на  http://www.quirksmode.org/js/keys.html

Варианты значений свойств объекта события keypress для разных браузеров:
Chrome 15/Safari 5.1.1
Примечание: N- целое число не равное нулю.
Клавиши keyCode charCode which
Printable characters N N N
Non pintable characters - - -
Opera 11.52:
Клавиши keyCode charCode which
Printable characters N undefined N
Non pintable characters N undefined 0
FireFox 7:
Клавиши keyCode charCode which
Printable characters 0 N N
Non pintable characters N 0 0
IE6/7/8/9:
Клавиши keyCode charCode which
Printable characters N undefined undefined
Non pintable characters - - -

Привожу несколько примеров работы с событиями keypress и keydown.
Последний пример где производится проверка ввода - а именно в поле разрешается вводить только вещественное положительное число, имеет небольшой недостаток при использование. Текст можно ввести не только с клавиаткры, но и вставить из буфера обмена и подстановки самого браузера.
JS код:
Copy Source | Copy HTML
  1. window.onload=function(){
  2.     var tst1=id("tst1"),
  3.         dbg1=id("dbg1"),
  4.         tst2=id("tst2"),
  5.         dbg2=id("dbg2");
  6.     event(tst1,"keypress",function(e){
  7.         var nonPrintable,printable; // параметры с помощью которых попытаемся решить проблему с кросс браузерностью
  8.         if((e.charCode=== 0||e.charCode===undefined)&&e.which=== 0)
  9.             nonPrintable=e.keyCode;
  10.         printable=e.which?e.which:(e.which===undefined&&e.charCode===undefined)?e.keyCode:undefined;
  11.         dbg1.innerHTML="keyCode: "+e.keyCode+", charCode: "+e.charCode+", which: "+e.which+
  12.         " #### caf: "+printable+", yk: "+nonPrintable;
  13.     });
  14.     event(tst2,"keydown",function(e){
  15.         dbg2.innerHTML="keyCode: "+e.keyCode+", charCode: "+e.charCode+", which: "+e.which;
  16.     });
  17.     var num1=id("num1");
  18.     // тегу input добавлем проверку ввода
  19.     event(num1,"keydown",function(e){
  20.         e = (e)?e: (window.event)?window.event:undefined;
  21.         var target;
  22.         if(e) {
  23.             target = (e.target)?e.target:e.srcElement;
  24.         }
  25.         console.dir(e);
  26.         if (e.keyCode===13){// Enter if need
  27.             e.preventDefault? e.preventDefault() : e.returnValue = false;
  28.         }
  29.         if ((e.keyCode>47&&e.keyCode<58)||e.keyCode===8||e.keyCode===37||e.keyCode===39||e.keyCode==46){
  30.             e.returnValue=true; // [0..9] -> [47..58], 8 - backsace, 37 - Left, 39 - Right, 46 -Delete
  31.         }else if (e.keyCode===188 || e.keyCode===190){
  32.             var s=target.value, // input field!
  33.                     i= 0,count= 0;
  34.             while (s[i]){
  35.                 if (s[i]==='.' || s[i]===',')
  36.                     count++;
  37.                 i++;
  38.             }
  39.             e.returnValue=count=== 0;// Точка или запятая может быть только одна в водимом числе
  40.             if(count!== 0 && e.preventDefault)// Condition for FireFox
  41.                 e.preventDefault();
  42.         }else{
  43.             e.preventDefault? e.preventDefault() : e.returnValue = false;
  44.         }
  45.     });
  46. };
  47. function id(s){
  48.     return document.getElementById(s);
  49. }
  50. function event(obj,name,handler){
  51.     if (obj.attachEvent){
  52.         obj.attachEvent("on"+name,handler);
  53.     }else{
  54.         obj.addEventListener(name,handler);
  55.     }
  56. }
html код:
Copy Source | Copy HTML
  1. <p id="tst1" contenteditable="true">Пример на событие keypress</p>
  2. <p id="dbg1"></p>
  3. <p id="tst2" contenteditable="true">Пример на событие keydown</p>
  4. <p id="dbg2"></p>
  5. <p>Пример - Введите число 1: <input type="text" id="num1"/></p>

16 октября 2011 г.

О чем может рассказать Ваш аккаунт в Gmail


Интересные заметки про сбор данных Гуглом. Т. е. то что недавно обнаружил у себя под боком.
Google, конечно же сделал правильно, что все свои сервисы прикрепляет к одному аккаунту, но.
В дебрях интерфеса gmail есть ссылка на "Google Личный кабинет" или "Google аккаунты"  ( ссылка https://www.google.com/dashboard/ ) , где можно узнать к каким сервисам гугла ВЫ подключены.

Пункты меню по пикантности данных
Почта, записи в блогах, Google Документы - пускай сохраняют (это нормально в личном кабинете).



Пункт "История веб-поиска"
По умолчанию сохраняет все запросы. На всякий случай отключил.



Пункт "Устройство Android"
Любопытный момент:

  • Определил устройство Android;
  • Сохраняет IMEI и время последний активности.

Так как я редко выхожу в инет с телефона и gps пользуюсь когда припрет, то ...

  1. В общем в тот день и в следующий я выходил в инет
  2. Определял свое местоположение с помощью GPS + подгружал карты (Яндекс.карты)

Пункт "Android Маркет"


То же отслеживает дату "последнего включения"

Вывод:
С точки зрения централизованного хранения google сделало все правильно. Но это только если к этим данным имеете доступ только ВЫ. Информация довольно таки-избыточна, так что не стоит подпускать третьи лица к своему аккаунту.

9 октября 2011 г.

Альтернатива тегу center или центрирование по горизонтали html элементов


В 2011 еще можно обнаружить случаи, когда приходится сталкиваться с применением древнего тега center. Примером такой встречи из повседневной работы может быть cms Bitrix, где тег center применяется довольно часто (да и другие древние теги, такие как font тоже).
Поэтому в этом посте хочу привести примеры, как можно перейти от использования тега center к использованию возможностей css.

Центрирование таблицы по горизонтали средствами css:
<table style=”margin: 0 auto;”>...</table>

Центрируем изображение по горизонтали:
<img src="fig1.jpg" style=”margin: 0 auto;”/>
Центрируем текст в выпадающем списке select
Работает только в FireFox и Opera!
html код:
<select>
	<option value="..." checked>...</option>
	<option value="...">...</option>
</select>
css код:
select option{
	text-align:center;
}
Выравнивание по центру Гиперссылки
Так к тегу по умолчанию установлено свойство display присвоено значение inline, то это свойство надо изменить на block:
<a href="#" style="text-align:center;display:block;">link</a>

Назначение шаблонов в Bitrix

Содержание поста довольно тривиально, по край ней мере для тех, кто достаточно долго работает с CMS Bitrix. Поэтому следует рассказать о причинах побудивших написать пост на такую скучную тему.
Осень 2011 года — компания 1C Bitrix анонсировала Bitrix 11.0 . Из пресс-релиза, как я понял, ничего, такого капитального не предвидится (в 10-й тоже ничего не было). Система прирастает, только раскрученными в IT/бизнес медиа фишками — облачными технологиями, поддержкой мобильных устройств, оставаясь по прежнему на уровне начала 2000-ных. Да система Bitrix построена на тех «практиках» которые уже редко где можно найти работающими, в этом можно убедится просмотрев исходные коды.
Интерфейс с помощью которого происходит назначения правил применения шаблонов в системе Битрикс — к сожалению, не удачный. Шаблоны применяются согласно правилам, которые не как не вытекают из дизайна интерфейса. Описанию по работы с ним мало. Официальная документация лаконична, как энциклопедическая справка.
Основные проблемы.
 Назначаются шаблоны различным ветвям структуры сайта, для тех кто не знает, в разделе «Редактирования сайта», который надо искать в Администрирование  → Настройки. Назначение шаблонов в CMS Битрикс реализовано довольно таки топорно. Во первых явно учитывается иерархия в виде порядка сортировки. Во вторых учитывается иерархия файловой системы.
К примеру имеется такой фрагмент файловой структуры и задаем шаблоны с помощью типа условия «для папки и файла» (о типах условиях так же будет написано далее).
/folder1
/sub_folder1
/sub_folder2
 Так если назначить какой либо шаблон для папки sub_folder1 c порядком сортировки 2, а /folder1 назначить другой шаблон иномер сортировки 1, то и у subfolder1 (да и subfolder2 и у всех остальных дочерних) будет тот же шаблон, что и у folder1 не смотря на то какой шаблон был им задан. Если порядок сортировки задать наоборот: sub_folder1 - 1, folder1 - 2. То тогда у каждой директории будут свои шаблоны.
Типы условия.
Одни из самых "ходовых" типов условий это:
  • для папки и файла 
  • выражение PHP 
Недостаток условия для "Папок и файлов" - это то что он задается для одной папки (и всех его дочерних директорий и файлов)/файла. И как следствие его придется задавать почти для каждой папки если получился сайт с "богатой" палитрой шаблонов. В помощь может пригодится тип условия «выражение PHP» Вариант с использованием выражения php:
preg_match("/\/yslygi_phis_licam\/(?:obmen_valyti|migom|denejnie_perevodi|bank_seif|priem_plateghei|internet_bank)\//i",$APPLICATION->GetCurDir())
Здесь уже можно использовать регулярные выражения. Почти как в кошерных Django или Ruby On Rails. [Вопрос про производительность такого решения лучше незадавать]
Таким образом, используя регулярные выражения, задаем правило назначения шаблона для адресов:
*/yslygi_phis_licam/obmen_valyti/*
*/yslygi_phis_licam/migom/*
*/yslygi_phis_licam/denejnie_perevodi/*
*/yslygi_phis_licam/bank_seif/*
*/yslygi_phis_licam/priem_plateghei/*
*/yslygi_phis_licam/internet_bank/*
И как следствие уменьшаем муки + повышаем производительность труда разработчика по сравнению с применением  типа условия «для папки и файла».


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

Перехват кликов на flash элементах при помощи java script


Иногда случается так что у тебя имеется flash баннер с «темной историей», т. е. не известно, кто и когда (зачем/почему?) его создал, но необходимо из него  сделать работающий баннер — чтобы при клике на него происходил переход в нужный раздел сайта.
Вариант как это делать, не обрабатывая сам flash баннер поместить под прозрачный элемент. Этот элемент может быть div элементом или gif картинкой 1 на 1 px.
Поэтому при помощи верстки необходимо разместить html объект по верх баннера,
и при помощи javascript прикрепить необходимые обработчики событий к этому объекту.

Html код:
<div class="flash-container">
<!-- Атрибут wmode="transparent" тега embed обязателен, иначе ничего не получится -->
	<embed  class="banner" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" src="http://www.tetrapolis.ru/flash/a7.swf" salign="top" autoplay="true" loop="true" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" wmode="transparent"></embed>
	<div class="flash-fon" id="click-field">
	</div>
</div>
CSS код:
/* styles for flash banners: */
/* Контейнер для flash и сам flash д.б. одного размера*/
embed.banner, .flash-container{
	width:1200px;
	height:165px;
}
.flash-container{
	/*border: 1px solid black;*/
	position:relative;
}
div.flash-fon{
	position:absolute;
	top:0;
	left:0;
	width:100%;
	height:100%;
	background:transparent;
	cursor:pointer;
}
/* :styles for flash banners */
Javascript код:
Javascript код:   
var setEvent=(function(){
if (window.addEventListener!=null){
	return function(obj,eventName,handler){
		obj.addEventListener(eventName,handler,false);
	}
}else{
	return function(obj,eventName,handler){
		obj.attachEvent("on"+eventName,handler);
	}
}
}());
setEvent(window,"load",function(){
	var clickField=document.getElementById("click-field");
	setEvent(clickField,"click",function(){
		location.href="http://tetrapolis.ru";
	});
});

23 сентября 2011 г.

Haskell. Начало программирования. Матрицы.

Небольшая предыстория о том, как начал программировать на Haskell-е
Среда
После встречи piter.rb#4 (в позапрошлую среду) познакомился через докладчиков с языком Haskell. Язык показался интересным для изучения и непонятным, с точки зрения применения. Т. е. нахрена он нужен.
Четверг
Почитал на wikipedia, на паре других ресурсах... меня терзало
Пятница
Невыдержал. Установил компилятор GHC. Накачал книжек в pdf формате. И покодил (вместо телевизара и прсомотра фильмов с торрентов). Да, я отказался от просмотра последних филтьмов с торрентов и просмотра передач с канала Discavery в пользу программирования на Haskell. На Haskell - как основной язык программирования не подсел - так чисто любительское.
Суббота
Меня осенило. Haskell чудокават, но в полне по силам его освоить. Ребята (Дронов к примеру) не так позиционирует язык. Многие термины используемые в программировании Haskell не являются чем-то не знакомым обычным (особенно после внятной расшифровки) программистам императивщикам.
Сегодня
Неделю спустя, все еще программирую на запале интереса...

Объективные "-" компилятора ghc (так как он единственный для языка то и языка )
  1. Довольно объемный исполняемый файл ~ 1 Mb,
  2. Быстрой работы пока не заметил.
Самокритика объективных "-"
  • А нужна ли эффективность-быстродействие программы?
Для практики написал программу с реализацией на Haskell алгоритма подсчета определителя матрицы, перемножения двух матриц. Функцию транспонирования матриц на Haskell позаимствовал в рунете:

main=do
	print "Matrix detrminant:"
	print (matrixDet [[1,2,3],[7,5,6],[7,8,9]])
	print (matrixDet [[1,2],[4,5]])
	print (matrixDet [[1]])
	print (matrixDet [[]])
	print $ matrixDet [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]
	print $ matrixDet [[7,4,6,0],[3,1,2,4],[9,7,2,3],[1,5,8,2]]
	print $ matrixDet [[(-7.9),4.1,6.7,0],[3,1,2,4],[9,7,2.3,3],[1,5,8,2]]
	print "Get row & column:"
	print $ getRow [[1,2,3],[4,5,6],[7,8,9]] 1 
	print $ getColumn [[1,2.9,3.0],[4,5.7,6],[7,8,9]] 1
	print "Multiplication of lists:"
	print $ sum $ map (\(x,y) -> x*y) (zip [1,2,3] [3,0,6])
	print $ sum $ zipWith (*) [1,2,3] [3,0,6]
	print $ sum $ zipWith (*) [1,2,3] [3,0]
	print "Matrix transponation:"
	print $ matrixTransp [[1,2,3],[1,2,3]]
	print $ transp [[1,2,3],[1,2,3]]
	print "matrix mult:"
	print $ matrMult [[1,2,3],[7,9,8]] [[8,7],[5,4],[1,0]]
	print $ matrMult [[1,2,3],[7,9,8]] [[5],[6],[7]]
	print $ matrixDet $ matrMult [[1,2,3],[7,9,8]] [[8,7],[5,4],[1,0]]
 
-- Функция подсчета определителя матрицы
matrixDet [[]] = 0
matrixDet [[x]] = x
matrixDet [[x1, x2], [x3, x4]] = x1*x4 - x2*x3
matrixDet m = let row = head m in
	sum (indexMap (\(x,i)->x*(znakFunc i)*(matrixDet (minor m 0 i))) row)
	where
	znakFunc x = if x`mod`2==0 then 1 else (-1) -- (-1)^(i+j), i=0, j=0.. 
-- перебор списка с индексом, \(x,i) -> ... , but i is Enum
	indexMap _func _list = map _func (zip _list [0..])
-- фильтрация списка с учетом индекса элемента в списке:
	indexFilter _func _array = map (\x -> fst x) (filter _func (zip _array [0..])) 
-- функция определения матрицы минора
	minor :: (Num a, Integral b) =>[[a]] -> b ->b -> [[a]] 
	minor matrix _i _j = map (\row -> indexFilter (\(_,j)->not(j==_j)) row) 
		(indexFilter (\(_,i)-> not(i==_i) ) matrix)
 
-- примитив: функция для извлечения строки
getRow :: (Integral b, Num a) => [[a]] -> b -> [a]
getRow m i = (m!!(fromEnum i))
 
-- примитив: функция для извлечения вектора
getColumn :: (Integral b, Num a) => [[a]] -> b -> [a]
getColumn m j = map (\row -> row!!(fromEnum j) ) m
 
-- 1-й вариант транспонирования матрицы:
matrixTransp :: [[a]] -> [[a]]
matrixTransp m = [[m!!i!!j| i<-[0..((length m)-1)]] |j<-[0..(length(m!!0)-1)]]
-- 2-й вариант транспонирования матрицы. Определение транспонированной матрицы через рекурсию:
-- транспонированная матрица - это такая матрица, первая строка которой равна
-- первому столбцу исходной, а остаток равен траспонированному остатку исходной
transp ([]:_) = []
transp m = map head m: transp (map tail m)
 
-- Функция перемножения двух матриц
matrMult :: (Num m)=> [[m]]->[[m]]->[[m]]
matrMult m1 m2= let _m2 = (transp m2) in 
	map (\r1-> (map (\c2-> sum(zipWith(*) r1 c2)) _m2 )) m1
-- Здесь чтобы во второй матрице не извлекать вектор (строку у добнее) 
-- вторую матрицу просто транспонируем в _m2
 

8 августа 2011 г.

Кросдоменный ajax с помощью javascript


В прошлом посте был описан способ передачи данных на сервер с помощью тега img на странице. Но он не единственный способ.
Для этих целей можно приспособить тег script, котороый так же будет передовать данные на сервер и еще асинхронно передавать ответ обратно.
html код:
Copy Source | Copy HTML
  1. <input type="text" id="textField"/>
  2. <input type="button" id="sendMessage" value="Отправить IMG"/>
  3. <img id="imaGet" src="picture.php?add=hello" style="width:30px;height:30px;"/>
javascript код:
Copy Source | Copy HTML
  1. function ScriptDataSend(urlEncoded_str,callBack_func){
  2.     ////generate uniq id for elements (I don't check it, only random!!)
  3.     var uniqId=(Math.random()*10000000).toFixed( 0);
  4.     //console.log("uniq: %s",uniqId);
  5.     ////add script element and encoded data
  6.     var th = document.getElementsByTagName("body")[ 0];
  7.     var s = document.createElement('script');
  8.     s.setAttribute('type','text/javascript');
  9.     s.setAttribute('src',"script.php?uid="+uniqId+"&add="+urlEncoded_str);
  10.  
  11.     s.setAttribute('id',uniqId);
  12.     ////auto remove script element
  13.     s.onload=function(){
  14.         //console.log("I'm loaded");
  15.         s.parentNode.removeChild(s);
  16.         delete window.responceCollection[uniqId];
  17.     }
  18.     th.appendChild(s);
  19.     delete s;
  20.     //// add callback in globalScope!
  21.     if (window.responceCollection==undefined)
  22.         window.responceCollection={};
  23.     window.responceCollection[uniqId]=callBack_func;
  24. }
  25.  
  26. var messageField=document.getElementById("textField");
  27. var sendButton2=document.getElementById("sendMessage2");
  28. sendButton2.addEventListener("click",function(){
  29.         ScriptDataSend(encodeURIComponent(messageField.value),function(time){
  30.             console.log("Get Javascript response: %s",time);
  31.         });
  32.     },false);
Функция ScriptDataSend создает тег script с заранее определенным url адресом и уникальным id (uid),  чтобы используя этот uid задать уникальный обработчик события для этого соединения в глобальном хэше обработчиков window.responceCollection (другого "красивого" варианта реализации пока не придумал ...).
код файла script.php:
Copy Source | Copy HTML
  1. <?php
  2. header('Content-type: text/javascript');//Делаем вид что javascript
  3. $time=date("H:i:s");
  4. $uid=$_GET["uid"];
  5. // отправляем ответ
  6. echo "window.responceCollection[\"".$uid."\"]('".$time."');";
  7. //- сохраняем полученные данные
  8. $fileName="script_data.txt";
  9. if (strlen($_GET["add"])> 0){
  10.     $fh = fopen($fileName, "a+");
  11.     fwrite($fh, urldecode($_GET["add"])." ".date("H:i:s")."\n");
  12.     fclose($fh);
  13. }
  14. ?>
Как видите, script.php формирует ответ в виде набора данных и передает их в обработчик с уникальным id (uid).
Повозможности никакого управляющего кода на js в php скрипте не генирируется, чтобы было удобнее отлаживать.

Недостатки метода отправки данных сервер с помощью тега script:

  1. только запросы get
  2. вся отладка на плечи кодера
  3. добавление и удаление элемента из DOM
  4. обработчик в глобальной области видимости 

Кросдоменный ajax с помощью тега IMG

С помощью тега img можно отправить данные на сервер. Таким же образом действует и счетчик Яндекс Метрики, когда отправляет данные о кликах посетителей на страницах сайтов.
Создавать фейковую картинку 1px x 1px необязательно, картинка может быть обычной и может служить элементом декора на странице.
И при этом, главное, что, может передавать данные на сервер.
html код:
Copy Source | Copy HTML
  1. <input type="text" id="textField"/>
  2. <input type="button" id="sendMessage" value="Отправить IMG"/>
  3. <img id="imaGet" src="picture.php?add=hello" style="width:30px;height:30px;"/>
javascript код:
Copy Source | Copy HTML
  1. var messageField=document.getElementById("textField");
  2. var sendButton=document.getElementById("sendMessage");
  3. var imageElement=document.getElementById("imaGet");
  4. //Отправка данных на сервер методом get:
  5. sendButton.addEventListener("click",function(){
  6.     //определяем куда в аддресс вставлять сообщение по позиции фрагмента:
  7.     var pos= imageElement.src.indexOf("add=");
  8.     var newLink= imageElement.src.substring( 0,pos)+"add="+encodeURIComponent(messageField.value);
  9.     imageElement.src=newLink;//Присваиваем новый адресс - пофакту отправляем сообщение на сервер
  10. },false);
код файла picture.php:
Copy Source | Copy HTML
  1. <?php
  2. header('Content-type: image/jpeg');
  3. // Можно вывести картинку
  4. $content = implode ( "", file ( 'image.jpg' ) );
  5. echo $content;
  6. // Полученные данные можно отлажить куда-нибудь
  7. $fileName="data.txt";
  8. if (strlen($_GET["add"])> 0){
  9.     $fh = fopen($fileName, "a+");
  10.     fwrite($fh, urldecode($_GET["add"])." ".date("H:i:s")."\n");
  11.     fclose($fh);
  12. }
  13. ?>
Недостатки метода отправки данных сервер с помощью тега img:
  1. только запросы get;
  2. вся отладка на плечи кодера.