Видео: Firefox developer tools
Скачать новый Firefox (beta): http://blog.mozilla.com/futurereleases/2011/12/23/firefox-beta-with-new-developer-tools-and-enhanced-sync-is-ready-for-testing/
Видео: Firefox developer tools
Скачать новый Firefox (beta): http://blog.mozilla.com/futurereleases/2011/12/23/firefox-beta-with-new-developer-tools-and-enhanced-sync-is-ready-for-testing/
http://pimpmyjs.com/ – Uglify or Beautify your JavaScript, it’s your choice… (minify js online)
На jquery.min.js выдал ошибку, но в общем работает, и причём быстро.
Основная идея – проверять перед сохранением данных значение navigator.onLine и слушать события online (для сохранения на сервере), offline (для сохранения в localStorage) и load (для загрузки несохранённых данных на сервер при загрузке страницы после подключения к сети).
Continue reading »
The Top 10 Javascript MVC Frameworks Reviewed
Лучшим признан Ember.js. Надо поковырять)
UPD: поковырял. В принципе, более стройная архитектура, чем у того же backbone, у которого часть логики контроллера во вью. Но из коробки нет router и не так просто с pushState, так что не стал использовать серьёзно. Возможно, через полгода-год этот фреймворк будет интереснее.
https://github.com/bartaz/impress.js.
Вот что получается: http://bartaz.github.com/impress.js/#/bored.
Кстати, есть и описание на хабре – http://habrahabr.ru/blogs/css/136505/
http://popcornjs.org/ – позволяет показывать видео и другой контент пользователю без использования flash.
Есть большой каталог примеров:
http://popcornjs.org/Demo/semantic-video, http://popcornjs.org/Demo/popcorn-remote
Использовали мы на проекте одну лебедевскую библиотеку – include.js. Умеет она интересные вещи, вроде последовательной загрузки зависимых библиотек в нужном порядке. Нужно в яваскрипте написать js.include( ‘my/coolwiidget’ );. Работает она, если на пальцах, следующим образом: делается ajax-запрос, которым грузится текст нужного скрипта, потом ищутся в тексте все js.include, делается их подгрузка, после чего делается eval полученных текстов в обратном порядке. При этом ещё и отсекаются попытки по несколько раз грузить один и тот же яваскрипт.
Работал он очень неплохо, хотя и неудобно было в firebug-е искать скрипт по его имени.
Основная проблема встала, когда стало нужно вынести все яваскрипты на отдельный домен. Вот тут-то и проявились проблемы – ajax-ом нельзя грузить код с других доменов. Помогает заголовок Access-Control-Allow-Origin, но его понимают только современные браузеры (к примеру, firefox с версии 3.5), а на проекте критична максимально возможная совместимость (как обычно и бывает). Так что этот способ может быть актуален года через 3-5.
Посмотрели на другие способы, в итоге пришли к динамическому добавлению тега script в head, но особого смысла в этом нет, т.к. зависимости, для которых и городился изначально огород, при этом способе не отслеживаются (JSONP с колбэками не подошёл, т.к. в проекте много legacy-кода, который использует глобальные переменные, причём даже не как константы).
В итоге было принято гениальное в своей простоте решение – отказаться от всех этих изысков нафик и грузить скрипты как раньше – при загрузке страницы.
Выбирал между backbone (habrahabr.ru/blogs/javascript/118782/) и knockout (http://habrahabr.ru/blogs/javascript/121926/).
Для выбора полезно почитать stackoverflow.com/questions/5112899/knockout-js-vs-backbone-js-vs.
В итоге выбрал knockout – менее монструозный и лично мне показался понятнее, к тому же не навязывает REST и не требует дополнительных библиотек.
Он реализует паттерн MVVM, который, думаю, больше подходит для клиентской части web-приложения, чем MVC.
На сайте есть подробные доки и очень удобный интерактивный туториал.
Пока в проектах не использовал, так что писать особо нечего(
Сегодня из-за очередных проблем с sol.adbureau.net было решено реализовать ленивую загрузку баннеров.
Начальное решение было использовать iframe, но идея была не очень удачной, т. к. могут быть проблемы с подсчётом кликов, да и модные картинки, увеличивающиеся при наведении, будут вести себя странно. А как этот iframe смотрится в разных браузерах – это вообще сказка)
В итоге родилась идея: при генерации страницы размещать в нужных местах, к примеру, пустые дивы с id с нужным префиксом (<div id=»js-advert-place-{num}»></div>), а сами баннеры загружать в футер вот в такую, к примеру, разметку:
<div id="js-all-advert-block" class="all-advert-block">
<div class="js-advert-block-content" data-number="1">
{баннер}
</div>
<div class="js-advert-block-content" data-number="5">
{баннер}
</div>
<div class="js-advert-block-content" data-number="15">
{баннер}
</div>
</div>
{здесь грузится счётчик показов}
После загрузки страницы размещаем все баннеры на нужных местах:
(function( $ ) {
function moveAdvert() {
$( '#js-all-advert-block' ).find( '.js-advert-block-content' ).each( function() {
var $oldContent = $( this );
// в ие перемещаем
if ( $.browser.msie ) {
$( "#js-advert-place-" + $oldContent.data( "number" ) ).append( $oldContent );
}
// в других браузерах убираем лишнее и вставляем заново
else
{
var cleanedContent = $oldContent.html().replace( /document\.write/gi, 'function a__(){}' );
$( "#js-advert-place-" + $oldContent.data( "number" ) ).html( cleanedContent );
$oldContent.remove();
}
// этот вариант менее хороший - глюки в ие и проблемы с яндекс.директ
// var $newContent = $.browser.msie ? $( $oldContent.html() ) : $oldContent.clone( true );
//// не надо заново создавать скрипты - они заново выполнятся, а там document.write
// $newContent.find( "script, link" ).each( function() {
// $( this ).remove();
// } );
//// вставляем
// $( "#js-advert-place-" + $oldContent.data( "number" ) ).html( $newContent );
//// на старом месте нужно убить всё, кроме скриптов
//// (в скриптах могут быть необходимые для правильного подсчёта кликов переменные)
// $oldContent.find( '*' ).each( function() {
// var $el = $( this );
// if ( ! $( 'script, link', $el ).length && this.tagName.toLowerCase() != 'script' && this.tagName.toLowerCase() != 'link' ) {
// $el.remove();
// }
// } );
} );
}
$( document ).ready( function() {
moveAdvert();
} );
}( jQuery ));
В итоге отвалившаяся внешняя баннерная площадка позволяет корректно отображать сайт без лишних задержек.
За рамками осталась работа сервер-сайда, где нужно собрать данные по всем отображаемым на странице баннерам, а потом вывести их все сразу внизу.
UP:
Всё-таки проблем оказалась куча. К примеру, opera 10.10 (хотя у меня 11.52) некорректно понимает тег script при .html( cleanedContent ), поэтому приходится чистить все теги script, но тогда умирает yandex.direct. К сожалению, на проекте нет нормальной возможности отделить директ от остальной рекламы (нет возможности объяснить тем людям, как что нужно настраивать), поэтому этот способ подходит с большими оговорками.
UP2: Наткнулся на гениальное решение – переопределить document.write, потом немного доработал и вот что получилось:
(function ( $ )
{
// для собирания тега script
var previousValue = '',
// для проверки, собрали ли уже полный тег script
reTestIfScript = new RegExp( '<\/scr' + 'ipt>$', 'i' ),
// чтобы найти id дива, куда вставлять код соли
reGetSolId = new RegExp( "sol\\.adbureau\\.net[^>]+aamsz=(\\d+x\\d+)", 'i' ),
// для проверки, что это относится к яндекс-директу
reYandexDirect = new RegExp( "an\\.yandex\\.ru", 'i' );
// заменяем стандартные методы
document.writeln = document.write = function(value)
{
// если последовательно пишут в документ для получения тега script (document.write( '<SCR' ); document.write( '<IPT src="...' );)
try {
$( value );
}
catch ( e )
{
var joinedValue = previousValue + value;
// сли собрали полный тег скрипт, то вставляем его, куда надо
if ( reTestIfScript.test( joinedValue ) )
{
value = joinedValue;
previousValue = '';
}
// если начали писать тег скрипт, продолжаем до сбора всего тега
else if ( joinedValue.toLowerCase().indexOf( '<sc' ) == 0 )
{
previousValue = joinedValue;
return;
}
// непонятно что это - ничего не делаем
else {
return;
}
}
var parentNodeId,
matches;
// если в строке есть упоминание яндекса - вставляем в яндекс
if ( reYandexDirect.test( value ) ) {
parentNodeId = 'adv_media';
}
// если в строке есть упоминание соли - ищем id и вставляем в нужный div
else if ( matches = value.match( reGetSolId ) ) {
parentNodeId = 'sol' + matches[1];
}
// скрипт вставляем только в нужный элемент
if ( $( value )[0].tagName == "SCRIPT" && parentNodeId )
{
var js = document.createElement( 'SCRIPT' );
var obj = document.getElementById( parentNodeId ).appendChild( js );
// подгрузка внешнего скрипта
if ( $( value ).attr( 'src' ) !== "undefined" ) {
$( obj ).attr( {type: 'text/javascript', 'src': $( value ).attr( "src" )} );
}
// выполнение скрипта
else
{
var reReplaceWrite = new RegExp( '(document\\.write[a-zA-Z]{0,2}\\([^)]+)(\\))', 'ig' );
eval( $( value ).text().replace( reReplaceWrite, "$`$1, '" + parentNodeId + "'$2$'" ) );
}
}
// стили ставим в head
else if ( $( value )[0].tagName == "LINK" )
{
var js = document.createElement( 'LINK' );
var obj = document.getElementsByTagName( 'head' )[0].appendChild( js );
$( obj ).attr( {rel: 'stylesheet', 'href': $( value ).attr( "href" ) , type: 'text/css'} );
}
else if ( $( value )[0].tagName == "STYLE" )
{
if ( $.browser.msie ) {
var css = document.createElement( 'STYLE' );
document.documentElement.firstChild.appendChild( css );
$( obj ).attr( {type: "text/css"} );
css.styleSheet.cssText = $( value ).html();
}
else {
$( value ).appendTo( $( "head" ) );
}
}
// любой другой элемент вставляем в DOM, только если поняли, в какой элемент вставлять
else if ( parentNodeId ) {
$( value ).appendTo( $( "#" + parentNodeId ) );
}
};
}( jQuery ));
Всё бы ничего с этим решением, и даже знает, как собирать тег script из кусочков, но всё-таки мы не можем знать, в какое место DOM должна была производится запись, так что там есть костыли с определением места по контенту, и это решение не универсально.