diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..3c3629e6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+node_modules
diff --git a/collections/VideoCollection.js b/collections/VideoCollection.js
new file mode 100644
index 00000000..6db9215f
--- /dev/null
+++ b/collections/VideoCollection.js
@@ -0,0 +1,19 @@
+var key = 'AIzaSyAPYWVgR_Q5sELYDsNvyPsUWdZnjZIrBRM';
+var VideoCollection = Backbone.Collection.extend({
+ model: VideoModel,
+
+ url: 'https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=5&type=video&videoEmbeddable=true&key=' + key,
+
+ parse: function(response) {
+ return response.items.map(function (video){
+ return {
+ id: video.id.videoId,
+ title: video.snippet.title,
+ text: video.snippet.description,
+ thumb: video.snippet.thumbnails.default.url
+ }
+ })
+ }
+
+
+});
\ No newline at end of file
diff --git a/index.html b/index.html
new file mode 100644
index 00000000..941098a1
--- /dev/null
+++ b/index.html
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+ Project Youtube
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/main.js b/main.js
new file mode 100644
index 00000000..37153a42
--- /dev/null
+++ b/main.js
@@ -0,0 +1,187 @@
+
+var sampleData = {
+ "kind": "youtube#searchListResponse",
+ "etag": "jrLgj0XGHegO7nztkfHkqGu_dmo",
+ "nextPageToken": "CAUQAA",
+ "regionCode": "US",
+ "pageInfo": {
+ "totalResults": 1000000,
+ "resultsPerPage": 5
+ },
+ "items": [
+ {
+ "kind": "youtube#searchResult",
+ "etag": "BJ_pZJtlXOdlNB7-r4tAl81I3C4",
+ "id": {
+ "kind": "youtube#video",
+ "videoId": "L5dM6NdZXd4"
+ },
+ "snippet": {
+ "publishedAt": "2021-01-17T16:00:09Z",
+ "channelId": "UCBvnS6nyNGAl8EUNt-40xoQ",
+ "title": "Josh Bridges 2021 CrossFit Open Prep + Dave Castro Gets Called Out | Paying the Man Ep.083",
+ "description": "The CrossFit season is just around the corner so I show you how I am preparing for the 2021 CrossFit Open. I also call out Dave Castro, to get him and I in a ring, ...",
+ "thumbnails": {
+ "default": {
+ "url": "https://i.ytimg.com/vi/L5dM6NdZXd4/default.jpg",
+ "width": 120,
+ "height": 90
+ },
+ "medium": {
+ "url": "https://i.ytimg.com/vi/L5dM6NdZXd4/mqdefault.jpg",
+ "width": 320,
+ "height": 180
+ },
+ "high": {
+ "url": "https://i.ytimg.com/vi/L5dM6NdZXd4/hqdefault.jpg",
+ "width": 480,
+ "height": 360
+ }
+ },
+ "channelTitle": "Josh Bridges",
+ "liveBroadcastContent": "none",
+ "publishTime": "2021-01-17T16:00:09Z"
+ }
+ },
+ {
+ "kind": "youtube#searchResult",
+ "etag": "JFhM06PZ2Ck-Tu0ZALY18QhBKog",
+ "id": {
+ "kind": "youtube#video",
+ "videoId": "vcDsh9Sjm18"
+ },
+ "snippet": {
+ "publishedAt": "2020-10-23T16:43:38Z",
+ "channelId": "UCRs1pHnES3QDdh43xbjOmzw",
+ "title": "Event 1 & 2 - 2007 Reload and Corn Sack Sprint - 2020 CrossFit Games",
+ "description": "The hardest test in CrossFit Games history begins here. Five men. Five women. Three days of grueling competition. Grab snacks. #CrossFitGames The CrossFit ...",
+ "thumbnails": {
+ "default": {
+ "url": "https://i.ytimg.com/vi/vcDsh9Sjm18/default.jpg",
+ "width": 120,
+ "height": 90
+ },
+ "medium": {
+ "url": "https://i.ytimg.com/vi/vcDsh9Sjm18/mqdefault.jpg",
+ "width": 320,
+ "height": 180
+ },
+ "high": {
+ "url": "https://i.ytimg.com/vi/vcDsh9Sjm18/hqdefault.jpg",
+ "width": 480,
+ "height": 360
+ }
+ },
+ "channelTitle": "CrossFit Games",
+ "liveBroadcastContent": "none",
+ "publishTime": "2020-10-23T16:43:38Z"
+ }
+ },
+ {
+ "kind": "youtube#searchResult",
+ "etag": "gW7BE77piDCVb3uIFDXHGyK1BNA",
+ "id": {
+ "kind": "youtube#video",
+ "videoId": "tzD9BkXGJ1M"
+ },
+ "snippet": {
+ "publishedAt": "2010-07-23T15:09:19Z",
+ "channelId": "UCtcQ6TPwXAYgZ1Mcl3M1vng",
+ "title": "What is CrossFit?",
+ "description": "What is CrossFit? CrossFit is an effective way to get fit. Anyone can do it. It is a fitness program that combines a wide variety of functional movements into a timed ...",
+ "thumbnails": {
+ "default": {
+ "url": "https://i.ytimg.com/vi/tzD9BkXGJ1M/default.jpg",
+ "width": 120,
+ "height": 90
+ },
+ "medium": {
+ "url": "https://i.ytimg.com/vi/tzD9BkXGJ1M/mqdefault.jpg",
+ "width": 320,
+ "height": 180
+ },
+ "high": {
+ "url": "https://i.ytimg.com/vi/tzD9BkXGJ1M/hqdefault.jpg",
+ "width": 480,
+ "height": 360
+ }
+ },
+ "channelTitle": "CrossFit®",
+ "liveBroadcastContent": "none",
+ "publishTime": "2010-07-23T15:09:19Z"
+ }
+ },
+ {
+ "kind": "youtube#searchResult",
+ "etag": "8KgGsXPicxGt8j-2-OuRhutT9s4",
+ "id": {
+ "kind": "youtube#video",
+ "videoId": "860T3WYvyoc"
+ },
+ "snippet": {
+ "publishedAt": "2020-10-23T20:16:34Z",
+ "channelId": "UCRs1pHnES3QDdh43xbjOmzw",
+ "title": "Event 3 - CrossFit Total - 2020 CrossFit Games",
+ "description": "The wait is finally over. Watch the three-day finale of the 2020 CrossFit Games right here. Buckle up for an unforgettable weekend. Every event of the final stage ...",
+ "thumbnails": {
+ "default": {
+ "url": "https://i.ytimg.com/vi/860T3WYvyoc/default.jpg",
+ "width": 120,
+ "height": 90
+ },
+ "medium": {
+ "url": "https://i.ytimg.com/vi/860T3WYvyoc/mqdefault.jpg",
+ "width": 320,
+ "height": 180
+ },
+ "high": {
+ "url": "https://i.ytimg.com/vi/860T3WYvyoc/hqdefault.jpg",
+ "width": 480,
+ "height": 360
+ }
+ },
+ "channelTitle": "CrossFit Games",
+ "liveBroadcastContent": "none",
+ "publishTime": "2020-10-23T20:16:34Z"
+ }
+ },
+ {
+ "kind": "youtube#searchResult",
+ "etag": "a53xv9Yh4o643yVpPbDCpFyCZIM",
+ "id": {
+ "kind": "youtube#video",
+ "videoId": "JZpVUNiSg5s"
+ },
+ "snippet": {
+ "publishedAt": "2020-12-28T18:09:41Z",
+ "channelId": "UCEQi1ZNJiw3YMRwni0OLsTQ",
+ "title": "I TRIED CROSSFIT FOR THE FIRST TIME",
+ "description": "Trying a crossfit workout for the first time with the @Buttery Bros BUTTERY BROS https://www.youtube.com/channel/UCp00ppkfPFBUTiiNNd8kS9Q BUTTERY ...",
+ "thumbnails": {
+ "default": {
+ "url": "https://i.ytimg.com/vi/JZpVUNiSg5s/default.jpg",
+ "width": 120,
+ "height": 90
+ },
+ "medium": {
+ "url": "https://i.ytimg.com/vi/JZpVUNiSg5s/mqdefault.jpg",
+ "width": 320,
+ "height": 180
+ },
+ "high": {
+ "url": "https://i.ytimg.com/vi/JZpVUNiSg5s/hqdefault.jpg",
+ "width": 480,
+ "height": 360
+ }
+ },
+ "channelTitle": "Whitney Simmons",
+ "liveBroadcastContent": "none",
+ "publishTime": "2020-12-28T18:09:41Z"
+ }
+ }
+ ]
+}
+
+var appModel = new AppModel();
+
+var appView = new AppView({ model: appModel });
\ No newline at end of file
diff --git a/models/AppModel.js b/models/AppModel.js
new file mode 100644
index 00000000..f756efcc
--- /dev/null
+++ b/models/AppModel.js
@@ -0,0 +1,16 @@
+var AppModel = Backbone.Model.extend({
+ defaults: function() {
+ return {
+ videos: new VideoCollection(),
+ current_video: null,
+ };
+ },
+
+ updateCurrentVideo: function(clickedVidId) {
+ var allVids = this.get('videos');
+
+ var currentVideo = allVids.findWhere({id: clickedVidId});
+
+ this.set('current_video', currentVideo);
+ }
+});
\ No newline at end of file
diff --git a/models/VideoModel.js b/models/VideoModel.js
new file mode 100644
index 00000000..1b573a49
--- /dev/null
+++ b/models/VideoModel.js
@@ -0,0 +1,11 @@
+var VideoModel = Backbone.Model.extend({
+
+ defaults: {
+ id: null,
+ title: '',
+ text: '',
+ thumb: '',
+ },
+
+ urlRoot: "https://www.youtube.com/embed/"
+})
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..61a6eb15
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,69 @@
+{
+ "name": "backbone-youtube",
+ "version": "1.0.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "backbone": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.4.0.tgz",
+ "integrity": "sha512-RLmDrRXkVdouTg38jcgHhyQ/2zjg7a8E6sz2zxfz21Hh17xDJYUHBZimVIt5fUyS8vbfpeSmTL3gUjTEvUV3qQ==",
+ "requires": {
+ "underscore": ">=1.8.3"
+ }
+ },
+ "bootstrap": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.0.tgz",
+ "integrity": "sha512-bs74WNI9BgBo3cEovmdMHikSKoXnDgA6VQjJ7TyTotU6L7d41ZyCEEelPwkYEzsG/Zjv3ie9IE3EMAje0W9Xew=="
+ },
+ "handlebars": {
+ "version": "4.7.7",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz",
+ "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==",
+ "requires": {
+ "minimist": "^1.2.5",
+ "neo-async": "^2.6.0",
+ "source-map": "^0.6.1",
+ "uglify-js": "^3.1.4",
+ "wordwrap": "^1.0.0"
+ }
+ },
+ "jquery": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
+ "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw=="
+ },
+ "minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
+ },
+ "neo-async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+ },
+ "uglify-js": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.1.tgz",
+ "integrity": "sha512-JhS3hmcVaXlp/xSo3PKY5R0JqKs5M3IV+exdLHW99qKvKivPO4Z8qbej6mte17SOPqAOVMjt/XGgWacnFSzM3g==",
+ "optional": true
+ },
+ "underscore": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz",
+ "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g=="
+ },
+ "wordwrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..cace0d38
--- /dev/null
+++ b/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "backbone-youtube",
+ "version": "1.0.0",
+ "description": "This project has been created by a student at Parsity, an online software engineering course. The work in this repository is wholly of the student based on a sample starter project that can be accessed by looking at the repository that this project forks.",
+ "main": "main.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/zdresser/backbone-youtube.git"
+ },
+ "author": "",
+ "license": "ISC",
+ "bugs": {
+ "url": "https://github.com/zdresser/backbone-youtube/issues"
+ },
+ "homepage": "https://github.com/zdresser/backbone-youtube#readme",
+ "dependencies": {
+ "backbone": "^1.4.0",
+ "bootstrap": "^5.1.0",
+ "handlebars": "^4.7.7",
+ "jquery": "^3.6.0",
+ "underscore": "^1.13.1"
+ }
+}
diff --git a/style.css b/style.css
new file mode 100644
index 00000000..c909f6ed
--- /dev/null
+++ b/style.css
@@ -0,0 +1,14 @@
+.page-header {
+ margin-top: 40px;
+ text-align: center;
+}
+
+.search-form {
+ margin-top: 40px;
+ margin-bottom: 40px;
+}
+
+.large-vid {
+ height: 100%;
+ width: 100%;
+}
diff --git a/views/AppView.js b/views/AppView.js
new file mode 100644
index 00000000..f0c2ef3b
--- /dev/null
+++ b/views/AppView.js
@@ -0,0 +1,65 @@
+/*To fix
+/ Alignment
+/ Main video is tiny
+/ Allow for a second search without refreshing
+/ The five videos render multiple times
+*/
+
+var AppView = Backbone.View.extend({
+ el: $('body'),
+
+ mainVideo: null,
+
+ initialize: function() {
+ this.$searchInput = this.$('#search-query');
+ this.listenTo(this.model.get('videos'), 'add', this.renderVideos);
+ this.listenTo(this.model.get('videos'), 'add', this.renderInitVideo);
+ this.listenTo(this.model, 'change:current_video', this.renderMainVideo)
+
+ },
+
+ events: {
+ 'click .search': 'ytSearch',
+ 'click .view-video': 'viewVideo'
+ },
+
+ viewVideo: function(e) {
+ var clickedVidId = $(e.currentTarget).data().id;
+
+ this.model.updateCurrentVideo(clickedVidId);
+ },
+
+ ytSearch: function() {
+ $(".videos").empty();
+ var query = this.$searchInput.val();
+ this.model.get('videos').fetch({data: { q: query, reset: true}});
+
+ },
+
+ renderVid: function (video) {
+ var videoView = new VideoView({model: video});
+ this.$('.videos').append(videoView.render().el);
+ },
+
+ renderVideos: function () {
+ this.model.get('videos').each(function(m) {
+ this.renderVid(m);
+ }, this);
+ },
+
+
+ renderMainVideo: function() {
+
+ if (this.mainVideo) {
+ this.mainVideo.remove();
+ }
+ this.mainVideo = new MainVideoView({ model: this.model.get('current_video')})
+
+ this.$('.current-video').append(this.mainVideo.render().el);
+ },
+
+ renderInitVideo: function () {
+ this.model.set('current_video', this.model.get('videos').at(0));
+ },
+
+})
\ No newline at end of file
diff --git a/views/MainVideoView.js b/views/MainVideoView.js
new file mode 100644
index 00000000..8c25976f
--- /dev/null
+++ b/views/MainVideoView.js
@@ -0,0 +1,11 @@
+var MainVideoView = Backbone.View.extend({
+ className: 'main-video',
+
+ template: Handlebars.compile($('#main-video-template').html()),
+
+ render: function() {
+ this.$el.html(this.template(this.model.toJSON()));
+
+ return this;
+ }
+})
\ No newline at end of file
diff --git a/views/VideoView.js b/views/VideoView.js
new file mode 100644
index 00000000..44f34683
--- /dev/null
+++ b/views/VideoView.js
@@ -0,0 +1,11 @@
+var VideoView = Backbone.View.extend({
+ className: 'video',
+
+ template: Handlebars.compile($('#video-template').html()),
+
+ render: function() {
+ this.$el.html(this.template(this.model.toJSON()));
+ return this;
+ }
+
+});
\ No newline at end of file