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/VideosCollection.js b/collections/VideosCollection.js new file mode 100644 index 00000000..3e704b1a --- /dev/null +++ b/collections/VideosCollection.js @@ -0,0 +1,17 @@ +let VideosCollection = Backbone.Collection.extend({ + model: VideoModel, + + parse: function(response) { + let configuredResponse = response.items.map(function(vid) { + return { + id: vid.id.videoId, + video_url: 'https://www.youtube.com/embed/' + vid.id.videoId, + thumbnail_url: vid.snippet.thumbnails.default.url, + title: vid.snippet.title, + description: vid.snippet.description + } + }) + + return configuredResponse; + } +}); \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 00000000..b939aa46 --- /dev/null +++ b/index.html @@ -0,0 +1,92 @@ + + + + + + + + + Backbone Youtube + + +
+
+
+
+

Project Youtube

+
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + diff --git a/main.js b/main.js new file mode 100644 index 00000000..ed1fe78d --- /dev/null +++ b/main.js @@ -0,0 +1,6 @@ +let appModel = new AppModel(); + +let appView = new AppView({model: appModel}); + +appModel.get('videos').fetch({reset: true}); +appModel.set('selectedVideo', appModel.get('videos').models[0]); \ No newline at end of file diff --git a/models/AppModel.js b/models/AppModel.js new file mode 100644 index 00000000..2448c84f --- /dev/null +++ b/models/AppModel.js @@ -0,0 +1,207 @@ +// 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" +// } +// } +// ] +// } + +let API_KEY = 'AIzaSyCHd0mgg00maYbK6w6fRP8iIBfqK6Jsmn4'; + +let AppModel = Backbone.Model.extend({ + defaults: function() { + let videosCollection = new VideosCollection(); + videosCollection.url = `https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=5&q=taskmaster&type=video&videoEmbeddable=true&key=${API_KEY}`; + videosCollection.fetch(); + return { + selectedVideo: videosCollection.models[0], + videos: videosCollection + } + }, + + setSelectedVideo: function(id) { + let allVideos = this.get('videos'); + console.log(allVideos); + let selectedVideo = allVideos.findWhere({id: id}); + console.log(selectedVideo); + console.log('setting selected video id'); + this.set('selectedVideo', selectedVideo); + }, + + +}); \ No newline at end of file diff --git a/models/VideoModel.js b/models/VideoModel.js new file mode 100644 index 00000000..0aacec7e --- /dev/null +++ b/models/VideoModel.js @@ -0,0 +1,9 @@ +let VideoModel = Backbone.Model.extend({ + defaults: { + id: '', + video_url: '', + thumbnail_url: '', + title: '', + description: '' + } +}); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..7dad80ca --- /dev/null +++ b/package-lock.json @@ -0,0 +1,164 @@ +{ + "name": "backbone-youtube", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "backbone-youtube", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "backbone": "^1.3.3", + "bootstrap": "^3.3.7", + "handlebars": "^4.0.11", + "jquery": "^3.2.1", + "underscore": "^1.8.3" + } + }, + "node_modules/backbone": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.4.1.tgz", + "integrity": "sha512-ADy1ztN074YkWbHi8ojJVFe3vAanO/lrzMGZWUClIP7oDD/Pjy2vrASraUP+2EVCfIiTtCW4FChVow01XneivA==", + "dependencies": { + "underscore": ">=1.8.3" + } + }, + "node_modules/bootstrap": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.4.1.tgz", + "integrity": "sha512-yN5oZVmRCwe5aKwzRj6736nSmKDX7pLYwsXiCj/EYmo16hODaBiT4En5btW/jhBF/seV+XMx3aYwukYC3A49DA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/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==", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/jquery": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz", + "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==" + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "node_modules/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==" + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uglify-js": { + "version": "3.15.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.3.tgz", + "integrity": "sha512-6iCVm2omGJbsu3JWac+p6kUiOpg3wFO2f8lIXjfEb8RrmLjzog1wTPMmwKB7swfzzqxj9YM+sGUM++u1qN4qJg==", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/underscore": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.2.tgz", + "integrity": "sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g==" + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + } + }, + "dependencies": { + "backbone": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.4.1.tgz", + "integrity": "sha512-ADy1ztN074YkWbHi8ojJVFe3vAanO/lrzMGZWUClIP7oDD/Pjy2vrASraUP+2EVCfIiTtCW4FChVow01XneivA==", + "requires": { + "underscore": ">=1.8.3" + } + }, + "bootstrap": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.4.1.tgz", + "integrity": "sha512-yN5oZVmRCwe5aKwzRj6736nSmKDX7pLYwsXiCj/EYmo16hODaBiT4En5btW/jhBF/seV+XMx3aYwukYC3A49DA==" + }, + "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.15.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.3.tgz", + "integrity": "sha512-6iCVm2omGJbsu3JWac+p6kUiOpg3wFO2f8lIXjfEb8RrmLjzog1wTPMmwKB7swfzzqxj9YM+sGUM++u1qN4qJg==", + "optional": true + }, + "underscore": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.2.tgz", + "integrity": "sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g==" + }, + "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..c598e4a5 --- /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": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/SkyeWulff/backbone-youtube.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/SkyeWulff/backbone-youtube/issues" + }, + "homepage": "https://github.com/SkyeWulff/backbone-youtube#readme", + "dependencies": { + "backbone": "^1.3.3", + "bootstrap": "^3.3.7", + "handlebars": "^4.0.11", + "jquery": "^3.2.1", + "underscore": "^1.8.3" + } +} diff --git a/style.css b/style.css new file mode 100644 index 00000000..19673543 --- /dev/null +++ b/style.css @@ -0,0 +1,15 @@ +.fitted { + width: 100%; +} + +.header { + text-align: center; +} + +.search-container { + margin-bottom: 3em; +} + +.video-options:hover { + cursor: pointer; +} diff --git a/views/AppView.js b/views/AppView.js new file mode 100644 index 00000000..b74fc0c3 --- /dev/null +++ b/views/AppView.js @@ -0,0 +1,53 @@ +let AppView = Backbone.View.extend({ + el: $('body'), + events: { + 'click #search': 'handleGetVideos', + 'click .video-option': 'handleSelectVideo' + }, + initialize: function() { + this.listenTo(this.model, 'change:selectedVideo', this.renderSelectedVideo); + this.listenTo(this.model.get('videos'), 'reset', this.handleVideoChange); + this.listenTo(this.model, 'change:videos', function() { + this.renderVideoOptions(); + this.model.set('selectedVideoId', this.model.get('videos').models[0]); + }); + + }, + + handleVideoChange: function() { + this.renderVideoOptions(); + this.model.set('selectedVideo', this.model.get('videos').models[0]); + }, + + handleGetVideos: function() { + let searchTerm = this.$('#search-bar').val().replace(' ', '+'); + this.model.get('videos').url = `https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=5&q=${searchTerm}&type=video&videoEmbeddable=true&key=${API_KEY}`; + this.model.get('videos').fetch({reset: true}); + }, + + handleSelectVideo: function(evt) { + let clickedVideoId = this.$(evt.target).data().id; + this.model.setSelectedVideo(clickedVideoId); + }, + + renderSelectedVideo: function() { + let selectedVideo = this.model.get('selectedVideo'); + let selectedVideoView = new SelectedVideoView({model: selectedVideo}); + this.$('.selected-video').empty(); + + this.$('.selected-video').append(selectedVideoView.render().el); + }, + + renderVideoOption: function(vid) { + let videoOptionView = new VideoOptionView({model: vid}); + this.$('.video-options').append(videoOptionView.render().el); + }, + + renderVideoOptions: function() { + this.$('.video-options').empty(); + this.model.get('videos').each(function(vid) { + this.renderVideoOption(vid); + }, this); + } + +}); \ No newline at end of file diff --git a/views/SelectedVideoView.js b/views/SelectedVideoView.js new file mode 100644 index 00000000..c50c4931 --- /dev/null +++ b/views/SelectedVideoView.js @@ -0,0 +1,11 @@ +let SelectedVideoView = Backbone.View.extend({ + className: 'video', + + template: Handlebars.compile($('#selected-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/VideoOptionView.js b/views/VideoOptionView.js new file mode 100644 index 00000000..67758a4f --- /dev/null +++ b/views/VideoOptionView.js @@ -0,0 +1,19 @@ +let VideoOptionView = Backbone.View.extend({ + className: 'video', + + events: { + 'click .player': 'handleSelectVideo' + }, + + handleSelectVideo: function() { + + }, + + template: Handlebars.compile($('#video-option-template').html()), + + render: function() { + this.$el.html(this.template(this.model.toJSON())); + return this; + } + +}); \ No newline at end of file