jQuery-UI themeselect widget published
I’ve recently published jQuery-UI themeselect widget allowing users to add friendly looking, themable dropdown to instant theme switch.
In oppose to standard jQuery-UI themeswitcher, this one looks exacly like the rest of the theme.
My ui-themeselect has been published on New BSD License making it freely available and the code is hosted at Google Code. If You are familiar with Mercurial, you can clone the repository using command below:
hg clone https://ui-themeselect.googlecode.com/hg/ ui-themeselect
I have ripped the code out from my own Django / Google App Engine applications development Javascript bootstrap, because I think that someone else might consider it useful.
As an example how can it look inside jQuery-UI themed interface, take a look at this screenshot taken from one of my apps:
Enjoy!
Prosty plugin jQuery: Dynamiczny favicon
Ten prosty wpis ma za zadanie przybliżyć Wam dwa tematy jednocześnie – jak stworzyć prosty plugin do jQuery oraz jak z poziomu JavaScriptu podmienić obrazek favicon dla strony. Zamierzam przedstawić plugin który właśnie taką czynność wykonuje
jQuery chyba nie trzeba przedstawiać. Jest to w mojej opinii najłatwiejsza w użyciu biblioteka czyniąca język JavaScript prostym i wygodnym w użyciu. Co więcej – można ją bardzo łatwo rozszerzać o dodatkową funkcjonalność za pomocą pluginów. Uważam, że warto wiedzieć jak taki plugin można sobie szybko przygotować.
jquery.favicon_replace.js wygląda następująco:
(function($) {
$.fn.favicon_replace = function(url) {
$('head link').each(function() {
if ($(this).attr('rel') == 'shortcut icon') {
$(this).remove();
}
});
$('<link rel="shortcut icon" href="'+url+'" />')
.appendTo('head');
};
})(jQuery);
Jak powyższy kod działa: tworzymy closure której jako parametr podajemy strukturę jQuery. Wewnątrz closury definiujemy funkcję, która sprawdza atrybut rel każdego taga typu link wewnątrz head porównując go do shortcut icon. Znaleziony w ten sposób element jest kasowany. Następnie tworzony jest nowy link typu shortcut icon, z nowym url’em. Urla podajemy funkcji jako parametr. Closura w postaci anonimowej funkcji jest wykonywana, a w efekcie jQuery zyskuje nową funkcję.
Zastosowanie tego plugina jest proste – oto przykład:
$(document).favicon_replace('/images/new-favicon.ico');
Warto napomnieć, że favicon wcale nie musi być ikoną. Możecie równie dobrze zaserwować przeglądarce animowanego gifa, nawet z rozszerzeniem ico, który zostanie automatycznie przeskalowany przez przeglądarkę i animowany na pasku adresu
Kontrolka button w jQuery UI
Ogólny trend rozwoju interfejsów aplikacji spycha je coraz bardziej w kierunku webowym. Zmieniają się dzięki temu developerzy i sposób postrzegania aplikacji. Lekkość aplikacji zaczyna być w cenie. Krowiaste applety Javy, przerośnięte Flashe zaczynają powoli ustępować lżejszym rozwiązaniom. Jednym z nich jest jQuery UI.
Czym jest jQuery UI
jQuery UI to mini-framework dla aplikacji po stronie klienta. Powstał on przy projekcie jQuery, a zespoły tych projektów są dość mocno zintegrowane – świadczy o tym m.in About jQuery, wspólna dokumentacja oraz tempo rozwoju. Zespół UI jest dodatkowo zrzeszony w Filament Group, pod szyldem którego publikuje całkiem często dość ciekawe artykuły bądź to opisując UI, Aria, bądź uzupełniając kontrolki o nowe i jednocześnie je omawiając. Na ich stronie nie znalazłem RSS’a, ale o nowościach zawsze wspominają na swoim Twitterowym profilu filamentgroup – którego kanał RSS sobie zasubskrybowałem w moim Google Readerze – rozwiązanie spisuje się dobrze
Za pomocą jQuery UI możemy przygotować graficzny interfejs naszej aplikacji webowej. Mamy do dyspozycji bibliotekę funkcji rozszerzających standardowe efekty o nowe, własne kontrolki, tematy graficzne (które możemy rozszerzać o własne) wraz z rewelacyjnie narysowanymi ikonkami, dostosowanymi do każdego z tematów. Żeby zobaczyć jak to wszystko wygląda w różnych standardowych tematach graficznych – przykładowy podgląd ThemeRoller.
Standardowa baza kontrolek (widget’ów) jest niewielka – kontrolka daty, okno dialogowe, progress bar, taby. Dodatkowych można się doszukać na SVN’ie w labsach albo poprzez wspomnianą wyżej stronę filamentgroup.
Wprowadzenie do jQuery UI w Twitterowym stylu
Do html’a naszej aplikacji załaczamy CSS z wybranego tematu jQuery UI oraz skrypty jQuery i jQuery UI – przykładowo:
<link href="theme/start/jquery-ui.css" type="text/css" rel="stylesheet"/>
<script src="js/jquery.js" type="text/javascript"></script>
<script src="js/jquery-ui.js" type="text/javascript"></script>
Mamy teraz dostępną całą paletę styli i narzędzi jQuery UI. Zarówno jedno jak i drugie jest bardzo łatwo rozszerzalne.
Jak to rozbudować?
Stworzymy sobie przycisk (ang. button) – przyda się nam omówiona w poprzednim wpisie biblioteka do generowania dynamicznego CSS. Tak mniej więcej wygląda początkowy szkielet aplikacji:
$(document).ready(function() {
var self = this;
self.css = new CSS('dynamic');
//include('button.js');
});
Jak wszyscy wiemy, w JavaScript nie ma include. Można zrobić tak aby był, ale o tym napiszę niebawem. Póki co w jego miejsce możemy wstawić kod właściwej kontrolki.
// ui.button
self.css.replace('.cx-button',{
'outline':'0',
'margin':'0 4px 0 0',
'padding':'.4em 1em',
'text-decoration':'none !important',
'cursor':'pointer',
'position':'relative',
'text-align':'center',
'zoom':'1'
});
// remove extra button width in IE
self.css.replace('a.cx-button',{
'float':'left'
});
(function($) {
$.widget('ui.button', {
_init: function() {
this.element
.addClass('cx-button ui-state-default ui-corner-all')
.hover(function() {
if (! $(this).hasClass('ui-state-disabled')) {
$(this).addClass('ui-state-hover');
}
},function() {
$(this).removeClass('ui-state-hover');
})
.mousedown(function() {
if (! $(this).hasClass('ui-state-disabled')) {
$(this).addClass('ui-state-active');
}
})
.mouseup(function() {
$(this).removeClass('ui-state-active');
})
.disableTextSelect();
},
disabled: function() {
this.element.addClass('ui-state-disabled');
},
enabled: function() {
this.element.removeClass('ui-state-disabled');
},
destroy: function() {
this.element
.removeClass('cx-button ui-state-default ui-corner-all ui-state-hover ui-state-active ui-state-disabled')
.unbind('mouseenter mouseleave');
}
});
})(jQuery);
Takie właśnie przyciski zostały przeze mnie użyte na przykładowej stronie flatforma.appspot.com – należy najpierw przekliknąć się w accordeonie po lewej, a wtedy pojawią się przyciski, które dodają nowe zakładki. Dla leniwych zrzut ekranu:

Zachęcam do analizy przedstawionego w tym tekscie kodu – można go uznać za bazowy szkielet do tworzenia kontrolek.
Styling Buttons and Toolbars with the jQuery UI CSS Framework
Dynamiczny CSS z poziomu JavaScript
Jak zapewne większość developerów używających JavaScriptu, mam swoją własną biblioteczkę skryptów oraz dodatkowych narzędzi która stanowi dla mnie formę bootstrapu do tworzenia nowej aplikacji. Z każdym projektem biblioteczka rośnie i staje się bardziej dojżała. Przymierzam się od jakiegoś czasu do opublikowania całego bootstrapu, bo uważam, że jest warty uwagi, ale jakoś nie miałem do tej pory zbytnio na to czasu.
W tym tekscie poniżej przedstawie jeden ze skryptów który należy do mojej biblioteczki. Załączam go standardowo do każdej swojej aplikacji, szczególnie teraz, kiedy zacząłem przygotowywać swoje własne rozszerzenia JQuery UI.
Swobodniejsze podejście
Bardzo lubie dbać o to, aby struktura aplikacji była zwięzła, łatwa do ogarnięcia oraz estetyczna. W dobie Javascriptu mamy coraz to większe możliwości aby samemu sobie ustrukturyzować aplikacje.
Wielu doświadczonych developerów zakłada, sztywny podział – czyli, że wygląd powinien być w plikach CSS, funkcjonalność w JavaScript, a dane na serwerze. Oczywiście z trzecim się zgadzam, ale z podziałem na wygląd i funkcjonalność – już nie koniecznie.
Przykładowo – w naszym pliku CSS wykorzystujemy -moz-border-radius i -webkit-border-radius, dzięki czemu mamy zaokrąglone rogi wszędzie, poza Internet Explorerem. Musimy dodać zaokrąglone rogi w Explorerze. Co począć? Nabrudzić sobie w CSSie? A później za pomocą JQuery sprawdzić wersję przeglądarki i ustawić te klasy? Można i tak. Możemy jednak sprawdzić czy mamy do czynienia z IE i jeśli tak – dopiero wtedy dodać nowe klasy CSS.
Dynamiczne ładowanie lub generowanie CSS nie jest czymś bez czego nie możnaby się obyć, ale dzięki warstwom abstrakcji możemy sobie nie śmiecić w plikach z layoutem.
Poniższy kod działa bez problemu w każdej zaawansowanej przeglądarce WWW oraz w Internet Explorerze
/* * CSS Gen * Copyright (c) 2009 nme.pl * Dual licensed under MIT and GPL. * * from scratch, based on article http://www.hunlock.com/blogs/Totally_Pwn_CSS_with_Javascript * * Example usage: * * var css = new CSS('dynamic'); // Create new stylesheet * css.replace('.asd',{'color':'green'}); // If there is no asd class definition yet, it will be created * */ var CSS = function(name,url) { if (name === undefined) { return false; } var styles = document.styleSheets; if (!styles) { return null; } this.find_style = function(name) { if (styles) { for (var i in styles) { if (styles[i].title === name) { return styles[i]; } } } return null; }; var create_style = function(name) { var node = document.createElement('style'); node.type = 'text/css'; node.rel = 'stylesheet'; node.media = 'screen'; node.title = name; document.getElementsByTagName(”head”)[0].appendChild(node); return node; }; var load_style = function(name,url) { var node = document.createElement('link'); node.type = 'text/css'; node.rel = 'stylesheet'; node.href = url; node.media = 'screen'; node.title = name; document.getElementsByTagName(”head”)[0].appendChild(node); return node; }; var style = this.find_style(name); if (style === null) { if (url === undefined) { style = create_style(name); } else { style = load_style(name,url); } } var sheet = null; if (style !== null) { if (style.styleSheet !== undefined) { sheet = style.styleSheet; } else { sheet = style.sheet; } } this.index = function(search) { search = search.toLowerCase(); var i=0; var rule=null; if (sheet === undefined) { return null; } do { if (sheet.cssRules) { rule = sheet.cssRules[i]; } else if (sheet.rules) { rule = sheet.rules[i]; } if (rule) { if (rule.selectorText.toLowerCase() == search) { return i; } i++; } } while (rule); return null; }; this.rule = function(search) { search = search.toLowerCase(); var i=0; var rule=null; if (sheet === undefined) { return null; } do { if (sheet.cssRules) { rule = sheet.cssRules[i]; } else if (sheet.rules) { rule = sheet.rules[i]; } if (rule) { if (rule.selectorText.toLowerCase() == search) { return rule; } i++; } } while (rule); return null; }; this.add = function(search, body) { if (sheet === undefined) { return null; } if (typeof body === 'object') { var rules=''; for (var i in body) { if (body.hasOwnProperty(i)) { rules = rules+i+':'+body[i]+';\n'; } } body = rules; } if (!this.rule(search)) { if (sheet.cssRules) { sheet.insertRule(search+' {'+body+'}', 0); } else if (sheet.rules) { sheet.addRule(search, body); } } return this.rule(search); }; this.remove = function(search) { var i = this.index(search); if (i !== null) { if (sheet.cssRules) { sheet.deleteRule(i); } else if (sheet.rules) { sheet.removeRule(i); } } }; this.replace = function(search, body) { this.remove(search); return this.add(search,body); }; };
Jak tego używać? Na początku przygotowujemy nowy, dynamiczny stylesheet:
var css = new CSS('dynamic');
Jeśli klasa asd nie została jeszcze zdefiniowana, robimy to teraz oraz ustawiamy jej następujące atrybuty:
css.replace('.asd',{'color':'green'});
Pseudoklasa CSS umożliwia również dynamiczne ładowanie CSS’ów czy usuwanie poszczególnych reguł styli. W razie wątpliwości – zachęcam do zaglądania do kodu oraz do kontaktu ze mną
Przykładową aplikacją, która dość intensywnie korzysta z tego fagmentu kodu jest http://mlchat.appspot.com – czat, który uruchomiłem na Google App Engine, który jednak zarzuciłem, bo nie znalazłem już sił po stworzeniu go na jego odpluskwianie i promocję
Wygląd aplikacji miał być z zamierzenia łudząco podobny do Google Web Toolkit‘u i co za tym idzie takich aplikacji jak Google Reader, ale tak naprawdę było to JQuery i trochę pracy
Zachęcam do poklikania – ludzi tam raczej nie ma, jak sugerowałyby liczby, to tylko duchy – stare sesje się nie kasują jak powinny

