:: Обсуждаем проекты
Web-интерфейс для FoxPro-программы
maple4
Автор

Сообщений: 210
Откуда: Москва
Дата регистрации: 26.10.2007
Генератор Интерфейса 1.0.0

Прямая ссылка на архив (450 кб, исходники + 7za.exe):
www.maple4.ru

Для чего?
Формирование HTML-Интерфейса для программы, написанной на FoxPro.
Зачем?
Без сложных ухищрений FoxPro создает только "стандартный" интерфейс, который в настоящий момент выглядит несколько аскетично.
Программа может предоставить интерфейс "как-бы" в стиле web - без установки каких-либо дополнительных сервисов (например IIS или сервера Апач ).

Что дает web-интерфейс?
Естественно (ради этого все и задумывалось), гораздо большие возможности по графическому оформлению.
Используя Javascript, можно добиться таких спецэффектов, что дух захватывает - плавающие разделы, напоминания, часы, календари и т.д. - все зависит от знания этого самого javascript.
Простое размещение видео (+ роликов с youtube), флеш-анимации, файлов мультимедиа непосредственно на форме - еще один плюс web-интерфейса.

Где еще можно применить данную разработку?
Программа ИДЕАЛЬНО подходит для создания пошаговых игр (и все это - средствами FoxPro).
Или, например, "электронных" книг с реакцией на действия пользователя (по сути - получается все та же пошаговая игра Помню время, когда компьютеров не было, были очень распространены книги с сюжетом, зависящим от случайности и личным предпочтением читателя. Это лирическое отступление , но вторым приложением в примере будет "Стань Стальной Крысой!" Г. Гаррисона. Как бы авторские права не нарушить...).
Или, еще вариант, загрузчиков/инсталляторов с CD/DVD-ROM.
И вот еще...
Применяя javascript ВМЕСТЕ с FoxPro (при обработке может использоваться код FoxPro, а не только javascript - представьте, какие открываются горизонты), можно создавать код, срабатывающий по какому-либо событию (например, после нажатия кнопки или в момент времени) и не требующий переформирования всей страницы.
Данная возможность "выросла" из примера на странице forum.foxclub.ru (Роме - спасибо).
При том, что данная возможность - самая интересная, используется она до обидного меньше всего. Одна из причин - я не совсем разобрался с JavaScript.
Подробнее - чуть ниже ("Альтернатива <action>").
Ну и отдельное спасибо piva - за "запрет" возможности перехода на предыдущую страницу по Backspace или Alt-X, причем БЕЗ применения JavaScript.

Приложенный пример (проект 2x2) всего лишь демонстрирует возможности проекта, вот только выглядит не очень (нет оформления, файлов графики и т.д.). Причина банальна - не хватает времени. Даже не нарисовать, не хватает времени просто найти рисунки в Интернет-е
ОЧЕНЬ нужны анимированные gif-картинки золотых, серебряных и медных монет с прозрачным слоем для использования в любом фоне. Заранее спасибо!

"Защита" с логином/паролем не претендует на что-либо, кроме демонстрации.

Согласен, в дальнейшем по тексту "многа букафф"... что делать. Во первых, хотелось сразу ответить на предполагаемые вопросы, а во вторых - по возможности дать некий пример использования.
Символами --------------------------------------------- отмечены логические разделы, по первой строке которого можно понять, читать дальше или нет.

Особенности программы - весь код в основном размещается в HTML-странице (т.е. в бланке).
Но программный код, при необходимости (например, для исключения дублирования), можно вывести в отдельный prg-файл (будет находиться в рабочем каталоге/подкаталоге).
Конечно же, самый "удобный" вариант - включить foxpro-код непосредственно в исполняемый exe-модуль, но предлагаю делать это в самых крайних случаях - для обеспечении безопасности или повышения скорости обработки.
На разработку проекта повлияли некоторые знания в ASP.NET и PHP (будь оно все неладно...)


---------------------------------------------
Создание интерфейса.
Процесс создания разбивается на несколько шагов.
I. Каркас.
Необходимо создать набор html-страниц (далее -> gi-форм), оформленных в виде небольшого сайта:
меню, кнопки, картинки, ссылки на ресуры в Интернет (например - на видео Ютуба) и т.д.
Названия gi-форм ДОЛЖНЫ начинаться с m4gi_ (признак gi-формы, без этого gi-форма обрабатываться НЕ БУДЕТ), а в содержимом - должен быть код <!--m4gi (признак правильной gi-формы, начало комментария html, и одновременно - процедуры инициализации и настройки) !

II. Наполнение.
1. В теле каждой gi-формы нужно разместить код загрузки и обновления load (аналог FoxPro : Load ) и init ("аналог" Init или Refresh FoxPro).
2. Нужно создать ссылки (или изменить существующие) для запуска gi-форм с параметрами (аналог FoxPro - передача параметров в форму) - добавление реакции.
Переданные параметры gi-форма будет обрабатывать в load перед формированием страницы и в init после формирования.
3. Нужно разместить в gi-формах специальные теги - управляемые элементы.
Их назначение - в нужных местах gi-формы вставить текст: - значения полей, функций или переменных или код FoxPro, который сформирует нужный текст.
---------------------------------------------
Настройки формы.
ВАЖНО! Даже если в настройках ничего не будет размещаться - ОБЯЗАТЕЛЬНО добавьте в страницу хотя бы текст <!--m4giproperties -->
Настройки задаются в секции <!--m4giproperties -->, пока в этой конструкции можно разместить только процедуру load

Подпрограммы gi-формы:

<load>
Текст программы foxpro</load>
ПодПрограмма инициализации gi-формы
Срабатывает ПЕРЕД формированием страницы.
Здесь размещается код, необходимый для генерации HTML-страницы - открытие таблиц, создание глобальных переменных.

<init>
Текст программы foxpro</init>
ПодПрограмма обновления gi-формы
Срабатывает ПОСЛЕ формирования страницы.
В init можно разместить запуск таймеров и кода обновления HTML-элементов уже сформированной страницы.

load и init срабатывают и при загрузке новой страницы, и при обновлении текущей.
Просто одна - до формирования, а другая - после формирования.

Пример размещения можно посмотреть в разделе "Пример типичной gi-формы".

---------------------------------------------
1. Каталог запуска программы - это каталог, где запускается exe-модуль.
Обычно в этом каталоге размещаются общие таблицы/файлы для всех приложений или общие таблицы/файлы для всех пользователей, архивы, распаковываемые в дальнейшем в рабочий каталог.

2. Каталог интерфейса - место, где находятся html-формы и процедуры интерфейса.
Каталог интерфейса может быть одновременно рабочим каталогом (что такое рабочий каталог - чуть ниже).

3. Рабочий каталог - это место, где будут формироваться готовые страницы.
Зачем?
Представьте, что Вы создаете интерфейс для программы, которая будет находиться на CD-ROM.
Куда будут формироваться готовые страницы, если CD-ROM НЕ ПОЗВОЛЯЕТ записывать на него информацию (только считывание)?
Рабочий каталог эту проблему снимает:
Во время ПЕРВОГО запуска все файлы из каталога интерфейса переносятся в рабочий каталог, а уже в рабочем каталоге формируется то, что надо!

В случае, если программа НЕ находится на CD/DVD-ROM (т.е. запускается прямо с жесткого диска) рабочим каталогом является каталог интерфейса.
В этом случае НЕТ затрат времени на копирование и html-страницы формируются там же, где находятся gi-формы и процедуры интерфейса.

---------------------------------------------
Объекты post и this_form
В процедурах load и init (а так же во всех <fp></fp>-тегах) при обработке доступны специальные объекты post и this_form.
1. post
Если gi-форме были переданы параметры - ВСЕ свойства этих параметров доступны через работу с этим объектом (нужно учитывать, что все параметры - текстового вида).
2. this_form
с помощью этого объекта Вы можете напрямую обращаться к свойствам и методам foxpro-формы.
*Например, можно задать заголовок окна:
this_form.caption="Новый заголовок"
*или изменить ширину и высоту окна:
this_form.width=800
this_form.height=600

---------------------------------------------
Управляемые элементы вставляются специальными тегами - <fp></fp>
Два способа вставки:
1. Внутри может размещаться функция, переменная или поле - ОБЯЗАТЕЛЬНО первым символом должне быть амперсанд - &
Пример:
Текущие дата/время: <fp>&datetime()</fp>

2. Внутри может размещаться код FoxPro. Если результат будет возвращен оператором Return - текстовое значение результата будет вставлено при формировании страницы.
Пример:
Текущие дата/время: <fp>local ii
ii=datetime()
return ii</fp>

---------------------------------------------
Нужно "научить" формы реагировать на клики (по кнопкам, ссылкам, рисункам и т.д.).
Реакция задается специальными тегами <action> </action>, внутри которых размещается код обработки:
1. внутри ссылки <a href="<action>ЗДЕСЬ"</action>></a>
2. внутри формы <form action="<action>ЗДЕСЬ"</action>></form>

Есть еще один вариант с javascript - но об этом снова чуть ниже.

Типичные примеры использования:

Переход к странице, находящейся в рабочем каталоге
<a href="<action>return '/m4gi_gl_form.html?session=_vbrtdgd&uroven=1'</action>">I уровень</a>
или
<a href="<action>&'/m4gi_gl_form.html?session=_vbrtdgd&uroven=1'</action>">I уровень</a>

Переход к странице, которая находится в подкаталоге 2x2 рабочего каталога программы
<a href="<action>return '/2x2/m4gi_2x2_form.html?session=_vbrtdgd&uroven=1'</action>">I уровень</a>
или
<a href="<action>&'/2x2/m4gi_2x2_form.html?session=_vbrtdgd&uroven=1'</action>">I уровень</a>

Закрытие формы ссылкой
<a href="<action>this_form.close</action>">Закрыть окно</a>

Закрытие формы кнопкой
<form name="main_form" id="main_form_id" method="post" action="<action>this_form.close</action>" onsubmit="return false;">
<input type="button" value="Закрыть" onclick="submit();">

Открытие gi-формы в новом окне:
<a href="<action>m4gi_newform('\m4gi_magazin.html')</action>">К магазину!</a>

Присвоение глобальным переменным значений
<a href="<action>
_m4gi_type_app='2x2'
_m4gi_type_lg=1
</action>">Присвоить</a>

Задание реакции в форме - с передачей процедуре (которая находится в корне рабочего каталога программы) текущих параметров
<form name="main_form" id="main_form_id1" method="post" action="<action>return m4gi_exec('/prg_m4gi_avtoriz.prg',post,this_form)</action>" onsubmit="return false;">
Необходимо авторизоваться (что-бы понять, а кто, собственно, здесь).<br><br>
<br>Введите имя: <input type="text" name="text_name" id="text_id1" value="" />
<br>Введите пароль: <input type="password" name="text_pwd" id="text_id2" value="" />
</form>
<input type="button" value="Войти" onclick="document.forms['main_form'].submit();">

Задание реакции в форме - с передачей процедуре (которая находится в подкаталоге 2x2 рабочего каталога программы) текущих и дополнительных параметров
<form name="main_form" id="main_form_id1" method="post" action="<action>return m4gi_exec('/2x2/prg_m4gi_2x2_prover.prg',post,this_form,1,2)</action>" onsubmit="return false;">
<INPUT TYPE="text" NAME="proiz" ID="text_proiz" VALUE="" />
<input type="button" value="Ответить" onclick="submit();"></form>

Задание реакции в форме - с открытием другой gi-формы:
<form name="test_form_name" id="test_form_id" method="post" action="<action>return '/2x2/m4gi_2x2_start.html'</action>" onsubmit="return false;">
<br><b>Name?</b>
<br><input type="text" name="fio" id="text_id" value="" /><br/>
<br><textarea name="textarea_notes" id="textarea_id" cols="20" rows="5" ></textarea><br/>
</form>
<br>
<input type="button" value="Отправить" onclick="document.forms['test_form_name'].submit();">
Далее, при нажатии кнопки Отправить, данные со страницы (значение полей в fio и textarea_notes) будут переданы заданной в <form action... gi-форме.
Обратите внимание, method="post" !
Далее, в gi-форме /2x2/m4gi_2x2_start.html будут доступны параметры post.fio и post.textarea_notes, которые можно будет использовать при формировании страницы.



Внутри тегов <action></action> могут размещаться теги <fp></fp> для формирования нужного кода реакции.
Например, открытие страницы в подкаталоге 2x2 с передачей ей параметров
<a href="<action>return '/2x2/m4gi_2x2_form.html?session=<fp>&LOWER(SYS(2015))</fp>&uroven=3'</action>">Третий уровень</a>


Тег <action></action> может размещаться в теге <fp></fp>
<fp>
return _m4gi_name+":&nbsp;&nbsp;&nbsp;"+m4gi_money(_m4gi_names.money_)+" "+iif(vartype(post.pwd)="C",[(запомните Ваш пароль! <font color=red size=+1><b>]+post.pwd+[</font>)],"")+;
iif(_m4gi_names.money_>0,[<br>&nbsp;<a href="<action>m4gi_newform('\m4gi_magazin.html')</action>">К магазину!</a>],"")
</fp>

Если реакция размещается внутри тега <fp></fp> и текст реакции состоит из многострочного кода, может понадобиться специальная комбинация {{ для вставки "перехода строки" в коде текста реакции
<fp>return iif(_m4gi_type=0,[<p align=right><i><font size=-1><a href="<action>_m4gi_type=1{{_m4gi_name=''</action>">Перейти к добавлению нового пользователя</a></font></i></p>],;
[<p align=right><i><font size=-2><br><br><a href="<action>_m4gi_type=0{{_m4gi_name=''</action>">Отказаться от добавления нового пользователя</a></font></i></p>])</fp>

А вот если бы реакция НЕ РАЗМЕЩАЛАСЬ в теге <fp></fp>, текст
<a href="<action>{{_m4gi_type=0{{_m4gi_name=''</action>">Отказаться от добавления нового пользователя</a>
мог БЫ быть несколько другим:
<a href="<action>
_m4gi_type=0
_m4gi_name=''
</action>">Отказаться от добавления нового пользователя</a>

Комбинация {{ избавляет от ошибки при выполнении кода в теге <fp></fp> при МНОГОСТРОЧНОМ коде <action></action>

Дело в том, что при формировании страницы из gi-формы СНАЧАЛА обрабатываются теги <fp></fp> - т.е. попросту выполняется некий одно- или многострочный FoxPro-код в этих тегах.
А так как код реакции <ation></action> ТАК ЖЕ может содержать многострочный код, то БЕЗ {{ возникает "конфликт интересов"
ПРИМЕР НЕПРАВИЛЬНОГО написания реакции (в теге <fp></fp>):
<fp>return iif(_m4gi_type=0,[<p align=right><i><font size=-1><a href="<action>_m4gi_type=1{{_m4gi_name=''</action>">Перейти к добавлению нового пользователя</a></font></i></p>],;
[<p align=right><i><font size=-2><br><br><a href="<action>
_m4gi_type=0
_m4gi_name=''
</action>">Отказаться от добавления нового пользователя</a></font></i></p>])</fp>

...который, при попытке выполнить его вернет ошибку (можете проверить у себя в окне FoxPro )


---------------------------------------------
Альтернатива <action></action>.
Код <action></action> срабатывает при кликах на ссылках или отправкой данных html-формы. После чего идет переформирование текущей страницы или открытие новой.

Есть АЛЬТЕРНАТИВНЫЙ способ, который позволит сделать многое прямо с текущей страницей.
Для этого используется специальная конструкция <scriptaction></scriptaction> внутри которого размещается код обработки.

<a onclick="<scriptaction>
wait window "Добрый день! Сегодня "+dtoc(datetime()) nowait
</scriptaction>" href="javascript:void(0)">Пример вывода в окне Wait приветствия с текущей датой</a>


Еще вариант использования <scriptaction></scriptaction> - в обработке события таймера:
(правда В РАБОЧЕЙ СТРАНИЦЕ Я ЭТОТ КОД ЗАКОММЕНТИРОВАЛ!Объяснение почему - чуть ниже)
<p align=right>Текущие Дата/время: <b id="timestr"><fp>&datetime()</fp></b></p>
<script type="text/javascript">
setInterval(function(){
<scriptaction>
local jj
jj=ttoc(datetime())
this_form.caption="Кто тут? ("+jj+")"
this_form.wb.Document.getElementByID("timestr").innerHtml=jj
</scriptaction>;
}, 755);
</script>
Данный пример срабатывает через каждые 755 мс и одновременно меняет заголовок окна и значение элемента с id равном timestr на текущей странице.
Обратите внимание - для доступа к caption формы и web-контролу (wb) на ней используется специальный временный объект this_form (а не Thisform)
Обратите внимание - в html-странице обязательно должен быть объявлен элемент timestr (это идентификатор позиции, куда будет вставлен результат работы fox-процедуры)
Тоже самое, наверное, можно сделать и на чистом Javascript, но тут стоит простейшая задача - показать, что FoxPro-код выполняется

ВСЕ ОТЛИЧНО в работе данного кода НО... ПРИ БОЛЬШОЙ НАГРУЗКЕ на процессор setInterval срабатывает ДАЖЕ после перехода на другую страницу. Мало того, это вообще иногда ведет к ошибке javascript.
К сожалению, пока победить setInterval мне не удалось. А так как конструкция <scriptaction></scriptaction> применяется при использовании в javascript, необходимо хотя-бы начальное знание об этом javascript.
К сожалению, это как раз мой случай - ну не работал я с javascript вплотную.
Нужен, видимо, совет эксперта по JavaScript и IE, чтобы НАКОНЕЦ-ТАКИ разобраться, как IE работает с таймером setInterval (если честно, думал, что при открытии новой страницы предудущие события таймера отменяются. Наверное, это все таки не так...)
Обобщаю: НЕ РЕКОМЕНДУЕТСЯ использовать <scriptaction></scriptaction> с таймерами. Зато в остальных случаях - пожалуйста.

ОЧЕНЬ бы хотелось бы получить примеры работы с javascript от его знатоков.
Кстати, на странице forum.foxclub.ru есть пример запуска javascript-функции из FoxPro и обработка результатов этой функции.

---------------------------------------------
Размещение <scriptaction></scriptaction> в gi-форме.
Нужно понимать, что код <scriptaction></scriptaction> может сработать ТОЛЬКО ПОСЛЕ ЗАГРУЗКИ ВСЕЙ СТРАНИЦЫ.
Код javascript, приведенный ниже, создает объект htmlapp (), посредством которого в дальнейшем (после совершения какого-либо события) обрабатывается код FoxPro :
<script type="text/javascript">
var htmlapp = null;
</script>

А вот нижеследующий код вызовет ОШИБКУ:
<script language="JavaScript">
document.write(<scriptaction>dtoc(date())</scriptaction>)
</script>
Ведь здесь на момент выполнения document.write html-страница ЕЩЕ НЕ ЗАГРУЖЕНА!

---------------------------------------------
Таймер на FoxPro - гораздо более стабильный таймер.
Обычно создается в init gi-формы.
Например, следующий код делает то-же самое, что и процедура с setInterval (выше), а именно - через определенный интервал меняет заголовок формы и время на странице:

THIS_FORM.AddObject("_timer1","cmoitimer")
local stro
TEXT TO stro TEXTMERGE NOSHOW
if left(this_form.caption,3)=="Кто" && исключение срабатывания на другой форме
local jj,zz
jj=ttoc(datetime())
this_form.caption="Кто тут? ("+jj+")"
zz=This_form.wb.Document.getElementByID("timestr")
if not isnull(zz)
zz.innerHtml=jj
endif
endif
ENDTEXT
this_form._timer1.post=post
this_form._timer1.this_form=this_form
this_form._timer1.textproc=stro
this_form._timer1.interval=100

Данный код - ГОРАЗДО более стабилен в работе.
Единственный минус - в следующей открываемой форме необходимо в load (именно в load, а не init) работающий таймер надо удалить (c другой стороны - раз созданный таймер будет работать все время, что иногда может быть и плюсом ).
Например, уже после авторизации, в load gi-форме /m4gi_list.html есть код :
this_form.RemoveObject("_timer1")
который удаляет добавленный ранее таймер.

---------------------------------------------
Во внешних процедурах (prg-файлах) ОБЯЗАТЕЛЬНО наличие строки с МИНИМУМОМ двумя параметрами:
LPARAMETERS post,this_form

Например, в /2x2/prg_m4gi_2x2_prover.prg, кроме основных параметров, есть так же параметры ur и shag, требующиеся для работы именно этой процедуры:

LPARAMETERS post,this_form,ur,shag
IF VAL(post.proiz)=0
RETURN .T.
ENDIF
SELECT _m4gi_table_2x2
GO shag
REPLACE otvet WITH .T.,proizv WITH VAL(post.proiz),pravilno WITH IIF(mnoj1*mnoj2=VAL(post.proiz),.T.,.F.),time_ with DATETIME()
IF RECCOUNT()=shag && пройден
m4gi_newform("2x2\m4gi_2x2_rezultat.html?urrez="+ALLTRIM(STR(ur))+"&shagrez="+ALLTRIM(STR(shag)))
ENDIF
RETURN .T.

---------------------------------------------
Страница HTML НЕ ДОЛЖНА реагировать на вторую (неосновную) кнопку мыши, так как все действия с формой ведутся ТОЛЬКО с помощью основной кнопки.
В BODY html-страницы необходимо внести код oncontextmenu="return false", например:
<BODY oncontextmenu="return false">
Если этого не сделать - у пользователя "возникнут" вопросы (контекстное меню не контролируется)

---------------------------------------------
Результат реакции <action></action> может быть различных типов:
1. При C(символьное) - подразумевается, что это адрес gi-формы или html-страницы - открытие "результата", т.е. gi-формы или страницы html в том же окне.
По сути - переход к нужной форме.
2. При L(логическое) - .T. - переформирование и обновление текущей страницы (по умолчанию, .t. возвращается, если нет в коде нет оператора Return со значением)
Обновление gi-формы требуется после выполнения каких-либо действий, например, после окончания редактирования элемента.
3. При L(логическое) - .F. - ничего не переформировывается.

---------------------------------------------
Встроенные функции для работы каталогами и файлами в рабочем каталоге

m4gi_file(file) - возвращает .t. при существовании файла.

m4gi_file_path(file) - возвращает полный путь файла из краткого (это требуется, например, для создания временных таблиц).
m4gi_file_path("\2x2\metka.gif")


m4gi_folder(folder) - возвращает .t. при существовании каталога.
m4gi_folder(folder,type)- при type=.t. - возвращает .t., если каталог существует или каталог был успешно создан и существует.
функция создает ТОЛЬКО каталоги, относительные к рабочему

m4gi_folder("\3x3",.t.)
будет .t. и будет создан подкаталог 3x3 в рабочем каталоге

m4gi_folder("\pr\tk\sd",.t.) -> создается (если его нет) каталог в рабочем каталоге
Функция m4gi_folder - удобный способ для быстрого создания дерева каталогов со всеми ветками.

---------------------------------------------
Встроенные функции для работы c каталогами и файлами в каталоге запуска.
Данные функции работают ИМЕННО с каталогом ЗАПУСКА.
Сделано это для того, что-бы, например, разные пользователи с расшаренного ресурса обращались к одной и той же таблице.
Или, например, различные приложения работали с теми же таблицами или файлами.

m4gi_file_default(file) - возвращает .t. при существовании файла.

m4gi_file_path_default(file) - возвращает полный путь файла из краткого. Например, при каталоге запуска c:\m4gi результатом работы функции
m4gi_file_path_default("\2x2\metka.gif")
будет
c:\m4gi\2x2\metka.gif


m4gi_folder_default(folder) - возвращает .t. при существовании каталога.
m4gi_folder_default(folder,type)- при type=.t. - возвращает .t., если каталог существует или каталог был успешно создан и существует.
функция создает ТОЛЬКО каталоги, относительные к главному
например, при каталоге запуска c:\m4gi результатом работы функции
m4gi_folder_default("\3x3",.t.)
будет .t. и будет создан каталог
c:\m4gi\3x3

m4gi_folder_default("\pr\tk\sd",.t.) -> создается (если его нет) каталог c:\m4gi\pr\tk\sd
Функция m4gi_folder - удобный способ для быстрого создания дерева каталогов, вне зависимости, существуют или нет ветки каталогов.

---------------------------------------------
Прочие функции

m4gi_newform(m4gi_form) - открытие модальной gi-формы в НОВОМ окне (т.е. старое не закрывается).
например, открытие формы в новом окне с передачей ей параметра:
m4gi_newform("\m4gi_error.html?texterror=Неправильно введен пароль, либо пользователь <font color%3Dred>"+ALLTRIM(post.text_name)+"</font> не найден!")


m4gi_money(money) - возращает результат в виде html-кода в стиле WarCraft-е, т.е. в виде количества золотых, серебрянных и медных монет.
m4gi_money(money,img) - задается другой рисунок для отображения результата.
если img - пустой, по умолчанию будет 3 рисунка
1_money.gif
2_money.gif
3_money.gif
с путями к рабочему каталогу

При задании, например, m4gi_money(10395,"graf.jpg") функция вернет html-код с рисунками:
1_graf.jpg
2_graf.jpg
3_graf.jpg
с путями к рабочему каталогу

m4gi_money(money,img,gr1,gr2,gr3)
gr1,gr2,gr3 - задает название для рисунков (этот текст будет появляться при наведении мышкой).
Если gr1,gr2,gr3 пустые - по умолчанию gr1="золото",gr2="серебро",gr3="медь"

m4gi_exec(path_file,post,this_form,par1,par2,par3,par4,par5) - запуск процедуры с параметрами.
Применяется, для запуска процедур, находящихся в рабочем каталоге, например,
m4gi_exec('/2x2/prg_m4gi_2x2_prover.prg',post,this_form,1,2)
запустит процедуру prg_m4gi_2x2_prover.prg в подкаталоге 2x2 рабочего каталога и передаст ее параметры post, this_form, 1, 2

m4gi_exec('/prg_m4gi_avtoriz.prg',post,this_form)
запуск процедуры, находящейся в корне рабочего каталога

m4gi_iconnected() - при .t. - доступ к Интернет существует.
Функцию удобно использовать при формировании gi-формы в зависимости от наличия Интернет - формируется тот или иной код страницы.

m4gi_unzip("file.zip") - распаковать file.zip (БЕЗ УКАЗАНИЯ ПОЛНОГО ПУТИ!) в папку Рабочего каталога

m4gi_unzip_file_to_folder("file.zip","c:\m4gi\") - распаковать file.zip (С ПОЛНЫМ УКАЗАНИЕМ ПУТИ!) в папку c:\m4gi\

m4gi_set_inner(this_form,element,value) - функция замены значения элемента (element) HTML новым значением (value)
В случае невозможности замены (нет такого элемента) возвращает Null
m4gi_set_inner(this_form,"timestr",ttoc(datetime()))

m4gi_get_inner(this_form,element) - получение значения элемента (element) HTML
В случае невозможности получения значения (нет такого элемента) возвращает Null
wait window m4gi_get_inner(this_form,"timestr") nowait

---------------------------------------------
ВАЖНО!Краткая сводка:
I. Имя gi-формы должно начинаться с m4gi_ - иначе страница просто не будет обрабатываться.

II.
<!--m4giproperties -->
Даже если ничего не собираетесь обрабатывать при загрузке - данная конструкция все равно ДОЛЖНА НАХОДИТЬСЯ в HTML-файле! Иначе - страница не будет обрабатываться.

III.
<script type="text/javascript">
var htmlapp = null;
</script>
Конструкция объявляет переменную htmlapp, к которой в дальнейшем может быть обращение (например, для замены значения HTML-элемента средствами FoxPro).
Данная конструкция ДОЛЖНА НАХОДИТЬСЯ в HTML-файле в любом случае!

IV.
<body oncontextmenu="return false">
Данная кострукция запрещает вызов контекстного меню (что изначально отсекает массу проблем).
Если хотите получить неудобные вопросы со стороны пользователя - можете ее не использовать

V. Если в gi-форме применяется <form></form> , ОБЯЗАТЕЛЬНО должен быть method="post" - иначе, данные не будут передаваться.

VI. Если в теле <fp></fp> находится многострочный текст <action></action> или <scriptaction></scriptaction> - ОБЯЗАТЕЛЬНО в теле <action> или <scriptaction> замените перевод строки на комбинацию {{
Иначе - в процессе обработки кода <fp></fp> получите ошибку.
Пример правильного применения:
<fp>return iif(_m4gi_type=0,[<p align=right><i><font size=-1><a href="<action>_m4gi_type=1{{_m4gi_name=''</action>">Перейти к добавлению нового пользователя</a></font></i></p>],;
[<p align=right><i><font size=-2><br><br><a href="<action>_m4gi_type=0{{_m4gi_name=''</action>">Отказаться от добавления нового пользователя</a></font></i></p>])</fp>


VII. ВСЕГДА рекомендуется записывать обращение по путям, начиная с / или \, т.е. с начального каталога.
Для чего - сберечь себе нервы.
Примеры:
m4gi_exec('/2x2/prg_m4gi_2x2_prover.prg',post,this_form,1,2)
m4gi_file_path('\2x2\metka.gif')
<a href="<action>m4gi_newform('\m4gi_magazin.html')</action>">К магазину!</a>

VIII. Старайтесь оставлять двойные кавычки "" для html, ограничивая ими href(ссылка), action(html-форма) или код javascript. При написании кода используйте '' или [].
Для чего - сберечь себе нервы (хотя, признаюсь, сам это правило иногда не соблюдаю).
Примеры:
<form name="main_form" id="main_form_id1" method="post" action="<action>return m4gi_exec('/prg_m4gi_avtoriz.prg',post,this_form)</action>" onsubmit="return false;">
<a href="<action>m4gi_newform('\m4gi_magazin.html')</action>">К магазину!</a>
<a onclick="<scriptaction>wait window 'Добрый день! Сегодня '+dtoc(datetime()) nowait</scriptaction>" href="javascript:void(0)">Пример вывода в окне Wait приветствия с текущей датой</a>

IX. РЕКОМЕНДУЕТСЯ закрывать (если, конечно, есть такая необходимость) текущую форму ТОЛЬКО коммандой this_form.close

---------------------------------------------
ПРИМЕР ТИПИЧНОЙ gi-формы - \m4gi_start.html - авторизация на <form></form> с таймером отображения текущего времени (реализован на FoxPro):

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd">
<HTML><HEAD>
<META http-equiv=Content-Type content="text/html; charset=windows-1251">
<title>Страница Интерфейса</title>
<META HTTP-EQUIV="Keywords" CONTENT="Генератор Интерфейса">
<META name="Author" content="Igor Croschin">
<META name=keywords content="">
<META name=description content="">

<script type="text/javascript">
var htmlapp = null;
</script>

<!--m4giproperties <load>

this_form.caption="Кто тут?"
IF VARTYPE(_m4gi_name)<>"C"
PUBLIC _m4gi_name
_m4gi_name="" && ""-никто не выбран, иначе - будет проставлено в поле имени
ENDIF

IF VARTYPE(_m4gi_type)<>"N"
PUBLIC _m4gi_type
_m4gi_type=0 && 0-предполагается авторизация; 1-предполагается добавление
ENDIF

if not used("_m4gi_korzina")
select 0
create cursor _m4gi_korzina (id_ n(15),name_ c(50),id_predmet n(15))
endif

if not used("_m4gi_magazin")
local ims
ims = m4gi_file_path_default("\_m4gi_magazin.dbf")
if m4gi_file(ims)
select 0
use (ims)
else
select 0
create table (ims) (id_predmet n(15),kratkoe c(200), polnoe m, cena n(15), img_ c(200))
use (ims)
endif

endif

if not used("_m4gi_trofei")
ims = m4gi_file_path_default("\_m4gi_trofei.dbf")
if m4gi_file(ims)
select 0
use (ims)
else
select 0
create table (ims) (id_ n(15),id_predmet n(15),date_ t)
use (ims)
endif

endif

select * from _m4gi_magazin into cursor _m4gi_magazin_temp order by cena

IF USED("_m4gi_names")
SELECT _m4gi_names
USE
ENDIF

IF m4gi_file_default("_m4gi_names.dbf")
SELECT 0
USE (m4gi_file_path_default("_m4gi_names.dbf"))
ELSE
SELECT 0
CREATE TABLE m4gi_file_path_default("_m4gi_names.dbf") (id_ N(15), name_ c(50), pwd_ c(20), money_ n(15))
ENDIF

</load>

<init>

THIS_FORM.AddObject("_timer1","cmoitimer")
local stro
TEXT TO stro TEXTMERGE NOSHOW
if left(this_form.caption,3)=="Кто" && срабатывание только на этой форме
local jj,zz
jj=ttoc(datetime())
this_form.caption="Кто тут? ("+jj+")"
m4gi_set_inner(this_form,"timestr",jj)
else
this_form._timer1.enabled=.f. && таймер работает только при .t.
endif
ENDTEXT
this_form._timer1.textproc=stro
this_form._timer1.post=post
this_form._timer1.this_form=this_form
this_form._timer1.interval=100

</init>
-->

</HEAD>

<body oncontextmenu="return false">

<br>
<SCRIPT LANGUAGE="JavaScript" SRC="fly.js">
</script>


<p align=right>Текущие Дата/время: <b id="timestr"><fp>&datetime()</fp></b></p>

<!--
<script type="text/javascript">
setInterval(function(){
<scriptaction>
local jj
jj=ttoc(datetime())
this_form.caption="Кто тут? ("+jj+")"
This_form.wb.Document.getElementByID("timestr").innerHtml=jj
</scriptaction>;
}, 755);
</script>
-->


<br>

<form name="main_form" id="main_form_id1" method="post" action="<action>return m4gi_exec('/prg_m4gi_avtoriz.prg',post,this_form)</action>" onsubmit="return false;">
<fp>

LOCAL stro
stro=""
DO CASE
CASE _m4gi_type=0
TEXT TO stro TEXTMERGE NOSHOW
Необходимо авторизоваться (что-бы понять, а кто, собственно, здесь находится).<br><br>
<br>Введите имя: <input type="text" name="text_name" id="text_id1" value="<<_m4gi_name>>" />
<br>Введите пароль: <input type="password" name="text_pwd" id="text_id2" value="" />
ENDTEXT



CASE _m4gi_type=1
TEXT TO stro TEXTMERGE NOSHOW
Добавление нового пользователя.<br><br>
<br>Введите имя: <input type="text" name="text_name" id="text_id3" value="<<_m4gi_name>>" />
<br>Отнеситесь к этому внимательно! Восстановление пароля "невозможно"!<br><br>Введите пароль: <input type="password" name="text_pwd1" id="text_id4" value="" />
<br>Введите пароль еще раз (для проверки): <input type="password" name="text_pwd2" id="text_id5" value="" />
ENDTEXT
ENDCASE
return stro

</fp>
</form>
<input type="button" value="<fp>&iif(_m4gi_type=0,"Войти","Добавить и войти")</fp>" onclick="document.forms['main_form'].submit();">
<fp>&iif(_m4gi_type=0,[<p align=right><i><font size=-1><a href="<action>_m4gi_type=1{{_m4gi_name=''</action>">Перейти к добавлению нового пользователя</a></font></i></p>],;
[<p align=right><i><font size=-2><br><br><a href="<action>_m4gi_type=0{{_m4gi_name=''</action>">Отказаться от добавления нового пользователя</a></font></i></p>])</fp>

<br>
<a onclick="<scriptaction>
wait window "Добрый день! Сегодня "+dtoc(datetime()) nowait
</scriptaction>" href="javascript:void(0)">Кликните! Пример вывода в окне Wait приветствия с текущей датой</a>

</body>
</html>

---------------------------------------------



Исправлено 1 раз(а). Последнее : maple4, 08.12.10 11:59
Ratings: 0 negative/0 positive
Re: Генератор Интерфейса (с использованием web-контрола)
maple4
Автор

Сообщений: 210
Откуда: Москва
Дата регистрации: 26.10.2007
Обсуждения, как обычно в этом разделе, не получилось.

Я уже Игорю про ересь по мотивам Windows писАл....
alexzima
Возможно.
Лично я для себя тему с применением web-контрола закрыл - выжал, по моему, все что мог, попутно наступив на все возможные грабли.
Естественно, через это (грабли) должен пройти каждый, но попытаюсь описать то, с чем пришлось столкнуться (все было РЕШЕНО!):

I.
1. Обработка адреса в ссылке и web-форме.
Проблема начинается, если в адресе присутствуют буквы национального алфавита, спецсимволы и пробел.
Предварительная кодировка (в esc-последовательность) - только себя путать + страдает удобочитаемость кода.
2. Передача параметров.
Это можно сделать как ссылкой, так и формой <form></form> (при этом не забыть, что в <form> method="post").
Если в параметрах есть буквы национального алфавита, спецсимволы и пробел - тут же проблема с адресом.
При клике по ссылке, сформированной с параметрами - при обработке адреса параметры не обрабатываются, если страница находится не в текущем каталоге - нет объяснения.
4. Многострочный текст кода FoxPro.
Если по клику на ссылке запускается функция - вроде бы никаких проблем нет. Ну а если текст обработки клика состоит из двух и более строк?
Нужно так же учесть, что есть ограничение на длину выражения в самом <a href=""> - длина почему-то меньше заявленной для IE.

РЕШЕНИЕ ВСЕЙ ЭТОЙ группы проблем - при клике по ссылке или кнопке запускается функция (а уже в ней - ведется обработка) , которая и возвращает правильный адрес.

Например,
вместо
<br><a href="m4gi_2x2_form.html?session=_dsddg1234&igrok=Валера&uroven=1">I</a>
НАДО записать
<br><a href="<action>return '/2x2/m4gi_2x2_form.html?session=_dsddg1234&igrok=Валера&uroven=1'</action>">I</a>

и уже во время формирования конструкция <action>....</action> будет АВТОМАТИЧЕСКИ заменена строкой
VFP:///return m4gi_progar(post,this_form,1)

или, полностью:
<br><a href="VFP:///return m4gi_progar(post,this_form,1)">I</a>

ОБРАТИТЕ ВНИМАНИЕ - в строке НЕТ упоминания о странице html,параметрах и коде FoxPro , что СРАЗУ снимает кучу проблем, например, с той же кодировкой национального алфавита, спецсимволов и пробела.

II. Код FoxPro в коде Javascript.
В коде JavaScript можно вставить код FoxPro, результат которого может быть обработан далее тем же javascript.

Во время формирования страницы ....

<script type="text/javascript">
setInterval(function(){
<scriptaction>
local jj
jj=ttoc(datetime())
this_form.caption="Кто тут? ("+jj+")"
This_form.wb.Document.getElementByID("timestr").innerHtml=jj
</scriptaction>;
}, 755);
</script>

... следующая конструкция <scriptaction>... </scriptaction> будет автоматически заменена на функцию javascript, которая, в свою очередь, запустит на выполнение код FoxPro:
<script type="text/javascript">
setInterval(function(){
htmlapp.scriptaction(1);
}, 755);
</script>

Почему может потребоваться код FoxPro в Javascript?
Самое главное: Используя FoxPro Вы НИ В ЧЕМ себя не ограничиваете - полная свобода действий, в отличие от стандарта JavaScript.

И сразу немного дегтя - НЕ рекомендуется использовать setInterval вместе с FoxPro (для этого лучше использовать Timer FoxPro в <init></init> формы).
this_form.AddObject("_timer1","cmoitimer")
local stro
TEXT TO stro TEXTMERGE NOSHOW
if left(this_form.caption,3)=="Кто" && срабатывание только на этой форме
local jj,zz
jj=ttoc(datetime())
this_form.caption="Кто тут? ("+jj+")"
m4gi_set_inner(this_form,"timestr",jj)
else
this_form._timer1.enabled=.f. && таймер работает только при .t.
endif
ENDTEXT
this_form._timer1.textproc=stro
this_form._timer1.post=post
this_form._timer1.this_form=this_form
this_form._timer1.interval=100


Именно для использования <action></action> и <scriptaction></scriptaction> требовался запрет (по Backspace и Alt+влево) перехода на предыдущую страницу.


P.S.
Всех с Cataclyzm-ой
Ratings: 0 negative/0 positive
Re: Web-интерфейс для FoxPro-программы
maple4
Автор

Сообщений: 210
Откуда: Москва
Дата регистрации: 26.10.2007
Обновился Генератор Интерфейса.
Страница проекта:
www.maple4.ru

Дистрибутив:
www.maple4.ru ~650 Кб.
Дистрибутив содержит prg-файлы и формы. Для запуска требуется
установленный FoxPro 9!
Рекомендую ИЗ ОКНА FoxPro запустить готовый m4gi.exe или
скомпилировать его из проекта.

Что появилось/изменилось:
I. Теперь тестовый проект (gi-формы) использует стили. Соответственно, и вид - совершенно другой.
Обратите внимание - для игры 2x2 используется свой особенный стиль.

II. Появилась возможность добавлять текстовые многострочные сообщения, причем текст редактируется в WYSIWYG-режиме (т.е. например, как во FrontPage. Или Word-e).
Есть кнопки управления начертанием шрифта, управления цветом и фоном, видом нумерованного списка. Остальные - не рассматривал, но, при необходимости, можно добавить нужные кнопки с нужной реакцией (поиск в google по "execCommand").

Код - смесь JavaScript, FoxPro (в конструкции <action></action>, отвечающей за реакцию при добавлении сообщения. Обратите внимание - при попытке добавить пустое сообщение "вылезет" окно с предупреждением) и языка разметки HTML:

<script>
function Post()
{
document.myform.message.value = newTextArea.document.body.innerHTML;
myform.submit();
}
function EditorExecCommand( command_param,prm )
{
var tr = frames.newTextArea.document.selection.createRange();
tr.select();
switch (command_param) {
case 'ForeColor':tr.execCommand("foreColor", false, prm); break;
case 'BackColor':tr.execCommand("backColor", false, prm); break;
default:
tr.execCommand( command_param );
}
frames.newTextArea.focus();
}
</script>
<form name="myform" method="POST" action="<action>
if empty(post.message)
m4gi_newform("/m4gi_error.html?texterror=Не введен текст сообщения!")
return .t.
endif
local maxx
select _m4gi_posts
calculate max(id_) to maxx
if isnull(maxx)
maxx=1
else
maxx=maxx+1
endif
append blank
replace id_ with maxx, name_ with _m4gi_name, date_ with datetime(), text_ with post.message
_m4gi_page_post=1
</action>">
<input type="hidden" name="message">
</form>
<input type="button" onClick="EditorExecCommand( 'Bold' );" value=" B ">
<input type="button" onClick="EditorExecCommand( 'Italic' );" value=" I ">
<input type="button" onClick="EditorExecCommand( 'Underline' );" value=" U ">
&nbsp;
<input type="button" onClick="EditorExecCommand( 'JustifyLeft' );" value=" Left ">
<input type="button" onClick="EditorExecCommand( 'JustifyCenter' );" value=" Center ">
&nbsp;
<input type="button" onClick="EditorExecCommand( 'InsertOrderedList' );" value=" OL ">
<input type="button" onClick="EditorExecCommand( 'InsertUnorderedList' );" value=" UL ">
<br>Цвет текста
&nbsp;
<input type="button" onClick="EditorExecCommand( 'ForeColor','#ff0000');" value=" RED ">
<input type="button" onClick="EditorExecCommand( 'ForeColor','#00ff00');" value=" GREEN ">
<input type="button" onClick="EditorExecCommand( 'ForeColor','#0000ff');" value=" BLUE ">
<input type="button" onClick="EditorExecCommand( 'ForeColor','#ffffff');" value=" WHITE ">
<input type="button" onClick="EditorExecCommand( 'ForeColor','#000000');" value=" BLACK ">
<br>Цвет фона
&nbsp;
<input type="button" onClick="EditorExecCommand( 'BackColor','#ff0000');" value=" RED ">
<input type="button" onClick="EditorExecCommand( 'BackColor','#00ff00');" value=" GREEN ">
<input type="button" onClick="EditorExecCommand( 'BackColor','#0000ff');" value=" BLUE ">
<input type="button" onClick="EditorExecCommand( 'BackColor','#ffffff');" value=" WHITE ">
<input type="button" onClick="EditorExecCommand( 'BackColor','#000000');" value=" BLACK ">
<p>
<iframe width=500px height=200px id="newTextArea" name="newTextArea"></iframe>
</p>
<input type="button" onClick="Post();" value="Добавить сообщение">
<script>
newTextArea.document.designMode = "on";
</script>

III. Добавил страницу отображения всех сообщений с возможностью листания (по 10 сообщений на страницу).
Реализуется кодом FoxPro (в конструкциях <fp>...</fp>) и языком разметки HTML (сначала выводится навигатор по страницам, потом - список сообщений на страницу в HTML-таблице):

<br>Последние сообщения
<fp>
LOCAL stro,vsegg,p_sta,p_end,vseg
p_sta=0
p_end=0
stro=""
select _m4gi_posts
CALCULATE count(id_) TO vseg
IF not ISNULL(vseg)
if vseg/10=int(vseg/10)
vsegg=INT(vseg/10)
else
vsegg=INT(vseg/10)+1
endif
else
vsegg=1
endif
IF vsegg<=5
p_end=vsegg
p_sta=1
ELSE
p_end=_m4gi_page_post+2
IF p_end>vsegg
p_end=vsegg
ENDIF
p_sta=_m4gi_page_post-2
IF p_sta<=0
p_sta=1
ENDIF
ENDIF
LOCAL ii
FOR ii=p_sta TO p_end
IF ii<>_m4gi_page_post
stro=stro+[<a href="<action>_m4gi_page_post=]+alltr(str(ii))+[</action>">]+alltr(str(ii))+[</a>&nbsp;&nbsp;]
ELSE
stro=stro+[<font color=red><b>]+alltr(str(ii))+[</b></font>&nbsp;&nbsp;]
ENDIF
ENDFOR
if _m4gi_page_post>3 and vsegg>5
stro=[<a href="<action>_m4gi_page_post=]+alltr(str(1))+[</action>">]+alltr(str(1))+[</a>&nbsp;&nbsp;.....&nbsp;&nbsp;]+stro
endif
if _m4gi_page_post<vsegg-2 and vsegg>5 and _m4gi_page_post<>1
stro=stro+[.....&nbsp;&nbsp;]+[<a href="<action>_m4gi_page_post=]+alltr(str(vsegg))+[</action>">]+alltr(str(vsegg))+[</a>]
endif
RETURN stro
</fp>
<table border=1 width=100%><tr><td align=left valign=top width=10px>Дата/время</td><td align=left valign=top width=10px>Пользователь</td><td align=left valign=top width=100%>Примечание</td></tr>
<fp>
local stro
stro=""
if _m4gi_page_post=1
select top 10 * from _m4gi_posts into cursor vibor_posts ORDER BY date_ desc
else
execscript([SELECT TOP 10 * FROM _m4gi_posts WHERE id_ NOT IN (SELECT top ]+ALLTRIM(str((_m4gi_page_post-1)*10))+ [ id_ FROM _m4gi_posts ORDER BY date_ desc) ORDER BY date_ desc into cursor vibor_posts])
endif
select vibor_posts
scan
TEXT TO stro TEXTMERGE ADDITIVE NOSHOW
<tr><td align=left valign=top width=10px><<ttoc(vibor_posts.date_)>></td><td align=left valign=top width=10px><<vibor_posts.name_>></td><td align=left valign=top width=100%><<vibor_posts.text_>></td></tr>
ENDTEXT
endscan
return stro
</fp>
</table>


IV. Добавил страницу с отображением курсов валют на текущее число (cbr.ru) и событий за число в истории (wikipedia.org)
По сути - грабберы, т.е. нет никаких xml-файлов (но, например, для cbr.ru такой файл, скорее всего, есть).
Например, код для считывания курсов прямо со страницы сайта cbr.ru.

<!-- cbr.ru -->
<fp>
LOCAL txtwiki,ncn,virez,nc1,kc1
LOCAL ddate,simv,tdate
tdate=datetime()
ddate=ttod(tdate)
simv=DTOC(ddate,1)
LOCAL prover,tprover
IF NOT m4gi_file(LOWER(SYS(2023))+"\dtcbr"+"txt.txt")
prover=.T.
ELSE
tprover=CTOT(FILETOSTR(LOWER(SYS(2023))+"\dtcbr"+"txt.txt"))
IF tdate-tprover>600 or ttod(tdate)<>ttod(tprover)&& обновление - каждые 10 минут
prover=.T.
ENDIF
ENDIF
txtwiki=""
nc1=0
kc1=0
virez=""
ncn=0
IF prover
txtwiki=STRCONV(m4gi_page_from_internet([http://www.cbr.ru/currency_base/daily.aspx?C_month=]+SUBSTR(simv,5,2)+[&C_year=]+SUBSTR(simv,1,4)+;
[&date_req=]+SUBSTR(simv,7,2)+[.]+SUBSTR(simv,5,2)+[.]+SUBSTR(simv,1,4)),11)
nc1=ATC([<table class="cbrtbl">],txtwiki)
IF nc1<>0
kc1=ATC([<div align="right">],txtwiki)
IF nc1<>0 AND kc1<>0 AND kc1>nc1
virez= SUBSTR(txtwiki,nc1,kc1-nc1)
ENDIF
STRTOFILE(virez,LOWER(SYS(2023))+"\cbrtxt.txt")
STRTOFILE(TTOC(tdate),LOWER(SYS(2023))+"\dtcbr"+"txt.txt")
ELSE
STRTOFILE("",LOWER(SYS(2023))+"\cbrtxt.txt")
virez=""
ENDIF
ELSE
virez=FILETOSTR(LOWER(SYS(2023))+"\cbrtxt.txt")
ENDIF
IF NOT EMPTY(virez)
RETURN [<h2>Курсы валют Центрального банка Российской Федерации на ]+TTOC(tdate)+[ </h2><br>]+;
strtran(virez,[<table class="CBRTBL">],[<table width=80% border=1>])+;
"<hr>"
ENDIF
RETURN ""
</fp>
<!-- cbr.ru -->
[attachment 11282 form_m4gi.JPG]


Исправлено 1 раз(а). Последнее : maple4, 24.12.10 15:12
Ratings: 0 negative/0 positive


Извините, только зарегистрированные пользователи могут оставлять сообщения в этом форуме.

On-line: 2 (Гостей: 2)

© 2000-2024 Fox Club 
Яндекс.Метрика