Realizacja Aplikacji Internetowych JavaScript
W trakcie ogladania tego slajdu (2-4min) Oczekiwanie na zwrot różnych treści (głównie w tradycyjnych witrynach) zajmuje na świecie ~ 35 000 000 osobo-minut to oznacza 66 osobo-lat albo np. 86 lotów na Marsa (wg. czasów np. lądownika Curiosity)
Tradycyjne strony Strona jest kompletowana (markup+dane) po stronie serwerowej Struktura DOM-a jest w zasadzie okreslona w momencie wysyłki z serwera Strona może zawierać javascript dla dodania efektów, dynamiki, formatowania, zmiany layout-u dla różnych rodzajów urzadzeń Historia, uprawnienia itd. mają naturalne odbicie w URL-ach
SPA Markup (mockup) HTML może być ładowany z bez/z małą ilościa danych Dane są doładowywane asynchronicznie Brak wielokrotnego przesyłania redundantnych elementów (tagi/DOM/skrypty) Przeniesienie cześci ciężaru renderowania na stronę kliencką Mniejsze obciążenie łączy/serwerów Brak migania/zamrażania strony Wrażenie pracy z resoponsywną aplikacją
SPA vs strona serwerowa Przy SPA serwer odpowiada za przesłanie inicjalnego mockup-u strony (definicji) ew. wypełnionej wstępnie danymi Dane: formaty natywne HTML, obrazki lub JSon XML Podejście hybrydowe Backend vs API
Rozwiązania klienckie Silverlight Flash/Flex HTML 5 + CSS 3 Javascript CofeeScript, Dart, TypeScript GWT (java 2 javascript) Traceur (google: kompilator javascript next 2 javascript) Ecma Harmony / Narcisius (jw) Script# (c# 2 javascript) Pyjamas (python 2 javascript)
Javascript i biblioteki klienckie Biblioteki operujące na DOOM JQuery, Prototype, MooTools Frameworki: Yui, Sencha ExtJS, Dojo, Knockout, EmberJS, Angular, Aurelia, Meteor BackBone ReactJS Bootstrap CSS, Less, Sass
Narzędzia Debugowanie, profiling: Pakiety, komendy FireBug, Chrome Developer Tools, IE Inspector / WebDeveloper Fiddler Pakiety, komendy npm, jspm.io, Bower, Grunt Testy jednostkowe i akceptacyjne Junit Jasmine Automatyczne uruchamianie testów: Selenium Headless browsers PantomJS Zombi+NodeJs, ENVJS. + Rhino engine
Realizacja Aplikacji Internetowych TESTOWANIE
Testowanie Jakieś testy są potrzebne … Testy jednostkowe Testy integracyjne Testy akceptacyjne lub end-to-end
Środowisko Framework Narzędzia dodatkowe FireUnit Środowisko uruchomieniowe Przeglądarka uruchamiana ręcznie Selenium + przeglądarka Na serwerze: PhantomJS (SlimerJS,CasperJS…) Node.js + Chimera | Karma rhino + env.js,
Framework QUnit (używany np. przez wiele plug-inów JQuery) module('calculator'); test('add numbers', function() { var calc = new Calculator(); equals(4, calc.add(2, 2)); })
Framework Jasmine (BDD, hierarchiczna definicja setup-ów) describe('panda',function(){ it('is happy',function(){ expect(panda).toBe('happy'); });
Framework Mocha (BDD, hierarchiczna definicja setup-ów, pozwala używać różnych assert libraries, raportuje wolne testy przy t. asynchr., coverrage) describe('panda',function(){ it('is happy',function(){ assert.equal('happy',panda.state); expect(panda).toBe('happy'); });
Framework Screw.Unit JSSpec describe("Calculator", function() { it("should add numbers", function() { var calc = new Calculator(); expect(calc.add(2, 2)).to(equal, 4); }); }); JSSpec describe("Calculator", function() { with(this) { it("should add numbers", function() { var calc = new Calculator(); calc.add(2, 2).should(equal(4)); }); }});
Framework jsUnitTest jsUnitTest + jShoulda new Test.Unit.Runner({ testCalculatorAdd: function() { with (this) { var calc = new Calculator(); assertEqual(4, calc.add(2, 2)); }} }); jsUnitTest + jShoulda context('Calculator', { should('add numbers', function() { with (this) { var calc = new Calculator(); assertEqual(4, calc.add(2, 2)); }})
Framework I wiele innych m.in Unittest, jsAssertUnit, YUItest, Qooxdoo, JSUnit, JSUnit , Crosscheck, Ecmaunit, Dojo Objective Harness, FireUnit, Qunit, RhinoUnit, JSpec
Tools Smoke Amok MockMe new Smoke.Stub(Calculator, 'add').and_return(5); Smoke.Mock(Calculator).should_receive('add').at_least('once') .with_arguments(2, 2).and_return(5); Amok var mock = amok.mock(Calculator); mock.should_receive('add').with_args(2, Number) .times(1).and_return(5); MockMe mock(Calculator).andDo(function() { SomethingThatUsesCalculator.calculate("2 + 2"); verify(Calculator.add)(2, 2); });
Tools qMock Jack Interesujace: JSMockito, Sinon.js, Mock.js var mock = new Mock(); mock.expects(1).method('add').withArguments(2, 2).andReturn(5); Jack jack.expect("Calculator.add").exactly("1 time") .withArguments(2, 2).mock(function() { … return sth; }) jack.create("mock", ['add', 'substract']); .whereArgument(0).isOneOf(2, 3, 4); Interesujace: JSMockito, Sinon.js, Mock.js
Istotne uwagi Izolacja od drzewa DOM i globalnych odwołań do obiektów Lokalne referencje do używanych obiektów Rozluznienie połączeń miedzy obiektami Mozliwość stosowanania wzorca obserwator (event) Stosowanie broker event (serviceBus) do komunikacji
Pokrycie kodu testami JSCoverage Istambul …
Realizacja Aplikacji Internetowych JQuery
JQuery Silny model selekcji Wsparcie dla typowych akcji np. ustawienie obsługi zdarzenia, dodanie, usunięcie klasy stylu itd. Gotowe rozwiazania typowych problemów Rozszerzalny model - wtyczki Dużo gotowych wtyczek Testowalny
JQuery Problem: wyszukiwanie elementów na stronie np:for ( var (i in document.getElementsByTagName("p") ) { i.onclick = function(event) { alert("Hello!"); } } JQuery: $("p").click(function(event){ alert("Hello!!"); }
Jquery – Referencjonowanie <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"> </script> <script> // Standard. jQuery $ jQuery(document).ready( function () { alert('DOM is ready!'); }); // Shortcut, but same thing as above. jQuery( function () { alert('No really, the DOM is ready!'); }); // Shortcut with fail-safe usage of $ jQuery(function($) {alert('Seriously its ready!'); });
JQuery Problem: wyszukiwanie elementów na stronie np:for ( var (i in document.getElementsByTagName("p") ) { i.onclick = function(event) { alert("Hello!"); } } JQuery: $("p").click(function(event){ alert("Hello!!"); }
JQuery Selektor zwraca kolekcję obiektów DOM "owiniętych" w obiekty JQuery (dodatkowe metody) Pojedynczy obiekt można owinac stosujac $ np. $(this) Kolekcję można ją zapamiętać i wyłuskać z niej referencje do poszczególnych elementów var jqlinki = $("a"); var pierwszy_link = linki[0]; Kolekcję można też sprowadzic do tablicy obiektów DOM var jqlinki = $("a").get();
JQuery - selektory Selektory zwracają kolekcję elementów JQuery opakowujących elementy DOM: $("*") – wszystko $("a") - po nazwie tagu $('#myDivId') - po id $('.myCssClass'), $(‘a.myCssClass')- po klasie CSS $("[id]") – po atrybucie $("[id=wartosc]") – po wartości atrybutu Np. $('div').each( function() { alert($(this).html(); });
JQuery – selektory strukturalne $ (“selektor1, selektor2, selektorN”) $("p,a,div,code.bold") $("rodzic > dziecko") $(„p>a") $("przodek potomek") $("p a.bold") $ (“poprzedni+nastepny”) $ (“label.MyLabelClass + input”) $ (“poprzedni ~ cale_rodzenstwo”) $ (“h3 ~ p”)
JQuery - atrybuty $("[id]") $("p[id][name]") $("[id=wartosc]") $("input[name='newsletter']") $("[id~=slowo]") $("input[name~='test']") $("[id|=poczatek_do_kreski_lub_rowny]") $("a[hreflang|='en']") $("[id*=podciag]") $("input[name~='xx']") $("[id^=poczatek]") $("input[name^='news']") $("[id$=koniec]") $("input[name^=letter']")
JQuery - selektory Selektory można łaczyć i dodawać $("a.myCssClass, '#myDivId>a")
JQuery – modyfikatory selekcji :animated, :checked, :disabled :enabled, :empty, :hidden, :visible, :selected :file, :checkbox, :button, :header, :password, :radio, :reset, :submit, :text :even, :odd, :first, :first-child, :last, :last-child, :parent :contains() $("div:contains('John')") :has() $("div:has('p')") :not(sektor) $("input:not(:checked) + span") :eq(), :lt(), :gt() $("td:eq(2)")
JQuery – filtry .eq(), .first(), .last(), .slice(od,[do]) $('li').eq(2) $('li').eq(-1) $('li').slice(3,-2) .filter(selector), filter(function) $('li').filter(':even') $('li').filter(function(index) { return index % 3 == 2; }) .has(selektor_potomka), .has(DOM_element_potomny) $('li').has('ul') .not(selektor), not(DOM_elementy), not(function(index)) $('li').not(':even') $("p").not( $("#selected")[0] )
JQuery – pobieranie zawartości .get([pozycja]) - pobieranie elementów Dom $("a").get() .html() $('div.demo-container').html(); $( "#myDiv p:first" ) .html( "New <strong>first</strong> paragraph!" ); .length, size() var n = $("div").length; .val() $('select.foo option:selected').val() .attr() var title = $("em").attr("title"); $("em").attr("title“,”new title”);
JQuery – trawersowanie drzewa .find(), .end() – kończy ostatnie find $("p").find("a") .siblings(), .children(), .contents(), .closest(), .next(), .prev(), .parent(), .parents() .prevAll(), .nextAll(), .andSelf() $("div:last").prevAll(".myClass") .nextUntil(), .parentsUntil(), .prevUntil() #("#term-2").nextUntil("dt") .offsetParent() – pozycja na ekranie .add() $('li').add('p')
JQuery – ZMIANY .replaceAll(co_zastapic_selekcja) $("<b>Paragraph.</b>").replaceAll("p") .replaceWith(noweElementy), replaceWith(funkcja), $('.second').replaceWith( "<h2>New heading</h2>"); $('.third').replaceWith($('.first'));
JQuery – transformacje .map( callback(this, index) ) var oneBased = $.map([0,1,2,3,4], function(value){return value+1;}); var ids = $(':checkbox').map(function() { return this.id; })
JQuery – predykaty .hasClass() $('#mydiv').hasClass('foo') .is() $(this).is(".blue,.red")
JQuery – modyfikacje .addClass(), removeClass() $('p').addClass('myClass yourClass'); .toggleClass( className ), .toggleClass( className, switch ), .toggleClass( function(index, class), [ switch ] ) $("p").click(function () { $(this).toggleClass("highlight"); }); .removeAttr() $(this).next().removeAttr("disabled") .css(), .attr(), .val() $(‘p’). css( { fontSize: "100px", color: "red“ } )
JQuery – zdarzenia .error(), .resize(), .scroll() $('#book').error(function() { alert('Handler called.') }); .load()*, .ready(), .unload()* .blur(), .change(), .focus(), .select(), .submit() $('form').submit(function() { alert('Handler for .submit() called.'); return false; }); .focusin(), .focusout(), .keydown(), .keypress(), .keyup() .hover(), .mousedown(), .mouseenter(), .mouseleave(), .mousemove(), .mouseout(), .mouseover(), .mouseup(), .click(), .dblclick(), on() $('#target').click( function() { alert(‘click handler called.'); } ); * - deprecated -> on(“load”…
JQuery – efekty .hide(), show() $('p').addClass('myClass yourClass'); .focus() .slideDown(), .slideToggle(), .slideUp() ("div").slideDown("slow"); .fadeIn(), .fadeOut(), .fadeTo() $("div:hidden:first").fadeIn("slow"); .animate(), .stop(), .delay(), jQuery.fx.off $('#book').animate({ width: ['toggle', 'swing'], height: ['toggle', 'linear'], opacity: 'toggle' }, 5000, function() { // Animation complete. }); );
JQuery – Ajax load(), jQuery.get(), jQuery.getJSON(), jQuery.getScript(), jQuery.post() $.get("test.cgi", { name: "John", time: "2pm" }, function(data){ alert("Data Loaded: " + data); }); $('#result').load('ajax/test.html', function() { alert('Load was performed.'); }); jQuery.ajax(), jQuery.ajaxSetup(), $.ajax({ type: 'POST', url: url, data: data, success: success, dataType: dataType }); .ajaxComplete(), .ajaxError(), .ajaxSend(), .ajaxStart(), .ajaxStop(), .ajaxSuccess(), . jQuery.param(), .serialize(), .serializeArray()
JQuery – Różne .clone(), .append(), appendTo(), .prepend(), .prependTo(), text(), html() $('h2').appendTo($('.container')); $("p").text("<b>Some</b> new text."); .unwrap(), .wrap(), .wrapAll(), .wrapInner() $("span").wrap("<div><p><em><b></b></em></p></div>"); $("span").wrapAll("<div></div>") .replaceAll(), .replaceWith() $('<h2>New heading</h2>').replaceAll('.inner'); $(‘<div/>’).replaceWith(‘<p></p>’); .detach(), .empty(), .remove() $('.hello').empty();
JQuery – Literatura Materiały: Ksiązki Doc do VS jquery.com “JQuery in Action” “Learning jQuery” “jQuery UI” “jQuery Cookbook” Doc do VS http://appendto.com/community/jquery-vsdoc/
Realizacja Aplikacji Internetowych Ext-Js
Ext-JS (Sencha, Aptana) Ext-core – skrócona wersja – bez frameworka UI Ext-all – pełna wersja z kontrolkami Jako selektora DOM może korzystać z GWT, jQuery, DOM, YUI Framework: Komercyjny Komercyjny: Ext builder
Ext - core Uruchamianie skryptów na DOM Szukanie elementow po id Ext.onReady(function() { ... }); Szukanie elementow po id var el=Ext.get('myElementId'); el.addClass('error'); Lekkie "nietrwałe" referencje Ext.fly('elId').hide(); Szukanie elementow Ext.query('div:nth-child(2)'). findParent('div');
Ext - core Zdarzenia Animacje Ext.fly('myEl').on('click', function(e, t) { alert("clicked!!!"); }); Animacje var el=Ext.get('myElementId'); el.slideOut('t', { easing: 'easeOut', duration: .5, remove: false, useDisplay: false });
Ext - "klasy" Dziedziczenie Person = Ext.extend(Object, { constructor: function(first, last){ this.firstName = first; this.lastName = last; } getName: function(){ return this.firstName + ' ' + this.lastName; } }); var p1 = new Person('John', 'Smith');
Ext - narzędzia Odwołania do klasy bazowej Przestrzenie nazw MyClass = Ext.extend(Object, { constructor: function(config){ ... MyClass.constructor.call(this, config); }, foo: function(){ ... } }); Przestrzenie nazw Ext.namespace( 'MyCompany', 'MyCompany.Application'); Ext.namespace('MyCompany.Application');
Ext – wzorzec obserwator var MyClass = Ext.extend(Ext.util.Observable, { constructor: function(config){ this.addEvents('datachanged'); // event MyClass.constructor.call(this, config); }, update: function(){ // jakies transformacje // Przy "odpaleniu" zdarzenia mozemy okreslic // parametry ktore maja byc przekazane this.fireEvent('datachanged', this, this.data.length); } }); // Subskrypcja eventu var c = new MyClass(); c.on('datachanged', function(obj, num){ /* obsluga.*/ });
Ext - DOM Modyfikacja DOMa createHtml (przestarzale), markup insertHtml,insertBefore,insertAfter, insertFirst, append, overwrite Ext.DomHelper.append(document.body, { id: 'my-div', cn: [{ tag: 'a', href: 'http://www.yahoo.com/', html: 'My Link', target: '_blank' }] });
Ext - DOM Skryptowanie: var myEl = document.createElement('a'); myEl.href = 'http://www.yahoo.com/'; myEl.innerHTML = 'My Link'; myEl.setAttribute('target', '_blank'); var myDiv = document.createElement('div'); myDiv.id = 'my-div'; myDiv.appendChild(myEl); document.body.appendChild(myDiv);
Ext - AJAX Globalne handlery: Typowe wołanie: Ext.Ajax.on('beforerequest', this.showSpinner, this); Ext.Ajax.on('requestcomplete', this.hideSpinner, this); Ext.Ajax.on('requestexception', this.hideSpinner, this); Typowe wołanie: Ext.Ajax.request({ url: 'ajax_demo/sample.json', success: function(response, opts) { var obj = Ext.decode(response.responseText); console.dir(obj); }, failure: function(response, opts) { console.log('server-side failure with status code ' + response.status); }});
Ext - GUI ViewPort, Window Toolbar, Progresbar, TabPanel, Panel, Editor, DataPicker, DataView Button, ButtonGroup
Ext - przykład Ext.onReady(function() { var item1 = new Ext.Panel({ title: 'Accordion Item 1', html: '<empty panel>', cls:'empty' }); var item2 = new Ext.Panel({ title: 'Accordion Item 2', html: '<empty panel>', cls:'empty' }); ... var accordion = new Ext.Panel({ renderTo: Ext.getBody(), // do elementu 'myDiv' margins:'5 0 5 5', split:true, width: 210, layout:'accordion', items: [item1, item2, item3, item4, item5] });
Ext - przykład Ext.onReady(function() { var item1 = new Ext.Panel({ .... }); ... var accordion = new Ext.Panel({ region:'west', margins:'5 0 5 5', split:true, width: 210, layout:'accordion', items: [item1, item2, item3, item4, item5] }); var viewport = new Ext.Viewport({ layout:'border', items:[ accordion, { region:'center', margins:'5 5 5 0', cls:'empty',bodyStyle:'background:#f1f1f1', html:'<br/><br/><empty center panel>' }] });
Ext – przykład: reużycie kontrolek Ext.onReady(function() { MyPanel = extend(Ext.Panel, { title: 'Accordion Item 1', html: '<empty panel>', cls:'empty' }); ... var accordion = new Ext.Panel({ region:'west', margins:'5 0 5 5', split:true, width: 210, layout:'accordion', items: [ new MyPanel(), new MyPanel(), new MyPanel(), new MyPanel(), new MyPanel() ] });
Ext-GUI Przykłady: Lauout Drag&Drop Form Desktop
Ext-GUI Rozluznienie powiazań m. Komponentami Lokalne referencje Obserwator Mediator: publish/subscribe Simplebus Ext.bus
Realizacja Aplikacji Internetowych AngularJS
Kompleksowy framework JS “opinionated” Dwukierunkowy binding HTML jako szablon – deklaratywna składnia MVC - MVVM Dependency Injection Rozszerzalny Testowalny Dobrze współpracuje z JQuery, Bootstrap 2.0 NIEKOMPATYBILNY Z 1. (1.5 w. przejściowa)
Dyrektywy Atrybuty: <html ng-app="project"> Tagi <ng-pluralize count="beerCount" when="beerForms"> <my-widget title=“My Widget">
Binding
Kontroler <div ng-controller="MyController"> <span class="My-"+{{getClass()}}> {{GeneralInfo}} </span> <ol ng-repeat="element in form.elementsToShow"> <li ng-show="element.visible">{{element.content}}</li> </ol> </div>
Kontroler angular.module('myApp', []) .controller('MyController', ['$scope', function($scope) { $scope.elementsToShow = [ {content:'aaa', exists:true}, {content:'bbb', exists:false}]; $scope.neneralInfo = "Info"; $scope.getClass = function() { if (..) return "claccSuffix"; }); }]);
Kontroler
Filtry (1.0) / potoki (2.0) {{ data | filter:options }} {{ element.price | currency }} currency uppercase date:' h:mma dd/MM/yyyy‘ limitTo:8 ng-repeat="…|orderBy:'-content'"
Formatowanie ng-class/ ngClass (2.0) ng-show / [hidden] <h3 ng-show="vm.favoriteHero"> <h3 [hidden]="!favoriteHero">
ng-pristine, ng-submitted ng-invalid, ng-valid ng-dirty Formatowanie <a href ng-click="tab = 1"> ng-pristine, ng-submitted ng-invalid, ng-valid ng-dirty ng-scope, ng-isolate-scope ng-binding
Zdarzenia ng-submit / submit ng-init / init ng-click (1.0) / click (2.0) <a href ng-click="tab = 1"> <button (click)="toggleImage()"> ng-submit / submit ng-init / init
Dane i binding binding {{GeneralInfo}} ng-repeat="todo in todos" ngFor (2.0) <input type="checkbox" ngModel (2.0)
Modularyzacja ng-include="'sub-page.html‘" ng-controller= "MyController as ctrl« <my-directive></my-directive > <span my-attribute></span>
Custom element <my-directive> app.directive('myDirective', function(){ return { restrict: 'E', templateUrl: 'myTemlate.html', }; }); Element Attribute Class
Custom element <my-directive> app.directive('myDirective', function(){ return { restrict: 'E', templateUrl: 'myTemlate.html', controller: function(){...}, controllerAs: 'ctrl' }; }); Element Attribute Class
Modularyzacja ng-include="'sub-page.html‘" ng-controller= "MyController as ctrl« <my-directive></my-directive > <span my-attribute></span> var myModule = angular.module('myModule', []);
Serwisy, DI app.controller('MyController', ['$http', $log, function($http, $log){ }]); Serwisy
Serwisy, DI app.controller('MyController', ['$http', $log, function($http, $log){ }]); Leniwa inicjalizacja Model singleton Standardowe s.: $anchorScroll, $animate, $cacheFactory, $compile, $controller, $document, $exceptionHandler, $filter, $http, $httpBackend, $interpolate, $interval, $locale, $location, $log, $parse, $q, $rootElement, $rootScope, $sce, $sceDelegate, $templateCache, $templateRequest, $timeout, $window Serwisy
Własny serwis var myModule = angular.module('myModule', []); myModule.factory('myService', function() { var serviceInstance; // inicjalizacja return serviceInstance; }); myModule.factory('myServiceWithDep', [$log, myService, function($log, mySrvc){ return ...;
Wykorzystanie DI – serwis locator var di = angular.injector(['myModule', 'ng']); var split = di.get('splitter'); var contr = di.instantiate('MyController'); Pot. wykorzystanie cache
Własne filtry {{name | reverse}} angular.module('myApp', []) .filter('reverse', function() { return function(input, uppercase) { input = input || ''; var out = ""; for (var i = 0; i < input.length; i++) { out = input.charAt(i) + out; } // conditional based on optional argument if (uppercase) { out = out.toUpperCase(); return out; }; })
Angular - kontekst $scope może być zagnieżdzony może odwołać sie do rodzica i odczytać jego wartości nie może odczytać wartości dzieci
Angular - wady Relatywnie drobne błedy potrafią spowodować niedziałanie całej strony Wolno działa pod starszymi przeglądarkami IE7, IE8 Pot. wolny dla skomplikowanych stron Trudno zrzumieć niektóre interakcje Niektóre mechanizmy są trudne np uaktualnianie scope vs event loop vs binding
Realizacja Aplikacji Internetowych ReactJS
ReactJS - jeszcze inne podejście https://facebook.github.io/react/ var Hello = React.createClass({displayName: 'Hello', render: function() { return React.createElement("div", null, "Hello ", this.props.name); } }); ReactDOM.render( React.createElement(Hello, {name: "World"}), document.getElementById('container') );
ReactJS - jeszcze inne podejście Skupia się na izolacji komponentów (za to kod html jest pomieszany z kodem js). Unika zarządzania stanem – przy każdej zmianie cały widok jest renderowany ponownie (w przeglądarce lub na serwerze) Koncepcja funkcyjna: wyświetlenie strony polega na wyrenderowaniu html-a na podstawie stanu Modele sa zwykłymi obiektami js Jednokierunkowe wiązanie Szybki - asynchroniczne odświeżanie widoku (kolejka) może być bardziej efektywne niż natychmiastowe odświeżanie przy b. rozbudowanych aplikacjach http://facebook.github.io/react/docs/thinking-in-react.html
Realizacja Aplikacji Internetowych CoffeeScript
Instalacja Node.js + coffee-script.js CoffeeSharp.zip NUGet: Install-Package CoffeeSharp Coffee.exe CoffeeScriptHttpHandler.dll CoffeeSharp.dll Jurassic.dll
Motywacja Zwięzła składnia Redukcja szumu {} itd Dodatkowe możliwości Poprawione problemy (==, var) Produkowany jest dobry kod Javascript Informacje http://jashkenas.github.com/coffee-script/#top Ksiazki: http://arcturo.github.com/library/coffeescript/, http://eloquentjavascript.net/ Screencast: http://peepcode.com/products/coffeescript
Podstawy Komentarz # Przypisanie – tworzy zmienna Abc =5 var Abc = 5 Bloki realizowane za pomoca wcięć if happy and knowsIt clapsHands() chaChaCha() else showIt() if (happy && knowsIt) { clapsHands(); chaChaCha(); } else { showIt();
Operatory Operatory CoffeeScript vs JavaScript is isnt not and or true, yes, on false, no, off @, this of in, ? === !== ! && || true false this in Brak odpowiednika alert "I knew it!" if elvis? if (typeof elvis !== "undefined" && elvis !== null) alert("I knew it!");
Interpolacja zmiennych Zmienne w " " podlegają interpolacji a w ' ' nie podlegają author = "Wittgenstein" quote = "A picture is a fact. -- #{ author }" sentence = "#{ 22 / 7 } is a decent approximation of π" var author, quote, sentence; author = "Wittgenstein"; quote = "A picture is a fact. -- " + author; sentence = "" + (22 / 7) + " is a decent approximation of π";
Wieloliniowe literały i komentarze blokowe html = ''' <strong> cup of coffeescript </strong> ''' ### CoffeeScript Compiler v1.1.2 Released under the MIT License
Kaskadowe porównania cholesterol = 127 healthy = 200 > cholesterol > 60 var cholesterol, healthy; cholesterol = 127; healthy = (200 > cholesterol && cholesterol > 60);
Funkcje Składnia ala Ruby, zwraca wynik ostaniej instrukcji Nawiasy przy wywołaniach można pomijać square(x) -> x*x test(x,y) -> a=5 x+y+a function square(x) { return x*x; } function test(x,y) { var a = 5; return x+y+5; } z = square 5 var z = square(5);
Funkcje i this Account = (customer, cart) -> @customer = customer @cart = cart $('.shopping_cart').bind 'click', (event) -> @customer.purchase @cart var Account; var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; Account = function(customer, cart) { this.customer = customer; this.cart = cart; return $('.shopping_cart').bind('click', __bind(function(event) { return this.customer.purchase(this.cart); }, this)); };
Obiekty Składnia wymaga wcięć math = root: Math.sqrt square: square cube: (x) -> x * square x math = { root: Math.sqrt, square: square, cube: function(x) { return x * square(x); } };
Wszystko jest wyrażeniem grade = (student) -> if student.excellentWork "A+" else if student.okayStuff if student.triedHard then "B" else "B-" else "C" eldest = if 24 > 21 then "Liz" else "Ike" var eldest, grade; grade = function(student) { if (student.excellentWork) { return "A+"; } else if (student.okayStuff) { if (student.triedHard) { return "B"; } else { return "B-"; } return "C"; }; eldest = 24 > 21 ? "Liz" : "Ike";
Pętle for, while, until, Petle zwracają wartość eat food for food in ['toast', 'cheese', 'wine'] var food, _i, _len, _ref; _ref = ['toast', 'cheese', 'wine']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { food = _ref[_i]; eat(food); }
Pętle Zwrot z petli mozna przypisac cubes = (math.cube num for num in list) cubes = (function() { var _i, _len, _results; _results = []; for (_i = 0, _len = list.length; _i < _len; _i++) { num = list[_i]; _results.push(math.cube(num)); } return _results; })();
Przypisania wielokrotne theBait = 1000 theSwitch = 0 [theBait, theSwitch] = [theSwitch, theBait] var theBait, theSwitch, _ref; theBait = 1000; theSwitch = 0; _ref = [theSwitch, theBait], theBait = _ref[0], theSwitch = _ref[1];
switch switch (day) { case "Tue": go(relax); break; case "Thu": go(iceFishing); case "Fri": case "Sat": if (day === bingoDay) { go(bingo); go(dancing); } case "Sun": go(church); default: go(work); switch day when "Tue" then go relax when "Thu" then go iceFishing when "Fri", "Sat" if day is bingoDay go bingo go dancing when "Sun" then go church else go work
try/catch/finally try allHellBreaksLoose() try { allHellBreaksLoose(); catsAndDogsLivingTogether() catch error print error finally cleanUp() try { allHellBreaksLoose(); catsAndDogsLivingTogether(); } catch (error) { print(error); } finally { cleanUp(); }
Sklejanie tablic numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] copy = numbers[0..numbers.length] middle = copy[3..6] var copy, middle, numbers; numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; copy = numbers.slice(0, numbers.length); middle = copy.slice(3, 7);
Sklejanie tablic numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] var numbers, _ref; numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; [].splice.apply(numbers, [3, 4].concat(_ref = [-3, -4, -5, -6])), _ref;
Przypisania mogą dekomponowac struktury var city, futurists, name, street, _ref, _ref2; futurists = { sculptor: "Umberto Boccioni", painter: "Vladimir Burliuk", poet: { name: "F.T. Marinetti", address: ["Via Roma 42R", "Bellagio, Italy 22021"] } }; _ref = futurists.poet, name = _ref.name, _ref2 = _ref.address, street = _ref2[0], city = _ref2[1]; futurists = sculptor: "Umberto Boccioni" painter: "Vladimir Burliuk" poet: name: "F.T. Marinetti" address: [ "Via Roma 42R" "Bellagio, Italy 22021" ] {poet: {name, address: [street, city]}} = futurists
Przypisania mogą dekomponowac zakresy tag = "<impossible>" [open, contents..., close] = tag.split("") var close, contents, open, tag, _i, _ref; var __slice = Array.prototype.slice; tag = "<impossible>"; _ref = tag.split(""), open = _ref[0], contents = 3 <= _ref.length ? __slice.call(_ref, 1, _i = _ref.length - 1) : (_i = 1, []) , close = _ref[_i++];
Argumenty Funkcji gold = silver = rest = "unknown" awardMedals = (first, second, others...) -> gold = first silver = second rest = others contenders = [ "Michael Phelps" "Liu Xiang" "Yao Ming" "Allyson Felix" "Shawn Johnson" "Roman Sebrle" "Guo Jingjing" "Tyson Gay" "Asafa Powell" "Usain Bolt" ] awardMedals contenders... var awardMedals, contenders, gold, rest, silver; var __slice = Array.prototype.slice; gold = silver = rest = "unknown"; awardMedals = function() { var first, others, second; first = arguments[0], second = arguments[1], others = 3 <= arguments.length ? __slice.call(arguments, 2) : []; gold = first; silver = second; return rest = others; }; contenders = ["Michael Phelps", "Liu Xiang", "Yao Ming", "Allyson Felix", "Shawn Johnson", "Roman Sebrle", "Guo Jingjing", "Tyson Gay", "Asafa Powell", "Usain Bolt"]; awardMedals.apply(null, contenders);
Klasy CoffeScript oferuje koncepcję klas Klasy mają składnię zbliżoną do obiektów Wspierane jest dziedziczenie i odwołania do klas bazowych
class Animal constructor: (@name) -> move: (meters) -> alert @name + " moved #{meters}m." class Snake extends Animal move: -> alert "Slithering..." super 5 sam = new Snake "Sammy the Python" sam.move() class Horse extends Animal alert "Galloping..." super 45 tom = new Horse "Tommy the Palomino" tom.move()
var Animal, Horse, Snake, sam, tom; var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; }; Animal = (function() { function Animal(name) { this.name = name; } Animal.prototype.move = function(meters) { return alert(this.name + (" moved " + meters + "m.")); return Animal; })();
Snake = (function() { __extends(Snake, Animal); function Snake() { Snake.__super__.constructor.apply(this, arguments); } Snake.prototype.move = function() { alert("Slithering..."); return Snake.__super__.move.call(this, 5); }; return Snake; })(); Horse = (function() { __extends(Horse, Animal); function Horse() { Horse.__super__.constructor.apply(this, arguments); } Horse.prototype.move = function() { alert("Galloping..."); return Horse.__super__.move.call(this, 45); return Horse; sam = new Snake("Sammy the Python"); tom = new Horse("Tommy the Palomino"); sam.move(); tom.move()
Realizacja Aplikacji Internetowych Wygląd …
Wygląd czyli CSS to początek Zerowanie stylów LESS, SASS Twitter BootStrap
Zerowanie stylów Problem: w poszczególnych przeglądarkach różne są domyślne wartości dla poszczególnych znaczników Minimum: * { padding: 0; margin: 0; } Podstawowy: * { vertical-align: baseline; font-weight: inherit; font-family: inherit; font-style: inherit; font-size: 100%; border: 0 none; outline: 0; padding: 0; margin: 0; } Zerujące CSS-y: Eric Meyer, Shaun Inmann, Chris Poteet
Zerowanie stylów Zalety: Piszemy od zera, a nie łatamy Tylko wybrane (zdefiniowane) style będą dostępne Wiele rzeczy jest prostszych np. okreslamy padding I nie musimy werifikować czy przypadkiem marginnie jest gdzieś ustawiony Łatwiej opanować róznicew między przegladarkami
Zerowanie stylów Wady: Wieksza waga wytryny Trzeba zdefiniować wszystko od początku = sporo pracy I tak nie załatwia wszystkich problemów kompatybilnościwych
Preprocesory CSS Najpopularniejsze: LESS, SASS Pozwalają uzywać rozszerzeń np: Zagnieżdzanie reguł Zmienne Instrukcje warunkowe Funkcje Importowanie plików Mix-iny Wymagają dodatkowego kroku - kompilacji
Twitter - Bootstrap Pozwala szybko tworzyć strony z gotowych elementów Dostępnych wiele gotowych rozwiązań/tematów 12 kolumnowy grid oparty na pixelach/procentach Reaktywny Wiele gotowych do użycia komponentów i wtyczek Wielo-platformowy: smartfony, przeglądarki http://bootstrapdocs.com/v3.0.3/docs/getting-started/ http://bootstrapdocs.com/v3.0.3/docs/components/