├── tasks ├── 09 │ ├── src │ │ ├── Scope.js │ │ ├── app.js │ │ ├── DOMCompiler.js │ │ ├── Provider.js │ │ └── Utils.js │ ├── index.html │ └── README.md ├── 02 │ ├── solutions │ │ ├── cons-car-cdr.js │ │ ├── sum.js │ │ ├── sequence-and-compose.js │ │ ├── sumSquaresOfArgs.js │ │ ├── map-filt-red-native.js │ │ ├── curry.js │ │ └── map-filt-red-with-pair.js │ ├── more-examples │ │ └── complement.js │ └── README.md ├── 03 │ ├── homework │ │ ├── bootstrap │ │ │ ├── lib │ │ │ │ ├── observers │ │ │ │ │ ├── Observer.js │ │ │ │ │ ├── LogObserver.js │ │ │ │ │ └── MailObserver.js │ │ │ │ └── observables │ │ │ │ │ ├── PostsCollection.js │ │ │ │ │ └── Observable.js │ │ │ ├── public │ │ │ │ ├── index.html │ │ │ │ └── src │ │ │ │ │ └── main.js │ │ │ ├── package.json │ │ │ └── index.js │ │ └── README.md │ ├── README.md │ └── solutions.js ├── 05 │ ├── 01.js │ ├── solutions │ │ ├── simpleGenerator.js │ │ ├── asyncgen.js │ │ └── task1.js │ └── README.md ├── 08 │ └── wsChat │ │ ├── package.json │ │ ├── app.js │ │ └── client.js ├── 04 │ ├── solutions │ │ ├── task2-es6.js │ │ ├── task1.js │ │ └── task2.js │ └── README.md ├── 07 │ └── README.md ├── 01 │ └── README.md └── 06 │ └── README.md ├── lectures ├── 07-express │ ├── expressServer │ │ ├── static │ │ │ └── index.html │ │ ├── package.json │ │ ├── app.js │ │ └── router.js │ ├── generator.js │ └── httpServer │ │ └── app.js ├── 04-01-moar-modules.markdown ├── 03-01-moar-functional.markdown ├── 00-intro.markdown ├── 12-d3.markdown ├── 02-modules.markdown ├── 09-websockets.markdown ├── 01-variables-functions.markdown ├── 08-dom-intro-and-events.markdown ├── 05-02-promises-and-generators-short.markdown ├── 05-01-es2015.markdown ├── 06-node-js.markdown └── 03-functional.markdown ├── html └── img │ ├── ko.jpg │ ├── arch.png │ ├── both.png │ ├── fmi.jpg │ ├── http.png │ ├── lion.jpg │ ├── mvw.png │ ├── mvw2.png │ ├── node.gif │ ├── rest.png │ ├── w3c.png │ ├── complex.jpg │ ├── ie-glue.jpg │ ├── jungle.jpg │ ├── magic.jpg │ ├── runtime.png │ ├── savior.png │ ├── scopes.png │ ├── socket.jpg │ ├── sofiajs.png │ ├── worship.jpg │ ├── arch-view.png │ ├── backbone.gif │ ├── crockford.png │ ├── promises.png │ ├── prototype.png │ ├── tailfact.png │ ├── tcp-joke.png │ ├── waitforit.jpg │ ├── all-objects.png │ ├── arch-scope.png │ ├── assignment.png │ ├── broken-heart.jpg │ ├── call_stack.png │ ├── event_loop.png │ ├── great-power.png │ ├── heavy-loaded.jpg │ ├── io_callback.png │ ├── outofthebox.png │ ├── prototype1.png │ ├── scopes-real.png │ ├── typed_arrays.png │ ├── arch-services.png │ ├── callback-hell.png │ ├── dennis_ritchie.jpg │ ├── hidden_classes.png │ ├── not-understand.jpg │ ├── praise-lambda.png │ ├── pure_function.jpg │ ├── arch-controller.png │ ├── arch-directives.png │ ├── call_stack_unfold.png │ ├── consumers_sources.jpg │ ├── impure_function.jpg │ ├── much_much_later.jpg │ ├── mvc-server-side.png │ ├── node-event-loop.jpg │ ├── spring-triangle.png │ ├── tooling │ ├── android.jpg │ ├── cpu-profiler.png │ ├── chrome-timeline.jpg │ ├── facebook-sprite.png │ ├── memory-profiling.png │ └── chrome-dev-tools-network.png │ ├── assignment-example.png │ ├── pure_phylosoraptor.jpg │ ├── promise_states_simple.jpg │ ├── separation-of-concerns.png │ ├── separationofconcerns.jpg │ ├── javascript-objects-treasure-map.png │ ├── composite.svg │ ├── strategy.svg │ ├── observer.svg │ └── facade.svg ├── README.markdown ├── package.json ├── layout.html.mustache ├── compile.js └── .gitignore /tasks/09/src/Scope.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tasks/09/src/app.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tasks/09/src/DOMCompiler.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tasks/09/src/Provider.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lectures/07-express/expressServer/static/index.html: -------------------------------------------------------------------------------- 1 | Hello world! 2 | -------------------------------------------------------------------------------- /html/img/ko.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/ko.jpg -------------------------------------------------------------------------------- /html/img/arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/arch.png -------------------------------------------------------------------------------- /html/img/both.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/both.png -------------------------------------------------------------------------------- /html/img/fmi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/fmi.jpg -------------------------------------------------------------------------------- /html/img/http.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/http.png -------------------------------------------------------------------------------- /html/img/lion.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/lion.jpg -------------------------------------------------------------------------------- /html/img/mvw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/mvw.png -------------------------------------------------------------------------------- /html/img/mvw2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/mvw2.png -------------------------------------------------------------------------------- /html/img/node.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/node.gif -------------------------------------------------------------------------------- /html/img/rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/rest.png -------------------------------------------------------------------------------- /html/img/w3c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/w3c.png -------------------------------------------------------------------------------- /html/img/complex.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/complex.jpg -------------------------------------------------------------------------------- /html/img/ie-glue.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/ie-glue.jpg -------------------------------------------------------------------------------- /html/img/jungle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/jungle.jpg -------------------------------------------------------------------------------- /html/img/magic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/magic.jpg -------------------------------------------------------------------------------- /html/img/runtime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/runtime.png -------------------------------------------------------------------------------- /html/img/savior.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/savior.png -------------------------------------------------------------------------------- /html/img/scopes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/scopes.png -------------------------------------------------------------------------------- /html/img/socket.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/socket.jpg -------------------------------------------------------------------------------- /html/img/sofiajs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/sofiajs.png -------------------------------------------------------------------------------- /html/img/worship.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/worship.jpg -------------------------------------------------------------------------------- /html/img/arch-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/arch-view.png -------------------------------------------------------------------------------- /html/img/backbone.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/backbone.gif -------------------------------------------------------------------------------- /html/img/crockford.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/crockford.png -------------------------------------------------------------------------------- /html/img/promises.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/promises.png -------------------------------------------------------------------------------- /html/img/prototype.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/prototype.png -------------------------------------------------------------------------------- /html/img/tailfact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/tailfact.png -------------------------------------------------------------------------------- /html/img/tcp-joke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/tcp-joke.png -------------------------------------------------------------------------------- /html/img/waitforit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/waitforit.jpg -------------------------------------------------------------------------------- /html/img/all-objects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/all-objects.png -------------------------------------------------------------------------------- /html/img/arch-scope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/arch-scope.png -------------------------------------------------------------------------------- /html/img/assignment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/assignment.png -------------------------------------------------------------------------------- /html/img/broken-heart.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/broken-heart.jpg -------------------------------------------------------------------------------- /html/img/call_stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/call_stack.png -------------------------------------------------------------------------------- /html/img/event_loop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/event_loop.png -------------------------------------------------------------------------------- /html/img/great-power.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/great-power.png -------------------------------------------------------------------------------- /html/img/heavy-loaded.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/heavy-loaded.jpg -------------------------------------------------------------------------------- /html/img/io_callback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/io_callback.png -------------------------------------------------------------------------------- /html/img/outofthebox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/outofthebox.png -------------------------------------------------------------------------------- /html/img/prototype1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/prototype1.png -------------------------------------------------------------------------------- /html/img/scopes-real.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/scopes-real.png -------------------------------------------------------------------------------- /html/img/typed_arrays.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/typed_arrays.png -------------------------------------------------------------------------------- /html/img/arch-services.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/arch-services.png -------------------------------------------------------------------------------- /html/img/callback-hell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/callback-hell.png -------------------------------------------------------------------------------- /html/img/dennis_ritchie.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/dennis_ritchie.jpg -------------------------------------------------------------------------------- /html/img/hidden_classes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/hidden_classes.png -------------------------------------------------------------------------------- /html/img/not-understand.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/not-understand.jpg -------------------------------------------------------------------------------- /html/img/praise-lambda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/praise-lambda.png -------------------------------------------------------------------------------- /html/img/pure_function.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/pure_function.jpg -------------------------------------------------------------------------------- /html/img/arch-controller.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/arch-controller.png -------------------------------------------------------------------------------- /html/img/arch-directives.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/arch-directives.png -------------------------------------------------------------------------------- /html/img/call_stack_unfold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/call_stack_unfold.png -------------------------------------------------------------------------------- /html/img/consumers_sources.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/consumers_sources.jpg -------------------------------------------------------------------------------- /html/img/impure_function.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/impure_function.jpg -------------------------------------------------------------------------------- /html/img/much_much_later.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/much_much_later.jpg -------------------------------------------------------------------------------- /html/img/mvc-server-side.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/mvc-server-side.png -------------------------------------------------------------------------------- /html/img/node-event-loop.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/node-event-loop.jpg -------------------------------------------------------------------------------- /html/img/spring-triangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/spring-triangle.png -------------------------------------------------------------------------------- /html/img/tooling/android.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/tooling/android.jpg -------------------------------------------------------------------------------- /html/img/assignment-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/assignment-example.png -------------------------------------------------------------------------------- /html/img/pure_phylosoraptor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/pure_phylosoraptor.jpg -------------------------------------------------------------------------------- /html/img/promise_states_simple.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/promise_states_simple.jpg -------------------------------------------------------------------------------- /html/img/separation-of-concerns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/separation-of-concerns.png -------------------------------------------------------------------------------- /html/img/separationofconcerns.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/separationofconcerns.jpg -------------------------------------------------------------------------------- /html/img/tooling/cpu-profiler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/tooling/cpu-profiler.png -------------------------------------------------------------------------------- /html/img/tooling/chrome-timeline.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/tooling/chrome-timeline.jpg -------------------------------------------------------------------------------- /html/img/tooling/facebook-sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/tooling/facebook-sprite.png -------------------------------------------------------------------------------- /html/img/tooling/memory-profiling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/tooling/memory-profiling.png -------------------------------------------------------------------------------- /html/img/javascript-objects-treasure-map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/javascript-objects-treasure-map.png -------------------------------------------------------------------------------- /html/img/tooling/chrome-dev-tools-network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FMIjs/js-lectures-2015/HEAD/html/img/tooling/chrome-dev-tools-network.png -------------------------------------------------------------------------------- /tasks/02/solutions/cons-car-cdr.js: -------------------------------------------------------------------------------- 1 | 2 | function cons(a, b) { 3 | return (executor) => executor(a, b); 4 | } 5 | 6 | function car(c) { 7 | return c((a, b) => a); 8 | } 9 | 10 | function cdr(c) { 11 | return c((a, b) => b); 12 | } 13 | -------------------------------------------------------------------------------- /tasks/02/solutions/sum.js: -------------------------------------------------------------------------------- 1 | function sum(a, b) { 2 | if (typeof b === 'undefined') { 3 | return function (c) { 4 | return a + c; 5 | }; 6 | } else { 7 | return a + b; 8 | } 9 | } 10 | var addTen = sum(10); 11 | 12 | addTen(10) 13 | // > 20 -------------------------------------------------------------------------------- /tasks/03/homework/bootstrap/lib/observers/Observer.js: -------------------------------------------------------------------------------- 1 | /* global module */ 2 | 3 | function Observer() {} 4 | 5 | Observer.prototype.update = function () { 6 | 'use strict'; 7 | throw new Error('Not implemented'); 8 | }; 9 | 10 | module.exports = Observer; 11 | 12 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # JavaScript за напреднали @ ФМИ 2015/16 2 | 3 | [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/FMIjs/gitter?utm_source=share-link&utm_medium=link&utm_campaign=share-link) 4 | 5 | ```sh 6 | npm install 7 | ./compile.js 8 | firefox compiled/00-intro.html 9 | ``` 10 | -------------------------------------------------------------------------------- /tasks/05/01.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Writable = require('stream').Writable; 4 | 5 | 6 | class FileReader extends Writable { 7 | constructor() { 8 | super(); 9 | this.buffer = []; 10 | console.log(this); 11 | } 12 | 13 | _write() 14 | } 15 | 16 | 17 | let fr = new FileReader(); 18 | console.log(fr); 19 | -------------------------------------------------------------------------------- /tasks/03/homework/bootstrap/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /tasks/09/src/Utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * DO NOT USE IN PRODUCTION 3 | */ 4 | var Utils = { 5 | equals: function (a, b) { 6 | 'use strict'; 7 | return JSON.stringify(a) === JSON.stringify(b); 8 | }, 9 | clone: function (a) { 10 | 'use strict'; 11 | try { 12 | return JSON.parse(JSON.stringify(a)); 13 | } catch (e) { 14 | return undefined; 15 | } 16 | } 17 | }; 18 | 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js2015-slides", 3 | "version": "0.0.1", 4 | "description": "slides for the javascript course at FMI 2015/2016", 5 | "main": "compile.js", 6 | "repository": "https://github.com/FMIjs/js-lectures-2015", 7 | "scripts": {}, 8 | "author": "", 9 | "license": "MIT", 10 | "dependencies": { 11 | "cpr": "^1.0.0", 12 | "hogan": "^1.0.2", 13 | "q": "^1.0.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tasks/03/homework/bootstrap/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "observer", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "mgechev", 10 | "license": "MIT", 11 | "dependencies": { 12 | "express": "~4.4.1", 13 | "nodemailer": "~0.6.5", 14 | "body-parser": "~1.3.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lectures/07-express/expressServer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "07-express", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "= <=>", 10 | "license": "ISC", 11 | "dependencies": { 12 | "body-parser": "^1.14.1", 13 | "connect-busboy": "0.0.2", 14 | "express": "^4.13.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tasks/08/wsChat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ws-socket", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "= <=>", 10 | "license": "ISC", 11 | "dependencies": { 12 | "express": "^4.13.3", 13 | "keypress": "^0.2.1", 14 | "socket.io": "^1.3.7", 15 | "socket.io-client": "^1.3.7" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tasks/03/homework/bootstrap/lib/observables/PostsCollection.js: -------------------------------------------------------------------------------- 1 | /* global require, module */ 2 | 3 | var Observable = require('./Observable'); 4 | 5 | // Extend Observable 6 | function PostsCollection() { 7 | 'use strict'; 8 | this.posts = []; 9 | } 10 | 11 | PostsCollection.prototype.addPost = function (title, content) { 12 | 'use strict'; 13 | // Add post and notify about the change 14 | }; 15 | 16 | module.exports = PostsCollection; 17 | 18 | -------------------------------------------------------------------------------- /tasks/02/solutions/sequence-and-compose.js: -------------------------------------------------------------------------------- 1 | function sequence() { 2 | var funcs = [].slice.call(arguments); 3 | return function (arg) { 4 | return funcs.reduce(function (accum, fn) { 5 | return fn(accum); 6 | }, arg); 7 | }; 8 | } 9 | 10 | function compose() { 11 | var funcs = [].slice.call(arguments); 12 | return function (arg) { 13 | return funcs.reduceRight(function (accum, fn) { 14 | return fn(accum); 15 | }, arg); 16 | }; 17 | } -------------------------------------------------------------------------------- /tasks/02/solutions/sumSquaresOfArgs.js: -------------------------------------------------------------------------------- 1 | function sumSquaresOfOddArgs() { 2 | return [].call(arguments) 3 | .filter(function (el) { 4 | return el % 2 === 1; 5 | }) 6 | .map(function (el) { 7 | return el * el; 8 | }) 9 | .reduce(function (accumulation, current) { 10 | return accumulation + current; 11 | }, 0); 12 | } 13 | 14 | 15 | function mapAndFilter() { 16 | return [].map.call(arguments, function (a) { 17 | return a * a; 18 | }).filter(function (el) { 19 | return el % 2 === 0; 20 | }); 21 | } -------------------------------------------------------------------------------- /tasks/03/homework/bootstrap/lib/observers/LogObserver.js: -------------------------------------------------------------------------------- 1 | /* global module, require */ 2 | 3 | var Observer = require('./Observer'), 4 | // use fs.writeFileSync(fileName, content) 5 | fs = require('fs'); 6 | 7 | function LogObserver(config) { 8 | 'use strict'; 9 | this.config = config; 10 | } 11 | 12 | LogObserver.prototype = Object.create(Observer.prototype); 13 | 14 | LogObserver.prototype.update = function (title, data) { 15 | 'use strict'; 16 | // Implement the method 17 | }; 18 | 19 | module.exports = LogObserver; 20 | 21 | -------------------------------------------------------------------------------- /tasks/05/solutions/simpleGenerator.js: -------------------------------------------------------------------------------- 1 | function countFive(gen) { 2 | var n = 0; 3 | var nextNumber = function() { 4 | setTimeout(function() { 5 | if(n < 6) { 6 | return iterable.next(n++);//go to yield with n++ 7 | } 8 | },0); 9 | return undefined; 10 | }; 11 | var iterable = gen(nextNumber); 12 | iterable.next(); //kick start 13 | } 14 | 15 | countFive(function* (nextNumber) { 16 | while(true){ 17 | var num = yield nextNumber(); 18 | console.log(num); 19 | } 20 | }) -------------------------------------------------------------------------------- /tasks/02/more-examples/complement.js: -------------------------------------------------------------------------------- 1 | function complement(predicate) { 2 | return function () { 3 | return !predicate.apply(null, arguments) 4 | }; 5 | } 6 | 7 | // example usage 8 | 9 | function isCool(person) { 10 | return person.isCool === true; 11 | } 12 | 13 | var isNotCool = complement(isCool); 14 | 15 | var people = [{ 16 | name: 'Ivan' 17 | isCool: true 18 | }, { 19 | name: 'Pesho', 20 | isCool: false 21 | }, { 22 | name: 'Dragan', 23 | isCool: false 24 | }]; 25 | 26 | var coolPeople = people.filter(isCool); 27 | var notCoolPeople = people.filter(complement(isCool)); -------------------------------------------------------------------------------- /lectures/07-express/generator.js: -------------------------------------------------------------------------------- 1 | function readFile(filename, callback) { 2 | setTimeout(function() { 3 | callback(null, 'File ' + filename + ' contents'); 4 | }, 5000); 5 | } 6 | 7 | function* f(maxN) { 8 | while(maxN < 5) { 9 | var x = yield readFile('alabala', niakva); 10 | console.log(x); 11 | maxN++; 12 | //return 1000; 13 | } 14 | } 15 | 16 | function niakva(error, result) { 17 | //console.log(result); 18 | iter.next(result); 19 | } 20 | 21 | 22 | var iter = f(1); 23 | console.log(iter.next()); //kick start 24 | //console.log(iter.next(500)); 25 | -------------------------------------------------------------------------------- /tasks/03/homework/bootstrap/lib/observers/MailObserver.js: -------------------------------------------------------------------------------- 1 | /* global require, module */ 2 | 3 | var Observer = require('./Observer'), 4 | // more information about the API at 5 | // http://www.nodemailer.com/docs/usage-example 6 | mail = require('nodemailer').mail; 7 | 8 | function MailObserver(config) { 9 | 'use strict'; 10 | this.config = config; 11 | } 12 | 13 | MailObserver.prototype = Object.create(Observer.prototype); 14 | 15 | MailObserver.prototype.update = function (title, data) { 16 | 'use strict'; 17 | // Implement the method 18 | }; 19 | 20 | module.exports = MailObserver; 21 | 22 | -------------------------------------------------------------------------------- /tasks/03/homework/bootstrap/lib/observables/Observable.js: -------------------------------------------------------------------------------- 1 | /* global module */ 2 | 3 | function Observable() { 4 | 'use strict'; 5 | this.observers = []; 6 | } 7 | 8 | Observable.prototype.addObserver = function (ob) { 9 | 'use strict'; 10 | this.observers.push(ob); 11 | }; 12 | 13 | Observable.prototype.removeObserver = function (ob) { 14 | 'use strict'; 15 | this.observers.splice(this.observers.indexOf(ob), 1); 16 | }; 17 | 18 | Observable.prototype.update = function () { 19 | 'use strict'; 20 | var args = arguments; 21 | this.observers.forEach(function (ob) { 22 | ob.update.apply(ob, args); 23 | }); 24 | }; 25 | 26 | module.exports = Observable; 27 | 28 | -------------------------------------------------------------------------------- /tasks/03/homework/bootstrap/public/src/main.js: -------------------------------------------------------------------------------- 1 | /* global document, 2 | location, XMLHttpRequest, JSON */ 3 | 4 | (function () { 5 | 6 | 'use strict'; 7 | 8 | function $(id) { 9 | return document.getElementById(id); 10 | } 11 | 12 | function postArticle(title, content) { 13 | var xhr = new XMLHttpRequest(), 14 | post = { title: title, content: content }; 15 | xhr.open('post', location.protocol + '//' + location.host + '/post', true); 16 | xhr.setRequestHeader('Content-type', 'application/json'); 17 | xhr.send(JSON.stringify(post)); 18 | } 19 | 20 | $('send-btn').onclick = function () { 21 | postArticle($('title').value, $('content').value); 22 | }; 23 | }()); 24 | -------------------------------------------------------------------------------- /tasks/04/solutions/task2-es6.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var promisify = require('es6-promisify'); 3 | var _readFile = promisify(fs.readFile); 4 | var _writeFile = promisify(fs.writeFile); 5 | var text1; 6 | 7 | function wait(time) { 8 | return new Promise(res => setTimeout(res, time)); 9 | } 10 | 11 | wait(1000) 12 | .then(() => _writeFile('hello.txt', 'text of file I', 'utf8')) 13 | .then(() => wait(1000)) 14 | .then(() => _writeFile('hello2.txt', 'text of file II', 'utf8')) 15 | .then(() => wait(1000)) 16 | .then(() => _readFile('hello.txt')) 17 | .then((txt) => text1 = txt) 18 | .then(() => wait(1000)) 19 | .then(() => _readFile('hello2.txt')) 20 | .then((text2) => _writeFile('result.txt', `${text1} ${text2}`, 'utf8')) 21 | .catch((err) => console.log(err)); -------------------------------------------------------------------------------- /tasks/02/solutions/map-filt-red-native.js: -------------------------------------------------------------------------------- 1 | /** 2 | * this implementation works on a list with a last element always a list or nothing 3 | * cons(1, cons(2, cons(3, cons(4, cons(5))))) 4 | */ 5 | 6 | function map(c, f) { 7 | return typeof c === 'function' && cons(f(car(c)), map(cdr(c), f)); 8 | } 9 | 10 | function filter(c, f) { 11 | if (typeof c !== 'function') { 12 | return undefined; 13 | } 14 | if (f(car(c))) { 15 | return cons(car(c), filter(cdr(c), f)); 16 | } 17 | return filter(cdr(c), f); 18 | } 19 | 20 | function forEach(c, f) { 21 | if (typeof c === 'function' && car(c)) { 22 | f(car(c)); 23 | forEach(cdr(c), f); 24 | } 25 | } 26 | 27 | function reduce(c, fn, init) { 28 | return typeof c === 'function' ? reduce(cdr(c), fn, fn(init, car(c))) : init; 29 | } -------------------------------------------------------------------------------- /tasks/08/wsChat/app.js: -------------------------------------------------------------------------------- 1 | var port = 8888, 2 | app = require('express')(), 3 | http = require('http').Server(app), 4 | io = require('socket.io')(http), 5 | counter = 0; 6 | 7 | io.on('connection', function(socket) { 8 | socket.username = socket.handshake.query.username || 'user' + (++counter); 9 | socket.emit('username', socket.username); 10 | 11 | socket.on('message', function(msg) { 12 | console.log('new message from ' + socket.username + ': ' + msg); 13 | this.broadcast.emit('message', { text: msg, username: this.username }); 14 | }); 15 | 16 | socket.on('disconnect', function() { 17 | console.log('user ' + this.username + ' disconnected'); 18 | counter--; 19 | }); 20 | }); 21 | 22 | http.listen(port, function() { 23 | console.log('listening on ' + port); 24 | }); -------------------------------------------------------------------------------- /tasks/05/solutions/asyncgen.js: -------------------------------------------------------------------------------- 1 | // runner for async shit 2 | var run = function(generator) { 3 | var gi; 4 | var util = function(asyncFun) { 5 | console.log('use the util to have a reference to thyself in async.'); 6 | asyncFun(gi); 7 | } 8 | 9 | gi = generator(util); 10 | gi.next(); // kick/start the generator 11 | } 12 | 13 | // main program 14 | run( function *(util) { 15 | 16 | util(function(main) { 17 | setTimeout(function () { 18 | console.log('first async completed.'); 19 | main.next(); 20 | }); 21 | }); 22 | yield; 23 | console.log('do something after first async'); 24 | /* do some more shit */ 25 | 26 | yield util(function(main) { 27 | setTimeout(function () { 28 | console.log('seocnd async completed.'); 29 | main.next(); 30 | }); 31 | }); 32 | 33 | console.log('do something after first async'); 34 | /* do some more shit */ 35 | }) 36 | 37 | 38 | -------------------------------------------------------------------------------- /lectures/07-express/httpServer/app.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var port = 8888; 3 | 4 | var server = http.createServer(function(req, res) { 5 | var allData = []; 6 | 7 | req.on('data', function(chunk) { 8 | allData.push(chunk); 9 | }); 10 | 11 | req.on('end', function() { 12 | var host = allData.toString(); 13 | var request = http.request({ host: host, path: '/' }, function(req) { 14 | var htmlData = []; 15 | req.on('data', function(htmlChunk) { 16 | htmlData.push(htmlChunk); 17 | }); 18 | req.on('end', function() { 19 | res.setHeader('Status code', 200); 20 | res.write(htmlData.toString()); 21 | res.end(); 22 | }); 23 | }); 24 | request.end(); 25 | }); 26 | }); 27 | 28 | server.listen(port, function(){ 29 | console.log('Server listening on ' + port); 30 | }); -------------------------------------------------------------------------------- /tasks/03/homework/bootstrap/index.js: -------------------------------------------------------------------------------- 1 | /* global require, __dirname, console */ 2 | 3 | var MailObserver = require('./lib/observers/MailObserver'), 4 | LogObserver = require('./lib/observers/LogObserver'), 5 | logConfig = { path: __dirname + '/logs' }, 6 | mailerConfig = { from: 'Foo Bar ', to: '' }, 7 | PostsCollection = require('./lib/observables/PostsCollection'), 8 | posts = new PostsCollection(), 9 | express = require('express'), 10 | bodyParser = require('body-parser'), 11 | app = express(); 12 | 13 | // Create and attach observers 14 | 15 | app.use(express.static(__dirname + '/public')); 16 | app.use(bodyParser.json()); 17 | 18 | app.post('/post', function (req, res) { 19 | 'use strict'; 20 | posts.addPost(req.body.title, req.body.content); 21 | res.status(200); 22 | res.end(); 23 | }); 24 | 25 | app.listen(3000); 26 | 27 | console.log('Listening on port 3000...'); 28 | 29 | -------------------------------------------------------------------------------- /tasks/09/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 |
    12 |
  • 13 |
14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /tasks/02/solutions/curry.js: -------------------------------------------------------------------------------- 1 | var curry = function(fn) { 2 | return function(){ 3 | var outerArgs = [].slice.call(arguments, 0); 4 | if (outerArgs.length < fn.length) { 5 | return function() { 6 | var innerArgs = [].slice.call(arguments, 0); 7 | var totalArgsLength = outerArgs.length + innerArgs.length; 8 | if (totalArgsLength < fn.length) { 9 | return fn.apply(this, outerArgs.concat(innerArgs).concat([].slice.call(arguments, 0))); 10 | } 11 | return fn.apply(this, outerArgs.concat(innerArgs)); 12 | } 13 | } 14 | return fn.apply(this, outerArgs); 15 | } 16 | } 17 | 18 | var curry = function (fn) { 19 | var arity = fn.length; 20 | return function g() { 21 | var args = [].slice.call(arguments); 22 | if (args.length >= arity) { 23 | return fn.apply(null, args); 24 | } else { 25 | return function () { 26 | var innerArgs = [].slice.call(arguments); 27 | return g.apply(null, args.concat(innerArgs)); 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /lectures/07-express/expressServer/app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = require('./router'); 3 | var fs = require('fs'); 4 | var bodyParser = require('body-parser'); 5 | var busboy = require('connect-busboy'); 6 | var app = express(); 7 | var port = 8888; 8 | 9 | app.use(bodyParser.json()); 10 | app.use(bodyParser.urlencoded({ extended: true })); 11 | app.use('/api/books', router); 12 | 13 | app.post('/upload', busboy(), function(req, res) { 14 | if(!req.busboy) return res.status(500).send('Incorrect post headers!'); 15 | req.busboy.on('file', function(fieldname, file, filename, encoding, mimetype) { 16 | var ws = fs.createWriteStream('./uploads/'+ filename); 17 | ws.on('finish', function() { 18 | console.log('File saved ...'); 19 | res.status(200).end(); 20 | }); 21 | file.pipe(ws); 22 | }); 23 | req.pipe(req.busboy); 24 | }); 25 | 26 | //Serve static content 27 | app.use(express.static(__dirname + '/static')); 28 | 29 | app.listen(port, function() { 30 | console.log('Server listening on ' + port + ' ...'); 31 | }); -------------------------------------------------------------------------------- /lectures/07-express/expressServer/router.js: -------------------------------------------------------------------------------- 1 | var expressRouter = require('express').Router(); 2 | var fs = require('fs'); 3 | 4 | var books = [{ 5 | id: '1', 6 | author: 'None', 7 | name: 'book1' 8 | },{ 9 | id: '2', 10 | author: 'None1', 11 | name: 'book2' 12 | },{ 13 | id: '3', 14 | author: 'None', 15 | name: 'book3' 16 | }]; 17 | 18 | expressRouter.get('/', function(req, res) { 19 | var filter = req.query; 20 | if(Object.keys(filter).length === 0) return res.json(books); 21 | var result = books.filter(function(book) { 22 | var flag = true; 23 | for(var prop in filter) { 24 | flag = (filter[prop] === book[prop]); 25 | } 26 | return flag; 27 | }); 28 | res.json(result); 29 | }); 30 | 31 | expressRouter.get('/:bookId', function(req, res) { 32 | var book = books[req.params.bookId]; 33 | if(!book) res.status(404).send('Book not found!'); 34 | res.json(book); 35 | }); 36 | 37 | expressRouter.post('/', function(req, res) { 38 | books.push(req.body); 39 | res.status(201).end(); 40 | }); 41 | 42 | module.exports = expressRouter; 43 | -------------------------------------------------------------------------------- /layout.html.mustache: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | js @ ФМИ 5 | My Awesome Presentation 6 | 7 | 33 | 34 | 35 | 45 | 47 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /tasks/08/wsChat/client.js: -------------------------------------------------------------------------------- 1 | var username = process.argv[2], 2 | query = username ? { query: "username=" + username } : undefined, 3 | socket = require('socket.io-client')('http://localhost:8888', query), 4 | keypress = require('keypress'), 5 | line = ''; 6 | 7 | keypress(process.stdin); 8 | 9 | socket.on('connect', function() { 10 | console.log('connected ...'); 11 | }); 12 | 13 | socket.on('disconnect', function() { 14 | console.log('disconnected'); 15 | }); 16 | 17 | socket.on('username', function(data){ 18 | username = data; 19 | console.log('username is ' + username); 20 | }); 21 | 22 | socket.on('message', function(message) { 23 | process.stdout.clearLine(); 24 | process.stdout.cursorTo(0); 25 | process.stdout.write(message.username + ': ' + message.text + '\n'); 26 | if(line) process.stdout.write(line); 27 | }); 28 | 29 | process.stdin.on('keypress', function (ch, key) { 30 | if (key && key.ctrl && key.name == 'c') { 31 | process.exit(); 32 | } else if(key && key.name === 'return') { 33 | socket.emit('message', line); 34 | process.stdout.write('\n'); 35 | line = ''; 36 | } else if(key && key.name === 'backspace') { 37 | process.stdout.clearLine(); 38 | process.stdout.cursorTo(0); 39 | line = line.slice(0 , -1); 40 | process.stdout.write(line); 41 | } else { 42 | if(ch === undefined) return; 43 | line += ch; 44 | process.stdout.write(ch); 45 | } 46 | }); 47 | 48 | process.stdin.setRawMode(true); 49 | process.stdin.resume(); 50 | -------------------------------------------------------------------------------- /tasks/04/README.md: -------------------------------------------------------------------------------- 1 | # ES2015 Задачи 2 | 3 | ## Async Execution and Promises 4 | 5 | 1. Направете функция която изчаква 1000ms след което записва във файл произволен стринг. След това изчаква още 1000ms и записва в друг файл отново произволен стринг. След още 1000ms изчакване отваря първия файл, отново изчаква 1000ms, отваря втория файл, изчаква отново 1000ms и накрая записва в трети файл конкатениран резултатa от четенето на двата предходни файла. 6 | Използвайте функции [writeFile](https://nodejs.org/api/fs.html#fs_fs_writefile_file_data_options_callback) и [readFile](https://nodejs.org/api/fs.html#fs_fs_readfile_file_options_callback) и _не използвайте_ Promises. 7 | 8 | 1. Направете предната задача използвайки Promises 9 | 10 | 1. Използвайте примера за httpGet oт първия час и напишете генератор на произволни имена на класове за браузъра. За целта вземете имена на класове от [ClassNamer](http://www.classnamer.com/) - подайте http request към адрес http://www.classnamer.com/index.txt?generator=generic. 11 | 12 | 1. Напишете функция simulateDocs, която ще ви помогне да симулирате, че сте написали документация за кода си. Тя трябва да използва генератора от предната задача, за да получи произволни имена на класове и при последващи извиквания да връща стрингoве: 13 | simulateDocs() -> "ClassName1 extends ClassName2" 14 | simulateDocs() -> "ClassName1 extends ClassName2 extends ClassName3" 15 | ... 16 | ClassName1,..N са върнати от генератора имена и при всяко следващо извикване функцията simulateDocs върнатият стринг е продължение на този от предходното извикване. При повече от 5 извиквания функцията трябва да спре да връща резултат. 17 | 18 | -------------------------------------------------------------------------------- /tasks/07/README.md: -------------------------------------------------------------------------------- 1 | # Maze traversal 2 | 3 | 1. Отворете следната връзка http://plnkr.co/edit/HFdwuA1B48eG01Rn1IGY 4 | 2. На базата на API описан в plnkr, в `script.js`, реализирайте: 5 | - `drawMaze(maze, parentNode)` - метод, който рисува лабиринта в елемента `parentNode`. 6 | - `traverseMaze(maze, start, target, strategy)` - метод, който на базата на подадените аргументи открива път в лабиринта и го рисува на екрана. 7 | - `maze` - матрица, която представлява дадения лабиринт. 8 | - `start` - начална точка от вида `[row, col]`. 9 | - `target` - стойност на целта (в зададения като пример масив е 2). 10 | - `strategy` - генератор, който обхожда матрицата. 11 | - Генераторът връща следните стойности за `value`: 12 | - `current` - текущата клетка в лабиринта. 13 | - `neighbors` - съседите на текущата клетка. 14 | 15 | 16 | # Query selector 17 | 18 | 1. Имплементирайте query selector (polyfill за `document.querySelectorAll`). Вашата имплементация трябва да поддържа следните опрерации: 19 | - `*` - селектира всички елементи. 20 | - `tagName` - селектира всички елементи с име `tagName`. 21 | - `#id` - селектира всички елементи с id със стойност `id`. 22 | - `.className` - селектира всички елементи, които имат добавен клас със стойност `className`. 23 | - `parentSelector childSelector` - селектира всички преки и непреки наследници, които удовлетворяват `childSelector`, на всички елементи, които са селектирани от `parentSelector`. 24 | - `parentSelector>childSelector` - селектира всички преки наследници, които удовлетворяват `childSelector`, на всички елементи, които са селектирани от `parentSelector`. -------------------------------------------------------------------------------- /tasks/02/solutions/map-filt-red-with-pair.js: -------------------------------------------------------------------------------- 1 | /** 2 | * this implementation works on a list which can have last element 3 | * different than a list 4 | * cons(1, cons(2, cons(3, cons(4, cons(5, 6))))) 5 | */ 6 | 7 | function applyFunction(fn) { 8 | var args = [].slice.call(arguments, 1); 9 | args.forEach(fn); 10 | } 11 | 12 | function isPair(c) { 13 | return typeof c === 'function' && typeof cdr(c) !== 'function' && 14 | typeof cdr(c) !== 'undefined'; 15 | } 16 | 17 | function forEach(c, f) { 18 | if (typeof c === 'function' && typeof (cdr(c)) === 'function') { 19 | f(car(c)); 20 | forEach(cdr(c), f); 21 | } else if (isPair(c)) { 22 | applyFunction(f, car(c), cdr(c)); 23 | } else { 24 | f(car(c)); 25 | } 26 | } 27 | 28 | function print(list) { 29 | forEach(list, function (el) { 30 | console.log(el); 31 | }); 32 | }; 33 | 34 | function map(c, fn) { 35 | if (typeof c === 'function' && typeof cdr(c) === 'function') { 36 | return cons(fn(car(c)), map(cdr(c), fn)); 37 | } else if (isPair(c)) { 38 | return cons(fn(car(c)), fn(cdr(c))); 39 | } 40 | } 41 | 42 | function filter(c, pred) { 43 | if (typeof c !== 'function') { 44 | return undefined; 45 | } 46 | if (isPair(c)) { 47 | var args = [car(c), cdr(c)].filter(pred); 48 | return args.length > 0 ? cons.apply(null, args) : undefined; 49 | } 50 | if (pred(car(c))) { 51 | return cons(car(c), filter(cdr(c), pred)); 52 | } 53 | return filter(cdr(c), pred); 54 | } 55 | 56 | function reduce(c, fn, init) { 57 | if (typeof c !== 'function') { 58 | return init; 59 | } else if (isPair(c)) { 60 | return car(c) + cdr(c) + init; 61 | } 62 | return reduce(cdr(c), fn, fn(init, car(c))); 63 | } -------------------------------------------------------------------------------- /tasks/01/README.md: -------------------------------------------------------------------------------- 1 | 1. Напишете функция `fib(n)`, която по зададен параметър `n`, връща `n`-тото число на Фибоначи. **Не с рекурсия.** 2 | 2. Нека an и an-1 са две последователни числа на Фибоначи. При `n -> ∞` an / an-1 -> φ. Напишете функция `phiEstimation(n)`, която по зададен параметър `n`, връща приближение на числото `φ` (the golden ratio). 3 | 3. Напишете функция `reverseWordsOrderInString`, която по зададен низ връща нов низ с думите от оригиналния низ в обратен ред. Пример: 4 | 5 | 'Foo bar baz' 6 | 'baz bar Foo' 7 | 8 | 4. Напишете функция `reverseWordsInString`, която по зададен низ връща нов низ с думите от входния низ с думи в противоположна посока. Пример: 9 | 10 | 'Foo bar baz' 11 | 'ooF rab zab' 12 | 13 | 5. Напишете функция `findNthNumber(n, arr)`, която по зададен параметър `n`, намира `n`-тото по големина число(или `undefined` ако няма толкова) в даден масив `arr`. 14 | - сортирайте масива и намерете `n`-тото най-голямо число. 15 | - използвайте partition от quick sort и рекурсивно подредете масива. 16 | 17 | 6. `median(arr)` - Намерете [медианата](http://bg.wikipedia.org/wiki/%D0%9C%D0%B5%D0%B4%D0%B8%D0%B0%D0%BD%D0%B0_(%D1%81%D1%82%D0%B0%D1%82%D0%B8%D1%81%D1%82%D0%B8%D0%BA%D0%B0)) на несортиран масив с `n` елемента. 18 | 19 | 7. Напишете функция `setBits(m, n, i, j)`, която приема като аргументи числата `m`, `n`, `i`, `j`. Функцията задава битовете на `m` в диапазона `i`, `j` със стойност `n`, т.е.: 20 | 21 | ```text 22 | Вход: 23 | m = 10000010 24 | n = 11010 25 | i = 2 26 | j = 5 27 | Резултат: 28 | 10110110 29 | ``` 30 | 31 | Както входните параметри, така и резултатът трябва да бъдат числа в десетична бройна система т.е.: 32 | 33 | ``` 34 | setBits(130, 26, 2, 5); //182 35 | ``` 36 | -------------------------------------------------------------------------------- /tasks/03/homework/README.md: -------------------------------------------------------------------------------- 1 | # Observers 2 | 3 | ## Directory structure 4 | 5 | ``` 6 | . 7 | ├── index.js 8 | ├── lib 9 | │ ├── observables 10 | │ │ ├── Observable.js 11 | │ │ └── PostsCollection.js 12 | │ └── observers 13 | │ ├── LogObserver.js 14 | │ ├── MailObserver.js 15 | │ └── Observer.js 16 | ├── logs 17 | ├── package.json 18 | └── public 19 | ├── index.html 20 | └── src 21 | └── main.js 22 | ``` 23 | 24 | Run `npm install` in the root of the bootstrap directory in order to install all node dependencies. 25 | 26 | Run `node index.js` in order to start the express application. When you open the URL [http://localhost:3000](http://localhost:3000), you should see HTML page with text input, text area and a button. 27 | 28 | Take a look at the implementation of the constructor function `Observable`. 29 | Review the "interface" `Observer`. 30 | 31 | Extend the constructor function `Observable` in `PostsCollection` and implement the method `addPost`. 32 | 33 | ### Implement the "interface" of `Observer` in `LogObserver` and `MailObserver`. 34 | 35 | When the method update of `LogObserver` is being called it should create a new file with name `Date.now() + '.txt'` and content: "[current date] Title Content". 36 | When the method update of `MailObserver` is being called it should send a new email message with subject the title of the post and content, the post's content. 37 | 38 | Instructions about the required APIs are available in `LogObserver.js` and `MailObserver.js`. 39 | 40 | Inside `index.js` create new instances of the both observers and set the appropriate configuration. Add the observers to a new object, instance of `PostsCollection`. 41 | 42 | If the implementation is correct, when you add new post through the web interface or `addPost` method, the application should create new file in the `logs` directory and email with the title of the article should be sent, to the destination you've set. 43 | -------------------------------------------------------------------------------- /tasks/04/solutions/task1.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | function getRandomString() { 4 | return Math.random().toString(36).substring(7); 5 | } 6 | 7 | function handleError(err) { 8 | console.log(err); 9 | } 10 | 11 | function wait1000(err, callback) { 12 | if(err) return handleError(err); 13 | console.log('simulating async operation...'); 14 | setTimeout(function(){ 15 | callback(null); 16 | }, 1000); 17 | } 18 | 19 | function writeFile(err, data, callback) { 20 | if(err) return handleError(err); 21 | fs.writeFile(data.path, data.string, callback); 22 | } 23 | 24 | function readFile(err, data, callback) { 25 | if(err) return handleError(err); 26 | fs.readFile(data.path, callback); 27 | } 28 | 29 | function doStuff(){ 30 | var processPath = process.cwd(); 31 | var FILE1_PATH = processPath + '/file1'; 32 | var FILE2_PATH = processPath + '/file2'; 33 | var FILE3_PATH = processPath + '/file3'; 34 | 35 | wait1000(null, function() { 36 | var randString = getRandomString(); 37 | writeFile(null, { path: FILE1_PATH, string: randString } , function(err) { 38 | wait1000(err, function(err) { 39 | randString = getRandomString(); 40 | writeFile(err, { path: FILE2_PATH, string: randString }, function(err) { 41 | wait1000(err, function(err) { 42 | readFile(err, { path: FILE1_PATH }, function(err, content1) { 43 | wait1000(err, function(err){ 44 | readFile(err, { path: FILE2_PATH }, function(err, content2) { 45 | wait1000(err, function(err) { 46 | writeFile(err, { path: FILE3_PATH, string: content1 + content2 }, function(err) { 47 | if(err) return handleError(err); 48 | console.log('This is so ugly ... '); 49 | }); 50 | }); 51 | }); 52 | }); 53 | }); 54 | }); 55 | }); 56 | }); 57 | }); 58 | }); 59 | } 60 | 61 | doStuff(); -------------------------------------------------------------------------------- /tasks/06/README.md: -------------------------------------------------------------------------------- 1 | # Express.js Server 2 | 3 | * Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications. 4 | 5 | [API Reference](http://expressjs.com/api.html) 6 | 7 | 8 | ## Task 1 - Restful API 9 | 10 | * `GET /all_books` - returns all the books for all the users 11 | * `GET /books/:bookId` - returns the data for a book by a given bookId 12 | * `POST /book` - expects user, author, title, descriptionText and rate in the body data. Creates a new book and returns a bookId, which should be unique for every book! 13 | * `GET /all_users` - returns all the registered users. 14 | * `GET /all_authors` - returns all the authors for the books we have in our library. 15 | * `POST /register` - expects user as an argument. Creates a new user and returns a key for that user. If the user exists just returns a 409 response code. 16 | * `DELETE /book` - expects bookId as an argument. Deletes the bookId if the book exists, otherwise return a 403 response code. 17 | 18 | * `GET /all_books?:someField=:someVal` - A dynamic filter API endpoint which returns all books by a passed fiter properties 19 | ``` 20 | /all_books?rate=7 21 | /all_books?title=The%20Great%20Gatsby 22 | ``` 23 | 24 | ### Testing 25 | ``` 26 | curl -H "Content-Type: application/json" --data '{"user": "billy", "author": "Steinbeck",... }' http://localhost:8080/ 27 | ``` 28 | ## Task 2 - Async each Function 29 | ### Направете функция forEach, която приема като аргументи: 30 | * масив от елементи 31 | * итератор функция, която приема аргументи 32 | * пореден елемент от масива 33 | * callback фунцкия, която да извика, когато е приключил с обработката на елемента от масива 34 | * callback функция, приемаща единствен аргумент грешка. Тя ще се извика, когато възникне грешка или всички други callbacks са приключили 35 | 36 | Идеята на вашата функция forEach e да осигури паралелна "обработка" на елементи в масива чрез итератор функцията (втория си аргумент) 37 | 38 | Пример за употребата: 39 | 40 | ```javascript 41 | forEach(openFiles, function (file, next) { 42 | 43 | // Асинхронна обработка на елемента от масива 44 | console.log('Processing file ' + file); 45 | fs.readFile(file, function (err, file) { 46 | if (err) { 47 | next(err); 48 | } 49 | console.log(file); 50 | next(); 51 | }); 52 | 53 | }, function (err) { 54 | // Ако при обработката на елементите се хвърли грешка, тя ще се намира в аргумента err 55 | if (err) { 56 | console.log('A file failed to process', err); 57 | } else { 58 | console.log('All files have been processed successfully'); 59 | } 60 | }); 61 | ``` -------------------------------------------------------------------------------- /compile.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var hogan = require('hogan.js'), 4 | fs = require('fs'), 5 | path = require('path'), 6 | Q = require('q'), 7 | cpr = require('cpr').cpr, 8 | lecturesDir = './lectures', 9 | compiledDir = './compiled', 10 | layoutFilename = './layout.html.mustache', 11 | requiredForComiple = process.argv.slice(2); 12 | 13 | 14 | function discoverLectures() { 15 | var deferred = Q.defer(); 16 | fs.readdir(lecturesDir, function (error, lectures) { 17 | if (error) { 18 | deferred.reject(new Error(error)); 19 | } else { 20 | deferred.resolve(lectures); 21 | } 22 | }); 23 | 24 | return deferred.promise; 25 | } 26 | 27 | function loadLayout() { 28 | var deferred = Q.defer(); 29 | fs.readFile(layoutFilename, function (error, data) { 30 | if (error) { 31 | console.error('Too bad: ' + error); 32 | deferred.reject(new Error(error)); 33 | } else { 34 | deferred.resolve(hogan.compile(data.toString())); 35 | } 36 | }); 37 | 38 | return deferred.promise; 39 | } 40 | 41 | function compile(layout, template_name) { 42 | var deferred = Q.defer(); 43 | fs.readFile(path.join(lecturesDir, template_name), function (error, data) { 44 | if (error) { 45 | deferred.reject(new Error(error)); 46 | } else { 47 | deferred.resolve(layout.render({slides: data.toString()})); 48 | } 49 | }); 50 | 51 | return deferred.promise; 52 | } 53 | 54 | loadLayout().then(function (layout) { 55 | discoverLectures().then(function (lectures) { 56 | var templatePattern = process.argv[2], 57 | templates = lectures.filter(function (lecture) { 58 | return RegExp(templatePattern).test(lecture); 59 | }); 60 | 61 | console.log(templates); 62 | 63 | templates.forEach(function (template) { 64 | compile(layout, template).then(function (output) { 65 | var basename = template.split('.')[0], 66 | compiled = path.join(compiledDir, basename); 67 | fs.writeFile(compiled + '.html', output, function (error) { 68 | if (error) { 69 | console.error('Writing file failed: ' ); 70 | console.error(error); 71 | } 72 | }); 73 | }, function (error) { 74 | console.error('Could not read lecture file ' + template); 75 | console.error(error); 76 | }); 77 | }); 78 | 79 | cpr('html', 'compiled', { 80 | overwrite: true, 81 | confirm: true 82 | }, function (error) { 83 | if (error) { 84 | console.error('Error copying src and img folders: '); 85 | console.error(error); 86 | } 87 | }); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /lectures/04-01-moar-modules.markdown: -------------------------------------------------------------------------------- 1 | # Modules (в малко повече детайл) 2 | 3 | Разделяме кода в отделни файлове с цел по-добра изолация на отделните парчета от програмата ни. 4 | 5 | Спазваме DRY (Don't repeat yourself) принципа - модулите са удобен начин за преизползване на код. 6 | 7 | Възползваме се от ["стандартната библиотека"](https://nodejs.org/api/) на Node.js и [огромната база](http://npmjs.com/) от допълнителни модули. 8 | 9 | --- 10 | 11 | # Собствен модул - библиотека 12 | 13 | ```javascript 14 | // file math.js 15 | function add(x, y) { 16 | return x + y; 17 | } 18 | 19 | module.exports = { 20 | add: add 21 | }; 22 | 23 | ``` 24 | 25 | ```javascript 26 | // file index.js 27 | var math = require('./math'); 28 | console.log(math.add(4, 6)); 29 | ``` 30 | 31 | --- 32 | 33 | # Собствен модул - клас 34 | 35 | ```javascript 36 | // file my_class.js 37 | var MyClass = function() { 38 | this.member = 42; 39 | } 40 | 41 | module.exports = MyClass; 42 | 43 | ``` 44 | 45 | ```javascript 46 | // file index.js 47 | var myClass = require('./my_class'); 48 | var instance = new myClass(); 49 | 50 | console.log(instance.member); 51 | ``` 52 | 53 | --- 54 | 55 | # Модули от "стандартната библиотека" 56 | 57 | ```javascript 58 | var fs = require('fs'); 59 | 60 | // `__dirname` връща пътя към текущия модул 61 | var fileName = __dirname + '/out.txt'; 62 | fs.writeFileSync(fileName, "This will go in the file"); 63 | ``` 64 | 65 | Документация - в страничката на [съответния модул](https://nodejs.org/api/fs.html). 66 | 67 | --- 68 | 69 | # Външни модули 70 | 71 | Инсталираме с `npm install <име>` в основната папка на нашето приложение: 72 | 73 | ```bash 74 | # в терминала 75 | npm install uuid 76 | ``` 77 | 78 | ```javascript 79 | // в някой сорс файл 80 | 81 | var uuid = require('uuid'); 82 | console.log(uuid.v1()); 83 | ``` 84 | 85 | Документация - в страничка на модула в NPM repository-то, в случая [uuid](https://www.npmjs.com/package/uuid). 86 | 87 | --- 88 | 89 | # Пътища за зареждане 90 | 91 | Забележете require-ването само по име на модула - Node търси за файл/папка с това име в подпапка **node_modules** на нашата основна папка. Ако не намери, продължава да търси **node_modules** нагоре по дървото с папки. 92 | 93 | ```bash 94 | /home/modder/some_project/node_modules/uuid 95 | /home/modder/node_modules/uuid 96 | /home/node_modules/uuid 97 | /node_modules/uuid 98 | ``` 99 | 100 | Повече информация за начина, по който Node търси модули - [тук](https://nodejs.org/api/modules.html#modules_loading_from_node_modules_folders). 101 | -------------------------------------------------------------------------------- /tasks/05/solutions/task1.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | 3 | //Resource for writable stream: https://nodejs.org/api/stream.html#stream_class_stream_writable 4 | var WritableStream = require('stream').Writable; 5 | 6 | function FileReader() { 7 | WritableStream.call(this); 8 | this.lastDone = null; 9 | this.lastLine = null; 10 | this.buffer = []; 11 | this.hasFinished = false; 12 | var self = this; 13 | 14 | //Add callback to the finish event and when its called set the property hasFinished to true. 15 | //We can use self._writableState.finished instad of doing this but we have to know about it. 16 | this.on('finish', function() { 17 | self.hasFinished = true; 18 | }); 19 | } 20 | 21 | util.inherits(FileReader, WritableStream); 22 | 23 | FileReader.prototype.fetchLine = function() { 24 | var self = this; 25 | 26 | //If stream's not finished and we don't have a nextLine we must let some data to be written to the stream 27 | //and try to fetch the line again. We use setTimeout because if we use process.nextTick the callback 28 | //will be executed before the _write is called (its executed before any I/O operations) 29 | //Resource: 30 | //https://nodejs.org/api/process.html#process_process_nexttick_callback_arg 31 | setTimeout(function() { 32 | var nextLine = self.buffer[0]; 33 | self.buffer = self.buffer.slice(1); //create new array without the first element 34 | if(!nextLine) { 35 | if(!self.hasFinished) { 36 | //if there is a done callback and stream is not closed 37 | //then exec done so data can be written into the stream 38 | if(self.lastDone && !self.hasFinished) self.lastDone(); 39 | self.fetchLine(); 40 | } else { 41 | //If stream is closed then send null 42 | self.emit('next-line', null); 43 | } 44 | return; //Don't execute line 46 45 | } 46 | self.emit('next-line', nextLine); 47 | }, 0); 48 | }; 49 | 50 | FileReader.prototype._write = function(chunk, encoding, done) { 51 | var chunkString = chunk.toString(); 52 | 53 | //Check if lastLine is set. If so append it to the current result. 54 | if(this.lastLine) { 55 | chunkString = this.lastLine + chunkString; 56 | this.lastLine = null; 57 | } 58 | 59 | var chunkLines = chunkString.split('\n'); //Split the lines 60 | 61 | //If the last symbol is not a \n we don't have the full line so we must save the 62 | //last element and append next chunkString to it 63 | if(chunkString.slice(chunkString.length - 1) !== '\n' && this.hasFinished) { 64 | this.lastLine = chunkLines.pop(); 65 | } 66 | this.buffer = this.buffer.concat(chunkLines); 67 | 68 | //Keep a reference to the done call so we can execute the function later when the 69 | //buffer array is empty 70 | this.lastDone = done; 71 | }; 72 | 73 | module.exports = FileReader; 74 | -------------------------------------------------------------------------------- /lectures/03-01-moar-functional.markdown: -------------------------------------------------------------------------------- 1 | # bind 2 | 3 | `bind` задава нов `this` на дадена функция и „забранява“ замяната му 4 | 5 | ```javascript 6 | var getThisValue = function () { 7 | return this.value; 8 | }; 9 | var thing = { value: 42 }, 10 | otherThing = { value: 73 }; 11 | ``` 12 | 13 | ```javascript 14 | > getThisValue.bind(thing)(); 15 | 42 16 | > getThisValue.bind(thing).call(otherThing) 17 | 42 18 | ``` 19 | 20 | --- 21 | 22 | # Функционални забавления 23 | 24 | В javascript липсват някои дребни удобства, които идват „безплатно“ в повечето други популярни езици. Пример за това е `range`. В много езици има възможност код като `range(10, 45)` да създаде обект, който да представлява нещо итеруемо, обхождащо стойностите от 10 до 45. Същото можем да постигнем с прости функционални похвати и в javascript: 25 | 26 | ```javascript 27 | function range(start, end, stopIteration) { 28 | return function () { 29 | if (start > end) { 30 | return stopIteration; 31 | } else { 32 | return start++; 33 | } 34 | } 35 | } 36 | ``` 37 | 38 | --- 39 | 40 | След това можем да използваме `range` функцията по следния начин: 41 | 42 | ```javascript 43 | var stopIteration = {}, 44 | next = range(42, 73, stopIteration), 45 | item; 46 | 47 | while((item = next()) !== stopIteration) { 48 | console.log(item); 49 | } 50 | ``` 51 | 52 | --- 53 | 54 | # `===` и `!==` 55 | 56 | `===` и `!==` са **строги** проверки. Това ще рече, че за стойности от тип `string` и `number` не се прави type coercion. 57 | 58 | * `5 === '5'` се оценява до `false` 59 | * `6 !== '6'` се оценява до `true`. 60 | * в javascript няма предефиниране на оператори, така че `{} == {}` винаги ще бъде `false`, тъй като това са два различни обекта. 61 | * но поради имплицитния type coercion `{} == '[object Object]'` се оценява до `true` 62 | 63 | ⇨ правим проверката с `!==`, а не с `!=` 64 | 65 | --- 66 | 67 | # Алтернативи 68 | 69 | * Вместо проверка за `stopIteration` да „хвърлим“ грешка 70 | * Да получим тялото на цикъла като функция 71 | 72 | ```javascript 73 | function range(start, end, stopIteration) { 74 | return function () { 75 | if (start > end) { 76 | throw new Error('RANGE OVER'); 77 | } else { 78 | return start++; 79 | } 80 | } 81 | } 82 | ``` 83 | 84 | Обаче: 85 | * Все още не знаем какво точно е `Error` 86 | * Поведението му може да ни изненада неприятно 87 | * В javascript „хвърлянето“ на грешки не е на почит, защото не се държи възпитано при асинхронен код 88 | 89 | --- 90 | 91 | # Задачка 92 | ### `function iterator(array, stopIteration)` 93 | 94 | Прави същото като `range`, но обхождайки подадения **Array-like** обект. 95 | 96 | `function iterator(array, stopIteration)`, която прави същото като `range`, но обхождайки подадения **Array-like** обект. 97 | 98 | --- 99 | 100 | # Задачка 101 | ### `rangeMap` 102 | 103 | 104 | ```javascript 105 | > var doubled = rangeMap(1, 3, function (item) { return item + item }); 106 | > console.log(doubled) 107 | [2, 4, 6] 108 | ``` 109 | Алтернативна имплементация на `range`, която не очаква `stopIteration` обект, а се държи като `map`, очаквайки функция, която да изпълни върху всички елементи. 110 | -------------------------------------------------------------------------------- /tasks/03/README.md: -------------------------------------------------------------------------------- 1 | I. Разширете прототипа на Array с функция myMap, която прилага функция към всеки елемент от масива и връща нов списък с резултата. 2 | 3 | ```javascript 4 | [1,2,3,4,5,6,7,8,9].myMap(function(el){ return el + 10; }); // -> [11,12,13,14,15,16,17,18,19] 5 | ``` 6 | 7 | II. Напишете конструктор Vehicle, който взима един аргумент mileage, което е private свойство на обекта. Vehicle има следните методи: getMileage и toString. 8 | getMilage връща стойността private свойството, а toString връща string: This Vehicle Mileage is (mileage e обект с единствено свойство val!) 9 | 10 | Напишете конструктор Car, който взима два аргумента: brand и consumption. Car има 3 метода: 11 | - drive(miles), който прибавя miles към mileage.val 12 | - getBrand(), който връща марката 13 | - getConsumption(), който връща consumption 14 | - toString(), който връща string съдържащ + резултата от извикването на toString на "super" класа. 15 | 16 | При създаването на нов обект от Car mileage.val трябва да бъде 0. 17 | 18 | ```javascript 19 | 20 | var c1 = new Car('honda', 5); 21 | c1.getMileage(); // -> 0 22 | c1.toString(); // -> honda 5 This Vehicle mileage is 0 23 | c1.drive(1000); 24 | c1.getMileage(); // -> 1000 25 | c1.toString(); // -> honda 5 This Vehicle mileage is 1000 26 | 27 | ``` 28 | Какво ще се случи, ако mileage не e обект, а е променлива? 29 | 30 | III. Напишете конструктор Point, който взима 2 аргумента - x и y. Point има метод: toString, който връща - x, y. Напишете конструктор Point3D, който наследява 31 | всички свойства на Point и има допълнителен аргумент z. Направете прототипно наследяване муждy Point3D и Point. 32 | Напишете toString метод към прототипа на Point3D, който връща x, y, z като използва Point.toString за да върне x, y. 33 | Напишете метод toArray към прототипа на Point, който връща координатите в списък. За Point3D toArray НЕ трябва бъде имплементиран, но ако бъде извикан той трябва да върне [x, y, z] 34 | 35 | IV. Създайте библиотека FMIjs, която обработва списъци от числа и съдържа : 36 | 1) BinaryHeap - клас с име MinHeap, чийто прототип има за прототип Array.prototype. 37 | 2) heapSort - метод, който връща сортиран списък. 38 | 39 | MinHeap съдържа методи: 40 | - parentIndex 41 | - leftIndex 42 | - rightIndex 43 | - parentElement 44 | - leftElement 45 | - rightElement 46 | - getMinimum 47 | - isEmpty 48 | - insert (използвайте push, за слагане на елемент в списъка. Методът връща heap-а като string) 49 | - bubbleUp 50 | - bubbleDown 51 | 52 | и конструктор, който взима един аргумент - списък. Ако няма подаден списък или е подедено нещо друго да се създава празен heap. 53 | 54 | (При създаване на повече от един heap методите на MinHeap трябва да се създадат само веднъж !) 55 | 56 | Обяснете следния ефект: 57 | ```javascript 58 | var b = new FMIjs.BinaryHeap(); 59 | b // -> [] 60 | b[0] = 1000; 61 | b // -> [] 62 | b[0] // -> 1000 63 | b.insert(5); 64 | b // -> [5] 65 | b[0] // -> 5 66 | ``` 67 | ```javascript 68 | 69 | var arr = [1,2,6,9,2,4,9,87,36,21,1]; 70 | FMIjs.heapSort(arr); // -> [1,1,2,2,4,6,9,9,21,36,87] 71 | 72 | var b = FMIjs.BinaryHeap(); 73 | b.insert(5); 74 | b.insert(10); 75 | b.insert(1); 76 | 77 | b.getMinimum(); //-> 1 78 | b.getMinimum(); //-> 5 79 | b.getMinimum(); //-> 10 80 | 81 | ``` 82 | -------------------------------------------------------------------------------- /tasks/04/solutions/task2.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | //In this solution we can create a function promisify which takes a function as an argument 4 | //and retuns a promise but I will stick to the more generic way. 5 | //PS. On github there are modules that provide this functionality. 6 | 7 | //Because we dont have an async operation here the better solution is to return a resolved Promise 8 | //instead of creating a new promise like readFile and writeFile and resolve it inside. 9 | function getRandomString(data) { 10 | data = data || {}; //if data is undefined or null assign an empry object 11 | data.string = Math.random().toString(36).substring(7); 12 | return Promise.resolve(data); 13 | } 14 | 15 | function handleError(err) { 16 | console.log(err); 17 | } 18 | 19 | function wait1000(data) { 20 | return new Promise(function(resolve, reject){ 21 | console.log('simulating async operation...'); 22 | 23 | //Its always a good idea to name our functions for debuging purposes. 24 | //This way in the stack trace we can see the function name instad of anonymous function. 25 | setTimeout(function asyncOperation(){ 26 | resolve(data); 27 | }, 1000); 28 | }); 29 | } 30 | 31 | //I will curry the writeFile and readFile Functions so I can pass the necessary paths when I chain the promises. 32 | function writeFile(path) { 33 | return function writeFilePromise(data) { 34 | return new Promise(function(resolve, reject){ 35 | fs.writeFile(path, data.string, function(err){ 36 | if(err) return reject(err); 37 | resolve(data); 38 | }); 39 | }); 40 | }; 41 | } 42 | 43 | function concatContents(data){ 44 | if(!data.contents) throw new Error('File contents not found!'); 45 | data.string = data.contents.join(''); 46 | return Promise.resolve(data); 47 | } 48 | 49 | //We will store the contents of the files in an array - contents. 50 | function readFile(path) { 51 | return function readFilePromise(data) { 52 | return new Promise(function(resolve, reject) { 53 | fs.readFile(path, function(err, content) { 54 | if(err) return reject(err); 55 | data = data || {}; 56 | if(!data.contents) data.contents = []; 57 | data.contents.push(content); 58 | resolve(data); 59 | }); 60 | }); 61 | }; 62 | } 63 | 64 | function doStuff() { 65 | var processPath = process.cwd(); 66 | var FILE1_PATH = processPath + '/file1'; 67 | var FILE2_PATH = processPath + '/file2'; 68 | var FILE3_PATH = processPath + '/file3'; 69 | var data = { 70 | string : '', 71 | contents: [] 72 | }; 73 | 74 | wait1000(data) 75 | .then(getRandomString) //Then expects a function as arguments .then(onFulfilled, onRejected). 76 | .then(writeFile(FILE1_PATH)) //Because readFile and writeFile are curried we can pass an argument. 77 | .then(wait1000) 78 | .then(getRandomString) 79 | .then(writeFile(FILE2_PATH)) 80 | .then(wait1000) 81 | .then(readFile(FILE1_PATH)) 82 | .then(wait1000) 83 | .then(readFile(FILE2_PATH)) 84 | .then(wait1000) 85 | .then(concatContents) 86 | .then(writeFile(FILE3_PATH)) 87 | .catch(handleError) //handleError will catch an error from any of the above functions. 88 | .then(function finalMessage(data) { 89 | console.log('Thats much better...'); 90 | }); 91 | } 92 | 93 | doStuff(); 94 | -------------------------------------------------------------------------------- /tasks/05/README.md: -------------------------------------------------------------------------------- 1 | # ES2015 Задачи 2 | 3 | ## NodeJS Streams, Events, Http 4 | 5 | 1. Да се напише конструктор функция **FileReader**, която има properties: 6 | 7 | ``` 8 | lastLine (число) 9 | buffer (масив) 10 | ... и каквито други са ви необходими 11 | ``` 12 | 13 | и [разширява](https://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor) [stream.Writable](https://nodejs.org/api/stream.html#stream_class_stream_writable). 14 | 15 | При идване на данни към потока те трябва да се convert-нат към String, който се разцепва спрямо '\n'. 16 | Ако последния елемент на стринга, не е '\n' и все още потокът не е затворен трябва да запомним (в `lastLine`) 17 | последния елемен от масива (не е цял ред). На следващото писане трябва да го конкатенираме с 18 | новополучените данни. Всички останали редове се слагат във вътрешния масив `buffer`. Четенето на нови 19 | данни от страна на потока трябва да става, когато масивът `buffer` е празен. 20 | 21 | **[stream.Readable](https://nodejs.org/api/stream.html#stream_class_stream_readable)** (прочетен файл, мрежов източник, пр.) -----*data*-----> **FileReader** (наследник на stream.Writable, имплементира [write](https://nodejs.org/api/stream.html#stream_writable_write_chunk_encoding_callback)) 22 | 23 | FileReader също трябва да има метод `fetchLine`, който извършва асинхронна операция, която трябва да 24 | се реализира със `setTimeout`. Този метод трябва да хвърля евент `next-line` с поредния елемент от 25 | масива `buffer`, а ако той не съществува и все още потокът не е затворен трябва да се прочете нов 26 | chunk от данни и да се повтори същата операция. Ако потокът е затворен значи сме приключили с 27 | четенето на данни и можем да хвърлим евента с `null`. 28 | 29 | Въпрос: Защо трябва `fetchLine` да е асинхронен и да използваме `setTimeout` вместо [`process.nextTick`](https://nodejs.org/api/process.html#process_process_nexttick_callback_arg)? 30 | 31 | ------ 32 | 33 | 2. Да се реализира конструктор функция с името **RoundRobin**, която взима като аргументи масив streams от FileReader-и. RoundRobin разширява EventEmitter и има properties: 34 | 35 | ``` 36 | streams, 37 | currentLine (масив), 38 | generator 39 | ... и каквито други са ви необходими. 40 | ``` 41 | 42 | Обектите създадени с тази функция трябва да имат и метод start, който предизвиква стартирането на генератора. 43 | 44 | Ако имаме n на брой файла трябва към всеки един от тях да има отворен [fs.ReadStream](https://nodejs.org/api/fs.html#fs_class_fs_readstream), който е pipe-нат 45 | към FileReader. Искаме да обикаляме по всеки един от тези FileReader-и и да взимаме по един ред. 46 | След първото преминаване по всички потоци резултатът намиращ се в currentLine (масив с N елемента в случая) трябва да изглежда така: 47 | 48 | ``` 49 | ["file1-Line1", "file2-Line1", ... , "fileN-Line1"] 50 | ``` 51 | 52 | След създаването на string от вида: 53 | 54 | ``` 55 | file1-Line1 file2-Line1 ... fileN-Line1 56 | ``` 57 | 58 | искаме да хвърлим евент `new-line` с получения резултат и да продължим нататък. 59 | Процесът спира, когато всички неща в `currentLine` са `null` и трябва да хвърлим евент `finish`. 60 | (Някои файлове може да са по-дълги от други) 61 | 62 | ------ 63 | 64 | 3. Да се създаде http сървър работещ на порт, който сме подали при стартирането на скрипта. Когато дойде request искаме да вземем pathname-а (пример http://localhost/file1/file2/file3 - pathname е /file1/file2/file3) и да го разбием на '/'. В зависимост, колко файла има зададени в url-то и дали ги има на файловата система искаме да използваме нещата от задача 1 и 2 за да върнем резултат от вида: 65 | 66 | ```text 67 | file1-Line1 file2-Line1 file3-Line1 68 | file1-Line2 file2-Line2 file3-Line2 69 | ... 70 | ``` 71 | 72 | Когато получим request, който иска от сървъра да му върне favicon трябва да върнем 404 със съобщение "File not found". 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | x64/ 49 | build/ 50 | [Bb]in/ 51 | [Oo]bj/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | *_i.c 58 | *_p.c 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.log 79 | *.scc 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | 101 | # TeamCity is a build add-in 102 | _TeamCity* 103 | 104 | # DotCover is a Code Coverage Tool 105 | *.dotCover 106 | 107 | # NCrunch 108 | *.ncrunch* 109 | .*crunch*.local.xml 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.Publish.xml 129 | *.pubxml 130 | *.publishproj 131 | 132 | # NuGet Packages Directory 133 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 134 | #packages/ 135 | 136 | # Windows Azure Build Output 137 | csx 138 | *.build.csdef 139 | 140 | # Windows Store app package directory 141 | AppPackages/ 142 | 143 | # Others 144 | sql/ 145 | *.Cache 146 | ClientBin/ 147 | [Ss]tyle[Cc]op.* 148 | ~$* 149 | *~ 150 | *.dbmdl 151 | *.[Pp]ublish.xml 152 | *.pfx 153 | *.publishsettings 154 | 155 | # RIA/Silverlight projects 156 | Generated_Code/ 157 | 158 | # Backup & report files from converting an old project file to a newer 159 | # Visual Studio version. Backup files are not needed, because we have git ;-) 160 | _UpgradeReport_Files/ 161 | Backup*/ 162 | UpgradeLog*.XML 163 | UpgradeLog*.htm 164 | 165 | # SQL Server files 166 | App_Data/*.mdf 167 | App_Data/*.ldf 168 | 169 | ############# 170 | ## Windows detritus 171 | ############# 172 | 173 | # Windows image file caches 174 | Thumbs.db 175 | ehthumbs.db 176 | 177 | # Folder config file 178 | Desktop.ini 179 | 180 | # Recycle Bin used on file shares 181 | $RECYCLE.BIN/ 182 | 183 | # Mac crap 184 | .DS_Store 185 | 186 | 187 | ############# 188 | ## Python 189 | ############# 190 | 191 | *.py[cod] 192 | 193 | # Packages 194 | *.egg 195 | *.egg-info 196 | dist/ 197 | build/ 198 | eggs/ 199 | parts/ 200 | var/ 201 | sdist/ 202 | develop-eggs/ 203 | .installed.cfg 204 | 205 | # Installer logs 206 | pip-log.txt 207 | 208 | # Unit test / coverage reports 209 | .coverage 210 | .tox 211 | 212 | #Translations 213 | *.mo 214 | 215 | #Mr Developer 216 | .mr.developer.cfg 217 | 218 | node_modules 219 | compiled 220 | -------------------------------------------------------------------------------- /html/img/composite.svg: -------------------------------------------------------------------------------- 1 |

Component




+ operation()

[Not supported by viewer]

Leaf




+ operation()

[Not supported by viewer]

Composite


+ components


+ operation()

[Not supported by viewer]
for component in componentscomponent.operation()
-------------------------------------------------------------------------------- /html/img/strategy.svg: -------------------------------------------------------------------------------- 1 |

<<Interface>>
IStrategy




+ execute()

[Not supported by viewer]

ConcreteStrategy1




+ execute()

[Not supported by viewer]

ConcreteStrategy2




+ execute()

[Not supported by viewer]

Context





[Not supported by viewer]
-------------------------------------------------------------------------------- /tasks/02/README.md: -------------------------------------------------------------------------------- 1 | # 02. Функционално Програмиране с Javascript - Задачи 2 | 3 | 1. Направете функция която която приема произволен брой аргументи и връща сумата от квадратите на нечетните аргументи (числа, а не по индекси). 4 | 5 | ```javascript 6 | sumSquaresOfOddArgs(1, 2, 3, 4, 5, 6) 7 | > 35 8 | ``` 9 | 10 | 2. Направете функция sum която взима като аргументи 2 числа но при подадено само едно връща нова функция (взимаща 1) която ще сумира с първия аргумент. 11 | 12 | ```javascript 13 | var partialSum = sum(5); 14 | partialSum 15 | > [Function] 16 | 17 | partialSum(10) 18 | > 15 19 | 20 | sum(5)(2) 21 | > 7 22 | ``` 23 | 24 | 3. Направете функция curry взимаща друга функция (а) като аргумент и връща а със способност да и се подават частично аргументи 25 | 26 | ```javascript 27 | var curried = curry(function (a, b, c, d) { 28 | return a + b + c; 29 | }); 30 | 31 | curried(6, 6); 32 | > [Function] 33 | 34 | 35 | curried(6, 6)(6, 6); 36 | > 18 37 | 38 | ``` 39 | 40 | 4. Направете функция sequence която взима като аргументи произволен брой функции и връща нова функция която прилага всички тези функции върху своя аргумент. 41 | 42 | ```javascript 43 | var number = sequence() 44 | var toNumber = sequence(parseFloat, Math.round); 45 | 46 | // първо прилага parseFloat а после Math.round 47 | toNumber('66.7') 48 | > 67 49 | ``` 50 | 51 | 5. Направете функция compose която работи като sequence но изпълнява аргументите (функциите) в обратен ред. 52 | 53 | ```javascript 54 | var number = compose(Math.round, parseFloat); 55 | number('66.6') 56 | > 67 57 | 58 | ``` 59 | 60 | 6. Направете функция cons (двойка) която взима като аргументи два елемента.cons връща функция която взима като аргумент функция която се изпълнява върху двойката а и b. 61 | 62 | ```javascript 63 | cons(a, b) 64 | > [Function] 65 | ``` 66 | 7. Направете функция car(глава) която взима като аргумент вече създадена двойка от cons, прилагайки му функция която връща първия елемент на двойката (а). 67 | 68 | ```javascript 69 | var pair = cons(7, 8); 70 | car(pair) 71 | > 7 72 | ``` 73 | 74 | 8. Направете функция cdr(опашка) която взима като аргумент вече създадена двойка(cons) и му прилага функция която връща втория елемент на двойката (b). 75 | 76 | ```javascript 77 | var list = cons(7, cons(8, 9)); 78 | var tail = cdr(list) 79 | tail 80 | > [Function] 81 | cdr(tail) 82 | > 9 83 | ``` 84 | 85 | 9. Направете функция forEach която взима аргументи списък от двойки и функция която да се изпълни върху всеки елемент от списъка. 86 | 87 | ```javascript 88 | var log = console.log.bind(console); 89 | var list = cons(1, cons(2, cons(3, cons(4, 5)))); 90 | forEach(list, log); 91 | 92 | > 1 93 | 2 94 | 3 95 | 4 96 | 5 97 | ``` 98 | 99 | 10. Направете функция map която взима като аргументи свързан списък от двойки и функция f. 100 | map връща нов списък като на всеки елемент има приложена функцията f. 101 | 102 | ```javascript 103 | var list = cons(1, cons(2, cons(3, cons(4, 5)))); 104 | var mapped = map(list, function (el) { 105 | return el * el; 106 | }); 107 | 108 | forEach(mapped, log); 109 | > 1 110 | 4 111 | 9 112 | 16 113 | 25 114 | ``` 115 | 116 | 117 | 11. Направете функция filter която взима като аргументи списък от двойки и предикатна функция и връща нов списък само с елементите за които тази предикатна функция връща true. 118 | 119 | ```javascript 120 | var list = cons(1, cons(2, cons(3, cons(4, 5)))); 121 | var filtered = filter(list, function (e) { 122 | return e % 2 === 0; 123 | }); 124 | 125 | forEach(filtered, log); 126 | > 2 127 | 4 128 | ``` 129 | 130 | 12. Направете функция reduce която взима като аргументи списък от двойки, функция на два аргумента и първоначална стойност. Функцията която е като втори аргумент акумулира всички елементи върху началната стойност 131 | 132 | ```javascript 133 | var list = cons(1, cons(2, cons(3, cons(4, 5)))); 134 | 135 | var sum = reduce(list, function (accumulation, current) { 136 | return accumulation + current; 137 | }, 0); 138 | 139 | sum 140 | > 15 141 | 142 | var product = reduce(list, function (accum, curr) { 143 | return accum * curr; 144 | }, 1); 145 | 146 | product 147 | > 120 148 | ``` -------------------------------------------------------------------------------- /lectures/00-intro.markdown: -------------------------------------------------------------------------------- 1 | # JavaScript @ ФМИ 2 | 3 | ### 2015/2016 4 | 5 | --- 6 | 7 | # JavaScript като цяло 8 | 9 | * Client side (демек в браузъра) 10 | * Server side (демек не в браузъра) 11 | * На някои странни места (tessel.io) 12 | 13 | --- 14 | # Ще си говорим за 15 | 16 | * Програмиране с JavaScript генерално 17 | * Какво е асинхронен код и защо го ползваме 18 | * Какво (не) харесваме в асинхронния код 19 | * Как да се справим с проблемите 20 | * Каква е разликата между server и client side JavaScript 21 | * ФУНКЦИОНАЛНО ПРОГРАМИРАНЕ!!! 22 | 23 | --- 24 | 25 | # Бегъл план 26 | 27 | * Основните неща в езика 28 | * Променливи 29 | * Функции 30 | * Callbacks 31 | * Събития 32 | * По-интересни js специфични теми 33 | * ООП (без класове) 34 | * Прототипи 35 | * Наследяване 36 | 37 | --- 38 | 39 | class: center 40 | # За да е по-лесно за всички 41 | 42 | ## Node first 43 | 44 | ## Browser later 45 | 46 | --- 47 | 48 | # Защото 49 | 50 | * Консистентност 51 | * Тестването с node е значително по-просто от тестването в браузър. 52 | * Не искаме да рискуваме изказвания като „ама то може и в браузъра“ 53 | 54 | --- 55 | 56 | # Лекции 57 | 58 | * Веднъж в седмицата 59 | * Три часа 60 | * Присъствието очевидно не е задължително 61 | * Но със сигурност помага много 62 | 63 | ??? 64 | 65 | * Кои дни 66 | * От колко часа 67 | * Кои зали 68 | 69 | --- 70 | 71 | # ~Упражнения 72 | 73 | ** < see previous slide > ** 74 | 75 | ??? 76 | 77 | * Кои дни 78 | * От колко часа 79 | * Кои зали 80 | 81 | --- 82 | 83 | # Домашни 84 | 85 | * Планираме да са ~ веднъж на две седмици (но сме реалисти) 86 | * Обикновено срокът ще е една седмица 87 | * Ще се тестват автоматично 88 | * „Ама то sum е почти същото като summ“ 89 | * „Е добре де, връщам String, а не Number, ама е правилно“ 90 | * … 91 | * Очевидно ще формират част от оценката 92 | 93 | ??? 94 | 95 | * Колко често(ни се иска) 96 | * Как ще се проверяват 97 | * Каква тежест 98 | 99 | --- 100 | 101 | # Проекти 102 | 103 | * Не може да завършвате курса без проект 104 | * Няма проблем да е web, с back-end на друг език(ruby/go/perl/…) 105 | * Много харесваме идеята за нещо, което е тотално несвързано с web 106 | * Никога не е твърде рано да започнете 107 | * Трябва да одобрим идеята 108 | 109 | По-лесно е да научите технология, ако я ползвате за реализирането на нещо, което ви е интересно 110 | 111 | ??? 112 | 113 | * Добре е да се почнат възможно най-рано 114 | * Добрата идея помага да научиш нужната технология 115 | * Каква тежест ще имат? 116 | 117 | --- 118 | 119 | # Тестове 120 | 121 | * Два на брой 122 | * В средата на семестъра 123 | * В края на семестъра/през сесията 124 | * В moodle 125 | * Може да изпуснете един 126 | * Може да поправите някой през сесията 127 | * НЕ РАЗЧИТАЙТЕ ТВЪРДЕ МНОГО НА ПОСЛЕДНИТЕ ДВЕ 128 | 129 | --- 130 | 131 | # Дните 132 | 133 | Ще се постараем да не сме толкова досадни. Да заспивате след курса и да се събуждате, за да дойдете на курса определено не е най-прекрасното преживяване. За нас също. 134 | 135 | --- 136 | 137 | # And now for something completely different … 138 | 139 | --- 140 | class: center 141 | # UNIX 142 | ![Dennis Ritchie](img/dennis_ritchie.jpg) 143 | 144 | ??? 145 | 146 | Горещо препоръчваме ползването на UNIX-like среди. 147 | 148 | --- 149 | 150 | # Но все пак 151 | 152 | В [download секцията](http://nodejs.org/download) на сайта на nodejs има msi инсталатори. 153 | 154 | --- 155 | 156 | # nvm/n 157 | 158 | По ред причини глобалното инсталиране на node за dev цели не е твърде удобен подход. 159 | 160 | Двата варианта, които предлагаме са [nvm](github.com/creationix/nvm) и [n](https://github.com/visionmedia/n). 161 | 162 | --- 163 | 164 | # REPL demo 165 | 166 | --- 167 | 168 | # Скриптове 169 | 170 | Пишем програмата си във файл. След това подаваме името на файла като аргумент на `node` командата и по този начин го изпълняваме. 171 | 172 | Изненадани сте, нали? 173 | 174 | ```sh 175 | $ cat hi.js 176 | function sayHi(name) { 177 | console.log('Hello, ' + name); 178 | } 179 | sayHi('Pencho'); 180 | 181 | $ node hi.js 182 | Hello, Pencho 183 | ``` 184 | -------------------------------------------------------------------------------- /lectures/12-d3.markdown: -------------------------------------------------------------------------------- 1 | class: center,middle 2 | #d3.js 3 | 4 | .center[ 5 | data driven documents 6 | ] 7 | --- 8 | .center[ 9 | #data driven documents 10 | ] 11 | .left-column[ 12 | * реактивна библиотека 13 | 14 | * на крактко: съпоставя множества от структурирани данни с SVG множества 15 | 16 | * идва с иструменти като 17 | - подсистема за избиране на елементи 18 | - скали 19 | - механизъм за различаване на разликите при промяна на данните 20 | 21 | * вероятно най-съществената библиотека за работа с уеб графика 22 | 23 | * един от най-важните инструментариуми наред с след jQuery 24 | 25 | ] 26 | 27 | --- 28 | 29 | ```JS 30 | 31 | var dataset = [20, 5, 10, 0, 50]; 32 | 33 | d3.select('body') 34 | .selectAll('p') // selection 35 | .data(dataset) // data binding 36 | .enter() 37 | .append('p') // dom manipulation 38 | .attr('class', 'paragraph') // static property 39 | .text(function(d, i) { // dynamic property 40 | return i + ': my value is ' + d; 41 | }) 42 | .style('font-size', function(d) { 43 | return (d / 2 + 25) + 'px'; // maps to [25, 50] 44 | }); 45 | 46 | ``` 47 | 48 | [https://scottcheng.github.io/d3js-101/#/programming-pattern] 49 | 50 | --- 51 | 52 | .center[ 53 | #data/enter/exit 54 | 55 | * data - обновяваме данните и в следствие на тях...: 56 | 57 | * enter - новите елементи, които ще цъфнат 58 | 59 | * exit - елементите, които си отиват 60 | 61 | 62 | DataEnterUpdateElementsExit 63 | 64 | [http://bost.ocks.org/mike/join/] 65 | 66 | ] 67 | 68 | --- 69 | 70 | .center[ 71 | #data joins 72 | 73 | * ^^^ така се нарича съпоставянето на елементи и данни 74 | 75 | * вместо да казваме на D3 как да направи нещо му казваме какво искаме 76 | 77 | * тоест -> пишем декларативен код... 78 | 79 | * ...и работим с хипероперации в/у изброими(подредени) мулти-множества 80 | 81 | ```JS 82 | /* add many circles according to data */ 83 | 84 | let circle = svg.selectAll("circle") 85 | .data(data) 86 | 87 | circle.exit().remove(); 88 | 89 | circle.enter().append("circle") 90 | .attr("r", 2.5); 91 | 92 | circle 93 | .attr("cx", function(d) { return d.x; }) 94 | .attr("cy", function(d) { return d.y; }); 95 | 96 | ``` 97 | 98 | ] 99 | 100 | --- 101 | 102 | .center[ 103 | #скали 104 | 105 | * функции, които изобразяват данни в накакъв друг домейн, 106 | 107 | * ...запазвайки пропорциите. 108 | 109 | * промяната на скалите няма да предизвика автоматична промяна на на гафикaта 110 | 111 | 112 | ```JS 113 | 114 | var x = d3.time.scale() 115 | .range([10, 280]) 116 | 117 | ``` 118 | ] 119 | --- 120 | 121 | .center[ 122 | 123 | #range / domain 124 | 125 | * range - обемът на пиксели от екрана, които искаме да ... заангажираме 126 | * ***Внимание - в d3 величните за y са обърнати. нулата е в горната част на екрана.*** 127 | * domain - областта на допустимите велични за входните данни 128 | 129 | #видове скали 130 | 131 | * Quantitative Scales - за непрекъснати входни данни като числени велиничи 132 | * Линейни 133 | - d3.scale.linear 134 | - d3.scale.identity 135 | - d3.scale.sqrt 136 | - d3.scale.pow 137 | - d3.scale.log 138 | * Дискретни 139 | - d3.scale.quantize 140 | - d3.scale.qunatile 141 | * Прагови 142 | - d3.scale.threshold 143 | 144 | * Ordinal Scales - за дискретни данни като имена, категории, цветове 145 | 146 | * Time Scales - за времеви области/домени 147 | 148 | ] 149 | 150 | --- 151 | 152 | .center[ 153 | #transition 154 | ] 155 | .left-column[ 156 | * може да се анимират промените по елементите 157 | * анимацията може да се контролира 158 | ] 159 | 160 | --- 161 | 162 | * A Technical Intro - A Technical Intro 163 | * D3@HN - https://hn.algolia.com/?query=d3 164 | ] 165 | -------------------------------------------------------------------------------- /lectures/02-modules.markdown: -------------------------------------------------------------------------------- 1 | # js @ фми 2 | 3 | --- 4 | 5 | # Modules (в кратце) 6 | 7 | Разделяме кода в отделни файлове с цел по-добра изолация на отделните парчета от програмата ни. 8 | 9 | Получаваме по-лесен за поддръжка код, с по-лесно подменими компоненти(стига да се постараем да спазваме консистентни интерфейси между отделните парчета). 10 | 11 | --- 12 | 13 | # require 14 | 15 | Вградена функция в nodejs, с която „поискваме“ модули. 16 | 17 | ```javascript 18 | var fs = require('fs'); 19 | ``` 20 | 21 | Получаваме обект, отговарящ на модула, който сме `require`-нали. 22 | 23 | --- 24 | 25 | # module.exports 26 | 27 | Във всеки файл, който изпълняваме с node имаме достъп до `module` обекта. Това е обект, чрез който можем да достъпваме функционалност свързана с текущия модул. 28 | 29 | След време ще си поговорим по-подробно за това, за сега **don't overthink it!** 30 | 31 | --- 32 | # Домашни/задачки 33 | 34 | Обещахме! 35 | 36 | Като добри хора ще (се постараем много сериозно да) спазим обещанието си. 37 | 38 | --- 39 | # Домашни/задачки 40 | 41 | В тоя ред на мисли всяко домашно, което предавате ще трябва да спазва някаква форма. 42 | 43 | ```javascript 44 | module.exports = { 45 | fib: function (n) { 46 | … 47 | }, 48 | … 49 | } 50 | ``` 51 | 52 | Така си гарантираме, че имаме хубав интерфейс, през който да тестваме решенията ви. 53 | 54 | --- 55 | # Домашни/задачки 56 | 57 | [В github (ще) има задачка](). 58 | 59 | Живейте с мисълта, че имате срок до следващия петък. 60 | 61 | Скоро ще се появи обяснение как да предавате решенията си. 62 | 63 | --- 64 | # но първо 65 | 66 | --- 67 | # List quirks 68 | 69 | ## `for … in …` 70 | 71 | Най-вероятно не прави това, което очаквате. Ако знаехте какво точно прави, по-скоро бихте го избягвали. 72 | 73 | --- 74 | # List quirks 75 | 76 | ## `for … in …` 77 | 78 | ### python 79 | ```python 80 | a = ['brie', 42, 'bacon', 'cheddar', []] 81 | for thing in a: 82 | print thing 83 | 84 | brie 85 | 42 86 | bacon 87 | cheddar 88 | [] 89 | ``` 90 | 91 | --- 92 | # List quirks 93 | 94 | ## `for … in …` 95 | 96 | ### javascript 97 | ```javascript 98 | var a = ['brie', 42, 'bacon', 'cheddar', []] 99 | for (thing in a) { 100 | console.log(thing); 101 | } 102 | 103 | 0 104 | 1 105 | 2 106 | 3 107 | 4 108 | ``` 109 | 110 | --- 111 | # List quirks 112 | 113 | ## `for … in …` 114 | 115 | ### javascript 116 | ```javascript 117 | var a = ['brie', 42, 'bacon', 'cheddar', []] 118 | for (thing in a) { 119 | console.log(a[thing]); 120 | } 121 | brie 122 | 42 123 | bacon 124 | cheddar 125 | [] 126 | ``` 127 | 128 | --- 129 | # List quirks 130 | 131 | ## `for … in …` 132 | 133 | ### javascript 134 | ```javascript 135 | var a = ['brie', 42, 'bacon', 'cheddar', []] 136 | a.foo = 'bar' 137 | for (thing in a) { 138 | console.log(a[thing]); 139 | } 140 | brie 141 | 42 142 | bacon 143 | cheddar 144 | [] 145 | bar 146 | ``` 147 | 148 | --- 149 | # List quirks 150 | ## `for … in …` 151 | 152 | [В MDN пише ](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in): 153 | > … 154 | > 155 | > A for...in loop iterates over the properties of an object in an arbitrary order 156 | > 157 | > … 158 | 159 | --- 160 | # List quirks 161 | ## `for … in …` 162 | 163 | За да сте информирани казваме изрично: 164 | 165 | Списъци се обхождат **винаги и само** с `forEach`. 166 | 167 | --- 168 | # List quirks 169 | ## `in` 170 | 171 | Същия аналог 172 | 173 | ### python 174 | ```python 175 | > a = [4, 5, 42, 'chunky'] 176 | > 'chunky' in a 177 | True 178 | ``` 179 | 180 | ### javascript 181 | ```javascript 182 | > var a = [4, 5, 42, 'chunky'] 183 | > 'chunky' in a 184 | false 185 | ``` 186 | 187 | --- 188 | # List quirks 189 | ## `in` 190 | 191 | Проверка дали нещо принадлежи на списък се прави с `indexOf`. 192 | 193 | ```javascript 194 | var a = [4, 5, 42, 'chunky'] 195 | a.indexOf('chunky') > -1 196 | true 197 | ``` 198 | 199 | --- 200 | # List quirks 201 | 202 | `Array` в javascript не е свързан списък, или последователни „клетки“ от паметта, или друга структура, която се използва често за представяне на масиви/списъци. 203 | 204 | `Array` е „javascript обект“, което означава, че се държи по-скоро като dict/Hash/HashMap/etc. от колкото като линейна или свързана структура. 205 | 206 | --- 207 | # `global` 208 | 209 | Обекта, в който се пазят всички глобални имена за текущия процес. 210 | 211 | --- 212 | # `typeof` 213 | 214 | Задължителното „ако идвате от php“ 215 | -------------------------------------------------------------------------------- /html/img/observer.svg: -------------------------------------------------------------------------------- 1 |

Observer




+ notify()

[Not supported by viewer]

ConcreteObserverB




+ notify()

[Not supported by viewer]

ConcreteObserverA




+ notify()

[Not supported by viewer]

Subject


- observers


+ attachObserver(o:Observer)

+ detachObserver(o:Observer)

+ notifyObservers()

[Not supported by viewer]
for o in observerso.notify()
-------------------------------------------------------------------------------- /tasks/03/solutions.js: -------------------------------------------------------------------------------- 1 | //1. 2 | Array.prototype.myMap = function(operation){ 3 | var result = []; 4 | this.forEach(function(elem){ 5 | result.push(operation(elem)); 6 | }); 7 | return result; 8 | }; 9 | 10 | [1,2,3,4,5,6,7,8,9].myMap(function(el){ return el + 10; }); // -> [11,12,13,14,15,16,17,18,19] 11 | 12 | 13 | 14 | //2. 15 | function Point(x, y){ 16 | this.x = x; 17 | this.y = y; 18 | } 19 | 20 | Point.prototype.toArray = function(){ 21 | var self = this; 22 | var result = []; 23 | Object.keys(this).forEach(function(key){ 24 | var prop = self[key]; 25 | if(prop instanceof Function) return; 26 | result.push(prop); 27 | }); 28 | return result; 29 | }; 30 | 31 | function Point3D(x, y, z){ 32 | Point.call(this, x, y); 33 | this.z = z; 34 | } 35 | 36 | Point3D.prototype = Object.create(Point.prototype); 37 | 38 | var p = new Point(1,2); 39 | var p1 = new Point3D(1,2,3); 40 | console.log(p.toArray()); 41 | console.log(p1.toArray()); 42 | 43 | 44 | 45 | //3 46 | function Vehicle(mileage){ 47 | 48 | this.getMileage = function(){ 49 | return mileage.val; 50 | }; 51 | 52 | this.tostring = function(){ 53 | return 'This Vehicle mileage is ' + mileage.val; 54 | }; 55 | 56 | } 57 | 58 | 59 | function Car(brand, consumption){ 60 | var mileage = { val: 0 }; 61 | Vehicle.call(this, mileage); 62 | var superToString = this.tostring; 63 | 64 | this.drive = function(miles){ 65 | mileage.val += miles; 66 | }; 67 | 68 | this.getBrand = function(){ 69 | return brand; 70 | }; 71 | 72 | this.getConsumption = function(){ 73 | return consumption; 74 | }; 75 | 76 | this.tostring = function(){ 77 | return brand + ' ' + consumption + ' ' + superToString(); 78 | }; 79 | } 80 | 81 | var c1 = new Car('honda', 5); 82 | console.log(c1.getMileage()); // -> 0 83 | console.log(c1.tostring()); // -> honda 5 This Vehicle mileage is 0 84 | c1.drive(1000); 85 | console.log(c1.getMileage()); // -> 1000 86 | console.log(c1.tostring()); // -> honda 5 This Vehicle mileage is 1000 87 | 88 | 89 | 90 | //4 91 | var FMIjs = (function(){ 92 | 93 | var BinaryHeap = function(list){ 94 | var self = this; 95 | if(!(list instanceof Array)) return this; //or use Array.isArray(list) 96 | list.forEach(function(el){ 97 | self.insert(el); 98 | }); 99 | }; 100 | 101 | BinaryHeap.prototype = Object.create(Array.prototype); 102 | 103 | BinaryHeap.prototype.parentIndex = function(i){ 104 | return i === 0 ? 0 : Math.ceil(i/2) - 1; 105 | }; 106 | 107 | BinaryHeap.prototype.leftIndex = function(i){ 108 | return (2 * i) + 1; 109 | }; 110 | 111 | BinaryHeap.prototype.rightIndex = function(i){ 112 | return (2 * i) + 2; 113 | }; 114 | 115 | BinaryHeap.prototype.parentElement = function(i){ 116 | return this[this.parentIndex(i)]; 117 | }; 118 | 119 | 120 | BinaryHeap.prototype.leftElement = function(i){ 121 | return this[this.leftIndex(i)]; 122 | }; 123 | 124 | BinaryHeap.prototype.rightElement = function(i){ 125 | return this[this.rightIndex(i)]; 126 | }; 127 | 128 | BinaryHeap.prototype.getMinimum = function(){ 129 | var lastIndex = this.length - 1; 130 | this.swap(0, lastIndex); 131 | var result = this.pop(); 132 | this.bubbleDown(); 133 | return result; 134 | }; 135 | 136 | BinaryHeap.prototype.isEmpty = function(){ 137 | return this.length === 0; 138 | }; 139 | 140 | BinaryHeap.prototype.insert = function(value){ 141 | this.push(value); 142 | this.bubbleUp(); 143 | return this.toString(); 144 | }; 145 | 146 | BinaryHeap.prototype.swap = function(firstIndex, secondIndex){ 147 | var tmp = this[firstIndex]; 148 | this[firstIndex] = this[secondIndex]; 149 | this[secondIndex] = tmp; 150 | }; 151 | 152 | BinaryHeap.prototype.bubbleUp = function(){ 153 | var index = this.length - 1; 154 | while(this.parentElement(index) > this[index]){ 155 | var parentIndex = this.parentIndex(index); 156 | this.swap(index, parentIndex); 157 | index = parentIndex; 158 | } 159 | }; 160 | 161 | BinaryHeap.prototype.bubbleDown = function(){ 162 | var index = 0; 163 | var rightValue = null; 164 | var leftIndex = null; 165 | var currentValue = null; 166 | while(this.leftElement(index)){ 167 | leftValue = this.leftElement(index); 168 | rightValue = this.rightElement(index); 169 | currentValue = this[index]; 170 | if(currentValue > leftValue || currentValue > rightValue){ 171 | if(leftValue > rightValue){ 172 | this.swap(this.rightIndex(index), index); 173 | index = this.rightIndex(index); 174 | }else{ 175 | this.swap(this.leftIndex(index), index); 176 | index = this.leftIndex(index); 177 | } 178 | }else{ 179 | break; 180 | } 181 | } 182 | }; 183 | 184 | var heapSort = function(list){ 185 | var heap = new BinaryHeap(list); 186 | var result = []; 187 | while(heap.length){ 188 | result.push(heap.getMinimum()); 189 | } 190 | return result; 191 | }; 192 | return { 193 | BinaryHeap: BinaryHeap, 194 | heapSort: heapSort 195 | }; 196 | }()); 197 | 198 | var arr = [1,2,6,9,2,4,9,87,36,21,1]; 199 | console.log(FMIjs.heapSort(arr)); 200 | 201 | var b = new FMIjs.BinaryHeap(); 202 | b.insert(5); 203 | b.insert(10); 204 | b.insert(1); 205 | 206 | console.log(b.getMinimum()); //-> 1 207 | -------------------------------------------------------------------------------- /lectures/09-websockets.markdown: -------------------------------------------------------------------------------- 1 | class: center,middle 2 | #websockets 3 | 4 | --- 5 | .center[ 6 | #pros 7 | ] 8 | .left-column[ 9 | #socket 10 | * Живее „безкрайно“ 11 | * Изисква относително малко meta информация 12 | * Комуникацията тече постоянно в двете посоки 13 | ] 14 | 15 | .right-column[ 16 | #long polling 17 | * Дава механизъм за оторизация 18 | * Така или иначе имаме работещ web сървър 19 | * Имаме добре дефинирана последователност request ⇨ response 20 | ] 21 | 22 | --- 23 | 24 | .center[ 25 | #cons 26 | ] 27 | .left-column[ 28 | #socket 29 | * Low level операци ⇨ ред security забележки 30 | * Нужно е сами да имплементираме протоколи 31 | ] 32 | 33 | .right-column[ 34 | #long polling 35 | * Твърде много повтаряща се meta информация 36 | * Всяка заявка е отваряне на нов socket 37 | ] 38 | 39 | --- 40 | class: center,middle 41 | ###DECISIONS 42 | ##DECISIONS 43 | #DECISIONS 44 | 45 | --- 46 | class: center,middle 47 | ![why don't we have both?](img/both.png) 48 | 49 | --- 50 | #WebSockets 51 | 52 | В крайна сметка една HTTP заявка се реализира чрез отваряне на TCP socket. Браузъра обаче затваря този socket след като получи response(при HTTP/1.1 може да направим няколко заявки по един socket) 53 | 54 | WebSocket-ите са разширение на HTTP/1.1, което позволява да кажем, че искаме този socket да остане отворен неопределено време и по него да реализираме двупосочна комуникация между клиента и сървъра. 55 | 56 | Това се случва, когато в HTTP заявката ни има header `Connection: Upgrade` 57 | 58 | --- 59 | #WebSocket-и в клиента 60 | ``` 61 | WebSocket WebSocket( 62 | in DOMString url, 63 | in optional DOMString protocols 64 | ); 65 | 66 | WebSocket WebSocket( 67 | in DOMString url, 68 | in optional DOMString[] protocols 69 | ); 70 | ``` 71 | 72 | --- 73 | #WebSocket-и в клиента 74 | ```javascript 75 | var socket = new WebSocket("ws://www.example.com/socketserver"); 76 | 77 | socket.onopen = function(event) { 78 | console.log('connected!'); 79 | socket.send('Very first transmission on this socket'); 80 | }; 81 | 82 | socket.onmessage = function(event) { 83 | console.log('The server sent', event.data, 'through the socket'); 84 | }; 85 | ``` 86 | 87 | --- 88 | #WebSocket-и в клиента 89 | 90 | Едно извикване на `send` в клиента предизвиква точно един `message` event в сървъра и обратното. 91 | 92 | --- 93 | #Typed arrays 94 | 95 | В някои случаи имаме нужда от данни в по-суров вид. Такива случаи могат да бъдат комуникация по websocket или пък комуникация с webworker. 96 | Изпращането на инстанции на `Number` през websocket, или пък някаква форма на странно кодиране на числа в `String` би било доста неефективно. 97 | 98 | --- 99 | #Typed arrays 100 | 101 | За целта javascript ни предоставя `ArrayBuffer` обекти, които представляват буквално „парче памет“. Дължината им се измерва в байтове. Не можем директно да манипулираме елементите на един `ArrayBuffer`. За целта използваме типизирани масиви, които предоставят `DataView`, през което да достъпваме данните в него. 102 | 103 | --- 104 | #Typed arrays 105 | 106 | * `Int8Array` 107 | * `Int16Array` 108 | * `Int32Array` 109 | * `Uint8Array` 110 | * `Uint8CLampedArray` 111 | * `Uint16Array` 112 | * `Uint32Array` 113 | * `Float32Array` 114 | * `Float64Array` 115 | 116 | --- 117 | #Typed arrays 118 | 119 | ![typed arrays](img/typed_arrays.png) 120 | 121 | --- 122 | #Typed arrays = WebSockets 123 | 124 | ```javascript 125 | var array = Int8Array(3); 126 | array[0] = 13; 127 | array[1] = 42; 128 | array[2] = 66; //666 > 255 :( 129 | socket.binaryType = 'arraybuffer'; 130 | socket.send(array); 131 | ``` 132 | 133 | --- 134 | #socket.io 135 | И все пак светът е страшен, може пък да попаднем в среда, където няма websocket-и. 136 | 137 | Според [Can I Use](http://caniuse.com/#search=websockets) около 82% от интернет потребителите ползват браузър поддържащ WebSocket-и. За някои проекти това число може да е малко. 138 | 139 | Отделно в случаите когато не се интересуваме толкова много от пренасяне на binary данни, а предпочитаме по-лесна комуникация с json [socket.io](http://socket.io/) е прекрасно решение. 140 | 141 | Съществуват servers имплементации за node, python, ruby, go, java, C#(WTF?!) etc… 142 | 143 | --- 144 | #socket.io 145 | ##благини 146 | Автоматичен fallback до различни транспорти според средата, в която се изпълнява кода. 147 | 148 | * WebSockets 149 | * AJAX long polling 150 | * JSONP 151 | * HTTP Streaming 152 | * Flash sockets(евентуално с допълнителна конфигурация) 153 | 154 | --- 155 | #socket.io 156 | ##благини 157 | 158 | Имаме `EventEmitter` интерфейс(`on` метод) и можем да се закачаме за произволни event-и. Доста по-добре от един единствен `onmessage`. 159 | 160 | ```javascript 161 | var socket = io('http://localhost'); 162 | socket.on('someeventwemadeup', function(dataр) { 163 | console.log('We got that custom event we just made up!'); 164 | }); 165 | ``` 166 | -- 167 | Пращаме/получаваме директно js обекти. 168 | ```javascript 169 | socket.send({ it: 'is', a: 'real', javascript: 'object'}); 170 | ``` 171 | 172 | --- 173 | #socket.io 174 | ##namespaces 175 | Всеки `socket.io` обект може да принадлежи на определен namespace. Това позволява separation of concerns, като едно съобщение може да бъде пращано само по определени socket-и, а не по всички. 176 | 177 | ```javascript 178 | var socket = io('http://localhost/stats'); 179 | ``` 180 | 181 | ```javascript 182 | io.emit('data', '/stats'); 183 | ``` 184 | 185 | Това ще рече, че socket.io сървъра слуша на `http://localhost`, а нашия сокет обект ще бъде в `/stats` namespace-а. 186 | 187 | --- 188 | #socket.io 189 | ##channels 190 | 191 | Когато един socket.io обект се свърже към определен namespace, той не може да го промени. Можем обаче да групираме socket-ите в канали и да променяме каналите, в които се намират в runtime. 192 | 193 | ```javascript 194 | socket.join('a channel'); 195 | 196 | … 197 | 198 | socket.leave('a channel'); 199 | ``` 200 | 201 | ```javascript 202 | io.to('a channel').emit('data'); 203 | ``` 204 | 205 | --- 206 | class: center,middle 207 | ###VNC имплементацията на Минко е [в джитхюб](https://github.com/FMIjs/js-lectures-2014/tree/master/tasks/08/vnc) 208 | -------------------------------------------------------------------------------- /html/img/facade.svg: -------------------------------------------------------------------------------- 1 |

Client



[Not supported by viewer]

Facade




+ do()

[Not supported by viewer]

ComplicatedClassA




+ do()

[Not supported by viewer]

ComplicatedClassA




+ do()

[Not supported by viewer]

ComplicatedClassA




+ do()

[Not supported by viewer]

ComplicatedClassA




+ do()

[Not supported by viewer]

ComplicatedClassA




+ do()

[Not supported by viewer]
-------------------------------------------------------------------------------- /tasks/09/README.md: -------------------------------------------------------------------------------- 1 | # Lightweight AngularJS implementation 2 | 3 | In this exercise we are going to create a lightweight implementation of AngularJS. 4 | 5 | Our framework is going to support directives, two-way data-binding, services, controllers...and even more! 6 | 7 | ## Provider 8 | 9 | 0. Create object literal called `Provider`. The provider object should has the following public methods: 10 | * `get` - used for getting services. 11 | * `directive` - used for defining directives. 12 | * `controller` - used for defining controllers. 13 | * `service` - used for defining services. 14 | * `annotate` - used for getting array of the names of the dependencies of given "provider" (i.e. service, controller or directive). 15 | * `invoke` - used for invoking services (i.e. resolving their dependencies and calling the factory method). 16 | * `Provider` should have properties called `DIRECTIVES_SUFFIX` and `CONTROLLERS_SUFFIX` with values "Directive" and "Controller". 17 | 18 | 0. Define property of type object, which is called `_providers` 19 | 20 | 0. Define a property called `_cache`. It should contains a property with value `new Scope` and key `rootScope` (we are going to implement the `Scope` in later section, for now you can comment the statement). 21 | 22 | 0. Define a method called `annotate`, which accepts a function and returns an array of its arguments' names. 23 | 24 | 0. Add method called `_register`. `_register` should accept two arguments - `name` (name of the provider) and `fn` (factory method of the provider). It should add new property of the `_providers` hash map with key the first argument passed to the method and value `fn`.. 25 | 26 | 0. Define a method called `get`. It should accept arguments called `name` (name of the provider) and `locals` (hash with local dependencies, the keys in the hash are the names of the dependencies and its values are the actual dependencies). `get` should return service with name `name`, if it is already cached (i.e. property of `this._cache`), otherwise it should call the factory method of the service (`this._provider[name]`) with the method `this.invoke` and cache the result. Do not forget to pass the local dependencies to `this.invoke`. 27 | 28 | 0. Define method called `invoke`. It should accept two arguments - `fn` (factory method) and `locals` (local dependencies). Using `annotate` and `get` resolve all dependencies of the current factory method (`fn`) and invoke the factory method. Return the result of the invocation. Note that the dependencies could be located both in `locals` hash and 29 | 30 | 0. Add methods called `directive`, `controller` and `service`. They should accept two arguments - `name` (name of the provider) and `fn` (factory method). They should call `_register` with appropriate name for the provider (i.e. with special suffix for `directive` and `controller` - `DIRECTIVES_SUFFIX`, `CONTROLLERS_SUFFIX`) and the factory method, which is passed as second argument. 31 | 32 | 33 | ## Scope 34 | 35 | 0. Define a constructor function called `Scope`. It should initialize the properties: 36 | * `$$watchers` - an empty array. 37 | * `$$children` - an empty array. 38 | * `$parent` - it should accept the value of a parameter called `parent` passed to the constructor function. 39 | * `$id` - it should accept the value of a parameter called `id` passed to the constructor function or `0` if the passed parameter is `undefined`. 40 | * `Scope` should have a "static" property called `counter`, with initial value `0`. 41 | 0. Define method called `$eval`. It should **evaluate expressions in the context of the current scope**. The expressions, which would be evaluated are: 42 | * Method invocation (i.e. `foo()`) 43 | * Function invocation, i.e. the value of the expression passed to `$eval` will be a function, which should be invoked in the context of the scope. 44 | * Get property value (i.e. `bar`). 45 | 46 | 0. Add method called `$watch` to the prototype of the `Scope` function. It should accept two arguments `exp` and `fn`. It should add new object to the `$$watchers` array. The properties of the new object should be called `exp`, `fn` and `last`. The first two properties should accept the values of the arguments passed to `$watch`, `last` should be set to be equals to **cloned** (`Utils.clone`) value of the value result from evaluation of the expression (`exp`). 47 | 48 | 0. Add method called `$new` - a factory method for creating new scopes. The created scope should inherit prototypically from `this` and its `$id` should be equals to `Scope.counter + 1` (do not forget to increment `Scope.counter`). Add the created scope to the `$$children` array and return it. 49 | 50 | 0. Define method called `$destroy`. It should remove the current scope from the `$$children` array of its parent. 51 | 52 | 0. Define method called `$digest`. Inside the body of the method a loop should iterate over the watchers (`$$watchers`) until all watchers are "clean" (i.e. their current value is equals to their last value - `Utils.equals`). In the end of the method invocation it should be called recursively for all children of the method. If any of the values of the watchers is found dirty, the `fn` (i.e. the observer associated with the current watcher), should be invoked with arguments: 53 | * the current value of the watcher expression 54 | * the previous value of the watcher expression 55 | 56 | 57 | ## DOMCompiler 58 | 59 | 0. Define an object literal called `DOMCompiler`, which has the following public interface: 60 | * `bootstrap` - method responsible for doing the initial parsing of the DOM tree. 61 | * `compile` - method, which accepts a DOM element and scope and compiles the subtree, which has root the passed element, in the context of the passed scope. 62 | 63 | 0. `bootstrap` accepts a single argument - the root element of your application (for example `document.body`) and should invoke `compile` with the `$rootScope` and the root element of your application. 64 | 65 | 0. `compile`, should apply the `link` function of all directives found on the current element and after that should invoke itself recursively with all children elements of the current element. Each registered directive must have two properties: 66 | * `scope` - a boolean property, which indicates that the current directive requires new scope to be created. **NOTE** that no more than one new scope per directive should be created (you should implement this restriction in the `DOMCompiler`). 67 | * `link` - a link function, which is responsible for encapsulating the directive's logic. 68 | 69 | **NOTE** Do not use third party libraries for the implementation of the `DOMCompiler`. For getting all children of given element use: `el.children`. For getting all attributes of given element use `el.attributes`. The returned collection from `el.attributes` will be of type `NamedNodeMap`, which means that you may need to cast it into an array, if necessary. You can access the name of the attribute by: `attr.name` and its value by `attr.value`. 70 | 71 | ## ngl-bind 72 | 73 | Define a directive called `ngl-bind`. When applied to given DOM element as attribute, it should accept an expression as value. When the value of the expression is being changed it should update the content of the element according to the new value of the expression. As initial value of the DOM element `ngl-bind` should set the initial value of the evaluation of the expression. 74 | 75 | ## ngl-model 76 | 77 | Define a directive called `ngl-model`. When applied to given DOM input element as attribute, it should accept name of property of the scope associated with the directive. When the value of the scope's property is being changed this should reflect on the value of the input, once the value of the input is being changed by user interaction this should reflect on the value of the property (i.e. it should implement two-way data-binding). 78 | 79 | ## ngl-controller 80 | 81 | Define a directive called `ngl-controller`. When applied to given DOM element as attribute, as value it should accept name of controller. The link function of this directive should create new controller with name the value of the attribute. The controller should be invoked with local dependencies hash containing property called `$scope` and value the new scope passed to the directive's link function (i.e. the directive should require creation of new scope). 82 | 83 | ## ngl-repeat 84 | 85 | Create new directive called `ngl-repeat`. When applied to given DOM element as attribute it should accept expression in the format `item in items`, as value. When the scope associated with the directive has collection called `items` the directive should iterate over the items in the collection and reference each item in the collection as `item`. The current item should be accessible via the property `item`. For each item in the collection should be created new scope with property called `item`. 86 | 87 | Example: 88 | 89 | When compiled in the context of the scope: 90 | 91 | ```JavaScript 92 | $scope.items = [1, 2, 3]; 93 | ``` 94 | the result of the compilation of the following markup: 95 | 96 | ```HTML 97 |
    98 |
  • 99 | 100 |
  • 101 |
102 | ``` 103 | should be: 104 | 105 | ```HTML 106 |
    107 |
  • 108 | 1 109 |
  • 110 |
  • 111 | 2 112 |
  • 113 |
  • 114 | 3 115 |
  • 116 |
117 | 118 | ``` 119 | 120 | ## ngl-click 121 | 122 | Define directive called `ngl-click`. When applied to given DOM element as attribute it should accept expression, expressing function invocation (i.e. `foo()`). 123 | When the user clicks on element on which the `ngl-click` directive is applied the expression associated with the directive should be invoked in the context of the current scope. 124 | 125 | 126 | ### Sample directive 127 | 128 | ```JavaScript 129 | Provider.directive('ngl-click', function () { 130 | 'use strict'; 131 | return { 132 | scope: false, 133 | link: function (el, scope, exp) { 134 | el.onclick = function () { 135 | scope.$eval(exp); 136 | scope.$digest(); 137 | }; 138 | } 139 | }; 140 | }); 141 | ``` 142 | -------------------------------------------------------------------------------- /lectures/01-variables-functions.markdown: -------------------------------------------------------------------------------- 1 | # Variables, functions, linting 2 | 3 | --- 4 | ## Книжки 5 | 6 | * JavaScript - The Good Parts /David Crockford/ 7 | 8 | --- 9 | 10 | # Променливи 11 | 12 | ```javascript 13 | > a = 5 // BAD 14 | 5 15 | > b = 7; // BAD 16 | 7 17 | > var c = 43 // meeeeeeeh... aaaalmost 18 | undefined 19 | > var d = 42; // GOOD 20 | undefined 21 | ``` 22 | 23 | --- 24 | 25 | # Оценяване 26 | 27 | ```javascript 28 | > var a = 20 29 | undefined 30 | > a 31 | 20 32 | > var b = '20' 33 | undefined 34 | > a == b 35 | true 36 | > a === b 37 | false 38 | ``` 39 | 40 | * Познатото ни двойно равно (==) ще сравно стойността обръщайки типовете до подходящ (според вируталната машина) тип 41 | * Ето защо проверката за еквивалентност по тип и стойност в JS се прави с === (тройно равно) 42 | * Макар и незначителна тази разлика може да доведе до неочаквани и най-често неприятни резултати 43 | 44 | --- 45 | 46 | # Типове „неща“ 47 | ## Числа 48 | 49 | ```javascript 50 | > var lowBoundary = 9000; 51 | undefined 52 | > typeof lowBoundary 53 | 'number' 54 | > var pi = 3.14; 55 | undefined 56 | > typeof pi 57 | 'number' 58 | ``` 59 | * Всичко има тип 60 | * `undefined` е ключова дума в езика 61 | * За да проверим дали нещо е дефинирано или не... : 62 | 63 | ```javascript 64 | > typeof notDefinedVar === 'undefined' 65 | true 66 | ``` 67 | 68 | може и така: 69 | 70 | ```javascript 71 | > notDefinedVar === undefined 72 | true 73 | ``` 74 | 75 | --- 76 | 77 | # Типове „неща“ 78 | ## Низове 79 | 80 | ```javascript 81 | > var name = 'Pen\ncho'; 82 | ``` 83 | 84 | Съвсем същото като 85 | 86 | ```javascript 87 | > var name = "Pen\ncho"; 88 | ``` 89 | 90 | --- 91 | 92 | # Типове „неща“ 93 | ## Списъци 94 | 95 | ```javascript 96 | > var team = ['Joro', 'Minko', 'Evgeni']; 97 | undefined 98 | > team.length 99 | 3 100 | > team[0] 101 | 'Joro' 102 | > team[1] 103 | 'Minko' 104 | > team[-1] 105 | undefined 106 | ``` 107 | * Списъците както всичко друго - са обекти. 108 | * Можем да ги създадем с new или директно с `[]` (квадратни скоби) 109 | * Добавянето на елементи НЕ предполага явно 'разщиряване на размера' 110 | * Списъците са динамични и се оразмеряват според броя елементи 111 | 112 | --- 113 | # Типове „неща“ 114 | ## Обекти 115 | 116 | ```javascript 117 | > var panda = {name: 'Стамат', age: 12, cuteness: 9000.001 }; 118 | undefined 119 | > panda 120 | { name: 'Стамат', 121 | age: 12, 122 | cuteness: 9000.001 } 123 | > panda.name 124 | 'Стамат' 125 | > panda.weight = 30 126 | 30 127 | > panda.class 128 | undefined 129 | > panda['cuteness'] 130 | 9000.001 131 | ``` 132 | 133 | * Обект/асоциативен списък са едно и също в javascript се оказва 134 | че всички атрибути на даден обект са обект на autovivification ... 135 | * ...в момента на първото присвояване на стойност!!! 136 | * ...но ако не е било присвоено нищо на даден атрибут, първото му 137 | прочитане връща undefined 138 | * т.е. -> при добавяне на атрибут се самосъздава елемента без да е нужно 139 | неговото експлицитно (по някакъв начин) предефиниране. 140 | 141 | --- 142 | # Функции 143 | 144 | ```javascript 145 | function sayHi(name) { 146 | console.log('Hello, ' + name); 147 | } 148 | ``` 149 | 150 | ```javascript 151 | function sumTwoThings(a, b) { 152 | return a + b; 153 | }; 154 | ``` 155 | 156 | ```javascript 157 | function sumAllTheThings () { 158 | var result = arguments[0]; 159 | for(var i = 1; i < arguments.length; ++i) { 160 | result += arguments[i]; 161 | } 162 | 163 | return result; 164 | } 165 | ``` 166 | 167 | * Дефиницията на функциите е като в C и Java 168 | * Но в JavaScript дефиниция на функция води до 169 | създаване на екземпляр на обект от типа `Function` 170 | * т.е. всичи функции са обекти, но за това повече после... 171 | 172 | ```javascript 173 | var sumTwoThings = function (a, b) { 174 | return a + b; 175 | }; 176 | ``` 177 | 178 | # Асинхронни функции 179 | ```javascript 180 | setTimeout(function () { 181 | console.log('Hello There'); 182 | }, 1000); 183 | ``` 184 | 185 | * Ще се изпълни след хиляда мили секунди 186 | 187 | 188 | ```javascript 189 | for (var i = 0; i < 5; i++) { 190 | setTimeout(function () { 191 | console.log(i); 192 | }, 1000); 193 | } 194 | > 5 195 | 5 196 | 5 197 | 5 198 | 5 199 | ``` 200 | 201 | * Иска да изпечата **i**, но докато е минала една секунда цикълът отдавна е стигнал вече до 5 202 | * Асинхронна операция (не е в нормалния поток на изпълнение) 203 | * Изпълнява се от Event Loop модела на Javascript 204 | 205 | --- 206 | ## `for` е кофти 207 | 208 | * Не, няма смисъл да спорим 209 | * Повече по този въпрос после 210 | * Има няколко други механизъма за итериране, които са в духа на JavaScript 211 | 212 | --- 213 | 214 | # По-подробно за списъци 215 | 216 | ## Методи 217 | 218 | ```javascript 219 | > Object.getOwnPropertyNames(Object.getPrototypeOf(a)) 220 | [ 'length', 221 | 'constructor', 222 | 'toString', 223 | 'toLocaleString', 224 | 'join', 225 | 'pop', 226 | 'push', 227 | 'concat', 228 | 'reverse', 229 | 'shift', 230 | 'unshift', 231 | 'slice', 232 | 'splice', 233 | 'sort', 234 | 'filter', 235 | 'forEach', 236 | 'some', 237 | 'every', 238 | 'map', 239 | 'indexOf', 240 | 'lastIndexOf', 241 | 'reduce', 242 | 'reduceRight' ] 243 | ``` 244 | 245 | --- 246 | ### `for` е гаден 247 | 248 | ```javascript 249 | var albums = ['Lateralus', '10,000 days', 'Ænima']; 250 | albums.forEach(function (album) { 251 | console.log(album + ' is an album by Tool'); 252 | }); 253 | ``` 254 | 255 | --- 256 | # filter 257 | 258 | ```javascript 259 | > albums.filter(function (album) { 260 | return album.charAt(0) === 'Æ'; 261 | }); 262 | ['Ænima'] 263 | ``` 264 | 265 | --- 266 | # map 267 | ```javascript 268 | > albums.map(function (album) { 269 | return album.toLowerCase(); 270 | }); 271 | ['lateralus', '10,000 days', 'ænima'] 272 | ``` 273 | 274 | --- 275 | # push/pop 276 | 277 | ```javascript 278 | > albums.push('Undertow'); 279 | 4 280 | > albums 281 | ['Lateralus', '10,000 days', 'Ænima', 'Undertow'] 282 | > albums.pop(); 283 | 'Undertow' 284 | albums 285 | ['Lateralus', '10,000 days', 'Ænima'] 286 | ``` 287 | 288 | --- 289 | # `Array` 290 | 291 | ```javascript 292 | > var bands = new Array(10); 293 | undefined 294 | > bands 295 | [ , , , , , , , , , ] 296 | ``` 297 | 298 | --- 299 | # `Array` 300 | 301 | ```javascript 302 | > var things = new Array(10, 'asd'); 303 | undefined 304 | > things 305 | [ 10, 'asd' ] 306 | ``` 307 | 308 | --- 309 | # shift/unshift 310 | 311 | Абсолютно същото, но в началото на списъка, а не в края 312 | 313 | --- 314 | # Прости структури от данни 315 | 316 | * Сам по себе си е списък 317 | * `pop`/`push` ⇨ стек 318 | * `unshift`/`pop` или `push`/`shift` ⇨ опашка 319 | 320 | --- 321 | 322 | # Нехомогенни 323 | 324 | ```javascript 325 | > things = [42, 'brie', {species: 'unicorn', пробабилитъ: '0.000000001'}] 326 | ``` 327 | 328 | 329 | --- 330 | # Сложност 331 | 332 | Сложността на операциите върху `Array` обекти най-вероятно не е каквато очаквате. За това има [много добро обяснение](http://stackoverflow.com/questions/11514308/big-o-of-javascript-arrays#answer-11535121). 333 | 334 | **TL;DR** Списъците са обекти, обектите са хешове. 335 | --- 336 | # Javascript runtime (v8) 337 | 338 | --- 339 | # Какво представлява? 340 | ![alt text](../html/img/runtime.png "runtime image") 341 | --- 342 | # Stacking 343 | 344 | javascript 345 | function f(b){ 346 | var a = 12; 347 | return a+b+35; 348 | } 349 | 350 | 351 | function g(x){ 352 | var m = 4; 353 | return f(m*x); //f се слага в stack 354 | } 355 | 356 | g(21); //g се слага в stack 357 | 358 | 359 | * Когато имаме function call push-ваме в stack-а. 360 | * Когато функцияна приключи pop-ваме от stack-а. 361 | 362 | --- 363 | #Javascript is single threaded 364 | --- 365 | # Какво се случва, когато възникне event докато Stack-a е пълен? 366 | --- 367 | ![alt text](../html/img/event_loop.png "event loop image") 368 | * Koгато stack-а Е ПРАЗЕН Event loop-а чете от TaskQueue-то 369 | и ако има task го push-ва в stack-а. 370 | --- 371 | 372 | ## Pushing tasks 373 | 374 | * В browser-а съобщения се добавят, когато възникне някакъв event и 375 | имаме event handler за него. (webAPIs -> addEventListener) 376 | 377 | * Също setTimeout(), process.nextTick() и др. 378 | 379 | javascript 380 | setTimeout(function(){ alert(1); }, 1000) //Изпълни се след минимум 1 сек 381 | 382 | 383 | setTimeout е функция на браузъра (Web APIs). Когато я извикаме browser-а пускай timer и след изтичането му 384 | callback-а се push-ва в TaskQueue. Подобно е и когано правим ajax requests. 385 | 386 | 387 | javascript 388 | setTimeout(function(){ alert(1); }, 0) 389 | // Може да си мислим като - TaskQueue.push(function(){ alert(1); }) 390 | 391 | 392 | * Важно е да пешем non-blocking code! 393 | 394 | * Трябва да се внимава с for, while, forEach защото са синхронни. 395 | 396 | * Render Queue също зависи от javascript. 397 | Не можем да рендерираме, когато имаме функции в stack-а.Render Queue има 398 | по-високо priority от callback-овете, които слагаме в TaskQueue. 399 | --- 400 | ### Blocking the event loop 401 | 402 | javascript 403 | var fs = require('fs'); //require the module for file system ops 404 | 405 | var file = fs.readFileSync('./myBigFile'); //synchronously read file 406 | 407 | 408 | Това блокира thread-а докато целия файл не бъде прочетен. 409 | 410 | Решението е fs.readFile(filepath, callback), което чете файла 411 | на малки парчета (chunks) и след това извиква 412 | callback-а с прочетения файл като аргумент. 413 | 414 | 415 | --- 416 | # За разнообразие и по нужда 417 | ### linter-и(мъхясване?) 418 | 419 | * jslint/jshint 420 | * Интегрират се с всяка разумна среда 421 | 422 | *** 423 | 424 | * vim : 425 | [чрез syntastic](https://github.com/scrooloose/syntastic) 426 | * emacs : 427 | [чрез flycheck](http://www.emacswiki.org/emacs/Flycheck) 428 | * sublime text : 429 | [sublime-jslint](http://opensourcehacker.com/2013/04/12/jslint-integration-for-sublime-text-2/) 430 | * ако не сте си писали редактора сами най-вероятно има разумен начин да подкарате linter с него. 431 | 432 | -------------------------------------------------------------------------------- /lectures/08-dom-intro-and-events.markdown: -------------------------------------------------------------------------------- 1 | # Съдържание 2 | 3 | * DOM 4 | * DOM vs HTML 5 | * Типове данни 6 | * Селектиране на елементи 7 | * Добавяне на събития 8 | * addEventListener 9 | * eventObject 10 | * bubbling 11 | * capturing 12 | * on.\* 13 | * Премахване на събития 14 | * Memory leaks при обработката на събития 15 | --- 16 | 17 | # Document Object Model 18 | 19 | API за работа с HTML и XML 20 | 21 | Можем да си мислим за HTML като за сериализирана версия на DOM. 22 | 23 | --- 24 | 25 | # DOM, типове елементи 26 | 27 | - `document` - коренът на DOM дървото 28 | - `Element` - елемент в DOM дървото, който има дъщерни елементи 29 | - `NodeList` - списък с DOM обекти, които могат да бъдат както елементи, така и `text` 30 | - `Text` - DOM обект, който съдържа само текст, не можа да съдържа елементи 31 | 32 | --- 33 | 34 | ## Достъп до елементи в DOM дървото 35 | 36 | - `document.createElement(tag_name)` - създава нов елемент 37 | - `element.childNodes` - достъп до всички дъщерни елементи на даден елемент 38 | - върнатият резултат е от тип `NodeList`, не е масив 39 | - `element.children` - връща всички дъщерни елементи от тип `Element` 40 | - `element.appendChild(child)` - добавя дъщерен елемент на даден елемент 41 | - `element.removeChild(child)` - изтрива дъщерен елемент от даден елемент 42 | 43 | --- 44 | 45 | # Атрибути vs полета 46 | 47 | в HTML можем да зададем атрибути на различните елементи: 48 | 49 | ```html 50 | 51 | ``` 52 | 53 | След като браузърът обработи HTML ще създаде `HTMLInputElement`, който ще има различни полета: 54 | 55 | - checked 56 | - type 57 | - disabled 58 | - value 59 | - ... 60 | 61 | --- 62 | 63 | # Работа с атрибути 64 | 65 | - `element.setAttribute(attr_name, value)` 66 | - `element.getAttribute(attr_name)` 67 | - `element.removeAttribute(attr_name)` 68 | 69 | --- 70 | 71 | # Селектиране на елементи 72 | 73 | - `document.getElementById(id)` - селектира елемент по уникален идентификатор 74 | - `element.getElementsByTagName(tag_name)` - селектира елементи по име на таг 75 | - `element.getElementsByClassName(class_name)` - селектира елементи по име на клас 76 | - `element.querySelectorAll(selector)` - селектира всички елементи, които отговарят на зададения селектор 77 | - `element.querySelector(selector)` - селектира първият елемент, които отговаря на зададения селектор 78 | 79 | --- 80 | 81 | # CSS селектори 82 | 83 | - `element` - селектира всички елементи с име `element` 84 | - `#element-id` - селектира елемента с идентификатор `element-id` 85 | - `.class-name` - селектира всички елемент, които използват клас с име `class-name` 86 | - `[attribute="value"]` - селектира всички елементи, които имат атрибут `attribute` със стойност `value` 87 | - `parentSelector childSelector` - селектира всички елемент, които съвпадат със селектора `childSelector` и се намират в елементи съвпадащи със селектора `parentSelector` 88 | 89 | --- 90 | 91 | # Примери 92 | 93 | ```javascript 94 | document.querySelectorAll('#foo'); 95 | 96 | document.querySelector('.foo-bar'); 97 | 98 | document.querySelectorAll('content.container [title="foobar"]:nth-child(2)'); 99 | ``` 100 | --- 101 | 102 | # Shadow DOM 103 | 104 | Част от стандарта на WebComponents. Позволява ни по-добра енкапсулация. 105 | 106 | - Можем да създадем т.нар. "shadow root", който е корен на поддърво, което се третира по други правила от браузъра 107 | - Създаденото поддърво е независимо откъм стилове 108 | 109 | --- 110 | 111 | # Shadow DOM 112 | 113 | ```html 114 | 124 |
Foobar
125 | ``` 126 | 127 | 128 | ```javascript 129 | let root = document.querySelector('#foo').createShadowRoot(); 130 | let clone = document.importNode(document.querySelector('#greeting').content, true); 131 | root.appendChild(clone); 132 | ``` 133 | 134 | --- 135 | 136 | # Template елемента 137 | 138 | Позволява да добавим markup в документа без той да бъде обработван от браузъра. 139 | 140 | Не е нужно да използваме `