X   Сообщение сайта
(Сообщение закроется через 3 секунды)



 

Здравствуйте, гость (

| Вход | Регистрация )

Открыть тему
Тема закрыта
> Динамическое получение данных
Mutant
Mutant
Topic Starter сообщение 2.8.2008, 21:00; Ответить: Mutant
Сообщение #1


Доброго времени суток.

Задача следующая: при нажатии на ссылку в блок должен загружаться контент, вынутый из БД, без перезагрузки страницы.
0
Вернуться в начало страницы
 
Ответить с цитированием данного сообщения
Diablo_hb
Diablo_hb
сообщение 3.8.2008, 10:54; Ответить: Diablo_hb
Сообщение #2


Subsys_JsHttpRequest: подкачка данных без перезагрузки страницы (AJAX)

Не так давно определенную популярность получил новый сервис Google: так называемый Google Suggest. Те, кто еще не видел, что это такое, могут посмотреть прямо сейчас: http://www.google.com/webhp?complete=1&hl=en..

Работа Google Suggest заключается в том, что по нескольким введенным буквам специальная программа на JavaScript обращается к сайту Google и запрашивает у него 10 самых «популярных» слов, начинающихся с тех же букв. Скрипт срабатывает настолько быстро, что выпадающий список с вариантами появляется практически мгновенно. Естественно, перезагрузка страницы при этом не производится — все реализовано на JavaScript и DHTML.

Для реализации «динамической подгрузки» Google использует следующие средства:

1. В Internet Explorer: ActiveX-компонента с именем Msxml2.XMLHTTP или Microsoft.XMLHTTP.
2. В Mozilla и FireFox: встроенный класс XMLHttpRequest.
3. В Opera: динамически создаваемый нулевого размера (скрытый).

Недостатки подхода Google Suggest


Итак, в разных браузерах Google применяет совершенно различные методы подгрузки. Рассмотрим их недостатки.

1. Т.к. в IE используется ActiveX-компонента, вы должны включить ActiveX в настройках браузера. И хотя по умолчанию данная функция как раз включена, многие пользователи, наслышанные о многочисленных дырах IE, вручную ее отключают.


Лирическое отступление

Несмотря на это, вы все же можете убедиться, что Google Suggest продолжает работать и после выключения ActiveX. Видимо, задействуется механизм, основанный на (как в Opera). В любом случае, на ваших сайтах Microsoft.XMLHTTP при выключенных ActiveX работать не будет (это проверено). Про недостатки -метода сказано ниже.
2. Класс XMLHttpRequest, используемый в Mozilla и FireFox, в настоящий момент присутствует только в этих браузерах (поддержка этого класса в Opera 8.01 весьма ограничена). У него есть небольшой недостаток: при умолчательных настройках FireFox запрещено загружать данные откуда-то, кроме как с текущего сайта.
3. Применение динамически создаваемого связано с массой проблем. Главный недостаток — при изменении атрибута src у раздается характерный щелчок и добавляется запись в «историю браузера», так что кнопка Back (Назад) начинает работать неправильно. И хотя данный «подводный камень» можно обойти (весьма искусственным способом), возникают новые проблемы, различные в разных браузерах. Я не буду их сейчас перечислять; скажу только, что за 2 дня перепробовал множество (штук 20) всевозможных вариантов, но добиться кроссбраузерного кода, работающего одинаково и без посторонних эффектов во всех браузерах, мне так и не удалось. Другой недостаток — большой расход памяти и медлительность: фактически, для каждого фрейма создается новый отдельный браузер, который независимо обрабатывает загруженный HTML-код.

Короче говоря, Google использует разные (ортогональные, несовместимые) подходы в различных браузерах.


Принцип работы Subsys_JsHttpRequest


Многочисленных проблем и особенностей с ActiveX, XMLHttpRequest и можно избежать, если... не использовать данные технологии. Это звучит действительно банально, однако — работает!

Дело в том, что существует один прекрасный и более-менее кроссбраузерный способ загрузки данных на страницу. Очень странно, что разработчики Google до него не догадались. Речь о динамическом создании и присоединении к текущей странице тэга . Такому тэгу следует указать атрибут src, совпадающий с адресом серверного скрипта подгрузки данных (написанного, к примеру, на PHP).
Вернуться в начало страницы
 
Ответить с цитированием данного сообщения
Diablo_hb
Diablo_hb
сообщение 3.8.2008, 10:56; Ответить: Diablo_hb
Сообщение #3


Разложим и обжуем для Чайников

Конечно, загружаемый скрипт должен выдавать корректный код на JavaScript. Обычный текст таким методом не подгрузишь.

Рассмотрим на примере, как работает данный подход. Предположим, что при нажатии на кнопку JavaScript-программа вставляет (c использованием DHTML) в текущую страницу следующий тэг:


<script language="JavaScript"
src="load.php?ff=ok&opera=sucks&123"></script>


Что при этом произойдет? Браузер немедленно обратится к серверу со следующим запросом:

Листинг 3 скопировать код в буфер обмена


load.php?ff=ok&opera=sucks&123


В результате на сервере запустится скрипт load.php, который получит в QUERY_STRING параметры ff=ok&opera=sucks&123 (конечно, аргументы могут быть произвольными). Программа отработает (к примеру, обратится к базе данных) и напечатает в качестве результирующей страницы следующий текст:

Листинг 4 скопировать код в буфер обмена


Subsys_JsHttpRequest_Js.dataReady(
123,
[
'Это некоторые данные.',
'Они могут иметь произвольную структуру...',
{ test: '...и вложенность' }
],
'А здесь идет простой отладочный текст.'
)


Итак, PHP-скрипт load.php напечатал в свой выходной поток текст, являющийся по совместительству корректной JavaScript-программой. Он будет использован браузером в качестве источника данных произвольной структуры.

М-ммм... «Программа, пишущая другие программы»... «Источник»... Определенно «Matrix has you».

В итоге код на JavaScript, сгенерированный PHP-скриптом load.php, будет выполнен браузером! Как видите, вызывается метод dataReady() объекта Subsys_JsHttpRequest_Js, которому передается:

1. Уникальный идентификатор загрузки (чтобы не спутать одни данные с другими, ведь страница может одновременно запросить сведения сразу из нескольких источников).
2. Произвольные данные, полученные программой load.php, например, из БД.
3. Некоторый текст, который может быть использован в отладочных целях (например, там удобно указывать сообщения об ошибках, возникших в PHP-программе).

Ну а уж функция Subsys_JsHttpRequest_Js.dataReady() заботится о доставке загруженых данных конечному потребителю, осуществляя также кэширование одинаковых запросов (если это разрешено).

Динамическая генерация тэга <script> имеет одно важное достоинство: при использовании такого подхода «история» браузера (history) не засоряется лишними ссылками, а при загрузке не слышно щелчка, издаваемого многими браузерами во время перехода на другую страницу. Нужно также заметить, что в FireFox имеется небольшая ошибка, в результате которой статус-строка не очищается после загрузки <script>-компонента (в ней остается сообщение "Loading ..."). Впрочем, эта ошибка ни на что не влияет и, вероятно, будет в скором времени исправлена разработчиками.

Библиотека Subsys_JsHttpRequest состоит из двух частей, работающих совместно друг с другом:

* Subsys/JsHttpRequest/Js.js, 8 КБ: JavaScript-код, определяющий класс-объект Subsys_JsHttpRequest_Js. Это — так называемый frontend системы («передний проход»). Его следует подключать к страницам с помощью тэга:

Листинг 5 скопировать код в буфер обмена


<script language="JavaScript" src="Subsys/JsHttpRequest/Js.js">
</script>



* Subsys/JsHttpRequest/Php.php, 10 КБ: PHP-код, в котором определяются функции для облегчения написания загрузчиков на PHP. Это — так называемый backend системы («задний проход»). Его следует включать в самое начало программы оператором:

Листинг 6 скопировать код в буфер обмена

require_once "Subsys/JsHttpRequest/Php.php";


В качестве языка для написания загрузчиков выбран PHP, потому что он:

* Весьма распространен.
* Крайне быстр, если приходится работать с маленькими скриптами, коими как раз и являются загрузчики. (Это, естественно, касается только mod_php — он так чаще всего и ставится хостерами.)
* В большинстве случаев имеет встроенную поддержку Unicode (расширение iconv), которая, как вы увидите ниже, нам очень понадобится.

Можно, конечно, писать скрипты загрузки и на CGI-perl, однако в этом случае нагрузка на сервер резко возрастет, что для динамической подгрузки данных противопоказано. Ну а mod_perl встретишь далеко не на каждом хостинге.
Класс Subsys_JsHttpRequest_Js: frontend

Использовать объект Subsys_JsHttpRequest_Js в JavaScript-программе совсем просто. Собственно, его интерфейс практически не отличается от интерфейсов FireFox-овского XMLHttpRequest или IE-шного Microsoft.XMLHTTP (он специально так разрабатывался).

Приведу пример страницы, которая обеспечивает генерацию хэш-кода MD5 для введенной пользователем строки. Само вычисление происходит на сервере, а браузер лишь обращается к последнему за данными, используя объект Subsys_JsHttpRequest_Js. (Этот же пример в действии.)

Листинг 7 скопировать код в буфер обмена


<?php
// Проверка работы с сессиями.

session_start();
$_SESSION['hello'] = 'Backend loaded at '.date('r');
?>
<html>
<head></head>
<body>

<script language="JavaScript"
src="../../../lib/Subsys/JsHttpRequest/Js.js"></script>
<script>
// Вызывается по тайм-ауту или при щелчке на кнопке.

function doLoad(force) {
// Получаем текст запроса из <input>-поля.

var query = '' + document.getElementById('query').value;
// Создаем новый объект JSHttpRequest.

var req = new Subsys_JsHttpRequest_Js();
// Код, АВТОМАТИЧЕСКИ вызываемый при окончании загрузки.

req.onreadystatechange = function() {
if (req.readyState == 4) {
if (req.responseJS) {
// Записываем в <div> результат работы.

document.getElementById('result').innerHTML =
'MD5("'+(req.responseJS.q||'')+'") = ' +
'"' + (req.responseJS.md5||'') + '"<br> ' +
'Session data: ' +
'"' + (req.responseJS.hello || 'unknown') + '"';
}
// Отладочная информация.

document.getElementById('debug').innerHTML =
req.responseText;
}
}
// Разрешаем кэширование (чтобы при одинаковых запросах

// не обращаться к серверу несколько раз).

req.caching = true;
// Подготваливаем объект.

req.open('POST', 'load.php?test=abc', true);
// Посылаем данные запроса (задаются в виде хэша).

req.send({ q: query, test:303 });
}
// Поддержка загрузки данных по тайм-ауту (1 секунда после

// последнего отпускания клавиши в текстовом поле).

var timeout = null;
function doLoadUp() {
if (timeout) clearTimeout(timeout);
timeout = setTimeout(doLoad, 1000);
}
</script>
<!-- Форма -->
<a href="<?=$_SERVER['REQUEST_URI']?>">Reload myself</a>
<form onsubmit="return false">
<input type="text" id="query" onkeyup="doLoadUp()">
<input type="button" onclick="doLoad(true)" value="load">
<br><i>Введите "error", чтобы протестировать отладочные возможности библиотеки.</i>
</form>
<!-- Результаты работы (заполняется динамически) -->
<div id="result" style="border:1px solid #000; margin:2px">
Results
</div>
<!-- Отладочная информация (заполняется динамически) -->
<div id="debug" style="border:1px dashed red; margin:2px">
Debug info
</div>

</body>
</html>
<hr>
<?show_source(__FILE__)?>


Из-за обилия комментариев выглядит страшно, однако, если внимательно посмотреть, хорошо видно, что применение Subsys_JsHttpRequest_Js ничем принципиальным не отличается от использования XMLHttpRequest или Microsoft.XMLHTTP.

Имеется одна важная особенность библиотеки: результат работы load.php удобно получать из свойства req.responseJS. Как видно, в него загрузчик помещает следующий хэш:

Листинг 8 скопировать код в буфер обмена


{
q: 'запрос',
md5: 'MD5-код введенной строки'
}



В поле req.responseText хранятся данные, выданные скриптом load.php в свой выходной поток (операторами echo). В большинстве случаев они содержат лишь сообщения об ошибках (если ошибки имели место), и именно поэтому данное свойство трактуется как отладочное.

Небольшое лирическое отступление

Впрочем, ничто не мешает написать загрузчик так, чтобы он передавал основной результат своей работы именно в виде req.responseText (хотя это и не очень удобно — см. ниже).
Вернуться в начало страницы
 
Ответить с цитированием данного сообщения
Diablo_hb
Diablo_hb
сообщение 3.8.2008, 10:57; Ответить: Diablo_hb
Сообщение #4


Класс Subsys_JsHttpRequest_Php: backend


Теперь пришло время посмотреть, как выглядит загрузчик load.php. Помните, мы говорили, что результатом его работы должен быть текст, являющийся корректным JavaScript-кодом. Т.е. просто вывести "Привет, это сгенерированные данные!" нельзя — нужно вначале «обернуть» их вызовом функции Subsys_JsHttpRequest_Js.dataReady(). Вы можете подумать: сколько же мороки возникает из-за этого... Ведь достаточно допустить одну маленькую ошибку (к примеру, пропустить запятую), как результирующий код перестанет быть корректным, в то время как загрузчик не выдаст никакой ошибки.

Но взгляните на код загрузчика. Вы увидите, что всех описанных выше проблем в нем попросту не возникает! (Этот же PHP-скрипт.)

Листинг 9 скопировать код в буфер обмена


<?php
//

// ВНИМАНИЕ! До подключения библиотеки в браузер не должно быть выведено

// ни одного символа. В противном случае функция header(), используемая

// библиотекой, не сработает (см. документацию), и возникнет ошибка.

//


// Стартуем сессию.

session_start();
// Подключаем библиотеку поддержки.

require_once "../../../lib/config.php";
require_once "Subsys/JsHttpRequest/Php.php";
// Создаем главный объект библиотеки.

// Указываем кодировку страницы (обязательно!).

$JsHttpRequest =& new Subsys_JsHttpRequest_Php("windows-1251");
// Получаем запрос.

$q = $_REQUEST['q'];
// Формируем результат прямо в виде PHP-массива!

$_RESULT = array(
"q" => $q,
"md5" => md5($q),
'hello' => isset($_SESSION['hello'])? $_SESSION['hello'] : null
);
// Демонстрация отладочных сообщений.

if (strpos($q, 'error') !== false) {
callUndefinedFunction();
}

echo "<b>REQUEST_URI:</b> ".$_SERVER['REQUEST_URI']."<br>";
echo "<b>Loader used:</b> ".$JsHttpRequest->LOADER;
?>



Итак, библиотека Subsys_JsHttpRequest_Php берет на себя всю «грязную работу» по «обертыванию» результата работы загрузчика в JavaScript-код. В программе достаточно лишь присвоить значение специальному массиву $_RESULT, и данные, благодаря слаженной работе frontend- и backend-частей библиотеки, благополучно поступят в браузер, сохранив свою структуру.

К счастью, массивы и хэши JavaScript и PHP устроены практически одинаково, поэтому можно безболезненно производить перевод PHP-массива...

Листинг 10 скопировать код в буфер обмена


$_RESULT === array(
"q" => 'запрос',
"md5" => 'MD5-код введенной строки'
)

...в JavaScript-хэш:

Листинг 11 скопировать код в буфер обмена


req.responseJS === {
q: 'запрос',
md5: 'MD5-код введенной строки'
}



Ну а чтобы все окончательно прояснилось, приведу примерный результат работы скрипта load.php, как его видит браузер (уже после «обертывания» библиотекой Subsys_JsHttpRequest_Php):

Листинг 12 скопировать код в буфер обмена


Subsys_JsHttpRequest_Js.dataReady(
123,
'Отладочные сообщения.',
{
q: 'запрос',
md5: 'MD5-код введенной строки'
}
)



Итак, простое присваивание значения массиву $_RESULT приводит к генерации вот такого вот JavaScript-кода. Он просто физически не может оказаться некорректным (ибо всегда создается по одинаковому шаблону, и кавычки с апострофами в нем экранируются backend-частью Subsys_JsHttpRequest_Php).
Перехват ошибок в PHP-загрузчике

«Обертывание» работает с использованием функции PHP ob_start(), которая позволяет перехватывать данные, поступающие в выходной поток скрипта, и производить с ними любые преобразования. По счастливой случайности, ob_start() позволяет также перехватывать ошибки, произошедшие в скрипте, в том числе фатальные, не поддающиеся перехвату никакими другими способами!

Кстати, эта полезная особенность функции ob_start() довольно малоизвестна. Вы можете использовать ее и для других целей — например, чтобы гарантировано вывести нижнюю часть страницы даже в случае серьезного краха скрипта.

Т.к. все сообщения об ошибках (например, вызов несуществующей функции) PHP печатает прямо в выходной поток (как будто бы через echo), логично воспринимать все содержимое выходного потока скрипта в качестве отладочного текста. Если вы помните, этот текст доступен в свойстве req.responseText, пустом при корректном завершении загрузчика. Благодаря механизму «обертывания» ни одна, даже самая серьезная, ошибка в PHP-программе не сгенерирует некорректного JavaScript-кода. Вместо этого текст ошибки попадет в третий параметр функции Subsys_JsHttpRequest_Js.dataReady(), и в итоге окажется в req.responseText.

Вы можете убедиться, что перехват ошибок работает, введя на тестовой странице (см. выше) строчку, содержащую слово "error". Вы получите в нижнем динамическом поле сообщение:

Листинг 13 скопировать код в буфер обмена


Fatal error: Call to undefined function: callundefinedfunction()
in load.php on line 15


Решение проблемы с кодировками


При формировании запроса к загрузчику может потребоваться передать ему строки, содержащие русские буквы. Естественно, их нельзя напрямую передавать в URL, а вначале нужно URL-кодировать — преобразовать каждый символ русского алфавита к виду %XX, где XX — код символа.

В JavaScript имеется функция escape(), которая URL-кодирует строку данных. К сожалению, она возвращает результат только в виде Unicode. Например, строка "проба" представляется ей так "проба". В PHP нет функций, умеющих раскодировать такое представление данных (urldecode() тут плохой помощник, ибо она не поддерживает формат %uXXXX). Функция escape() позволяет закодировать совершенно любой символ, будь то русская буква, литера греческого алфавита или даже китайский иероглиф.

Лирическое отступление

Вообще говоря, в последний версиях JavaScript имеется функция encodeURIComponent(), умеющая кодировать данные в обход Unicode. Однако она не поддерживается, например, в Internet Explorer 5.0, так что из соображения кроссбраузерности нам не подходит.

К счастью, популярное расширение iconv для PHP поддерживает функцию для преобразования данных во всевозможных кодировках, так что перекодировать из Unicode в Windows-1251 не составляет для backend-библиотеки Subsys_JsHttpRequest_Php особых сложностей.

Лирическое отступление

По многочисленным просьбам, начиная с версии 3.0 библиотека Subsys_JsHttpRequest_Php может работать и без iconv, если основной кодировкой сайта является windows-1251 или koi8-r. Функции и таблицы перевода из Unicode в одну из этих однобайтовых кодировок встроены в сам модуль.

Итак, вы можете вызывать метод send() объекта Subsys_JsHttpRequest_Js, не задумываясь о кодировках данных. Вам не нужно ничего перекодировать вручную ни в серверном, ни в клиентском коде: библиотека берет всю эту работу на себя.

Еще раз: если вы хотите использовать библиотеку Subsys_JsHttpRequest с кодировками, отличными от windows-1251 и koi8-r (например, с UTF-8), на сервере должно быть установлено расширение PHP iconv. У большинства хостеров оно стоит, но, если вдруг окажется, что его нет (к позору провайдера), хостеру не составит труда установить модуль.
Что нового в версии 3.0

Ранние версии библиотеки (1.x и 2.x) назывались JSHttpRequest. Они обладали несколько меньшей функциональностью, чем Subsys_JsHttpRequest. Новая версия — 3.x — поддерживает следующие возможности, недоступные ее предшественнице:

* Если браузер имеет встроенный объект XMLHttpRequest или же ActiveX-элемент Msxml2.XMLHTTP/Microsoft.XMLHTTP, и при этом не возникает проблем с безопасностью (обращение к загрузчику на том же самом домене), они автоматически задействуются. В противном случае — используется метод динамического создания элемента <script>.
* В режиме использования XMLHttpRequest (или соответствующего ActiveX) поддерживается метод отправки запроса POST (указывается при вызове open()). Однако, если с POST возникают проблемы (например, в Opera 8.01), библиотека автоматически переключается на стандартный метод GET. Это же происходит, когда XMLHttpRequest не поддерживается браузером — ошибок в любом случае не выдается.
* URL загрузчика, передаваемый методу open(), теперь может содержать параметры (например, load.php?test=abc).
* Параметры теперь передаются в QUERY_STRING совершенно обычным способом, традиционным для PHP- и CGI-скриптов. Типичный URL запроса к загрузчику: load.php?test=abc&q=проба&4. Здесь:
1. load.php?test=abc — адрес загрузчика, указанный в методе open().
2. &4 — «безвредная» добавка, содежащая ID сессии загрузки (уникальна для каждого запроса).
3. q=проба — параметры, переданные в методе send().
* Полностью поддерживаются стандартные сессии PHP, причем backend библиотеки Subsys_JsHttpRequest_Php о них «ничего не знает», передавая полномочия управления сессиями интерпретатору PHP. Параметр PHPSESSID, указанный либо в куках, либо в GET, передается frontend-ом Subsys_JsHttpRequest_Js PHP-загрузчику (в дополнение к обычным GET-параметрам). Соответственно, если скрипт-frontend установил какие-то переменные в сессию, загрузчик-backend может их прочитать стандартными средствами.
* Параметры загрузчику (в методе send()) можно передавать в стандартной PHP-форме (например, с именами a[abc], b[xyz] и т. д.). Т.е. разбор QUERY_STRING — на плечах стандартных функций PHP, а не идет вручную, как в предыдущей версии.
* В случае, если базовая кодировка сайта — windows-1251 или koi8-r, а расширение iconv в PHP не подключено, используется своя собственная функция перекодирования. Так что в подавляющем большинстве случаев библиотека будет корректно работать с русскими буквами даже при отсутствии iconv!

Еще большая кроссбраузерность?

У меня есть большое подозрение, что трюк с динамической генерацией тэга <script> сработал бы и в других, более старых версиях браузеров (по крайней мере, в IE4). Однако в этом случае код библиотеки уже не был бы таким универсальным — пришлось бы «привязываться» к особенностям браузеров. Кроме того, старые версии не поддерживают DOM на должном уровне (имеются в виду функции getElementById(), createElement(), appendChild() и т. д.), в то время как класс Subsys_JsHttpRequest_Js использует исключительно DOM. В IE, например, можно было бы использовать document.all и присавивания значений свойству innerHTML некоторого элемента.

Если вы решите модифицировать библиотеку так, чтобы она работала и в старых браузерах, я буду рад получить от вас измененный код, а также обсудить его нюансы в форуме.
Резюме

Подведем итоги этой большой наблы. Вначале я перечислю ссылки на программные модули, упоминаемые выше.

* JavaScript-frontend библиотеки Subsys_JsHttpRequest_Js: Subsys/JsHttpRequest/Js.js.
* PHP-backend библиотеки Subsys_JsHttpRequest_Php: Subsys/JsHttpRequest/Php.php.

Пример использования библиотеки:

* Все примеры из статьи (zip).
* Клиентский код (JavaScript): test.htm.
* Серверный код загрузчика (PHP): load.php.

Библиотека Subsys_JsHttpRequest активно используется на форуме forum.dklab.ru. А именно, через нее реализованы следующие функции:

* "Живой поиск": на каждой странице форума имеется поле, в которое можно ввести поисковый запрос и сразу же получить результат, минуя перезагрузку страницы.
* "Живой поиск" в форме добавления нового топика: то же самое, но срабатывает при вводе темы нового сообщения.
* "Живой предпросмотр": рядом со ссылками на топики приведена специальная пиктограмма, наведя мышь на которую, можно просмотреть первое сообщение топика.
* "Живая карма": пользователи могут изменять карму (рейтинг) друг другу, не перезагружая страницу.

Автор: нет данных
Вернуться в начало страницы
 
Ответить с цитированием данного сообщения
Mutant
Mutant
Topic Starter сообщение 4.8.2008, 18:53; Ответить: Mutant
Сообщение #5


оО Diablo, спасибо огромное. Я еще не читал, но, полагаю, это то,что мне нужно.

Замечание модератора:
Эта тема была закрыта автоматически ввиду отсутствия активности в ней на протяжении 100+ дней.
Если Вы считаете ее актуальной и хотите оставить сообщение, то воспользуйтесь кнопкой
или обратитесь к любому из модераторов.
Вернуться в начало страницы
 
Ответить с цитированием данного сообщения
Открыть тему
Тема закрыта
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0


Свернуть

> Похожие темы

  Тема Ответов Автор Просмотров Последний ответ
Открытая тема (нет новых ответов) Интеграция спортивных данных API. Коэффициенты БК, Live результаты
15 yaroslav89 6846 8.4.2024, 17:17
автор: spoyer_ru
Открытая тема (нет новых ответов) Получение гос. гранта для IT сферы
4 metvekot 1541 22.3.2024, 21:06
автор: Rebex
Открытая тема (нет новых ответов) ГОРЯЧИЕ FOREX|CRYPTO ЛИДЫ. БАЗЫ ДАННЫХ
[Чарджбек|Рекавери|Возврат]
9 Leado 3893 9.2.2024, 16:04
автор: baza0013
Открытая тема (нет новых ответов) Базы данных, различной тематики, выкладываем тут.
Делимся, обмениваемся, заказываем, парсим.
12 InfoObmen 15252 4.9.2022, 2:32
автор: Галецкая
Открытая тема (нет новых ответов) Сбор Данных Услуг И Специалистов Яндекс.Услуги
2 zkalinin 2377 26.5.2022, 17:10
автор: zkalinin


 



RSS Текстовая версия Сейчас: 20.4.2024, 11:44
Дизайн