var p = {};
		p.foo = 'foo';
		console.log( p.foo );
		
		vs
		var p = 1; p.foo = 'foo'; console.log( p.foo );
	var q = 1;
	var p = "qwe";
	
	var s = `ala ${q} ma ${p} kota ${2*q+1}`;
	
			
			var foo = {
				_i : 0,
				get bar() {
					return foo._i;
				},
				set bar(i) {
					foo._i = i;
				}
			}
			console.log( foo.bar );
			foo.bar = 5;
			console.log( foo.bar );
			
				
			var foo = {
				_i : 0,
				get bar() {
					return foo._i;
				},
				set bar(i) {
					foo._i = i;
				}
			}
			Object.defineProperty( foo, 'qux', {
				get : function() {
					return 17;
				}
			});
			console.log( foo.qux );				
			
		
		function sump(x) {
			var _sum = x;
			var _f = function(y) {
				_sum += y;
				return _f;
			}
			_f.valueOf = function() {
				return _sum;
			} 
			return _f;
		}
		console.log( sump(4)(5)(6) + 1 );		
		
		
		function createGenerator() {
			var _state = 0;
			return {
				next : function() {
					return {
						value : _state,
						done : _state++ >= 10
					}
				}
			}
		}
		// enumerowanie przez while
		var it = createGenerator();		
		var _result;
		while ( _result = it.next(), !_result.done )
		  console.log( _result.value );
		// enumerowanie przez for
		var it = createGenerator();
		for ( var _result; _result = it.next(), !_result.done; )
		  console.log( _result.value );
		
		
		var foo = {
			[Symbol.iterator] : createGenerator
		};
		for ( var f of foo )
		  console.log(f);
		
		
		function* createGenerator() {
			for ( var _state = 0; _state<10; _state++ )
				yield _state;
		}
			
		
		function Person(name, surname) {
			return {
				name    : name,
				surname : surname,
				say     : function() {
					return `${this.name} ${this.surname}`;
				}
			}
		}
		
		var p = Person('jan', 'kowalski');
		
		console.log( p.say() );
		
		
		Proszę we własnym zakresie spróbować w tym podejściu zaimplementować dziedziczenie (czyli możliwość zdefiniowania "podklasy" w której
		w implementacji "konstruktora" i metod można odwołać się do implementacji konstruktora i metod z "klasy bazowej").
	
		
            var p = {
                name : 'jan',
                say : function() {
                    return this.name;
                }                
            };
            var q = {}
			
            // prototypem q będzie p
            Object.setPrototypeOf( q, p );
			
            q.name = 'tomasz';
            
            // q ma już metodę say, bo pochodzi ona z prototypu
            console.log( q.say() );		
		
		
		Prosty eksperyment z pomocą funkcji
		
		
		function getLastProto(o) {
			var p = o;
			do {
				o = p;
				p = Object.getPrototypeOf(o);
			} while (p);
			return o;
		}		
		
		
		pokazuje że wszystkie obiekty Javascript mają jedną, tę samą instancję obiektu jako swój prototyp (jest to ładna analogia do języków
		w których mamy hierarchie typów z jednym typem bazowym dla całej hierarchii - tu mamy jeden obiekt który jest wspólnym prototypem wszystkich
		obiektów, to w nim znajdują się wyjściowe implementacje m.in. (toString) - dlatego te 
		"podziedziczone" metody są dostępne dla wszystkich obiektów).
		
		
			var person = {
				init : function(name, surname) {
					this.name    = name;
					this.surname = surname;
				},
				say  : function() {
					return `${this.name} ${this.surname}`;
				}
			}
			
			var p = Object.create( person );
			p.init( 'jan', 'kowalski' );
			
			console.log( p.say() );
			
			
			var worker = Object.create( person );
			worker.init = function( name, surname, age ) {
				// "wywołanie konstruktora klasy bazowej"
				person.init.call( this, name, surname ); 
				this.age = age;
			}
			worker.say = function() {
				// "wywołanie metody z klasy bazowej"
				var _ = person.say.call( this );
				return `${_} ${this.age}`;
			}
			
			var w = Object.create( worker );
			w.init('tomasz','malinowski',48);
			console.log( w.say() );
			
			
            var Person = function(name, surname) {
                this.name = name;
                this.surname = surname;
            }
            Person.prototype.say = function() {
				return `${this.name} ${this.surname}`;                
            }
			
            var p = new Person('jan', 'kowalski');
            console.log( p.say() ); 
			
			
			
            var Worker = function(name, surname, age) {
                // wywołanie bazowej funkcji konstruktorowej
                Person.call( this, name, surname );
                this.age = age;
            }
            // powiązanie łańcucha prototypów
            Worker.prototype = Object.create( Person.prototype );
			
            Worker.prototype.say = function() {
                // "wywołanie metody z klasy bazowej"
                var _ = Person.prototype.say.call( this );
                return `${_} ${this.age}`;                
            }
            var w = new Worker('jan', 'kowalski', 48);
            console.log( w.say() );			
			
			
            // alternatywa dla new f()
            // wyrażona przy pomocy Object.create
            function New( f, ...args ) {
                var _ = Object.create( f.prototype );
                var o = f.apply( _, args );
                if ( o )
                    return o;
                else 
                    return _;
            }
            var p = New( Person, 'jan', 'kowalski' );
            console.log( p.say() );			
			
			
			
            // alternatywa dla Object.create( p ) 
            // wyrażona przy pomocy "new"
            function ObjectCreate( p ) {
                var f = function() { };
                f.prototype = p;
                return new f();
            }
            var p = ObjectCreate( person );
            p.init('jan', 'kowalski');
            console.log( p.say() );			
			
			
		String.prototype.reverse = function() {
			return this.split('').reverse().join("");
		}
		console.log( 'foo bar'.reverse() );		
		
		W ten sposób można wręcz rozszerzyć prototyp Object.prototype, dodając nowe metody do wszystkich
		możliwych obiektów.
		
		
            class Person {
                constructor(name, surname) {
                    this.name = name;
                    this.surname = surname;
                }
                say() {
                    return `${this.name} ${this.surname}`;
                }
            }
            class Worker extends Person {
                constructor(name, surname, age ) {
                    super(name, surname);
                    this.age = age;
                }
                say() {
                    // "wywołanie metody z klasy bazowej"
                    var _ = super.say();
                    return `${_} ${this.age}`;                
                }
            }
            var w = new Worker('tomasz', 'malinowski', 48);
            console.log( w.say() );			
			
			| C++/Java/C# | Javascript | 
|---|---|
| Klasy | Funkcje konstruktorowe | 
| Przestrzenie nazw | Obiekty z polami które są referencjami do innych obiektów | 
| Składowe prywatne | Umieszczanie elementów w domknięciach funkcji | 
| Modularność include/import/using | Modularność require, przykład z wykładu | 
		
		var fs = require('fs');
		function read1Sync()
		{
			var content = fs.readFileSync( './test.txt', 'utf-8' );
			console.log( content ); 
		}
		read1Sync();
		
		
		
		var fs = require('fs');
		function read1Async()
		{
			fs.readFile( './test.txt', 'utf-8', 
				function( err, data ) 
				{
					console.log( data );
				}
			);
		}
		read1Async();		
		
		
		var fs = require('fs');
		fs.readFile( 'foo.txt', 'utf-8', 
			function( err, data ) {
				fs.readFile( 'bar.txt, 'utf-8', 
					function( err, data2 ) {
						console.log( data );
						console.log( data2 );
					}
				}
			}
		);
		
		
		O takiej strukturze zagnieżdżonych wywołań funkcji oczekujących callbacków mówi się "callback-hell".
		Problem ten można tylko częściowo rozwiązać refaktoryzując kod do
		
		
		var fs = require('fs');
		fs.readFile( 'foo.txt', 'utf-8', 
			function( err, data ) {
				continuation(data);
			}
		);
		function continuation(data) 
		{
			fs.readFile( 'bar.txt, 'utf-8', 
				function( err, data2 ) {
					console.log( data );
					console.log( data2 );
				}
			}
		}
		
		
		ale to rozwiązanie nie jest najlepsze - powoduje że kod asynchroniczny staje się listą funkcji, których wzajemne wywoływania się nawzajem
		nie są łatwe do odczytania (niektórzy stosują konwencje komentowania, inni konwencje nazewnicze, ale to tylko półśrodki).
		
		O ultymatywnym rozwiązaniu problemu callback-hell za moment.
		
		
		var fs = require('fs');
		function read1Stream()
		{
			var buf ='';
			var s = fs.createReadStream( './test.txt', { encoding :'utf-8' } );
			s.on( 'data', function(data) {
				buf += data.toString();
			});
			s.on('end', function() {
				console.log( buf );
			});
		}
		read1Stream();		
		
		
		var fs = require('fs');
		function read1Stream()
		{
			var buf ='';
			var s = fs.createReadStream( './test.txt', { encoding :'utf-8' } );
			s.pipe( process.stdout );
		}
		read1Stream();				
		
				
		
		function fspromise(path) {
			return new Promise( function( res, rej ) {
				fs.readFile(path, function(err, data) {
					if ( err )
						rej(err );
					res( data.toString() );
				});
			} );
		}
		fspromise('foo.txt')
			.then(function(data) {
				console.log(data);
			});		
		
		
		Problem zaczyna się przy odczycie danych z dwóch lub więcej plików i można do tego podejść naiwnie zagnieżdżając wywołania
		
		
		fspromise('foo.txt')
			.then(function(data) {
				fspromise('bar.txt', function(data2) {
					console.log(data);				
					console.log(data2);				
				});
			});		
		
		
		albo zwracając Promise z then poprzedniej Promise, wtedy kolejny then
		odnosi się już do tej nowej Promise
		
		
		fspromise('foo.txt')
			.then(function(data) {
				console.log(data);				
				return fspromise('bar.txt');		
				});
			.then(function(data2) {
				console.log(data2);		
				})
			});		
		
		
		
		var f1 = fspromise('./alamakota.txt');
		var f2 = fspromise('./alamakota2.txt');
			
		var f3 = Promise.all([f1,f2])
			.then(function([data, data2]) {
				console.log( data );
				console.log( data2 );
			} );				
				
		Jak to możliwe? Bardzo naiwna implementacja zrobienia promise z dwóch innych:
		
		function promiseall(p1, p2) {
			return p1.then(function(p1r) {
				return p2.then(function(p2r) {
					return new Promise( function(res, rej) {
						res([p1r,p2r]);
					});
				});
			});
		}		
		
		
		Implementacja biblioteczna robi to oczywiście lepiej.
		Nie. Zarówno node.js jak i przeglądarki (oprócz IE) obsługują konstrukcję async/await za pomocą której pisanie kodu asynchronicznego nie będzie już wymagało ani funkcji zwrotnych ani promis, a kod będzie wyglądał jakby był synchroniczny.
W Javascript, async/await jest oparte o obiekty typu Promise.
						
			// funkcja oznakowana jako async
			// zwraca Promise
			function promisedGet(url) {
				return new Promise(function (resolve, reject) {
				var client = http.get(url, function (res) {
					var buffer = '';
					res
						.on('data', function (data) {
							buffer += data.toString();
						})
						.on('end', function () {
							resolve(buffer);
						});
				});
				});
			}
			// funkcja oznakowana jako async, 
			// wolno w niej wywoływać inną funkcję zwracającą Promise przez await
			(async function() {
			
			  // await ___ ...
			  var result = await promisedGet('http://www.google.pl');
			  console.log( result );
			  
			  // jest równoważne dzisiejszemu
			  //
			  // promisedGet('http://www.google.pl')
			  //   .then( function(result) { console.log( result );} );
			  // 
			  // a umieszczeniem całego kodu napisanego "pod" await w kontynuacji 
			  // doczepionej do Promise przez then zajmuje się już sam kompilator
			})();
			
			I to właśnie async/await jest ultymatywnym rozwiązaniem problemu callback-hell:
			(async function() {
			
				var data1 = await promisedGet('http://www.www1.com');
				var data2 = await promisedGet('http://www.www2.com');
							
			})();
			
		
		
			var fs   = require('fs');
			var util = require('util');
			
			var readFileAsync = util.promisify(fs.readFile);
			
			(async function() {
			
				var data1 = await readFileAsync('foo.txt');
				var data2 = await readFileAsync('bar.txt');
							
			})();
						
			
			
		
	var http = require('http');
	
	http.createServer( (req, res) => {
	
		res.setHeader('Content-type', 'text/html; charset=utf-8');
		res.write('Witamy w node.js, polskie znaki ąłżńóź');
		res.end();
	
	})
	.listen(3000);
	
	
	Adresowalny z przeglądarki przez http://localhost:3000
	
     https.createServer( {
        pfx : fs.readFileSync( 'test.pfx'),
        passphrase : 'foo'
		},
		(req, res) => ....
	);
	
	
	
var http = require('http');
var express = require('express');
var ejs = require('ejs');
var session = require('express-session');
var cookieParser = require('cookie-parser');
function createResponse(req, res, name) {
    res.setHeader('Content-type', 'text/html; charset=utf-8');
    res.write(
`<html>
<body>
Witaj ${name}, <br/>
<form method='POST'>
<input type='text' name='name' /> <input type='submit' value='Zapisz' />
</form>
</body>
</html>`            
    );
    res.end();
}
http
    .createServer((req, res) => {
        var imie;
        if ( req.method == 'GET') {
            createResponse(req, res, '');
        } else {
            var buf = '';
            req.on('data', data => {
                buf += data.toString();
            });
            req.on('end', () => {
                var postData = 
                    buf.split("&").map( s => s.split('='))
                        .reduce( (o,[k,v]) => (o[k] = v, o), {} );
                createResponse(req, res, postData.name);
            });
        }
    })
    .listen(3000);
console.log('started');	
	
	npm init -y npm install express --save npm install ejs --save
		var http = require('http');
		var express = require('express');
		
		var app = express();
		
		app.set('view engine', 'ejs');
		app.set('views', './views');
		
		// tu dodajemy middleware
		
		...
		
		// tu uruchamiamy serwer
		var server = http.createServer(app).listen(3000);
		
		console.log( 'serwer started' );
		
		
		
		<html>
		<body>
		  <% var foo = 'bar' %>
		  <% for ( var i=0; i<5; i++ ) { %>
			 <input value='<%= i %>' />
		  <% } %>
		</body>
		</html>		
		
		
		app.get('/', (req, res) => {
			var model = { user : 'jan' };
			res.render('user', model);
		});
		
		// user.ejs
		<html>
		<body>
		  Użytkownik : <%= user %>
		</body>
		</html>		
		
		
		
		app.get('/przelewy', (req, res) => {
			var przelewy = [
				{ kwota : 123, data : '2016-01-03', id = 1 },
				{ kwota : 124, data : '2016-01-02', id = 2 },
				{ kwota : 125, data : '2016-01-01', id = 3 },
			];
			
			res.render('przelewy', { przelewy : przelewy } );
		});
		
		// przelewy.ejs
		<html>
		<body>
		  <table>
			<tr>
			   <th>Data</th>
			   <th>Kwota</th>
			   <th></th>
			</tr>
		  <% przelewy.forEach( przelew => { %>
			<tr>
			   <td><%= przelew.data %></td>
			   <td><%= przelew.kwota %></td>
			   <td><a href='/przelew/<%= przelew.id %>'>Więcej</a></td>
			</tr>
		  <% }) %>
		  </table>
		</body>
		</html>				
		
		ol>li*5i naciśnięcie TAB spowoduje rozwinięcie tego w listę z 5 elementami. Zamiana rozszerzeń polega na tym że zmienia się rozszerzenia plików a w kodzie aplikacji rejestruje się nowy, "udawany", silnik renderowania, na funkcję która pochodzi z EJS
		var ejs = require('ejs');
		...
		
		app.set('view engine', 'html');
		app.set('views', './views');
		
		app.engine('html', ejs.renderFile);
		
		
		
		
		app.get('/plik', (req, res) => {
			
			// proszę zaremować i odremować tę linijkę i porównać wynik
			res.setHeader('Content-disposition', 'attachment; filename=plik.txt');
			
			res.write('123');
			res.end();
		});
		
				
		
		res.redirect('innastrona');
		
		
		
		żądanie do http://localhost:3000/strona?p1=v1&p2=v2
		
		app.get('/strona', (req, res) => {
			var p1 = req.query.p1;
			var p2 = req.query.p2;
		});
		
		Uwaga na możliwy atak Querystring Tamepring
				
					
		app.use( express.urlencoded({extended:true}) ) ;
		
		app.get('/login', (req, res) => {
		   res.render('login');
		});
		
		app.post('/login', (req, res) => {
			var username = req.body.username;
			var pwd      = req.body.pwd;
		});
		
		// login.ejs
		<html>
		<body>
		  <form method="POST">
			<div>
			   Login: <input type='text' name='username' />
			</div>
			<div>
			   Password: <input type='password' name='pwd' /> 
			</div>
			<div>
			   <button>Login</button>
			</div>
		  </form>
		</body>
		</html>		
		
		
		
		// instalacja middleware
		app.use( express.static('./static'));		
		
		Od tego momentu wszystkie pliki w folderze '/static' są serwowane na żądania adresowane przez '/', np. do pliku 
		'/static/app.css' odwołamy się przez 
		<link rel='stylesheet' href='/app.css'>
		import * as http from "http" // zaremować przed uruchomieniem
		
		/**
		 * 
		 * @param {http.IncomingMessage} req 
		 * @param {http.ServerResponse}  res 
		 * @param {*} next 
		 */
		function middleware(req, res, next) {
			req. // <- tu działa podpowiadanie składni
		}		
		
		
		app.use((req,res,next) => {
			res.render('404.ejs', { url : req.url });
		});
		
		// 404.ejs
		<html>
		<body>
		  Strona <%= url %> nie została znaleziona.
		</body>
		</html>
		
		
		app.get('/przelew/:id', (req, res) => {
			var id = req.params.id;
			res.render('przelew', { id : id } );
		});
				
		Tak zdefiniowany routing może korzystać z wyrażeń regularnych
		Przykładowo, ścieżka która chwyta wyłącznie liczby
		
		app.get('/przelew/:id(\\d+)', (req, res) => {
			var id = req.params.id;
			res.render('przelew', { id : id } );
		});
				
		"console": "integratedTerminal"w .vscode/launch.json w Visual Studio Code, umożliwiająca przełączenie konsoli do terminala (z zakładki debug console)
		app.disable('etag');
		
		
		// instalacja middleware
		npm install cookie-parser --save
		
		// w aplikacji
		var cookieParser = require('cookie-parser');
		app.use( cookieParser() );
		
		// wydanie ciastka
		res.cookie('foo', 'bar');
		
		// sprawdzenie ciastka
		req.cookies.foo;
		
		
		// wydanie ciastka
		res.cookie('foo', 'bar', { maxAge : 60 * 1000 } ); // 60 sekund
		
		// usunięcie ciastka z przeglądarki
		res.cookie('foo', '', { maxAge : -1 } ); 
		
		
		// w aplikacji
		var cookieParser = require('cookie-parser');
		app.use( cookieParser('9877dg9fb8sd79b87sdt9b87ds98b') ); // secret
		
		// wydanie ciastka
		res.cookie('foo', 'bar', { signed : true });
		
		// sprawdzenie ciastka
		req.signedCookies.foo;
		
		
		// instalacja middleware
		npm install express-session --save
		
		// deklaracja użycia middleware w potoku	
		app.use(session({resave:true, saveUninitialized: true, secret: 'qewhiugriasgy'}));
		// dodawanie danych do sesji
		req.session.foo = 'bar';
		
		// odczyt danych z sesji
		var foo = req.session.foo;
		
		
		// select.ejs
		<select name='<%= name %>'>
		   <% options.forEach( option => { %>
			   <option value='<%= option.value %>'><%= option.text %></option>
		   <% }) %>
		</select>
		
		// wywołanie szablonu z innego szablonu
		<% var name='combo1' %>
		<% var options= [
			 { value : 1, text : 'element 1' },
			 { value : 2, text : 'element 2' },
			 { value : 3, text : 'element 3' }
		   ]
		%>
		<% include select %>		
				
		
				app.get('/', (req, res) => {
					if ( !req.cookie.user )
						res.redirect('/login');
					else
						...
				});
				
				
				// middleware logowania
				function authorize(req, res, next) {
					if ( req.cookies.user ) {
					   // użytkownik jest zalogowany
					   // "przepisanie" danych użytkownika żeby dostęp był jednolity
					   req.user = req.cookies.user; 
					   next();
					} else {
						// użytkownik niezalogowany
						// przekierowanie z zapamiętaniem strony która spowodowała przekierowanie
						res.redirect( '/login?returnUrl='+req.url); 
					}
				}
				
				// wymuszenie logowania na konkretnym zasobie
				app.get('/', authorize, (req, res) => {
					// to wykona się tylko jeśli middleware authorize przejdzie przez ścieżkę zalogowanego użytkownika
					// w przeciwnym przypadku żądanie zostanie tam przekierowane na /login?returnUrl=/
				});
				
				// logowanie
				app.get('/login', (req, res) => {
					// wyrenderuj stronę logowania
					res.render('login');
				});
				
				app.post('/login', (req, res) => {
					// użytkownik kliknął przycisk "zaloguj" na stronie logowania (POST)
					var username = req.body.txtUser; // na stronie jest <input type='text'     name='txtUser' ...
					var pwd      = req.body.txtPwd;  // na stronie jest <input type='password' name='txtPwd' ...
					
					if ( username == pwd ) { // w rzeczywistości - sprawdzenie w bazie danych 
						res.cookie('user', username); // wydanie ciastka, od tego momentu użytkownik jest zalogowany
						
						var returnUrl = req.query.returnUrl; 
						res.redirect( returnUrl ); // powrót do miejsca z którego nastąpiło przekierowanie tutaj
					} else {
						// nie udało się zalogować, trzeba znów pokazać stronę logowania z komunikatem
						res.render('login', { message : 'Zła nazwa logowania lub hasło' });
					}
				});
				
				npm installżeby dociągnąć wszystkie pakiety wymienione w package.json.
git init git add . git commit -m "initial commit" heroku login heroku create git push heroku master
CREATE TABLE [dbo].[Child]( [ID] [int] IDENTITY(1,1) NOT NULL, [ChildName] [nvarchar](150) NOT NULL, [ID_PARENT] [int] NOT NULL, CONSTRAINT [PK_Child] PRIMARY KEY CLUSTERED ( [ID] ASC ) ) ON [PRIMARY] GO CREATE TABLE [dbo].[Parent]( [ID] [int] IDENTITY(1,1) NOT NULL, [ParentName] [nvarchar](150) NOT NULL, CONSTRAINT [PK_Parent] PRIMARY KEY CLUSTERED ( [ID] ASC ) ) ON [PRIMARY] ALTER TABLE [dbo].[Child] WITH CHECK ADD CONSTRAINT [FK_Child_Parent] FOREIGN KEY([ID_PARENT]) REFERENCES [dbo].[Parent] ([ID])
	try {
		var conn = new sql.ConnectionPool('server=localhost,1433;database=database2;user id=foo;password=foo');
		await conn.connect();
		
		var request   = new sql.Request(conn);
		var result    = await request.query('select * from Parent')	
		
		result.recordset.forEach( r => {
			console.log( `${r.ID} ${r.ParentName}<br/>`);
		});
		
		conn.close();
	}
	catch ( err ) {
		console.log( err );
	}
		
	
	// repo/parentRepo
	module.exports.insert = async function(conn, parent) {
		
		try {
			var req = new sql.Request(conn);
			req.input("ParentName", parent.parentName);
			
			var result = await req.query( "insert into Parent (ParentName) values (@ParentName) select scope_identity() as id");
			
			return result.recordset[0].id;
		}
		catch ( err ) {
			// cokolwiek, włącznie z throw err;
		}
		
	}
	
	
var assert = require('assert');
describe('Top level', function() {
    describe('Sub level', function() {
        
    it( 'Unit test function definition', function() {
            assert.equal( [1,2,3].indexOf(4), -1 );
        });
    });
});		
		
		
var webdriverio = require('webdriverio');
var options = { desiredCapabilities: { browserName: 'chrome' } };
var client = webdriverio.remote(options);
client
    .init()
    .url('https://duckduckgo.com/')
    .setValue('#search_form_input_homepage', 'WebdriverIO')
    .click('#search_button_homepage')
    .getTitle().then(function(title) {
        console.log('Title is: ' + title);
        // outputs: "Title is: WebdriverIO (Software) at DuckDuckGo"
    })
    .end();