Ajax profesionálně

Page 1

E N C Y K LO P E D I E

W E B D E S I G N E R A

Programmer to ProgrammerTM

Profesionรกlnฤ

Ajax Nicholas C. Zakas, Jeremy McPeak, Joe Fawcett

www.zonerpress.cz


Ajax Profesionรกlnฤ

Nicholas C. Zakas, Jeremy McPeak, Joe Fawcett

uvod.indd 1

15.10.2007 14:05:45


Professional Ajax Nicholas C. Zakas, Jeremy McPeak, Joe Fawcett Published by Wiley Publishing, Inc., 10475 Crosspoint Boulevard, Indianapolis, IN 46256, www.wiley.com. Copyright © 2007 by Wiley Publishing, Inc., Indianapolis, Indiana. © Translation: ZONER software, s.r.o., 2007. All Rights Reserved. This translation published under license with the original publisher John Wiley & Sons, Inc. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from Wiley Publishing, Inc. Všechna práva vyhrazena. Tento překlad je vydán na základě licenční smlouvy s John Wiley & Sons, Inc. Žádná část této publikace nesmí být reprodukována nebo předávána žádnou formou nebo způsobem, elektronicky ani mechanicky, včetně fotokopií, natáčení ani žádnými jinými systémy pro ukládání bez výslovného svolení Wiley Publishing, Inc.

Ajax Profesionálně Autoři: Nicholas C. Zakas, Jeremy McPeak, Joe Fawcett Copyright © ZONER software, s.r.o. Vydání první v roce 2007. Všechna práva vyhrazena. Zoner Press Katalogové číslo: ZR712 ZONER software, s.r.o. Nové sady 18, 602 00 Brno Překlad: Jiří Koutný Odpovědný redaktor: Miroslav Kučera Šéfredaktor: Ing. Pavel Kristián DTP: Miroslav Kučera Trademarks: Wiley, the Wiley logo, Wrox, the Wrox logo, Programmer to Programmer, and related trade dress are trademarks or registered trademarks of John Wiley & Sons, Inc. and/or its affiliates, in the United States and other countries, and may not be used without written permission. All other trademarks are the property of their respective owners. Wiley Publishing, Inc., is not associated with any product or vendor mentioned in this book. The Wrox Brand trade dress is a trademark of John Wiley & Sons, Inc. in the United States and/or other countries. Used by permission. Ochranné známky: Wiley, logo Wiley, Wrox, logu Wroxu a souvisejicí obchodní známky jsou ochrannými známkami nebo registrovanými ochrannými známkami vydavatelství John Wiley & Sons, Inc, nebo jejich poboček, ve Spojených státech a/ nebo ostatních zemích a nemohou být používány bez písemného oprávnění. Všechny ostatní ochranné známky jsou majetkem jejich vlastníků. Wiley Publishing, Inc. není propojen s jakýmkoliv produktem nebo výrobcem zmíněným v této knize. Obchodní značka Wrox je ochrannou známkou John Wiley & Sons, Inc. ve Spojených státech a/nebo ostatních zemích. Veškeré dotazy týkající se distribuce směřujte na: Zoner Press ZONER software, s.r.o. Nové sady 18, 602 00 Brno tel.: 532 190 883, fax: 543 257 245 e-mail: knihy@zoner.cz http://www.zonerpress.cz

ISBN 978-80-86815-77-0

uvod.indd 2

15.10.2007 14:06:03


Mé matce, otci, Gregovi, Yiayii a zbytku mé rodiny a přátel, kteří mě podporovali při cestování z místa na místo. – Nicholas C. Zakas

Mé životní lásce Starle. Děkuji ti za tvou lásku, trpělivost a povzbuzení. – Jeremy McPeak

Mým rodičům, Sheile a Williamovi, kteří mi vštípili lásku ke čtení. Děkuji! – Joe Fawcett

uvod.indd 3

15.10.2007 14:06:03


4

O autorech Nicholas C. Zakas získal titul BS v oboru informační technologie na Merrimack College a titul MBA na Endicott College. Je autorem knihy Professional JavaScript for Web Developers (Wiley 2005) a také několika internetových článků. Nicholas je předním inženýrem ve společnosti Yahoo! a vývoji webu se věnuje více než 6 let. Podílel na vývoji webových řešení pro největší společnosti na světě. Nicholase můžete zkontaktovat prostřednictvím jeho stránek www.nczonline.net. Jeremy McPeak začal s vývojem webu jako se svým koníčkem v roce 1998. Nyní pracuje v IT oddělení ve školství. Jeremy má zkušenosti s vývojem webových řešení pomocí JavaScriptu, PHP a C#. Napsal několik internetových článků na téma XSLT, WebForms a C#. Je spoluautorem knihy Beginning JavaScript, 3rd Edition (Wiley 2007). Můžete jej zkontaktovat prostřednictvím jeho stránek www.wdonline.com. Joe Fawcett začal s programováním v 70. letech a po studiích začal pracovat v IT. Vystřídal spoustu zaměstnání, aby se v roce 1994 opět vrátil k vývoji software. V roce 2003 mu byl udělen titul Microsoft Most Valuable Professional v oblasti XML. V současné době se zabývá systémovou integrací a pracuje jako vývojář pro FTC Kaplan v Londýně, což je přední mezinárodní poskytovatel účetního a obchodního školení.

Poděkování Vydání knihy, jako je tato, vyžaduje práci mnoha lidí, a proto bychom jim zde chtěli poděkovat za jejich příspěvky. Ze všeho nejdříve patří dík všem lidem ve vydavatelství Wiley za jejich podporu: Jimovi Minatelimu za opětovné nastartování prací na druhém vydání této knihy. Kevinu Kentovi za připomínky a korektury na poslední chvíli (a za zábavu po celou dobu naší práce). Poděkování také patří našemu technickému editorovi, který je Alexej Gorkov. Na závěr patří velké poděkování těm, kteří nám poskytli zpětnou vazbu ještě před samotným vydáním knihy. Patří sem Peter Frueh, Adam Moore, Jenny Han, Matt Sweeney, Tyson Guskiken, Steve Carlson, a hlavně Hedger Wang, který na žádost vedení navrhl přidat do knihy jednu kapitoly.

uvod.indd 4

15.10.2007 14:06:03


5

Obsah Úvod Komu je tato kniha určena

15

O čem tato kniha je

16

Struktura knihy

16

Co potřebujete k používání této knihy

18

Konvence

18

Sdělte nám svůj názor

18

Zdrojové kódy

19

Kapitola 1

uvod.indd 5

15

Co je Ajax?

21

Zrození Ajaxu

21

Evoluce webu

22

JavaScript

22

Rámce

23

Technika neviditelných rámců

23

Dynamické HTML a DOM

23

Plovoucí rámce

24

XMLHttp

24

Skutečný Ajax

25

Principy Ajaxu

26

Technologie v pozadí Ajaxu

27

Kdo používá Ajax?

28

Google Suggest

28

Gmail

29

Google Maps

30

A9

31

Yahoo! News

31

Blog Liip

32

Zmatky a spory

33

Ajax a Web 2.0

34

Shrnutí

34

15.10.2007 14:06:03


6 Kapitola 2

Základy Ajaxu

Základy HTTP

37

HTTP požadavek

37

HTTP odpověď

40

Techniky komunikace pro Ajax

41

Technika neviditelných rámců

41

Požadavek XMLHttp (XHR)

59

Ajax a obrázky

72

Dynamické načítání skriptu

82

Řízení cache

86

Shrnutí

87

Kapitola 3

Ajaxové vzory

Vzory pro řízení komunikace

89 89

Předpovídání přenosu (predictive fetch)

90

Příklad – přednačtení stránky

90

Průběžné odesílání (submission throttling)

98

Příklad – průběžné ověřování dat ve formuláři

100

Příklad – postupná validace pole formuláře

107

Periodické obnovení (periodic refresh)

110

Příklad – oznamování nových komentářů

111

Vícestupňové stahování (multi-stage download)

116

Příklad – dodatečné zobrazení odkazů

117

Vzory pro výskyt problémů

120

Vzor pro zrušení nevyřízených požadavků

120

Vzor pro zopakování požadavku

122

Shrnutí

Kapitola 4

124

Knihovny pro Ajax

Knihovna Yahoo! Connection Manager

uvod.indd 6

37

125 125

Instalace

125

Základní požadavky

126

Objekt zpětného volání

126

15.10.2007 14:06:03


7 Sledování a správa požadavků

130

Interakce formuláře

131

Upload souborů

131

Příklad s GET

132

Příklad s POST

134

Dodatečné vlastnosti

135

Omezení

135

Knihovna Prototype Objekt Ajax.Request

136

Objekt pro nastavení

136

Příklad s GET

140

Příklad s POST

141

Objekt Ajax.Updater

141

Objekt Ajax.Responders

144

Výhody a nevýhody

145

Knihovna jQuery

146

Jednoduchý výraz jQuery

146

Vykonání požadavku GET

147

Příklad s GET

148

Metoda $.post()

149

Příklad s POST

149

Metoda load()

151

Metoda $.ajax()

152

Metody ajaxStart() a ajaxStop()

153

Omezení

154

Shrnutí

Kapitola 5

uvod.indd 7

136

154

Správa požadavků

155

Prioritní fronty

155

Objekt RequestManager

160

Objekty pro popis požadavků

160

Řazení požadavků do front

161

Posílání požadavků

163

Zrušení požadavků

168

15.10.2007 14:06:03


8 Problém se staršími položkami

169

Ajaxové vzory pro ovládání

171

Použití objektu RequestManager

174

Shrnutí

177

Kapitola 6

XML, XPath a XSLT

Podpora XML v prohlížečích

179

XML DOM v IE

179

XML v ostatních prohlížečích

190

XML napříč webovými prohlížeči

193

Jednoduchý příklad XML

194

Podpora XPath v prohlížečích

201

Představení XPath

201

XPath v IE

203

Práce se jmennými prostory

204

XPath v ostatních prohlížečích

207

Práce s rozkladačem jmenného prostoru

208

XPath napříč prohlížeči

210

Podpora XSL transformací v prohlížečích

211

Úvod do XSLT

211

XSLT v Internet Exploreru

214

XSLT v ostatních prohlížečích

219

XSLT napříč prohlížeči

220

Úprava příkladu pro nejprodávanější položky

221

Shrnutí

Kapitola 7

224

RSS a Atom

RSS

225 225

RSS 0.91

226

RSS 1.0

227

RSS 2.0

228

Atom

229

XParser

230

Jmenný prostor xparser

uvod.indd 8

179

230

15.10.2007 14:06:03


9 Získávání dat

230

Abstraktní třídy

231

Vytvoření proužku se zprávami Komponenta na straně serveru

244

Komponenta na straně klienta

245

Stylování zpráv

255

Použití proužku se zprávami

257

Webové hledání s RSS

258

Komponenta na straně serveru

259

Komponenta na straně klienta

260

Přizpůsobení vzhledu

266

Použití webového vyhledávání na stránce

269

Shrnutí

Kapitola 8

270

JSON

Co je JSON?

271 271

Literály pole

271

Literály objektu

272

Smíšené literály

273

Syntaxe JSON

274

JSON kódování/dekódování

275

JSON versus XML

276

Nástroje JSON na straně serveru

277

JSON-PHP

278

Další nástroje

280

Vytvoření textového pole s napovídáním

uvod.indd 9

244

280

Přehled funkcionalit

281

HTML kód

281

Databázová tabulka

284

Architektura

284

Třídy

285

Třída AutoSuggest

286

Poskytovatel návrhů

303

Komponenta na straně serveru

305

15.10.2007 14:06:03


10 Komponenta na straně klienta Shrnutí

Kapitola 9

308

COMET

HTTP streaming

309 310

Zpoždění požadavků

310

Příklad – modifikace souboru

312

Použití rámců

314

Přístupy specifické pro prohlížeč

319

Události DOM posílané serverem

328

Správa spojení

334

Podpora na straně serveru

334

Shrnutí

335

Kapitola 10

Práce s API pro mapy

337

Vzestup mashups

338

Geokódování

338

Stránky pro geokódování

339

Služby pro geokódování

339

API Google Maps

uvod.indd 10

306

340

Jak API pracuje?

340

Začínáme

340

Základy Google Maps

341

Ovládání

343

Posouvání mapy

344

Informační okna (bubliny)

345

Události

351

Překrytí mapy

352

Další informace

361

API Yahoo! Maps

362

Začínáme

362

Základy Yahoo! Maps

363

Ovládací prvky

364

Posun mapy

366

15.10.2007 14:06:03


11 Chytrá okna

366

Události

367

Překrytí mapy

369

Vyhledávání adres

375

Další informace

375

Další API pro mapy

376

Shrnutí

376

Kapitola 11

Nástroje pro ladění Ajaxu

Problém

377

FireBug

378

Instalace a nastavení

378

Rozhraní

379

Protokolování objektu XHR

380

Ladění Ajaxu pomocí FireBugu

382

Omezení FireBugu

382

Microsoft Fiddler

382

Instalace a nastavení

383

Rozhraní

384

Body přerušení HTTP

387

Ladění Ajaxu pomocí Fiddleru

388

Shrnutí

Kapitola 12

389

Widgety pro webové stránky

Vytvoření widgetu s počasím

391 391

SDK Weather.com

391

Komponenta na straně serveru

392

Komponenta na straně klienta

402

Získávání dat ze serveru

402

Přizpůsobení widgetu

403

Widget s počasím jako aplikace

408

Vložení widgetu s počasím do stránky

411

Widget pro sledování akcií Získávání finančních informací z Yahoo!

uvod.indd 11

377

412 412

15.10.2007 14:06:03


12 Ceny akcií

413

Komponenta na straně klienta: třída AjaxStockWatcher

418

Stylování widgetu

426

Používání widgetu s cenami akcií

428

Widget pro vyhledávání Komponenta na straně serveru

431

Komponenta na straně klienta

440

Stylování widgetu pro vyhledávání

447

Přidání widgetu ke stránce Shrnutí

Kapitola 13

448 450

Pracovní rámce pro Ajax

JPSpan

451 451

Použití JPSpan

452

Příklad použití JPSpan

457

JPSpan – shrnutí

460

DWR

461

Používání DWR

461

Příklad použití DWR

464

Více informací o souboru dwr.xml

469

Shrnutí DWR

473

Ajax.NET Professional

473

Použití Ajax.NET Professional

473

Konverze typů

476

Přístup k relaci

477

Příklad použití Ajax.NET Professional

477

Shrnutí Ajax.NET Professional

483

Shrnutí

Kapitola 14

uvod.indd 12

430

483

ASP.NET AJAX Extensions (Atlas)

485

Požadavky a nastavení

485

Klientské komponenty

486

Přístup ke klientským nástrojům v ASP.NET

486

Přístup ke klientským nástrojům bez ASP.NET

488

15.10.2007 14:06:04


13 Používání tříd

488

Psaní kódu s ASP.NET AJAX Library

489

Používání ovládacích prvků

495

Vytváření HTTP požadavků

499

Ovládací prvek UpdatePanel Přidání UpdatePanel do stránky

504

Přidání obsahu do UpdatePanel

505

Spouštění aktualizace

506

Dokončení

507

Přepracovaný příklad pro vyhledávání

508

Uživatelské rozhraní

509

Začínáme

509

Deklarace formuláře

510

Vyhledání

513

Mazání výsledků

518

Zpracování chyb

518

Nastavení ovladačů událostí

518

Shrnutí

Kapitola 15

521

Případová studie: FooReader.NET

Komponenty na straně klienta

523 524

Uživatelské rozhraní

524

Stylování rozhraní

527

Řízení UI

534

Komponenty na straně serveru

549

Možné vzory

549

Implementace

550

Nastavení a testování

562

Shrnutí

563

Kapitola 16

Případová studie: AjaxMail

565

Požadavky

565

Architektura

566

Použité zdroje

uvod.indd 13

504

566

15.10.2007 14:06:04


14 Databázové tabulky

567

Konfigurační soubor

568

Třída AjaxMailbox

570

Vykonávání akcí

592

Uživatelské rozhraní

597

Pohled na adresář

601

Pohled na zprávu

604

Pohled na vytvoření nové zprávy

605

Layout

607

Spojení všeho dohromady Pomocné funkce

610

E-mailová schránka

611

Funkce zpětného volání

632

Ovladače událostí

633

Poslední krok

634

Shrnutí

634

Příloha A

Licence pro knihovny a pracovní rámce

635

Ajax.NET Professional

635

DWR

636

GNU General Public License

639

Preamble

640

Terms and Conditions for Copying, Distribution and Modification

640

NO WARRANTY

644

JPSpan

645

jQuery

646

JSON-PHP

646

Prototype

646

YUI Library

647

Rejstřík

uvod.indd 14

608

649

15.10.2007 14:06:04


Úvod S nedávnými pokroky v oblasti JavaScriptu jsou vývojáři schopni dosáhnout nevídaných uživatelských prožitků při práci s webovými aplikacemi. Webu od dob jeho vzniku dominovala architektura typu "klikni-a-čekej". Ovšem díky existenci Ajaxu mohou vývojáři webu poskytnout funkcionalitu, která byla dříve dostupná pouze v klasických desktopových aplikacích. Ajax je obecný termín a vztahuje se k použití asynchronních požadavků HTTP, které byly vytvořeny JavaScriptem za účelem získávání informací od serveru bez opětovného načítání stránky. Tyto požadavky mohou být vykonány mnoha různými způsoby a různými typy datových přenosů. Spojení tohoto způsobu získávání vzdálených dat s interaktivitou objektového modelu dokumentu (DOM, Document Object Model) vedlo ke vzniku nové generace webových aplikací, které překračují všechny tradiční možnosti webu. Velké společnosti jako Google, Yahoo! či Microsoft se tak dnes zaměřují na tvorbu webových aplikací, které se chovají a vypadají podobně jako klasické desktopové aplikace. Tato kniha se zabývá různými aspekty Ajaxu – nejenom popisem různých možností pro vytvoření požadavku HTTP na server, ale také popisem různých formátů přenos dat zpět ke klientovi. Pomocí této knihy ovládnete různé techniky Ajaxu a vzory komunikace klient-server, které se v současnosti používají na webových stránkách a webových aplikacích.

Komu je tato kniha určena Tato kniha je určena dvěma skupinám čtenářů:

Vývojářům webových aplikací, kteří se snaží zvýšit jejich použitelnost.

Pokročilým vývojářům v JavaScriptu, kteří chtějí více porozumět tomuto jazyku.

Rovněž i znalost následujících technologií je velmi důležitým ukazatelem toho, že tato kniha je určena právě vám:

XML.

XSLT.

Webové služby.

PHP.

C#.

HTML.

CSS.

Hned na začátku vám musíme říci, že tato kniha není určena na začátečníkům, kteří nemají alespoň základní znalosti výše zmíněných technologií. A dále – abyste dokonale porozuměli obsahu této knihy, je velmi důležité, abyste dobře ovládali JavaScript. Pokud vám tyto znalosti chybí, udě-

uvod.indd 15

15.10.2007 14:06:04


16 láte mnohem lépe, když se prvně podíváte na nějaké knihy, které vás podrobně uvedou do problematiky programování v JavaScriptu.

O čem tato kniha je Kniha Ajax Profesionálně poskytuje všechny informace, které jsou nezbytné pro webové vývojáře, kteří chtějí programovat webové aplikace pomocí Ajaxu. Tato kniha tak popisuje různé ajaxové techniky, vzory a praktické případy použití. Kniha začíná zkoumáním kořenů Ajaxu a popisem evoluce webu a nových technologií, které vedly k vývoji Ajaxu. Jsou zde také obsaženy podrobné informace o tom, jak Ajax souvisí s rámci, JavaScriptem, cookies, XML a požadavky XMLHttp. Po tomto úvodu se téma knihy přesouvá k implementaci specifických technik Ajaxu. Různé možnosti pro vytváření požadavků na server – skryté rámce, dynamické plovoucí rámce, XHR – jsou vzájemně porovnávány a stavěny proti sobě. Je také vysvětleno, jak může být jedna metoda zkombinována s metodou jinou. Pro větší srozumitelnost textu jsou v této knize zahrnuty i stručné informace o požadavcích a odpovědích HTTP. V tomto okamžiku jsou probrány základní informace o různých typech požadavků, takže téma knihy se přesouvá k poskytnutí pokročilejších příkladů, které demonstrují použití Ajaxu na webových stránkách nebo webových aplikacích. Jsou detailně popsány výhody a nevýhody různých formátů pro přenos dat (kam například patří čistý text, HTML, XML a JSON). Je také obsažena diskuse o webových službách a o tom, jak mohou být využity pro předvedení schopností Ajaxu. Dále jsou již zmiňována poněkud složitější témata. Kapitola popisuje pracovní rámec pro vyřizování požadavků a ukazuje, jak v ajaxové aplikaci spravovat všechny typy požadavků. Jsou zde zmíněny nejenom techniky pro ladění kódu Ajaxu, ale také oblíbené debuggery FireBug a Fiddler. Poslední část knihy se pak zaobírá tvorbou plnohodnotných webových aplikací pomocí Ajaxu. První aplikace – FooReader.NET – je čtečka RSS, která je založena na Ajaxu. Druhá aplikace – AjaxMail – je e-mailový systém postavený na Ajaxu. Obě tyto aplikace demonstrují praktické využití mnoha technik popisovaných v této knize.

Struktura knihy Text knihy začíná popisem vzniku Ajaxu, aby se následně přesunul k popisu současných implementací této technologie. Dále jsou probrány různé možnosti pro zajištění komunikace mezi klientem a serverem, což je téma zbývající části knihy. Knihu doporučujeme číst od začátku, protože každá další kapitola předpokládá znalost informací uvedených v předchozích kapitolách. Stručný obsah kapitol: 1. Co je Ajax? Vysvětluje počátky Ajaxu, související technologie a původ termínu Ajax. Popisuje také vliv vývoje webu na vývoj Ajaxu a kdo – pokud ovšem vůbec někdo – si může připisovat nárok na vlastnictví tohoto termínu a souvisejících technik.

uvod.indd 16

15.10.2007 14:06:04


17 2. Základy Ajaxu. Uvádí různé postupy pro zajištění ajaxové komunikace (včetně skrytých rámců a XHR). Jsou zde podrobně uvedeny i výhody a nevýhody každého přístupu. Samozřejmě nechybí doporučení, kdy je vhodné dané přístupy použít. 3. Ajaxové vzory. Zaměřuje se na návrhové vzory, které můžete použít pro Ajax. Existuje totiž spousta možností, jak začlenit Ajax do webových stránek a webových aplikací. A zde popisované návrhové vzory vám s tímto úkolem mohou velmi pomoci, protože obsahují ty nejlepší postupy pro začlenění Ajaxu. 4. Knihovny pro Ajax. Zabývá se třemi oblíbenými ajaxovými knihovnami: Yahoo! Connection Manager, Prototype a jQuery. V této kapitole jsou porovnány odlišné přístupy těchto knihoven, přičemž dříve uvedené příklady jsou přepsány s jejich využitím. 5. Správa požadavků. Věnuje se správě požadavků XHR v ajaxových aplikacích s ohledem na různá omezení prohlížečů. V souvislosti s ajaxovými vzory popisovanými v kapitole 3 je zde rozebrána metodologie tvorby prioritního systému. 6. XML, XPath a XSLT. Popisuje XML, XPath a XSLT jako doplňkové technologie pro Ajax. Soustředí se na použití XML jako ideálního formátu pro přenos dat a použití XPath/XSLT pro přístup k informacím a k jejich zobrazení. 7. RSS a Atom. Popisuje použití Ajaxu společně s datovými formáty RSS a Atom pro vytvoření widgetu, jehož úkolem je získávání nových zpráv. V této kapitole jsou využívány techniky, které byly popisovány v předchozích kapitolách knihy. 8. JSON. Představuje JSON (JavaScript Object Notation) jako užitečnou alternativu k obvyklým formátům určeným pro přenos dat v ajaxové komunikaci. V této kapitole jsou dále zmíněny různé výhody a nevýhody použití XML a čistého textu. 9. Comet. Popisuje vznik architektury nazvané jako Comet. Je zde probráno několik různých technik pro implementaci této architektury v závislosti na možnostech prohlížečů. 10. Práce s API pro mapy. Detailně popisuje dvě API pro mapy v Ajaxu – API Google Maps a API Yahoo! Maps. Každé z těchto API je podrobně prozkoumáno z hlediska možností a omezení pro použití v oblasti geokódování. 11. Nástroje pro ladění Ajaxu. Tato kapitola se zabývá různými metodami pro ladění požadavků posílaných serveru. Jsou zde představeny možnosti pro ladění HTTP požadavků prostřednictvím rozšíření FireBug pro Firefox a nástroje Fiddler pro Internet Explorer. 12. Widgety pro webové stránky. Představuje některé techniky z předchozích kapitol, které jsou zaměřeny na tvorbu ajaxových widgetů, a které mohou být vloženy do vašich stránek. 13. Pracovní rámce pro Ajax. Zabývá se třemi pracovní rámci pro Ajax – JPSPan pro PHP, DWR pro Javu/JSP a Ajax.NET pro ASP.NET. Každý z těchto pracovních rámců se snaží o automatizaci některého vývojového procesu v Ajaxu. 14. ASP.NET AJAX Extensions (Atlas). Popisuje pracovní rámec ASP.NET AJAX Extensions (dříve známý jako Atlas) a jeho přínos pro zjednodušení tvorby ajaxových aplikací. Tento pracovní rámec předpokládá, že je nainstalován .NET 2.0 pro kód na straně serveru.

uvod.indd 17

15.10.2007 14:06:04


18 15. Případová studie: FooReader.NET. Popisuje vytvoření agregátoru zpráv RSS. Tato aplikace ilustruje nejenom využití proxy na straně serveru, ale také použití XML v JavaScriptu. 16. Případová studie: AjaxMail. Tato kapitola vás provede procesem vývoje kompletní webové aplikace, která byla pojmenována jako AjaxMail. Jedná se o e-mailový systém založený na Ajaxu. V této poslední šestnácté kapitole je použito velké množství technik, které byly popisovány na předchozích stránkách této knihy.

Co potřebujete k používání této knihy Pro spuštění příkladů v knize budete potřebovat následující:

Windows 2000, Windows Server 2003, Windows XP nebo Mac OS X.

Internet Explorer 5.5 nebo novější (Windows), Firefox 1.5 nebo novější (všechny platformy), Opera 9.0 nebo novější (všechny platformy) nebo Safari 2.0 či novější (Max OS X).

Kompletní zdrojové kódy všech příkladů jsou ke stažení na stránce www.zonerpress.cz.

Konvence Abychom vám pomohli odnést si z textu knihy co nejvíce a usnadnili vám sledování, co se právě děje, používáme v této knize několik následujících konvencí. Tyto šedé rámečky souvisí s okolním textem a obsahují důležité informace, které byste neměli zapomenout. V těchto rámečcích jsou rovněž i případné tipy, rady a odbočky od tématu.

Konvence pro styly použité v textu jsou následující:

Klávesové zkratky sázíme takto: Ctrl+A.

Názvy souborů, URL a zdrojové kódy v textu sázíme tímto způsobem: persistence.properties.

Pro zobrazení výpisů kódů používáme dvě možnosti: Nový a důležitý kód je zvýrazněn tučným řezem. Tučný řez není použit pro kód, který má v aktuálním kontextu nižší důležitost nebo již byl v knize uveden dříve.

Sdělte nám svůj názor Jako čtenáři této knihy se stáváte těmi nejdůležitějšími kritiky a komentátory. Vážíme si vašeho názoru a chtěli bychom vědět, co děláme správně, co bychom mohli dělat lépe, ve kterých oblas-

uvod.indd 18

15.10.2007 14:06:04


19 tech bychom měli publikovat a také vaše další podnětné myšlenky, o které jste ochotni se s námi podělit. Jako odborný redaktor Zoner Press vítám vaše názory. Můžete mi psát – poslat e-mail nebo dopis – a sdělit mi, co se vám v této knize líbilo nebo nelíbilo, stejně tak, co bychom měli udělat, aby naše další knihy byly lepší. Pokud mi napíšete, nezapomeňte prosím připojit název knihy, ISBN, jméno autora, vaše jméno, telefon, fax nebo e-mail. Pozorně zhodnotím vaše názory a poskytnu je autorovi a redaktorům, kteří pracovali na této knize. Prosím, vězte, že nemohu pomoci s technickými problémy, které se týkají obsahu knihy, a že díky velkému množství e-mailů, které dostávám, nemohu zaručit odpověď na každou zprávu. E-mail: miroslav.kucera@zoner.cz nebo knihy@zoner.cz. Adresa: Zoner Press, ZONER software, s.r.o., Miroslav Kučera, Nové sady 18, 602 00 Brno.

Zdrojové kódy Zdrojové soubory k této knize pro jednotlivé programovací jazyky je možné stáhnout z níže uvedených adres.

Zdrojové kódy pro ASP.NET (3.6 MB). http://www.zonerpress.cz/download/ajax-profesionalne-asp-net.zip

Zdrojové kódy pro PHP (3.3 MB). http://www.zonerpress.cz/download/ajax-profesionalne-php.zip

Zdrojové kódy pro JSP (3.3 MB). http://www.zonerpress.cz/download/ajax-profesionalne-jsp.zip

uvod.indd 19

15.10.2007 14:06:04


KAPITOLA 2 Základy Ajaxu Hnací silou Ajaxu je interakce mezi klientem (webovým prohlížečem) a serverem. Dříve této komunikaci rozuměli jen ti, kteří vyvíjeli aplikace v jazycích Perl a C na straně serveru. Novější technologie, jako jsou ASP.NET, PHP a JSP, sice softwarovým inženýrům poskytují kombinaci různých technik pro obě strany, ale často jim chybí plná podpora různým technologiím na straně klienta (například JavaScriptu). Nyní se kyvadlo zhouplo opačným směrem a vývojáři na klientské straně potřebují více porozumět technologiím na straně serveru, aby mohli vytvářet ajaxová řešení.

Základy HTTP Základem pro pochopení technik Ajaxu je pochopení principů hypertextového přenosového protokolu (HTTP), což je protokol pro přenos webových stránek, obrázků a dalších typů souborů přes internet. Zadáte-li do webového prohlížeče nějakou URL adresu začínající na http://, specifikujete tím použití HTTP pro přístup k informacím na daném umístění. (Většina prohlížečů podporuje mnoho dalších protokolů – například FTP.) Mějte na paměti, že tato kapitola pokrývá jenom ty aspekty HTTP, které jsou zajímavé z hlediska vývoje v Ajaxu. Rozhodně nepředstavuje referenční manuál HTTP a ani tutoriál. HTTP se skládá ze dvou částí: požadavku a odpovědi. Když zadáte nějakou URL adresu, prohlížeč za vás vytvoří požadavek. Tento požadavek obsahuje zadanou URL adresu a další informace o prohlížeči. Server obdrží požadavek a pošle zpět odpověď. Tato odpověď obsahuje informace o požadavku a data z požadované URL adresy (pokud tedy nějaká jsou). Pak je na samotném prohlížeči, aby odpověď ze serveru zpracoval a zobrazil danou webovou stránku (nebo jiný zdroj).

HTTP požadavek Formát HTTP požadavku je následující:

Kapitola 02.indd 37

15.10.2007 14:07:15


38

Kapitola 2 – Základy Ajaxu

<řádek-požadavku> <hlavičky> <prázdný-řádek> [<tělo-požadavku>]

První řádek HTTP požadavku musí být řádek požadavku, který specifikuje typ požadavku, požadovaný zdroj a použitou verzi HTTP. Následuje sekce hlaviček udávající doplňující informace, které mohou být serverem využity. Za hlavičkami najdeme prázdný řádek, který může být následován nepovinnými daty (nazvanými jako tělo požadavku). V HTTP existuje mnoho různých typů požadavků, ale pro vývojáře v Ajaxu jsou důležité pouze dva. Jsou to GET a POST. Kdykoliv zadáte URL adresu, prohlížeč pošle serveru požadavek GET na tuto adresu. Tím v podstatě serveru říká, aby vzal příslušný zdroj a poslal jej zpět. Zde vidíte, jak může vypadat požadavek GET pro adresu www.wrox.com: GET / HTTP/1.1 Host: www.wrox.com User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6) Gecko/20050225 Firefox/1.0.1 Connection: Keep-Alive

První část prvního řádku udává, že se jedná o požadavek GET. Druhá část tohoto řádku je lomítko, které říká, že požadavek je na kořenový adresář domény. Poslední část tohoto řádku specifikuje použití HTTP verze 1.1 (alternativou je 1.0). A kam je požadavek odeslán? To je na dalším řádku. Na tomto druhém řádku je Host, což je první hlavička požadavku. Tato hlavička udává cíl požadavku. Spojení hlavičky Host s lomítkem z prvního řádku znamená, že požadavek je na www. wrox.com/. (Hlavička Host je požadována v HTTP 1.1 – starší verze 1.0 ji nevyžaduje.) Třetí řádek obsahuje hlavičku User-Agent, která je dostupná jak pro skripty na straně serveru, tak i pro skripty na straně klienta. Je to základní kámen logiky většiny prohlížečů. Tyto informace jsou definovány použitým prohlížečem (v tomto případě se jedná o Firefox 1.0.1) a jsou posílány automaticky při každém požadavku. Posledním řádkem je hlavička Connection, která je obvykle nastavena na Keep-Alive (může být nastavena na jiné hodnoty, ale to je nad rámec této knihy). Pamatujte, že za poslední hlavičkou následuje jeden prázdný řádek. Tento prázdný řádek je nutný i v případě, kdy nenásleduje žádné tělo požadavku. Chcete-li požádat o stránku pod doménou www.wrox.com, například http://www.wrox.com/ books, bude váš HTTP požadavek vypadat následovně: GET /books/ HTTP/1.1 Host: www.wrox.com User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6) Gecko/20050225 Firefox/1.0.1 Connection: Keep-Alive

Kapitola 02.indd 38

15.10.2007 14:07:21


Ajax Profesionálně

39

Všimněte si, že se změnil pouze první řádek, který nově obsahuje tu část URL, jež následuje za www.wrox.com. Pokud existují nějaké parametry pro požadavek GET, jsou přidány za tuto URL. Obecný formát těchto parametrů vypadá nějak takto: URL?jmeno1=hodnota1&jmeno2=hodnota2&...&jmenoN=hodnotaN

Tyto informace se nazývají dotazovací řetězec (query string) a jsou zkopírovány do řádku požadavku následujícím způsobem: GET /books/?name=Professional%20Ajax HTTP/1.1 Host: www.wrox.com User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6) Gecko/20050225 Firefox/1.0.1 Connection: Keep-Alive

Všimněte si, že text Professional Ajax je určitým způsobem zakódován. Mezery jsou nahrazeny znaky %20. Říká se tomu zakódování URL (URL encoding) a je to používáno v mnoha oblastech HTTP. (JavaScript má zabudované funkce pro zakódování a dekódování URL, které jsou probírány v pozdějších kapitolách). Dvojice jméno-hodnota (name-value) jsou odděleny znakem ampersand (&). Většina technologií na straně serveru obsah těla požadavku dekóduje a nějakým způsobem poskytne přístup k těmto hodnotám. Samozřejmě záleží na rozhodnutí serveru, co s daty udělá. Prohlížeče často posílají mnohem více hlaviček, než bylo zmíněno ve výše uvedených příkladech. To znamená, že příklady uvedené v této kapitole byly pro jednoduchost zkráceny.

Požadavek POST poskytuje dodatečné informace ve svém těle. Například když vyplníte nějaký online formulář a odešlete jej, data jsou typicky odeslána prostřednictvím požadavku POST. Typický POST požadavek vypadá nějak takto: POST / HTTP/1.1 Host: www.wrox.com User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6) Gecko/20050225 Firefox/1.0.1 Content-Type: application/x-www-form-urlencoded Content-Length: 40 Connection: Keep-Alive name=Professional%20Ajax&publisher=Wiley

Povšimněte si několika odlišností mezi požadavkem POST a GET. Za prvé – první řádek začíná slovem POST namísto GET. Tím je určen typ požadavku. Dále si všimněte, že hlavičky Host a User-Agent zůstaly a přibyly dvě nové. Hlavička Content-Type specifikuje, jak je požadavek zakódován. Prohlížeče vždycky kódují POST data jako application/x-www-form-urlencoded,

Kapitola 02.indd 39

15.10.2007 14:07:21


Kapitola 2 – Základy Ajaxu

40

což je typ MIME pro jednoduché kódování URL. Hlavička Content-Length udává délku těla požadavku v bytech. Za hlavičkou Connection a prázdným řádkem pak následuje tělo požadavku. Jako u většiny požadavků POST má tělo podobu dvojic jméno-hodnota (name-value), kde jméno name má hodnotu Professional Ajax a jméno publisher má hodnotu Wiley. Měli byste si všimnout, že se jedná o stejný formát jako v případě parametrů v dotazovacím řetězci v URL. Jak bylo zmíněno dříve, existují i jiné typy HTTP požadavků, nicméně všechny mají stejný základní formát jako HTTP požadavky GET a POST. Podívejme se dále, co server posílá nazpět jako odpověď na HTTP požadavek. Z bezpečnostních důvodů může být požadavek GET použit pouze pro získávání dat. Pokud je potřeba nějaká data vkládat, editovat nebo mazat, musí být použit HTTP požadavek POST.

HTTP odpověď Formát HTTP odpovědi je velmi podobný formátu HTTP požadavku: <řádek-se-stavem> <hlavičky> <prázdný-řádek> [<tělo-odpovědi>]

Jak můžete vidět, jediná odlišnost je v prvním řádku, který nyní místo řádku s požadavkem obsahuje informaci o stavu. Tento řádek se stavem obsahuje stavový kód pro požadovaný zdroj. Příklad HTTP odpovědi následuje: HTTP/1.1 200 OK Date: Sat, 31 Dec 2005 23:59:59 GMT Content-Type: text/html;charset=ISO-8859-1 Content-Length: 122 <html> <head><title>Wrox Homepage</title></head> <body> <!-- zde následuje tělo WWW stránky --> </body> </html>

V tomto příkladu je v řádku se stavem uveden HTTP kód 200 a zpráva OK. Nemohou nastat žádné nejasnosti, protože řádek se stavem vždy obsahuje stavový kód a odpovídající krátkou zprávu. Nejpoužívanější stavové kódy jsou následující:

200 (OK). Zdroj byl nalezen a všechno je v pořádku.

Kapitola 02.indd 40

15.10.2007 14:07:22


Ajax Profesionálně

41

304 (NOT MODIFIED). Zdroj nebyl od posledního požadavku modifikován (upravován). Tento kód nejčastěji využívají různé cache webových prohlížečů.

401 (UNAUTHORIZED). Klient nemá oprávnění k přístupu ke zdroji. Tento kód často vyvolá dotaz na uživatelské jméno a heslo pro přihlášení se k serveru.

403 (FORBIDDEN). Klient nezískal oprávnění. Tato situace typicky nastane, když se nezdaří přihlášení s uživatelským jménem a heslem po kódu 401.

404 (NOT FOUND). Zdroj na dané adrese neexistuje.

Za prvním řádkem se stavem následují hlavičky. Server obvykle vrátí hlavičku Date reprezentující datum a čas, kdy byla odpověď vygenerována. (Servery často také vrací nějaké informace o sobě, i když to není požadováno.) Další dvě hlavičky jsou vám už známé – je to Content-Type a Content-Length, stejně jako v požadavku POST. V tomto případě Content-Type udává MIME typ pro HTML (text/html) s kódováním ISO-88-59-1, což je standard pro zdroje v americké angličtině. Tělo odpovědi obsahuje HTML kód požadovaného zdroje (ale pro jiné typy zdrojů může obsahovat i prostý text nebo binární data). Právě tato data pak prohlížeč zobrazuje uživateli. Všimněte si, že v odpovědi nenaleznete žádné označení požadavku, který si tuto odpověď ze serveru vyžádal – pro server toto nemá žádný význam. Je na klientovi, aby věděl, jaký typ dat má pro daný požadavek v odpovědi očekávat, a je pouze na něm, aby se rozhodl, co s těmito daty udělat.

Techniky komunikace pro Ajax Nyní už rozumíte základům HTTP, takže je čas se podívat na techniky komunikace na webové stránce. Jak už sami víte, existuje mnoho požadavků, které jsou posílány mezi klientem a serverem, zatímco vy surfujete na webu. Všechny tyto požadavky vzniknou pokaždé, když uživatel učiní nějakou akci, která tyto požadavky vyžaduje. Techniky Ajaxu osvobozují vývojáře od čekání, než uživatel tuto akci provede, protože umožňují vytvořit pro volání serveru kdykoliv. Jak bylo podrobně popsáno v kapitole 1, Ajax podporuje velké množství různých komunikačních technik. Každá z nich má výhody a nevýhody a je velmi důležité pochopit, kterou z nich je vhodné v dané situaci použít.

Technika neviditelných rámců Technika neviditelných rámců se zrodila se vznikem HTML rámců. Základní myšlenkou v pozadí této techniky je vytvoření skupiny rámců s jedním neviditelným rámcem, který se používá pro komunikaci mezi klientem a serverem. Rámec lze skrýt tak, že mu nastavíte šířku nebo výšku na 0 pixelů, čímž jej efektivně skryjete na stránce. Tato technika je mezi vývojáři stále značně oblíbena, ačkoliv některé starší prohlížeče (jako třeba Netscape 4) neuměly rámce úplně schovat a často zobrazovaly jejich tlusté okraje. Tyto starší prohlížeče jsou ovšem již dávno minulostí.

Kapitola 02.indd 41

15.10.2007 14:07:22


42

Kapitola 2 – Základy Ajaxu

Vzor Technika neviditelného rámce používá speciální čtyřkrokový vzor (viz obrázek 2-1). První krok vždy začíná viditelným rámcem, kde se uživateli zobrazuje webová stránka. Uživatel ovšem neví, že stránka také obsahuje neviditelný rámec, který v moderních prohlížečích není viditelný. Se stránkou pracuje obvyklým způsobem. Ve stejný moment, kdy uživatel požaduje nějaká další data ze serveru, nastane první krok tohoto procesu – funkce JavaScriptu zavolá skrytý rámec. Toto volání může být buď velmi jednoduché, například přesměrování skrytého rámce na jinou adresu, nebo složitější, například zaslání dat prostřednictvím formuláře. Bez ohledu na složitost této funkce je výsledkem druhý krok procesu – odeslání požadavku na server.

Obrázek 2-1. Čtyřkrokový vzor. Třetím krokem ve vzoru je odpověď obdržená od serveru. Protože pracujeme s rámci, musí být v odpovědi poslána kompletní webová stránka. Ta musí obsahovat jednak data požadovaná od serveru a dále kód JavaScriptu, který tato data přenese do viditelného rámce. Typicky se to děje odchycením události onload vrácené stránky, což znamená, že po načtení stránky se zavolá funkce ve viditelném rámci (to je čtvrtý krok). V tomto okamžiku se data nachází ve viditelném rámci. Když nyní chápeme techniku neviditelných rámců, je čas se o ní dozvědět více informací. Stejně jako u každé nové techniky vede nejlepší cesta k jejímu vysvětlení přes názorný příklad. Budeme vytvářet jednoduchou vyhledávací stránku, kde pracovník zákaznického servisu může vyhledávat informace o zákaznících. Tento příklad bude velmi jednoduchý: uživatel zadá ID zákazníka a obdrží informace o něm. Bude používána databáze, takže se neobejdeme bez programování na straně serveru. Naštěstí toho programování nebude moc. Tento příklad je založen na PHP, což je výtečný open-source jazyk pro programování na straně serveru a open-source dababázi MySQL, která je zdarma dostupná na adrese www.mysql.org. V PHP 5 je standardně podpora databáze MySQL vypnuta. Pro informace, jak ji zapnout, navštivte webovou adresu http://www.php.net/mysql/.

Kapitola 02.indd 42

15.10.2007 14:07:22


Ajax Profesionálně

43

Za prvé – než bude možné vyhledávat v databázi informace o nějakém uživateli, musíte mít tabulku, která bude tato data obsahovat. Tuto tabulku vytvoříte pomocí následujícího SQL skriptu: CREATE TABLE 'Customers' ( 'CustomerId' int(11) NOT NULL auto_increment, 'Name' varchar(255) NOT NULL default '', 'Address' varchar(255) NOT NULL default '', 'City' varchar(255) NOT NULL default '', 'State' varchar(255) NOT NULL default '', 'Zip' varchar(255) NOT NULL default '', 'Phone' varchar(255) NOT NULL default '', 'Email' varchar(255) NOT NULL default '', PRIMARY KEY ('CustomerId') ) TYPE=MyISAM COMMENT='Sample Customer Data';

Nejdůležitější políčko v tabulce je CustomerId, které budete používat pro vyhledání informací o daném zákazníkovi. Tento skript, včetně vzorových dat, si můžete stáhnout z adresy www.zonerpress.cz.

S existující databázovou tabulkou je čas přesunout se k HTML kódu. Pro použití techniky neviditelných rámců musíte začít s definicí skupiny rámců: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"> <html> <head> <title>Hidden Frame GET Example</title> </head> <frameset rows="100%,0" style="border: 0px"> <frame name="displayFrame" src="DataDisplay.php" noresize="noresize" /> <frame name="hiddenFrame" src="about:blank" noresize="noresize" /> </frameset> </html>

Důležitou součástí tohoto kódu je atribut rows prvku <frameset>. Jeho nastavením na hodnotu 100%, 0 se prohlížeč dozví, že tělo rámce pojmenovaného jako hiddenFrame se nemá ve stránce zobrazovat. Atribut style se používá pro nastavení nulového orámování rámce – tím se zabezpečí, že rámce nebudou mít kolem sebe nějaké orámování. Posledním důležitým krokem v této definici skupiny rámců je použití atributu noresize každého rámce, takže uživatel nebude schopen změnit velikost rámců a zjistit, co se nachází v tom skrytém rámci. Obsah neviditelného rámce by měl zůstat před uživatelem vždy skrytý.

Kapitola 02.indd 43

15.10.2007 14:07:22


44

Kapitola 2 – Základy Ajaxu

Následuje stránka pro zadání požadavku a pro zobrazení informací o zákazníkovi (DataDisplay. php). Tato stránka je poměrně jednoduchá – skládá se z textového pole formuláře pro zadání ID zákazníka, tlačítka pro odeslání požadavku a prvku <div> pro zobrazení získaných informací: <p>Enter customer ID number to retrieve information:</p> <p>Customer ID: <input type="text" id="txtCustomerId" value="" /></p> <p><input type="button" value="Get Customer Info" onclick="requestCustomerInfo()" /></p> <div id="divCustomerInfo"></div>

Povšimněte si, že odesílací tlačítko volá funkci requestCustomerInfo(), která pomocí skrytého rámce zařídí získání informací. Jednoduše vezme hodnotu textového pole a přidá ji do dotazovacího řetězce stránky GetCustomerData.php, čímž tak vytvoří URL ve tvaru GetCustomerData. php?id=23. Tato URL je následně přiřazena skrytému rámci. Kód této funkce je následující: function requestCustomerInfo() { var sId = document.getElementById("txtCustomerId").value; top.frames["hiddenFrame"].location = "GetCustomerData.php?id=" + sId; }

První krok v této funkci je získání identifikačního čísla zákazníka z textového pole. Toho je dosaženo voláním document.getElementById() s ID textboxu, což je txtCustomerId. (Hodnota proměnné obsahuje text v textovém poli.) Potom je ID přidáno do řetězce GetCustomerData. php?id= pro vytvoření kompletní URL. Druhý řádek vytvoří URL a nastaví ji do skrytého rámce. Pro získání odkazu na skrytý rámec musíte nejprve pomocí objektu top přistoupit k nejvyššímu oknu v prohlížeči. Tento objekt má pole rámců, v němž najdeme i skrytý rámec. Protože každý rámec je jiný objekt okna, můžete nastavit umístění každého z nich na požadovanou URL. Tohle je vše, co je potřeba pro získání informací. Protože se jedná o požadavek GET, který přenáší informace v dotazovacím řetězci, je jeho provedení velmi jednoduché. V této kapitole si samozřejmě ukážeme, jak s použitím skrytého rámce provést i požadavek typu POST. Dále budeme potřebovat funkci, která zobrazí informace o zákazníkovi. Funkce displayCustomerInfo() bude zavolána skrytým rámcem, jakmile budou data načtena. Jediným jejím argumentem je řetězec obsahující informace, které mají být zobrazeny: function displayCustomerInfo(sText) { var divCustomerInfo = document.getElementById("divCustomerInfo"); divCustomerInfo.innerHTML = sText; }

Ve druhém řádku této funkce je získána reference na prvek <div>, ve kterém mají být získaná data zobrazena. Ve třetím řádku je řetězec s informacemi o zákazníkovi (sText) přiřazen do vlastnosti innerHTML prvku <div>. Použití vlastnosti innerHTML umožní naformátovat řetězec pomocí HTML. Tím máme hotový kód hlavní stránky. Nyní je čas vytvořit logiku na straně serveru.

Kapitola 02.indd 44

15.10.2007 14:07:22


Ajax Profesionálně

45

Stránka GetCustomerData.php je jednoduchá HTML stránka s PHP kódem na dvou místech: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head> <title>Get Customer Data</title> <?php // kód PHP ?> </head> <body><div id="divInfoToReturn"><?php echo $sInfo ?></div></body> </html>

První blok PHP na této stránce obsahuje logiku pro získání dat o zákazníkovi. Druhý PHP blok pošle proměnnou $sInfo s daty o zákazníkovi do prvku <div>. Pomocí tohoto prvku <div> jsou data poslána do viditelného rámce. Je potřeba vytvořit javascriptovou funkci, která bude zavolána, jakmile je stránka kompletně načtena: window.onload = function () { var divInfoToReturn = document.getElementById("divInfoToReturn"); top.frames["displayFrame"].displayCustomerInfo(divInfoToReturn.innerHTML); };

Tato funkce je přiřazena k události windows.onload. Nejprve získá odkaz na prvek <div>, který obsahuje informace o zákazníkovi. Potom s použitím pole top.frames přistoupí k rámci a zavolá funkci displayCustomerInfo() pro předání informací do vlastnosti innerHTML tohoto prvku <div>. Celé zasílání informací má na svědomí JavaScript. Ale odkud se tyto informace vezmou? Je k tomu potřeba krátký PHP kód, který je získá z databáze. Prvním krokem při vytvoření tohoto PHP kódu je definice všech dat, která budeme potřebovat. Zde se jedná o ID zákazníka, proměnnou $sInfo pro vrácení informací a nezbytné informace pro přístup k databázi (databázový server, jméno databáze, uživatelské jméno, heslo a SQL dotaz): <?php $sID = $_GET["id"]; $sInfo = ""; $sDBServer = "your.databaser.server"; $sDBName = "your_db_name"; $sDBUsername = "your_db_username"; $sDBPassword = "your_db_password"; $sQuery = "Select * from Customers where CustomerId=".$sID; // zde bude další kód ?>

Kapitola 02.indd 45

15.10.2007 14:07:22


Kapitola 2 – Základy Ajaxu

46

Začátek tohoto kódu slouží pro získání argumentu id z dotazovacího řetězce. PHP umístí všechny argumenty dotazovacího řetězce do pole $_GET. Tento identifikátor je pak uložen do $sID a používá se k vytvoření SQL dotazu uloženého v $sQuery. Do proměnné $sInfo je vložen prázdný řetězec. Všechny zbývající proměnné v tomto kódu slouží pro vložení přístupových informací k vaší databázi, takže je nahraďte vašimi vlastními hodnotami. Po získání informací od uživatele a nastavení přístupu k databázi bude naším dalším krokem spojení s databází, vykonání dotazu a vrácení výsledků. Pokud se v tabulce nachází zákazník s daným ID, proměnná $sInfo je naplněna HTML řetězcem obsahujícím všechna jeho data, včetně odkazu pro e-mailovou adresu. Pokud je ID zákazníka neplatné, proměnná $sInfo je naplněna chybovou hláškou, která je pak poslána zpět do viditelného rámce: <?php $sID = $_GET["id"]; $sInfo = ""; $sDBServer = "your.databaser.server"; $sDBName = "your_db_name"; $sDBUsername = "your_db_username"; $sDBPassword = "your_db_password"; $sQuery = "Select * from Customers where CustomerId=".$sID; $oLink = mysql_connect($sDBServer,$sDBUsername,$sDBPassword); @mysql_select_db($sDBName) or $sInfo="Unable to open database"; if ($sInfo == "") { if($oResult = mysql_query($sQuery) and mysql_num_rows($oResult) > 0) { $aValues = mysql_fetch_array($oResult,MYSQL_ASSOC); $sInfo = $aValues['Name']."<br />".$aValues['Address']."<br/>". $aValues['City']."<br />".$aValues['State']."<br />". $aValues['Zip']."<br /><br />Phone: ".$aValues['Phone']."<br/>". "<a href=\"mailto:".$aValues['Email']."\">". $aValues['Email']."</a>"; mysql_free_result($oResult); } else { $sInfo = "Customer with ID $sID doesn't exist."; } } mysql_close($oLink); ?>

První dva řádky ve zvýrazněné části kódu obsahují připojení k databázi MySQL z PHP. Následuje volání funkce mysql_query() pro vykonání SQL dotazu. Pokud tato funkce vrátí výsledek s alespoň jedním řádkem, pak kód pokračuje získáním informací a jejich uložením do $sInfo. V opačném případě je do $sInfo vložena chybová hláška. Předposlední řádek tohoto kódu pak ukončí spojení s databází.

Kapitola 02.indd 46

15.10.2007 14:07:22


Ajax Profesionálně

47

Vysvětlení detailů ohledně programování v PHP a MySQL je mimo rozsah této knihy. Pokud se chcete dozvědět více, zvažte pořízení těchto titulů z vydavatelství Zoner Press: Velká kniha PHP a MySQL 5 – kompendium znalostí pro začátečníky i profesionály nebo PhpMyAdmin – efektivní správa MySQL.

Než se posuneme dále, je nezbytný ještě jeden krok. Předcházející kód má z hlediska bezpečnosti jeden nedostatek. Protože ID zákazníka je přenášeno v dotazovacím řetězci, není bezpečné přidat jeho hodnotu přímo do SQL dotazu. Co když do něj uživatel vložil nějaký jiný SQL příkaz? Toto je tzv. útok injektáží SQL (SQL injection attack) a jedná se o velmi nebezpečný typ útoku. Oprava našeho příkladu je jednoduchá: ujistit se, že ID zákazníka je jen číslo a nic více. PHP nabízí velmi užitečnou funkci is_numeric(), která určí, zdali řetězec reprezentuje pouze číslo: <?php $sID = $_GET["id"]; $sInfo = ""; if (is_numeric($sID)) { $sDBServer = "your.databaser.server"; $sDBName = "your_db_name"; $sDBUsername = "your_db_username"; $sDBPassword = "your_db_password"; $sQuery = "Select * from Customers where CustomerId=".$sID; $oLink = mysql_connect($sDBServer,$sDBUsername,$sDBPassword); @mysql_select_db($sDBName) or $sInfo="Unable to open database"; if ($sInfo == "") { if($oResult = mysql_query($sQuery) and mysql_num_rows($oResult) > 0) { $aValues = mysql_fetch_array($oResult,MYSQL_ASSOC); $sInfo = $aValues['Name']."<br />".$aValues['Address']."<br/>". $aValues['City']."<br />".$aValues['State']."<br />". $aValues['Zip']."<br /><br />Phone: ".$aValues['Phone']."<br/>". "<a href=\"mailto:".$aValues['Email']."\">". $aValues['Email']."</a>"; mysql_free_result($oResult); } else { $sInfo = "Customer with ID $sID doesn't exist."; } } } else { $sInfo = "Invalid customer ID."; } mysql_close($oLink); ?>

Kapitola 02.indd 47

15.10.2007 14:07:23


48

Kapitola 2 – Základy Ajaxu

Přidáním této jednoduché kontroly dat se vyhnete případným útokům založeným na injektáži SQL tak, že místo zobrazení informací v takovém případě vrátíte z databáze chybovou zprávu. Když je proměnná @sInfo přenesena, bude prvek <div> obsahovat požadované informace. Zachycení události onload přečte data a pošle je zpět do zobrazeného rámce ve stránce. Pokud je nějaký zákazník nalezen, budou zobrazeny informace jako na obrázku 2-2.

Obrázek 2-2. Zobrazení konkrétních informací o zákazníkovi. Pokud daný zákazník neexistuje nebo zadané ID není číslo, bude ve stránce zobrazena chybová zpráva. Každopádně – ať už to dopadne tak, či onak, pracovník zákaznického servisu bude mít hezké uživatelské dojmy při práci s touto aplikací. A tímto končí náš první příklad v Ajaxu. Tento příklad (a vlastně i všechny ostatní příklady uvedené v této knize) je rovněž dostupný i ve verzi pro ASP.NET a JSP. Naleznete je ke stažení na adrese www.zonerpress.cz.

Požadavek POST a neviditelný rámec Předchozí příklad používal pro získání informací z databáze požadavek GET. Bylo to docela jednoduché, protože ID zákazníka bylo přidáno do URL a jako dotazovací řetězec posláno serveru. Ale co když chcete poslat požadavek typu POST? S použitím techniky neviditelného rámce je to rovněž možné, pouze si to vyžádá o trochu práce navíc. Požadavek POST je obvykle použit v případě, kdy je potřeba poslat na server nějaká data (na rozdíl od požadavku GET, který data ze serveru pouze získává). A další podstatný rozdíl je ve velikosti

Kapitola 02.indd 48

15.10.2007 14:07:23


KAPITOLA 6 XML, XPath a XSLT S rostoucí popularitou XML chtěli vývojáři tuto technologii použít na obou stranách – jak na straně serveru, tak i na straně uživatele. Microsoft a Mozilla, počínaje Internet Explorerem 5.0 a Mozillou 1.0 (předchůdcem Firefoxu), do svých prohlížečů implementovali podporu XML v JavaScriptu. Opera 8 a Safari 1.2 umožnily základní formu podpory XML. Zatímco Opera podporu XML v JavaScriptu neustále rozvíjí, Safari o něco zaostává a dnes má nejmenší podporu XML. Tvůrci prohlížečů pokračují v rozšiřování podpory XML pomocí různých nových rysů, čímž vývojářům nabízejí silné nástroje, které jsou podobné těm, jež bylo původně možné nalézt pouze na serveru.

Podpora XML v prohlížečích V současné době je dostupných mnoho webových prohlížečů, ale jen několik z nich má úplnou podporu XML a s tím spojených technologií. Vedoucí postavení mezi nimi zaujímá Internet Explorer (IE) a Mozilla Firefox, těsně za nimi je Opera (verze 9). O velký kus dále za nimi se vleče Safari od Apple, který podporuje pouze základní rysy XML. Navzdory těmto rozdílům všechny zmíněné prohlížeče obsahují základní funkčnosti XML, takže v této části se můžeme zabývat těmito čtyřmi hlavními prohlížeči.

XML DOM v IE Microsoft přidal podporu XML do IE 5.0 tím, že do něj zařadil ActiveX knihovnu MSXML, což byla komponenta, která byla původně vytvořena pro zpracování aktivních kanálů v IE 4.0. Tato původní verze komponenty nebyla zamýšlena pro užívání veřejností, nicméně vývojáři ji objevili a začali ji používat. Microsoft pak zareagoval celkovou aktualizací verze MSXML, která byla obsažena v IE 4.01. Knihovna MSXML byla zpočátku součástí pouze IE.

Kapitola 06.indd 179

15.10.2007 14:08:34


Kapitola 6 – XML, XPath a XSLT

180

Toto trvalo až do roku 2001, kdy Microsoft vydal MSXML 3.0 – samostatné rozšíření, které bylo dostupné skrze webové stránky společnosti. Později v tomto roce byla vydána verze 4.0 a knihovna MSXML se přejmenovala na Microsoft XML Core Services Component. Od svého vzniku MSXML prošla vývojem od základního a neplatného (nevalidního) XML parseru až po silnou komponentu, která může ověřovat platnost XML dokumentů, provádět XSL transformace, podporovat jmenné prostory, jednoduché API pro XML (SAX) či různé W3C standardy jako Xpath nebo schémata XML. A každá nová verze je výkonnější.

Vytvoření objektu XML DOM Za účelem usnadnění vytváření objektů ActiveX v JavaScriptu zavedl Microsoft třídu zvanou ActiveXObject. Její konstruktor přijímá jeden argument – řetězec obsahující jméno a verzi objektu

ActiveX, který má být vytvořen. V tomto případě se jedná o verzi XML dokumentu. První ActiveX objekt XML DOM byl pojmenován jako Microsoft.XmlDom a jeho vytvoření vypadá takto: var oXmlDom = new ActiveXObject("Microsoft.XmlDom");

Nově vytvořený objekt XML DOM se nechová stejně jako každý jiný objekt DOM – umožňuje vám totiž procházet stromovou strukturou DOM a manipulovat s uzly DOM. V době, kdy vznikala tato kniha, existovalo celkem 6 různých verzí MSXML DOM. Jedná se o následující řetězce verzí:

Microsoft.XmlDom.

MSXML2.DOMDocument.

MSXML2.DOMDocument.3.0.

MSXML2.DOMDocument.4.0.

MSXML2.DOMDocument.5.0.

MSXML2.DOMDocument.6.0.

Knihovna MSXML je dostupná pouze na Internet Exploreru ve Windows. Internet Explorer 5 na počítačích Mac nemá podporu XML DOM.

Protože každá nově vydaná knihovna MSXML přináší mnoho vylepšení, měli byste vždy používat tu nejnovější. Microsoft doporučuje kontrolovat existenci nejnovějších verzí (v době psaní této knihy to byla MSXML6). Dále doporučuje v případě problémů používat verzi MSXML3. Z pohledu vývojáře tedy bude užitečné vytvořit funkci, jež by určila, kterou verzi použít. Následující funkce createDocument() vytvoří MSXML6 DOM, pokud ji počítač uživatele podporuje. V opačném případě je vytvořena MSXML3 DOM: function unction createDocument() { var aVersions = [

Kapitola 06.indd 180

15.10.2007 14:08:38


Ajax Profesionálně

181

"MSXML2.DOMDocument.6.0", "MSXML2.DOMDocument.3.0", ]; for (var i = 0; i < aVersions.length; i++) { try { var oXmlDom = new ActiveXObject(aVersions[i]); return oXmlDom; } catch (oError) { //Nedělej nic } } throw new Error("MSXML is not installed."); }

Tato funkce provádí iterace skrz pole aVersions, jež zahrnuje řetězce verzí. Začíná s nejnovější verzí – MSXML2.DOMDocument.6.0 – a snaží se vytvořit dokument DOM. Pokud je vytvoření objektu úspěšné, je vrácen a funkce createDocument() je ukončena. V opačném případě je vyhozena výjimka, která je pak zachycena pomocí bloku try...catch, takže smyčka pokračuje a vyzkouší se další verze. Pokud tvorba MSXML DOM dokumentu selže po dvou pokusech, je vyhozena výjimka, že knihovna MSXML není nainstalována. Volání funkce vypadá takto: var oXmlDom = createDocument();

V tuto chvíli máte XML dokument k dispozici a je čas načíst nějaká data XML.

Načítání XML dat v IE Dokument MSXML DOM podporuje dvě metody načítání dat XML – load() a loadXML(). Metoda load() přijímá jeden argument, což je URL, ze které má být stažen XML soubor. Metoda loadXML() také přijímá jeden argument, jedná o řetězec dat XML. Obě metody mají za následek analýzu (parsování) dat XML a vytvoření struktury XML DOM. Metoda load() se chová podobně jako XHR v tom, že může načítat data z externího souboru ve dvou režimech: asynchronním nebo synchronním. To nastavíte ve vlastnosti async. Standardně má async hodnotu true, takže load() metoda je asynchronní. Pro použití synchronního režimu musí být vlastnost async nastavena na false: oXmlDom.async = false;

Obecně je považováno za nepraktické vykonávat požadavky v synchronním režimu (kvůli možnosti zamrznutí uživatelského rozhraní). Synchronní režim by měl být používán šetrně a pouze v případě, kdy je ze serveru posíláno velmi malé množství dat.

Kapitola 06.indd 181

15.10.2007 14:08:38


Kapitola 6 – XML, XPath a XSLT

182

V asynchronním režimu vystavuje objekt MSXML vlastnost readyState, která má v podstatě pět stejných stavů jako XHR vlastnost readyState (popsáno v kapitole 2). Výjimkou je, že objekt MSXML nemá stav 0 (UNITIALIZED). Dokument DOM navíc podporuje ovladač události onreadystatechange, který umožňuje sledovat vlastnost readyState: oXmlDom.onreadystatechange = function () { if (oXmlDom.readyState == 4) { //Dělej něco, když jsou data kompletně načtena. } }; oXmlDom.load("myxml.xml");

V tomto příkladě je do XML DOM načten fiktivní XML dokument s názvem myxml.xml. Když readyState dosáhne hodnoty 4, je dokument plně načten a kód uvnitř bloku if bude proveden. Všimněte si, že na rozdíl od objektu XHR neexistuje v objektu XML DOM vlastnost status.

Druhá možnost, jak načíst XML data – metoda loadXML() – je poněkud jednodušší a nevyžaduje žádné HTTP příkazy, protože data jsou již přítomna v počítači klienta. Předaná data musí obsahovat správně zformované XML, jako v následujícím příkladu: var sXml = "<root><person><name>Jeremy McPeak</name></person></root>"; oXmlDom.loadXML(sXml);

V tomto případě jsou XML data obsažena v proměnné sXML a jsou načítána do dokumentu oXmlDom. Není zde žádný důvod kontrolovat vlastnost readyState nebo nastavovat vlastnost async,

protože neobsahuje příkazy serveru – data jsou načítána synchronně a jsou dostupná okamžitě.

Kontrola platnosti XML dat v průběhu načítání Objekt MSXML DOM standardně ověřuje platnost XML dokumentu, když analyzuje data. Platný XML dokument je takový dokument, který obsahuje referenci na definici typu dokumentu (DTD) v deklaraci DOCTYPE a přizpůsobuje se tomuto DTD. Může se stát, že toto chování nebude žádoucí. V takovém případě bude vhodnější, aby byla zkontrolována pouze struktura dokumentu. Aby to bylo možné, poskytuje objekt MSXML DOM vlastnost validateOnParse. Povolené hodnoty jsou true (výchozí), nebo false, přičemž by měla být nastavena před tím, než objekt DOM načte dokument. var oXmlDom = createDocument(); oXmlDom.async = false; oXmlDom.validateOnParse = false; oXmlDom.load("myxml.xml");

Kapitola 06.indd 182

15.10.2007 14:08:38


Ajax Profesionálně

183

V tomto kódu, kdy objekt XML DOM načítá a analyzuje kód XML, bude kontrola probíhat pouze za účelem kontroly správné struktury dokumentu.

Ochrana prázdných znaků MSXML DOM zachází s prázdnými znaky (whitespace) jinak než je standardem v DOM. MSXML DOM standardně odstraňuje z dokumentu pouze uzly s prázdnými znaky – nenechává nic kromě XML a textových uzlů. Zatímco mnozí považují tuto vlastnost za rozumnou, skutečnost je taková, že je to docela nepraktické. MSXML DOM ovšem nabízí vlastnost preserveWhiteSpace, která říká parseru, aby uzly s prázdnými znaky zamítl nebo povolil. Tato vlastnost má logickou hodnotu – výchozí je false. Následující kód načítá XML dokument a zabraňuje odstranění prázdných znaků v něm: var oXmlDom = createDocument(); oXmlDom.async = false; oXmlDom.preserveWhiteSpace = true; oXmlDom.load("myxml.xml");

Pokud je tato vlastnost preserveWhiteSpace nastavena na true, dovoluje, aby se objekt MSXML DOM choval jako standardní DOM.

Procházení XML DOM v IE Navigace v dokumentu XML DOM je podobná navigaci dokumentu DOM HTML – je to struktura hierarchicky uspořádaných uzlů. Na vrcholu stromu je documentElement, který obsahuje kořenový prvek dokumentu. Z tohoto místa můžete zpřístupnit kterýkoliv prvek nebo atribut dokumentu pomocí vlastností uvedených v tabulce 6-1. Tabulka 6-1. Vlastnosti XML DOM. Vlastnost

Popis

attributes

Kolekce atributů pro tento uzel.

childNodes

Kolekce potomků (dceřiných uzlů).

firstChild

První potomek uzlu.

lastChild

Poslední potomek uzlu.

nextSibling

Uzel bezprostředně následující po aktuálním uzlu.

nodeName

Jméno uzlu.

nodeType

XML DOM typ uzlu.

nodeValue

Text spojený s uzlem, pokud existuje.

ownerDocument

XML DOM dokument, jehož částí uzel je.

Kapitola 06.indd 183

15.10.2007 14:08:38


184

Kapitola 6 – XML, XPath a XSLT

Vlastnost

Popis

ParentNode

Rodičovský uzel aktuálního uzlu.

PreviousSibling

Uzel bezprostředně předcházející aktuálnímu uzlu.

Text

Vrací obsah uzlu nebo zřetězený text současného uzlu a jeho potomků.

Xml

Vrací XML řetězec reprezentující současný uzel a jeho potomky. Pouze v IE.

Procházení a získávání dat z DOM je přímočarý proces. Mějme následující XML dokument: <?xml version="1.0" encoding="utf-8"?> <books> <book isbn="9780470109496">Professional Ajax</book> <book isbn="0764579088">Professional JavaScript for Web Developers</book> <book isbn="0764557599">Professional C#</book> <book isbn="1861002025">Professional Visual Basic 6 Databases</book> </books>

Tento jednoduchý XML dokument obsahuje kořenový prvek <books> se čtyřmi potomky <book>. Použitím tohoto dokumentu jako ukazatele můžete prozkoumat DOM. Strom DOM je založen na vztazích mezi uzly. Jeden uzel může obsahovat jiné uzly, které jsou nazývány jako dceřiné uzly (každý prvek <book> je dceřiným uzlem prvku <books>). Další uzel může sdílet stejné rodiče jako jiné uzly – v takovém případě se uzly nazývají jako sourozenci (siblings, každý prvek <book> je sourozencem jiných prvků <book>). Předpokládejme, že budete chtít získat první prvek <book> v dokumentu. Toho snadno dosáhnete pomocí vlastnosti firstChild: var oFirstBook = oXmlDom.documentElement.firstChild;

Použitím vlastnosti firstChild je získána reference na první prvek <book> a je přiřazen do proměnné oFirstBook, protože je to první dceřiný prvek kořenového prvku <books>. K získání stejného výsledku můžete rovněž použít kolekci childNodes: var oFirstBook2 = oXmlDom.documentElement.childNodes[0];

Výběr prvního prvku v kolekci childNodes (na indexu 0) vrací prvního potomka uzlu (stejně jako použití vlastnosti firstChild). Pomocí vlastnosti length můžete snadno určit počet potomků, které má daný uzel. var iChildren = oXmlDom.documentElement.childNodes.length;

Pokud může mít nějaký uzel potomky, znamená to, že potomci mohou mít rodiče. Vlastnost ParentNode vrací rodiče daného uzlu. var oParent = oFirstBook.parentNode;

Kapitola 06.indd 184

15.10.2007 14:08:38


Ajax Profesionálně

185

Připomínáme, že oFirstBook je první prvek <book> v dokumentu. Vlastnost ParentNode tohoto uzlu se odkazuje na prvek <books>, na documentElement dokumentu. Jednotlivé prvky <book> jsou vzájemně sourozenci, protože mají stejného přímého rodiče. Existují dvě vlastnosti pro zpřístupnění těchto sousedních uzlů – nextSibling a previousSibling. Vlastnost nextSibling se odkazuje na následujícího sourozence, zatímco vlastnost previousSibling vybírá předcházejícího sourozence: var oSecondBook = oFirstBook.nextSibling; var oFirstBook2 = oSecondBook.previousSibling;

V tomto kódu je druhý prvek <book> přiřazen k oSecondBook. Proměnná oFirstBook2 je pak přiřazena k předchozímu sourozenci oSecondBook, což má za následek to, že oFirstBook2 obsahuje stejné hodnoty jako oFirstBook. Pokud uzel nemá žádné předcházející nebo následující sourozence, previousSibling a nextSibling budou null. Když nyní víte, jak procházet skrze hierarchii dokumentu, je dalším krokem provést extrakci z uzlů ve stromu. Například – pro získání textu obsaženého uvnitř třetího prvku <book> ("Professional C#") můžete použít vlastnost text následujícím způsobem: var sText = oRoot.childNodes[2].text;

Vlastnost text získá kompletní text, který je obsažený v tomto uzlu. Je to sice patentovaná vlastnost Microsoftu, ale je velice užitečná. Bez ní byste museli text uzlu zpřístupnit takto: var sText = oRoot.childNodes[2].firstChild.nodeValue;

Tento kód získává stejné výsledky jako použití vlastnosti text. Stejně jako v předchozím příkladě i zde je reference na třetí prvek <book> získána pomocí kolekce childNodes. Reference na text uzlu z prvku <book> je pak získána s použitím firstChild, protože textový uzel je pořád DOM uzel. Samotný text je pak získáván s použitím vlastnosti nodeValue, která je pro textový uzel vždy nastavena na jeho obsah. Výsledky těchto dvou příkladů jsou identické, ačkoliv se vlastnost text chová jinak, než je použití vlastnosti nodeValue na textový uzel. Vlastnost text získává hodnotu všech textových uzlů obsažených v prvku a jeho potomcích, zatímco vlastnost nodeValue získává hodnoty pouze ze současného uzlu. Vlastnost text je sice užitečná, ale může také vrátit více textu, než by bylo žádoucí. <?xml version="1.0" encoding="utf-8"?> <books> <book isbn="9780470109496"> <title>Professional Ajax</title> <author>Nicholas C. Zakas, Jeremy McPeak, Joe Fawcett</author> </book> <book isbn="0764579088">Professional JavaScript for Web Developers</book> <book isbn="0764557599">Professional C#</book> <book isbn="1861002025">Professional Visual Basic 6 Databases</book>

Kapitola 06.indd 185

15.10.2007 14:08:38


186

Kapitola 6 – XML, XPath a XSLT

</books>

Tento nový XML dokument přidává dva nové potomky k prvnímu prvku <book> – prvek <title>, který obsahuje název knihy, a prvek <author>, jenž obsahuje data o autorech. Znovu použijte

vlastnost text: alert(oFirstBook.text);

V tomto kódu není nic nového, ale podívejte se na výsledky, které jsou ukázány na obrázku 6-1.

Obrázek 6-1. Výsledek kódu. Všimněte si, že textové uzly z prvků <title> a <author> byly získány a zřetězeny. Pokud byste použili oFirstBook.nodeValue, vrátilo by se null, protože oFirstBook není textovým uzlem. Existuje mnoho metod pro získání uzlů a hodnot z XML uzlů – dvě nejčastěji používané jsou getAttribute() a getElementsByTagName(). Metoda getAttribute() vezme řetězec argumentů obsahujících název atributu, který chcete získat. Pokud atribut neexistuje, vrátí se hodnota null. Pokud použijeme stejný XML dokument, který byl uveden dříve v této kapitole, můžeme pracovat s následujícím kódem: var sAttribute = oFirstBook.getAttribute("isbn"); alert(sAttribute);

Tento kód získává hodnotu atributu isbn z prvního prvku <book> a přiřazuje ho k proměnné sAttribute. Tato hodnota je pak zobrazena pomocí alert(). Metoda getElementsByTagName() vrací NodeList dceřiných prvků se specifikovaným jménem prvku. Tato metoda vyhledává prvky pouze v daných potomcích uzlu, takže vrácený NodeList neobsahuje žádné prvky, které by byly předky nebo potomky předků. Například:

Kapitola 06.indd 186

15.10.2007 14:08:38


Ajax Profesionálně

187

var cBooks = oRoot.getElementsByTagName("book"); alert(cBooks.length);

Tento kód získává všechny prvky <book> v dokumentu a vrací NodeList do cBooks. V našem příkladě s XML dokumentem se zobrazí výstražné okno, že byly nalezeny 4 prvky <book>. K získání všech potomků prvků zadejte * jako parametr do getElementsByTagName(). Následovně: var cElements = oRoot.getElementsByTagName("*");

V tomto případě kolekce cElements obsahuje prvky <book> i <title> a <author>.

Získávání dat XML v IE Získávání dat XML je stejně jednoduché jako používání nějaké vlastnosti, v tomto případě vlastností xml. Tato vlastnost serializuje data XML ze současného uzlu. Serializace je proces konverze objektů do snadno uložitelných a přenositelných formátů. Vlastnost xml kompletně zkonvertuje XML do řetězcové reprezentace se jmény prvků, atributů a textem: var sXml = oRoot.xml; alert(sXml);

Tento kód serializuje data XML počínaje kořenovým prvkem dokumentu. Výsledek je pak předán metodě alert(). Část serializovaných dat vypadá nějak takto: <books><book isbn="9780470109496">Professional Ajax</book></books>

Serializovaná data je možné načíst do jiného objektu XML DOM, poslat je serverové aplikaci nebo je předat jiné stránce. Serializovaná data XML, která jsou vrácená vlastností xml, závisí na aktuálním uzlu. Použití vlastnosti xml v uzlu documentElement vrací data XML z celého dokumentu, zatímco její použití v prvku <book> vrací pouze data obsažená v tomto prvku <book>. Vlastnost xml je pouze pro čtení. Pokud chcete přidat nějaké prvky do dokumentu, budete muset použít metody DOM, což je popsáno dále v této kapitole.

Manipulace s DOM v IE Do této chvíle jste se naučili, jak procházet strukturou DOM, jak z něj vytáhnout informace, a jak převést XML do řetězce. Nyní si ukážeme, jak v DOM přidat, smazat a přemístit uzly.

Vytváření uzlů Použitím metod DOM můžete vytvořit několik různých uzlů – nejčastěji používaná je ovšem metoda createElement(). Tato metoda přijímá jeden argument: řetězec obsahující název prvku, který má vytvořit. Vrátí ukazatel na XMLDOMElement:

Kapitola 06.indd 187

15.10.2007 14:08:38


188

Kapitola 6 – XML, XPath a XSLT

var oNewBook = oXmlDom.createElement("book"); oXmlDom.documentElement.appendChild(oNewBook);

Tento kód vytvoří nový prvek <book> a připojí ho k documentElement použitím metody appendChild(). Tato metoda přidává nové prvky, specifikované jejími argumenty, za poslední dceřiný

uzel. Výše uvedený kód přidá do dokumentu prázdný prvek <book>, takže je potřeba k němu doplnit nějaký text, jako zde: var oNewBook = oXmlDom.createElement("book"); var oNewBookText = oXmlDom.createTextNode("Professional .NET 2.0 Generics"); oNewBook.appendChild(oNewBookText); oXmlDom.documentElement.appendChild(oNewBook);

Tento kód vytvoří textový uzel pomocí metody createTextNode() a připojí ho k nově vytvořenému prvku <book> pomocí appendChild(). Metoda createTextNode() přijímá řetězec jako argument, který specifikuje textový obsah uzlu. V tomto bodě jsme programově vytvořili nový prvek <book>, poskytli mu textový uzel a připojili ho k dokumentu. Aby se tento nový prvek mohl stát sourozeneckým s okolními prvky <book>, potřebujeme specifikovat ještě jednu informaci – atribut isbn. Vytvoření tohoto atributu je jednoduché – stačí použít metodu setAttribute(), která je přístupná pro každý prvek uzlu. var oNewBook = oXmlDom.createElement("book"); var oNewBookText = oXmlDom.createTextNode("Professional .NET 2.0 Generics"); oNewBook.appendChild(oNewBookText); oNewBook.setAttribute("isbn","0764559885"); oXmlDom.documentElement.appendChild(oNewBook);

Zvýrazněný řádek kódu v tomto případě vytvoří atribut isbn a přiřadí mu hodnotu 0764559885. Metoda setAttribute() pracuje se dvěma řetězci jako argumenty: první argument je název atributu, druhý argument je jeho hodnota. IE poskytuje i jiné metody pro přidávání atributů k prvku. Tyto metody ovšem nenabízejí žádnou významnou výhodu oproti setAttribute(), nehledě na to, že vyžadují mnohem více kódování.

Odstraňování, nahrazování a vkládání uzlů Pokud umíte přidávat uzly do dokumentu, zdá se být logické, že byste měli být schopni je stejně snadno i odstranit. To uděláte pomocí metody removeChild(). Tato metoda přijímá jeden argument – uzel k odstranění. K odstranění prvního prvku <book> z dokumentu může být použit následující kód: var oRemovedChild = oRoot.removeChild(oRoot.firstChild);

Metoda removeChild() vrací dceřiný uzel, který byl odstraněn, takže oRemovedChild se odkazuje na odstraněný prvek <book>. Pokud máte odkaz (referenci) na starý uzel, můžete jej umístit kamkoliv jinam v dokumentu.

Kapitola 06.indd 188

15.10.2007 14:08:39


KAPITOLA 10 Práce s API pro mapy Na začátku byla služba MapQuest (www.mapquest.com) stránkou, která dovolovala uživateli vyhledat mapy a získat itinerář cesty po Spojených státech. Během éry dot-com se MapQuest stala neuvěřitelně populární, takže se dostala i na burzu. MapQuest následně upoutala pozornost America Online, která tuto službu nakonec v roce 2000 získala do svého vlastnictví. Je pravda, že i ostatní vyvinuli konkurenční stránky s mapami (nejpozoruhodnější z nich jsou od Yahoo! a Microsoftu), ale pro mapy a plánování cest stále zůstala nejpopulárnější MapQuest. Během několika následujících let prošly stránky s mapami koloběhem změn, ovšem v základu zůstaly pořád stejné. Když v roce 2004 přišly Google Maps (maps.google.com, později local.google.com), přinesly skutečně revoluční rozhraní pro tradiční webové mapové systémy. Místo klasické interakce typu "klikni a čekej", kterou pro otáčení a přibližování map používala MapQuest a další, Google Maps použil pro stahování dalších informací (včetně přiblížení mapy) komunikaci založenou na Ajaxu, což znamenalo, že stránka se nikdy nemusela načítat znovu. A navíc tu byla schopnost chytnout a táhnout mapu, která poskytla uživateli opravdu mimořádný zážitek v online světě s mapami. Zdokonalování Google Maps vzbudilo zájem o online práci s mapami, a také o možnosti, které pro tyto potřeby nabízí Ajax. Společnosti Yahoo!, Microsoft a dokonce i MapQuest byly donuceny aktualizovat své mapové projekty takovým způsobem, aby mohly lépe konkurovat mapám od Google. Dosáhly toho – jak jinak – s použitím Ajaxu, a s použitím různých vzorů pro uživatelská rozhraní. S nástupem mnoha různých vývojových trendů se vývojáři samozřejmě inspirovali novým rozhraním, které mapy od Google používaly (a také ostatními ajaxovými aplikacemi). Mnoho webových vývojářů pak dokázalo prostřednictvím reverzního inženýrství vložit Google Maps do svých vlastních stránek, čímž v podstatě všem ukázali ohromné možnosti Ajaxu. Ačkoliv to nebylo nijak na škodu, tato skutečnost otevřela společnosti Google oči, což vedlo ke zveřejnění API Google Maps. Společnosti Yahoo!, Microsoft a MapQuest pak samozřejmě následovaly tohoto příkladu (stejně jako předtím), takže i ony poskytly ajaxové API pro práci s mapami, což zaplavilo svět mnoha různými technologiemi pro vložení map do webových stránek.

Kapitola 10.indd 337

15.10.2007 14:09:41


338

Kapitola 10 – Práce s API pro mapy

Vzestup mashups Blízký příbuzný různých typům API pro mapy je tzv. koncept mashups. Mashups jsou webové aplikace, které na jednom místě kombinují informace z mnoha zdrojů tak, aby vám poskytly nové uživatelské prožitky. Tyto informace přitom nejsou umístěny v jediném zdroji – je tomu právě naopak. Informace se získávají z mnoha různých zdrojů, které zveřejňují užitečné informace např. prostřednictvím webových služeb, RSS atd. Mashups tradičně kombinují tyto informace s mapou. Služba Chicago Crime (www.chicagocrime.org) je obecně považována za první mashup, který zkombinoval informace o kriminalitě v Chicagu s mapou vygenerovanou pomocí API Google Maps. Je zajímavou skutečností, že vývojáři této služby integrovali Google Maps do své aplikace dlouho před tím, než bylo dostupné oficiální API od Google. Postupem času (a za použití neustále se rozvíjejícího API Google Maps) se Chicago Crime rozrostlo do rozsáhlé aplikace, která pokrývá téměř všechny aspekty zločinu v oblasti Chicaga s rozdělením typu zločinu na pouliční, okresní a mnohé další. Dalším populární projektem ve sféře mashups byl Housing Maps (www.housingmaps.com), který kombinuje seznamy ubytování z Craig’s List (www.craigslist.org) s mapou vygenerovanou pomocí Google Maps. Mapa je používána pro zobrazení lokace, kde jsou navíc umístěny různé potřebné informace, jako třeba adresy či fotografie dostupných ubytovacích kapacit. Abyste mohli vytvořit mashup s mapou, musíte mít přístup k informacím o poloze. Většina takových informací je reprezentována fyzickou adresou ulice. Tyto adresy musí být asociovány ke specifickým pozicím na mapě, což je možné udělat prostřednictvím techniky geokódování.

Geokódování Geokódování je proces, během kterého jsou informace spojovány s konkrétními geografickými body ve světě. Tyto body jsou identifikovány pomocí stupňů v zeměpisné šířce a délce, které si možná pamatujete ze základní školy jako severní-jižní a východní-západní míry (v tomto pořadí). Možná vás překvapí, že většina API pro práci s mapami nezná pozici adres jako takových – znají pouze pozici bodu, který je dán zeměpisnou šířkou a délkou. Všechny adresy musejí být převedeny na množinu bodů, a teprve pak mohou být tyto body zobrazeny na mapě. Všechny API pro práci s mapami vyžadují použití desetinného čísla pro vložení hodnoty zeměpisné šířky i délky. To je pravděpodobně odlišné od toho, co jste se naučili ve škole, kde se zeměpisná šířka a délka udávala pomocí stupňů, minut a sekund. Pokud máte pozici v tomto formátu, budete muset použít nějaký převaděč pro získání hodnot v desetinném formátu, které pak můžete následně použít pro dané API.

Kapitola 10.indd 338

15.10.2007 14:09:45


Ajax Profesionálně

339

Stránky pro geokódování Většina zemí poskytuje geokódovanou informaci o terénu. Ve Spojených státech například U.S. Census Bureau geokóduje téměř všechny dálnice a pozemní silnice v zemi. Tato data jsou k dispozici veřejnosti a mohou být přístupna skrze systém Tiger (Topologically Integrated Geographic Encoding and Referencing system, www.census.gov/geo/www/tiger). Procházení všech těchto informací je ovšem obtížným procesem, protože pouze pro Spojené státy mají tato data více než 20 GB. S novým zájmem o mapy a mashups se na webu objevilo mnoho nových služeb, které poskytují snadnější přístup k těmto informacím.

Geocoder.us (www.geocoder.us). Tato webová stránka umí vrátit zeměpisnou šířku a délku jakékoliv adresy ve Spojených státech. Jednoduše jděte na stránku a vložte adresu. Vrácená informace poskytuje oba formáty pozice: stupně/minuty/sekundy i desetinný formát.

Travel GIS (www.travelgis.com/geocode). Tato stránka nabízí geokódovanou informaci pro 24 zemí prostřednictvím velmi jednoduchého rozhraní. Adresy jsou vráceny pouze v desetinném formátu.

WorldKit GeoCoder (http://brainoff.com/geocoder). Je to jednoduchá webová stránka, kde můžete vložit adresu a získat zeměpisnou šířku a délku v desetinném formátu společně s některými dalšími informacemi. Tato stránka vám poskytne mapu světa a přesně určí každou pozici, kterou jste do mapy vložili pomocí červené tečky. Mapu si můžete přiblížit či oddálit, přičemž stačí kliknout do mapy pro získání požadovaných zeměpisných údajů.

Služby pro geokódování Ačkoli jsou stránky poskytující geokódované informace velmi užitečné, jsou pouze minimem pro vytvoření samotného mashup. Většina mashup totiž vyžaduje dynamické vyhledávání geokódovaných informací, protože uživatel je s aplikací v přímé interakci. V tomto případě vám pomůže několik webových geokódovacích služeb, které nabízejí vyhledávání adres v reálném čase:

Yahoo! Maps Geocoding Service (http://developer.yahoo.com/maps/rest/V1/geocode.html). Tato služba vrací XML kód, který obsahuje zeměpisnou šířku a délku, adresu ulice, města, státu a zip kód dané adresy. Tato služba je určena pro nekomerční účely, takže jste omezeni na 5 000 vyhledávání za den. Pro získání Yahoo! Application ID se navíc musíte registrovat na http://api.search.yahoo.com/webservices/register_application.

Google Maps Geocoding Service (www.google.com/apis/maps/documentation/#Geocoding_Examples). Toto API může být nastaveno tak, aby vracelo data v XML, KML (Google’s Keyhole Markup Language), CSV nebo JSON, přičemž vrací všechny informace o dané adrese – její souřadnice a plnou informaci o adrese (země, zip kód atd.). Stejně jako v případě Yahoo!, je i tato služba určena pro nekomerční použití. Zde je ovšem limit 50 000 hledání na den. Před použitím Google Maps Geocoding Service se musíte zaregistrovat na www.google.com/apis/maps/signup.html, abyste získali klíč k API. Toto API v neposlední řadě poskytuje i javascriptový přístup pro geokódování informací.

Kapitola 10.indd 339

15.10.2007 14:09:46


340

Kapitola 10 – Práce s API pro mapy

API Google Maps Když se poprvé objevily Google Maps, byly obětí mnoha hackerů. Tito lidé byli totiž fascinováni touto novou generací ajaxových aplikací, která uměla věci, jež předtím nikdo neviděl. Vývojáři z celého světa chtěli vědět, jak tato aplikace pracuje a jak by to mohli využít pro sebe. Ačkoliv tito hackeři nakonec neudělali společnosti Google žádné škody, otevřelo to oči lidem v Mountain View v Kalifornii, takže nějakou chvíli poté bylo zveřejněno API Google Maps a nabídnuto veřejnosti.

Jak API pracuje? API Google Maps je jedno z nejzajímavějších využití Ajaxu v tom ohledu, že nepotřebuje používat XHR nebo plovoucí rámce (alespoň ne nezbytně). API místo toho používá dynamické vlastnosti obrázků pro získání nových informací ze serveru na požádání. Ačkoli nepoužívá obrázkovou techniku popsanou v kapitole 2, pracuje se stejnou základní myšlenkou – zdroje obrázků se mohou v kteroukoliv chvíli změnit. API Google Maps používá tuto funkčnost pro vytvoření iluze posunu jednoho velkého obrázku, i když ve skutečnosti je to tak, že se načítají pouze malé části celého obrázku, čímž je vytvořen dojem posunu obrázku mnohem většího. Prvotní pohled na mapu je rozdělen do několika obrázků, které jsou umístěny vedle sebe, čímž vyvolávají dojem jednoho velkého obrázku. Když je mapa poprvé načtena, API určuje, kolik těchto obrázků je nezbytných pro kompletní vyplnění kontejneru mapy. Obrázky jsou uspořádány v mřížce, která přesahuje okraje kontejneru mapy. Pokud je mapa uživatelem přiblížena, každý z obrázků má nastavenou novou URL adresu, což načte nové obrázky do HTML prvků <img>. Tím je vytvořena iluze asynchronního přiblížení – ve skutečnosti je ovšem nový obraz položen na starý, což je nahrazovací technika, která existuje už od konce devadesátých let. Když je mapa uživatelem posunuta do stránky, díky zajímavě použitému JavaScriptu to vypadá, jako by mapa byla nekonečným obrázkem. Obrázky se skutečně pohybují tak, jak uživatel táhne myší, ale jakmile obrázky zmizí z viditelného prostoru mapy, jsou odstraněny a umístěny na jiný konec mapy. Například obrázky, které zmizí z pravé strany mapy, jsou umístěny mimo viditelnou oblast levé strany mapy a obrázky, jež zmizí ze spodní části mapy, jsou umístěny mimo viditelnou oblast horní strany mapy. A právě tímto je vytvořena iluze, kdy uživatel si myslí, že posunuje jeden velký obrázek. Joel Webber, jeden z prvních vývojářů, který se snažil zjistit, jak Google Maps fungují, přirovnal tuto techniku k výstavbě železniční trati, kdy se vezme jeden kus z konce a přemístí se na začátek – obrázky nejsou vytvářeny, a ani rušeny. Jsou pouze přemisťovány dokola. Na pozadí sice existuje ještě několik dalších činností, které zajišťují různé dodatečné funkce pro mapu, ale většina práce je prováděna samotnými obrázky.

Začínáme Pro začátek potřebujete mít nějaký Google účet (například pro přístup k Gmailu). Pokud ho ještě nemáte, jděte na adresu www.google.com/accounts, kde získáte informace, jak se zaregistrovat.

Kapitola 10.indd 340

15.10.2007 14:09:46


Ajax Profesionálně

341

Dalším krokem je zajít na adresu www.google.com/apis/maps/signup.html a získat klíč k API. K tomu musíte zadat URL adresu, která se bude odkazovat na umístění, kde bude API použito. Toto umístění je konkrétním adresářem na vašem serveru, takže adresy www.mydomain.com/maps1 a www.mydomain.com/maps2 budou vyžadovat dva samostatné klíče. Google Maps API má některá významná omezení, kterých byste si měli být vědomi:

API je pouze pro nekomerční účely. K získání komerční licence musíte kontaktovat přímo společnost Google.

Stránka používající API Google Maps nemá žádné omezení ohledně počtu zobrazení této stránky, ovšem pokud předpokládáte více než půl milionu zobrazení za den, měli byste kontaktovat společnost Google pro získání komerční licence.

Není dovoleno vkládat reklamy, které se objeví uvnitř zobrazovacího pole mapy.

Vaše stránka musí vždy používat nejnovější verzi API. Společnost Google obecně dává uživatelům měsíc na přechod na nově vydanou verzi.

A dále potřebujete jeden soubor JavaScriptu, který je nutný k tomu, abyste mohli začít používat API Google Maps. Na rozdíl od jiných API zde není možné stahovat soubory lokálně. Místo toho musíte zpřístupnit soubor, který je umístěný na serveru Google Maps. Tento soubor musí obsahovat verzi a váš klíč v následujícím formátu: http://maps.google.com/maps?file=api&v={version}&key={your-key}

Například – pokud je nejnovější verze 2, měli byste mít ve stránce obsažen následující kód: <script type="text/javascript" src="http://maps.google.com/maps?file=api&v=2&key={your key}"></script>

Jakmile je tento soubor vložen do stránky, můžete začít vytvářet svou aplikaci.

Základy Google Maps Hlavní objekt v API Google Maps se jmenuje GMap2. Konstruktor přijímá jeden argument, kterým je prvek, jenž by měl obsahovat mapu. Je doporučováno, aby tento obalový prvek byl prvek <div> – tak se zajistí nejlepší kompatibilita a rozšiřitelnost. Tento prvek <div> může být ostylován obvyklým způsobem, ovšem jako minimum musí mít nastavenu výšku a šířku. Objekt GMap2 je dost inteligentní na to, aby mohl pracovat uvnitř stylů nastavených pro obalující prvek <div>, takže nebudete muset přistupovat ke kompromisům ohledně designu vaší stránky kvůli zobrazení mapy. Pro vytvoření mapy za použití prvku <div> s identifikátorem divMap použijte následující kód: var oMap = new GMap2(document.getElementById("divMap"));

Jakmile je vytvořen objekt mapy, musíte inicializovat pohled na danou oblast. To je provedeno zavoláním metody setCenter(), která přijímá dva argumenty – bod udávající zeměpisnou šířku/ výšku a úroveň přiblížení. První argument musí být objekt GlatLng (který je vytvořen předáním

Kapitola 10.indd 341

15.10.2007 14:09:46


342

Kapitola 10 – Práce s API pro mapy

zeměpisné šířky a délky v desetinném formátu). Druhý argument je úroveň přiblížení, kde 0 je úplné oddálení, přičemž jakékoliv číslo větší než 0 odkrývá více detailů na mapě. Například následující kód soustřeďuje mapu na Spojené státy tak, aby byla vidět celá země: var oMap = new GMap2(document.getElementById("divMap")); oMap.setCenter(new GLatLng(32, -92), 3);

Existují i prohlížeče, které nemusí API Google Maps podporovat, takže před tvorbou nového objektu GMap2 je nejlepší provést kontrolu za použití funkce GBrowserIsCompatible(): if (GBrowserIsCompatible()) { var oMap = new GMap2(document.getElementById("divMap")); oMap.setCenter(new GLatLng(32, -92), 3); }

Tyto čtyři řádky kódu jsou vším, co potřebujete k umístění jednoduché mapy na stránku (podívejte se na obrázek 10-1).

Obrázek 10-1. Mapa USA. Mapa, která je zobrazena pomocí tohoto kódu, je velmi jednoduchá a hodně omezená. Zatímco mapa Spojených států je dobře viditelná, nabízí velmi málo možností pro interakci s uživatelem. Je sice možné pohybovat zobrazovacím polem mapy, ale to je tak zhruba všechno, co nám tato mapa nabízí. Takže přidejme do mapy nějaké ovládací prvky, které zvýší její schopnosti.

Kapitola 10.indd 342

15.10.2007 14:09:46


Ajax Profesionálně

343

Ovládání Oficiální rozhraní Google Maps na http://maps.google.com nabízí velké množství možností, jak může uživatel manipulovat s mapou. Každá z těchto možností je řízena jiným ovládacím prvkem. API Google Maps poskytuje mnoho standardních ovládacích prvků, které mohou být použity buď pro implementaci plného rozhraní Google Maps, nebo pouze pro vybrané součásti, jež jsou nezbytné pro vaše záměry:

GLargeMapControl. Obvyklý ovládací prvek pro posun a přiblížení, který je zobrazen na http://maps.google.com.

GSmallMapControl. Zmenšená verze předchozího ovládacího prvku. Obsahuje pouze ikon-

ky plus/minus a ovládání směru (ovšem bez posuvníku pro přiblížení).

GSmallZoomControl. Zobrazí posuvník přiblížení, ale bez ovladačů pro směr.

GScaleControl. Stupnice udávající jednotky v mílích a kilometrech.

GOverviewMapControl. Oddálený pohled na mapu se zvýrazněným aktuálním zobrazova-

cím polem.

GMapTypeControl. Ovládací prvek pro výběr typu mapy (klasická mapa, satelitní mapa,

hybridní mapa). Jeden nebo více z těchto ovládacích prvků mohou být přidány do mapy metodou addControl(). Každý z těchto ovládacích prvků může být vytvořen bez parametrů a vložen do této metody: oMap.addControl(new GSmallMapControl());

Ačkoli toto se provádí nejčastěji po vytvoření objektu GMap2, ovládací prvek může být přidán v kteroukoliv dobu. Ovládací prvek může být odstraněn s použitím metody removeControl(), pokud tedy na něj máte referenci: var oControl = new GSmallMapControl(); oMap.addControl(oControl); // dělej nějaký úkol oMap.removeControl(oControl);

První tři ovládací prvky, GLargeMapControl, GSmallMapControl a GSmallZoomControl, by neměly být použity dohromady, protože všechny zabírají stejné místo na mapě (horní levý roh). GmapTypeControl může být bezpečně použit s libovolným, protože zabírá horní pravý roh. Pokud chcete, aby vaše mapa měla ovládací prvky hned od počátku, měli byste je přidat ihned po vytvoření objektu GMap2, ale současně před tím, než zavoláte setCenter(), například: if (GBrowserIsCompatible()) { var oMap = new GMap2(document.getElementById("divMap")); oMap.addControl(new GSmallMapControl()); oMap.addControl(new GMapTypeControl());

Kapitola 10.indd 343

15.10.2007 14:09:46


Kapitola 10 – Práce s API pro mapy

344

oMap.setCenter(new GLatLng(32, -92), 3); }

Přidání těchto ovládacích prvků přináší mapu jako na obrázku 10-2.

Obrázek 10-2. Vylepšená mapa s ovládacími prvky.

Posouvání mapy Použitím několika metod objektu GMap2 je možné dynamicky ovládat pohled na mapu, která byla jednou načtena. Ačkoli můžete uživateli zpřístupnit různé ovládací prvky mapy pro přibližování a pohyb mapy, někdy může být nezbytné ovládat mapu odděleně. Všechna činnost, která může být vykonána prostřednictvím ovládacích prvků, může být vykonána i přímo – zavoláním metody JavaScriptu pro dané chování. Metoda setCenter() byla použita dříve pro inicializaci zobrazení mapy, ale může být také kdykoliv použita znovu pro zacílení mapy na konkrétní bod. Nové zacílení proběhne okamžitě a bez animace. Pro hladší přechod k novému bodu mapy existuje několik následujících metod:

panBy (vzdálenost). Specifikuje vzdálenost (jako GSize), o kterou by se mapa měla posu-

nout.

panDirection(x, y). Specifikuje směr, kterým by se mapa měla posunout. Argument x by měl být -1 pro pohyb vlevo, 0 aby se mapa nepohybovala, nebo 1 pro pohyb vpravo. Argument y by měl být -1 pro pohyb nahoru, 0 aby se mapa nepohybovala, nebo 1 pro pohyb směrem dolů.

panTo(center). Specifikuje objekt GlatLng, který by měl být novým středem mapy. Mapa

provede animovaný přesun na tuto pozici (stejně jako setCenter() s výjimkou animace).

Kapitola 10.indd 344

15.10.2007 14:09:46


Ajax Profesionálně

345

Tyto metody mohou být použity kdykoliv pro pohyb mapou na novou pozici, například: oMap.panBy(new GSize(20,20));

//Posuň mapu o 20 pixelů v obou směrech

oMap.panDirection(1, 0);

//Posuň mapu doprava

oMap.panTo(new GLatLng(50, -80));

//Posuň mapu na danou pozici

Informační okna (bubliny) Informační okna poskytují dodatečné informace o bodu na mapě. Na stránkách Google Maps jsou tato okna použita pro poskytnutí adresy o bodu na mapě, ale mohou být použita i pro více účelů. Informační okna vypadají jako dialogové bubliny z kreslených komiksů – zaoblená bílá bublina, která ukazuje na dané místo na mapě (podívejte se na obrázek 10-3).

Obrázek 10-3. Informační okno na mapě.

Základy informačních oken Informační okno může být pomocí metody openInfoWindow() objektu GMap2 otevřeno kdykoliv. Tato metoda přijímá tři argumenty: objekt GlatLng udávající, kde by mělo být informační okno zobrazeno, uzel DOM poskytující obsah informačního okna a nepovinně konfigurační objekt. Pro otevření jednoduchého informačního okna ve středu mapy použijte následující kód: oMap.openInfoWindow(oMap.getCenter(), document.createTextNode("Center of the map!"));

Metoda getCenter() objektu GMap2 vrací objekt GlatLng pro střed mapy, což zajistí, že informační okno bude v tomto případě směřovat přesně ke středu. Ačkoliv toto informační okno zobrazuje

Kapitola 10.indd 345

15.10.2007 14:09:46


Kapitola 10 – Práce s API pro mapy

346

pouze text, je pořád nutné vložit jako druhý argument uzel DOM, takže textový uzel je vytvořen se zprávou k zobrazení. Následuje druhá metoda, openInfoWindowHtml(), která dovoluje, aby jako tělo informačního okna byl vložen HTML řetězec místo uzlu DOM. Tato metoda přijímá tři stejné argumenty (bod, na který má být ukázáno, obsah okna a nepovinný konfigurační objekt). Metoda je volána takto: oMap.openInfoWindowHtml(oMap.getCenter(), "<em>Center of the map!</em>");

Tento příklad otevře informační okno s textem zvýrazněným pomocí kurzívy (za předpokladu, že zde nejsou použity styly CSS, které by přepisovaly výchozí zobrazení prvku <em>). Takto je možné vytvořit text za běhu a zobrazit jej v informačním okně bez potřeby vytvářet objekty DOM.

Konfigurační volby Třetím argumentem výše zmíněných metod je konfigurační objekt pro informační okno. Tento objekt obsahuje navíc jednu z následujících vlastností:

maxWidth. Maximální dovolená šířka informačního okna v pixelech.

onCloseFn. Funkce, která se má zavolat po zavření informačního okna.

onOpenFn. Funkce, která se má zavolat po otevření informačního okna.

Tento konfigurační objekt může být zahrnut přímo v konstruktoru, například: oMap.openInfoWindowHtml(oMap.getCenter(), "<em>Center of the map!</em>", { onCloseFn: function() { alert("Closed") } });

Když spustíte tento kód, je zobrazena výstraha poté, co uživatel klikne na tlačítko pro zavření informačního okna. Obecně řečeno – nastavení vlastnosti onCloseFn je nejužitečnější z dostupných možností, protože poskytuje možnost zachytit jinak nevypátratelné události. Vlastnost MaxWidth může být nahrazena prostřednictvím CSS a vlastnost onOpenFn může být jednoduše napodobena zavoláním náležité funkce ihned po zavolání openInfoWindow(), nebo openInfoWindowHtml(), protože obě jsou synchronní operace.

Informační okna se záložkami Novým vylepšením API Google Maps verze 2 je informační okno se záložkami, které může být použito pro zobrazení více informací o konkrétním bodě na mapě, aniž by to zabralo nějaký velký prostor navíc (viz obrázek 10-4).

Kapitola 10.indd 346

15.10.2007 14:09:46


Ajax Profesionálně

347

Obrázek 10-4. Informační okno se záložkami. Stejně jako obyčejná informační okna může být i verze se záložkami vytvořena dvěma metodami: openInfoWindowTabs() a openInfoWindowTabsHtml(). Obě tyto metody přijímají tři argumenty: objekt GlatLng specifikující bod, na který má být ukázáno, pole objektu GinfoWindowTab reprezentující záložky a nepovinný konfigurační objekt. Rozdíl mezi těmito dvěma metodami má co dělat s daty dostupnými uvnitř každého objektu GinfoWindowTab. Když používáte openInfoWindowTabs(), každý objekt GinfoWindowTab musí být vytvořen za použití řetězce pro titulek záložky a uzlu DOM pro obsah záložky. Metoda openInfoWindowTabsHtml() očekává, že každý GinfoWindowTab bude vytvořen za použití řetězce pro titulek záložky a řetězce pro jeho obsah, který může obsahovat i HTML kód. Tento kód vytvoří informační okno se dvěma záložkami: var aTabs = [ new GInfoWindowTab("First tab", document.createTextNode("First tab text")), new GInfoWindowTab("Second tab", document.createTextNode("Second tab text")) ]; oMap.openInfoWindowTabs(oMap.getCenter(), aTabs);

První část tohoto kódu vytváří pole obsahující dva objekty GinfoWindowTab, jejichž obsahem jsou dva textové uzly. Toto pole je pak předáno jako druhý argument openInfoWindowTabs() pro zobrazení informačního okna. Pro zobrazení naformátovaného HTML textu místo prostého textu použijte metodu openInfoWindowTabs() a připojte obsah záložky jako řetězec: var aTabs = [ new GInfoWindowTab("First tab", "<em>First</em> tab text"), new GInfoWindowTab("Second tab", "<em>Second</em> tab text") ]; oMap.openInfoWindowTabsHtml(oMap.getCenter(), aTabs);

Kapitola 10.indd 347

15.10.2007 14:09:47


348

Kapitola 10 – Práce s API pro mapy

Tento kód vytváří výsledek, který jste viděli na obrázku 10-4. Všimněte si, že pouze tři řádky se změnily – dva řádky definující objekty GinfoWindowTab a jeden řádek s voláním metody. Konfigurační objekt může obsahovat stejné možnosti jako konfigurační objekt, který byl použit s obyčejnými informačními okny, ale také další vlastnost selectedTab. Hodnotou této vlastnosti je celé číslo udávající záložku, která by měla být vybrána, když je informační okno zobrazeno ve výchozím stavu – standardní hodnota je 0, což vybírá první záložku. Abychom vybrali druhou záložku, následující kód přidává konfigurační objekt s vlastností selectedTab nastavenou na 1: var aTabs = [ new GInfoWindowTab("First tab", "<em>First</em> tab text"), new GInfoWindowTab("Second tab", "<em>Second</em> tab text") ]; oMap.openInfoWindowTabsHtml(oMap.getCenter(), aTabs, { selectedTab: 1 });

Zvětšeniny mapy Zvětšenina mapy je speciální typ informačního okna, které ukazuje přiblížený pohled na konkrétní bod na mapě. Obsahem tohoto informačního okna je menší verze hlavní mapy, která je doplněna o tlačítka pro změnu typu mapy a pro ovládání přiblížení (viz obrázek 10-5).

Obrázek 10-5. Zvětšenina mapy. Metoda showMapBlowup() je použita pro otevření informačního okna se zvětšeninou mapy. Tato metoda přijímá dva argumenty: objekt GlatLng, který specifikuje bod, na nějž má být ukázáno a nepovinný konfigurační objekt. Například pro zobrazení zvětšeniny středu mapy použijte následující kód:

Kapitola 10.indd 348

15.10.2007 14:09:47


KAPITOLA 15 Případová studie: FooReader.NET Kapitola 7 se zabývala syndikací obsahu pomocí RSS a Atom a tím, jak jednoduše sdílet informace. Za účelem zobrazit informace z několika různých zdrojů, volala aplikace tzv. agregátor, který zkombinoval různé zdroje do jediné lokace. Agregátor usnadňuje získávání aktuálních informací posbíraných z celého webu, což je jednodušší, než navštěvovat spoustu stránek každý den. FooReader.NET je webově založený agregátor .NET RSS/Atom vycházející z ColdFusionbased Fooreader (http://reader.forgetfoo.com/) od ForgetFoo. Proč ovšem budovat webový RSS/ Atom agregátor, když už existuje mnoho tradičních aplikací se stejnou funkcionalitou (včetně e-mailových aplikací a prohlížečů)? Důvodů je několik:

Web je multiplatformní. Agregátorem založeným na webu zajistíte, že jej bude moci používat kterýkoliv uživatel s Internet Explorerem 6+, Firefoxem nebo Operou.

Web je umístěný centrálně. Jedním z problémů tradičních agregátorů, které jsou nainstalovány na počítači, je oblast správy dat při více lokacích. Pokud chcete číst dané zdroje v práci a současně doma, musíte nainstalovat agregátor na dvou počítačích a nastavit jim požadované zdroje. Agregátor založený na webu tento problém odstraňuje, protože každá změna v seznamu zdrojů je vidět vždy, bez ohledu na to, odkud uživatel přistupuje k agregátoru.

Tato kapitola popisuje, jak je FooReader.NET vytvořen pomocí Ajaxu. A jak už nyní asi tušíte – jako každá webová aplikace se bude i tato aplikace skládat ze dvou hlavních druhů komponent: z komponent na straně klienta a z komponent na straně serveru. Pokud máte nainstalovanou nějakou verzi Visual Studia, otevřete ji a vytvořte novou webovou stránku nazvanou FooReader. Ujistěte se, že použitý jazyk je C#.

Kapitola 15.indd 523

15.10.2007 14:12:16


Kapitola 15 – Případová studie: FooReader.NET

524

Komponenty na straně klienta Komponenty na straně klienta jsou v tomto ajaxovém řešení zodpovědné za komunikaci se serverem a zobrazování přijatých dat uživateli. Pro FooReader.NET je nezbytných několik následujících komponent na straně klienta, aby se dosáhlo těch správných uživatelských prožitků.

Uživatelské rozhraní. UI svazuje uživatele s daty. Protože UI je v podstatě webová stránka, jsou použity obvyklé technologie podporované ve webových prohlížečích. Kód stránky je vytvořen pomocí HTML. CSS je pak použito pro vylepšení celkového dojmu.

XParser. Javascriptová knihovna odpovědná za požadavky na zdroje a jejich analýzu.

Javascriptový kód. Řídí UI, získává data od XParseru a zobrazuje je uživateli. Tento kód bude obsažen v souboru fooreader.js.

Uživatelské rozhraní Klíč ke každé úspěšné aplikaci spočívá v návrhu uživatelského rozhraní. Když uživatel neumí aplikaci ovládat, není důvod, aby taková aplikace existovala. FooReader.NET je navržen pro jednoduché použití. Ve skutečnosti dost silně těží z uživatelského rozhraní Microsoft Outlook 2003 (a pozdějších verzí). Jak sami vidíte na obrázku níže, jeho rozhraní se skládá ze tří části. První dvě části mají pevnou šířku, zatímco třetí část má šířku proměnlivou (viz obrázek 15-1).

Obrázek 15-1. Uživatelské rozhraní aplikace.

Kapitola 15.indd 524

15.10.2007 14:12:25


Ajax Profesionálně

525

Rozhraní je obsaženo v souboru default.htm a jeho kód je následující: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xml:lang="en" lang="en" xmlns="http://www.w3.org/1999/xhtml"> <head> <title>FooReader.NET (Version 1.5)</title> <link rel="stylesheet" type="text/css" href="css/FooReader.css" /> <script type="text/javascript" src="js/zxml.src.js"></script> <script type="text/javascript" src="js/XParser.js"></script> <script type="text/javascript" src="js/FooReader.js"></script> </head> <body> <div id="divLoading"> <img src="img/progress.gif" alt="Loading" /> </div> <div id="divTopBar"> <img src="img/top_logo.gif" alt="FooReader.NET" /> <div id="divLicense"> <a href="http://creativecommons.org/licenses/by-nc-sa/2.5/" title="Some Rights Reserved" target="_blank">License</a> </div> </div> <div id="divPaneContainer"> <div id="divFeedsPane"> <div class="paneheader">Feeds</div> <div id="divFeedList"></div> </div> <div id="divItemsPane"> <div id="divViewingItem" class="paneheader">Items</div> <div id="divItemList"></div> </div> <div id="divReadingPane"> <div id="divMessageContainer"> <div id="divMessageHeader"> <div id="divMessageTitle"></div> <a href="" id="aMessageLink" title="Click to goto posting." target="_new">Travel to Post</a> </div> <div id="divMessageBody"></div> </div> </div>

Kapitola 15.indd 525

15.10.2007 14:12:25


Kapitola 15 – Případová studie: FooReader.NET

526 </div> </body> </html>

Prvními dvěma přímými potomky těla dokumentu jsou prvky <div> s identifikátory divLoading a divTopBar. První prvek zajišťuje ten úkol uživatelského rozhraní, který uživateli říká, že aplikace načítá zdroj. Objevuje se vždy, když je učiněn požadavek na server aplikace a schová se, když server odpoví a odpověď je zpracována. Prvek s ID divTopBar se snaží napodobit titulkový řádek z tradičních aplikací Windows a říká uživateli, že aplikace, kterou nyní používá, je FooReader.NET (a současně poskytuje odkaz na licenci Creative Commons, pod níž je aplikace zveřejněna). Dále je tu prvek s identifikátorem divPaneContainer. Jak už jeho jméno napovídá, v tomto prvku jsou obsaženy všechny tři panely aplikace. Tento kontejnerový prvek tak dovoluje, aby bylo možné ostylovat všechny tyto tři panely současně (na rozdíl od aplikace stylu pro každý prvek zvlášť). První panel, který je pojmenován jako divFeedsPane, zobrazuje jednotlivé zdroje v podobě odkazů, na něž může uživatel kliknout. Vnořený prvek <div> s identifikátorem divFeedList v panelu se zdroji dovoluje, aby seznam zdrojů byl do dokumentu vepsán dynamickým způsobem. Samotné zdroje jsou bloky prvků <a> s CSS třídou feedlink. HTML kód těchto odkazů je zde: <a href="[url of feed]" class="feedlink" title="Load [feed title]">Feed title</a>

Když uživatel klikne na nějaký zdroj, bude daný zdroj načten do druhého prostředního panelu. Tento panel obsahuje dva hlavní prvky <div>, které jsou používány pro zobrazování informací:

První je prvek <div> s id nastaveným na divViewingItem. Jedná se vlastně o hlavičku tohoto druhého panelu. Tento prvek totiž zobrazuje, který zdroj je právě načten.

Druhý prvek je <div>, jehož atribut id je nastaven na divItemList. Tento prvek bude obsahovat seznam RSS prvků <item> nebo Atom prvků <entry>. Oba tyto druhy prvků jsou do divItemList přidány dynamicky.

HTML struktura jednotlivých položek z druhého panelu je následující: <a class="itemlink" href="[item url]" frfeeditem="[item number]" id="item[number]"> <div class="itemheadline">[Headline]</div> <div class="itemdate">[Date]</div> </a>

Toto HTML je zcela standardní, kromě atributu frfeeditem prvku <a>. Když je zdroj načten a položka je přidána do stránky, je každé položce přiřazeno číslo, pomocí kterého je identifikována. Když uživatel klikne na nějakou položku, bude načtena do posledního třetího panelu s identifikátorem divReadingPane. Tento panel se skládá ze tří prvků, které zobrazují informace z dané položky. První prvek s identifikátorem divMessageTitle je místem, kde jsou zobrazeny prvky <title> ze zdroje RSS nebo Atom. Druhý prvek má ID aMessageLink a jeho atribut href je

Kapitola 15.indd 526

15.10.2007 14:12:25


Ajax Profesionálně

527

měněn dynamicky. A konečně – poslední prvek je divMessageBody a zobrazuje samotný obsah prvků <rss:description> a <atom:content>. Tato stránka vyžaduje jeden stylový předpis (soubor FooReader.css) a tři následující javascriptové soubory: knihovnu zXML, XParser a soubor FooReader.js, který obsahuje veškerou funkčnost u klienta. Protože FooReader.NET používá XParser, nebude tato aplikace pracovat v Safari. Ovšem bude bez problémů pracovat v IE 6+, ve Firefoxu 1+ a v Opeře 9+, což jsou ty nejpoužívanější webové prohlížeče.

Kompletního uživatelského rozhraní je dosaženo kombinací CSS a JavaScriptu. CSS nastavuje velikost těla dokumentu (což je nezbytné pro Operu), velikost všech tří panelů a samozřejmě i vzhled všech ostatních prvků. JavaScript pak umisťuje prvky do panelů.

Stylování rozhraní Jediný použitý stylový předpis ve FooReader.NET je v souboru FooReader.css, který je umístěn v adresáři css. Pro naši aplikaci je klíčová velikost zobrazovacího pole v prohlížeči. Stránka je explicitně nastavena tak, aby používala všechno dostupné vertikální místo v okně prohlížeče. html, body { height: 100%; margin: 0px; overflow: hidden; background-color: gray; font: 11px verdana, arial, helvetica, sans-serif; }

Vlastnost height je nastavena na 100%. Tato vlastnost je nezbytná kvůli Opeře. Vlastnost overflow je nastavena na hodnotu hidden, protože některé prvky ve stránce způsobují zobrazení posuvníků v okně prohlížeče. Naším cílem je dosáhnout toho, aby aplikace FooReader.NET vypadala jako normální aplikace a viditelné posuvníky na úrovni dokumentu takovému dojmu logicky brání. Posuvníky v jednotlivých panelech samozřejmě budou k dispozici.

Horní pruh Následující pravidla jsou pro divTopBar a pro další prvky v něm vnořené: /* Horní pruh */ #divTopBar { background: gray url("../img/top_bg.gif") repeat-x; height: 31px; padding-left: 25px;

Kapitola 15.indd 527

15.10.2007 14:12:26


528

Kapitola 15 – Případová studie: FooReader.NET

position: relative; }

Horní pruh bude mít šedou barvu pozadí, která bude ve shodě s šedým pozadím stránky. Pro tento horní pruh je dále specifikován obrázek na pozadí (soubor top_bg.gif). Levá strana má nastavenou výplň na 25 pixelů, což posune prvek <img> s logem doprava. Relativní pozicování horního pruhu dovoluje všem dceřiným prvkům (kterým je například prvek divLicense), aby mohly být pozicovány vůči tomuto prvku: #divLicense { position: absolute; right: 10px; top: 3px; } #divLicense a { color: white; padding: 1px 4px 2px 4px; } #divLicense a:hover { background: blue url("../img/toolbar_back.gif") repeat-x; border: 1px solid #000080; color: #000080; padding: 0px 3px 1px 3px; }

Horní pruh obsahuje pouze jeden odkaz – licenci. Tento odkaz je pozicován absolutně 10 pixelů od pravé hrany a 3 pixely od horní hrany prvku divTopBar. Tento odkaz s identifikátorem divLicense má nastavenou bílou barvu textu. Když na něj uživatel ukáže kurzorem myši, odkaz změní své pozadí na obrázek toolbar_back.gif, který se opakuje v horizontálním směru. Dále se objeví 1 pixel široké modré orámování, barva textu se změní na tmavě modrou, a výplň se zmenší o jeden pixel na každé straně. Tato změna velikosti výplně je nezbytná, protože tento prvek má specifikováno orámování. Z tohoto vyplývá, že kdybychom hodnoty výplně nezmenšili, odkaz by se ve skutečnosti zvětšil o 1 pixel na každé straně.

Informace o načítání stránky Ve zdrojovém HTML kódu je v prvku <div> s identifikátorem divLoading umístěn obrázek nazvaný jako progress.gif. Tento obrázek, který je 5 až 10 pixelů vysoký a široký něco málo pod 300 pixelů, zobrazuje animaci v podobě ukazatele s průběhem načítání stránky. Tento prvek <div> s ID divLoading je pozicován absolutně. Protože má vlastnost display nastavenou na hodnotu none, bude kompletně vyjmut z toku dokumentu, takže bude neviditelný. /* The loading <div/> */ #divLoading {

Kapitola 15.indd 528

15.10.2007 14:12:26


Ajax Profesionálně

529

position: absolute; display: none; top: 20%; left: 35%; width: 302px; z-index: 5; background: transparent url("../img/loading.gif") no-repeat; padding: 30px 10px; }

Pravděpodobně nejdůležitějším pravidlem v tomto stylu je vlastnost z-index. Protože tento prvek <div> je úplně prvním prvkem v těle dokumentu (z hlediska struktury HTML), je logicky překryt ostatními následujícími prvky ve stránce. Ovšem díky tomu, že nastavíte vlastnost z-index na hodnotu větší než 0, bude tento prvek <div> s obrázkovým ukazatelem průběhu umístěn "nad" ostatními prvky (z hlediska osy z), takže se pro uživatele stane viditelným (samozřejmě až po změně hodnoty vlastnosti display na block, což si popíšeme o několik stránek dále).

Obrázek 15-2. Informace o stavu načítání.

Styly pro panely Všechny tři panely jsou obsaženy uvnitř kontejnerového prvku <div> s ID divPaneContainer. Tento prvek umisťuje hromadně všechny panely na požadovanou pozici, což je 10 pixelů od levé hrany prohlížeče a 10 pixelů od horního pruhu s logem aplikace a licencí:

Kapitola 15.indd 529

15.10.2007 14:12:26


530

Kapitola 15 – Případová studie: FooReader.NET

#divPaneContainer { position: relative; top: 10px; left: 10px; }

Pozicování tohoto jediného kontejnerového prvku vás osvobozuje od jednotlivého otravného pozicování všech tří panelů. Panely se zdroji a položkami rovněž mají hlavičku ve své horní části. Tato hlavička dává uživateli vědět, jaký typ dat daný panel obsahuje. Hlavičky jsou 20 pixelů vysoké a jejich text je bílý a tučný. Zde je jejich styl: .paneheader { height: 20px; background-image: url("../img/header_background.gif"); font: bold 16px arial; color: white; padding: 2px 0px 2px 5px; letter-spacing: 1px; overflow: hidden; }

Levý panel se zdroji První panel úplně vlevo je panel, který obsahuje zdroje. CSS kód pro HTML prvek s identifikátorem divFeedsPane je velmi jednoduchý, jak ostatně ukazuje následující pravidlo: #divFeedsPane { float: left; width: 148px; border: 1px solid navy; background-color: white; overflow: hidden; }

Prvek je nastaven tak, aby byl 148 pixelů široký a byl plovoucí vlevo. Nastavení vlastnosti float posune prvek doprava nebo doleva. V tomto případě je divFeedsPane posunut doleva. To umístí prostřední panel s položkami doprava, ačkoliv jsou to oba blokové elementy. Aby panel měl pořád stejnou velikost, je jeho vlastnost overflow nastavena na hidden. Toto nastavení skryje jakýkoliv obsah, který by se mohl rozšířit za hranice panelu. Nicméně je žádoucí, aby obsahem panelu bylo možné rolovat. Tohle zajišťuje prvek s ID divFeedList, jehož definice stylu je následující: #divFeedList { padding: 5px 1px 5px 1px;

Kapitola 15.indd 530

15.10.2007 14:12:26


Ajax Profesionálně

531

overflow: auto; }

Tento prvek obsahuje odkazy na jednotlivé zdroje. Jeho vlastnost overflow je nastavena na hodnotu auto. Toto umožňuje, aby se v prvku divFeedList mohl objevit posuvník, pokud bude obsah tohoto prvku větší než je rozměr divFeedsPane. Posuvníky se objeví uvnitř divFeedsPane, a tím bude možné obsahem panelu rolovat, zatímco panel samotný se nezmění (viz obrázek 15-3).

Obrázek 15-3. Rolovací lišta uvnitř panelu. Odkazy v tomto panelu jsou stylovány jako blokové prvky. Mají specifikovanou výplň o velikosti 5px, aby byly vizuálně odděleny od okolních odkazů. a.feedlink { display: block; padding: 5px; font: bold 12px arial; text-decoration: none; color: #5583d3; } a.feedlink:hover { color: #3768B9; text-decoration: underline; }

Prostřední panel s položkami CSS styly pro prostřední panel s položkami se trochu podobají stylům pro levý panel se zdroji: #divItemsPane { float: left; width: 225px; border: 1px solid navy; background-color: white; margin-left: 5px; margin-right: 5px !important; margin-right: 2px;

Kapitola 15.indd 531

15.10.2007 14:12:26


532

Kapitola 15 – Případová studie: FooReader.NET

overflow: hidden; }

Tento panel je také plovoucí vlevo a jeho vlastnost overflow je rovněž nastavena na hidden. Pravý a levý okraj přidávají malou mezeru mezi oběma panely. Zde se ovšem vyskytne drobný problém, protože IE6 obsahuje chybu ohledně správné velikosti okrajů. Aby každý prohlížeč vykreslil UI stejně, musí být použita deklarace !important. Tu má nastavenou první vlastnost margin-right ve stylu. Toto v praxi znamená, že IE7, Firefox a Opera použijí hodnotu této vlastnosti bez ohledu na skutečnost, že ve stylu je ještě specifikována jedna vlastnost margin-right. Tuto druhou vlastnost margin-right použije IE6. Ano – je to jeden řádek ve stylu navíc, který tam v podstatě být nemusí, nicméně odměnou vám bude jednotný vzhled UI ve všech čtyřech prohlížečích. Položky v tomto panelu mají rovněž specifikovány své vlastní styly. Pokud se podíváte o několik stránek zpět na strukturu HTML kódu stránky, zjistíte, že jednotlivé položky jsou vlastně obyčejné prvky <a> s CSS třídou itemlink. Pro tyto odkazy chceme, aby měly dva stavy. První stav je pochopitelně jejich výchozí stav (před kliknutím kurzorem myši). Pokud uživatel ovšem klikne na nějakou položku, pak kód JavaScriptu změní jejich CSS třídu na itemlink-selected. Oba stavy pro odkazy (resp. položky) používají tuto shodnou definici CSS: a.itemlink, a.itemlink-selected { border-bottom: 1px solid #EAE9E1; background-image: url("../img/item_icon.gif"); background-repeat: no-repeat; cursor: pointer; text-decoration: none; display: block; padding: 2px; font: 11px tahoma; }

Následující styly pro položky prostředního panelu slouží pro vzájemné odlišení těchto dvou stavů. Povšimněte si, že je zde také definována pseudotřída :hover, která je určena pro normální stav: a.itemlink { background-color: white; color: #808080; } a.itemlink:hover { background-color: #D3E5FA; } a.itemlink:hover .itemheadline { color: black; } .itemheadline,.itemdate {

Kapitola 15.indd 532

15.10.2007 14:12:26


Ajax Profesionálně

533

margin-left: 20px; } a.itemlink-selected { background-color: #316AC5; color: white; }

Třetí panel pro čtení Třetí panel se od předchozích dvou panelů liší v tom, že nemá pevně specifikovanou šířku, jak to ostatně ukazuje následující kód CSS: #divReadingPane { margin: 0px 20px 0px 0px; border: 1px solid black; background-color: white; height: 100%; overflow: hidden; }

Prohlížeč automaticky nastaví šířku tohoto prvku tak, aby vyplnila zbývající volný prostor prvku divPaneContainer. Nastavení vlastnosti margin vytvoří pravý okraj o velikosti 20 pixelů. Stejně jako u ostatních panelů je i zde vlastnost overflow nastavena na hidden. Ovšem na rozdíl od ostatních dvou panelů je zde nastavena výška na 100%. Díky tomu bude mít tento panel stejnou výšku jakou má divPaneContainer. Přímým potomkem divReadingPane je divMessageContainer, který obsahuje jednotlivé prvky zpráv. Následující styl nastavuje výplň na 5 pixelů z každé strany. #divMessageContainer { padding: 5px; }

První částí zprávy je hlavička, která obsahuje nadpis článku a odkaz pro přesměrování uživatele ke kompletnímu textu článku. CSS kód je následující: #divMessageHeader { height: 34px; background-color: white; border-bottom: 1px solid #ACA899; padding: 8px; } #divMessageTitle { font: bold 16px arial; }

Kapitola 15.indd 533

15.10.2007 14:12:26


Kapitola 15 – Případová studie: FooReader.NET

534

#aMessageLink { font: 11px arial; }

Prvek s ID divMessageBody je místem, kde se zobrazuje obsah jednotlivých položek. Na tento prvek je aplikováno následující pravidlo CSS. #divMessageBody { background-color: white; padding: 0px 0px 0px 5px; font: 13px tahoma; overflow: auto; }

Tento styl mimo jiné zajišťuje, že se objeví posuvníky pro rolování obsahu, pokud výška obsahu bude větší, než výška třetího panelu pro obsah. Výška jednotlivých prvků, které obsahují obsah (tzn. prvky s identifikátory divFeedList, divItemList a divMessageBody, viz struktura HTML kódu, jež byla uvedena dříve v této kapitole)

není nastavována prostřednictvím CSS – je řízena pomocí JavaScriptu.

Řízení UI Kód JavaScriptu, který je obsažen v souboru fooreader.js, ovládá všechny aspekty UI. Získává seznam zdroje, analyzuje ho a naplňuje panel se zdroji. Dále vytváří objekty XParser pro požadavky, přijímá a analyzuje zdroje RSS a Atom a používá tyto informace pro naplnění položek a panelu pro čtení. Dále nastavuje velikost mnoha prvků UI při změně velikosti okna. Řečeno ve zkratce – jedná se o páteřní komponentu na straně klienta.

Pomocné funkce Ačkoliv je většina kódu obsažena v objektu fooReader, dvě funkce zůstanou samostatně, aby nám pomohly se změnou velikostí prvků. První funkce se jmenuje getStyle() a zajišťuje změnu hodnoty konkrétní vlastnosti stylu bez ohledu na použitý prohlížeč. Funkce přijímá dva argumenty – prvek a název CSS vlastnosti. function getStyle(oElement, sProperty) { var sStyle; if (typeof window.getComputedStyle == "undefined") { sStyle = oElement.currentStyle[sProperty]; } else { sStyle = getComputedStyle(oElement, "")[sProperty]; } return sStyle; }

Kapitola 15.indd 534

15.10.2007 14:12:27


Ajax Profesionálně

535

Kód používá vlastnost Internet Exploreru currentStyle a W3C metodu DOM getComputedStyle() pro získání hodnoty dané vlastnosti. Druhá funkce je getStyleName() a vykonává podobnou aktivitu. Rozdílem je to, že vrací celé číslo (integer) místo řetězce (string): function getStyleNumber(oElement, sProperty) { return parseInt(getStyle(oElement, sProperty)); }

Objekt fooReader Jak už bylo zmíněno dříve, objekt fooReader obsahuje většinu javascriptového kódu, čímž tak tvoří hlavní část aplikace. Obsahuje různé vlastnosti a metody, které jsou nezbytné pro správný běh UI. Je to jediný objekt svého druhu v aplikaci: var fooReader = { parser : null, feeds : [], //HTML prvky divFeedList : null, divViewingItem : null, divItemList : null, divMessageTitle : null, aMessageLink : null, divMessageBody : null, divLoading : null, selectedItem : null, //zde bude další kód } //zde bude další kód

Vlastnosti, které jsou použity v této definici, jsou následující:

První vlastnost je parser a obsahuje objekt XParser.

Další je pole nazvané feeds a obsahuje seznam zdrojů.

Dalších sedm vlastností jsou odkazy na objekty HTMLElement. Tyto prvky jsou používány skrze celou relaci aplikace, což je dobrý důvod, aby byly cachovány.

Poslední vlastnost je selectedItem, která slouží jako ukazatel na položku (prvek <a>), na niž uživatel naposledy kliknul.

Všechny tyto vlastnosti jsou inicializovány na null, aby se předešlo případným chybám.

Kapitola 15.indd 535

15.10.2007 14:12:27


Kapitola 15 – Případová studie: FooReader.NET

536

Inicializace UI Předtím, než uživatel může pracovat s UI, musí být inicializovány vlastnosti HTMLElement. K tomu slouží metoda init(), která kromě přiřazení vlastností pro prvky nastavuje velikost těch prvků UI, jež potřebují dynamickou velikost. Tato metoda je volána při událostech load a resize okna. Proto funkce existuje jako metoda objektu fooReader, ale její definice leží mimo definici hlavního objektu. To je jediný vizuální rozdíl mezi touto metodou a ostatními členy fooReader. var fooReader = { parser : null, feeds : [], //HTML prvky divFeedList : null, divViewingItem : null, divItemList : null, divMessageTitle : null, aMessageLink : null, divMessageBody : null, divLoading : null, selectedItem : null, //zde bude další kód } fooReader.init = function (evt) { evt = evt || window.event; if (evt.type == "load") { //Tyto inicializace proběhnou //jen při události load fooReader.divFeedList = document.getElementById("divFeedList"); fooReader.divViewingItem = document.getElementById("divViewingItem"); fooReader.divItemList = document.getElementById("divItemList"); fooReader.divMessageTitle = document.getElementById("divMessageTitle"); fooReader.aMessageLink = document.getElementById("aMessageLink"); fooReader.divMessageBody = document.getElementById("divMessageBody"); fooReader.divLoading = document.getElementById("divLoading"); //zde bude další kód } var divPaneContainer = document.getElementById("divPaneContainer"); var divReadingPane = document.getElementById("divReadingPane"); var divMessageContainer = document.getElementById("divMessageContainer"); var divMessageHeader = document.getElementById("divMessageHeader"); //zde bude další kód }; window.onload = fooReader.init; window.onresize = fooReader.init;

Kapitola 15.indd 536

15.10.2007 14:12:27


Ajax Profesionálně

537

Protože vývojáři musí pořád zápasit s rozdíly mezi různými implementacemi modelu událostí, první řádek této metody získává správný objekt události. Dále je ověřeno, jestli typ události byl load. Pokud ano, jsou pomocí metody getElementById() přiřazeny různé vlastnosti HTMLElement. Mimo blok if jsou získány ostatní vlastnosti HTMLElement a přiřazeny proměnným. Tyto proměnné jsou používány pro operace s velikostí prvku: //zde je kód objektu fooReader fooReader.init = function (evt) { evt = evt || window.event; if (evt.type == "load") { // Tyto inicializace proběhnou // jen při události load fooReader.divFeedList = document.getElementById("divFeedList"); fooReader.divViewingItem = document.getElementById("divViewingItem"); fooReader.divItemList = document.getElementById("divItemList"); fooReader.divMessageTitle = document.getElementById("divMessageTitle"); fooReader.aMessageLink = document.getElementById("aMessageLink"); fooReader.divMessageBody = document.getElementById("divMessageBody"); fooReader.divLoading = document.getElementById("divLoading"); //zde bude další kód } var divPaneContainer = document.getElementById("divPaneContainer"); var divReadingPane = document.getElementById("divReadingPane"); var divMessageContainer = document.getElementById("divMessageContainer"); var divMessageHeader = document.getElementById("divMessageHeader"); var iDocHeight = document.documentElement.clientHeight; divPaneContainer.style.height = iDocHeight – divPaneContainer.offsetTop - 12 + "px"; var iFeedsListHeight = divPaneContainer.offsetHeight – fooReader.divViewingItem.offsetHeight getStyleNumber(fooReader.divFeedList, "paddingTop") – getStyleNumber(fooReader.divFeedList, "paddingBottom"); fooReader.divFeedList.style.height = iFeedsListHeight + "px"; //zde bude další kód }; window.onload = fooReader.init; window.onresize = fooReader.init;

Zvýrazněný kód začíná získáním výšky oblasti pro zobrazení pomocí dokument.documentElement.clientHeight. Tato hodnota je pak společně s divPaneContainer.offsetTop použita pro nastavení výšky prvku s identifikátorem divPaneContainer. Číselná konstanta 12 je použita pouze pro vizuální účely, protože poskytuje 12 pixelů volného prostoru mezi spodní hranou kontejneru a spodní hranou okna.

Kapitola 15.indd 537

15.10.2007 14:12:27


Kapitola 15 – Případová studie: FooReader.NET

538

Následuje přiřazení proměnné iFeedsListHeight, která je použita pro nastavení výšky divFeedList. Výška tohoto prvku je nastavena tak, aby prvek vyplnil celý dostupný prostor panelu.

Výpočet se udělá tak, že se vezme výška kontejneru pro panely a prostřednictvím vlastnosti offsetHeight prvku divViewingItem od ní odečte velikost hlavičky panelu. Nesmíme také zapomenout na odečtení hodnot paddingTop a paddingBottom prvku divFeedList. Tyto dvě hodnoty se totiž rovněž podílejí na celkové výšce prvku divFeedList, takže musejí být ve výpočtu zahrnuty. Získaná velikost v kombinaci s pravidlem overflow:auto způsobí, že panel bude obsahovat posuvníky pro případ, kdy se do něj obsah nevejde. Naprosto stejný postup je použit i pro druhý (prostřední) panel s položkami – fooReader.divFeedList je pouze nahrazen za fooReader.divItemList, takto: //kód objektu fooReader fooReader.init = function (evt) { evt = evt || window.event; if (evt.type == "load") { // Tyto inicializace proběhnou // jen při události load fooReader.divFeedList = document.getElementById("divFeedList"); fooReader.divViewingItem = document.getElementById("divViewingItem"); fooReader.divItemList = document.getElementById("divItemList"); fooReader.divMessageTitle = document.getElementById("divMessageTitle"); fooReader.aMessageLink = document.getElementById("aMessageLink"); fooReader.divMessageBody = document.getElementById("divMessageBody"); fooReader.divLoading = document.getElementById("divLoading"); //zde bude další kód } var divPaneContainer = document.getElementById("divPaneContainer"); var divReadingPane = document.getElementById("divReadingPane"); var divMessageContainer = document.getElementById("divMessageContainer"); var divMessageHeader = document.getElementById("divMessageHeader"); var iDocHeight = document.documentElement.clientHeight; divPaneContainer.style.height = iDocHeight – divPaneContainer.offsetTop - 12 + "px"; var iFeedsListHeight = divPaneContainer.offsetHeight – fooReader.divViewingItem.offsetHeight getStyleNumber(fooReader.divFeedList, "paddingTop") – getStyleNumber(fooReader.divFeedList, "paddingBottom"); fooReader.divFeedList.style.height = iFeedsListHeight + "px"; var iItemListHeight = divPaneContainer.offsetHeight – fooReader.divViewingItem.offsetHeight – getStyleNumber(fooReader.divItemList, "paddingTop") – getStyleNumber(fooReader.divItemList, "paddingBottom"); fooReader.divItemList.style.height = iItemListHeight + "px";

Kapitola 15.indd 538

15.10.2007 14:12:27


Ajax Profesionálně

539

var iMessageBodyHeight = divReadingPane.offsetHeight – divMessageHeader.offsetHeight – getStyleNumber(divMessageContainer, "paddingTop") – getStyleNumber(divMessageContainer, "paddingBottom"); fooReader.divMessageBody.style.height = iMessageBodyHeight + "px"; }; window.onload = fooReader.init; window.onresize = fooReader.init;

Výpočet pro nastavení výšky pro prvek divMessageBody je založen na podobném vzoru, který jsme vám právě ukázali. Rozdíl je pouze v tom, že je použita výška panelu pro čtení (divReadingPane) a výška hlavičky zprávy (divMessageHeader) místo kontejneru pro panely a jeho hlavičky. Co se týče vertikálního odsazení, tak to se získává z prvku divMessageContainer (a nikoliv z prvku divMessageBody). Nicméně výsledný efekt je stejný jako v minulých případech – jakmile je nastavena výška těla pro zprávy, bude možné rolovat obsahem v případě potřeby.

Zobrazování a skrývání ukazatele s průběhem načítání Objekt fooReader poskytuje dvě metody pro zobrazení a skrytí ukazatele s průběhem načítání: hideLoadingDiv : function () { this.divLoading.style.display = "none"; }, showLoadingDiv : function () { this.divLoading.style.display = "block"; },

Tyto metody jednoduše změní hodnotu vlastnosti display z none na block, čímž tento ukazatel se stavem načítání buď skryjí, nebo zobrazí.

Nastavení obsahu pro třetí panel Následující metoda setMessage() nastavuje obsah třetího panelu. Tato metoda přidává obsah do prvků s identifikátory divmessageTitle, aMessageLink a divMessageBody. Přijímá tři argumenty – nadpis zprávy, odkaz související s článkem a tělo zprávy. setMessage : function (sTitle, sHref, sMessageBody) { this.divMessageTitle.innerHTML = sTitle; this.aMessageLink.href = sHref; this.divMessageBody.innerHTML = sMessageBody; },

Kapitola 15.indd 539

15.10.2007 14:12:27


Kapitola 15 – Případová studie: FooReader.NET

540

Metody pro položky Následující čtyři metody souvisí s panelem položek (prostřední panel). Konkrétně jsou odpovědné za naplnění tohoto panelu položkami, za změnu hlavičky tohoto panelu, za odstraňování položek z panelu a samozřejmě za programový výběr položek.

Přidávání položek První metoda je addItem() a dynamicky vytváří HTML kód položky, který následně připojuje do panelu položek (tzn. do prostředního panelu). Přijímá dva argumenty: objekt XParser položky a číslo asociované s položkou. addItem : function (oItem, iNum) { var aItem = document.createElement("A"); aItem.className = "itemlink"; aItem.href = oItem.link.value; aItem.setAttribute("frFeedItem",iNum); aItem.id = "item" + iNum; var divHeadline = document.createElement("DIV"); divHeadline.className = "itemheadline"; divHeadline.innerHTML = oItem.title.value; var divDate = document.createElement("DIV"); divDate.className = "itemdate"; divDate.appendChild(document.createTextNode("Date: " + oItem.date.value)); aItem.appendChild(divHeadline); aItem.appendChild(divDate); //zde bude další kód this.divItemList.appendChild(aItem); },

Tento kód používá informace obsažené v objektu XParser, aby vytvořil HTLM kód pro položku. Všimněte si, že pro prvek aItem je vytvořen atribut s názvem frFeedItem. Tento atribut obsahuje číslo asociované s touto položkou a je později použit pro přidání obsahu do třetího panelu. Kliknutí na položku zatím nic nedělá. Ve skutečnosti ovšem kliknutí na položku přenese uživatele na URL zadanou ve vlastnosti href prvku aItem. Tohle ale není žádoucí, takže musíme zajistit ovládání události click. Kliknutí na nějakou položku by mělo způsobit dvě věci.

Za prvé – aktuálně vybraná položka by se měla vrátit do normálního stavu a položka, na kterou bylo teď nově kliknuto, by se měla stát vybranou položkou.

Za druhé – třetí panel pro obsah by měl být naplněn obsahem této položky.

Ovladač události onclick se vykoná v rozsahu prvku <a>. Kód proto potřebuje použít API fooReaderu pro přístup k částem UI: addItem : function (oItem, iNum) {

Kapitola 15.indd 540

15.10.2007 14:12:27


Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.