программирование
Opera 10.50: скоро
Safari, Chrome и «решётка»
У браузеров на движке WebKit
Иногда я верю, что один браузер это хорошо.
Иногда.
Добавлено 13 декабря 0:40: в комментариях мне подсказываю, что, кодируя «решётку» как «%23», WebKit поступает по RFC. То есть, формально, претензий никаких. По смыслу же, мне кажется, что «решётку» тут кодировать не надо.
Опера ест память

Это очень странно, но стоит включить Flash в «Опере», как она начинает нещадно есть память. На скриншоте (открыто всего 12 закладок) далеко не худшая ситуация, я видел как она съедала вдвое большее количество. Помогает только снятие задачи (закрыть не получается, «Опера» продолжает висеть в памяти) и запуск заново.
Ситуация повторяется на обоих моих ноутах, под разными ОС (на одном — Windows XPSP3, на втром — Windows 7) и с разными версиями браузера (10.10 и 10.20).
Добавлено 11 декабря 2009 13:55: в комментах говорят, что с Flash так себя ведёт любой браузер. Видимо, новые версии Flash имеют
User.js и FF
Сегодня написал небольшой user.js для «Оперы» и решил его переписать под плагин «Greasemonkey», под «Firefox». Оказалось, что есть немаловажные различия.
выглядит как настоящий, только не радует на него, например, рекомендуется вешать все события.
location.href="javascript:void(pageFunc(123))", утвержается, что это более безопасно, видимо, чтобы не передавать контекст выполнения наружу. Говорят, что в Chrome 4 это единственный способ добраться до «window».
Ну и можно использовать localStorage (FF 3.5+) и globalStorage (FF 2.0+) для сохранения данных user.js.
TestLink
Время от времени занимаюсь модификацией используемого внутри компании инструмента «TestLink». Сложная задача — приделать к нему ЧПУ (TestLink целиком на фреймах, у него не то что ЧПУ,
С задачей справился чуть менее, чем полностью — осталась пара мелочей, а в пятницу по половины одиннадцатого вечера боролся с деревом
Осложняется всё тем, что дерево снаружи недоступно (изолируется внутри функции), а менять
function fireEvent(element, eventname){
if (document.createEventObject){
return element.fireEvent('on' + eventname, document.createEventObject())
} else{
var event = document.createEvent("HTMLEvents")
event.initEvent(eventname, true, true)
return !element.dispatchEvent(event)
}
}В итоге получилась дикая вещь — когда человек вводит этот самый понятный ему URL, браузер спрашивает у сервера путь по дереву, получает его, по таймеру долбит дерево в плюсики (дерево грузит каждый узер через AJAX), чтобы развернуть каждый узел по пути следования к test case, а когда добирается до цели, таймер тушится.
Такие развесистые клюквы там приходится программировать на каждом шагу. Зато все URL абсолютно понятны пользователю и роутинг правится очень просто.
Добавлено 8 декабря 2009 0:41: потом я переписал всё на чистом ExtJS, воспользовавшись getCmp, expandPath и внутренней реализацией fireevent, но код всё равно тут оставлю — пригодится.
Presto 2.4
На Web Standards Days увидел, наконец, новый движок «Оперы» — Presto 2.4 в действии! Новую «Оперу» показывал Вадим Макеев (он работает в этой компании), версия 10.50 (название кодовое) поддерживает
Кстати говоря, Presto 2.4 уже работает в Opera Mobile 10 beta, которую можно скачать, но, к сожалению, эти самые интересные вещи
Input multiple-2
По мотивам предыдущего поста. Чтобы Opera 9+ (а так же 3.5—6.05), Firefox 3.6+, Chrome 3.0.191.0+ и Safari 4.0.1+ позволили выбрать несколько файлов в диалоге загрузки файлов, нужно написать вот такой код:
<form enctype="multipart/form-data" method="post">
<input type="file" min="1" max="9999" name="file[]" multiple="true" />
<input type="submit" name="submit" />
</form>Я указал «file[]» для того, чтобы PHP сразу разложил полученные файлы по массиву $_FILES. К сожалению, запрос от «Оперы» PHP понимает неверно, так как «Опера» шлёт такой запрос в формате multipart/mixed. К счастью, PHP в этом случае положит содержимое файлов одним
Я попробовал, это несложно. Для того, чтобы разобрать
if (isset($_POST['file'], $_POST['file'][0])) {
if ($idx = strpos($_POST['file'][0], "\n")) {
$bound = substr($_POST['file'][0], 2, $idx-2);
$body = "MIME-Version: 1.0\nContent-type: multipart/form-data; boundary={$bound}\n\n".
$_POST['file'][0];
unset($_POST['file'][0]);
$f = &$_FILES['file'];
$f['name'] = $f['type'] = $f['tmp_name'] = $f['error'] = $f['size'];
$msg = mailparse_msg_create();
if (mailparse_msg_parse($msg, $body)) {
$i = 0;
foreach(mailparse_msg_get_structure($msg) as $st) {
$section = mailparse_msg_get_part($msg, $st);
$data = mailparse_msg_get_part_data($section);
if ($data['content-type'] == 'multipart/form-data') {
continue;
}
ob_start();
if (mailparse_msg_extract_part($section, $body)) {
$tmp = tempnam(sys_get_temp_dir(), 'php');
file_put_contents($tmp, ob_get_clean());
$f['name'][$i] = $data['disposition-filename'];
$f['type'][$i] = $data['content-type'];
$f['tmp_name'][$i] = $tmp;
$f['error'][$i] = 0;
$f['size'][$i] = filesize($tmp);
$i++;
} else {
ob_end_clean();
}
}
}
unset($f);
mailparse_msg_free($msg);
}
}
var_dump($_FILES); // результат помещатся в $_FILES, не забудьте удалить временные файлы после обработки!У меня ожидается, что содержимое файлов будет передано как параметр «file[]» (в $_POST это будет $_POST[’file’][0]), но пример несложно переделать на любое другое имя или сделать это имя параметром.
Input multiple
Я
<input type="file" name="input" multiple="true" />Ещё более интересно, что в свежей бете (№4) этого браузера есть возможность (как я понял) получить доступ к выбранным файлам
Попробовал посмотреть как выгдядит прикрепление нескольких файлов со стороны PHP, оказалось всё просто — достаточно сказать PHP, что из входящих данных нужно сделать массив и всё работает (например, указать name=«input[]»): в каждом поле каждого элемента массива $_FILES содержится массив значений. Вот как это выглядит для двух файлов:
array(1) {
["input"]=>
array(5) {
["name"]=>
array(2) {
[0]=>
string(14) "2009.09.23.jpg"
[1]=>
string(14) "2009.08.25.jpg"
}
["type"]=>
array(2) {
[0]=>
string(10) "image/jpeg"
[1]=>
string(10) "image/jpeg"
}
["tmp_name"]=>
array(2) {
[0]=>
string(18) "Z:\tmp\php3740.tmp"
[1]=>
string(18) "Z:\tmp\php3741.tmp"
}
["error"]=>
array(2) {
[0]=>
int(0)
[1]=>
int(0)
}
["size"]=>
array(2) {
[0]=>
int(35005)
[1]=>
int(63030)
}
}
}Конструкция работает в Chrome с версии 3.0.191.0 и в Safari с версии 4.0.1. К сожалению, в Opera 10.20 alpha 1 поддержки нет, в IE8 — тоже.
Добавлено 29 ноября 2009 23:08: оказывается, «Опера» поддерживала с версии 3.5 загрузку сразу нескольких файлов в обычном, «однофайловом» диалоге,
Добавлено 29 ноября 2009 23:14: «Опера» 9.0 и выше поддерживает загрузку множества файлов в одном контроле, достаточно указать параметры min и max:
<form enctype="multipart/form-data" method="post">
<input type="file" min="1" max="9999" name="file" />
<input type="submit" name="submit" />
</form>Плохие новости заключаются в том, что PHP такой запрос от «Оперы» разбирать не умеет.
Добавлено через год: в «Опере» 11 атрибуты «min» и «max» удалили, но зато работает «multiple».
Функциональное тестирование и Selenium
Посмотрел «Selenium» — прекрасный, на мой взгляд, инструмент для автоматизации функционального тестирования. Из аналогов я нашёл только «Watir», но он мне показался сильно беднее (или только показался?). Платных аналогов много, в том числе от именитых IBM и HP, но хочется именно бесплатного.
Собственный опыт у меня пока небольшой (я написал несколько тестов и бегло прочитал документацию), а из прочитанного в сети я сделал вывод, что у «Селениум» есть проблемы. Вроде как он не любит фреймы (сомнительно) и стабильно работает только в Firefox (мне FF пока достаточно).
Я пока использовал только версию для Firefox (дополнение Selenium IDE). Самый простой способ изготовить тест выглядит следующим образом: открываем Selenium IDE, создаём новый тест, подготавливаем браузер (переходим на страницу, с которой тест начнётся), жмём на красную кнопку записи (справа на панели инструментов), в браузере выполняем
После того как последовательность действий в браузере была записана «Селениумом», нужно посмотреть что получилось, возможно
Вот простенький тест, который я записал, что проиллюстрировать принцип:
сохраняем тестовую строку
store THIS-IS-TEST-STRING testPhrase
открываем страницу в браузере
open /EvgenyStepanischev
жмём на ссылку, в которой содержится указанное значение и ждём, когда браузер её полностью загрузит
clickAndWait link=EvgenyStepanischev / Тестирование
жмём на ссылку, которая выбирается CSS-селектором
clickAndWait css=.edittab a.b-button-yellow
печатаем
type postText ${testPhrase}
жмём на клавишу с указанным ID
clickAndWait really_save_button1
проверяем есть ли на странице наш текст
assertTextPresent ${testPhrase}Чтобы тест выполнился, нужно указать для него базовый URL (по которому будет выполняться тест и нажать кнопку запуска теста и тест сьюта). На нашей внутренней «Вики» в «Яндексе» этот тест откроет мой кластер, найдёт там страницу «Тестирование», нажмёт на ней кнопку редактирования, напечатает в страницу
Большинство потребностей покрывается языком «Селениума»,
Есть возможность удалённого запуска тестов на нескольких браузерах и использование языков C#, Perl, Ruby, Python, PHP и Java, но я пока с этим богатством не разбирался, первые потребности вполне покрываются тем, что я уже знаю, дальнейшее — дело будущего.
Добавлено 22 ноября 2009 13:40: Андрей Ларионов подсказал ещё один бесплатный продукт — «Tellurium», но я не вижу преимуществ перед «Селениумом», зато вижу недостатки, например, усложнённый (по сравнению с «Селениумом») язык описания тестов.
mod_rewrite: просмотр списка правил только один раз
У модуля mod_rewrite сервера «Апач» есть особенность, которая иногда портит много крови — после каждого переписывания URL, он просматривает список правил снова. Если ничего не предпринять, иногда происходит зацикливание, что ввергает новичков в ступор.
Мне эта особенность ни разу не пригодилась, но избавляться от неё приходится постоянно. В комментариях на «Хабре» к статье, название которой я вынес в заголовок, подсказали очень хороший способ, добавить первым правилом следующее:
# Don't loop.
RewriteCond %{ENV:REDIRECT_STATUS} !^$
RewriteRule .* - [L]Если перезапись URL уже происходила, то любой URL оставляем без изменений и следующего цикла обработки уже не происходит.
IE9
В блоге Microsoft, посвящённом их браузеру появилась статья об Internet Explorer 9. То, что есть сейчас уже очень неплохо — IE9 по скорости (если верить данным Microsoft) лишь
Internet Explorer 9.0 проходит тест селекторов CSS3 с результатом 574 из 578 (IE8 — 330/578), тест ACID3 — 32% (IE8 — 20%), поддерживает
Не могу поверить, что этот движок и есть «Газель» о которой говорили ранее, ведь «Газель» имела большие проблемы с производительностью, да и в статье из блога о безопасности и изоляции вкладок (основные фишки «Газели») ни слова.
P.S. Кстати, если покопаться, то можно найти как выглядит
Движок, как видим, всё ещё называется «Trident», хотя номер смущает — 4.0, как мы знаем, версия, которая входит в IE8.
Flash и безопасность
В качестве вялых воскресных препирательств, на «Хабре»
Между тем, правил для защиты от таких «уязвимостей» всего два: выдавать при обращении к заливаемым пользователем Flash заголовки для скачивания (а то и вообще отдавать все пользовательские ролики с другого домена, порта или протокола) и указывать правильные параметры для проигрываемых роликов, залитых пользователем. Последнее — нехитрая наука, таких параметров всего три: allowScriptAccess, allowNetworking и allowFullScreen.
Их назначение легко расшифровывается по названию.
Параметр allowScriptAccess (введен во Flash 6.0.40.0) управляет доступом к JavaScript/DOM браузера и имеет три возможных значения: always, sameDomain и never. Пояснения, на мой взгляд, требует только значение sameDomain. Если выбрано это значение, то доступ предоставляется только скриптам, которые загружены с того же домена, что и страница, куда они подключены.
Следующий параметр, allowNetworking (работает, начиная с Flash 9.0) разрешает или запрещает обращение Flash к собственным сетевым функциям и сетевым функциям браузера. Имеет значения all, internal (запрещает использование сетевых функций браузера) и none.
Параметр allowFullScreen (Flash 9 и выше) принимает два возможных значения — false (выставлено по умолчанию) или true, которые (соотвественно) запрещают или разрешают использование полноэкранного режима.
Очевидно, что наиболее безопасные параметры — allowScriptAccess="never", allowNetworking="none" и allowFullScreen="false":
<object height="400" width="500">
<param name="allowScriptAccess" value="never" />
<param name="allowNetworking" value="none" />
<param name="movie" value="ролик.swf" />
<embed type="application/x-shockwave-flash"
allowScriptAccess="never" allowNetworking="none" src="ролик.swf" height="400" width="500" />
</object>Очень жаль, но значение «never» для параметра для параметра «allowScriptAccess» сейчас является deprecated, так же как и использование параметра allowNetworking, когда страница, на которой подключен ролик и сам ролик загружаются с одного и того же домена.
Насколько я понимаю, это связано с тем, что фирма Adobe, текущий владелец технологии Flash, не хочет брать на себя ответственно за утечку критичных данных через Flash-ролики. Вместо использования этих значений параметров рекомендуется загружать Flash-ролики с другого домена и не допускать того, чтобы критичные данные и ролики были доступны с одного домена.
Локальные переменные и JS
Читал тут по диагонали стандарт ECMAScript и подумалось мне, что сэмулировать локальные переменные (да, я осознаю, что это свойства объекта) можно вот так:
var a = 10;
with ( { a : 20 }) {
alert(a);
}
alert(a);Ожидаемый результат: сначала код выведет «20», потом — «10». Я ещё не задумывался над тем насколько это практично и изящно, просто придумалось.
Кстати, обратите внимание, что следующий код выведет «30», «30», «10», «30»:
var a = 10, b = 10;
with ({a:20}) {
var a = 30, b = 30;
alert(a);
alert(b);
}
alert(a);
alert(b);Добавлено 10 ноября 10:55: в комментариях
Полезные rgba в IE
Вадим Макишвили описал несколько примеров применения rgba в Internet Explorer. Примеры очень и очень полезны, и я скопирую их (без картинок, за ними идите в исходный пост) к себе:
Сегодня, благодаря этому новому знанию, я понял как решить две труднорешаемые задачи. Издавна, чтобы сделать фон блока полупрозрачным, мы вынуждены были использовать
или png-24 картинку в фонеили opacity-свойство. Но в случае
с png-24, для ИЕ6 нужно писать AlphaImageLoader фильтр, который катастрофически замедляет рендеринг страницы. А в случае с opacity приходилось придумывать трюки, чтобы дети блока не бледнели от родительского opacity.А теперь проблему opacity можно забыть, как страшный сон: .block { background: #rgba } * html .block { filter:progid:DXImageTransform.Microsoft.gradient (startColorstr=#99000000, endColorstr=#99000000); }
В начальной и конечной позиции один и тот же RGB (000000) и добавляю перед ним одно и то же значение Alpha (99) Значение Alpha указывается
в 16-тиричной системе, где 00 — полностью прозрачный, а ff — непрозрачный пикселЯ протестировал скорость рендеринга страницы, на которой выливается 300 элементов
с gradient-фильтром — он не замедляет рендеринг и это сильно радует.Но этот случай описывает ситуацию, когда нужно сделать однотонный полупрозрачный цвет, как например в модальных диалогах, когда вся остальная страница затемняется.
Иногда использовать
png-24 всё-таки необходимо, как например в случае, когда нужно обрезать длинную неразрывную строку, уводя ее градиентом в непрозрачность.В этом случае можно использовать второй способ. .block { background: url(image.png) } * html .block { filter:progid:DXImageTransform.Microsoft.gradient (startColorstr=#00ffffff, endColorstr=#ffffffff, GradientType=1); }
GradientType принимает значение 1, если градиент рисуется
слева-направо; и 0, если градиентсверху-вниз. MSIE хороший браузер.
Компрессоры JavaScript и точка с запятой
Точка с запятой — один байт, перевод строки — один байт. Мне тут подумал, что лучше бы компрессоры JS выкидывали при сжатии не переводы строки, а точки с запятой (там где это возможно, конечно, например, пустой return в некоторых случаях нельзя завершить только переводом строки).
Тогда читаемость кода почти не снижалась бы (об обфускатарах я сейчас не говорю) и можно было бы легко понять где произошла ошибка, пропущенная в production (такое случается).
Сколько нужно людей, чтобы получился Chrome
Бобук рассказывает про количество людей, имеющих отношение к появлению Chrome:
Итого, представь себе что 10 миллионов долларов в акциях размазывается между разработчиками проекта. Такие небольшие проекты как Chrome в гугле затрагивают очень небольшую группу, скажем всего
500-600 человек разработчиков,50-100 дизайнеров,200-300 маркетологов, под сотню админов, сотня менеджеров. Итого на нос получается в пике 10 тысяч на лицо. Теперь подумай, что такое в Калифорнии 10 тысяч долларов _В АКЦИЯХ_, т. е. после реализации акций и уплаты налогов — у тебя на руках 5 тыщ долларов.
В ответ на вопрос откуда информация и по поводу численности:
Про команду разработки? Есть некотрые инсайдеры. Не, вообще команда разработки там примерно в 3 раза меньше, но есть же масса людей, которые учавствуют в проекте «парттайм», а доля будет такая же, как и у тех кто работал только над ним.
После недавней фразы Брина «мы ведем исследования небольшими группами
по 200-300 человек», я уже ничему не удивляюсь.
Почти тысяча человек даже в самом минимальном случае!
IE и rgba
Век живи, век учись. Оказывается, в Internet Explorer есть возможность, через фильтры, указывать
.element{
/*color = black, opacity = 0.6 */
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000,endColorstr=#99000000); /*AARRGGBB*/
zoom: 1
}Большая часть фильтров понимает цвет в формате argb (первая компонента — прозрачность).
«Консультант» и IE
Вчера на «Хабре» люди в очередной раз подняли тему сайта «Консультанта» и Internet Explorer — первый, своей интересной частью, работает только на втором. Как я и ожидал, код завязан на особенности Internet Explorer, браузер богатый на возможности, на сайте использовано их немало. Впрочем, всё, если вдуматься, переводится и на более стандартные рельсы.
У меня давно руки чесались разобраться поподробнее как устроены userjs в «Опере» и посмотреть на отличия реализации
По результатам появился userjs, который позволяет увидеть две первые страницы базы «Консультанта» в «Опере», если
Текст userjs помещаю под кат.
// ==Userscript==
// @name Make Consultant base works
// @namespace /
// @description Make Consultant base works in non-IE browsers.
// @include http://base.consultant.ru/*
// @version 1.0
// @licence MIT
// ==/UserScript==
(function(){
/* TODO
saveCard — это behavior userData, сделать эмуляцию
VBArray — одна из переменных использует этот специфический массив
isBaseVisible, ForEachRow — убрать заглушку, сделать нормальную функцию
showModalDialog — дописать с передачей параметров (например, через window.name)
*/
document.addEventListener('load', function (e) {
showModalDialog = window.open;
return true;
}, false);
DOMParser.prototype.loadXML = function (text) {
return this.parseFromString(text,"text/xml")
}
opera.addEventListener('BeforeScript',
function(e){
if (e.element.getAttribute('for')) {
var script = e.element.text
var id = e.element.getAttribute('for')
var ev = e.element.getAttribute('event')
e.element.text = ''
document.addEventListener('load', new Function('',
'document.getElementById("' + id +
'").attachEvent("' + ev +
'", function () {'+ script + '})'
))
}
var repl = {
'document\\.body\\.attachEvent' : 'document.attachEvent',
'document\\.all\\.fieldsXML\\.XMLDocument\\.transformNode': 'document.transformNode',
'document\\.all' : 'document.getElementsByTagName("*")',
'document\\.frames\\(' : 'document.getElementsByTagName("iframe").item(',
'\\.XMLDocument\\.text' : '.textContent',
'document\\.body' : 'document.documentElement',
'\\.parentElement' : '.parentNode',
'\\.XMLDocument\\.documentElement': '',
'\\.XMLDocument' : '',
'basesRow\\.all\\.basesTable' : 'document.getElementById("basesTable")',
'\\.all\\.tags\\(' : '.getElementsByTagName(',
'\\.all\\.([^\\.;\\s]+)' : '.getElementById("$1")',
'\\.all\\(' : '.getElementById(',
'\\w+\\.getElementById' : 'document.getElementById',
'p\\.type\\.' : 'p.getAttribute("type").',
'\\.isEnabled\\s*=([^;]+)' : '.setAttribute("isEnabled", $1)',
'\\.isEnabled' : '.getAttribute("isEnabled")',
'(\\w+)\\.loadXML' : '$1=$1.loadXML',
}
for (var re in repl) {
e.element.text = e.element.text.replace(new RegExp(re, 'g'), repl[re])
}
}
,false);
opera.defineMagicFunction('writeInsertion', function () {});
opera.defineMagicFunction('getBasesTable', function (a, b, tDivRow) {
var basesRow = tDivRow && tDivRow.id ? document.getElementById("bases_" + tDivRow.id) : null;
return basesRow == null ? null : document.getElementById('basesTable');
})
opera.defineMagicFunction('forEachRow', function (a, b) { return true })
opera.defineMagicFunction('createXMLStylesheet', function (a, b, url) {
var r = new XMLHttpRequest()
r.open('GET', url, false)
r.send(null)
if (r.status == 200) {
var xml = new DOMParser()
return xml.parseFromString(r.responseText, "text/xml")
}
return null
})
opera.defineMagicFunction('isBaseVisible', function (a, b, code) {
return true
})
HTMLElement.prototype.transformNode = XMLDocument.prototype.transformNode = function (style) {
var xsltProcessor=new XSLTProcessor();
xsltProcessor.importStylesheet(style);
if (typeof this == 'HTMLElement') {
var resultDocument = (new DOMParser()).parseFromString(this.innerHTML, 'text/xml');
} else {
var resultDocument = xsltProcessor.transformToDocument(this);
}
var xmls = new XMLSerializer();
return xmls.serializeToString(resultDocument);
}
HTMLHtmlElement.prototype.selectSingleNode = HTMLInputElement.prototype.selectSingleNode = function (path) {
var xpe = new XPathEvaluator();
var nsResolver = xpe.createNSResolver(this.ownerDocument == null ? this.documentElement : this.ownerDocument.documentElement);
var results = xpe.evaluate(path,this,nsResolver,XPathResult.FIRST_ORDERED_NODE_TYPE, null);
return results.singleNodeValue;
}
ActiveXObject = function (str) {
return new ({
'Microsoft.XMLDOM': DOMParser,
'Microsoft.XMLHTTP': XMLHttpRequest,
'Scripting.Dictionary': Array,
})[str]
}
})()
Кстати, «Опере» нужно не забыть сказать, чтобы она маскировалась под Internet Explorer (правая клавиша мыши, «Настройки сайта», закладка «Сеть»).
Обращаюсь к тем, кто читает RSS и трансляцию, текст userjs по ссылке на исходный пост.
Сжатие исходящего трафика в браузере
Артель BrowserPlus выпустила плагин к браузеру («Опера», как обычно, не поддерживается, Linux — тоже), который позволяет использовать в браузере TAR и алгоритм сжатия LZMA.
TAR позволяет собрать несколько файлов в один, LZMA — упаковать их. Таким образом, файлы (или другие данные) на сервер можно отсылать уже упакованными. Ценно для заливки больших хорошо сжимаемых (например, текстовых) файлов.
Вообще говоря, работа с TAR и LZMA не единственное, что плагин позволяет делать, там очень много интересных возможностей, лучше посмотреть на сайте производителя. Тенденция,
Я был бы рад увидеть браузерное хранилище от Google во всех браузерах, вместо зоопарка, который мы сейчас имеем, тем более, что этот зоопарк по возможностям сильно не дотягивает до того, что придумали в Google (по правде говоря, я был бы рад увидеть
99 бутылок: PowerShell
45. Windows Powershell.
Язык просто изучается, имеет понятную структуру и широкие возможности. Я посмотрел первую версию языка, но уже доступна вторая версия оболочки.
Программа, выводящая песню о пиве (beer.ps1)
# Написал Евгений Степанищев aka BOLK
function bottles([int]$beer) {
switch ($beer) {
0 { "no bottles" }
1 { "1 bottle" }
default { "$beer bottles" }
}
}
99..1 | ForEach-Object {
$bottles = (bottles $_) + ' of beer'
$bottles + ' on the wall, ' + $bottles + '.'
'Take one down and pass it around, ' + (bottles($_-1)) + " of beer on the wall.`n"
}
'No more bottles of beer on the wall, no more bottles of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.'