2002-11-21 17:07:11 UTC
UPDATE: В связи с ошибками содержащимися в предыдущем варианте этого примера, пример был серьёзно переделан и вместо двух неправильных вариантов кода, приведён один, — правильный.
Первое, с чего хочу начать, — это собственно XML, формируемый парсером. Он имеет следующий вид (ноябрь 2002):
<month> <month_year month="11" month_title="ноябрь" next_month="12" prev_month="10" year="2002" next_year="2003" prev_year="2001" hit="1" /> <weekdays> <day title="Пн"/> <day title="Вт"/> <day title="Ср"/> <day title="Чт"/> <day title="Пт"/> <day title="Сб"/> <day title="Вс"/> </weekdays> <week> <day d="0"/> <day d="0"/> <day d="0"/> <day d="0"/> <day d="1"/> <day d="2"/> <day d="3" f="1"/> </week> <week> <day d="4"/> <day d="5"/> <day d="6" f="1" hit="1"/> <day d="7"/> <day d="8"/> <day d="9"/> <day d="10" in="1"/> </week> ... </month>
Или графически (сгенерировано XML Spy ) это будет выглядеть так:
Думаю особо объяснять тут нечего, расскажу лишь о назначении некоторых атрибутов. in
— появляется у дня если этот день является текущим днем, f
— появляется у дня, за который были материалы, hit
— этот день/месяц выбран пользователем. Да и вы наверно уже обратили внимание что элементов week
может быть несколько, сколько? — информация к размышлению.
Информация о том, за какой месяц выводить календарь передаются коду класса через параметр конструктора. Параметр должен быть хэшем следующей структуры:
$date_user[ $.day(2) $.month(10) $.year(2002) ]
Т.е. если данные берутся из строки запроса, хэш можно инициализировать следующим образом:
$date_user[ $.day(^form:d.int(0)) $.month(^form:m.int(0)) $.year(^form:y.int(0)) ]
Если при создании экземпляра класса ему передаётся хэш с нулевыми ключами, выводится календарь на текущий месяц текущего года.
Далее код класса:
####### # Calendar class @CLASS calendar ####### @init[date_user] # получение текущей даты $date_now[^date::now[]] ^if($date_user.year && $date_user.month){ $d($date_user.day) $m($date_user.month) $y($date_user.year) }{ $d($date_now.day) $m($date_now.month) $y($date_now.year) } # Инициал. хэш показывающий какой период времени передан # при создании экземпляра класса $self.date_user[$date_user] # вызов метода для создания специфических для языка переменных # по-умолчанию для русского, если нужен другой язык, сделать # класс производный от этого и переопределить в нем только один # метод @i18n[] ^i18n[] # определение переменных месяца и года для пред. месяца календаря $calendar_month_prev[^date::create($y;$m)] ^calendar_month_prev.roll[month](-1) $prev_m($calendar_month_prev.month) $prev_y($calendar_month_prev.year) # определение переменных месяца и года для след. месяца календаря $calendar_month_next[^date::create($y;$m)] ^calendar_month_next.roll[month](+1) $next_m($calendar_month_next.month) $next_y($calendar_month_next.year) # the hash of publications(files) $days_of_files[ ^hash::sql{ SELECT DAYOFMONTH(date_field), some_field FROM some_table WHERE MONTH(date_field) = $m and YEAR(date_field) = $y }[$.distinct(1)] ] ####### # Метод, в котором определяются специфические для разных языков # параметры календаря @i18n[] # the hash of local calendar (month and day names) $calendar_locale[ $.month_names[ $.1[Январь] $.2[Февраль] $.3[Март] $.4[Апрель] $.5[Май] $.6[Июнь] $.7[Июль] $.8[Август] $.9[Сентябрь] $.10[Октябрь] $.11[Ноябрь] $.12[Декабрь] ] $.day_names[ $.0[Пн] $.1[Вт] $.2[Ср] $.3[Чт] $.4[Пт] $.5[Сб] $.6[Вс] ] ] $month[$calendar_locale.month_names.$m] $calendar_month[^date:calendar[rus]($y;$m)] ####### @xml[] $result[ <month> ^month_year[] ^weekdays[] ^week[] </month> ] ####### # формирование тега для года и месяца @month_year[][hit] ^if( $date_user.day || ( $date_now.month == $date_user.month && $date_now.year == $date_user.year && $date_user.day ) ){ $hit[] }{ $hit[hit="1"] } <month_year month="$m" month_title="$month" next_month="$next_m" prev_month="$prev_m" year="$y" next_year="$next_y" prev_year="$prev_y" $hit /> ####### # тег названий дней недели @weekdays[] <weekdays> ^for[i](0;6){ <day title="$calendar_locale.day_names.$i"/> } </weekdays> ####### # формирование тегов для каждой недели месяца # day - день месяца, может быть нулем, если начало/конец # первой/последней недели месяца не приходятся на понедельник/воскресенье @week[][day;in;f;hit] ^calendar_month.menu{ <week> ^for[i](0;6){ $day($calendar_month.$i) # формирование атрибута текущего дня ^if( $date_now.day == $day && $date_now.month == $date.month && $date_now.year == $date.year ){ $in[in="1"] }{ $in[] } # формирование атрибута нахождения в данном дне ^if($day == $date.day){ $hit[hit="1"] }{ $hit[] } # формирование атрибута наличия материалов за день ^if($days_of_files.$day){ $f[f="1"] }{ $f[] } <day d="$day" $in $f $hit/> } </week> }
Для получения XML кода календаря нужно сформировать объект конструктором init $object[^calendar::init[$date_user]]
далее вызовом ^object.xml[]
получаем XML календаря.
Преобразуем полученный XML в HTML:
$date_user[ $.day(^form:d.int(0)) $.month(^form:m.int(0)) $.year(^form:y.int(0)) ] $object[^calendar::init[$date_user]] $calendar_xml[^xdoc::create{^object.xml[]}] $calendar_html[^$calendar_xml.transform[calendar.xsl]] ^calendar_html.string[ $.method[html] $.indent[no] ]
Пример стилевой таблицы calendar.xsl:
<?xml version="1.0" encoding="windows-1251"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > <!-- переменные начальных значений месяца и года календаря --> <xsl:variable name="start_year">2002</xsl:variable> <xsl:variable name="start_month">2</xsl:variable> <xsl:template match="month"> <table cellpadding="4" cellspacing="3" width="100%"> <tr> <td bgcolor="white" align="center" width="16%" > <xsl:call-template name="prev_month"/> </td> <td bgcolor="white" align="center" colspan="5" width="68%" > <xsl:choose> <xsl:when test="week/*/@in"> <strong> <xsl:call-template name="month_year_link"/> </strong> </xsl:when> <xsl:otherwise> <xsl:call-template name="month_year_link"/> </xsl:otherwise> </xsl:choose> </td> <td bgcolor="" align="center" width="16%"> <xsl:call-template name="next_month"/> </td> </tr> <tr> <xsl:apply-templates select="weekdays"/> </tr> <xsl:for-each select="week"> <tr> <xsl:call-template name="week"/> </tr> </xsl:for-each> </table> </xsl:template> <!-- ссылка на предыдущий месяц --> <xsl:template name="prev_month"> <xsl:choose> <xsl:when test=" month_year/@prev_year < $start_year " > << </xsl:when> <xsl:otherwise> <xsl:choose> <xsl:when test=" month_year/@prev_month < $start_month and month_year/@prev_year = $start_year " > << </xsl:when> <xsl:otherwise> <a href="{month_year/@prev_year}-{month_year/@prev_month}.html" style="text-decoration: none;" > << </a> </xsl:otherwise> </xsl:choose> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- ссылка на следующий месяц --> <xsl:template name="next_month"> <xsl:choose> <xsl:when test="week/*/@in"> >> </xsl:when> <xsl:otherwise> <a href="{month_year/@next_year}-{month_year/@next_month}.html" style="text-decoration: none;" > >> </a> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- ссылка на текущий месяц --> <xsl:template name="month_year_link"> <xsl:choose> <xsl:when test="month_year/@hit"> <xsl:call-template name="month_year"/> </xsl:when> <xsl:otherwise> <a href="{month_year/@year}-{month_year/@month}.html"> <xsl:call-template name="month_year"/> </a> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- название месяца и года --> <xsl:template name="month_year"> <xsl:value-of select="month_year/@month_title"/> <xsl:text> </xsl:text> <xsl:value-of select="month_year/@year"/> </xsl:template> <!-- названия дней недели --> <xsl:template match="weekdays"> <xsl:for-each select="day"> <td align="center" width="16%" bgcolor="#eeeeee" > <xsl:choose> <xsl:when test="position() != 7"> <xsl:value-of select="@title"/> </xsl:when> <xsl:otherwise> <span style="color: red"> <xsl:value-of select="@title"/> </span> </xsl:otherwise> </xsl:choose> </td> </xsl:for-each> </xsl:template> <!-- вывод недель месяца --> <xsl:template name="week"> <xsl:for-each select="day"> <td align="center" width="16%"> <xsl:attribute name="bgcolor"> <xsl:choose> <xsl:when test="@in"> #ffcc00 </xsl:when> <xsl:otherwise> white </xsl:otherwise> </xsl:choose> </xsl:attribute> <xsl:choose> <xsl:when test="position() != 7"> <xsl:call-template name="day"/> </xsl:when> <xsl:otherwise> <span style="color: red"> <xsl:call-template name="day"/> </span> </xsl:otherwise> </xsl:choose> </td> </xsl:for-each> </xsl:template> <!-- вывод дней --> <xsl:template name="day"> <xsl:choose> <xsl:when test="@hit"> <strong><xsl:call-template name="day_value"/></strong> </xsl:when> <xsl:otherwise> <xsl:call-template name="no_hit"/> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- вывод невыбранных дней --> <xsl:template name="no_hit"> <xsl:choose> <xsl:when test="@f"> <a href="{//*/@year}-{//*/@month}-{@d}.html"> <xsl:call-template name="day_value"/> </a> </xsl:when> <xsl:otherwise> <xsl:call-template name="day_value"/> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template name="day_value"> <xsl:choose> <xsl:when test="@d = 0"/> <xsl:when test="@d > 9"> <xsl:value-of select="@d"/> </xsl:when> <xsl:otherwise> <xsl:text>0</xsl:text><xsl:value-of select="@d"/> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet>
Этот календарь легко преобразуется в требуемый вид для другого языка, например английского. Для этого необходимо создать класс производный от класса calendar
в котором переопределяется всего лишь один метод ^i18n[]
, например для английского языка:
####### @CLASS calendar_eng ####### @USE calendar.p ####### @BASE calendar ####### # Метод, в котором определяются специфические для разных языков # параметры календаря @i18n[] # the hash of local calendar (month and day names) $calendar_locale[ $.month_names[ $.1[January] $.2[February] $.3[March] $.4[April] $.5[May] $.6[June] $.7[July] $.8[August] $.9[September] $.10[October] $.11[November] $.12[December] ] $.day_names[ $.0[Su] $.1[Mo] $.2[Tu] $.3[We] $.4[Th] $.5[Fr] $.6[Sa] ] ] $month[$calendar_locale.month_names.$m] $calendar_month[^date:calendar[eng]($y;$m)]
Ну и далее вместо класса calendar
подключайте и используйте calendar_eng
:
$object[^calendar_eng::init[]] $calendar_xml[<?xml version="1.0" encoding="$request:charset"?> ^xdoc::create{^object.xml[]}] $calendar_html[^$calendar_xml.transform[calendar_eng.xsl]] ^calendar_html.string[ $.method[html] $.indent[no] ]
Да и ещё, — в английском языке первый день недели не понедельник а воскресенье, поэтому стилевую таблицу необходимо подправить(переопределить в ней пару шаблонов). Текст calendar_eng.xsl
:
<?xml version="1.0" encoding="windows-1251"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > <xsl:import href="calendar.xsl"/> <xsl:template match="weekdays"> <xsl:for-each select="day"> <td bgcolor="#eeeeee" align="center" width="16%" > <xsl:choose> <xsl:when test="position() != 1"> <xsl:value-of select="@title"/> </xsl:when> <xsl:otherwise> <span style="color: red"> <xsl:value-of select="@title"/> </span> </xsl:otherwise> </xsl:choose> </td> </xsl:for-each> </xsl:template> <xsl:template name="week"> <xsl:for-each select="day"> <td align="center" width="16%"> <xsl:attribute name="bgcolor"> <xsl:choose> <xsl:when test="@in"> #ffcc00 </xsl:when> <xsl:otherwise> white </xsl:otherwise> </xsl:choose> </xsl:attribute> <xsl:choose> <xsl:when test="position() != 1"> <xsl:call-template name="day"/> </xsl:when> <xsl:otherwise> <span style="color: red"> <xsl:call-template name="day"/> </span> </xsl:otherwise> </xsl:choose> </td> </xsl:for-each> </xsl:template> </xsl:stylesheet>
Разумеется можно и не получать HTML из XML не отходя от кассы, — в случае если полностью вся страница/сайт формируется сначала в XML'е и затем преобразуется в HTML, это делать совсем не надо. Надо просто вставить вызов ^object.xml[]
в требуемое место, ну и затем в нужном xsl файле подключать calendar.xsl
и ловить им элемент month
в требуемом месте.
Ну вот и всё, ничего сложного я надеюсь. Работающий пример смотрите … сами знаете где смотреть :).