├── .gitignore ├── Application ├── 1-Mask-Words │ └── README.md ├── 2-A-Simple-Logger │ └── README.md ├── 3-Query-Language-Over-CSV │ └── README.md └── README.md ├── README.md ├── week1 ├── 1-hello-node │ └── README.md ├── 2-simple-blog │ └── README.md ├── 3-jade-blog │ └── README.md ├── README.md └── hangout │ ├── .bowerrc │ ├── .gitignore │ ├── bower.json │ ├── package.json │ ├── posts.json │ ├── public │ ├── css │ │ └── style.css │ └── img │ │ ├── bg.jpg │ │ ├── content-bg.png │ │ ├── logo.png │ │ ├── post1.png │ │ └── post2.jpg │ ├── server.js │ └── views │ ├── index.jade │ ├── layout.jade │ └── post.jade ├── week2 ├── 1-bulls-n-cows │ └── README.md ├── 2-console-crud │ └── README.md ├── README.md └── hangout │ ├── crud.js │ ├── data │ └── users.json │ └── package.json ├── week3 ├── 1-A-Panda │ ├── README.md │ └── panda.js ├── 2-Point │ ├── README.md │ └── points.js ├── 3-Prototypes │ ├── README.md │ └── prototypes.js ├── 4-Queue │ ├── README.md │ └── queue.js ├── 5-Event-Bus │ ├── README.md │ └── bus.js ├── 6-HTML-Generator │ └── README.md ├── README.md └── materials │ ├── call_apply.js │ ├── closure.js │ └── person.js ├── week4 ├── 1-Keeping-up-the-Score │ ├── README.md │ ├── mockup.png │ ├── page.html │ └── page.js ├── 2-DOM-Todo │ ├── README.md │ ├── example.html │ ├── example.js │ └── mockup.png ├── README.md ├── hangout │ ├── README.md │ ├── dom-wrapper.js │ ├── index.html │ └── interface.js └── materials │ ├── dom.js │ ├── page.html │ └── page.js ├── week5 ├── 2-todo-crud │ └── README.md ├── README.md └── hangout │ ├── .bowerrc │ ├── bower.json │ ├── css │ └── style.css │ ├── index.html │ └── js │ ├── app.js │ └── init.js ├── week6 ├── 1-bootstrap-shop │ └── README.md ├── 2-hobby-site │ ├── .bowerrc │ ├── README.md │ ├── server.js │ └── views │ │ ├── index.jade │ │ └── layout.jade ├── README.md ├── guides │ └── sass.md ├── hangout1 │ ├── .bowerrc │ ├── bower.json │ ├── data │ │ └── rest.json │ ├── package.json │ ├── public │ │ ├── css │ │ │ └── style.css │ │ └── js │ │ │ ├── app.js │ │ │ ├── init.js │ │ │ ├── taskModel.js │ │ │ └── tasksView.js │ ├── server.js │ └── views │ │ └── index.jade ├── hangout1_full │ ├── .bowerrc │ ├── bower.json │ ├── data │ │ └── rest.json │ ├── package.json │ ├── public │ │ ├── css │ │ │ └── style.css │ │ └── js │ │ │ ├── app.js │ │ │ ├── init.js │ │ │ ├── taskModel.js │ │ │ └── tasksView.js │ ├── server.js │ └── views │ │ └── index.jade └── hangout2 │ ├── .bowerrc │ ├── bower.json │ ├── package.json │ ├── public │ ├── css │ │ ├── inc │ │ │ ├── _base.scss │ │ │ ├── _layout.scss │ │ │ ├── helpers │ │ │ │ ├── _bootstrap_mod.scss │ │ │ │ └── _vars.scss │ │ │ └── sections │ │ │ │ ├── _events.scss │ │ │ │ └── _home.scss │ │ ├── style.css │ │ └── style.scss │ └── img │ │ ├── artists │ │ ├── james-brown.jpg │ │ ├── jamiroquai.jpg │ │ └── tom-jones.jpeg │ │ ├── events │ │ ├── event1.jpg │ │ ├── event2.jpg │ │ └── event3.jpg │ │ └── header-bg.jpg │ ├── server.js │ └── views │ ├── artists.jade │ ├── events.jade │ ├── index.jade │ └── layout.jade ├── week7 └── 1-students-crud │ └── README.md └── week8 ├── 2-rest-resource ├── .bowerrc ├── README.md ├── data │ └── restMap.json ├── public │ └── js │ │ └── init.js └── server.js ├── README.md ├── hangout ├── .bowerrc ├── .gitignore ├── bower.json ├── data │ └── restMap.json ├── package.json ├── public │ ├── js │ │ ├── init.js │ │ └── resource.js │ └── views │ │ ├── mags.jade │ │ ├── subs.jade │ │ └── users.jade ├── server.js └── views │ └── index.jade └── rest-example ├── .bowerrc ├── bower.json ├── data └── restMap.json ├── package.json ├── public └── js │ ├── init.js │ └── resource.js ├── server.js └── views └── index.jade /.gitignore: -------------------------------------------------------------------------------- 1 | README.html 2 | lib 3 | node_modules 4 | public/lib 5 | style.css.map 6 | npm-debug.log 7 | .sass-cache 8 | -------------------------------------------------------------------------------- /Application/1-Mask-Words/README.md: -------------------------------------------------------------------------------- 1 | # Problem 1 - Mask Out Words 2 | 3 | In a programming language of your choice, implement the following function/method: 4 | 5 | ``` 6 | maskOutWords(words, text) 7 | ``` 8 | 9 | If you read type declarations well, here it is: 10 | 11 | ```haskell 12 | maskOutWords :: [String] -> String -> String 13 | ``` 14 | 15 | Where: 16 | 17 | * `words` is a list of words (strings) 18 | * `text` is a string, that may contain newlines - `\n` 19 | 20 | 21 | The function should return a new text, where each matching word from `words` is replaced by the same number of characters `*`. 22 | 23 | Consider the following things: 24 | 25 | * **All matching should be case insensitive!** 26 | * Take care of words that end with `,` or `.` - they should be matached, without the punctiation. 27 | 28 | For example, if we have the following arguments: 29 | 30 | * `words = ["PHP"]` 31 | * `text = "We love coding in PHP!\nThis makes us really productive"` 32 | 33 | The result is going to be: 34 | 35 | ``` 36 | "We love coding in ***!\nThis makes us really productive" 37 | ``` 38 | 39 | Another example will be: 40 | 41 | * `words = ["yesterday", "Dog", "food", "walk"]` 42 | * `text = "Yesterday, I took my dog for a walk.\n It was crazy! My dog wanted only food."` 43 | 44 | The result is going to be: 45 | 46 | ``` 47 | ********, I took my *** for a ****.\n It was crazy! My *** wanted only ****." 48 | ``` 49 | -------------------------------------------------------------------------------- /Application/2-A-Simple-Logger/README.md: -------------------------------------------------------------------------------- 1 | # Problem 2 - A simple logger 2 | 3 | In a language of your choice, implement the following things: 4 | 5 | ## The interface 6 | 7 | Make an interface, called `MyLogger` with only 1 method - `log(level, message)` 8 | 9 | The two arguments should be: 10 | 11 | * `level` - an integer, from 1 to 3. 12 | * 1 means that your are logging with `INFO` level. 13 | * 2 means that you are logging with `WARNING` level. 14 | * 3 means that you are logging with `PLSCHECKFFS` level. 15 | * `message` is a string, that you are logging. 16 | 17 | There is a rule of how to make the log message, regardless where you are saving it: 18 | 19 | ``` 20 | {LOG_LEVEL_STRING}::{TIMESTAMP}::{MESSAGE} 21 | ``` 22 | 23 | For example, if we log with `level = 1`, and `message = "Hello World"`, this will produce the following line: 24 | 25 | ``` 26 | INFO::2015-02-02T01:43:19+00:00::Hello World 27 | ``` 28 | 29 | [The timestamp should be in ISO 8901 format.](http://en.wikipedia.org/wiki/ISO_8601) 30 | 31 | Make 3 different classes, that implement the interface `MyLogger`: 32 | 33 | ## `ConsoleLogger` 34 | 35 | The `ConsoleLogger` should log the messages directly to the console. 36 | 37 | ## `FileLogger` 38 | 39 | The `FileLogger` should log the messages to a given file. 40 | 41 | ## `HTTPLogger` 42 | 43 | The `HTTPLogger` shoud log the messages via a POST request to a given HTTP url. 44 | 45 | ## Examples 46 | 47 | For each logger, make sure to include examples - scripts or tests that show how the logging works. 48 | -------------------------------------------------------------------------------- /Application/3-Query-Language-Over-CSV/README.md: -------------------------------------------------------------------------------- 1 | # Problem 3 - A query language over a CSV file 2 | 3 | A CSV files (comma separated values) can be represented as a table. 4 | 5 | For example this CSV file: 6 | 7 | ```csv 8 | id, name, course 9 | 1, Rado, Haskell 10 | 2, Ivo, Python 11 | ``` 12 | 13 | Can be represented by this table: 14 | 15 | | id | name | course | 16 | |----|------|---------| 17 | | 1 | Rado | Haskell | 18 | 19 | 20 | We are going to use CSV files as tables and make a simple query language for fetching data. 21 | 22 | **In a language of your choice, make a program that:** 23 | 24 | * Reads a CSV file. 25 | * Gives the user an interactive input for queries. 26 | * Answers the queries with data from the CSV file. 27 | 28 | The queries, that should be supported are: 29 | 30 | * `SELECT [columns] LIMIT X` - where you can SELECT without giving the LIMIT. Then this will fetch all rows. 31 | * `SUM [column]` - returns the sum of all integers in the given column. 32 | * `SHOW` - returns a list of all column names in your data. 33 | * `FIND X` - returns all rows, that has `X` in some of their cells (Match X with at least one of the columns). **If `X` is a string, search for every string, that contains `X` as a substring.** 34 | 35 | Here are examples of all queries. 36 | 37 | Consider that we have loaded the following CSV: 38 | 39 | ```csv 40 | id,name,hometown 41 | 1,Kiara,Lunel 42 | 2,Mona,Henley-on-Thames 43 | 3,Kiayada,Villers-aux-Tours 44 | 4,Karly,Hillsboro 45 | 5,Igor,Oranienburg 46 | ``` 47 | 48 | Now, lets make queries: 49 | 50 | 51 | ``` 52 | query>SELECT id 53 | |id| 54 | |--| 55 | |1 | 56 | |2 | 57 | |3 | 58 | |4 | 59 | |5 | 60 | ``` 61 | 62 | ``` 63 | query> SELECT id, name 64 | |id| name | 65 | |--|--------| 66 | |1 |Kiara | 67 | |2 |Mona | 68 | |3 |Kiayada | 69 | |4 |Karly | 70 | |5 |Igor | 71 | ``` 72 | 73 | ``` 74 | query> SELECT id, name LIMIT 1 75 | |id| name | 76 | |--|--------| 77 | |1 |Kiara | 78 | ``` 79 | 80 | ``` 81 | query> SUM id 82 | 15 83 | ``` 84 | 85 | ``` 86 | query> SHOW 87 | id, name, hometown 88 | ``` 89 | 90 | ``` 91 | query> FIND "-" 92 | |id| name | hometown | 93 | |--|--------| ------------------| 94 | |2 |Mona | Henley-on-Thames | 95 | |3 |Kiayada | Villers-aux-Tours | 96 | ``` 97 | 98 | ## Features 99 | 100 | * If the query is non-valid - say it. Don't crash the program. 101 | * As you see in the examples, the results should be displayed in visual, tabular way. This is up to you. You don't have to follow the styles of the example. 102 | -------------------------------------------------------------------------------- /Application/README.md: -------------------------------------------------------------------------------- 1 | # Application problems for Frontend JavaScript 2 | 3 | ## Problem 1 - Mask out words 4 | 5 | [Read the description here.](1-Mask-Words) 6 | 7 | ## Problem 2 - A simple Logger 8 | 9 | [Read the description here.](2-A-Simple-Logger) 10 | 11 | ## Problem 3 - Query language over CSV 12 | 13 | [Read the description here.](3-Query-Language-Over-CSV) 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Frontend-JavaScript-2 2 | 3 | The second edition of the Frontend JavaScript course in HackBulgaria. 4 | 5 | ``` 6 | _ _____ _ _ 7 | | | / ____| (_) | | 8 | | | __ _ __ __ __ _ | (___ ___ _ __ _ _ __ | |_ 9 | _ | | / _` |\ \ / // _` | \___ \ / __|| '__|| || '_ \ | __| 10 | | |__| || (_| | \ V /| (_| | ____) || (__ | | | || |_) || |_ 11 | \____/ \__,_| \_/ \__,_||_____/ \___||_| |_|| .__/ \__| 12 | | | 13 | |_| 14 | ``` 15 | 16 | ## Douglas Crockford 17 | 18 | Watch those videos: 19 | 20 | * [JavaScript - The Early Years](https://www.youtube.com/watch?v=JxAXlJEmNMg) 21 | * [And Then There Was JavaScript](https://www.youtube.com/watch?v=RO1Wnu-xKoY) 22 | * [Function the Ultimate](https://www.youtube.com/watch?v=ya4UHuXNygM) 23 | ## Materials for prepartion 24 | * [Metamorphosis of AJAX](https://www.youtube.com/watch?v=Fv9qT9joc0M) 25 | * [The End of All Things](https://www.youtube.com/watch?v=47Ceot8yqeI) 26 | 27 | 28 | We strongly recommend that you check the following materials: 29 | 30 | * Throughout the course we will use the following book - http://eloquentjavascript.net/ 31 | * For the start of the course, **you can read from chapter 1 to chapter 4** - to get a good feeling of the language. 32 | * If you are into it, you can complete the JavaScript track in CodeCademy - http://www.codecademy.com/en/tracks/javascript 33 | 34 | 35 | ## Course Program 36 | 37 | **The program is subject to change and will change until it gets to its final form ;)** 38 | 39 | * JavaScript the good the bad and the weird parts - learning JavaScript as a language. 40 | * The Art and Science of Front-end development 41 | * Building an Interface with jQuery 42 | - DOM manipulation 43 | - event handling 44 | - plugins 45 | 46 | * Bootstrapping the UX 47 | * Sculpting with SASS 48 | * Thinking Async - Closures, Callbacks, Promises 49 | * CRUD Pattern with jQuery and Firebase 50 | * Data manipulation with lodash 51 | * SPAs with Angular Part 1 52 | * SPAs with Angular Part 2 53 | * HTML5 APIs - Canvas & WebSockets. Making games! 54 | * (Bonus) 3D Apps with Three.js 55 | * (Bonus) Diagrams, Charts with d3.js and Raphael.js 56 | 57 | -------------------------------------------------------------------------------- /week1/1-hello-node/README.md: -------------------------------------------------------------------------------- 1 | # Week 1 Task 1: Hello Node 2 | To start off let's create our directory structure and set up a new hello world project. 3 | 4 | ## Starting File Structure 5 | ```sh 6 | # in unix you can create it in 1 line: 7 | $ mkdir -p ~/Projects/frontendjs/week1/hello-node 8 | 9 | # enter the project folder 10 | $ cd ~/Projects/frontendjs/week1/hello-node 11 | 12 | # also creating a file is easy 13 | $ touch server.js 14 | ``` 15 | 16 | The file structure should look something like this: 17 | ``` 18 | . 19 | └── Projects 20 | └── frontendjs 21 | └── week1 22 | └── hello-node 23 | └── server.js 24 | ``` 25 | 26 | ## Inits and Dependencies 27 | 28 | Let's init our configuration file and fetch the modules we need. 29 | 30 | We will use [npm](https://www.npmjs.com/) (Node Package Manager) for that: 31 | 32 | ```sh 33 | # init the project. When prompted leave the defaults 34 | $ npm init 35 | ``` 36 | This will produce *package.json* which holds info about the project and it's dependencies. 37 | 38 | Speaking of which our dependency for this project would be [express](http://www.expressjs.com). 39 | 40 | ``` 41 | # installs the express module to the ./node_modules folder 42 | # --save adds it to the dependencies in package.json 43 | $ npm install --save express 44 | ``` 45 | 46 | ## Hello Express 47 | 48 | Open server.js and add the following code: 49 | 50 | ```javascript 51 | "use strict" 52 | 53 | // require the dependencies 54 | var express = require('express'); 55 | 56 | // declare the app 57 | var app = express(); 58 | 59 | // TODO: configure the app 60 | 61 | // add the routes 62 | app.get('/', function (req, res) { 63 | res.send('Hello World!'); 64 | }) 65 | 66 | // launch the server 67 | var server = app.listen(3000, function () { 68 | 69 | var host = server.address().address; 70 | var port = server.address().port; 71 | 72 | console.log('Example app listening at http://%s:%s', host, port); 73 | 74 | }) 75 | 76 | ``` 77 | 78 | Next run the example with *node* and check the result at http://localhost:3000/ 79 | 80 | ```sh 81 | $ node server.js 82 | ``` 83 | 84 | Nice we have running our first node server. Oh wait wasn't this a front-end course. 85 | 86 | ## Go Public 87 | 88 | With our example above we set up a server that prints out "Hello World" everytime someone opens the base url. In order to be able to serve different *static* files such as htmls, styles, images we eighter need to add another route for each or we can just use the middleware express graciously provided. 89 | 90 | Just add the following code in the "configure the app section": 91 | ```javascript 92 | // configure the app 93 | app.use(express.static('public')); 94 | ``` 95 | 96 | Next let's create the public folder, in it an index.html file. 97 | ```sh 98 | # Look ma, all in 1 line 99 | $ mkdir public; touch public/index.html 100 | ``` 101 | 102 | Also add the hello world message there: 103 | ```html 104 | 105 |

Hello World!

106 | ``` 107 | 108 | And remove or comment the old route in *server.js* so that it doesn't conflict with *index.html* 109 | ```javascript 110 | // add the routes 111 | /*app.get('/', function (req, res) { 112 | res.send('Hello World!'); 113 | })*/ 114 | ``` 115 | 116 | Save, restart and check the browser again. 117 | 118 | Well basically the same message. Shall we spice things up a bit. 119 | 120 | ## Bower up 121 | 122 | If you remember we used [npm](www.npmjs.com) to manage our dependencies for the server. 123 | 124 | Now that we have created a public folder for our front-end needs we need a front-end package manager. 125 | 126 | [Bower](bower.io) is our app. Pretty much works the same way as npm. 127 | 128 | Speaking of npm we will need it to install bower globally so that we can use it in all our projects. 129 | ```sh 130 | # -g stands for gangsta 131 | npm install -g bower 132 | ``` 133 | 134 | Next we need to configure a directory for our front-end libs. Create(touch) the file .bowerrc and add the following content to it: 135 | ```json 136 | { 137 | "directory": "public/lib" 138 | } 139 | ``` 140 | 141 | Next we be initing 142 | ```sh 143 | # same as npm, produces bower.json 144 | $ bower init 145 | ``` 146 | 147 | Alright let's add an awesome lib to mark our first steps in bower. 148 | ```sh 149 | # adds the lib to public/lib and saves the dependency to bower.json 150 | $ bower install --save fontawesome 151 | ``` 152 | 153 | Our directory structure should look something like this: 154 | ``` 155 | . 156 | ├── server.js 157 | ├── package.json 158 | └── node_modules 159 | │   └── express 160 | ├── .bowerrc 161 | ├── bower.json 162 | └── public 163 | ├── index.html 164 | └── lib 165 | └── fontawesome 166 | ``` 167 | 168 | Awesome, [Font Awesome](http://fontawesome.io/) 169 | 170 | Add to top things off add a nice icon to compliment our message: 171 | 172 | ```html 173 | 174 | 175 |

Hello World!

176 | ``` 177 | -------------------------------------------------------------------------------- /week1/2-simple-blog/README.md: -------------------------------------------------------------------------------- 1 | # Week 1 Task 2: Simple Blog 2 | Now let's create something more complex than a hello world page. -------------------------------------------------------------------------------- /week1/3-jade-blog/README.md: -------------------------------------------------------------------------------- 1 | # Week 1 Task 3: Jade Blog 2 | 3 | ## Init Stuff 4 | You know the drill, files, dependencies, server. If not check out the hello node task. 5 | 6 | You should end up with something like this: 7 | ``` 8 | . 9 | ├── server.js 10 | ├── package.json 11 | ├── node_modules 12 | │   └── express 13 | ├── .bowerrc 14 | ├── bower.json 15 | └── public 16 | ├── index.html 17 | └── lib 18 | └── fontawesome 19 | ``` 20 | 21 | ## Jade What 22 | 23 | There are a lot of template engines out there. Most of them are plain old html with additional patterns or masks that indicate where variables should be inserted, conditional statements, loops etc... 24 | 25 | One of the biggest things that differentiate jade is that it goes one step further and get's rid of the unneeded characters and leaves only the most important. 26 | 27 | so this: 28 | ```html 29 |
30 | ``` 31 | translates to this: 32 | ```jade 33 | div.className#idName(attr="1") 34 | ``` 35 | 36 | div's, since we all use them a lot in jade we can ommit them. So this also works: 37 | ```jade 38 | .className#idName(attr="1") 39 | ``` 40 | 41 | You may notice that this jade statemet looks very much like a css selector. 42 | 43 | To top things off you don't need closing tags. The structure is defined, well with a structure. 44 | 45 | Here is a more complex example: 46 | ```jade 47 | .className#idName(attr="1",style="border: 1px;") 48 | span.class1.class2#id1 Some Text Here 49 | p. 50 | Text positioned bellow a tag with a "." at the end 51 | and with additional identation will be inserted as is 52 | no matter on how many lines. Of course this works great for 53 | script(type="text/javascript"). 54 | var javaScript = "Code Here"; 55 | function forReal(){ 56 | alert("I am not kidding!"); 57 | } 58 | ``` 59 | 60 | Compiled will produce the following html: 61 | ```html 62 |
63 | Some Text Here 64 |

65 | Text positioned bellow a tag with a "." at the end 66 | and with additional identation will be inserted as is 67 | no matter on how many lines. Of course this works great for 68 |

69 | 75 |
76 | ``` 77 | 78 | Ok now that we know what jade code looks like and how to translate it to and back from html (if not check the jade lang site. It has a good documentation and examples) 79 | 80 | ## Install Jade 81 | 82 | Since we already inited our project and have installed and set up express we can just 83 | ```sh 84 | npm install --save jade 85 | ``` 86 | 87 | To make express play nice with jade just after 88 | ```javascript 89 | // configure the app 90 | app.use(express.static('public')); 91 | ``` 92 | 93 | add 94 | ```javascript 95 | app.set('view engine', 'jade'); 96 | ``` -------------------------------------------------------------------------------- /week1/README.md: -------------------------------------------------------------------------------- 1 | # Week 1 Start 2 | 3 | ## Prerequisites 4 | 5 | ### Environment 6 | - Node Installed 7 | - For Linux Users - Search Google for: "Digital Ocean install node via nvm" 8 | - Git Installed 9 | - IDE - minimum Sublime 10 | 11 | ### Knowledge 12 | - HTML, CSS (required) 13 | - Basic programming knowledge (recommended) 14 | - Data types, Operations, Arrays, Dictionaries, OOP 15 | 16 | ## Before you start 17 | 18 | - Create a folder for the course, preferably in a Projects folder in your home folder 19 | ```sh 20 | # in linux 21 | $ mkdir -p ~/Projects/frontendjs/week1 22 | $ cd ~/Projects/frontendjs 23 | ``` 24 | 25 | - init a git repo that you will add all your tasks each week 26 | ```sh 27 | # init the repo 28 | $ git init 29 | # create an empty file to be added to the repo 30 | $ touch README.md 31 | # add all 32 | $ git add . 33 | # your first commit 34 | $ git commit -m "initial commit" 35 | ``` 36 | 37 | - create a repo in github and follow the instructions there to link them 38 | 39 | Next up - Hello Node -------------------------------------------------------------------------------- /week1/hangout/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "public/lib" 3 | } -------------------------------------------------------------------------------- /week1/hangout/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | public/lib 3 | npm-debug.log -------------------------------------------------------------------------------- /week1/hangout/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "week1hangout", 3 | "version": "0.0.9", 4 | "authors": [ 5 | "Alex Milanov " 6 | ], 7 | "description": "week 1 final project plus some stuff", 8 | "main": "server.js", 9 | "keywords": [ 10 | "jade", 11 | "blog", 12 | "express" 13 | ], 14 | "license": "MIT", 15 | "ignore": [ 16 | "**/.*", 17 | "node_modules", 18 | "bower_components", 19 | "test", 20 | "tests" 21 | ], 22 | "dependencies": { 23 | "bootstrap": "~3.3.4" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /week1/hangout/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "week1hangout", 3 | "version": "0.0.9", 4 | "description": "week 1 final project plus some stuff", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "keywords": [ 11 | "jade", 12 | "express", 13 | "blog" 14 | ], 15 | "author": "Alex Milanov", 16 | "license": "ISC", 17 | "dependencies": { 18 | "express": "^4.12.3", 19 | "jade": "^1.9.2" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /week1/hangout/posts.json: -------------------------------------------------------------------------------- 1 | { 2 | "post1" : { 3 | "img": "img/post1.png", 4 | "title": "Optimizing Investing, Blood, Hormones, and Life (Podcast Double-Header: #63 and #65)", 5 | "text": "[DISCLAIMER: I’m not a doctor, nor do I play one on the Internet. Speak with a medical professional before doing anything medical-related, m’kay?]There is something here for everyone.This post details two jam-packed discussions — one with world-renowned macro investors and investment strategists (Mark Hart and Raoul Pal), and another with a top performance doc you’ve referenced hundreds of times (Peter Attia, MD).In both, we address dozens of topics." 6 | }, 7 | "post2" : { 8 | "img": "img/post2.jpg", 9 | "title": "The Good, The Bad, and The Ugly of CrossFit", 10 | "text": "This post delves into the good, the bad, and the ugly of all things CrossFit. It answers many important questions" 11 | } 12 | } -------------------------------------------------------------------------------- /week1/hangout/public/css/style.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | background: url("/img/bg.jpg") no-repeat scroll center top #1A1A1A; 4 | color: #333; 5 | font-family: Helvetica,Arial,sans-serif; 6 | font-size: 14px; 7 | } 8 | 9 | a { 10 | text-decoration: none; 11 | color: #144E9C 12 | } 13 | 14 | .container { 15 | margin: 20px auto; 16 | padding: 0px; 17 | width: 900px; 18 | } 19 | 20 | /* header */ 21 | 22 | header { 23 | height: 70px; 24 | } 25 | 26 | header a { 27 | margin:0px; 28 | margin-left: 20px; 29 | height: 60px; 30 | line-height: 60px; 31 | display: block; 32 | padding: 5px; 33 | } 34 | 35 | 36 | /* section */ 37 | 38 | 39 | /* section - content */ 40 | 41 | section { 42 | background: url("/img/content-bg.png") repeat-y scroll 0px 0px #FFF; 43 | box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.35); 44 | padding: 20px; 45 | min-height: 100%; 46 | height: 100%; 47 | } 48 | 49 | section hr { 50 | color: rgba(0, 0, 0, 0.13); 51 | } 52 | 53 | section .content { 54 | float: left; 55 | width: 510px; 56 | } 57 | 58 | 59 | /* post */ 60 | article a.title { 61 | color: #64AF1B; 62 | font-size: 28px; 63 | line-height: 30px; 64 | font-weight: bold; 65 | letter-spacing: -1px; 66 | } 67 | 68 | 69 | /* section - sidebar */ 70 | 71 | .sidebar { 72 | float: right; 73 | width: 300px; 74 | } 75 | 76 | .sidebar .widget-title { 77 | font-weight: bold; 78 | margin: 0px 0px 0.5em; 79 | font-size: 16px; 80 | } 81 | 82 | 83 | .sidebar ul.widget-list { 84 | list-style: none; 85 | background: white; 86 | margin: 0px; 87 | padding: 5px 7px; 88 | border: 2px solid #8CBF1F; 89 | } 90 | 91 | .sidebar ul.li { 92 | font-size: 16px; 93 | } 94 | 95 | -------------------------------------------------------------------------------- /week1/hangout/public/img/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Frontend-JavaScript-2/280c9d0fc8a1cb30c86101f3928afa3c05090df1/week1/hangout/public/img/bg.jpg -------------------------------------------------------------------------------- /week1/hangout/public/img/content-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Frontend-JavaScript-2/280c9d0fc8a1cb30c86101f3928afa3c05090df1/week1/hangout/public/img/content-bg.png -------------------------------------------------------------------------------- /week1/hangout/public/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Frontend-JavaScript-2/280c9d0fc8a1cb30c86101f3928afa3c05090df1/week1/hangout/public/img/logo.png -------------------------------------------------------------------------------- /week1/hangout/public/img/post1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Frontend-JavaScript-2/280c9d0fc8a1cb30c86101f3928afa3c05090df1/week1/hangout/public/img/post1.png -------------------------------------------------------------------------------- /week1/hangout/public/img/post2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Frontend-JavaScript-2/280c9d0fc8a1cb30c86101f3928afa3c05090df1/week1/hangout/public/img/post2.jpg -------------------------------------------------------------------------------- /week1/hangout/server.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | // dependancies 4 | var express = require('express') 5 | var fs = require("fs"); 6 | var posts = require("./posts.json"); 7 | 8 | // create app 9 | var app = express() 10 | 11 | 12 | // configuration and middleware 13 | app.use(express.static('public')); 14 | app.set('view engine', 'jade'); 15 | 16 | 17 | // routes 18 | app.get('/', function (req, res) { 19 | res.render('index', {posts: posts}); 20 | }) 21 | 22 | // listen for files: /post -> /views/post.jade 23 | app.get("/:fileName", function(req, res, next){ 24 | if(req.params && req.params.fileName){ 25 | var fileName = req.params.fileName.replace(".html",""); 26 | 27 | // if jade file exists 28 | if(fs.existsSync(__dirname+"/views/"+fileName+".jade")){ 29 | res.render(fileName); 30 | // if post is in posts 31 | } else if (posts[fileName]) { 32 | res.render("post", {post: posts[fileName]}); 33 | // else continue 34 | } else { 35 | next(); 36 | } 37 | 38 | } else { 39 | next(); 40 | } 41 | }) 42 | 43 | 44 | 45 | // set up server 46 | var server = app.listen(3000, function () { 47 | 48 | var host = server.address().address 49 | var port = server.address().port 50 | 51 | console.log('Example app listening at http://%s:%s', host, port) 52 | 53 | }) -------------------------------------------------------------------------------- /week1/hangout/views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | each post, id in posts 5 | hr 6 | article 7 | a.title(href="/"+id+".html")= post.title 8 | p.text= post.text 9 | -------------------------------------------------------------------------------- /week1/hangout/views/layout.jade: -------------------------------------------------------------------------------- 1 | html 2 | head 3 | title Jade Blog 4 | link(rel="stylesheet",type="text/css",href="css/style.css") 5 | body 6 | .container 7 | header 8 | a(href="/") 9 | img(src="img/logo.png") 10 | section 11 | .sidebar 12 | p.widget-title Category List 13 | ul.widget-list 14 | li: a(href="/") Category1 15 | li: a(href="/") Category2 16 | .content 17 | block content -------------------------------------------------------------------------------- /week1/hangout/views/post.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | article 5 | a.title= post.title 6 | img(src=post.img) 7 | p.text= post.text -------------------------------------------------------------------------------- /week2/1-bulls-n-cows/README.md: -------------------------------------------------------------------------------- 1 | # Week 2 : Task 1 : Bulls & Cows 2 | 3 | Let's recreate the game where you have to guess a four-digit number whereby all digits are unique. 4 | 5 | ## Dependencies 6 | - npm 7 | - prompt 8 | - chalk 9 | 10 | ## Premise 11 | - generate a 4 digit number with unique digits 12 | - 4375 is ok 13 | - 5566 not ok 14 | - prompt user for input 15 | - if guess is successful end game 16 | - if not prompt until successful 17 | 18 | ## Example 19 | ```sh 20 | node bulls-n-cows.js 21 | > 4582 22 | 1 bulls 2 cows 23 | > 4735 24 | 2 bulls 2 cows 25 | > 4375 26 | you guessed it! 27 | ``` -------------------------------------------------------------------------------- /week2/2-console-crud/README.md: -------------------------------------------------------------------------------- 1 | # Week 2 : Task 2 : Console CRUD 2 | 3 | ## Premise 4 | Create an app that allows you to create, read, update, delete users in a collection. 5 | - displays a menu with different CRUD actions 6 | - prompts for an action 7 | - each action either displays a result (List users) or prompts for additional information (Add User) 8 | - the data is stored in a global variable users of type collection (dictionary in array) 9 | ```js 10 | var users = [{ 11 | id: 1, name: "Pesho", email: "pesho@gmail.com" 12 | },{ 13 | id: 2, name: "Gosho", email: "gosho@gmail.com" 14 | }]; 15 | ``` 16 | 17 | ## Steps 18 | 1. Create interface and prompt 19 | 2. Implement list users and add user 20 | 3. Implement get user, edit user and delete user 21 | 4. Implement search users 22 | 5. Implement save, load to json file 23 | 6. Create function that draws the users table 24 | 25 | ## Dependencies 26 | - npm 27 | - prompt 28 | - chalk 29 | 30 | ## Intitial Interface 31 | ```sh 32 | node console-crud.js 33 | 34 | Pick action: 35 | 1. List users 36 | 2. Add user 37 | > action: _ 38 | 39 | # List users 40 | > action: 1 41 | Listing Users: 42 | 1 Ivan ivan@gmail.com 43 | 2 Pesho pesho@gmail.com 44 | 45 | # Add user 46 | > action: 2 47 | Enter user data: 48 | > id: 3 49 | > name: Todor 50 | > email: todor@gmail.com 51 | 52 | ``` 53 | 54 | ## Final Interface 55 | ```sh 56 | node console-crud.js 57 | 58 | |=================| 59 | | Pick action | 60 | |=================| 61 | | 1. List users | 62 | | 2. Add user | 63 | | 3. Get user | 64 | | 4. Edit user | 65 | | 5. Delete user | 66 | | 6. Search users | 67 | | 7. Load users | 68 | | 8. Save users | 69 | |=================| 70 | > action: _ 71 | 72 | # Search users 73 | > action: 6 74 | Search users: 75 | > keyword: sho 76 | Results: 2 77 | |===============================| 78 | | ID | Name | Email | 79 | |===============================| 80 | | 1 | Pesho | pesho@gmail | 81 | | 2 | Gosho | gosho@gmail | 82 | |===============================| 83 | 84 | # 85 | ``` 86 | -------------------------------------------------------------------------------- /week2/README.md: -------------------------------------------------------------------------------- 1 | # Week 2 Data Types & Operations 2 | 3 | ## Basic Syntax 4 | 5 | ### Variables 6 | 7 | As you know, variables are defined with the `var` keyword. 8 | 9 | ```javascript 10 | var a = 5; 11 | var b = 6; 12 | 13 | console.log(a + b); 14 | ``` 15 | 16 | Variables have types but the types are hidden from us since JavaScript is a dynamic language. 17 | 18 | We can have functions, numbers, strings, lists, objects and so on. 19 | 20 | ```javascript 21 | // numbers 22 | var n = 5; 23 | 24 | // strings 25 | var str = "JavaScript is Awesome!"; 26 | 27 | // arrays / lists 28 | var list = [1,2,3]; 29 | 30 | // functions 31 | var f = function(x) { 32 | return x * x; 33 | }; 34 | 35 | // objects - they serve as dictionaries 36 | var courses = { 37 | "FrontendJavaScript" : "https://github.com/HackBulgaria/Frontend-JavaScript-1/", 38 | "Core Java" : "https://github.com/HackBulgaria/Core-Java-1" 39 | }; 40 | ``` 41 | 42 | ### Function Scope for variables 43 | 44 | In languages like C / C++, Java, C#, etc. - we have block scope visibility for variables. 45 | 46 | That means, if we create a new variable inside a for-loop, it won't be visible outside it: 47 | 48 | ```c 49 | for(int i = 0; i < 10; i++) { 50 | // something with i 51 | } 52 | 53 | // i is not visible here 54 | printf("%d", i); 55 | ``` 56 | 57 | __In JavaScript, a variable is visible to the enclosing function!__ 58 | 59 | For example: 60 | 61 | ```javascript 62 | var arr = [1,2,3]; 63 | for(var i = 0; i < arr.length; i++) { 64 | console.log(arr[i]); 65 | } 66 | 67 | // i is visible 68 | // this will print 3 69 | console.log(i); 70 | ``` 71 | 72 | We have something called __variable hoisting__. All `var` statements are moved to the top of the enclosing function, but the value is assigned later, in the original place of the `var` statement 73 | 74 | The code above is translated to: 75 | 76 | ```javascript 77 | var arr = [1,2,3]; 78 | var i; // undefined 79 | 80 | for(i = 0; i < arr.length; i++) { 81 | console.log(arr[i]); 82 | } 83 | 84 | // i is visible 85 | // this will print 3 86 | console.log(i); 87 | ``` 88 | 89 | ### Iterating arrays 90 | 91 | In JavaScript, we have a standard for-loop: 92 | 93 | ```javascript 94 | var 95 | arr = [1,2,3], 96 | i = 0, 97 | n = arr.length; 98 | 99 | for(i; i < n; i++) { 100 | // do something with arr[i] 101 | } 102 | ``` 103 | 104 | But also, we have something far more powerful - a [`forEach` method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) of the array object: 105 | 106 | ```javascript 107 | var arr = [1,2,3]; 108 | 109 | arr.forEach(function(value, index, arr) { 110 | // do something with value 111 | console.log(value); 112 | }); 113 | ``` 114 | 115 | `forEach` takes a function (__we call this callback__) and calls the given function for every item in the array. 116 | 117 | The function is called with 3 arguments: 118 | 119 | * `value` - which is the current item in the array 120 | * `index` - which is the index of the current value in the array 121 | * `array` - the array itself. 122 | 123 | `forEach` is a method. Since everything in JavaScript is a function, we have methods for most of our types. 124 | 125 | ### Iterating objects 126 | 127 | Objects in JavaScript can serve as dictionaries (hash tables) where the key can be string. 128 | __In JavaScript, objects cannot have keys with different types than a string.__ 129 | 130 | A fully functioning hash table is coming in the next version of JavaScript. 131 | 132 | To iterate an object, we can use a `for .. in` loop: 133 | 134 | ```javascript 135 | var courses = { 136 | "FrontendJavaScript" : "https://github.com/HackBulgaria/Frontend-JavaScript-1/", 137 | "Core Java" : "https://github.com/HackBulgaria/Core-Java-1" 138 | }; 139 | 140 | for(var course in courses) { 141 | console.log("A link for " + course + " can be found here - " + courses[course]); 142 | }; 143 | ``` 144 | 145 | Later, we will find out that `for .. in` loop goes one step further and returns object properties that we do not want. 146 | 147 | Another way to iterate is to use the `Object.keys()` method, which returns all keys from a given object. 148 | 149 | For example: 150 | 151 | ```javascript 152 | var courses = { 153 | "FrontendJavaScript" : "https://github.com/HackBulgaria/Frontend-JavaScript-1/", 154 | "Core Java" : "https://github.com/HackBulgaria/Core-Java-1" 155 | }; 156 | 157 | console.log(Object.keys(courses)); // [ 'FrontendJavaScript', 'Core Java' ] 158 | ``` 159 | 160 | Knowing that arrays have `forEach` method, we can do the following: 161 | 162 | ```javascript 163 | var courses = { 164 | "FrontendJavaScript" : "https://github.com/HackBulgaria/Frontend-JavaScript-1/", 165 | "Core Java" : "https://github.com/HackBulgaria/Core-Java-1" 166 | }; 167 | 168 | // we can skip the index and array arguments 169 | Object.keys(courses).forEach(function(value) { 170 | console.log("A link for " + value + " can be found here - " + courses[value]) 171 | }); 172 | ``` 173 | 174 | ## Sorting Arrays 175 | 176 | It is a good idea to read the documentation about sort - https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/sort 177 | 178 | 179 | The default sort sorts the items lexicographically. 180 | 181 | 182 | Check this out: 183 | 184 | ```javascript 185 | var scores = [1, 2, 10, 21]; 186 | scores.sort(); // [1, 10, 2, 21] 187 | ``` 188 | 189 | If you want to sort numbers, you have to give a comparator function: 190 | 191 | ```javascript 192 | var numbers = [4, 2, 5, 1, 3]; 193 | numbers.sort(function(a, b) { 194 | return a - b; 195 | }); 196 | console.log(numbers); 197 | ``` 198 | 199 | ### TODO String Operations, String/Array Conversions -------------------------------------------------------------------------------- /week2/hangout/crud.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | // dependencies 3 | var prompt = require('prompt'); 4 | var jsonfile = require('jsonfile'); 5 | var chalk = require('chalk'); 6 | 7 | // file to store users 8 | var usersFile = 'data/users.json'; 9 | 10 | // searchable fields 11 | var searchableFields = ["name", "email"]; 12 | 13 | // value colors 14 | var valueColors = { 15 | "number" : "blue", 16 | "string" : "yellow" 17 | } 18 | 19 | // collection with users 20 | var users = [ 21 | { 22 | id: 123, 23 | name: "Alex", 24 | email: "alex@i4web.biz" 25 | }, 26 | { 27 | id: 456, 28 | name: "Gosho", 29 | email: "gosho@test.com" 30 | } 31 | ]; 32 | 33 | // collection with commands 34 | var commands = [ 35 | {"menu": "list"}, 36 | {"menu": "add"}, 37 | {"menu": "get"}, 38 | {"menu": "update"}, 39 | {"menu": "remove"}, 40 | {"menu": "load"}, 41 | {"menu": "save"}, 42 | {"menu": "quit"} 43 | ]; 44 | 45 | // util functions 46 | // reset screen 47 | function resetScreen() { 48 | return process.stdout.write('\x1Bc'); 49 | } 50 | 51 | function createMold(length){ 52 | var mold = "|"; 53 | for(var i=1; i<=length; i++){ 54 | mold += " "; 55 | } 56 | mold += "|"; 57 | return mold; 58 | } 59 | 60 | function buildSeparator(lengths){ 61 | var output = ""; 62 | lengths.forEach(function(length){ 63 | for(var i=1; i<=length+2; i++){ 64 | output += "-"; 65 | } 66 | }) 67 | console.log(output); 68 | } 69 | 70 | function buildColumn(value, length, color){ 71 | var mold = createMold(length); 72 | var fmtValue = (color) ? chalk[color](value) : value; 73 | return mold.substr(0,2) + fmtValue + mold.substr(value.length+2); 74 | } 75 | 76 | function buildHeader(item, lengths){ 77 | var output = ""; 78 | Object.keys(item).forEach(function(key, index){ 79 | output += buildColumn(key, lengths[index], "red") 80 | }); 81 | console.log(output); 82 | } 83 | 84 | function buildRow(item, lengths){ 85 | var output = ""; 86 | Object.keys(item).forEach(function(key, index){ 87 | 88 | var value = String(item[key]); 89 | var color = valueColors[typeof(item[key])] || false; 90 | 91 | output += buildColumn(value, lengths[index], color) 92 | }); 93 | console.log(output); 94 | } 95 | 96 | 97 | function calculateLengths(items){ 98 | var maxLengths = []; 99 | items.forEach(function(item, index) { 100 | Object.keys(item).forEach(function(key, keyIndex){ 101 | var value = String(item[key]); 102 | if(!maxLengths[keyIndex] || value.length+2 > maxLengths[keyIndex]){ 103 | maxLengths[keyIndex] = value.length+2; 104 | } 105 | }) 106 | }) 107 | return maxLengths; 108 | } 109 | 110 | // build table 111 | function buildTable(items){ 112 | var lengths = calculateLengths(items); 113 | buildSeparator(lengths) 114 | buildHeader(items[0], lengths); 115 | buildSeparator(lengths) 116 | items.forEach(function(item, index) { 117 | buildRow(item, lengths); 118 | }) 119 | buildSeparator(lengths) 120 | } 121 | 122 | // get functions 123 | // show list 124 | function showList(){ 125 | buildTable(users); 126 | } 127 | 128 | function selectIndexById(arr, id){ 129 | // var to store matched index 130 | var matchIndex = false; 131 | 132 | // select element by id 133 | arr.forEach(function(item, index) { 134 | if(item.id == id){ 135 | matchIndex = index; 136 | } 137 | }); 138 | 139 | return matchIndex; 140 | } 141 | 142 | // get by id 143 | function promptGetById(){ 144 | prompt.get(['id'], function (err, result) { 145 | 146 | var id = result.id; 147 | 148 | // var to store matched index 149 | var matchIndex = selectIndexById(users, id); 150 | 151 | // display it 152 | if(matchIndex !== false){ 153 | buildTable([users[matchIndex]]); 154 | } else { 155 | console.log("Could not find user"); 156 | } 157 | 158 | // prompt for next command 159 | promptMenuCommand(); 160 | 161 | }); 162 | } 163 | 164 | // search 165 | function promptSearch(){ 166 | prompt.get(['keyword'], function(err, result){ 167 | 168 | var keyword = result.keyword; 169 | 170 | var matchedItems = []; 171 | 172 | users.forEach(function(item, index) { 173 | var hasMatch = false; 174 | searchableFields.forEach(function(field){ 175 | if(item[field].match(keyword)){ 176 | hasMatch = true; 177 | } 178 | }) 179 | if(hasMatch){ 180 | matchedItems.push(item); 181 | } 182 | }); 183 | 184 | if(matchedItems.length > 0) 185 | buildTable(matchedItems); 186 | else 187 | console.log("Could not find matches!"); 188 | 189 | promptMenuCommand(); 190 | 191 | }) 192 | } 193 | 194 | // update by id 195 | function promptUpdateById(){ 196 | prompt.get(['id','field','value'], function (err, result) { 197 | 198 | if(!result.id || !result.field || result.value === undefined){ 199 | console.log("id, field, value are required"); 200 | promptUpdateById(); 201 | return false; 202 | } 203 | 204 | var id = result.id; 205 | var field = result.field; 206 | var value = result.value; 207 | 208 | // var to store matched index 209 | var matchIndex = selectIndexById(users, id); 210 | 211 | // update entry 212 | if(matchIndex !== false){ 213 | var oldValue = users[matchIndex][field]; 214 | 215 | users[matchIndex][field] = value; 216 | 217 | console.log("Successfully changed the value of "+field 218 | +" from "+oldValue+" to "+value+" for id "+id); 219 | 220 | console.log(users[matchIndex]); 221 | 222 | } else { 223 | console.log("Could not find user"); 224 | } 225 | 226 | // prompt for next command 227 | promptMenuCommand(); 228 | 229 | }); 230 | } 231 | 232 | // remove 233 | function promptRemoveById(){ 234 | prompt.get(['id'], function (err, result) { 235 | 236 | var id = result.id; 237 | 238 | // var to store matched index 239 | var matchIndex = selectIndexById(users, id); 240 | 241 | // remove it 242 | if(matchIndex !== false){ 243 | users.splice(matchIndex, 1); 244 | console.log("Successfully removed user for id "+id); 245 | 246 | // show updated list 247 | showList(); 248 | 249 | } else { 250 | console.log("Could not find user"); 251 | } 252 | 253 | // prompt for next command 254 | promptMenuCommand(); 255 | 256 | }); 257 | } 258 | 259 | // add item 260 | function promptAddItem(){ 261 | 262 | // 263 | console.log("Enter item to add:"); 264 | 265 | prompt.get(['id','name','email'], function (err, result) { 266 | 267 | users.push(result); 268 | console.log("Successfully added user!"); 269 | 270 | // show updated list 271 | showList(); 272 | 273 | // prompt for next command 274 | promptMenuCommand(); 275 | 276 | }); 277 | } 278 | 279 | function loadFromFile(){ 280 | users = jsonfile.readFileSync(usersFile); 281 | 282 | } 283 | 284 | function saveToFile(){ 285 | jsonfile.writeFileSync(usersFile, users); 286 | } 287 | 288 | 289 | // quit 290 | function quit(){ 291 | console.log("See you soon!"); 292 | } 293 | 294 | // show menu 295 | function showMenu(){ 296 | buildTable(commands); 297 | } 298 | 299 | // prompt menu command 300 | function promptMenuCommand(){ 301 | 302 | console.log("") 303 | showMenu(); 304 | 305 | prompt.get(['command'], function (err, result) { 306 | 307 | resetScreen(); 308 | 309 | if(result === undefined){ 310 | return quit(); 311 | } 312 | 313 | var command = result.command; 314 | 315 | switch(command){ 316 | case "list": 317 | showList(); 318 | promptMenuCommand(); 319 | break; 320 | case "add": 321 | promptAddItem(); 322 | break; 323 | case "get": 324 | promptGetById(); 325 | break; 326 | case "search": 327 | promptSearch(); 328 | break; 329 | case "update": 330 | promptUpdateById(); 331 | break; 332 | case "remove": 333 | promptRemoveById(); 334 | break; 335 | case "load": 336 | loadFromFile(); 337 | promptMenuCommand(); 338 | break; 339 | case "save": 340 | saveToFile(); 341 | promptMenuCommand(); 342 | break; 343 | case "quit": 344 | quit(); 345 | break; 346 | default: 347 | console.log("Unrecognized command!"); 348 | promptMenuCommand(); 349 | break; 350 | } 351 | 352 | }); 353 | 354 | } 355 | 356 | 357 | // code 358 | prompt.start(); 359 | 360 | promptMenuCommand(); 361 | -------------------------------------------------------------------------------- /week2/hangout/data/users.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 123, 4 | "name": "Alex", 5 | "email": "alex@i4web.biz" 6 | }, 7 | { 8 | "id": "567", 9 | "name": "Tosho", 10 | "email": "tosho@test.com" 11 | }, 12 | { 13 | "id": "456", 14 | "name": "Gosho", 15 | "email": "iam@back.com" 16 | } 17 | ] 18 | -------------------------------------------------------------------------------- /week2/hangout/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hangout2crud", 3 | "version": "0.0.0", 4 | "description": "crud operations in console", 5 | "main": "crud.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "crud", 11 | "console", 12 | "prompt" 13 | ], 14 | "author": "", 15 | "license": "ISC", 16 | "dependencies": { 17 | "prompt": "~0.2.14", 18 | "jsonfile": "~2.0.0", 19 | "chalk": "~1.0.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /week3/1-A-Panda/README.md: -------------------------------------------------------------------------------- 1 | # We will model a Panda 2 | 3 | In a file called `panda.js`, create a `Panda` object, which takes two arguments: 4 | 5 | * The `name` of the panda 6 | * The `sex` of the panda - can be `"male"` or `"female"` 7 | * Each `Panda` should have a `weight` property, which always starts at `20` 8 | 9 | Our `Panda` should have the following methods, attached to the `Panda`'s prototype: 10 | 11 | * `toString()` - returns a string representation of our panda. See the examples below for how it should look like 12 | * `isMale()` and `isFemale()` - returns true/false based on the `sex` of the Panda 13 | * `eat(bamboo)` where `bamboo` is an integer - how much kilograms does tha panda eat. When a panda eats `x` kg of `bamboo`, it gains `x/2` weight. If the weight of the panda goes above 80, the name of the panda should be prepended with `"Lazy Panda"`. See examples below. 14 | * `mate(anotherPanda)` - we will have a reproduction mechanism for our pandas. More on mating below. 15 | 16 | ## Basic examples for our Panda 17 | 18 | ```javascript 19 | var ivo = new Panda("Ivo", "male"); 20 | 21 | ivo.weight == 20; // true 22 | ivo.isMale() == true; // true 23 | ivo.isFemale() == false; // true 24 | ivo.toString() == "Ivo is a male panda which weights 20 kg" // true 25 | 26 | ivo.eat(80); 27 | ivo.weight == 60; // true 28 | 29 | ivo.eat(80); 30 | ivo.weight == 100; // true 31 | 32 | ivo.name == "Lazy Panda Ivo" // true 33 | ``` 34 | 35 | ## Examples for Panda Mating 36 | 37 | When we call the `mate(anotherPanda)` method, we should get a new panda with the following criteria: 38 | 39 | * The sex of the new panda should be a 50% chance between male and female. 40 | * **If the panda is male**, the name of the baby panda should be: `"{Name-of-Father} {Name-of-Mother}"` 41 | * Otherwise, it should be the other way around - `"{Name-of-Mother} {Name-of-Father}"` 42 | * If we try to mate male with male or female with female panda, the method should throw an error `CannotMatePandas`. Research how you can throw an error from JavaScript code :) 43 | 44 | Example: 45 | 46 | 47 | ```javascript 48 | var ivan = new Panda("Ivan", "male"); 49 | var ivanka = new Panda("Ivanka", "female"); 50 | 51 | var baby = ivan.mate(ivanka); 52 | 53 | // we can have one of the two options: 54 | 55 | baby.name == "Ivan Ivanka" && baby.sex == "male" 56 | baby.name == "Ivanka Ivan" && baby.sex == "female" 57 | ``` 58 | -------------------------------------------------------------------------------- /week3/1-A-Panda/panda.js: -------------------------------------------------------------------------------- 1 | function getRandomInt(min, max) { 2 | return Math.floor(Math.random() * (max - min)) + min; 3 | } 4 | 5 | function Panda(name, sex) { 6 | this.name = name; 7 | 8 | if(["male", "female"].indexOf(sex) === -1) { 9 | sex = "female"; 10 | } 11 | 12 | this.sex = sex; 13 | this.weight = 20; 14 | this.isLazy = false; 15 | } 16 | 17 | Panda.prototype.isMale = function() { 18 | return this.sex === "male"; 19 | } 20 | 21 | Panda.prototype.isFemale = function() { 22 | return this.sex === "female"; 23 | } 24 | 25 | Panda.prototype.toString = function() { 26 | return [this.name, "is a", this.sex, "panda which weighs", this.weight, "kg"].join(" "); 27 | } 28 | 29 | 30 | Panda.prototype.eat = function(bamboo) { 31 | this.weight += bamboo / 2; 32 | 33 | if(this.weight >= 80 && !this.isLazy) { 34 | this.name = "Lazy Panda " + this.name; 35 | this.isLazy = true; 36 | } 37 | } 38 | 39 | Panda.prototype.mate = function(anotherPanda) { 40 | var fatherName = ""; 41 | var motherName = ""; 42 | 43 | if(this.isMale() && anotherPanda.isFemale()) { 44 | fatherName = this.name; 45 | motherName = anotherPanda.name 46 | } else if(this.isFemale() && anotherPanda.isMale()) { 47 | fatherName = anotherPanda.name; 48 | motherName = this.name; 49 | } else { 50 | throw { 51 | "name": "PandasCannotMate", 52 | "message": "They love each other but sadly cannot mate ;(" 53 | }; 54 | } 55 | 56 | var babySex = ["female", "male"][getRandomInt(0, 2)]; 57 | 58 | var babyName = { 59 | "female": motherName + " " + fatherName, 60 | "male": fatherName + " " + motherName 61 | }[babySex]; 62 | 63 | return new Panda(babyName, babySex); 64 | } 65 | 66 | -------------------------------------------------------------------------------- /week3/2-Point/README.md: -------------------------------------------------------------------------------- 1 | # Point3D 2 | 3 | We are going to model a simple 3D point, holding `x`, `y` and `z`. 4 | 5 | 6 | With a little twist, we will make one mutable and one immutable version of that object. 7 | 8 | ## Mutable point 9 | 10 | We want to have the following things: 11 | 12 | * All the three components - `x`, `y` and `z` should be private with only getters - `getX()`, `getY()`, `getZ()`. 13 | * Our point should have a method, called `move(dx, dy, dz)`, which mutates our point in the following way: `x = x + dx`, `y = y + dy`, `z = z + dz`. 14 | * We want the `toString()` method to return a string looking like `({x}, {y}, {z})` 15 | 16 | For example: 17 | 18 | ```javascript 19 | var p1 = MutablePoint3d(0, 0, 0); 20 | 21 | p1.move(0, 0, -1); 22 | 23 | p1.getX() == 0; // true 24 | p1.getY() == 0; // true 25 | p1.getZ() == -1; // true 26 | 27 | p1.toString() == "(0, 0, -1)" // true 28 | ``` 29 | 30 | ## Immutable Point 31 | 32 | The twist. We want to create a `ImmutablePoint3d` object, which behaves the same as `MutablePoint3d`. 33 | 34 | The only difference is that the `move(dx, dy, dz)` method should return a new `ImmutablePoint3d`, instead of mutating the instance. 35 | 36 | For example: 37 | 38 | ```javascript 39 | var p2 = new ImmutablePoint3d(0, 0, 0); 40 | 41 | var result = p2.move(0, 0, -1); 42 | 43 | p2.getX() == 0; // true 44 | p2.getY() == 0; // true 45 | p2.getZ() == 0; // true 46 | 47 | 48 | result.getZ() == -1; // true 49 | 50 | p2.toString() == "(0, 0, 0)" // true 51 | result.toString() == "(0, 0, -1)" // true 52 | ``` 53 | -------------------------------------------------------------------------------- /week3/2-Point/points.js: -------------------------------------------------------------------------------- 1 | function MutablePoint3d(x, y, z) { 2 | this.getX = function() { 3 | return x; 4 | } 5 | 6 | this.getY = function() { 7 | return y; 8 | } 9 | 10 | this.getZ = function() { 11 | return z; 12 | } 13 | 14 | this.move = function(dx, dy, dz) { 15 | x += dx; 16 | y += dy; 17 | z += dz; 18 | } 19 | } 20 | 21 | MutablePoint3d.prototype.toString = function() { 22 | return "(" + [this.getX(), this.getY(), this.getZ()].join(", ") + ")"; 23 | } 24 | 25 | 26 | function ImmutablePoint3d(x, y, z) { 27 | this.getX = function() { 28 | return x; 29 | } 30 | 31 | this.getY = function() { 32 | return y; 33 | } 34 | 35 | this.getZ = function() { 36 | return z; 37 | } 38 | } 39 | 40 | ImmutablePoint3d.prototype.move = function(dx, dy, dz) { 41 | return new ImmutablePoint3d(this.getX() + dx, this.getY() + dy, this.getZ() + dz); 42 | } 43 | 44 | ImmutablePoint3d.prototype.toString = function() { 45 | return "(" + [this.getX(), this.getY(), this.getZ()].join(", ") + ")"; 46 | } 47 | 48 | var p2 = new ImmutablePoint3d(0, 0, 0); 49 | var result = p2.move(0, 0, -1); 50 | 51 | console.log(p2.toString()); 52 | console.log(result.toString()); 53 | 54 | -------------------------------------------------------------------------------- /week3/3-Prototypes/README.md: -------------------------------------------------------------------------------- 1 | # Augmeting existing object's prototypes 2 | 3 | We are going to play with the prototypes of the existing `String` and `Array` prototypes. 4 | 5 | In a file called `prototypes.js`, add the following methods to: 6 | 7 | ## String 8 | 9 | Extend the `String` prototype by adding the following methods: 10 | 11 | ## capitalize() 12 | 13 | Make the first letter of the string uppercase. 14 | 15 | ```javascript 16 | "javaScript".capitalize() == "JavaScript" 17 | ``` 18 | 19 | ## isBlank() 20 | 21 | Returns `true` if the string is an empty string or containing only empty strings. 22 | 23 | ```javascript 24 | " ".isBlank() == true 25 | " ".isBlank() == true 26 | " asda ".isBlank() == false 27 | ``` 28 | 29 | ## words() 30 | 31 | Splits a string into array of words. 32 | 33 | ```javascript 34 | var words = "This is a very clever sentence!".words() 35 | console.log(words) 36 | // ["This", "is", "a", "very", "clever", "sentence!"] 37 | ``` 38 | 39 | ## format() 40 | 41 | The missing format function in JavaScript. Formats a string with the specified arguments. 42 | 43 | The placeholders should be `"{}"` or named `"{name}"` curly braces. 44 | 45 | Here are some examples: 46 | 47 | ```javascript 48 | var name = "What?"; 49 | var result = "Hi, my name is {}. Nice to meet you {}".format(name, "Good sir!"); 50 | console.log(result); 51 | // "Hi, my name is What?. Nice to meet you Good sir!" 52 | 53 | ``` 54 | 55 | Here is an example with named curly braces: 56 | 57 | ```javascript 58 | var replaces = { "name": "Ivan", "language": "Bulgarian" }; 59 | var result = "Hello there {name}! Do you speak {language}?".format(replaces); 60 | console.log(result); 61 | // "Hello there Ivan! Do you speak Bulgarian?" 62 | ``` 63 | 64 | ## Array 65 | 66 | ### head, tail and last 67 | 68 | * `head` returns the first element of the array. 69 | * `tail` returns the array without the first element. 70 | * `last` returns the last element of the array. 71 | 72 | ```javascript 73 | var a = [1, 2, 3]; 74 | 75 | a.head() == 1; 76 | a.tail() == [2, 3] 77 | a.last() == [3] 78 | 79 | # We do not change the array 80 | a == [1, 2, 3]; 81 | ``` 82 | 83 | ## range 84 | 85 | Range is a method of two arguments - `start` and `end` and returns a new array of all integers between `[start, end]` 86 | 87 | ```javascript 88 | var result = [].range(1, 10); 89 | result == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 90 | ``` 91 | ## sum 92 | 93 | Self explanatory. Returns the sum of all numbers in the array. 94 | 95 | ```javascript 96 | [1, 2, 3].sum() == 6; 97 | ``` 98 | 99 | ## product 100 | 101 | Like sum, but returns the product of all numbers in the array 102 | 103 | ```javascript 104 | [1, 2, 3].product() == 6; 105 | ``` 106 | ## compact 107 | 108 | Returns a new version of the array where all **falsy** values have been removed. 109 | 110 | Here is a list of faulty values in JavaScript: 111 | 112 | * `false` 113 | * `0` (zero) 114 | * `""` (empty string) 115 | * `null` 116 | * `undefined` 117 | * `NaN` (a special Number value meaning Not-a-Number!) 118 | 119 | Example: 120 | 121 | ```javascript 122 | [false, true, 0, "", null, 5, undefined, NaN, "JavaScript"].compact() == [true, 5, "JavaScript"] 123 | ``` 124 | 125 | ## take and drop 126 | 127 | * `take` takes one argument - an integer value `n`, and returns the first `n` elements from the array 128 | * `drop` takes one argument - an integer value `n` and returns a new array, where the first `n` elements are removed 129 | 130 | If something overflows, either return the entire array or an empty one. 131 | 132 | Examples: 133 | 134 | ```javascript 135 | var a = [].range(1, 10); 136 | a.take(3) == [1, 2, 3]; 137 | a.drop(5) == [6, 7, 8, 9, 10]; 138 | a.take(100) == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 139 | a.drop(100) == []; 140 | ``` 141 | 142 | ## dedup 143 | 144 | Returns a new array where all duplicate elements are removed, leaving only one copy of each. 145 | 146 | **Compare elements with `===`** 147 | 148 | ```javascript 149 | [1, 1, 1, 1, 1].dedup() == [1]; 150 | ``` 151 | 152 | ## sample 153 | 154 | Returns a random sample from the array. 155 | 156 | Example: 157 | 158 | ```javascript 159 | [1, 2, 3].sample() // can be 1 160 | [1, 2, 3].sample() // can be 3 161 | [1, 2, 3].sample() // can be 2 162 | ``` 163 | 164 | -------------------------------------------------------------------------------- /week3/3-Prototypes/prototypes.js: -------------------------------------------------------------------------------- 1 | // variadic arguments 2 | String.prototype.format = function(dict) { 3 | var result = this; 4 | 5 | if(typeof(dict) === "object") { 6 | Object.keys(dict).forEach(function(key) { 7 | result = result.replace("{" + key + "}", dict[key]); 8 | }); 9 | return result; 10 | } 11 | 12 | var args = []; 13 | var n = arguments.length; 14 | var i = 0; 15 | 16 | for(i; i < n; i+=1) { 17 | args.push(arguments[i]); 18 | } 19 | 20 | var result = this; 21 | 22 | args.forEach(function(arg) { 23 | result = result.replace("{}", arg); 24 | }); 25 | 26 | return result; 27 | } 28 | 29 | console.log("{} {} {}".format("one", "two", "three")); 30 | console.log("Hello {name}, I am {friend}".format({ 31 | "name": "Ivo", 32 | "friend": "Rado" 33 | })); 34 | 35 | -------------------------------------------------------------------------------- /week3/4-Queue/README.md: -------------------------------------------------------------------------------- 1 | # A classic queue 2 | 3 | In a file called `queue.js` implement the following: 4 | 5 | Using a literal object - `{}` - create an object, that behaves like a queue. 6 | 7 | For example, if we store our queue like this: 8 | 9 | ```javascript 10 | var queue = { 11 | // implementation 12 | } 13 | ``` 14 | 15 | It should have the following methods: 16 | 17 | * `queue.push(item)` - pushes the item to the queue 18 | * `queue.pop()` - returns the item on top of the queue 19 | * `queue.isEmpty()` - returns true if the queue is empty 20 | 21 | Here is an example: 22 | 23 | ```javascript 24 | function bfs(graph, start, end) { 25 | prev = {}; 26 | visited = [start]; 27 | queue.push(start); 28 | 29 | while(!queue.isEmpty()) { 30 | var node = queue.pop(); 31 | 32 | if(node.is(end)) { 33 | // do something 34 | return 35 | } 36 | 37 | graph.getNeighbours(node).forEach(function(n) { 38 | if(visited.indexOf(n) !== -1) { 39 | visited.push(n); 40 | prev[n] = node; 41 | 42 | queue.push(n); 43 | } 44 | }); 45 | 46 | } 47 | } 48 | ``` 49 | -------------------------------------------------------------------------------- /week3/4-Queue/queue.js: -------------------------------------------------------------------------------- 1 | var queue = (function() { 2 | var data = []; 3 | 4 | function pop() { 5 | var head = data.shift(); 6 | return head; 7 | } 8 | 9 | function push(item) { 10 | data.push(item); 11 | return item; 12 | } 13 | 14 | function isEmpty() { 15 | return data.length == 0; 16 | } 17 | 18 | return { 19 | "pop": pop, 20 | "push": push, 21 | "isEmpty": isEmpty 22 | } 23 | })(); 24 | 25 | -------------------------------------------------------------------------------- /week3/5-Event-Bus/README.md: -------------------------------------------------------------------------------- 1 | ## An Event bus 2 | 3 | Create a single object, that behaves like an event bus! We can attach custom events with callbacks to them and also trigger those events. 4 | 5 | The object should have the following __public__ methods: 6 | 7 | * `.on(eventName, callback)` - adds the given `callback` for the given `eventName` 8 | * `.remove(eventName)` - removes all callbacks for the given `eventName` 9 | * `.trigger(eventName)` - fires the given `eventName` which calls all callbacks for that event 10 | 11 | __Everything else, regarding the implementation of the event bus should be private!__ 12 | 13 | Here is an example usage: 14 | 15 | ```javascript 16 | var bus = { // implementation } 17 | 18 | bus.on("PANIC_EVENT", function() { 19 | console.log("PANIC_EVENT HAPPENED!") 20 | }); 21 | 22 | bus.on("PANIC_EVENT", function() { 23 | console.log("PANIC_EVENT HAPPENED AGAIN!"); 24 | }); 25 | ``` 26 | 27 | Now, if we call: 28 | 29 | ```javascript 30 | bus.trigger("PANIC_EVENT"); 31 | ``` 32 | 33 | This will log: 34 | 35 | ``` 36 | "PANIC_EVENT HAPPENED!" 37 | "PANIC_EVENT HAPPENED AGAIN!" 38 | ``` 39 | -------------------------------------------------------------------------------- /week3/5-Event-Bus/bus.js: -------------------------------------------------------------------------------- 1 | // IIFE - Immediately Invoked Function Expression 2 | // design pattern in JavaScript 3 | var bus = (function() { 4 | var eventTable = {}; 5 | 6 | function trigger(event) { 7 | var events = eventTable[event] || []; 8 | 9 | events.forEach(function(callback) { 10 | callback(); 11 | }); 12 | } 13 | 14 | function remove(event) { 15 | delete eventTable[event]; 16 | } 17 | 18 | function on(event, callback) { 19 | if(typeof(eventTable[event]) === "undefined") { 20 | eventTable[event] = []; 21 | } 22 | 23 | eventTable[event].push(callback); 24 | } 25 | 26 | return { 27 | "trigger": trigger, 28 | "on": on, 29 | "remove": remove 30 | } 31 | })(); 32 | 33 | -------------------------------------------------------------------------------- /week3/6-HTML-Generator/README.md: -------------------------------------------------------------------------------- 1 | # A set of classes that generate HTML 2 | 3 | We are going to implement a set of different classes that takes data and renders as HTML. 4 | 5 | We are going to combine that into a `Page` class, which will render the entire HTML. 6 | 7 | * Each class is going to have a common interface of the `render()` method, which returns a string, representing the rendered HTML. 8 | * Some of the classes are going to be containers, having an `addChild()` method - to add different components as their children. 9 | * **Each level of nesting should be indented with 2 spaces!** 10 | 11 | ## The Paragraph class 12 | 13 | Here is an example usage: 14 | 15 | ```javascript 16 | var p = new Paragraph("Some text here"); 17 | p.render() == "

Some text here

"; 18 | ``` 19 | 20 | ## The Div class 21 | 22 | `Div` will be a container and for the case, our only container. 23 | 24 | **The `addChild` method should return `this` in order to have chaining. Check the page example for more info.** 25 | 26 | Here is an example usage: 27 | 28 | ```javascript 29 | var div = new Div(); 30 | div.addChild(new Paragraph("I am inside that div")); 31 | div.addChild(new Paragraph("I am inside that div too")); 32 | 33 | div.render() == 34 | "
35 |

I am inside that div

36 |

I am inside that div too

37 |
" 38 | ``` 39 | 40 | If we render a div with no children: 41 | 42 | ```javascript 43 | var div = new Div(); 44 | div.render() == "
"; 45 | ``` 46 | 47 | We can also nest divs: 48 | 49 | ```javascript 50 | var div1 = new Div(); 51 | var div2 = new Div(); 52 | 53 | div1.addChild(div1); 54 | 55 | div1.render() == 56 | " 57 |
58 |
59 |
60 | " 61 | ``` 62 | 63 | ## The Table class 64 | 65 | Our table class can accept a dictionary, with the following format: 66 | 67 | ```javascript 68 | { 69 | "column_name1": [column values, ...], 70 | "column_name2": [colun values, ...] 71 | } 72 | ``` 73 | 74 | Also, our table class can accept a list, with the following format: 75 | 76 | **Consider the first element the row with the name of the columns.** 77 | 78 | ```javascript 79 | [ ["Column1", "Column2"], 80 | ["data1, "data2"], ... 81 | ] 82 | ``` 83 | 84 | Here are examples: 85 | 86 | ```javascript 87 | var tableData = { 88 | "name": ["Ivo", "Rado", "Maria"], 89 | "age": [22, 24, 22] 90 | } 91 | 92 | 93 | var table = new Table(tableData); 94 | 95 | table.render() == 96 | " 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 |
nameage
Ivo22
Rado24
Maria22
119 | " 120 | ``` 121 | 122 | The same goes with list data: 123 | 124 | ```javascript 125 | var tableData = [ ["name", "age"], ["Ivo", 22], ["Rado", 24], ["Maria", 22] ]; 126 | 127 | var table = new Table(tableData); 128 | table.render() == 129 | " 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 |
nameage
Ivo22
Rado24
Maria22
152 | " 153 | ``` 154 | 155 | ## Rendering everything in a page 156 | 157 | We should have a `Page` class which takes a root element and reners everything from it. 158 | 159 | Here is an example: 160 | 161 | ```javascript 162 | var p = new Paragraph("Rolling in the deep"); 163 | var div = new Div(); 164 | 165 | div 166 | .addChild(new Div()) 167 | .addChild(new Div()) 168 | .addChild(p); 169 | 170 | var page = new Page(div); 171 | 172 | page.render() == 173 | " 174 |
175 |
176 |
177 |

Rolling in the deep

178 |
179 | " 180 | 181 | ``` 182 | -------------------------------------------------------------------------------- /week3/README.md: -------------------------------------------------------------------------------- 1 | # Basic JavaScript OOP & Working with the DOM 2 | 3 | Here are two very good guides for starting with JavaScript OOP from the Mozilla Dev Network: 4 | 5 | * [Introduction to Object Oriented JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript) 6 | * [Details of the Object Model in JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Details_of_the_Object_Model) 7 | * [What is that object prototype in JavaScript?](http://stackoverflow.com/questions/572897/how-does-javascript-prototype-work) 8 | * [The chapter from Eloquent JavaScript](http://eloquentjavascript.net/06_object.html) 9 | * There are good slides from John Resig for "advanced JavaScript" - http://ejohn.org/apps/learn - you can check it! 10 | * [Example for creating object with JavaScript literal](materials/person.js) 11 | * [Examples for Function.prototype.call and Function.prototype.apply](materials/call_apply.js) 12 | * [Examples for closures](materials/closure.js) 13 | -------------------------------------------------------------------------------- /week3/materials/call_apply.js: -------------------------------------------------------------------------------- 1 | function Person(name) { 2 | this.name = name; 3 | } 4 | 5 | Person.prototype.toString = function() { 6 | return "Hello, I am " + this.name; 7 | } 8 | // 1. Да открадна this 9 | // 2. Да открадна prototype 10 | function Student(name, fn) { 11 | Person.call(this, name); 12 | this.fn = fn; 13 | } 14 | 15 | Student.prototype = Object.create(Person.prototype); 16 | Student.prototype.toString = function() { 17 | return Person.prototype.toString.call(this) + " " + this.fn; 18 | } 19 | 20 | var p = new Person("Rado"); 21 | var s = new Student("Ivo", "800000"); 22 | 23 | console.log(p.toString()); 24 | console.log(s.toString()); 25 | 26 | -------------------------------------------------------------------------------- /week3/materials/closure.js: -------------------------------------------------------------------------------- 1 | function makeCounter() { 2 | return (function() { 3 | var count = 0; 4 | return function() { 5 | count += 1; 6 | return count; 7 | } 8 | }()); 9 | } 10 | 11 | var c1 = makeCounter(); 12 | console.log(c1()); 13 | console.log(c1()); 14 | 15 | console.log("Swiching counters"); 16 | 17 | var c2 = makeCounter(); 18 | console.log(c2()); 19 | console.log(c2()); 20 | 21 | // private for object literals 22 | // with closure 23 | var person = 24 | (function() { 25 | var name = "Ivo"; 26 | 27 | return { 28 | "getName": function() { 29 | return name; 30 | } 31 | } 32 | }()); 33 | 34 | console.log(person.getName()); 35 | -------------------------------------------------------------------------------- /week3/materials/person.js: -------------------------------------------------------------------------------- 1 | var person = { 2 | "name": "Ivo", 3 | "age": 24 4 | }; 5 | 6 | 7 | // "closure" -> "обвивка" 8 | 9 | function createPerson(name, age) { 10 | return { 11 | "getName": function() { 12 | return name; 13 | }, 14 | "getAge": function() { 15 | return age; 16 | } 17 | } 18 | 19 | } 20 | 21 | var ivo = createPerson("ivo", 22); 22 | ivo.name = ivo.getName(); 23 | ivo.name = "Rado"; 24 | console.log(ivo); 25 | 26 | -------------------------------------------------------------------------------- /week4/1-Keeping-up-the-Score/README.md: -------------------------------------------------------------------------------- 1 | # Keeping up the Score 2 | 3 | We are going to introduce ourselves to the DOM by creating elements and attaching events to them. 4 | 5 | Check the [page.html](page.html) file - it is a blank HTML with one `div` container. 6 | 7 | We are going to need a `page.js` file to control our code. 8 | 9 | ## Two Buttons 10 | 11 | We are going to need two buttons: 12 | 13 | * One for `TeamA` 14 | * One for `TeamB` 15 | 16 | Also, we are going to need to paragraphs of text, which will hold the score. 17 | 18 | When we click `TeamA`'s button, we are going to increase their score. The same thing goes for `TeamB`. 19 | 20 | See this mockup: 21 | 22 | ![](mockup.png) 23 | -------------------------------------------------------------------------------- /week4/1-Keeping-up-the-Score/mockup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Frontend-JavaScript-2/280c9d0fc8a1cb30c86101f3928afa3c05090df1/week4/1-Keeping-up-the-Score/mockup.png -------------------------------------------------------------------------------- /week4/1-Keeping-up-the-Score/page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /week4/1-Keeping-up-the-Score/page.js: -------------------------------------------------------------------------------- 1 | function text(content) { 2 | return document.createTextNode(content); 3 | } 4 | 5 | function hasKey(dict, key) { 6 | return typeof dict[key] !== "undefined"; 7 | } 8 | 9 | Element.createElement = function(tagName, content, attributes) { 10 | attributes = attributes || {}; 11 | 12 | var obj = document.createElement(tagName); 13 | obj.appendChild(text(content)); 14 | 15 | if(hasKey(attributes, "id")) { 16 | obj.id = attributes["id"]; 17 | } 18 | 19 | return new Element(obj); 20 | }; 21 | 22 | function Element(domElement) { 23 | this.domElement = domElement; 24 | } 25 | 26 | Element.prototype.appendChild = function(child) { 27 | if(child instanceof Element) { 28 | child = child.domElement; 29 | } 30 | 31 | this.domElement.appendChild(child); 32 | }; 33 | 34 | Element.prototype.on = function(type, callback) { 35 | this.domElement.addEventListener(type, callback); 36 | }; 37 | 38 | Element.prototype.attr = function(attributeName, attributeValue) { 39 | var aliases = { 40 | "class": "className" 41 | }; 42 | 43 | if(hasKey(aliases, attributeName)) { 44 | attributeName = aliases[attributeName]; 45 | } 46 | 47 | if(hasKey(this.domElement, attributeName)) { 48 | if (typeof attributeValue === "undefined") { 49 | return this.domElement[attributeName]; 50 | } else { 51 | this.domElement[attributeName] = attributeValue; 52 | return attributeValue; 53 | } 54 | } 55 | }; 56 | 57 | 58 | function print(obj) { 59 | console.log(obj); 60 | } 61 | 62 | document.addEventListener("DOMContentLoaded", function(event) { 63 | function buttonClickHandler(event) { 64 | var id = event.target.id; 65 | scores[id] += 1; 66 | 67 | document.getElementById(id + "Score").firstChild.data = scores[id]; 68 | } 69 | 70 | var scores = { 71 | "teamA": 0, 72 | "teamB": 0 73 | }, 74 | container = document.getElementById("container"); 75 | 76 | var teamAButton = Element.createElement("button", "Team A", { 77 | id: "teamA" 78 | }); 79 | 80 | var teamAScoreText = "Team A Score: "; 81 | var teamAHeading = Element.createElement("h1", teamAScoreText); 82 | 83 | var teamASpan = Element.createElement("span", "0", { 84 | id: "teamAScore" 85 | }); 86 | 87 | teamAHeading.appendChild(teamASpan); 88 | 89 | teamAButton.on("click", function(event) { 90 | console.log("Does it work?"); 91 | console.log(event); 92 | }); 93 | 94 | // var teamBButton = document.createElement("button"); 95 | // teamBButton.id = "teamB"; 96 | // teamBButton.appendChild(text("Team B")); 97 | 98 | // var teamBScoreText = "Team B Score: "; 99 | // var teamBHeading = document.createElement("h1"); 100 | // var teamBSpan = document.createElement("span"); 101 | // teamBSpan.appendChild(text("0")); 102 | // teamBSpan.id = "teamBScore"; 103 | 104 | // teamBHeading.appendChild(text(teamBScoreText)); 105 | // teamBHeading.appendChild(teamBSpan); 106 | 107 | // teamAButton.onclick = buttonClickHandler; 108 | // teamBButton.onclick = buttonClickHandler; 109 | 110 | container.appendChild(teamAHeading.domElement); 111 | container.appendChild(teamAButton.domElement); 112 | 113 | // container.appendChild(teamBHeading); 114 | // container.appendChild(teamBButton); 115 | }); 116 | -------------------------------------------------------------------------------- /week4/2-DOM-Todo/README.md: -------------------------------------------------------------------------------- 1 | # Really dead simple Todo list with DOM 2 | 3 | Every JavaScripter should implement at least one todo list in his life! 4 | 5 | We are going to honor this, right now. 6 | 7 | Again, use the pure DOM + JavaScript API, but this time, write whatever HTML and CSS you like. Think of how you can achieve that. 8 | 9 | We want to have the following feature: 10 | 11 | **A userinput, where we enter text, click the `Add` button and the task goes to a list of tasks.** 12 | 13 | That's it. 14 | 15 | Here is a mockup: 16 | 17 | ![](mockup.png) 18 | 19 | ## How to take a value from an input 20 | 21 | Consider the following HTML: 22 | 23 | ```html 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | ``` 33 | 34 | And the following JavaScript: 35 | 36 | ```javascript 37 | window.onload = function() { 38 | var button = document.getElementById("add-task-button"); 39 | button.onclick = function(event) { 40 | var input = document.getElementById("task-input"); 41 | 42 | console.log(input.value); 43 | }; 44 | }; 45 | ``` 46 | 47 | They are called `example.html` and `example.js`. Run them and check the console, by typing and clickin on the button. 48 | -------------------------------------------------------------------------------- /week4/2-DOM-Todo/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /week4/2-DOM-Todo/example.js: -------------------------------------------------------------------------------- 1 | window.onload = function() { 2 | var button = document.getElementById("add-task-button"); 3 | button.onclick = function(event) { 4 | var input = document.getElementById("task-input"); 5 | 6 | console.log(input.value); 7 | }; 8 | }; 9 | -------------------------------------------------------------------------------- /week4/2-DOM-Todo/mockup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Frontend-JavaScript-2/280c9d0fc8a1cb30c86101f3928afa3c05090df1/week4/2-DOM-Todo/mockup.png -------------------------------------------------------------------------------- /week4/README.md: -------------------------------------------------------------------------------- 1 | # The DOM API 2 | 3 | We are now going to write our JavaScript code in the browser and start manipulating the DOM. 4 | 5 | Here are some essential materials: 6 | 7 | * [Introduction to DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction) 8 | * [Events in the DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Events) 9 | * [More DOM examples](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Examples) 10 | * [Difference between window, document and screen](http://stackoverflow.com/questions/9895202/what-is-the-difference-between-window-screen-and-document-in-javascript) 11 | * [Code examples for DOM API](materials/dom.js) 12 | * [How to listen for DOM ready event](materials/page.js) 13 | 14 | -------------------------------------------------------------------------------- /week4/hangout/README.md: -------------------------------------------------------------------------------- 1 | # Hangout 3 - DOM and jQuery like implementation 2 | 3 | In that hangout we implement a simple class, that behaves like jQuery. 4 | 5 | The main idea is to provide our own DOM API, which is much better than the original. We do this by wrappingthe DOM API with methods of our own. 6 | 7 | [Here is the video from the hangout](https://www.youtube.com/watch?v=AnHwvAO2q0U) 8 | -------------------------------------------------------------------------------- /week4/hangout/dom-wrapper.js: -------------------------------------------------------------------------------- 1 | // s(element) -> element/s 2 | // element.text 3 | // element.appendTo(anotherElement) 4 | // element.on 5 | // element.attr 6 | // element.addClass() 7 | // element.removeClass() 8 | // element.show() 9 | // element.hide() 10 | 11 | // element.forEach 12 | 13 | function hasKey(dict, key) { 14 | return typeof dict[key] !== "undefined"; 15 | } 16 | 17 | 18 | function isArrayLike(object) { 19 | return Array.isArray(object) || 20 | (typeof(object.length) !== "undefined" && typeof(object.item) !== "undefined"); 21 | } 22 | 23 | 24 | function s(selector) { 25 | if(selector instanceof Element) { 26 | return selector; 27 | } else if(typeof(selector) === "string") { 28 | var domObjects = document.querySelectorAll(selector); 29 | 30 | return new Element(domObjects); 31 | } else if(typeof(selector) === "object") { 32 | return new Element(selector); 33 | } else { 34 | throw { 35 | "name": "Selection Error", 36 | "message": "Cannot select: " + selector.toString() 37 | }; 38 | } 39 | } 40 | 41 | function c(tag) { 42 | var domObject = document.createElement(tag); 43 | return new Element(domObject); 44 | } 45 | 46 | function Element(domObjects) { 47 | this.domRepr = function() { 48 | if(isArrayLike(domObjects) && domObjects.length === 1) { 49 | return domObjects[0]; 50 | } 51 | 52 | return domObjects; 53 | }; 54 | } 55 | 56 | Element.prototype.text = function(text) { 57 | var textNode = document.createTextNode(text); 58 | this.domRepr().appendChild(textNode); 59 | 60 | return this; 61 | }; 62 | 63 | Element.prototype.appendTo = function(container) { 64 | container.domRepr().appendChild(this.domRepr()); 65 | return this; 66 | }; 67 | 68 | Element.prototype.on = function(event, callback) { 69 | this.forEach(function(domElement) { 70 | domElement.addEventListener(event, callback); 71 | }); 72 | 73 | return this; 74 | }; 75 | 76 | Element.prototype.addClass = function(newClass) { 77 | this.forEach(function(domElement) { 78 | if(domElement.className === "") { 79 | domElement.className = newClass; 80 | return this; 81 | } 82 | 83 | var classes = domElement.className.split(" "); 84 | classes.push(newClass); 85 | domElement.className = classes.join(" "); 86 | 87 | }); 88 | 89 | return this; 90 | }; 91 | 92 | 93 | Element.prototype.removeClass = function(className) { 94 | var domElement = this.domRepr(); 95 | var classes = domElement.className.split(" "); 96 | 97 | classes = classes.filter(function(currentClass) { 98 | return currentClass !== className; 99 | }); 100 | 101 | domElement.className = classes.join(" "); 102 | 103 | return this; 104 | }; 105 | 106 | Element.prototype.hide = function() { 107 | this.domRepr().style.visibility = "hidden"; 108 | 109 | return this; 110 | }; 111 | 112 | Element.prototype.show = function() { 113 | this.domRepr().style.visibility = ""; 114 | 115 | return this; 116 | }; 117 | 118 | // s("#container").attr("id") == getter 119 | // s("#container").attr("class", "container2") == setter 120 | Element.prototype.attr = function(attributeName, attributeValue) { 121 | var aliases = { 122 | "class": "className" 123 | }; 124 | var domElement = this.domRepr(); 125 | 126 | if(hasKey(aliases, attributeName)) { 127 | attributeName = aliases[attributeName]; 128 | } 129 | 130 | if(hasKey(domElement, attributeName)) { 131 | if (typeof attributeValue === "undefined") { 132 | return domElement[attributeName]; 133 | } else { 134 | domElement[attributeName] = attributeValue; 135 | return this; 136 | } 137 | } 138 | }; 139 | 140 | Element.prototype.forEach = function(f) { 141 | var domElement = this.domRepr(); 142 | 143 | if(!isArrayLike(domElement)) { 144 | domElement = [domElement]; 145 | } 146 | 147 | for(var i = 0; i < domElement.length; i += 1) { 148 | f(domElement[i]); 149 | } 150 | 151 | return this; 152 | }; 153 | 154 | -------------------------------------------------------------------------------- /week4/hangout/index.html: -------------------------------------------------------------------------------- 1 | 2 | jQuery-like example 3 | 4 |

Buy!!!

5 |
6 | 12 |
13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /week4/hangout/interface.js: -------------------------------------------------------------------------------- 1 | // s - query 2 | // do something with elements 3 | 4 | s(document).on("DOMContentLoaded", function() { 5 | console.log("DOM Loaded"); 6 | 7 | var container = s("#container"), 8 | lists = s("#lists"), 9 | hideButton = c("button"), 10 | showButton = c("button"); 11 | 12 | hideButton 13 | .addClass("btn") 14 | .attr("id", "hide-button") 15 | .text("Hide") 16 | .appendTo(container); 17 | 18 | showButton 19 | .addClass("btn") 20 | .attr("id", "show-button") 21 | .text("Show!") 22 | .appendTo(container); 23 | 24 | s("button").on("click", function(event) { 25 | console.log(this); 26 | console.log(s(this).attr("id")); 27 | var id = s(this).attr("id"); 28 | 29 | if(id === "show-button") { 30 | lists.show(); 31 | } else if(id === "hide-button") { 32 | lists.hide(); 33 | } 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /week4/materials/dom.js: -------------------------------------------------------------------------------- 1 | // PART1 - QUERING 2 | // If you want to take an element by tag: 3 | 4 | // ps is an array 5 | var ps = document.getElementsByTagName("p"); 6 | 7 | for(var i = 0; i < ps.length; i += 1) { 8 | var p = ps[i]; 9 | // changing the style of each paragraph 10 | p.style.border = "dashed 1px green"; 11 | } 12 | 13 | // EVENTS 14 | 15 | var button = document.getElementById("main-button"); 16 | button.onclick = function(event) { 17 | console.log("button was clicked"); 18 | }; 19 | 20 | // CREATING ELEMENTS 21 | var button = document.createElement("button"); 22 | var text = document.createTextNode("I am Groot!"); 23 | button.appendChild(text); 24 | 25 | var div = document.getElementById("the-div"); 26 | div.appendChild(button); 27 | -------------------------------------------------------------------------------- /week4/materials/page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello! 4 | 5 | 6 |
7 |

HEADING!

8 | 14 |
15 |
16 |

I am Groot!

17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /week4/materials/page.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", function(event) { 2 | console.log("Hip hip"); 3 | }); 4 | -------------------------------------------------------------------------------- /week5/2-todo-crud/README.md: -------------------------------------------------------------------------------- 1 | # Week 5 : Task 2 : Todo CRUD App 2 | 3 | ## Reference 4 | - http://learn.jquery.com/code-organization/concepts/ 5 | - http://contribute.jquery.org/style-guide/js/ 6 | 7 | 8 | ## Last time 9 | - got the code from week 4 10 | - created and inited a new project 11 | - replaced dom modifications and event handling with jQuery 12 | - added "finish task" logic 13 | 14 | ## Today 15 | - no npm & express 16 | - code refactoring 17 | - CRUD pattern 18 | - Module pattern 19 | 20 | ## New Project Steps 21 | for more info see week1 1-hello-node 22 | 23 | - initial file structure 24 | - create new files and directories 25 | - copy the files with repeating functionality 26 | - inits and dependencies (bower, npm) 27 | - initial interface 28 | - initial functionality 29 | 30 | 31 | ## Directory Structure 32 | 33 | ``` 34 | . 35 | ├── .bowerrc # change public/lib to just lib here 36 | ├── bower.json # generated by bower init 37 | ├── index.html 38 | ├── js 39 | │ ├── app.js # TodoApp = (fucntion(){...})() 40 | │ └── init.js # $(document).ready(...) here 41 | └── lib 42 | └── jquery # bower install --save jquery 43 | ``` 44 | 45 | ## What is a collection 46 | 47 | ```js 48 | var task = { 49 | id: 25, 50 | name: "Do the dishes" 51 | finished: false 52 | } 53 | 54 | var tasks = [ 55 | { 56 | id: 15, 57 | name: "Get some groceries", 58 | finished: true 59 | }, 60 | { 61 | id: 20, 62 | name: "Call grandma", 63 | finished: false 64 | }, 65 | { 66 | id: 25, 67 | name: "Do the dishes" 68 | finished: false 69 | } 70 | ] 71 | ``` 72 | 73 | ## app.js 74 | 75 | http://learn.jquery.com/code-organization/concepts/#the-module-pattern 76 | 77 | ```js 78 | var TodoApp = (function() { 79 | // private vars 80 | var tasks = []; 81 | var index = 0; 82 | 83 | // (optional) store the reference with the jQuery selectors here 84 | var refs = { 85 | addTask: "input#addTask", 86 | container: "#container" 87 | } 88 | 89 | // (optional) interface for setting the 90 | var setSelectorRefs = function(refs){ 91 | 92 | } 93 | 94 | var addTask = function(taskName) { 95 | // add to tasks 96 | }; 97 | 98 | var finishTask = function(id) { 99 | // update task 100 | }; 101 | 102 | var displayList = function() { 103 | // clear the contents 104 | // loop through the tasks 105 | // append each task 106 | }; 107 | 108 | // public api 109 | return { 110 | createTask: addTask, 111 | finishTask: finishTask, 112 | displayList: displayList 113 | }; 114 | })(); 115 | 116 | // access via 117 | TodoApp.addTask("Do the dishes") 118 | 119 | ``` 120 | 121 | ## init.js 122 | 123 | ```js 124 | 'use strict' 125 | 126 | $(document).ready(function(){ 127 | // init stuff 128 | }) 129 | ``` -------------------------------------------------------------------------------- /week5/README.md: -------------------------------------------------------------------------------- 1 | # Week5 : jQuery 2 | 3 | Before we start here is a cheat sheet for jQuery 4 | 5 | ## Selecting elements 6 | 7 | ```js 8 | // dom objects 9 | var domObject = document.getElementById("idHere"); 10 | $( domObject ) 11 | 12 | // select by tagname 13 | $( "div" ) 14 | 15 | // select by class 16 | $( "span.superClass" ) 17 | $( ".anotherClass") 18 | 19 | // select by id 20 | $( "p#ultraId" ) 21 | 22 | // select childs 23 | $( "ul.list > li.list-element") 24 | ``` 25 | 26 | ## Getting and Setting 27 | 28 | ```js 29 | // html, text 30 | $("p.article").html("

Article Title

") 31 | $("textarea[name='summary']").text("Some Text Here") 32 | 33 | // input values 34 | $("input#search").val() 35 | 36 | // attributes 37 | $("img#logo").attr("src","dir.bg/logo.png") 38 | $("a#myLink").attr() 39 | 40 | // classes 41 | $("button#trigger").addClass("active") 42 | $("button#trigger").removeClass("active") 43 | $("button#trigger").toggleClass("active") 44 | ``` 45 | 46 | ## DOM Manipulation 47 | 48 | ```js 49 | // removing and emptying 50 | $("div").remove() 51 | $("div").empty() 52 | 53 | // creating 54 | var input = $(document.createElement("input")); 55 | var item = $("
  • "); 56 | 57 | // appending 58 | $("div#container").append($("")) 59 | $("").appendTo("div.withSpans") 60 | 61 | // prepending 62 | $("p.article").text("Article Text").prepend("

    Article Title

    ") 63 | ``` 64 | 65 | ## Event Handling 66 | 67 | ```js 68 | // on document ready 69 | $( document ).ready(function(){ 70 | // init stuff here 71 | }) 72 | 73 | // clicks 74 | $( "a#trigger" ).click(function(){ 75 | 76 | }) 77 | 78 | // multiple events 79 | $( "a#button" ).on("mouseenter mouseleave", function(){ 80 | 81 | }) 82 | ``` 83 | 84 | ## Naming Suggestions 85 | 86 | ```js 87 | // wtf is done here 88 | $("div#container").append($("").addClass("items-list").append($("
  • List Item
  • ").addClass("list-item"))) 89 | 90 | // better 91 | var divContainer = $("div#container"); 92 | var ulItemsList = $("").addClass("items-list"); 93 | var liListItem = $("
  • List Item
  • ").addClass("list-item") 94 | 95 | ulItemsList.append(liListItem); 96 | divContainer.append(ulItemsList); 97 | 98 | // or at least 99 | var container, itemsList, listItem; 100 | ``` 101 | 102 | ## Conventions, Style Guides etc. 103 | 104 | http://contribute.jquery.org/style-guide/js/ 105 | 106 | https://github.com/airbnb/javascript 107 | 108 | https://github.com/yuanyan/pragmatic-jquery -------------------------------------------------------------------------------- /week5/hangout/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "lib" 3 | } -------------------------------------------------------------------------------- /week5/hangout/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hangout", 3 | "version": "0.0.0", 4 | "homepage": "https://github.com/HackBulgaria/Frontend-JavaScript-2", 5 | "authors": [ 6 | "Alex Milanov " 7 | ], 8 | "description": "todo app", 9 | "main": "index.html", 10 | "license": "MIT", 11 | "ignore": [ 12 | "**/.*", 13 | "node_modules", 14 | "bower_components", 15 | "lib", 16 | "test", 17 | "tests" 18 | ], 19 | "dependencies": { 20 | "jquery": "~2.1.3", 21 | "jqueryui": "~1.11.4", 22 | "fontawesome": "~4.3.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /week5/hangout/css/style.css: -------------------------------------------------------------------------------- 1 | 2 | input#task-name { 3 | width: 600px; 4 | height: 30px; 5 | line-height: 30px; 6 | font-size: 18px; 7 | padding: 5px; 8 | } 9 | 10 | ul#tasks-list { 11 | list-style-type: none; 12 | width: 600px; 13 | padding-left: 0px; 14 | } 15 | 16 | 17 | ul#tasks-list li { 18 | width: 100%; 19 | height: 24px; 20 | line-height: 24px; 21 | font-size: 16px; 22 | } 23 | 24 | input#task-name, ul#tasks-list li { 25 | border: 1px solid #AAA; 26 | margin-top: 5px; 27 | border-radius: 5px; 28 | } 29 | 30 | li.finished { 31 | text-decoration: line-through; 32 | } 33 | 34 | #tasks-list li i.removeTask { 35 | float: right; 36 | height: 24px; 37 | line-height: 24px; 38 | margin-right: 5px; 39 | } 40 | 41 | #tasks-list li i.finishTask { 42 | float: left; 43 | height: 24px; 44 | line-height: 24px; 45 | margin-left: 5px; 46 | } -------------------------------------------------------------------------------- /week5/hangout/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hangout Week5 TODO App 4 | 5 | 6 | 7 | 8 | 9 |
    10 | 11 |
    12 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /week5/hangout/js/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var TodoApp = (function() { 4 | 5 | // private vars 6 | var tasks = []; 7 | var idIndex = 0; 8 | 9 | // adds a new task to the collection 10 | var addTask = function(taskName) { 11 | idIndex++; 12 | 13 | tasks.push({ 14 | name: taskName, 15 | id: idIndex, 16 | finished: false 17 | }) 18 | }; 19 | 20 | // updates the task 21 | var finishTask = function(id) { 22 | tasks.forEach(function(task, index){ 23 | if(task.id === id){ 24 | task.finished = !task.finished; 25 | tasks[index] = task; 26 | } 27 | }) 28 | }; 29 | 30 | var removeTask = function(id) { 31 | tasks = tasks.filter(function(task, index){ 32 | return task.id !== id; 33 | }) 34 | } 35 | 36 | var moveTask = function(fromIndex, toIndex){ 37 | 38 | // get the el and remove it from tasks 39 | var elToMove = tasks.splice(fromIndex, 1)[0]; 40 | 41 | // push it to the new index 42 | tasks.splice(toIndex, 0, elToMove); 43 | 44 | } 45 | 46 | var displayList = function() { 47 | 48 | var tasksList = $("#tasks-list"); 49 | 50 | // clear the contents 51 | tasksList.empty(); 52 | 53 | // loop through the tasks 54 | tasks.forEach(function(task){ 55 | 56 | var taskId = task.id; 57 | 58 | var liTask = $("
  • "); 59 | 60 | // finish task button 61 | var chkFinishTask = $(" "); 62 | chkFinishTask.addClass("finishTask fa fa-square-o"); 63 | chkFinishTask.click(function(){ 64 | finishTask(taskId); 65 | displayList(); 66 | }) 67 | 68 | // remove task button 69 | var chkRemoveTask = $(" "); 70 | chkRemoveTask.addClass("removeTask fa fa-trash"); 71 | chkRemoveTask.click(function(){ 72 | removeTask(taskId); 73 | displayList(); 74 | }) 75 | 76 | if(task.finished === true){ 77 | chkFinishTask.toggleClass("fa-square-o fa-check-square-o"); 78 | liTask.addClass("finished"); 79 | } 80 | 81 | // append contents to li 82 | liTask.append(chkFinishTask); 83 | liTask.append(task.name); 84 | liTask.append(chkRemoveTask); 85 | 86 | // append each task 87 | tasksList.append(liTask); 88 | }) 89 | 90 | // attach event listeners 91 | 92 | }; 93 | 94 | // public api 95 | return { 96 | addTask: addTask, 97 | finishTask: finishTask, 98 | moveTask: moveTask, 99 | displayList: displayList 100 | }; 101 | })(); -------------------------------------------------------------------------------- /week5/hangout/js/init.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | $(document).ready(function(){ 4 | // init stuff 5 | $("ul#tasks-list").sortable({ 6 | start: function(event, ui) { 7 | var fromIndex = ui.item.index(); 8 | ui.item.data('fromIndex', fromIndex); 9 | }, 10 | update: function (event, ui) { 11 | var fromIndex = ui.item.data('fromIndex'); 12 | var toIndex = ui.item.index(); 13 | 14 | TodoApp.moveTask(fromIndex, toIndex); 15 | TodoApp.displayList(); 16 | } 17 | }); 18 | 19 | $("#new-task-form").submit(function(){ 20 | 21 | var taskName = $("#task-name").val(); 22 | $("#task-name").val("") 23 | 24 | TodoApp.addTask(taskName); 25 | TodoApp.displayList(); 26 | 27 | event.preventDefault(); 28 | }) 29 | }) -------------------------------------------------------------------------------- /week6/1-bootstrap-shop/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Premise 3 | 4 | Create the interface of a shop app using bootstrap and jade. 5 | 6 | We will need the following screens: 7 | - index.jade - a catalogue with products 8 | - product.jade - a view product screen 9 | - cart.jade - view with shopping cart 10 | 11 | 12 | 13 | ## Snippets 14 | 15 | ### jade support for express 16 | 17 | ```js 18 | app.set('view engine', 'jade'); 19 | 20 | // routes 21 | app.get('/', function (req, res) { 22 | res.render('index'); 23 | }) 24 | 25 | // listen for files: /post -> /views/post.jade 26 | app.get("/:fileName", function(req, res, next){ 27 | if(req.params && req.params.fileName){ 28 | var fileName = req.params.fileName.replace(".html",""); 29 | 30 | // if jade file exists 31 | if(fs.existsSync(__dirname+"/views/"+fileName+".jade")){ 32 | res.render(fileName); 33 | // if post is in posts 34 | } else { 35 | next(); 36 | } 37 | 38 | } else { 39 | next(); 40 | } 41 | }) 42 | ``` 43 | 44 | ### layout structure 45 | 46 | - index.jade 47 | ```jade 48 | extends layout 49 | 50 | block content 51 | //- put content here 52 | ``` 53 | 54 | - layout.jade 55 | ```jade 56 | ... 57 | //- sidebar 58 | include nav.jade 59 | ... 60 | //- content here 61 | block content 62 | ``` 63 | 64 | ### bootstrap 65 | 66 | ## hello world example 67 | 68 | ```jade 69 | .container 70 | h1.text-success 71 | i.fa.fa-child   72 | | Hello Bootstrap Shop 73 | p.text-warning Some useless text here 74 | a.btn.btn-danger Hit me! 75 | 76 | ``` 77 | 78 | ## grid system basic example 79 | ```jade 80 | .container 81 | .row 82 | .col-xs-12 one huge column 83 | .row 84 | .col-xs-6 two 85 | .col-xs-6 columns 86 | .row 87 | .col-xs-4 one 88 | .col-xs-4 two 89 | .col-xs-4 three 90 | .row 91 | .col-xs-3 uno 92 | .col-xs-3 dos 93 | .col-xs-3 tres 94 | .col-xs-3 quatro 95 | .row 96 | .col-xs-2 a 97 | .col-xs-2 a 98 | .col-xs-2 a 99 | .col-xs-2 a 100 | .col-xs-2 a 101 | .col-xs-2 a 102 | ``` 103 | 104 | ## grid system responsive example 105 | ```jade 106 | .container 107 | .row 108 | .col-md-3.col-sm-4.col-xs-12 item 1 109 | .col-md-3.col-sm-4.col-xs-12 item 2 110 | .col-md-3.col-sm-4.col-xs-12 item 3 111 | .col-md-3.col-sm-4.col-xs-12 item 4 112 | .col-md-3.col-sm-4.col-xs-12 item 5 113 | .col-md-3.col-sm-4.col-xs-12 item 6 114 | .col-md-3.col-sm-4.col-xs-12 item 7 115 | .col-md-3.col-sm-4.col-xs-12 item 8 116 | ``` 117 | 118 | ## navbar header example 119 | ```jade 120 | nav.navbar.navbar-default 121 | .container-fluid 122 | .navbar-header 123 | button.navbar-toggle.collapsed(data-toggle="collapse",data-target="#my-menu") 124 | i.fa.fa-bars 125 | a(href="/").navbar-brand Bootstrap Shop 126 | 127 | .collapse.navbar-collapse#my-menu 128 | ul.nav.navbar-nav.navbar-left 129 | li: a(href="#") 130 | i.fa.fa-star-o   131 | | Promos 132 | ul.nav.navbar-nav.navbar-right 133 | li: a(href="/cart") 134 | i.fa.fa-shopping-cart   135 | | View Cart 136 | ``` -------------------------------------------------------------------------------- /week6/2-hobby-site/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "public/lib" 3 | } 4 | -------------------------------------------------------------------------------- /week6/2-hobby-site/README.md: -------------------------------------------------------------------------------- 1 | # Hobby Site Using Bootstrap & Sass 2 | 3 | ## ULTRA TASK 4 | Recreate 3 site types with 2-3 screens each using bootstrap & SASS 5 | 6 | ## Premise 7 | - Pick a site type that is related to your hobby - f1, fitness, books, world of warcraft 8 | 9 | ## Step 1 Brainstorm 10 | - Brainstorm what type of information will be shared there, also in what form 11 | - Do some research check other sites, possible layouts, images you would use, elements you like etc... 12 | 13 | ## Step 2 Create a mockup 14 | - Create a mockup on a sheet of paper or with a software following this process: 15 | - What would be the basic layout -> header, sidebar, footer 16 | - What would be the structure for each page -> list, table, thumbnails, article ... 17 | - Specify elements -> slider, product info, tabs ... 18 | 19 | ## Step 3 Identify MVP 20 | - Identify MVP -> the minimum amount of info, elements and structure in order to present how the ux will look and feel like the final version 21 | - in a catalogue we only need 2 rows of items 22 | - product page would have picture on the left, parameters on the right and product info below them 23 | 24 | ## Step 4 Project Setup (ref previous projects) 25 | - backend (npm) - express, jade 26 | - frontend (bower) - bootstrap, fontawesome 27 | - styles will be in public/css 28 | 29 | ## Step 5 Create the first screen (index.html) 30 | - include the styles and scripts 31 | - create the layout - header, sidebar ... 32 | - create the content structure - table, grid, thumbnails ... 33 | - create it using bootstrap classes and structure 34 | - header -> .navbar 35 | - sidebar -> .nav.nav-stacked 36 | - grid & structure -> .row and .col-xs-12.col-sm-4.col-md-3 37 | - thumnails -> .thumbnail 38 | - buttons -> .btn.btn-success 39 | 40 | ## Step 6 Separate the layout from the content 41 | 42 | ## Step 7 Create the base interface for the other screens 43 | 44 | ## Step 8 Use SASS to make things look better -------------------------------------------------------------------------------- /week6/2-hobby-site/server.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | // dependancies 4 | var express = require('express') 5 | var fs = require("fs"); 6 | 7 | // create app 8 | var app = express() 9 | 10 | 11 | // configuration and middleware 12 | app.use(express.static('public')); 13 | app.set('view engine', 'jade'); 14 | 15 | 16 | // routes 17 | app.get('/', function (req, res) { 18 | res.render('index'); 19 | }) 20 | 21 | // listen for files: /post -> /views/post.jade 22 | app.get("/:fileName", function(req, res, next){ 23 | if(req.params && req.params.fileName){ 24 | var fileName = req.params.fileName.replace(".html",""); 25 | 26 | // if jade file exists 27 | if(fs.existsSync(__dirname+"/views/"+fileName+".jade")){ 28 | res.render(fileName); 29 | // if post is in posts 30 | } else { 31 | next(); 32 | } 33 | 34 | } else { 35 | next(); 36 | } 37 | }) 38 | 39 | 40 | 41 | // set up server 42 | var server = app.listen(3000, function () { 43 | 44 | var host = server.address().address 45 | var port = server.address().port 46 | 47 | console.log('Example app listening at http://%s:%s', host, port) 48 | 49 | }) -------------------------------------------------------------------------------- /week6/2-hobby-site/views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content -------------------------------------------------------------------------------- /week6/2-hobby-site/views/layout.jade: -------------------------------------------------------------------------------- 1 | html 2 | head 3 | title Title 4 | //- styles here 5 | body 6 | 7 | //- header here 8 | 9 | block content 10 | 11 | 12 | //- scripts here -------------------------------------------------------------------------------- /week6/README.md: -------------------------------------------------------------------------------- 1 | # Week6 : Bootstrap & SASS 2 | 3 | -------------------------------------------------------------------------------- /week6/guides/sass.md: -------------------------------------------------------------------------------- 1 | ## Installation 2 | - http://www.hongkiat.com/blog/getting-started-saas/ - win & mac + basic info 3 | - http://sass-lang.com/install - linux 4 | 5 | 6 | ## Compile SASS to CSS 7 | ```sh 8 | sass css/style.scss:css/style.css --sourcemap 9 | ``` 10 | 11 | ## Basic example 12 | 13 | 14 | 15 | 16 | ## Style Guide 17 | - https://css-tricks.com/sass-style-guide/ 18 | - http://sass-guidelin.es/ -------------------------------------------------------------------------------- /week6/hangout1/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "public/lib" 3 | } 4 | -------------------------------------------------------------------------------- /week6/hangout1/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hangout1", 3 | "version": "0.0.0", 4 | "homepage": "https://github.com/HackBulgaria/Frontend-JavaScript-2", 5 | "authors": [ 6 | "Alex Milanov " 7 | ], 8 | "license": "MIT", 9 | "ignore": [ 10 | "**/.*", 11 | "node_modules", 12 | "bower_components", 13 | "public/lib", 14 | "test", 15 | "tests" 16 | ], 17 | "dependencies": { 18 | "jquery": "~2.1.3", 19 | "fontawesome": "~4.3.0", 20 | "bootstrap": "~3.3.4", 21 | "jqueryui": "~1.11.4" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /week6/hangout1/data/rest.json: -------------------------------------------------------------------------------- 1 | { 2 | "collections" : { 3 | "tasks": { 4 | "model": "Task", 5 | "schema": { 6 | "name" : "String", 7 | "finished" : "Boolean" 8 | } 9 | } 10 | }, 11 | "routes": { 12 | "api" : { 13 | "_meta": { 14 | "virtual": true, 15 | "crud": true, 16 | "contentType": "json" 17 | }, 18 | "tasks": { 19 | } 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /week6/hangout1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hangout1", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "body-parser": "^1.12.3", 14 | "express": "^4.12.3", 15 | "iblokz-node-restify": "git://github.com/iblokz/node-restify", 16 | "jade": "^1.9.2", 17 | "method-override": "^2.3.2", 18 | "mongoose": "^4.0.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /week6/hangout1/public/css/style.css: -------------------------------------------------------------------------------- 1 | 2 | input#task-name { 3 | width: 600px; 4 | height: 30px; 5 | line-height: 30px; 6 | font-size: 18px; 7 | padding: 5px; 8 | } 9 | 10 | ul#tasks-list { 11 | list-style-type: none; 12 | width: 600px; 13 | padding-left: 0px; 14 | } 15 | 16 | 17 | ul#tasks-list li { 18 | width: 100%; 19 | height: 24px; 20 | line-height: 24px; 21 | font-size: 16px; 22 | } 23 | 24 | input#task-name, ul#tasks-list li { 25 | border: 1px solid #AAA; 26 | margin-top: 5px; 27 | border-radius: 5px; 28 | } 29 | 30 | li.finished { 31 | text-decoration: line-through; 32 | } 33 | 34 | #tasks-list li i.removeTask { 35 | float: right; 36 | height: 24px; 37 | line-height: 24px; 38 | margin-right: 5px; 39 | } 40 | 41 | #tasks-list li i.finishTask { 42 | float: left; 43 | height: 24px; 44 | line-height: 24px; 45 | margin-left: 5px; 46 | } -------------------------------------------------------------------------------- /week6/hangout1/public/js/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var TodoApp = (function() { 4 | 5 | var init = function(){ 6 | TaskModel.read(function(tasks){ 7 | TasksView.update(tasks); 8 | }) 9 | } 10 | 11 | // adds a new task to the collection 12 | var addTask = function(taskName) { 13 | TaskModel.create({ 14 | name: taskName, 15 | finished: false 16 | },function(tasks){ 17 | TasksView.update(tasks); 18 | }) 19 | }; 20 | 21 | 22 | // updates the task 23 | var finishTask = function(task) { 24 | 25 | task.finished = !task.finished; 26 | 27 | TaskModel.update(task._id, task,function(tasks){ 28 | TasksView.update(tasks); 29 | }) 30 | }; 31 | 32 | var removeTask = function(task) { 33 | TaskModel.remove(task._id,function(tasks){ 34 | TasksView.update(tasks); 35 | }) 36 | } 37 | /* 38 | var moveTask = function(fromIndex, toIndex){ 39 | 40 | // get the el and remove it from tasks 41 | var elToMove = tasks.splice(fromIndex, 1)[0]; 42 | 43 | // push it to the new index 44 | tasks.splice(toIndex, 0, elToMove); 45 | 46 | } 47 | */ 48 | 49 | return { 50 | addTask: addTask, 51 | finishTask: finishTask, 52 | removeTask: removeTask, 53 | init: init 54 | } 55 | })(); -------------------------------------------------------------------------------- /week6/hangout1/public/js/init.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | $(document).ready(function(){ 4 | // init stuff 5 | /* 6 | $("ul#tasks-list").sortable({ 7 | start: function(event, ui) { 8 | var fromIndex = ui.item.index(); 9 | ui.item.data('fromIndex', fromIndex); 10 | }, 11 | update: function (event, ui) { 12 | var fromIndex = ui.item.data('fromIndex'); 13 | var toIndex = ui.item.index(); 14 | 15 | TodoApp.moveTask(fromIndex, toIndex); 16 | TodoApp.displayList(); 17 | } 18 | }); 19 | */ 20 | 21 | $("#new-task-form").submit(function(){ 22 | 23 | var taskName = $("#task-name").val(); 24 | $("#task-name").val("") 25 | 26 | TodoApp.addTask(taskName); 27 | 28 | event.preventDefault(); 29 | }) 30 | 31 | TodoApp.init(); 32 | 33 | 34 | 35 | }) -------------------------------------------------------------------------------- /week6/hangout1/public/js/taskModel.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | var TaskModel = (function() { 4 | 5 | // private vars 6 | var tasks = []; 7 | 8 | var getTasks = function(){ 9 | return tasks; 10 | } 11 | 12 | var create = function(task, cb){ 13 | 14 | $.ajax({ 15 | url: "/api/tasks/", 16 | data: task, 17 | success: function(){ 18 | read(cb); 19 | }, 20 | method: "POST" 21 | }); 22 | 23 | } 24 | 25 | var read = function(cb){ 26 | // empty the current list 27 | tasks = []; 28 | // add tasks from api 29 | $.get("http://localhost:3000/api/tasks", function(data){ 30 | tasks = data.list; 31 | cb(tasks); 32 | }) 33 | } 34 | 35 | var update = function(id, task, cb){ 36 | $.ajax({ 37 | url: "/api/tasks/"+id, 38 | data: task, 39 | success: function(){ 40 | read(cb) 41 | }, 42 | method: "PUT" 43 | }); 44 | } 45 | 46 | var remove = function(id, cb){ 47 | $.ajax({ 48 | url: "/api/tasks/"+id, 49 | success: function(){ 50 | read(cb) 51 | }, 52 | method: "DELETE" 53 | }); 54 | } 55 | 56 | return { 57 | getTasks: getTasks, 58 | create: create, 59 | read: read, 60 | update: update, 61 | remove: remove 62 | } 63 | })(); 64 | -------------------------------------------------------------------------------- /week6/hangout1/public/js/tasksView.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | 4 | var TasksView = (function() { 5 | 6 | var update = function(tasks) { 7 | 8 | var tasksList = $("#tasks-list"); 9 | 10 | // clear the contents 11 | tasksList.empty(); 12 | 13 | // loop through the tasks 14 | tasks.forEach(function(task){ 15 | 16 | var taskId = task._id; 17 | var _task = task; 18 | 19 | var liTask = $("
  • "); 20 | 21 | // finish task button 22 | var chkFinishTask = $(" "); 23 | chkFinishTask.addClass("finishTask fa fa-square-o"); 24 | chkFinishTask.click(function(){ 25 | TodoApp.finishTask(_task); 26 | }) 27 | 28 | // remove task button 29 | var chkRemoveTask = $(" "); 30 | chkRemoveTask.addClass("removeTask fa fa-trash"); 31 | chkRemoveTask.click(function(){ 32 | TodoApp.removeTask(_task); 33 | }) 34 | 35 | if(task.finished === true){ 36 | chkFinishTask.toggleClass("fa-square-o fa-check-square-o"); 37 | liTask.addClass("finished"); 38 | } 39 | 40 | // append contents to li 41 | liTask.append(chkFinishTask); 42 | liTask.append(task.name); 43 | liTask.append(chkRemoveTask); 44 | 45 | // append each task 46 | tasksList.append(liTask); 47 | }) 48 | 49 | // attach event listeners 50 | 51 | }; 52 | 53 | return { 54 | update: update 55 | }; 56 | 57 | })() -------------------------------------------------------------------------------- /week6/hangout1/server.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | // dependancies 4 | var express = require('express') 5 | var fs = require("fs"); 6 | var mongoose = require("mongoose"); 7 | var bodyParser = require('body-parser'); 8 | var methodOverride = require('method-override'); 9 | 10 | // restify 11 | var restify = require("iblokz-node-restify"); 12 | var restMap = require("./data/rest.json"); 13 | 14 | // create app 15 | var app = express() 16 | 17 | // connect to db 18 | var db = mongoose.connect("mongodb://localhost/todo-app"); 19 | 20 | // load model 21 | restify.loadModel(restMap, db); 22 | 23 | 24 | // configuration and middleware 25 | app.use(express.static('public')); 26 | app.set('view engine', 'jade'); 27 | 28 | app.use(bodyParser.urlencoded({ 29 | extended: true 30 | })); 31 | app.use(bodyParser.json()); 32 | app.use(methodOverride()); 33 | 34 | 35 | // routes 36 | app.get('/', function (req, res) { 37 | res.render('index'); 38 | }) 39 | 40 | // init routes 41 | restify.initRoutes(app,restMap,{},db); 42 | 43 | 44 | // listen for files: /post -> /views/post.jade 45 | /* 46 | app.get("/:fileName", function(req, res, next){ 47 | if(req.params && req.params.fileName){ 48 | var fileName = req.params.fileName.replace(".html",""); 49 | 50 | // if jade file exists 51 | if(fs.existsSync(__dirname+"/views/"+fileName+".jade")){ 52 | res.render(fileName); 53 | // if post is in posts 54 | } else if (posts[fileName]) { 55 | res.render("post"); 56 | // else continue 57 | } else { 58 | next(); 59 | } 60 | 61 | } else { 62 | next(); 63 | } 64 | }) 65 | */ 66 | 67 | 68 | // set up server 69 | var server = app.listen(3000, function () { 70 | 71 | var host = server.address().address 72 | var port = server.address().port 73 | 74 | console.log('Example app listening at http://%s:%s', host, port) 75 | 76 | }) -------------------------------------------------------------------------------- /week6/hangout1/views/index.jade: -------------------------------------------------------------------------------- 1 | html 2 | head 3 | title Week6 REST TODO 4 | link(rel="stylesheet", type="text/css", href="lib/fontawesome/css/font-awesome.css") 5 | link(rel="stylesheet", type="text/css", href="css/style.css") 6 | body 7 | 8 | form#new-task-form 9 | input#task-name(type="text", placeholder="Add new task") 10 | 11 | ul#tasks-list 12 | 13 | //- 3rd party libs 14 | script(type="text/javascript",src="lib/jquery/dist/jquery.min.js") 15 | script(type="text/javascript",src="lib/jqueryui/jquery-ui.min.js") 16 | 17 | //- our app 18 | script(type="text/javascript",src="js/taskModel.js") 19 | script(type="text/javascript",src="js/tasksView.js") 20 | script(type="text/javascript",src="js/app.js") 21 | script(type="text/javascript",src="js/init.js") -------------------------------------------------------------------------------- /week6/hangout1_full/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "public/lib" 3 | } 4 | -------------------------------------------------------------------------------- /week6/hangout1_full/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hangout1", 3 | "version": "0.0.0", 4 | "homepage": "https://github.com/HackBulgaria/Frontend-JavaScript-2", 5 | "authors": [ 6 | "Alex Milanov " 7 | ], 8 | "license": "MIT", 9 | "ignore": [ 10 | "**/.*", 11 | "node_modules", 12 | "bower_components", 13 | "public/lib", 14 | "test", 15 | "tests" 16 | ], 17 | "dependencies": { 18 | "jquery": "~2.1.3", 19 | "fontawesome": "~4.3.0", 20 | "bootstrap": "~3.3.4", 21 | "jqueryui": "~1.11.4" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /week6/hangout1_full/data/rest.json: -------------------------------------------------------------------------------- 1 | { 2 | "collections" : { 3 | "tasks": { 4 | "model": "Task", 5 | "schema": { 6 | "name" : "String", 7 | "finished" : "Boolean" 8 | } 9 | }, 10 | "settings": { 11 | "model": "Setting", 12 | "schema": { 13 | "tasksOrder": ["ObjectId"] 14 | } 15 | } 16 | }, 17 | "routes": { 18 | "api" : { 19 | "_meta": { 20 | "virtual": true, 21 | "crud": true, 22 | "contentType": "json" 23 | }, 24 | "tasks": { 25 | }, 26 | "settings": { 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /week6/hangout1_full/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hangout1", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "body-parser": "^1.12.3", 14 | "express": "^4.12.3", 15 | "iblokz-node-restify": "git://github.com/iblokz/node-restify", 16 | "jade": "^1.9.2", 17 | "method-override": "^2.3.2", 18 | "mongoose": "^4.0.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /week6/hangout1_full/public/css/style.css: -------------------------------------------------------------------------------- 1 | 2 | input#task-name { 3 | width: 600px; 4 | height: 30px; 5 | line-height: 30px; 6 | font-size: 18px; 7 | padding: 5px; 8 | } 9 | 10 | ul#tasks-list { 11 | list-style-type: none; 12 | width: 600px; 13 | padding-left: 0px; 14 | } 15 | 16 | 17 | ul#tasks-list li { 18 | width: 100%; 19 | height: 24px; 20 | line-height: 24px; 21 | font-size: 16px; 22 | } 23 | 24 | input#task-name, ul#tasks-list li { 25 | border: 1px solid #AAA; 26 | margin-top: 5px; 27 | border-radius: 5px; 28 | } 29 | 30 | li.finished { 31 | text-decoration: line-through; 32 | } 33 | 34 | #tasks-list li i.removeTask, #tasks-list li i.editTask{ 35 | float: right; 36 | height: 24px; 37 | line-height: 24px; 38 | margin-right: 5px; 39 | } 40 | 41 | #tasks-list li i.finishTask { 42 | float: left; 43 | height: 24px; 44 | line-height: 24px; 45 | margin-left: 5px; 46 | } -------------------------------------------------------------------------------- /week6/hangout1_full/public/js/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var TodoApp = (function() { 4 | 5 | var tasksOrder = []; 6 | 7 | var updateView = function(tasks){ 8 | var orderedTasks = []; 9 | if(tasksOrder.length > 0){ 10 | tasksOrder.forEach(function(taskId){ 11 | tasks.forEach(function(task){ 12 | if(task._id === taskId){ 13 | orderedTasks.push(task); 14 | } 15 | }) 16 | }) 17 | } else { 18 | orderedTasks = tasks; 19 | } 20 | TaskModel.setTasks(orderedTasks); 21 | TasksView.update(orderedTasks); 22 | } 23 | 24 | var updateOrder = function(cb){ 25 | 26 | if(!cb){ 27 | cb = function(){}; 28 | } 29 | 30 | var tasks = TaskModel.getTasks(); 31 | tasksOrder = []; 32 | tasks.forEach(function(task,index){ 33 | tasksOrder.push(task._id); 34 | }) 35 | $.ajax({ 36 | url: "/api/settings/55380aad1e7726c31b03670b", 37 | data: { 38 | tasksOrder: tasksOrder 39 | }, 40 | success: function(){ 41 | cb(); 42 | }, 43 | method: "PUT" 44 | }); 45 | } 46 | 47 | var init = function(){ 48 | // load the order 49 | $.get("/api/settings/55380aad1e7726c31b03670b",function(settings){ 50 | if(settings) 51 | tasksOrder = settings.tasksOrder || []; 52 | // load the tasks 53 | TaskModel.read(updateView) 54 | }); 55 | } 56 | 57 | // adds a new task to the collection 58 | var saveTask = function(task) { 59 | if(typeof task._id === "undefined") { 60 | TaskModel.create(task, function(tasks){ 61 | updateOrder(function(){ 62 | updateView(tasks); 63 | }) 64 | }) 65 | } else { 66 | TaskModel.update(task._id, task, updateView); 67 | } 68 | }; 69 | 70 | // updates the task 71 | var finishTask = function(task) { 72 | 73 | task.finished = !task.finished; 74 | 75 | TaskModel.update(task._id, task,updateView) 76 | }; 77 | 78 | var removeTask = function(task) { 79 | TaskModel.remove(task._id, function(tasks){ 80 | updateOrder(function(){ 81 | updateView(tasks); 82 | }) 83 | }) 84 | } 85 | 86 | var moveTask = function(fromIndex, toIndex){ 87 | 88 | var tasks = TaskModel.getTasks(); 89 | 90 | // get the el and remove it from tasks 91 | var elToMove = tasks.splice(fromIndex, 1)[0]; 92 | 93 | // push it to the new index 94 | tasks.splice(toIndex, 0, elToMove); 95 | 96 | TaskModel.setTasks(tasks); 97 | 98 | updateOrder(); 99 | 100 | } 101 | 102 | return { 103 | saveTask: saveTask, 104 | finishTask: finishTask, 105 | removeTask: removeTask, 106 | moveTask: moveTask, 107 | init: init 108 | } 109 | })(); -------------------------------------------------------------------------------- /week6/hangout1_full/public/js/init.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | $(document).ready(function(){ 4 | // init stuff 5 | 6 | $("ul#tasks-list").sortable({ 7 | start: function(event, ui) { 8 | var fromIndex = ui.item.index(); 9 | ui.item.data('fromIndex', fromIndex); 10 | }, 11 | update: function (event, ui) { 12 | var fromIndex = ui.item.data('fromIndex'); 13 | var toIndex = ui.item.index(); 14 | TodoApp.moveTask(fromIndex, toIndex); 15 | 16 | } 17 | }); 18 | 19 | 20 | $("#new-task-form").submit(function(){ 21 | 22 | var taskName = $("#task-name").val(); 23 | $("#task-name").val("") 24 | 25 | var task = { 26 | name: taskName 27 | }; 28 | 29 | if($("#task-id").val() !== ""){ 30 | task._id = $("#task-id").val(); 31 | $("#task-id").val(""); 32 | } 33 | 34 | TodoApp.saveTask(task); 35 | 36 | event.preventDefault(); 37 | }) 38 | 39 | TodoApp.init(); 40 | 41 | 42 | 43 | }) -------------------------------------------------------------------------------- /week6/hangout1_full/public/js/taskModel.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | var TaskModel = (function() { 4 | 5 | // private vars 6 | var tasks = []; 7 | 8 | var getTasks = function(){ 9 | return tasks; 10 | } 11 | 12 | var setTasks = function(_tasks){ 13 | tasks = _tasks; 14 | } 15 | 16 | var create = function(task, cb){ 17 | 18 | $.ajax({ 19 | url: "/api/tasks/", 20 | data: task, 21 | success: function(){ 22 | read(cb); 23 | }, 24 | method: "POST" 25 | }); 26 | } 27 | 28 | var read = function(cb){ 29 | // empty the current list 30 | tasks = []; 31 | // add tasks from api 32 | $.get("http://localhost:3000/api/tasks", function(data){ 33 | tasks = data.list; 34 | cb(tasks); 35 | }) 36 | } 37 | 38 | var update = function(id, task, cb){ 39 | $.ajax({ 40 | url: "/api/tasks/"+id, 41 | data: task, 42 | success: function(){ 43 | read(cb) 44 | }, 45 | method: "PUT" 46 | }); 47 | } 48 | 49 | var remove = function(id, cb){ 50 | $.ajax({ 51 | url: "/api/tasks/"+id, 52 | success: function(){ 53 | read(cb) 54 | }, 55 | method: "DELETE" 56 | }); 57 | } 58 | 59 | return { 60 | getTasks: getTasks, 61 | setTasks: setTasks, 62 | create: create, 63 | read: read, 64 | update: update, 65 | remove: remove 66 | } 67 | })(); 68 | -------------------------------------------------------------------------------- /week6/hangout1_full/public/js/tasksView.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | 4 | var TasksView = (function() { 5 | 6 | var update = function(tasks) { 7 | 8 | var tasksList = $("#tasks-list"); 9 | 10 | // clear the contents 11 | tasksList.empty(); 12 | 13 | // loop through the tasks 14 | tasks.forEach(function(task){ 15 | 16 | var taskId = task._id; 17 | var _task = task; 18 | 19 | var liTask = $("
  • "); 20 | 21 | // finish task button 22 | var chkFinishTask = $(" "); 23 | chkFinishTask.addClass("finishTask fa fa-square-o"); 24 | chkFinishTask.click(function(){ 25 | TodoApp.finishTask(_task); 26 | }) 27 | 28 | // remove task button 29 | var chkRemoveTask = $(" "); 30 | chkRemoveTask.addClass("removeTask fa fa-trash"); 31 | chkRemoveTask.click(function(){ 32 | TodoApp.removeTask(_task); 33 | }) 34 | 35 | // remove task button 36 | var chkEditTask = $(" "); 37 | chkEditTask.addClass("removeTask fa fa-pencil"); 38 | chkEditTask.click(function(){ 39 | $("#task-name").val(_task.name); 40 | $("#task-id").val(_task._id) 41 | }) 42 | 43 | if(task.finished === true){ 44 | chkFinishTask.toggleClass("fa-square-o fa-check-square-o"); 45 | liTask.addClass("finished"); 46 | } 47 | 48 | // append contents to li 49 | liTask.append(chkFinishTask); 50 | liTask.append(task.name); 51 | liTask.append(chkRemoveTask); 52 | liTask.append(chkEditTask); 53 | 54 | 55 | // append each task 56 | tasksList.append(liTask); 57 | }) 58 | 59 | // attach event listeners 60 | 61 | }; 62 | 63 | return { 64 | update: update 65 | }; 66 | 67 | })() -------------------------------------------------------------------------------- /week6/hangout1_full/server.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | // dependancies 4 | var express = require('express') 5 | var fs = require("fs"); 6 | var mongoose = require("mongoose"); 7 | var bodyParser = require('body-parser'); 8 | var methodOverride = require('method-override'); 9 | 10 | // restify 11 | var restify = require("iblokz-node-restify"); 12 | var restMap = require("./data/rest.json"); 13 | 14 | // create app 15 | var app = express() 16 | 17 | // connect to db 18 | var db = mongoose.connect("mongodb://localhost/todo-app"); 19 | 20 | // load model 21 | restify.loadModel(restMap, db); 22 | 23 | 24 | // configuration and middleware 25 | app.use(express.static('public')); 26 | app.set('view engine', 'jade'); 27 | 28 | app.use(bodyParser.urlencoded({ 29 | extended: true 30 | })); 31 | app.use(bodyParser.json()); 32 | app.use(methodOverride()); 33 | 34 | 35 | // routes 36 | app.get('/', function (req, res) { 37 | res.render('index'); 38 | }) 39 | 40 | // init routes 41 | restify.initRoutes(app,restMap,{},db); 42 | 43 | 44 | // listen for files: /post -> /views/post.jade 45 | /* 46 | app.get("/:fileName", function(req, res, next){ 47 | if(req.params && req.params.fileName){ 48 | var fileName = req.params.fileName.replace(".html",""); 49 | 50 | // if jade file exists 51 | if(fs.existsSync(__dirname+"/views/"+fileName+".jade")){ 52 | res.render(fileName); 53 | // if post is in posts 54 | } else if (posts[fileName]) { 55 | res.render("post"); 56 | // else continue 57 | } else { 58 | next(); 59 | } 60 | 61 | } else { 62 | next(); 63 | } 64 | }) 65 | */ 66 | 67 | 68 | // set up server 69 | var server = app.listen(3000, function () { 70 | 71 | var host = server.address().address 72 | var port = server.address().port 73 | 74 | console.log('Example app listening at http://%s:%s', host, port) 75 | 76 | }) -------------------------------------------------------------------------------- /week6/hangout1_full/views/index.jade: -------------------------------------------------------------------------------- 1 | html 2 | head 3 | title Week6 REST TODO 4 | link(rel="stylesheet", type="text/css", href="lib/fontawesome/css/font-awesome.css") 5 | link(rel="stylesheet", type="text/css", href="css/style.css") 6 | body 7 | 8 | form#new-task-form 9 | input#task-name(type="text", name="name", placeholder="Add new task") 10 | input#task-id(type="hidden", name="id", value="") 11 | 12 | ul#tasks-list 13 | 14 | //- 3rd party libs 15 | script(type="text/javascript",src="lib/jquery/dist/jquery.min.js") 16 | script(type="text/javascript",src="lib/jqueryui/jquery-ui.min.js") 17 | 18 | //- our app 19 | script(type="text/javascript",src="js/taskModel.js") 20 | script(type="text/javascript",src="js/tasksView.js") 21 | script(type="text/javascript",src="js/app.js") 22 | script(type="text/javascript",src="js/init.js") -------------------------------------------------------------------------------- /week6/hangout2/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "public/lib" 3 | } 4 | -------------------------------------------------------------------------------- /week6/hangout2/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "2-hobby-site", 3 | "version": "0.0.0", 4 | "authors": [ 5 | "Alex Milanov " 6 | ], 7 | "license": "MIT", 8 | "ignore": [ 9 | "**/.*", 10 | "node_modules", 11 | "bower_components", 12 | "public/lib", 13 | "test", 14 | "tests" 15 | ], 16 | "dependencies": { 17 | "bootstrap": "~3.3.4", 18 | "fontawesome": "~4.3.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /week6/hangout2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "2-hobby-site", 3 | "version": "1.0.0", 4 | "description": "## ULTRA TASK Recreate 3 site types with 2-3 screens each using bootstrap & SASS", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.12.3", 14 | "jade": "^1.9.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /week6/hangout2/public/css/inc/_base.scss: -------------------------------------------------------------------------------- 1 | 2 | 3 | body { 4 | background: $funkytown-color-dark; 5 | color: $funkytown-color-light; 6 | font-family: $funkytown-font-text; 7 | > .container-fluid { 8 | margin-top: 70px; 9 | a { 10 | color: $funkytown-color-yellow; 11 | &:hover { 12 | color: $funkytown-color-red; 13 | text-decoration: none; 14 | } 15 | } 16 | } 17 | } 18 | 19 | h1, h2, h3, .navbar-brand, .navbar-nav li { 20 | font-family: $funkytown-font-heading; 21 | } 22 | 23 | h1, h2, h3 { 24 | padding: 0 0 2px; 25 | border-bottom: 2px solid $funkytown-color-red; 26 | margin: 0 0 13px; 27 | } 28 | 29 | -------------------------------------------------------------------------------- /week6/hangout2/public/css/inc/_layout.scss: -------------------------------------------------------------------------------- 1 | 2 | 3 | /* default layout styles */ 4 | nav.navbar { 5 | #my-menu .navbar-nav li a { 6 | &:hover { 7 | background: darken($funkytown-color-light, 40%); 8 | color: lighten($funkytown-color-dark, 10%); 9 | } 10 | } 11 | } 12 | 13 | 14 | /* small screens */ 15 | @media only screen and (max-width : 768px) { 16 | nav.navbar { 17 | background: url(/img/header-bg.jpg) #000 no-repeat center -38px; 18 | .navbar-brand { 19 | height: 52px; 20 | line-height: 52px; 21 | padding: 0px 10px; 22 | } 23 | #my-menu { 24 | background: lighten($funkytown-color-dark, 10%); 25 | } 26 | } 27 | } 28 | 29 | /* big screens */ 30 | @media only screen and (min-width : 768px) { 31 | nav.navbar { 32 | background: url(/img/header-bg.jpg) #000 no-repeat center -25px; 33 | height: 70px; 34 | .container-fluid { 35 | padding: 0px; 36 | } 37 | .navbar-header { 38 | margin-left: 10px; 39 | width: 300px; 40 | } 41 | .navbar-brand { 42 | height: 70px; 43 | line-height: 70px; 44 | font-size: 24px; 45 | margin: 0px; 46 | padding: 0px 20px; 47 | } 48 | #my-menu { 49 | padding: 0px; 50 | .navbar-nav{ 51 | margin-top: -10px; 52 | padding-left: 10px; 53 | background: lighten($funkytown-color-dark, 10%); 54 | width: 100%; 55 | display: block; 56 | height: 40px; 57 | li a { 58 | height: 40px; 59 | line-height: 40px; 60 | font-size: 18px; 61 | margin: 0px; 62 | padding: 0px 10px; 63 | } 64 | } 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /week6/hangout2/public/css/inc/helpers/_bootstrap_mod.scss: -------------------------------------------------------------------------------- 1 | .thumbnail { 2 | background: inherit; 3 | border-radius: 0px; 4 | border: 0px; 5 | padding: 0px; 6 | position: relative; 7 | img { 8 | width: 100%; 9 | } 10 | h4 { 11 | font-family: $funkytown-font-heading; 12 | } 13 | p { 14 | color: $funkytown-color-text; 15 | } 16 | } 17 | 18 | 19 | /* small screens */ 20 | @media only screen and (max-width : 768px) { 21 | .thumbnail { 22 | img { 23 | width: 30%; 24 | display: inline-block; 25 | } 26 | .caption { 27 | width: 70%; 28 | display: inline-block; 29 | vertical-align: top; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /week6/hangout2/public/css/inc/helpers/_vars.scss: -------------------------------------------------------------------------------- 1 | 2 | $funkytown-color-light: #FFF; 3 | $funkytown-color-text: #dacea9; 4 | $funkytown-color-dark: #000; 5 | $funkytown-color-red: #e70000; 6 | $funkytown-color-yellow: #ffc100; 7 | 8 | $funkytown-font-text: Helvetica, Arial, sans-serif; 9 | $funkytown-font-heading: "Audiowide", Helvetica, Arial, sans-serif; 10 | -------------------------------------------------------------------------------- /week6/hangout2/public/css/inc/sections/_events.scss: -------------------------------------------------------------------------------- 1 | #events { 2 | #events-list { 3 | .thumbnail { 4 | .date { 5 | position: absolute; 6 | top: 0px; 7 | left: 0px; 8 | width: 80px; 9 | height: 30px; 10 | line-height: 30px; 11 | font-size: 16px; 12 | text-align: center; 13 | font-weight: bold; 14 | color: white; 15 | background: $funkytown-color-yellow; 16 | 17 | } 18 | &:hover { 19 | .date { 20 | background: $funkytown-color-red; 21 | } 22 | } 23 | cursor: pointer; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /week6/hangout2/public/css/inc/sections/_home.scss: -------------------------------------------------------------------------------- 1 | #home { 2 | #upcomming-events { 3 | .event { 4 | margin: 10px 0px; 5 | img { 6 | width: 100%; 7 | height: 150px; 8 | } 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /week6/hangout2/public/css/style.css: -------------------------------------------------------------------------------- 1 | @import url(http://fonts.googleapis.com/css?family=Audiowide); 2 | .thumbnail { 3 | background: inherit; 4 | border-radius: 0px; 5 | border: 0px; 6 | padding: 0px; 7 | position: relative; } 8 | .thumbnail img { 9 | width: 100%; } 10 | .thumbnail h4 { 11 | font-family: "Audiowide", Helvetica, Arial, sans-serif; } 12 | .thumbnail p { 13 | color: #dacea9; } 14 | 15 | /* small screens */ 16 | @media only screen and (max-width: 768px) { 17 | .thumbnail img { 18 | width: 30%; 19 | display: inline-block; } 20 | .thumbnail .caption { 21 | width: 70%; 22 | display: inline-block; 23 | vertical-align: top; } } 24 | body { 25 | background: #000; 26 | color: #FFF; 27 | font-family: Helvetica, Arial, sans-serif; } 28 | body > .container-fluid { 29 | margin-top: 70px; } 30 | body > .container-fluid a { 31 | color: #ffc100; } 32 | body > .container-fluid a:hover { 33 | color: #e70000; 34 | text-decoration: none; } 35 | 36 | h1, h2, h3, .navbar-brand, .navbar-nav li { 37 | font-family: "Audiowide", Helvetica, Arial, sans-serif; } 38 | 39 | h1, h2, h3 { 40 | padding: 0 0 2px; 41 | border-bottom: 2px solid #e70000; 42 | margin: 0 0 13px; } 43 | 44 | /* default layout styles */ 45 | nav.navbar #my-menu .navbar-nav li a:hover { 46 | background: #999999; 47 | color: #1a1a1a; } 48 | 49 | /* small screens */ 50 | @media only screen and (max-width: 768px) { 51 | nav.navbar { 52 | background: url(/img/header-bg.jpg) #000 no-repeat center -38px; } 53 | nav.navbar .navbar-brand { 54 | height: 52px; 55 | line-height: 52px; 56 | padding: 0px 10px; } 57 | nav.navbar #my-menu { 58 | background: #1a1a1a; } } 59 | /* big screens */ 60 | @media only screen and (min-width: 768px) { 61 | nav.navbar { 62 | background: url(/img/header-bg.jpg) #000 no-repeat center -25px; 63 | height: 70px; } 64 | nav.navbar .container-fluid { 65 | padding: 0px; } 66 | nav.navbar .navbar-header { 67 | margin-left: 10px; 68 | width: 300px; } 69 | nav.navbar .navbar-brand { 70 | height: 70px; 71 | line-height: 70px; 72 | font-size: 24px; 73 | margin: 0px; 74 | padding: 0px 20px; } 75 | nav.navbar #my-menu { 76 | padding: 0px; } 77 | nav.navbar #my-menu .navbar-nav { 78 | margin-top: -10px; 79 | padding-left: 10px; 80 | background: #1a1a1a; 81 | width: 100%; 82 | display: block; 83 | height: 40px; } 84 | nav.navbar #my-menu .navbar-nav li a { 85 | height: 40px; 86 | line-height: 40px; 87 | font-size: 18px; 88 | margin: 0px; 89 | padding: 0px 10px; } } 90 | /* sections */ 91 | #home #upcomming-events .event { 92 | margin: 10px 0px; } 93 | #home #upcomming-events .event img { 94 | width: 100%; 95 | height: 150px; } 96 | 97 | #events #events-list .thumbnail { 98 | cursor: pointer; } 99 | #events #events-list .thumbnail .date { 100 | position: absolute; 101 | top: 0px; 102 | left: 0px; 103 | width: 80px; 104 | height: 30px; 105 | line-height: 30px; 106 | font-size: 16px; 107 | text-align: center; 108 | font-weight: bold; 109 | color: white; 110 | background: #ffc100; } 111 | #events #events-list .thumbnail:hover .date { 112 | background: #e70000; } 113 | 114 | /*# sourceMappingURL=style.css.map */ 115 | -------------------------------------------------------------------------------- /week6/hangout2/public/css/style.scss: -------------------------------------------------------------------------------- 1 | 2 | @import url(http://fonts.googleapis.com/css?family=Audiowide); 3 | 4 | 5 | @import "inc/helpers/vars"; 6 | @import "inc/helpers/bootstrap_mod"; 7 | 8 | @import "inc/base"; 9 | @import "inc/layout"; 10 | 11 | /* sections */ 12 | @import "inc/sections/home"; 13 | @import "inc/sections/events"; 14 | 15 | 16 | -------------------------------------------------------------------------------- /week6/hangout2/public/img/artists/james-brown.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Frontend-JavaScript-2/280c9d0fc8a1cb30c86101f3928afa3c05090df1/week6/hangout2/public/img/artists/james-brown.jpg -------------------------------------------------------------------------------- /week6/hangout2/public/img/artists/jamiroquai.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Frontend-JavaScript-2/280c9d0fc8a1cb30c86101f3928afa3c05090df1/week6/hangout2/public/img/artists/jamiroquai.jpg -------------------------------------------------------------------------------- /week6/hangout2/public/img/artists/tom-jones.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Frontend-JavaScript-2/280c9d0fc8a1cb30c86101f3928afa3c05090df1/week6/hangout2/public/img/artists/tom-jones.jpeg -------------------------------------------------------------------------------- /week6/hangout2/public/img/events/event1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Frontend-JavaScript-2/280c9d0fc8a1cb30c86101f3928afa3c05090df1/week6/hangout2/public/img/events/event1.jpg -------------------------------------------------------------------------------- /week6/hangout2/public/img/events/event2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Frontend-JavaScript-2/280c9d0fc8a1cb30c86101f3928afa3c05090df1/week6/hangout2/public/img/events/event2.jpg -------------------------------------------------------------------------------- /week6/hangout2/public/img/events/event3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Frontend-JavaScript-2/280c9d0fc8a1cb30c86101f3928afa3c05090df1/week6/hangout2/public/img/events/event3.jpg -------------------------------------------------------------------------------- /week6/hangout2/public/img/header-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Frontend-JavaScript-2/280c9d0fc8a1cb30c86101f3928afa3c05090df1/week6/hangout2/public/img/header-bg.jpg -------------------------------------------------------------------------------- /week6/hangout2/server.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | // dependancies 4 | var express = require('express') 5 | var fs = require("fs"); 6 | 7 | // create app 8 | var app = express() 9 | 10 | 11 | // configuration and middleware 12 | app.use(express.static('public')); 13 | app.set('view engine', 'jade'); 14 | 15 | 16 | // routes 17 | app.get('/', function (req, res) { 18 | res.render('index'); 19 | }) 20 | 21 | // listen for files: /post -> /views/post.jade 22 | app.get("/:fileName", function(req, res, next){ 23 | if(req.params && req.params.fileName){ 24 | var fileName = req.params.fileName.replace(".html",""); 25 | 26 | // if jade file exists 27 | if(fs.existsSync(__dirname+"/views/"+fileName+".jade")){ 28 | res.render(fileName); 29 | // if post is in posts 30 | } else { 31 | next(); 32 | } 33 | 34 | } else { 35 | next(); 36 | } 37 | }) 38 | 39 | 40 | 41 | // set up server 42 | var server = app.listen(3000, function () { 43 | 44 | var host = server.address().address 45 | var port = server.address().port 46 | 47 | console.log('Example app listening at http://%s:%s', host, port) 48 | 49 | }) -------------------------------------------------------------------------------- /week6/hangout2/views/artists.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | .container-fluid#artists 5 | h1 Artists 6 | #artists-list 7 | .row 8 | .col-xs-3 9 | img(src="/img/artists/james-brown.jpg",width="100%") 10 | .col-xs-9 11 | h2 James Brown 12 | .row 13 | .col-xs-3 14 | img(src="/img/artists/tom-jones.jpeg",width="100%") 15 | .col-xs-9 16 | h2 Tom Jones 17 | .row 18 | .col-xs-3 19 | img(src="/img/artists/jamiroquai.jpg",width="100%") 20 | .col-xs-9 21 | h2 Jamiroquai -------------------------------------------------------------------------------- /week6/hangout2/views/events.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | .container-fluid#events 5 | h1 Events 6 | .row#events-list 7 | .col-xs-12.col-sm-4.col-md-3.col-lg-2 8 | .thumbnail 9 | span.date 9 JAN 10 | img(src="img/events/event1.jpg") 11 | .caption 12 | h4: a(href="#") JazzFM Funky Sensation 13 | p We know the release date of the brand new album of Riverside - Shrine of New Generation Slaves 14 | .col-xs-12.col-sm-4.col-md-3.col-lg-2 15 | .thumbnail 16 | span.date 9 JAN 17 | img(src="img/events/event2.jpg") 18 | .caption 19 | h4: a(href="#") JazzFM Funky Sensation 20 | p We know the release date of the brand new album of Riverside - Shrine of New Generation Slaves 21 | .col-xs-12.col-sm-4.col-md-3.col-lg-2 22 | .thumbnail 23 | span.date 9 JAN 24 | img(src="img/events/event3.jpg") 25 | .caption 26 | h4: a(href="#") JazzFM Funky Sensation 27 | p We know the release date of the brand new album of Riverside - Shrine of New Generation Slaves 28 | .col-xs-12.col-sm-4.col-md-3.col-lg-2 29 | .thumbnail 30 | span.date 9 JAN 31 | img(src="img/events/event1.jpg") 32 | .caption 33 | h4: a(href="#") JazzFM Funky Sensation 34 | p We know the release date of the brand new album of Riverside - Shrine of New Generation Slaves 35 | .col-xs-12.col-sm-4.col-md-3.col-lg-2 36 | .thumbnail 37 | span.date 9 JAN 38 | img(src="img/events/event1.jpg") 39 | .caption 40 | h4: a(href="#") JazzFM Funky Sensation 41 | p We know the release date of the brand new album of Riverside - Shrine of New Generation Slaves 42 | .col-xs-12.col-sm-4.col-md-3.col-lg-2 43 | .thumbnail 44 | span.date 9 JAN 45 | img(src="img/events/event2.jpg") 46 | .caption 47 | h4: a(href="#") JazzFM Funky Sensation 48 | p We know the release date of the brand new album of Riverside - Shrine of New Generation Slaves 49 | .col-xs-12.col-sm-4.col-md-3.col-lg-2 50 | .thumbnail 51 | span.date 9 JAN 52 | img(src="img/events/event3.jpg") 53 | .caption 54 | h4: a(href="#") JazzFM Funky Sensation 55 | p We know the release date of the brand new album of Riverside - Shrine of New Generation Slaves 56 | .col-xs-12.col-sm-4.col-md-3.col-lg-2 57 | .thumbnail 58 | span.date 9 JAN 59 | img(src="img/events/event1.jpg") 60 | .caption 61 | h4: a(href="#") JazzFM Funky Sensation 62 | p We know the release date of the brand new album of Riverside - Shrine of New Generation Slaves 63 | 64 | 65 | block scripts 66 | script. 67 | function reziseThumbs(){ 68 | $(".thumbnail > img").each(function(){ 69 | $(this).height($(this).width()/4*5); 70 | }) 71 | } 72 | 73 | $(document).ready(function(){ 74 | reziseThumbs(); 75 | $(window).resize(function(){ 76 | reziseThumbs(); 77 | }); 78 | }) -------------------------------------------------------------------------------- /week6/hangout2/views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | .container-fluid#home 5 | 6 | .row 7 | .col-md-6.col-lg-8 8 | h2 Welcome to FunkyTOWN Ibiza 9 | .carousel.slide#funkytown-slider(data-ride="carousel") 10 | ol.carousel-indicators 11 | li.active(data-target="#funkytown-slider",data-slide-to="0") 12 | li.active(data-target="#funkytown-slider",data-slide-to="1") 13 | li.active(data-target="#funkytown-slider",data-slide-to="2") 14 | .carousel-inner(role="listbox") 15 | .item.active 16 | img(data-src="holder.js/1600x700/auto/#777:#555/text:First slide" alt="First slide") 17 | .item 18 | img(data-src="holder.js/1600x700/auto/#777:#555/text:Second slide" alt="Second slide") 19 | .item 20 | img(data-src="holder.js/1600x700/auto/#777:#555/text:Third slide" alt="Third slide") 21 | a.left.carousel-control(href="#funkytown-slider",data-slide="prev") 22 | span.glyphicon.glyphicon-chevron-left(aria-hidden="true") 23 | span.sr-only Previous 24 | a.right.carousel-control(href="#funkytown-slider",data-slide="next") 25 | span.glyphicon.glyphicon-chevron-right(aria-hidden="true") 26 | span.sr-only Next 27 | .col-md-6.col-lg-4#upcomming-events 28 | h2 Upcomming Events 29 | .row.event 30 | .col-xs-4 31 | img(src="/img/events/event1.jpg") 32 | .col-xs-8 33 | h4: a(href="#") JazzFM Funky Sensation 34 | p Ultra cool event 35 | .row.event 36 | .col-xs-4 37 | img(src="/img/events/event2.jpg") 38 | .col-xs-8 39 | h4: a(href="#") Funky House & R'N'B 40 | p Ultra cool event 41 | .row.event 42 | .col-xs-4 43 | img(src="/img/events/event3.jpg") 44 | .col-xs-8 45 | h4: a(href="#") Funky Smugglers 46 | p Ultra cool event 47 | 48 | 49 | block scripts 50 | script(src="http://getbootstrap.com/assets/js/docs.min.js") 51 | 52 | -------------------------------------------------------------------------------- /week6/hangout2/views/layout.jade: -------------------------------------------------------------------------------- 1 | html 2 | head 3 | title FunkyTOWN Ibiza 4 | link(rel="stylesheet",type="text/css",href="/lib/bootstrap/dist/css/bootstrap.css") 5 | link(rel="stylesheet",type="text/css",href="/lib/fontawesome/css/font-awesome.css") 6 | link(rel="stylesheet",type="text/css",href="/css/style.css") 7 | body 8 | 9 | nav.navbar.navbar-inverse 10 | .container-fluid 11 | .navbar-header 12 | button.navbar-toggle.collapsed(data-toggle="collapse",data-target="#my-menu") 13 | i.fa.fa-bars 14 | a(href="/").navbar-brand FunkyTOWN Ibiza 15 | 16 | .collapse.navbar-collapse#my-menu 17 | ul.nav.navbar-nav.navbar-left 18 | li: a(href="/events") Events 19 | li: a(href="/artists") Artists 20 | 21 | block content 22 | 23 | 24 | 25 | //- scripts 26 | script(src="/lib/jquery/dist/jquery.min.js") 27 | script(src="/lib/bootstrap/dist/js/bootstrap.js") 28 | 29 | block scripts 30 | -------------------------------------------------------------------------------- /week7/1-students-crud/README.md: -------------------------------------------------------------------------------- 1 | # Week 7 : Task 1 Students CRUD 2 | 3 | ## Premise 4 | Using what we've learned so far we are going to create a full-stack app. 5 | We will 6 | - create a server that provides a RESTful api following this tutorial [Build RESTful APIs with Node and Express](http://alexmilanov.com/restful-apis-node-express) 7 | - modify it to use a collection instead of the structure used in the tutorial. 8 | ```js 9 | var students = [{ 10 | id: 1, 11 | name: "Pesho", 12 | email: "pesho@abv.bg" 13 | }] 14 | ``` 15 | - The endpoints should be at /api/students instead of /users 16 | - test the API with the chrome extension [Advanced REST Client](https://chrome.google.com/webstore/detail/advanced-rest-client/hgmloofddffdnphfgcellkdfbfbjeloo) 17 | - Finally build a front-end client using what we've learned with jQuery 18 | 19 | 20 | ## Cheat sheets 21 | 22 | ### Get index by id 23 | ```js 24 | function getIndexById(collection, id){ 25 | var docIndex = -1; 26 | collection.forEach(function(item, index){ 27 | if(item["id"] === id){ 28 | docIndex = index; 29 | } 30 | }) 31 | return docIndex; 32 | } 33 | ``` 34 | 35 | ### jQuery API Calls 36 | ```js 37 | // do get request with $.get 38 | $.get("/api/students/",function(students){ 39 | 40 | }) 41 | // post, put & delete via 42 | $.ajax({ 43 | url: "/api/students/1", // api endpoint 44 | method: "put", // method 45 | data: { // data to send 46 | name: "Pesho", 47 | }, 48 | dataType: "json" // data type 49 | }).done(function(result){ 50 | 51 | }) -------------------------------------------------------------------------------- /week8/2-rest-resource/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "public/lib" 3 | } 4 | -------------------------------------------------------------------------------- /week8/2-rest-resource/README.md: -------------------------------------------------------------------------------- 1 | # Week 8 Task 2 REST Resource 2 | 3 | ## Premise 4 | Create a helper class that encapsulates ajax calls to REST resources whereby 5 | - the constructor gets an `url` attribute. 6 | ```js 7 | function Resource(url){ 8 | 9 | } 10 | ``` 11 | - has the following methods 12 | - query() - calls a rest resource and returns a list of items (collection) 13 | - create(data) - sends a new entry (document) to the list 14 | - update(id, data) - updates a document by id 15 | - delete(id) - deletes by id 16 | - implements and returns a promise for each method 17 | 18 | ## Usage 19 | ### Basic 20 | ```js 21 | 22 | var student = Resource("/api/students") 23 | 24 | student.query().then(function(result){ 25 | console.log(result); 26 | }) 27 | 28 | ``` 29 | ### Advanced 30 | ```js 31 | var student = new Resource("http://192.168.0.66:3000/api/students"); 32 | 33 | var displayResult = function(res){ 34 | console.log(res) 35 | 36 | return res; 37 | } 38 | 39 | var getId = function(data){ 40 | 41 | var id = data._id; 42 | 43 | return id; 44 | }; 45 | 46 | // query 47 | 48 | // create 49 | var data = { 50 | name: "Gospodin", 51 | email: "gospodin@gospodinoff.cc" 52 | }; 53 | 54 | var newData = { 55 | name: "Peter" 56 | } 57 | student.query() 58 | .then(displayResult) 59 | .then(function(){ 60 | return student.create(data); 61 | }) 62 | .then(displayResult) 63 | .then(getId) 64 | .then(function(id){ 65 | return student.update(id, newData); 66 | }) 67 | .then(displayResult) 68 | .then(function(){ 69 | return student.query(); 70 | }) 71 | .then(displayResult); 72 | 73 | }) 74 | ``` 75 | 76 | ## Dependencies 77 | - bower - for the task itself 78 | - jquery 79 | - q#1.x 80 | - npm - to be able to run a RESTful API to test 81 | - express 82 | - method-override 83 | - body-parser 84 | - iblokz/node-restify 85 | - mongoose 86 | - jade 87 | 88 | ## Hints 89 | - CRUD pattern -> Create (POST), Read (GET), Update (PUT), Delete 90 | - REST resource archtypes 91 | - document 92 | - collection 93 | - REST endpoints 94 | - collection 95 | - /api/users GET, POST 96 | - document 97 | - /api/users/:id GET, PUT, DELETE 98 | 99 | ## Next Steps 100 | - Use it in a previous task where you have to access RESTful APIs -------------------------------------------------------------------------------- /week8/2-rest-resource/data/restMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "collections" : { 3 | "students": { 4 | "model": "Student", 5 | "schema": { 6 | "name" : "String", 7 | "email" : "String", 8 | "gitRepo" : "String", 9 | "hobbyProject" : "String" 10 | } 11 | } 12 | }, 13 | "routes": { 14 | "api" : { 15 | "_meta": { 16 | "virtual": true, 17 | "crud": true, 18 | "contentType": "json" 19 | }, 20 | "students": { 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /week8/2-rest-resource/public/js/init.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function(){ 2 | var student = new Resource("http://192.168.0.66:3000/api/students"); 3 | 4 | var displayResult = function(res){ 5 | console.log(res) 6 | 7 | return res; 8 | } 9 | 10 | var getId = function(data){ 11 | 12 | var id = data._id; 13 | 14 | return id; 15 | }; 16 | 17 | // query 18 | 19 | // create 20 | var data = { 21 | name: "Gospodin", 22 | email: "gospodin@gospodinoff.cc" 23 | }; 24 | 25 | var newData = { 26 | name: "Peter" 27 | } 28 | student.query() 29 | .then(displayResult) 30 | .then(function(){ 31 | return student.create(data); 32 | }) 33 | .then(displayResult) 34 | .then(getId) 35 | .then(function(id){ 36 | return student.update(id, newData); 37 | }) 38 | .then(displayResult) 39 | .then(function(){ 40 | return student.query(); 41 | }) 42 | .then(displayResult); 43 | 44 | }) -------------------------------------------------------------------------------- /week8/2-rest-resource/server.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | // dependancies 4 | var express = require('express') 5 | var fs = require("fs"); 6 | var mongoose = require("mongoose"); 7 | var bodyParser = require('body-parser'); 8 | var methodOverride = require('method-override'); 9 | 10 | // restify 11 | var restify = require("iblokz-node-restify"); 12 | var restMap = require("./data/restMap.json"); 13 | 14 | // create app 15 | var app = express() 16 | 17 | // connect to db 18 | var db = mongoose.connect("mongodb://localhost/week8-resource"); 19 | 20 | // load model 21 | restify.loadModel(restMap, db); 22 | 23 | app.use(function(req, res, next) { 24 | res.header('Access-Control-Allow-Origin', '*'); 25 | res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS'); 26 | res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With'); 27 | 28 | // intercept OPTIONS method 29 | if ('OPTIONS' == req.method) { 30 | res.send(200); 31 | } 32 | else { 33 | next(); 34 | } 35 | }); 36 | 37 | // configuration and middleware 38 | app.use(express.static('public')); 39 | app.set('view engine', 'jade'); 40 | 41 | app.use(bodyParser.urlencoded({ 42 | extended: true 43 | })); 44 | app.use(bodyParser.json()); 45 | app.use(methodOverride()); 46 | 47 | 48 | // routes 49 | app.get('/', function (req, res) { 50 | res.render('index'); 51 | }) 52 | 53 | // init routes 54 | restify.initRoutes(app,restMap,{},db); 55 | 56 | // set up server 57 | var server = app.listen(3000, function () { 58 | 59 | var host = server.address().address 60 | var port = server.address().port 61 | 62 | console.log('Example app listening at http://%s:%s', host, port) 63 | 64 | }) -------------------------------------------------------------------------------- /week8/README.md: -------------------------------------------------------------------------------- 1 | # Week 8 Async & Promises, MVC & App Architecture 2 | 3 | ## Check out theese resources 4 | 5 | - [What makes javascript weird and awesome](https://www.youtube.com/watch?v=JEq7Ehw-qk8&list=PLoYCgNOIyGABI011EYc-avPOsk1YsMUe_) 6 | - [Philip Roberts: What the heck is the event loop anyway](https://www.youtube.com/watch?v=8aGhZQkoFbQ) 7 | - https://github.com/kriskowal/q -------------------------------------------------------------------------------- /week8/hangout/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "public/lib" 3 | } 4 | -------------------------------------------------------------------------------- /week8/hangout/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | public/lib 3 | 4 | -------------------------------------------------------------------------------- /week8/hangout/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "week8-subsciptions", 3 | "version": "0.1.0", 4 | "authors": [ 5 | "Alex Milanov " 6 | ], 7 | "license": "MIT", 8 | "ignore": [ 9 | "**/.*", 10 | "node_modules", 11 | "bower_components", 12 | "test", 13 | "tests" 14 | ], 15 | "dependencies": { 16 | "jquery": "~2.1.4", 17 | "q": "1.x", 18 | "bootstrap": "~3.3.4", 19 | "jade": "~1.9.2" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /week8/hangout/data/restMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "collections" : { 3 | "users": { 4 | "model": "User", 5 | "schema": { 6 | "name" : "String", 7 | "email" : "String" 8 | } 9 | }, 10 | "mags": { 11 | "model": "Mag", 12 | "schema": { 13 | "name": "String", 14 | "vol": "Number", 15 | "price": "Number" 16 | } 17 | }, 18 | "subs": { 19 | "model": "Sub", 20 | "schema": { 21 | "userId": "ObjectId", 22 | "magId": "ObjectId", 23 | "months": "Number" 24 | } 25 | } 26 | }, 27 | "routes": { 28 | "api" : { 29 | "_meta": { 30 | "virtual": true, 31 | "crud": true, 32 | "contentType": "json" 33 | }, 34 | "users": { 35 | }, 36 | "mags": { 37 | }, 38 | "subs": { 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /week8/hangout/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "week8-subsciptions", 3 | "version": "0.1.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "body-parser": "^1.12.3", 14 | "express": "^4.12.3", 15 | "iblokz-node-restify": "git://github.com/iblokz/node-restify", 16 | "jade": "^1.9.2", 17 | "method-override": "^2.3.2", 18 | "mongoose": "^4.0.2" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /week8/hangout/public/js/init.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function(){ 2 | 3 | function displayWithJade(container, fileName, data){ 4 | return Q($.get(fileName)).then(function(jadeString){ 5 | var renderedHtml = jade.render(jadeString, data); 6 | container.html(renderedHtml); 7 | }) 8 | } 9 | 10 | function getDataFromForm(form){ 11 | 12 | var formArray = form.serializeArray(); 13 | 14 | var data = {}; 15 | 16 | formArray.forEach(function(field){ 17 | data[field.name] = field.value; 18 | }) 19 | return data; 20 | } 21 | 22 | // resources 23 | var mag = new Resource("/api/mags"); 24 | var sub = new Resource("/api/subs"); 25 | var user = new Resource("/api/users"); 26 | 27 | // queries 28 | mag.query().then(function(res){ 29 | var container = $("#mags"); 30 | var mags = res.list; 31 | 32 | return displayWithJade(container, "/views/mags.jade", { 33 | mags: mags 34 | }); 35 | 36 | }).then(function(){ 37 | $("#mags-form").on("submit", function(event){ 38 | 39 | var data = getDataFromForm($(this)); 40 | 41 | mag.create(data).then(function(){ 42 | console.log("Created Successfuly!") 43 | }) 44 | 45 | event.preventDefault(); 46 | }) 47 | 48 | }) 49 | 50 | 51 | sub.query().then(function(res){ 52 | var container = $("#subs"); 53 | var subs = res.list; 54 | 55 | displayWithJade(container, "/views/subs.jade", { 56 | subs: subs 57 | }); 58 | }) 59 | 60 | user.query().then(function(res){ 61 | var container = $("#users"); 62 | var users = res.list; 63 | 64 | displayWithJade(container, "/views/users.jade", { 65 | users: users 66 | }); 67 | }) 68 | 69 | 70 | }) -------------------------------------------------------------------------------- /week8/hangout/public/js/resource.js: -------------------------------------------------------------------------------- 1 | function Resource(url) { 2 | this.url = url; 3 | } 4 | 5 | // api/students -> GET, POST 6 | 7 | Resource.prototype.query = function(queryParams) { 8 | 9 | return Q($.ajax({ 10 | url: this.url, // api endpoint 11 | method: "get", // method 12 | data: queryParams, 13 | dataType: "json" // data type 14 | })); 15 | } 16 | 17 | Resource.prototype.create = function(data) { 18 | return Q($.ajax({ 19 | url: this.url, // api endpoint 20 | method: "post", // method 21 | data: data, 22 | dataType: "json" // data type 23 | })); 24 | } 25 | 26 | // api/students/:id -> GET, PUT, DELETE 27 | 28 | Resource.prototype.view = function(id) { 29 | return Q($.get(this.url+"/"+id)); 30 | } 31 | 32 | Resource.prototype.update = function(id, data) { 33 | return Q($.ajax({ 34 | url: this.url+"/"+id, // api endpoint 35 | method: "put", // method 36 | data: data, 37 | dataType: "json" // data type 38 | })); 39 | } 40 | 41 | Resource.prototype.delete = function(id) { 42 | return Q($.ajax({ 43 | url: this.url+"/"+id, // api endpoint 44 | method: "delete", 45 | dataType: "json" // data type 46 | })); 47 | } -------------------------------------------------------------------------------- /week8/hangout/public/views/mags.jade: -------------------------------------------------------------------------------- 1 | h4 Magazines 2 | 3 | .row 4 | .col-sm-4 5 | form.well#mags-form 6 | .form-group 7 | label Name 8 | input.form-control(name="name",type="text") 9 | .form-group 10 | label Volume 11 | input.form-control(name="vol",type="number") 12 | .form-group 13 | label Price 14 | input.form-control(name="price",type="number") 15 | button.btn.btn-primary(type="submit") Save 16 | button.btn.btn-default Reset 17 | 18 | .col-sm-8 19 | table.table 20 | thead: tr 21 | th Name 22 | th Vol 23 | th Price 24 | th Actions 25 | tbody 26 | each mag in mags 27 | tr 28 | td= mag.name 29 | td= mag.vol 30 | td= mag.price 31 | td 32 | a.btn.btn-primary edit 33 | |   34 | a.btn.btn-danger delete -------------------------------------------------------------------------------- /week8/hangout/public/views/subs.jade: -------------------------------------------------------------------------------- 1 | h4 Subscriptions 2 | 3 | .row 4 | .col-sm-4 5 | form.well#subs-form 6 | .form-group 7 | label Magazine 8 | input.form-control(name="magId",type="text") 9 | .form-group 10 | label User 11 | input.form-control(name="userId",type="text") 12 | .form-group 13 | label Months 14 | input.form-control(name="months",type="number") 15 | button.btn.btn-primary(type="submit") Save 16 | button.btn.btn-default Reset 17 | 18 | .col-sm-8 19 | table.table 20 | thead: tr 21 | th Mag 22 | th User 23 | th Months 24 | tbody 25 | each sub in subs 26 | tr 27 | td= sub.magId 28 | td= sub.userId 29 | td= sub.months -------------------------------------------------------------------------------- /week8/hangout/public/views/users.jade: -------------------------------------------------------------------------------- 1 | h4 Users 2 | 3 | .row 4 | .col-sm-4 5 | form.well#users-form 6 | .form-group 7 | label Magazine 8 | input.form-control(name="name",type="text") 9 | .form-group 10 | label User 11 | input.form-control(name="email",type="text") 12 | button.btn.btn-primary(type="submit") Save 13 | button.btn.btn-default Reset 14 | 15 | .col-sm-8 16 | table.table 17 | thead: tr 18 | th Name 19 | th Email 20 | tbody 21 | each user in users 22 | tr 23 | td= user.name 24 | td= user.email -------------------------------------------------------------------------------- /week8/hangout/server.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | // dependancies 4 | var express = require('express') 5 | var fs = require("fs"); 6 | var mongoose = require("mongoose"); 7 | var bodyParser = require('body-parser'); 8 | var methodOverride = require('method-override'); 9 | 10 | // restify 11 | var restify = require("iblokz-node-restify"); 12 | var restMap = require("./data/restMap.json"); 13 | 14 | // create app 15 | var app = express() 16 | 17 | // connect to db 18 | var db = mongoose.connect("mongodb://localhost/week8-subscriptions"); 19 | 20 | // load model 21 | restify.loadModel(restMap, db); 22 | 23 | app.use(function(req, res, next) { 24 | res.header('Access-Control-Allow-Origin', '*'); 25 | res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS'); 26 | res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With'); 27 | 28 | // intercept OPTIONS method 29 | if ('OPTIONS' == req.method) { 30 | res.send(200); 31 | } 32 | else { 33 | next(); 34 | } 35 | }); 36 | 37 | // configuration and middleware 38 | app.use(express.static('public')); 39 | app.set('view engine', 'jade'); 40 | 41 | app.use(bodyParser.urlencoded({ 42 | extended: true 43 | })); 44 | app.use(bodyParser.json()); 45 | app.use(methodOverride()); 46 | 47 | 48 | // routes 49 | app.get('/', function (req, res) { 50 | res.render('index'); 51 | }) 52 | 53 | // init routes 54 | restify.initRoutes(app,restMap,{},db); 55 | 56 | // set up server 57 | var server = app.listen(3000, function () { 58 | 59 | var host = server.address().address 60 | var port = server.address().port 61 | 62 | console.log('Example app listening at http://%s:%s', host, port) 63 | 64 | }) -------------------------------------------------------------------------------- /week8/hangout/views/index.jade: -------------------------------------------------------------------------------- 1 | html 2 | head 3 | title Resource Test 4 | link(rel="stylesheet",href="lib/bootstrap/dist/css/bootstrap.css") 5 | body 6 | 7 | 8 | .container 9 | 10 | h1 Ultra Cool Publisher 11 | ul.nav.nav-tabs.nav-pills 12 | li.active: a(href="#mags", data-toggle="tab") Magazines 13 | li: a(href="#users", data-toggle="tab") Users 14 | li: a(href="#subs", data-toggle="tab") Subscriptions 15 | 16 | .tab-content 17 | .active.tab-pane#mags 18 | 19 | .tab-pane#users 20 | 21 | .tab-pane#subs 22 | 23 | 24 | 25 | script(src="lib/jquery/dist/jquery.js") 26 | script(src="lib/q/q.js") 27 | script(src="lib/bootstrap/dist/js/bootstrap.js") 28 | script(src="lib/jade/jade.js") 29 | //- app files 30 | script(src="js/resource.js") 31 | script(src="js/init.js") 32 | 33 | -------------------------------------------------------------------------------- /week8/rest-example/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "public/lib" 3 | } 4 | -------------------------------------------------------------------------------- /week8/rest-example/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "week8-resource", 3 | "version": "0.0.0", 4 | "authors": [ 5 | "Alex Milanov " 6 | ], 7 | "license": "MIT", 8 | "ignore": [ 9 | "**/.*", 10 | "node_modules", 11 | "bower_components", 12 | "public/lib", 13 | "test", 14 | "tests" 15 | ], 16 | "dependencies": { 17 | "jquery": "~2.1.4", 18 | "q": "1.x" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /week8/rest-example/data/restMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "collections" : { 3 | "students": { 4 | "model": "Student", 5 | "schema": { 6 | "name" : "String", 7 | "email" : "String", 8 | "gitRepo" : "String", 9 | "hobbyProject" : "String" 10 | } 11 | } 12 | }, 13 | "routes": { 14 | "api" : { 15 | "_meta": { 16 | "virtual": true, 17 | "crud": true, 18 | "contentType": "json" 19 | }, 20 | "students": { 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /week8/rest-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "week8-resource", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "body-parser": "^1.12.3", 14 | "express": "^4.12.3", 15 | "iblokz-node-restify": "git://github.com/iblokz/node-restify", 16 | "jade": "^1.9.2", 17 | "method-override": "^2.3.2", 18 | "mongoose": "^4.0.2" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /week8/rest-example/public/js/init.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function(){ 2 | var student = new Resource("http://192.168.0.66:3000/api/students"); 3 | 4 | var displayResult = function(res){ 5 | console.log(res) 6 | 7 | return res; 8 | } 9 | 10 | var getId = function(data){ 11 | 12 | var id = data._id; 13 | 14 | return id; 15 | }; 16 | 17 | // query 18 | 19 | // create 20 | var data = { 21 | name: "Gospodin", 22 | email: "gospodin@gospodinoff.cc" 23 | }; 24 | 25 | var newData = { 26 | name: "Peter" 27 | } 28 | student.query() 29 | .then(displayResult) 30 | .then(function(){ 31 | return student.create(data); 32 | }) 33 | .then(displayResult) 34 | .then(getId) 35 | .then(function(id){ 36 | return student.update(id, newData); 37 | }) 38 | .then(displayResult) 39 | .then(function(){ 40 | return student.query(); 41 | }) 42 | .then(displayResult); 43 | 44 | }) -------------------------------------------------------------------------------- /week8/rest-example/public/js/resource.js: -------------------------------------------------------------------------------- 1 | function Resource(url) { 2 | this.url = url; 3 | } 4 | 5 | // api/students -> GET, POST 6 | 7 | Resource.prototype.query = function(queryParams) { 8 | 9 | return Q($.ajax({ 10 | url: this.url, // api endpoint 11 | method: "get", // method 12 | data: queryParams, 13 | dataType: "json" // data type 14 | })); 15 | } 16 | 17 | Resource.prototype.create = function(data) { 18 | return Q($.ajax({ 19 | url: this.url, // api endpoint 20 | method: "post", // method 21 | data: data, 22 | dataType: "json" // data type 23 | })); 24 | } 25 | 26 | // api/students/:id -> GET, PUT, DELETE 27 | 28 | Resource.prototype.view = function(id) { 29 | return Q($.get(this.url+"/"+id)); 30 | } 31 | 32 | Resource.prototype.update = function(id, data) { 33 | return Q($.ajax({ 34 | url: this.url+"/"+id, // api endpoint 35 | method: "put", // method 36 | data: data, 37 | dataType: "json" // data type 38 | })); 39 | } 40 | 41 | Resource.prototype.delete = function(id) { 42 | return Q($.ajax({ 43 | url: this.url+"/"+id, // api endpoint 44 | method: "delete", 45 | dataType: "json" // data type 46 | })); 47 | } -------------------------------------------------------------------------------- /week8/rest-example/server.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | // dependancies 4 | var express = require('express') 5 | var fs = require("fs"); 6 | var mongoose = require("mongoose"); 7 | var bodyParser = require('body-parser'); 8 | var methodOverride = require('method-override'); 9 | 10 | // restify 11 | var restify = require("iblokz-node-restify"); 12 | var restMap = require("./data/restMap.json"); 13 | 14 | // create app 15 | var app = express() 16 | 17 | // connect to db 18 | var db = mongoose.connect("mongodb://localhost/week8-resource"); 19 | 20 | // load model 21 | restify.loadModel(restMap, db); 22 | 23 | app.use(function(req, res, next) { 24 | res.header('Access-Control-Allow-Origin', '*'); 25 | res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS'); 26 | res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With'); 27 | 28 | // intercept OPTIONS method 29 | if ('OPTIONS' == req.method) { 30 | res.send(200); 31 | } 32 | else { 33 | next(); 34 | } 35 | }); 36 | 37 | // configuration and middleware 38 | app.use(express.static('public')); 39 | app.set('view engine', 'jade'); 40 | 41 | app.use(bodyParser.urlencoded({ 42 | extended: true 43 | })); 44 | app.use(bodyParser.json()); 45 | app.use(methodOverride()); 46 | 47 | 48 | // routes 49 | app.get('/', function (req, res) { 50 | res.render('index'); 51 | }) 52 | 53 | // init routes 54 | restify.initRoutes(app,restMap,{},db); 55 | 56 | // set up server 57 | var server = app.listen(3000, function () { 58 | 59 | var host = server.address().address 60 | var port = server.address().port 61 | 62 | console.log('Example app listening at http://%s:%s', host, port) 63 | 64 | }) -------------------------------------------------------------------------------- /week8/rest-example/views/index.jade: -------------------------------------------------------------------------------- 1 | html 2 | head 3 | title Resource Test 4 | link(rel="stylesheet",href="lib/bootstrap/dist/css/bootstrap.css") 5 | 6 | 7 | 8 | 9 | body 10 | 11 | script(src="lib/jquery/dist/jquery.js") 12 | script(src="lib/q/q.js") 13 | script(src="js/resource.js") 14 | script(src="js/init.js") 15 | 16 | --------------------------------------------------------------------------------