├── .floo ├── .jshintignore ├── .meteor ├── .gitignore ├── release ├── platforms ├── .finished-upgraders ├── .id ├── packages └── versions ├── .idea ├── .name ├── misc.xml ├── scopes │ └── scope_settings.xml ├── encodings.xml ├── vcs.xml └── jsLibraryMappings.xml ├── _.gitattributes ├── packages └── .gitignore ├── collections ├── messages.js └── decks.js ├── smart.json ├── _.travis.yml ├── .flooignore ├── private ├── Movies │ ├── Diehard.srt │ ├── Scarface.srt │ ├── Forrest_Gump.srt │ └── A_Few_Good_Men.srt └── stopwords.txt ├── .gitignore ├── client ├── timeshistorian │ ├── cards │ │ ├── white_card.html │ │ └── black_card.html │ ├── timesHistorianHand.html │ └── timeshistorian.html ├── cardsagainstsobriety │ ├── views │ │ ├── cards │ │ │ ├── black_card.html │ │ │ └── white_card.html │ │ ├── player-hand-view.html │ │ ├── game-board-view.html │ │ ├── playerHand.js │ │ └── gameBoard.js │ ├── cardsagainstsobriety.js │ └── cardsagainstsobriety.html ├── index.html ├── scoreboard │ ├── scoreboard.html │ └── scoreboard.js ├── chat │ ├── chat.html │ └── chat.js ├── layout │ └── layout.html ├── home │ ├── home.html │ └── home.js ├── stylesheets │ └── cardsagainstsobriety.css └── router.js ├── smart.lock ├── server ├── subscription.js ├── chat │ └── chat.js ├── fixtures.js ├── TimesHistorian.js └── blackCards.js ├── _.editorconfig ├── _.gitignore ├── _PRESS-RELEASE.md ├── README.md ├── _CONTRIBUTING.md ├── _.jshintrc └── _STYLE-GUIDE.md /.floo: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.jshintignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | Whiskey-and-Cake-Redux -------------------------------------------------------------------------------- /_.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /packages/.gitignore: -------------------------------------------------------------------------------- 1 | /streams 2 | -------------------------------------------------------------------------------- /.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@1.1.0.2 2 | -------------------------------------------------------------------------------- /.meteor/platforms: -------------------------------------------------------------------------------- 1 | server 2 | browser 3 | -------------------------------------------------------------------------------- /collections/messages.js: -------------------------------------------------------------------------------- 1 | Messages = new Meteor.Collection('Messages'); -------------------------------------------------------------------------------- /smart.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": { 3 | "streams": {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /_.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.8' 4 | - '0.10' 5 | -------------------------------------------------------------------------------- /.flooignore: -------------------------------------------------------------------------------- 1 | extern 2 | node_modules 3 | tmp 4 | vendor 5 | .idea/workspace.xml 6 | .idea/misc.xml 7 | -------------------------------------------------------------------------------- /private/Movies/Diehard.srt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eddolan/whiskey-and-cake/HEAD/private/Movies/Diehard.srt -------------------------------------------------------------------------------- /private/Movies/Scarface.srt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eddolan/whiskey-and-cake/HEAD/private/Movies/Scarface.srt -------------------------------------------------------------------------------- /private/Movies/Forrest_Gump.srt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eddolan/whiskey-and-cake/HEAD/private/Movies/Forrest_Gump.srt -------------------------------------------------------------------------------- /private/Movies/A_Few_Good_Men.srt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eddolan/whiskey-and-cake/HEAD/private/Movies/A_Few_Good_Men.srt -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/jsLibraryMappings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | bower_components/ 3 | *.log 4 | .DS_Store 5 | build/ 6 | dist/ 7 | .idea 8 | .idea/dbnavigator.xml 9 | .meteor 10 | .DS_Store 11 | .gitignore 12 | .meteor/packages 13 | client/.DS_Store 14 | client/moviecloud/.DS_Store 15 | -------------------------------------------------------------------------------- /client/timeshistorian/cards/white_card.html: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /.meteor/.finished-upgraders: -------------------------------------------------------------------------------- 1 | # This file contains information which helps Meteor properly upgrade your 2 | # app when you run 'meteor update'. You should check it into version control 3 | # with your project. 4 | 5 | notices-for-0.9.0 6 | notices-for-0.9.1 7 | 0.9.4-platform-file 8 | notices-for-facebook-graph-api-2 9 | -------------------------------------------------------------------------------- /client/timeshistorian/cards/black_card.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/cardsagainstsobriety/views/cards/black_card.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/cardsagainstsobriety/cardsagainstsobriety.js: -------------------------------------------------------------------------------- 1 | if (Meteor.isClient) { 2 | Accounts.ui.config({ 3 | passwordSignupFields: "USERNAME_ONLY" 4 | }) 5 | } 6 | 7 | //with autopublish turned off, subscribe functions are necessary to get what the server-side is 8 | //publishing 9 | Meteor.subscribe("CardsRoom"); 10 | 11 | 12 | -------------------------------------------------------------------------------- /client/cardsagainstsobriety/views/cards/white_card.html: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | CAS 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.meteor/.id: -------------------------------------------------------------------------------- 1 | # This file contains a token that is unique to your project. 2 | # Check it into your repository along with the rest of this directory. 3 | # It can be used for purposes such as: 4 | # - ensuring you don't accidentally deploy one app on top of another 5 | # - providing package authors with aggregated statistics 6 | 7 | 7lrj361jnwwykoraisu 8 | -------------------------------------------------------------------------------- /smart.lock: -------------------------------------------------------------------------------- 1 | { 2 | "meteor": {}, 3 | "dependencies": { 4 | "basePackages": { 5 | "streams": {} 6 | }, 7 | "packages": { 8 | "streams": { 9 | "git": "https://github.com/arunoda/meteor-streams.git", 10 | "tag": "v0.1.17", 11 | "commit": "9fcbbc9b7bf542827babc92089c19e730892a22f" 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server/subscription.js: -------------------------------------------------------------------------------- 1 | /** 2 | * All chat rooms are public so publish all of them 3 | */ 4 | Meteor.publish('allRooms', function() { 5 | return CardsRoom.find(); 6 | }); 7 | 8 | /** 9 | * Publish messages by roomId 10 | */ 11 | Meteor.publish('roomMessages', function (roomId) { 12 | return Messages.find({roomId: roomId}); 13 | }); 14 | /** 15 | * Allow messages to be added 16 | */ 17 | Messages.allow({ 18 | 'insert': function() { 19 | return true; 20 | } 21 | }); 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # Check this file (and the other files in this directory) into your repository. 3 | # 4 | # 'meteor add' and 'meteor remove' will edit this file for you, 5 | # but you can also edit it by hand. 6 | # insecure 7 | 8 | meteor-platform 9 | accounts-ui 10 | accounts-password 11 | twbs:bootstrap 12 | alanning:roles 13 | sanjo:jasmine 14 | velocity:html-reporter 15 | mizzao:user-status 16 | iron:router 17 | streams 18 | d3 19 | http 20 | underscore 21 | -------------------------------------------------------------------------------- /client/scoreboard/scoreboard.html: -------------------------------------------------------------------------------- 1 | 7 | 8 | 19 | 20 | -------------------------------------------------------------------------------- /client/timeshistorian/timesHistorianHand.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /client/chat/chat.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | 15 | 16 | -------------------------------------------------------------------------------- /client/scoreboard/scoreboard.js: -------------------------------------------------------------------------------- 1 | Template.leadeboard.helpers({ 2 | players: function(){ 3 | if (Session.get(Session.get('currentRoomId'))){ 4 | return Session.get(Session.get('currentRoomId')).scoreBoard; 5 | } 6 | return; 7 | }, 8 | 9 | lastWinner: function(){ 10 | if (Session.get(Session.get('currentRoomId'))){ 11 | return Session.get(Session.get('currentRoomId')).roundInfo.lastWinner; 12 | } 13 | return; 14 | }, 15 | 16 | roundNum: function(){ 17 | if (Session.get(Session.get('currentRoomId'))){ 18 | return Session.get(Session.get('currentRoomId')).roundInfo.roundNum; 19 | } 20 | return; 21 | } 22 | }); -------------------------------------------------------------------------------- /client/timeshistorian/timeshistorian.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | #.editorconfig 6 | # Meteor adapted EditorConfig, http://EditorConfig.org 7 | # By RaiX 2013 8 | 9 | root = true 10 | 11 | [*.js] 12 | end_of_line = lf 13 | insert_final_newline = true 14 | indent_style = space 15 | indent_size = 2 16 | trim_trailing_whitespace = true 17 | charset = utf-8 18 | max_line_length = 80 19 | indent_brace_style = 1TBS 20 | spaces_around_operators = true 21 | quote_type = auto 22 | # curly_bracket_next_line = true 23 | 24 | # We recommend you to keep these unchanged 25 | end_of_line = lf 26 | charset = utf-8 27 | trim_trailing_whitespace = true 28 | insert_final_newline = true 29 | 30 | [*.md] 31 | trim_trailing_whitespace = false 32 | 33 | 34 | -------------------------------------------------------------------------------- /_.gitignore: -------------------------------------------------------------------------------- 1 | ### node etc ### 2 | 3 | # Logs 4 | logs 5 | *.log 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # Compiled Dirs (http://nodejs.org/api/addons.html) 22 | build/ 23 | dist/ 24 | 25 | # Dependency directorys 26 | # Deployed apps should consider commenting these lines out: 27 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 28 | node_modules/ 29 | bower_components/ 30 | 31 | .idea/ 32 | 33 | 34 | 35 | .DS_Store 36 | .gitignore 37 | .meteor/packages 38 | client/.DS_Store 39 | client/moviecloud/.DS_Store 40 | -------------------------------------------------------------------------------- /client/layout/layout.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/cardsagainstsobriety/views/player-hand-view.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | -------------------------------------------------------------------------------- /client/cardsagainstsobriety/cardsagainstsobriety.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_PRESS-RELEASE.md: -------------------------------------------------------------------------------- 1 | # Cards Against Sobriety # 2 | 3 | ## Cards Against Humanity Redux ## 4 | Play Cards Against Humanity even if your deck or your friends aren't close by. 5 | All those against sobriety, preferably with something better to drink. 6 | 7 | ## Summary ## 8 | Cards against humanity without the humanity. Or apples to apples for grown-ups planning to get tipsy(read: no)/drunk/schwasted/totally blacked. Still no? Then here: the game is simple. Each round, one player asks a question from a Black Card, and everyone else answers with their funniest White Card. 9 | 10 | ## Problem ## 11 | So I hear you asking yourself, why not just play cards against humanity? Again, it's simple: you don't have the fucking deck, you don't have any friends, or both. 12 | 13 | ## Solution ## 14 | Hop on Cards Against Sobriety, play with your 'friends' while hanging out with your one true love(read: booze, duh), and have a damn drunk time. 15 | 16 | ## Quote from You ## 17 | "Fuck off, I'm playing Cards Against Sobriety." 18 | 19 | ## How to Get Started ## 20 | http://www.cardsagainstsobriety.meteor.com 21 | 22 | ## Customer Quote ## 23 | "I don't have to drink alone anymore!" 24 | 25 | ## Closing and Call to Action ## 26 | I'm impressed you read this far, or am I? Hint: I'm not. Go play the damn game. 27 | -------------------------------------------------------------------------------- /server/chat/chat.js: -------------------------------------------------------------------------------- 1 | chatStream = new Meteor.Stream('chat'); 2 | 3 | chatStream.permissions.write(function() { 4 | return true; 5 | }); 6 | 7 | chatStream.permissions.read(function() { 8 | return true; 9 | }); 10 | 11 | Meteor.methods({ 12 | getMessagesForRoom: function(roomId) { 13 | Messages.find({roomId: roomId}, function (err, room) { 14 | if (err) console.log('there was an error retrieving room chat messages: ', room); 15 | else if (!room) { 16 | return 'room does not exist'; 17 | } 18 | else { 19 | var begin = room.messages.length <= 100? 0: room.messages.length - 100; 20 | return room.messages.slice(begin, room.messages.length - 1); 21 | } 22 | }); 23 | }, 24 | addMessageForRoom: function(roomId, message) { 25 | //check if Messages already created for this room, create document if not 26 | //add message to document 27 | var newMessage = { 28 | createdById: (Meteor.user()._id), 29 | createdByName: (Meteor.user().username), 30 | createdAt: new Date(), 31 | message: message 32 | }; 33 | var status = Messages.update({roomId: roomId}, {$push: {messages: newMessage}}); 34 | console.log('status is ', status); 35 | if( status === 0 ) { 36 | //send back error message 37 | //chatStream.emit(roomId, 'testing testing testing ' + message); 38 | } 39 | } 40 | }); -------------------------------------------------------------------------------- /client/chat/chat.js: -------------------------------------------------------------------------------- 1 | var getId = function() { 2 | return Session.get('currentRoomId'); 3 | }; 4 | var roomId = getId() || null; 5 | 6 | Template.chatBox.helpers({ 7 | "messages": function() { 8 | var x = Session.get('currentRoomId'); 9 | var room = Messages.findOne({roomId: x}); 10 | scroll(); 11 | return room; 12 | } 13 | }); 14 | 15 | 16 | var subscribedUsers = {}; 17 | 18 | Template.chatMessage.helpers({ 19 | "user": function() { 20 | if(this.userId == 'me') { 21 | return "me"; 22 | } else if(this.userId) { 23 | var username = Session.get('user-' + this.userId); 24 | if(username) { 25 | return username; 26 | } else { 27 | getUsername(this.userId); 28 | } 29 | } else { 30 | return this.subscriptionId; 31 | } 32 | } 33 | }); 34 | 35 | Template.chatBox.events({ 36 | "click #send": function () { 37 | sendChat(); 38 | }, 39 | "keypress #chat-message": function(e) { 40 | if(e.which === 13 ) { 41 | sendChat(); 42 | } 43 | } 44 | }); 45 | 46 | function sendChat() { 47 | var message = $('#chat-message').val(); 48 | Meteor.call('addMessageForRoom', getId(), message, function(data) { 49 | //message data is returned via a server emit 50 | }); 51 | $('#chat-message').val(''); 52 | } 53 | 54 | function scroll() { 55 | setTimeout(function () { 56 | //force chat messages to scroll to bottom 57 | var chatBox = $('#messages'); 58 | var height = chatBox[0].scrollHeight; 59 | chatBox.scrollTop(height); 60 | }, 200); 61 | } 62 | 63 | 64 | function getUsername(id) { 65 | Meteor.subscribe('user-info', id); 66 | Deps.autorun(function() { 67 | var user = Meteor.users.findOne(id); 68 | if(user) { 69 | Session.set('user-' + id, user.username); 70 | } 71 | }); 72 | } -------------------------------------------------------------------------------- /.meteor/versions: -------------------------------------------------------------------------------- 1 | accounts-base@1.2.0 2 | accounts-password@1.1.1 3 | accounts-ui@1.1.5 4 | accounts-ui-unstyled@1.1.7 5 | alanning:roles@1.2.13 6 | amplify@1.0.0 7 | autoupdate@1.2.1 8 | base64@1.0.3 9 | binary-heap@1.0.3 10 | blaze@2.1.2 11 | blaze-tools@1.0.3 12 | boilerplate-generator@1.0.3 13 | callback-hook@1.0.3 14 | check@1.0.5 15 | coffeescript@1.0.6 16 | d3@1.0.0 17 | ddp@1.1.0 18 | deps@1.0.7 19 | ejson@1.0.6 20 | email@1.0.6 21 | fastclick@1.0.3 22 | geojson-utils@1.0.3 23 | html-tools@1.0.4 24 | htmljs@1.0.4 25 | http@1.1.0 26 | id-map@1.0.3 27 | iron:controller@1.0.7 28 | iron:core@1.0.7 29 | iron:dynamic-template@1.0.7 30 | iron:layout@1.0.7 31 | iron:location@1.0.7 32 | iron:middleware-stack@1.0.7 33 | iron:router@1.0.7 34 | iron:url@1.0.7 35 | jquery@1.11.3_2 36 | json@1.0.3 37 | launch-screen@1.0.2 38 | less@1.0.14 39 | livedata@1.0.13 40 | localstorage@1.0.3 41 | logging@1.0.7 42 | meteor@1.1.6 43 | meteor-platform@1.2.2 44 | minifiers@1.1.5 45 | minimongo@1.0.8 46 | mizzao:timesync@0.3.1 47 | mizzao:user-status@0.6.4 48 | mobile-status-bar@1.0.3 49 | mongo@1.1.0 50 | npm-bcrypt@0.7.8_2 51 | observe-sequence@1.0.6 52 | ordered-dict@1.0.3 53 | package-version-parser@3.0.3 54 | practicalmeteor:chai@1.9.2_3 55 | practicalmeteor:loglevel@1.1.0_3 56 | random@1.0.3 57 | reactive-dict@1.1.0 58 | reactive-var@1.0.5 59 | reload@1.1.3 60 | retry@1.0.3 61 | routepolicy@1.0.5 62 | sanjo:jasmine@0.13.3 63 | sanjo:karma@1.5.1 64 | sanjo:long-running-child-process@1.0.3 65 | sanjo:meteor-files-helpers@1.1.0_4 66 | sanjo:meteor-version@1.0.0 67 | service-configuration@1.0.4 68 | session@1.1.0 69 | sha@1.0.3 70 | spacebars@1.0.6 71 | spacebars-compiler@1.0.6 72 | srp@1.0.3 73 | streams@0.0.0 74 | templating@1.1.1 75 | tracker@1.0.7 76 | twbs:bootstrap@3.3.4 77 | ui@1.0.6 78 | underscore@1.0.3 79 | url@1.0.4 80 | velocity:chokidar@0.12.6_1 81 | velocity:core@0.6.1 82 | velocity:html-reporter@0.5.3 83 | velocity:meteor-internals@1.1.0_7 84 | velocity:meteor-stubs@1.0.3 85 | velocity:shim@0.1.0 86 | webapp@1.2.0 87 | webapp-hashing@1.0.3 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Stories in Ready](https://badge.waffle.io/FrogCircle/whiskey-and-cake.png?label=ready&title=Ready)](http://waffle.io/FrogCircle/whiskey-and-cake) 2 | # Cards Against Sobriety 3 | 4 | Play Cards Against Humanity & (obviously) get drunk, even if your deck or your friends aren't close by. 5 | 6 | ## Team 7 | 8 | - __Product Owner__: Kate Jefferson 9 | - __Scrum Master__: David Blanchard 10 | - __Development Team Members__: Sean Kim, John Games 11 | 12 | ## Table of Contents 13 | 14 | 1. [Usage](#Usage) 15 | 1. [Requirements](#requirements) 16 | 1. [Development](#development) 17 | 1. [Installing Dependencies](#installing-dependencies) 18 | 1. [Tasks](#tasks) 19 | 1. [Team](#team) 20 | 1. [Contributing](#contributing) 21 | 22 | ## Usage 23 | 24 | > BASIC RULES 25 | > To start the game, each player is dealt 10 cards and the motherfxckin' judge is randomly selected. The black card 26 | > of doom is displayed on the gameboard and everyone except the motherfxckin' judge plays the 'appropriate' white card. 27 | > For full effect, the motherfxckin' judge should read the white cards aloud, dramatically. Then she/he may choose 28 | > the winner, who gets a point. Then start a new round, and the judge will rotate. Play until you are drunk or out of alcohol. 29 | > Why's the rum gone? 30 | 31 | ## Requirements 32 | 33 | - Meteor 1.0.4.2 34 | 35 | ## Development 36 | 37 | ### Installing Dependencies 38 | 39 | From within the root directory: 40 | 41 | Install Meteor: curl https://install.meteor.com/ | sh 42 | 43 | Add dependencies: meteor add [following packages] 44 | 45 | meteor-platform, insecure, accounts-ui, accounts-password, twbs:bootstrap, 46 | sanjo:jasmine, velocity:html-reporter, mizzao:user-status 47 | 48 | ### Roadmap 49 | 50 | View the project roadmap [here](https://waffle.io/whiskey-and-cake/whiskey-and-cake-redux). 51 | View the game's original official rules [here](http://s3.amazonaws.com/cah/CAH_Rules.pdf). 52 | 53 | ## Contributing 54 | 55 | See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines. 56 | -------------------------------------------------------------------------------- /client/cardsagainstsobriety/views/game-board-view.html: -------------------------------------------------------------------------------- 1 | 84 | -------------------------------------------------------------------------------- /client/home/home.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/cardsagainstsobriety/views/playerHand.js: -------------------------------------------------------------------------------- 1 | var getId = function() { 2 | return Session.get('currentRoomId'); 3 | }; 4 | // Helper functions for player-hand-view.html 5 | Meteor.subscribe('CardsRoom'); 6 | 7 | Template.playerHand.helpers({ 8 | // 9 | playsHand: function(){ 10 | var user = Meteor.user(); 11 | var _roomId = Session.get('roomUrl') || getId(); 12 | // displays hand to user, filtered by username. 13 | var usersArray = CardsRoom.find({ '_id': _roomId}).fetch()[0].users; 14 | console.log('usersArray+++ is ', usersArray); 15 | var result; 16 | for( var i = 0; i < usersArray.length; i++ ) { 17 | if( usersArray[i]._id === user._id ) { 18 | result = usersArray[i].cards; 19 | } 20 | } 21 | //return PlayerHand.find({owner: user._id}); 22 | return result; 23 | } 24 | 25 | }); 26 | 27 | // player-hand-view.html template event listeners 28 | Template.playerHand.events({ 29 | 30 | // user clicks on a white card 31 | "click .playCard": function(){ 32 | var card = this; 33 | var user = Meteor.user(); 34 | var isJudge; 35 | var _roomId = Session.get('roomUrl') || getId(); 36 | var gameInformation = CardsRoom.findOne({_id: _roomId}, {users: 1}); // returns all users for that room 37 | var userArray = gameInformation.users; 38 | // if user is the judge, he cannot play a white card 39 | for( var i = 0, len = userArray.length; i < len; i++) { 40 | if ( userArray[i]._id === user._id ) { 41 | if( userArray[i].judge ) { 42 | isJudge = true; 43 | console.log('YOU DA JUDGE BRO, NO PLAYING CARDS'); 44 | return; 45 | } 46 | } 47 | } 48 | 49 | // each user can only play one white card per round 50 | //CardsRoom.find({_id: _roomId}).fetch()[0].GameBoard; 51 | var board = CardsRoom.find({_id: _roomId}).fetch()[0].GameBoard; 52 | for( var i = 0, len = board.length; i < len; i++ ) { 53 | if( board[i].owner === user._id ) { 54 | console.log("Yo, you've already played a card!"); 55 | return; 56 | } 57 | } 58 | 59 | // refer to decks.js for playCard function 60 | Meteor.call('playCard', card, _roomId, function(err, id) { 61 | console.log('card being played'); 62 | console.log('card is ', card); 63 | if (err) { 64 | throw err; 65 | } 66 | }); 67 | 68 | // refer to decks.js for drawWhite function 69 | Meteor.call('drawWhite', _roomId, function(err, id){ 70 | if(err){ 71 | throw err; 72 | } 73 | }); 74 | 75 | }, 76 | 77 | "click #clearBoard": function(){ 78 | // clear out white cards 79 | // redraw blackCard 80 | // choose new judge 81 | var _roomId = Session.get('roomUrl') || getId(); 82 | Meteor.call("drawBlack", _roomId, function(err, res){ 83 | if(err){ 84 | throw err; 85 | } else { 86 | console.log('Board Cleared'); 87 | } 88 | }) 89 | }, 90 | 91 | "click #dealHand": function(){ 92 | var user = Meteor.user(); 93 | var _roomId = Session.get('roomUrl') || getId(); 94 | console.log(user, 'this is the user'); 95 | var gameInformation = CardsRoom.findOne({_id: _roomId}, {users: 1}); // returns all users for that room 96 | var userArray = gameInformation.users; 97 | console.log('____++++++userArray is ', userArray); 98 | var numHandCards; 99 | for ( var i = 0, size = userArray.length; i < size; i++) { 100 | if ( userArray[i]._id === user._id ) { 101 | numHandCards = userArray[i].cards === undefined? 0: userArray[i].cards.length; 102 | if( numHandCards >= 10 ){ 103 | console.log('You already have ', numHandCards, ' why not try using them?'); 104 | //return; 105 | } 106 | } 107 | } 108 | 109 | // refer to decks.js for dealHand function 110 | Meteor.call("dealHand", _roomId, function(err, res){ 111 | console.log('called Meteor.call dealHand'); 112 | if(err){ 113 | throw err; 114 | } else { 115 | //console.log('Hand Dealt'); 116 | //console.log('Result object - ', res); 117 | } 118 | }); 119 | 120 | // refer to decks.js for drawBlack function 121 | Meteor.call("drawBlack", _roomId, function(err, res){ 122 | if(err){ 123 | throw err; 124 | } else { 125 | console.log('drawBlack called'); 126 | } 127 | }) 128 | } 129 | 130 | }); 131 | 132 | 133 | -------------------------------------------------------------------------------- /server/fixtures.js: -------------------------------------------------------------------------------- 1 | //Cmd line if meteor running on port 3000 already. 2 | //kill -9 `ps ax | grep node | grep meteor | awk '{print $1}'` 3 | /** 4 | /* DECK INSTANTIATION */ 5 | 6 | Meteor.methods({ 7 | CreateCardsRoom: function(roomName) { 8 | console.log('roomName is ', roomName); 9 | var WhiteDeck = []; 10 | var BlackDeck = []; 11 | // in-place shuffle algorithm for BlackCards 12 | // cards are shuffled prior to instantiating the database 13 | for (var i = 0; i < BlackCards.length; i++) { 14 | var j = Math.floor(Math.random() * i); 15 | var hole = BlackCards[i]; 16 | BlackCards[i] = BlackCards[j]; 17 | BlackCards[j] = hole; 18 | } 19 | 20 | // instantiate databases with shuffled data 21 | for (var i = 0; i < BlackCards.length; i++) { 22 | BlackDeck.push({ 23 | text: BlackCards[i]["text"], 24 | expansion: BlackCards[i]["expansion"], 25 | no: i 26 | }); 27 | } 28 | 29 | for (var i = 0; i < WhiteCards.length; i++) { 30 | WhiteDeck.push({ 31 | text: WhiteCards[i]["text"], 32 | expansion: WhiteCards[i]["expansion"] 33 | }); 34 | } 35 | //var returnRoom = {}; 36 | var returnRoom = CardsRoom.insert({ 37 | createdBy: (Meteor.userId()), 38 | createdAt: new Date(), 39 | roomName: roomName, 40 | WhiteDeck: WhiteDeck, 41 | BlackDeck: BlackDeck, 42 | RoundInfo: {}, 43 | PlayerHand: [], 44 | GameBoard: [], 45 | users: [] 46 | }, function(err, roomInserted) { 47 | console.log('roomInserted is ', roomInserted); 48 | returnRoom = roomInserted; 49 | }); 50 | Messages.insert({ 51 | createdById: (Meteor.user()._id), 52 | createdByName: (Meteor.user().username), 53 | createdAt: new Date(), 54 | roomId: returnRoom, 55 | messages: [] 56 | }, function (err, messageInserted) { 57 | console.log('messageInserted is ', messageInserted); 58 | if( err ) { 59 | console.log('error while creating doc in messages collection'); 60 | } 61 | }); 62 | 63 | console.log('returnRoom is ', returnRoom); 64 | return {room: returnRoom}; 65 | }, 66 | 67 | JoinCardsRoom: function(roomId, userObj) { 68 | var gameInfo = CardsRoom.findOne({_id: roomId}); 69 | console.log('Join cards room called'); 70 | var userArray = gameInfo.users; 71 | var userFound = false; 72 | for( var i = 0, len = userArray.length; i < len; i++ ) { 73 | if ( userArray[i]._id === userObj._id ) { 74 | console.log('UserFound', userFound); 75 | userFound = true; 76 | } 77 | } 78 | if( !userFound ) { 79 | CardsRoom.update({_id: roomId}, {$push: {'users': userObj }}); 80 | } 81 | return CardsRoom.find({_id: roomId}).fetch(); 82 | }, 83 | deleteRoom: function(roomId, userId, collection){ 84 | //check if user has rights to delete this room, i.e. created the room 85 | var room = collectionNames[collection].findOne({_id: roomId}); 86 | var roomOwner = room.createdBy; 87 | if( roomOwner === userId ) { 88 | var removeRoomCheck = collectionNames[collection].remove({_id: roomId}); 89 | var removeMessagesCheck = Messages.remove({roomId: roomId}); 90 | } 91 | } 92 | }); 93 | 94 | // fields added to Meteor.user on instantiation 95 | Accounts.onCreateUser(function(options, user) { 96 | user.score = 0; 97 | user.judge = false; 98 | return user; 99 | }); 100 | 101 | /* PUBLISHING */ 102 | 103 | Meteor.publish("CardsRoom", function() { 104 | return CardsRoom.find(); 105 | }); 106 | Meteor.publish("WhiteDeck", function(roomID) { 107 | return CardsRoom.find({}, {WhiteDeck: 1}); 108 | }); 109 | Meteor.publish("BlackDeck", function() { 110 | return CardsRoom.find({}, {BlackDeck: 1}); 111 | }); 112 | Meteor.publish("User", function() { 113 | return CardsRoom.find({}, {User: 1}); 114 | }); 115 | Meteor.publish("GameBoard", function() { 116 | return CardsRoom.find({}, {GameBoard: 1}); 117 | }); 118 | Meteor.publish("RoundInfo", function() { 119 | return CardsRoom.find({}, {RoundInfo: 1}); 120 | }); 121 | Meteor.publish("user-info", function(id) { 122 | return Meteor.users.find({_id: id}, {fields: {username: 1}}); 123 | }); 124 | Meteor.publish("MovieRooms", function() { 125 | return MovieRooms.find(); 126 | }); 127 | Meteor.publish("TimesHistorianRoom", function() { 128 | return TimesHistorianRoom.find(); 129 | }); 130 | 131 | var collectionNames = { 132 | 'TH': TimesHistorianRoom, 133 | 'MC': MovieRooms, 134 | 'CAS': CardsRoom 135 | } -------------------------------------------------------------------------------- /client/stylesheets/cardsagainstsobriety.css: -------------------------------------------------------------------------------- 1 | /* CSS declarations go here */ 2 | 3 | .container { 4 | width: 940px; 5 | margin: 0 auto; 6 | } 7 | 8 | .playerButton { 9 | background-color: white; 10 | border: 3px solid black; 11 | border-radius: 7px; 12 | padding: 5px 15px; 13 | box-shadow: 3px 3px; 14 | } 15 | 16 | .playerButton:focus { 17 | outline: 0; 18 | } 19 | 20 | .playerButton:active { 21 | box-shadow: none; 22 | transform: translate(3px, 3px); 23 | } 24 | 25 | .footer { 26 | padding-top: 15px; 27 | font-family: 'Pinyon Script', cursive; 28 | font-size: 27px; 29 | } 30 | 31 | /****** Navbar Group ******/ 32 | .navbar { 33 | margin-bottom: 0; 34 | } 35 | 36 | .navbar-brand { 37 | color: white; 38 | font-family: 'Miss Fajardose', cursive; 39 | font-size: 7em; 40 | overflow: hidden; 41 | text-overflow: clip; 42 | line-height: 75px; 43 | height: auto; 44 | } 45 | 46 | a:hover, a:focus { 47 | color: dodgerblue; 48 | outline: 0; 49 | } 50 | 51 | header { 52 | background-color: black; 53 | } 54 | 55 | #login-buttons a { 56 | background-color: black; 57 | text-decoration: none; 58 | color: white; 59 | } 60 | 61 | .btn-default, .btn-default:hover, .btn-default:active, .btn-default:visited { 62 | background-color: black; 63 | outline: 0; 64 | } 65 | 66 | .btn { 67 | position: relative; 68 | top: 50%; 69 | transform: translateY(25%); 70 | border-radius: 0; 71 | border: none; 72 | } 73 | 74 | .btn:focus, .btn:active { 75 | outline: 0; 76 | } 77 | 78 | .accounts-dialog label { 79 | display: block; 80 | } 81 | 82 | #login-dropdown-list { 83 | background-color: black; 84 | color: white; 85 | } 86 | 87 | #login-buttons .login-button:hover, .accounts-dialog .login-button:hover { 88 | background-color: white; 89 | color: black; 90 | } 91 | 92 | #login-buttons .login-button, .accounts-dialog .login-button { 93 | background-color: black; 94 | color: white; 95 | } 96 | 97 | .btn-default, .btn-default:hover, .btn-default:active, .btn-default:focus { 98 | background-color: black; 99 | } 100 | 101 | #login-username-label, #login-password-label { 102 | color: white; 103 | } 104 | 105 | #login-username, #login-password, #login-password-again { 106 | color: black; 107 | } 108 | 109 | #login-dropdown-list { 110 | left: -175px; 111 | top: 40px; 112 | } 113 | 114 | /*** End of Navbar Group ***/ 115 | 116 | 117 | /******* Card Group ********/ 118 | .card { 119 | background-color: black; 120 | color: white; 121 | width: 125px; 122 | height: 185px; 123 | border-radius: 3px; 124 | display: table-cell; 125 | } 126 | 127 | .white { 128 | background-color: white; 129 | color: black; 130 | width: 125px; 131 | height: 185px; 132 | border: 1px solid black; 133 | border-radius: 3px; 134 | } 135 | 136 | .card-content { 137 | width: 125px; 138 | height: 185px; 139 | position: relative; 140 | border-radius: 3px; 141 | top: 50%; 142 | transform: translateY(-50%); 143 | display: inline-block; 144 | padding: 5px; 145 | } 146 | 147 | .card-text { 148 | font-family: Tahoma, Geneva, sans-serif; 149 | font-weight: 700; 150 | font-size: 12px; 151 | text-align: left; 152 | display: inline-block; 153 | } 154 | 155 | .expansion { 156 | font-size: 7px; 157 | text-align: left; 158 | display: block; 159 | position: absolute; 160 | bottom: 0; 161 | } 162 | /**** End of Card Group ****/ 163 | 164 | 165 | /***** Gameboard Group *****/ 166 | .bs-docs-header { 167 | margin-bottom: 25px; 168 | } 169 | 170 | .answerCards { 171 | background-color: white; 172 | color: black; 173 | width: 125px; 174 | height: 185px; 175 | border-radius: 3px; 176 | display: inline-block; 177 | margin-right: 2px; 178 | } 179 | 180 | .pickWinner { 181 | border: 1px solid black; 182 | background-color: white; 183 | } 184 | 185 | .questionCard { 186 | clear: left; 187 | display: inline-block; 188 | } 189 | 190 | .playerBoard { 191 | float: left; 192 | } 193 | 194 | .roundCounter { 195 | float: right; 196 | border: 1px solid black; 197 | padding: 5px 10px; 198 | background-color: #eaeaea; 199 | -webkit-border-radius: 5px; 200 | -moz-border-radius: 5px; 201 | border-radius: 5px; 202 | } 203 | /* End of Gameboard Group **/ 204 | 205 | 206 | /**** Playerhand Group *****/ 207 | .bs-docs-container#player { 208 | background-color: black; 209 | color: white; 210 | padding-bottom: 30px; 211 | } 212 | 213 | .text { 214 | text-align: center; 215 | } 216 | 217 | .textarea { 218 | width: 250px; 219 | } 220 | 221 | .playCard { 222 | display: inline-block; 223 | } 224 | 225 | #dealHand, #clearBoard { 226 | background-color: black; 227 | border: 3px solid white; 228 | box-shadow: 3px 3px white; 229 | width: 7em; 230 | margin-right: 5px; 231 | } 232 | 233 | #dealHand:active, #clearBoard:active { 234 | box-shadow: none; 235 | } 236 | /* End of Playerhand Group */ 237 | 238 | #messages { 239 | height: 300px; 240 | overflow-y: scroll; 241 | scrollbar-face-color: #336699; scrollbar-3dlight-color: #336699; scrollbar-base-color: #336699; 242 | scrollbar-track-color: #336699; scrollbar-darkshadow-color: #000; scrollbar-arrow-color: #000; 243 | scrollbar-shadow-color: #fff; scrollbar-highlight-color: #fff; 244 | background-color: #eaeaea; 245 | padding: 3px 0; 246 | margin-bottom: 5px; 247 | } 248 | 249 | .chat-user { 250 | display: inline-table; 251 | width: 22%; 252 | text-align: right; 253 | padding: 0 5px 0 0; 254 | } 255 | 256 | .chat-message { 257 | display: inline-table; 258 | width: 75%; 259 | text-align: left; 260 | padding: 0 0 0 5px; 261 | } 262 | 263 | .create-room-name { 264 | display: none; 265 | } 266 | 267 | .rooms-list { 268 | list-style-type: none; 269 | padding-left: 10px; 270 | } 271 | 272 | .delete-room-div { 273 | display: inline-block; 274 | float: right; 275 | } 276 | 277 | .delete-room-link { 278 | font-size: 0.9em; 279 | font-style: italic; 280 | } -------------------------------------------------------------------------------- /client/home/home.js: -------------------------------------------------------------------------------- 1 | Template.home.helpers({ 2 | loadCardRooms: function(){ 3 | var array = CardsRoom.find().fetch(); 4 | var result = []; 5 | for (var i = 0, size = array.length; i < size; i++) { 6 | result.push({room: {room: array[i]._id, roomName: array[i].roomName, owner: array[i].createdBy}}); 7 | } 8 | return result.reverse(); 9 | }, 10 | roomOwner: function(){ 11 | if( this.room.owner === Meteor.user()._id ) { 12 | return true; 13 | } else { 14 | return false; 15 | } 16 | }, 17 | loadMovieRooms: function(){ 18 | var movieRooms = MovieRooms.find().fetch(); 19 | var result = movieRooms.map(function(elem, index){ 20 | return {room: {room: elem._id, roomName: elem.roomName, owner: elem.createdBy}}; 21 | }).reverse(); 22 | return result; 23 | }, 24 | loadTimesRooms: function(){ 25 | var TimesHistorianRooms = TimesHistorianRoom.find().fetch(); 26 | var result = TimesHistorianRooms.map(function(elem, index){ 27 | return {room: {room: elem._id, roomName: elem.roomName, owner: elem.createdBy}}; 28 | }).reverse(); 29 | return result; 30 | 31 | } 32 | }); 33 | 34 | var holder = []; 35 | // player-hand-view.html template event listeners 36 | Template.home.events({ 37 | "click #createRoomName": function(e){ 38 | //hide createRoomName button and show createRoomDiv 39 | var $this = $(e.target); 40 | $this.closest('.create-room-btn').css('display', 'none'); 41 | $this.parent().parent().find('.create-room-name').css('display', 'block'); 42 | 43 | }, 44 | "click .cancel": function(e) { 45 | var $this = $(e.target); 46 | $this.closest('.well').find('.create-room-btn').css('display', 'block'); 47 | $this.parent().parent().find('.create-room-name').css('display', 'none'); 48 | 49 | }, 50 | 51 | "click #createCardsRoom": function(e){ 52 | var $this = $(e.target); 53 | var newRoomName = $('#cardsRoomName').val(); 54 | $('#cardsRoomName').val('aa'); 55 | if( newRoomName.length === 0 ) { 56 | alert('Your room needs a name, come on!'); 57 | } else { 58 | $this.closest('.well').find('.create-room-btn').css('display', 'block'); 59 | $this.closest('.well').find('.create-room-name').css('display', 'none'); 60 | Meteor.call('CreateCardsRoom', newRoomName, function(error, room){ 61 | holder.push(room); 62 | Session.set('roomId', holder); 63 | }); 64 | //show createRoomButton 65 | $this.parent().parent().find('.create-room-btn').css('display', 'block'); 66 | $this.parent().parent().find('.create-room-name').css('display', 'none'); 67 | } 68 | }, 69 | 70 | "click #createMovieRoom": function(e){ 71 | var userId = Meteor.user()._id; 72 | var username = Meteor.user().username; 73 | var $this = $(e.target); 74 | var newRoomName = $('#movieRoomName').val(); 75 | if( newRoomName.length === 0 ) { 76 | alert('Your room needs a name, come on!'); 77 | } else { 78 | MovieRooms.insert({ 79 | "users": [], 80 | "scoreBoard": [], 81 | "answered": false, 82 | "gameBoard": {"result": [], "choices": [], "chosen": ""}, 83 | "roundInfo": {"roundNum": 0, "lastWinner": ""}, 84 | "createdAt": new Date(), 85 | "createdBy": userId, 86 | "roomName" : newRoomName 87 | }, function(err, roomInserted) { 88 | Messages.insert({ 89 | createdById: userId, 90 | createdByName: username, 91 | createdAt: new Date(), 92 | roomId: roomInserted, 93 | messages: [] 94 | }, function (err, messageInserted) { 95 | if( err ) { 96 | console.log('error while creating doc in messages collection'); 97 | } 98 | }); 99 | }); 100 | $this.parent().parent().find('.create-room-btn').css('display', 'block'); 101 | $this.parent().parent().find('.create-room-name').css('display', 'none'); 102 | } 103 | }, 104 | 105 | "click #createTimesRoom": function(e){ 106 | var userId = Meteor.user()._id; 107 | var username = Meteor.user().username; 108 | var $this = $(e.target); 109 | var newRoomName = $('#timesRoomName').val(); 110 | if( newRoomName.length === 0 ) { 111 | alert('Your room needs a name, come on!'); 112 | } else { 113 | TimesHistorianRoom.insert({ 114 | "users": [], 115 | "scoreBoard": [], 116 | "answered": false, 117 | "gameBoard": {"result": [], "choices": [], "chosen": ""}, 118 | "roundInfo": {"roundNum": 0, "lastWinner": ""}, 119 | "createdAt": new Date(), 120 | "createdBy": userId, 121 | "roomName" : newRoomName 122 | }, function(err, roomInserted) { 123 | Messages.insert({ 124 | createdById: userId, 125 | createdByName: username, 126 | createdAt: new Date(), 127 | roomId: roomInserted, 128 | messages: [] 129 | }, function (err, messageInserted) { 130 | if( err ) { 131 | console.log('error while creating doc in messages collection'); 132 | } 133 | }); 134 | }); 135 | $this.parent().parent().find('.create-room-btn').css('display', 'block'); 136 | $this.parent().parent().find('.create-room-name').css('display', 'none'); 137 | } 138 | }, 139 | 140 | "click .joinNewRoom": function() { 141 | var userObj = Meteor.user(); 142 | userObj.judge = false; 143 | userObj.cards = []; 144 | var roomId = this.room; 145 | Meteor.call('JoinCardsRoom', roomId, userObj, function(error, result) { 146 | }); 147 | }, 148 | "click .joinExistingRoom": function() { 149 | var userObj = Meteor.user(); 150 | userObj.judge = false; 151 | var roomId = this.room.room; 152 | Session.set('roomUrl', roomId); 153 | Meteor.call('JoinCardsRoom', roomId, userObj, function(error, result) { 154 | }); 155 | }, 156 | "click .delete-room": function(e) { 157 | var checkDelete = confirm('Are you sure you want to delete this room?'); 158 | if ( checkDelete ) { 159 | var roomId = $(this)[0].room.room; 160 | var userId = Meteor.user()._id; 161 | Meteor.call('deleteRoom', roomId, userId, e.target.name); 162 | } 163 | } 164 | 165 | }); 166 | 167 | 168 | -------------------------------------------------------------------------------- /private/stopwords.txt: -------------------------------------------------------------------------------- 1 | a 2 | as 3 | able 4 | about 5 | above 6 | according 7 | accordingly 8 | across 9 | actually 10 | after 11 | afterwards 12 | again 13 | against 14 | aint 15 | all 16 | allow 17 | allows 18 | almost 19 | alone 20 | along 21 | already 22 | also 23 | although 24 | always 25 | am 26 | among 27 | amongst 28 | an 29 | and 30 | another 31 | any 32 | anybody 33 | anyhow 34 | anyone 35 | anything 36 | anyway 37 | anyways 38 | anywhere 39 | apart 40 | appear 41 | appreciate 42 | appropriate 43 | are 44 | arent 45 | around 46 | as 47 | aside 48 | ask 49 | asking 50 | associated 51 | at 52 | available 53 | away 54 | awfully 55 | b 56 | be 57 | became 58 | because 59 | become 60 | becomes 61 | becoming 62 | been 63 | before 64 | beforehand 65 | behind 66 | being 67 | believe 68 | below 69 | beside 70 | besides 71 | best 72 | better 73 | between 74 | beyond 75 | both 76 | brief 77 | but 78 | by 79 | c 80 | cmon 81 | cs 82 | came 83 | can 84 | cant 85 | cannot 86 | cant 87 | cause 88 | causes 89 | certain 90 | certainly 91 | changes 92 | clearly 93 | co 94 | com 95 | come 96 | comes 97 | concerning 98 | consequently 99 | consider 100 | considering 101 | contain 102 | containing 103 | contains 104 | corresponding 105 | could 106 | couldnt 107 | course 108 | currently 109 | d 110 | definitely 111 | described 112 | despite 113 | did 114 | didnt 115 | different 116 | do 117 | does 118 | doesnt 119 | doing 120 | dont 121 | done 122 | down 123 | downwards 124 | during 125 | e 126 | each 127 | edu 128 | eg 129 | eight 130 | either 131 | else 132 | elsewhere 133 | enough 134 | entirely 135 | especially 136 | et 137 | etc 138 | even 139 | ever 140 | every 141 | everybody 142 | everyone 143 | everything 144 | everywhere 145 | ex 146 | exactly 147 | example 148 | except 149 | f 150 | far 151 | few 152 | fifth 153 | first 154 | five 155 | followed 156 | following 157 | follows 158 | for 159 | former 160 | formerly 161 | forth 162 | four 163 | from 164 | further 165 | furthermore 166 | g 167 | get 168 | gets 169 | getting 170 | given 171 | gives 172 | go 173 | goes 174 | going 175 | gone 176 | got 177 | gotten 178 | greetings 179 | h 180 | had 181 | hadnt 182 | happens 183 | hardly 184 | has 185 | hasnt 186 | have 187 | havent 188 | having 189 | he 190 | hes 191 | hello 192 | help 193 | hence 194 | her 195 | here 196 | heres 197 | hereafter 198 | hereby 199 | herein 200 | hereupon 201 | hers 202 | herself 203 | hi 204 | him 205 | himself 206 | his 207 | hither 208 | hopefully 209 | how 210 | howbeit 211 | however 212 | i 213 | id 214 | ill 215 | im 216 | ive 217 | ie 218 | if 219 | ignored 220 | immediate 221 | in 222 | inasmuch 223 | inc 224 | indeed 225 | indicate 226 | indicated 227 | indicates 228 | inner 229 | insofar 230 | instead 231 | into 232 | inward 233 | is 234 | isnt 235 | it 236 | itd 237 | itll 238 | its 239 | its 240 | itself 241 | j 242 | just 243 | k 244 | keep 245 | keeps 246 | kept 247 | know 248 | knows 249 | known 250 | l 251 | last 252 | lately 253 | later 254 | latter 255 | latterly 256 | least 257 | less 258 | lest 259 | let 260 | lets 261 | like 262 | liked 263 | likely 264 | little 265 | look 266 | looking 267 | looks 268 | ltd 269 | m 270 | mainly 271 | many 272 | may 273 | maybe 274 | me 275 | mean 276 | meanwhile 277 | merely 278 | might 279 | more 280 | moreover 281 | most 282 | mostly 283 | much 284 | must 285 | my 286 | myself 287 | n 288 | name 289 | namely 290 | nd 291 | near 292 | nearly 293 | necessary 294 | need 295 | needs 296 | neither 297 | never 298 | nevertheless 299 | new 300 | next 301 | nine 302 | no 303 | nobody 304 | non 305 | none 306 | noone 307 | nor 308 | normally 309 | not 310 | nothing 311 | novel 312 | now 313 | nowhere 314 | o 315 | obviously 316 | of 317 | off 318 | often 319 | oh 320 | ok 321 | okay 322 | old 323 | on 324 | once 325 | one 326 | ones 327 | only 328 | onto 329 | or 330 | other 331 | others 332 | otherwise 333 | ought 334 | our 335 | ours 336 | ourselves 337 | out 338 | outside 339 | over 340 | overall 341 | own 342 | p 343 | particular 344 | particularly 345 | per 346 | perhaps 347 | placed 348 | please 349 | plus 350 | possible 351 | presumably 352 | probably 353 | provides 354 | q 355 | que 356 | quite 357 | qv 358 | r 359 | rather 360 | rd 361 | re 362 | really 363 | reasonably 364 | regarding 365 | regardless 366 | regards 367 | relatively 368 | respectively 369 | right 370 | s 371 | said 372 | same 373 | saw 374 | say 375 | saying 376 | says 377 | second 378 | secondly 379 | see 380 | seeing 381 | seem 382 | seemed 383 | seeming 384 | seems 385 | seen 386 | self 387 | selves 388 | sensible 389 | sent 390 | serious 391 | seriously 392 | seven 393 | several 394 | shall 395 | she 396 | should 397 | shouldnt 398 | since 399 | six 400 | so 401 | some 402 | somebody 403 | somehow 404 | someone 405 | something 406 | sometime 407 | sometimes 408 | somewhat 409 | somewhere 410 | soon 411 | sorry 412 | specified 413 | specify 414 | specifying 415 | still 416 | sub 417 | such 418 | sup 419 | sure 420 | t 421 | ts 422 | take 423 | taken 424 | tell 425 | tends 426 | th 427 | than 428 | thank 429 | thanks 430 | thanx 431 | that 432 | thats 433 | thats 434 | the 435 | their 436 | theirs 437 | them 438 | themselves 439 | then 440 | thence 441 | there 442 | theres 443 | thereafter 444 | thereby 445 | therefore 446 | therein 447 | theres 448 | thereupon 449 | these 450 | they 451 | theyd 452 | theyll 453 | theyre 454 | theyve 455 | think 456 | third 457 | this 458 | thorough 459 | thoroughly 460 | those 461 | though 462 | three 463 | through 464 | throughout 465 | thru 466 | thus 467 | to 468 | together 469 | too 470 | took 471 | toward 472 | towards 473 | tried 474 | tries 475 | truly 476 | try 477 | trying 478 | twice 479 | two 480 | u 481 | un 482 | under 483 | unfortunately 484 | unless 485 | unlikely 486 | until 487 | unto 488 | up 489 | upon 490 | us 491 | use 492 | used 493 | useful 494 | uses 495 | using 496 | usually 497 | uucp 498 | v 499 | value 500 | various 501 | very 502 | via 503 | viz 504 | vs 505 | w 506 | want 507 | wants 508 | was 509 | wasnt 510 | way 511 | we 512 | wed 513 | well 514 | were 515 | weve 516 | welcome 517 | well 518 | went 519 | were 520 | werent 521 | what 522 | whats 523 | whatever 524 | when 525 | whence 526 | whenever 527 | where 528 | wheres 529 | whereafter 530 | whereas 531 | whereby 532 | wherein 533 | whereupon 534 | wherever 535 | whether 536 | which 537 | while 538 | whither 539 | who 540 | whos 541 | whoever 542 | whole 543 | whom 544 | whose 545 | why 546 | will 547 | willing 548 | wish 549 | with 550 | within 551 | without 552 | wont 553 | wonder 554 | would 555 | would 556 | wouldnt 557 | x 558 | y 559 | yes 560 | yet 561 | you 562 | youd 563 | youll 564 | youre 565 | youve 566 | your 567 | yours 568 | yourself 569 | yourselves 570 | z 571 | zero 572 | yeah -------------------------------------------------------------------------------- /_CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## General Workflow 4 | 5 | 1. Fork the repo 6 | 1. Cut a namespaced feature branch from master 7 | - bug/... 8 | - feat/... 9 | - test/... 10 | - doc/... 11 | - refactor/... 12 | 1. Make commits to your feature branch. Prefix each commit like so: 13 | - (feat) Added a new feature 14 | - (fix) Fixed inconsistent tests [Fixes #0] 15 | - (refactor) ... 16 | - (cleanup) ... 17 | - (test) ... 18 | - (doc) ... 19 | 1. When you've finished with your fix or feature, Rebase upstream changes into your branch. submit a [pull request][] 20 | directly to master. Include a description of your changes. 21 | 1. Your pull request will be reviewed by another maintainer. The point of code 22 | reviews is to help keep the codebase clean and of high quality and, equally 23 | as important, to help you grow as a programmer. If your code reviewer 24 | requests you make a change you don't understand, ask them why. 25 | 1. Fix any issues raised by your code reviwer, and push your fixes as a single 26 | new commit. 27 | 1. Once the pull request has been reviewed, it will be merged by another member of the team. Do not merge your own commits. 28 | 29 | ## Detailed Workflow 30 | 31 | ### Fork the repo 32 | 33 | Use github�s interface to make a fork of the repo, then add that repo as an upstream remote: 34 | 35 | ``` 36 | git remote add upstream https://github.com/hackreactor-labs/.git 37 | ``` 38 | 39 | ### Cut a namespaced feature branch from master 40 | 41 | Your branch should follow this naming convention: 42 | - bug/... 43 | - feat/... 44 | - test/... 45 | - doc/... 46 | - refactor/... 47 | 48 | These commands will help you do this: 49 | 50 | ``` bash 51 | 52 | # Creates your branch and brings you there 53 | git checkout -b `your-branch-name` 54 | ``` 55 | 56 | ### Make commits to your feature branch. 57 | 58 | Prefix each commit like so 59 | - (feat) Added a new feature 60 | - (fix) Fixed inconsistent tests [Fixes #0] 61 | - (refactor) ... 62 | - (cleanup) ... 63 | - (test) ... 64 | - (doc) ... 65 | 66 | Make changes and commits on your branch, and make sure that you 67 | only make changes that are relevant to this branch. If you find 68 | yourself making unrelated changes, make a new branch for those 69 | changes. 70 | 71 | #### Commit Message Guidelines 72 | 73 | - Commit messages should be written in the present tense; e.g. "Fix continuous 74 | integration script". 75 | - The first line of your commit message should be a brief summary of what the 76 | commit changes. Aim for about 70 characters max. Remember: This is a summary, 77 | not a detailed description of everything that changed. 78 | - If you want to explain the commit in more depth, following the first line should 79 | be a blank line and then a more detailed description of the commit. This can be 80 | as detailed as you want, so dig into details here and keep the first line short. 81 | 82 | ### Rebase upstream changes into your branch 83 | 84 | Once you are done making changes, you can begin the process of getting 85 | your code merged into the main repo. Step 1 is to rebase upstream 86 | changes to the master branch into yours by running this command 87 | from your branch: 88 | 89 | ```bash 90 | git pull --rebase upstream master 91 | ``` 92 | 93 | This will start the rebase process. You must commit all of your changes 94 | before doing this. If there are no conflicts, this should just roll all 95 | of your changes back on top of the changes from upstream, leading to a 96 | nice, clean, linear commit history. 97 | 98 | If there are conflicting changes, git will start yelling at you part way 99 | through the rebasing process. Git will pause rebasing to allow you to sort 100 | out the conflicts. You do this the same way you solve merge conflicts, 101 | by checking all of the files git says have been changed in both histories 102 | and picking the versions you want. Be aware that these changes will show 103 | up in your pull request, so try and incorporate upstream changes as much 104 | as possible. 105 | 106 | You pick a file by `git add`ing it - you do not make commits during a 107 | rebase. 108 | 109 | Once you are done fixing conflicts for a specific commit, run: 110 | 111 | ```bash 112 | git rebase --continue 113 | ``` 114 | 115 | This will continue the rebasing process. Once you are done fixing all 116 | conflicts you should run the existing tests to make sure you didn�t break 117 | anything, then run your new tests (there are new tests, right?) and 118 | make sure they work also. 119 | 120 | If rebasing broke anything, fix it, then repeat the above process until 121 | you get here again and nothing is broken and all the tests pass. 122 | 123 | ### Make a pull request 124 | 125 | Make a clear pull request from your fork and branch to the upstream master 126 | branch, detailing exactly what changes you made and what feature this 127 | should add. The clearer your pull request is the faster you can get 128 | your changes incorporated into this repo. 129 | 130 | At least one other person MUST give your changes a code review, and once 131 | they are satisfied they will merge your changes into upstream. Alternatively, 132 | they may have some requested changes. You should make more commits to your 133 | branch to fix these, then follow this process again from rebasing onwards. 134 | 135 | Once you get back here, make a comment requesting further review and 136 | someone will look at your code again. If they like it, it will get merged, 137 | else, just repeat again. 138 | 139 | Thanks for contributing! 140 | 141 | ### Guidelines 142 | 143 | 1. Uphold the current code standard: 144 | - Keep your code [DRY][]. 145 | - Apply the [boy scout rule][]. 146 | - Follow [STYLE-GUIDE.md](STYLE-GUIDE.md) 147 | 1. Run the [tests][] before submitting a pull request. 148 | 1. Tests are very, very important. Submit tests if your pull request contains 149 | new, testable behavior. 150 | 1. Your pull request is comprised of a single ([squashed][]) commit. 151 | 152 | ## Checklist: 153 | 154 | This is just to help you organize your process 155 | 156 | - [ ] Did I cut my work branch off of master (don't cut new branches from existing feature brances)? 157 | - [ ] Did I follow the correct naming convention for my branch? 158 | - [ ] Is my branch focused on a single main change? 159 | - [ ] Do all of my changes directly relate to this change? 160 | - [ ] Did I rebase the upstream master branch after I finished all my 161 | work? 162 | - [ ] Did I write a clear pull request message detailing what changes I made? 163 | - [ ] Did I get a code review? 164 | - [ ] Did I make any requested changes from that code review? 165 | 166 | If you follow all of these guidelines and make good changes, you should have 167 | no problem getting your changes merged in. 168 | 169 | 170 | 171 | [style guide]: https://github.com/hackreactor-labs/style-guide 172 | [n-queens]: https://github.com/hackreactor-labs/n-queens 173 | [Underbar]: https://github.com/hackreactor-labs/underbar 174 | [curriculum workflow diagram]: http://i.imgur.com/p0e4tQK.png 175 | [cons of merge]: https://f.cloud.github.com/assets/1577682/1458274/1391ac28-435e-11e3-88b6-69c85029c978.png 176 | [Bookstrap]: https://github.com/hackreactor/bookstrap 177 | [Taser]: https://github.com/hackreactor/bookstrap 178 | [tools workflow diagram]: http://i.imgur.com/kzlrDj7.png 179 | [Git Flow]: http://nvie.com/posts/a-successful-git-branching-model/ 180 | [GitHub Flow]: http://scottchacon.com/2011/08/31/github-flow.html 181 | [Squash]: http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html 182 | -------------------------------------------------------------------------------- /_.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | // JSHint Meteor Configuration File 3 | // Match the Meteor Style Guide 4 | // 5 | // By @raix with contributions from @aldeed and @awatson1978 6 | // Source https://github.com/raix/Meteor-jshintrc 7 | // 8 | // See http://jshint.com/docs/ for more details 9 | 10 | "maxerr" : 50, // {int} Maximum error before stopping 11 | 12 | // Enforcing 13 | "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) 14 | "camelcase" : true, // true: Identifiers must be in camelCase 15 | "curly" : true, // true: Require {} for every new block or scope 16 | "eqeqeq" : true, // true: Require triple equals (===) for comparison 17 | "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty() 18 | "immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` 19 | "indent" : 2, // {int} Number of spaces to use for indentation 20 | "latedef" : false, // true: Require variables/functions to be defined before being used 21 | "newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()` 22 | "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` 23 | "noempty" : true, // true: Prohibit use of empty blocks 24 | "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment) 25 | "plusplus" : false, // true: Prohibit use of `++` & `--` 26 | "quotmark" : false, // Quotation mark consistency: 27 | // false : do nothing (default) 28 | // true : ensure whatever is used is consistent 29 | // "single" : require single quotes 30 | // "double" : require double quotes 31 | "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) 32 | "unused" : true, // true: Require all defined variables be used 33 | "strict" : true, // true: Requires all functions run in ES5 Strict Mode 34 | "trailing" : true, // true: Prohibit trailing whitespaces 35 | "maxparams" : false, // {int} Max number of formal params allowed per function 36 | "maxdepth" : false, // {int} Max depth of nested blocks (within functions) 37 | "maxstatements" : false, // {int} Max number statements per function 38 | "maxcomplexity" : false, // {int} Max cyclomatic complexity per function 39 | "maxlen" : 80, // {int} Max number of characters per line 40 | 41 | // Relaxing 42 | "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) 43 | "boss" : false, // true: Tolerate assignments where comparisons would be expected 44 | "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. 45 | "eqnull" : false, // true: Tolerate use of `== null` 46 | "es5" : false, // true: Allow ES5 syntax (ex: getters and setters) 47 | "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`) 48 | "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) 49 | // (ex: `for each`, multiple try/catch, function expression�) 50 | "evil" : false, // true: Tolerate use of `eval` and `new Function()` 51 | "expr" : false, // true: Tolerate `ExpressionStatement` as Programs 52 | "funcscope" : false, // true: Tolerate defining variables inside control statements" 53 | "globalstrict" : true, // true: Allow global "use strict" (also enables 'strict') 54 | "iterator" : false, // true: Tolerate using the `__iterator__` property 55 | "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block 56 | "laxbreak" : false, // true: Tolerate possibly unsafe line breakings 57 | "laxcomma" : false, // true: Tolerate comma-first style coding 58 | "loopfunc" : false, // true: Tolerate functions being defined in loops 59 | "multistr" : false, // true: Tolerate multi-line strings 60 | "proto" : false, // true: Tolerate using the `__proto__` property 61 | "scripturl" : false, // true: Tolerate script-targeted URLs 62 | "smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment 63 | "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` 64 | "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation 65 | "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` 66 | "validthis" : false, // true: Tolerate using this in a non-constructor function 67 | // Environments 68 | "browser" : true, // Web Browser (window, document, etc) 69 | "couch" : false, // CouchDB 70 | "devel" : true, // Development/debugging (alert, confirm, etc) 71 | "dojo" : false, // Dojo Toolkit 72 | "jquery" : false, // jQuery 73 | "mootools" : false, // MooTools 74 | "node" : false, // Node.js 75 | "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) 76 | "prototypejs" : false, // Prototype and Scriptaculous 77 | "rhino" : false, // Rhino 78 | "worker" : false, // Web Workers 79 | "wsh" : false, // Windows Scripting Host 80 | "yui" : false, // Yahoo User Interface 81 | //"meteor" : false, // Meteor.js 82 | // Legacy 83 | "nomen" : false, // true: Prohibit dangling `_` in variables 84 | "onevar" : false, // true: Allow only one `var` statement per function 85 | "passfail" : false, // true: Stop on first error 86 | "white" : false, // true: Check against strict whitespace and indentation rules 87 | // Custom globals, from http://docs.meteor.com, in the order they appear there 88 | "globals" : { 89 | "Meteor": false, 90 | "DDP": false, 91 | "Mongo": false, //Meteor.Collection renamed to Mongo.Collection 92 | "Session": false, 93 | "Accounts": false, 94 | "Template": false, 95 | "Blaze": false, //UI is being renamed Blaze 96 | "UI": false, 97 | "Match": false, 98 | "check": false, 99 | "Tracker": false, //Deps renamed to Tracker 100 | "Deps": false, 101 | "ReactiveVar": false, 102 | "EJSON": false, 103 | "HTTP": false, 104 | "Email": false, 105 | "Assets": false, 106 | "Handlebars": false, // https://github.com/meteor/meteor/wiki/Handlebars 107 | "Package": false, 108 | "App": false, //mobile-config.js 109 | // Meteor internals 110 | "DDPServer": false, 111 | "global": false, 112 | "Log": false, 113 | "MongoInternals": false, 114 | "process": false, 115 | "WebApp": false, 116 | "WebAppInternals": false, 117 | // globals useful when creating Meteor packages 118 | "Npm": false, 119 | "Tinytest": false, 120 | // common Meteor packages 121 | "Random": false, 122 | "_": false, // Underscore.js 123 | "$": false, // jQuery 124 | "Router": false // iron-router 125 | } 126 | } 127 | 128 | //{ 129 | // "node": true, 130 | // "esnext": true, 131 | // "bitwise": true, 132 | // "camelcase": true, 133 | // "curly": true, 134 | // "eqeqeq": true, 135 | // "immed": true, 136 | // "indent": 2, 137 | // "latedef": true, 138 | // "newcap": true, 139 | // "noarg": true, 140 | // "quotmark": "single", 141 | // "regexp": true, 142 | // "undef": true, 143 | // "unused": true, 144 | // "strict": true, 145 | // "trailing": true, 146 | // "smarttabs": true, 147 | // "white": true 148 | //} 149 | -------------------------------------------------------------------------------- /client/cardsagainstsobriety/views/gameBoard.js: -------------------------------------------------------------------------------- 1 | var getId = function() { 2 | return Session.get('currentRoomId'); 3 | }; 4 | 5 | Template.gameBoard.helpers({ 6 | 7 | // Returns all online users in this game 8 | users: function(){ 9 | var roomId = Session.get('currentRoomId'); 10 | console.log('PBPBPB roomId in gameBoard is ', roomId); 11 | //returns an array of user objects 12 | var gameInformation = CardsRoom.findOne({_id: roomId}); // returns all users for that room 13 | var userArray = gameInformation.users; 14 | 15 | console.log('tesing', userArray); 16 | return userArray; 17 | // return Meteor.users.find({'status.online': true}); 18 | }, 19 | 20 | currentUser: function() { 21 | var user = Meteor.user(); 22 | var _roomId = Session.get('roomUrl') || getId(); 23 | var gameInformation = CardsRoom.findOne({_id: _roomId}, {users: 1}); // returns all users for that room 24 | var userArray = gameInformation.users; 25 | // if user is the judge, he cannot play a white card 26 | for( var i = 0, len = userArray.length; i < len; i++) { 27 | if ( userArray[i]._id === user._id ) { 28 | if( userArray[i].judge ) { 29 | return userArray[i]; 30 | } 31 | } 32 | } 33 | }, 34 | 35 | // Returns the black question card currently on the GameBoard 36 | question: function(){ 37 | var _roomId = Session.get('roomUrl') || getId(); 38 | //return GameBoard.find({black: true}); 39 | var board = CardsRoom.find({_id: _roomId}).fetch()[0].GameBoard; 40 | var result = []; 41 | for(var i = 0, size = board.length; i < size; i++) { 42 | if (board[i].black) { 43 | result.push(board[i]); 44 | } 45 | } 46 | return result; 47 | }, 48 | 49 | // Returns the white answer cards currently on the GameBoard 50 | answers: function(){ 51 | var _roomId = Session.get('roomUrl') || getId(); 52 | // return GameBoard.find({black: false}); 53 | var board = CardsRoom.find({_id: _roomId}).fetch()[0].GameBoard; 54 | var result = []; 55 | for(var i = 0, size = board.length; i < size; i++) { 56 | if (!board[i].black) { 57 | result.push(board[i]); 58 | } 59 | } 60 | return result; 61 | }, 62 | 63 | // Returns the total number of online players 64 | numPlayers: function(){ 65 | var _roomId = Session.get('roomUrl') || getId(); 66 | console.log('_roomId is ', _roomId); 67 | //get number of users in room 68 | var gameInformation = CardsRoom.findOne({_id: _roomId}, {users: 1}); // returns all users for that room 69 | var userArray = gameInformation.users; 70 | var numberPlayers = userArray.length; 71 | if ( numberPlayers === 0 ) { 72 | return 'NO'; 73 | } else { 74 | return numberPlayers; 75 | } 76 | //if (Meteor.users.find({'status.online': true}).count() === 0) { 77 | // return 'NO'; 78 | //} else { 79 | // return Meteor.users.find({'status.online': true}).count(); 80 | //} 81 | }, 82 | 83 | playersInRoom: function() { 84 | var _roomId = Session.get('roomUrl') || getId(); 85 | var players = CardsRoom.find({_Id: _roomId}, {_id: 1, username: 1}).fetch()[0].users; 86 | return players; 87 | }, 88 | 89 | // Returns a count of all played cards on the board 90 | cardsPlayed: function(){ 91 | var _roomId = Session.get('roomUrl') || getId(); 92 | // return GameBoard.find({black: false}).count(); 93 | var board = CardsRoom.find({_id: _roomId}).fetch()[0].GameBoard; 94 | var result = 0; 95 | for(var i = 0, size = board.length; i < size; i++) { 96 | if (!board[i].black) { 97 | result++; 98 | } 99 | } 100 | return result; 101 | }, 102 | 103 | // Returns a count of the cards still needed to be played for the round 104 | cardsLeft: function(){ 105 | var _roomId = Session.get('roomUrl') || getId(); 106 | //var count = Meteor.users.find({'status.online': true}).count(); 107 | var gameInformation = CardsRoom.findOne({_id: _roomId}, {users: 1}); // returns all users for that room 108 | var userArray = gameInformation.users; 109 | var count = userArray.length; 110 | var board = gameInformation.GameBoard; 111 | var result = 0; 112 | for(var i = 0, size = board.length; i < size; i++) { 113 | if (!board[i].black) { 114 | result++; 115 | } 116 | } 117 | return result; 118 | return Math.max(0, (count - 1) - result); 119 | }, 120 | 121 | // Returns true if all of the cards have been played, which signifies that the round is over 122 | allCardsPlayed: function(){ 123 | var _roomId = Session.get('roomUrl') || getId(); 124 | //count players in room, subtract one so judge not included 125 | var gameInformation = CardsRoom.findOne({_id: _roomId}, {users: 1}); // returns all users for that room 126 | var userArray = gameInformation.users; 127 | var players = userArray.length - 1; 128 | //var players = (Meteor.users.find().count() - 1); 129 | var board = CardsRoom.find({_id: _roomId}).fetch()[0].GameBoard; 130 | var played = 0; 131 | for ( var i = 0, size = board.length; i < size; i++ ) { 132 | if ( !board[i].black ) { played++; } 133 | } 134 | //var played = GameBoard.find({black: false}).count(); 135 | return players - played === 0; 136 | }, 137 | 138 | // Returns true if the judge has chosen the winning card. 139 | // When true, the game-board-view.html will display a button that starts the next round 140 | winnerChosen: function(){ 141 | var _roomId = Session.get('roomUrl') || getId(); 142 | var round = CardsRoom.find({_id: _roomId}).fetch()[0].RoundInfo; 143 | //var round = RoundInfo.findOne({}); 144 | return round.roundOver; 145 | } 146 | 147 | }); 148 | 149 | 150 | Template.gameBoard.events({ 151 | "click .answerCards": function (event) { 152 | console.log('this in answerCards ', this); 153 | event.stopPropagation(); 154 | var _roomId = getId(); 155 | // calls endRound from deck.js, which sets roundOver to true for the winnerChosen helper above 156 | Meteor.call('endRound', _roomId, function(err, res){ 157 | if(err){ 158 | throw err; 159 | } 160 | }); 161 | 162 | // store click context to pass into method call 163 | console.log(this, "this in gameboard event"); 164 | var cardOwner = this.owner; 165 | 166 | // calls incrementScore from decks.js 167 | Meteor.call('incrementScore', _roomId, cardOwner, function(err, id) { 168 | if (err) { 169 | throw err; 170 | } 171 | }); 172 | 173 | // stores the winning card 174 | var board = CardsRoom.find({_id: _roomId}).fetch()[0].GameBoard; 175 | var answer, question; 176 | for (var i = 0, size = board.length; i < size; i++) { 177 | if (board[i].cardOwner === cardOwner) { 178 | answer = board[i]; 179 | } 180 | if (board[i].black) { 181 | question = board[i]; 182 | } 183 | } 184 | // var answer = GameBoard.findOne({owner: cardOwner}); 185 | // // stores the question card 186 | // var question = GameBoard.findOne({black: true}); 187 | 188 | // calls clearLosers from decks.js, which clears the GameBoard, then inserts 189 | // the winning card along with the card it answered into GameBoard 190 | Meteor.call("clearLosers", _roomId, answer, question, function(err, result){ 191 | if(err) { 192 | throw err; 193 | } 194 | }); 195 | 196 | }, 197 | 198 | // Event listener tied to the 'Let's play another, you smarmy wench' button 199 | // which is only shown if the judge has chosen the winning card. 200 | "click #nextRound": function(){ 201 | var _roomId = getId(); 202 | // calls newRound which removes round data 203 | Meteor.call('newRound', _roomId, function(err, result){ 204 | if(err) { 205 | throw err; 206 | } 207 | }) 208 | 209 | // remove cards from GameBoard 210 | Meteor.call('clearGameBoard', _roomId, function (err, result) { 211 | if (err) { 212 | throw err; 213 | } 214 | }) 215 | 216 | // pass 'judgeship' to next user 217 | Meteor.call('toggleJudge', _roomId, function (err, result) { 218 | if (err) { 219 | throw err; 220 | } 221 | }) 222 | 223 | // draw next black card 224 | Meteor.call("drawBlack", _roomId, function(err, res){ 225 | if(err){ 226 | throw err; 227 | } 228 | }) 229 | } 230 | 231 | }); 232 | 233 | 234 | 235 | -------------------------------------------------------------------------------- /collections/decks.js: -------------------------------------------------------------------------------- 1 | // create shuffled decks 2 | CardsRoom = new Meteor.Collection('CardsRoom'); 3 | 4 | // MovieRoundData = new Meteor.Collection("MovieRoundData"); 5 | MovieRooms = new Meteor.Collection("MovieRooms"); 6 | TimesHistorianRoom = new Meteor.Collection("TimesHistorianRoom"); 7 | // create collection with all user hands 8 | 9 | // create collection of all cards on the game table (one black card & all played white cards) 10 | 11 | // Currently this collection provides a check for whether the round is over 12 | // This is done by initializing a roundOver property of this collection when the judge picks a winner 13 | // Then that property is deleted when a new round is started 14 | 15 | MovieRooms.allow({ 16 | update: function(userId, doc, fields, modifier){ 17 | return true; 18 | }, 19 | insert: function(userId, doc, fields, modifier){ 20 | return true; 21 | } 22 | }); 23 | 24 | TimesHistorianRoom.allow({ 25 | update: function(userId, doc, fields, modifier){ 26 | return true; 27 | }, 28 | insert: function(userId, doc, fields, modifier){ 29 | return true; 30 | } 31 | }); 32 | 33 | CardsRoom.allow({ 34 | update: function(userId, doc, fields, modifier){ 35 | return true; 36 | }, 37 | insert: function(userId, doc, fields, modifier){ 38 | return true; 39 | } 40 | }); 41 | 42 | //This is where we hold our methods that get called from the client side 43 | Meteor.methods({ 44 | // function deals a player hand at the beginning of the game 45 | dealHand: function(roomId) { 46 | //userArray holds an array of players that are logged in using the user-status package 47 | //var userArray = CardsRoom.find({ _id : roomId }, { users: 1 }).fetch(); 48 | var gameInformation = CardsRoom.findOne({_id: roomId}, {users: 1}); // returns all users for that room 49 | //console.log('gameInformation is ', gameInformation); 50 | var userArray = gameInformation.users; 51 | //console.log('userArray is ', userArray); 52 | var judgeCounter = 0; 53 | for (var i = 0; i < userArray.length; i++) { 54 | if (userArray[i].judge === true) { 55 | judgeCounter++; 56 | } 57 | } 58 | //if the deal button was pushed and no judges are assigned already, assign one randomly 59 | if (judgeCounter === 0) { 60 | var rng = Math.round(Math.random() * (userArray.length - 1)); 61 | var randomUserId = userArray[rng]._id; 62 | CardsRoom.update({_id: roomId, "users._id": randomUserId}, {$set: {'users.$.judge': true}}); 63 | //GameBoard.update({_id: gameId}, {$set: {'judge': true}}); 64 | } 65 | //if the deal button was pushed and there is 1 judge already, toggle that judge 66 | if (judgeCounter === 1) { 67 | Meteor.call("toggleJudge", function(err) { //function at the end of this file 68 | if (err) { 69 | throw err; 70 | } 71 | }); 72 | } 73 | //iterate over all active players and insert up to 10 cards in their hand 74 | for (var j = 0; j < userArray.length; j++) { 75 | //adding .fetch() onto the end of the find method returns an array, thus we can use length 76 | console.log('userArray[j] is ', userArray[j]); 77 | if ( !userArray[j].cards || userArray[j].cards.length === 0 ) {//PlayerHand 78 | for (var i = 0; i < 10; i++) { 79 | var whiteDeck = CardsRoom.find({_id: roomId}).fetch()[0].WhiteDeck; 80 | var cardNumber = whiteDeck.length; 81 | var whiteCard = whiteDeck.pop(); 82 | //var whiteCard = CardsRoom.find({_id: roomId}).fetch()[0].WhiteDeck.pop(); 83 | CardsRoom.update({_id: roomId}, {$pop: {WhiteDeck: 1}}); 84 | whiteCard.no = cardNumber; 85 | whiteCard.owner = userArray[j]._id; 86 | console.log('whiteCard is ', whiteCard); 87 | CardsRoom.update({_id: roomId, 'users._id': userArray[j]._id}, {$push: {'users.$.cards': whiteCard}}); 88 | } 89 | } 90 | } 91 | }, 92 | 93 | //var arrayUsers = CardsRoom.find({_id: roomId}, {'users': 1}).fetch(); 94 | 95 | 96 | 97 | 98 | 99 | 100 | // replenishes white cards in the player's hand 101 | drawWhite: function(roomId) { 102 | var gameInformation = CardsRoom.findOne({_id: roomId}, {users: 1}); // returns all users for that room 103 | var userArray = gameInformation.users; 104 | //for (var i = 0; i < userArray.length; i++) { 105 | // while (CardsRoom.find({_id: roomId, 'users._id': userArray[i]._id}, {'users.cards': 1}).length < 10) { 106 | // var _entry = CardsRoom.update({_id: roomId}, {$pop: {WhiteDeck: 1}}).fetch(); 107 | // CardsRoom.update({_id: roomId, 'users._id': userArray[i]._id}, {$push: {'users.cards': _entry}}); 108 | // } 109 | //} 110 | for( var j = 0, len = userArray.length; j < len; j++ ) { 111 | if ( userArray[j].cards.length <= 10 ) {//PlayerHand 112 | var cardLeft = 10 - userArray[j].cards.length; 113 | for (var i = 0; i < cardLeft; i++) { 114 | var whiteCard = CardsRoom.find({_id: roomId}).fetch()[0].WhiteDeck.pop(); 115 | CardsRoom.update({_id: roomId}, {$pop: {WhiteDeck: 1}}); 116 | console.log('whiteCard is ', whiteCard); 117 | CardsRoom.update({_id: roomId, 'users._id': userArray[j]._id}, {$push: {'users.$.cards': whiteCard}}); 118 | } 119 | } 120 | } 121 | }, 122 | // adds card to game board with the user id and removes from playerhand 123 | playCard: function(card, roomId) { 124 | console.log('in deck.js playCard'); 125 | console.log('car in deck.js playCard', card); 126 | var user = Meteor.user(); 127 | var gameInformation = CardsRoom.findOne({_id: roomId}, {users: 1}); // returns all users for that room 128 | var userArray = gameInformation.users; 129 | for( var i = 0, len = userArray.length; i < len; i++ ) { 130 | if( userArray[i]._id === user._id ) { 131 | CardsRoom.update({_id: roomId, 'users._id': userArray[i]._id}, {$pull: {'users.$.cards': card.text}}); 132 | console.log('card inside deck.js playCard is ', card); 133 | /* var cardObj = { "no": 1, 134 | "text": card.text, 135 | "expansion": card.expansion, 136 | "black": false, 137 | "owner": 'jonah' 138 | };*/ 139 | var cardObj = { "no": card.no, 140 | "text": card.text, 141 | "expansion": card.expansion, 142 | "black": false, 143 | "owner": card.owner 144 | }; 145 | console.log('roomId in deck.js ', roomId); 146 | CardsRoom.update({_id: roomId}, { $push: 147 | {"GameBoard": cardObj } 148 | } 149 | ); 150 | } 151 | } 152 | }, 153 | 154 | // this function starts a new hand by clearing the GameBoard and adding a black card 155 | drawBlack: function(roomId) { 156 | CardsRoom.update({_id: roomId}, {$set: {'GameBoard': []}}); 157 | //fetches array of BlackCards from collection and pops off card. NOTE: unattached array 158 | var blackDeck = CardsRoom.find({_id: roomId}).fetch()[0].BlackDeck; 159 | var cardNum = blackDeck.length; 160 | var blackCard = blackDeck.pop(); 161 | blackCard.no = cardNum; 162 | blackCard.black = true; 163 | blackCard.owner = 164 | 165 | //this line required to remove card from collection 166 | CardsRoom.update({_id: roomId}, {$pop: {BlackDeck: 1}}); 167 | CardsRoom.update({_id: roomId}, {$push: {'GameBoard': blackCard}}); 168 | }, 169 | 170 | //increment score of card owner 171 | incrementScore: function(roomId, cardOwner) { 172 | var gameInformation = CardsRoom.findOne({_id: roomId}, {users: 1}); // returns all users for that room 173 | var userArray = gameInformation.users; 174 | for( var i = 0, len = userArray.length; i < len; i++ ) { 175 | if( userArray[i]._id === cardOwner ) { 176 | CardsRoom.update({_id: roomId, "users._id": cardOwner}, {$inc: {'users.$.score': 1}}); 177 | Meteor.users.update({_id: cardOwner}, {$inc: {'score': 1}}); 178 | } 179 | } 180 | }, 181 | 182 | // signals the end of the inserting a roundOver property and setting it to true 183 | endRound: function(roomId) { 184 | CardsRoom.update({_id: roomId}, {$set: {'RoundInfo.roundOver': true}}) 185 | }, 186 | 187 | // resets the round by removing the roundOver property 188 | newRound: function(roomId) { 189 | var round = CardsRoom.update({_id: roomId}, {$set: {'RoundInfo.roundOver': false}}); 190 | //var round = RoundInfo.findOne({}); 191 | //RoundInfo.remove({_id: round._id}); 192 | }, 193 | 194 | // Clear losing cards from the gameboard by clearing the entire board 195 | // and then inserting the winning answer and corresponding question 196 | clearLosers: function(roomId, winnerCard, questionCard) { 197 | CardsRoom.update({_id: roomId}, {$set: {'GameBoard': []}}); 198 | CardsRoom.update({_id: roomId}, {$push: {'GameBoard': winnerCard}}); 199 | CardsRoom.update({_id: roomId}, {$push: {'GameBoard': questionCard}}); 200 | }, 201 | 202 | // clears gameboard & starts new round 203 | clearGameBoard: function(roomId) { 204 | CardsRoom.update({_id: roomId}, {$set: {'GameBoard': []}}); 205 | }, 206 | 207 | // rotates judge role after each round 208 | toggleJudge: function(roomId) { 209 | //var userArray = CardsRoom.find({_id: roomId}, {users: 1}); 210 | var userArray = CardsRoom.find({_id: roomId}, {users: 1}); 211 | 212 | //iterate through all active users 213 | for (var i = 0; i < userArray.length; i++) { 214 | //if that user is the judge 215 | if (userArray[i].judge === true) { 216 | //take his unique _.id 217 | var currentId = userArray[i]._id; 218 | //set his judge property to false 219 | CardsRoom.update({_id: roomId, "users._id": currentId}, {$set: {'users.$.judge': false}}); 220 | //if that user is the final element in the array 221 | if (i === (userArray.length - 1)) { 222 | //set the judge property to true for the first position in the array 223 | CardsRoom.update({_id: roomId, "users._id": userArray[0]._id}, {$set: {'users.$.judge': true}}); 224 | //break out 225 | break; 226 | } else { 227 | //for any other position make the next array index the judge 228 | CardsRoom.update({_id: roomId, "users._id": userArray[i + 1]._id}, {$set: {'users.$.judge': true}}); 229 | //breakout 230 | break; 231 | } 232 | } 233 | } 234 | }, 235 | 236 | newGame: function(roomId){ 237 | CardsRoom.update({_id: roomId}, {$set: {'GameBoard': []}}); 238 | CardsRoom.update({_id: roomId}, {$set: {'WhiteDeck': []}}); 239 | CardsRoom.update({_id: roomId}, {$set: {'BlackDeck': []}}); 240 | CardsRoom.update({_id: roomId}, {$set: {'RoundInfo': []}}); 241 | var gameInformation = CardsRoom.findOne({_id: roomId}, {users: 1}); // returns all users for that room 242 | var userArray = gameInformation.users; 243 | for(var i = 0, len = userArray; i < len; i++) { 244 | CardsRoom.update({_id: roomId, "users._id": userArray[i]._id}, {$set: {'users.$.cards': []}}); 245 | } 246 | } 247 | }); 248 | -------------------------------------------------------------------------------- /_STYLE-GUIDE.md: -------------------------------------------------------------------------------- 1 | ### Indentation 2 | 3 | When writing any block of code that is logically subordinate to the line immediately before and after it, that block should be indented two spaces more than the surrounding lines 4 | 5 | * Do not put any tab characters anywhere in your code. You would do best to stop pressing the tab key entirely. 6 | * Increase the indent level for all blocks by two extra spaces 7 | * When a line opens a block, the next line starts 2 spaces further in than the line that opened 8 | 9 | ```javascript 10 | // good: 11 | if(condition){ 12 | action(); 13 | } 14 | 15 | // bad: 16 | if(condition){ 17 | action(); 18 | } 19 | ``` 20 | 21 | * When a line closes a block, that line starts at the same level as the line that opened the block 22 | ```javascript 23 | // good: 24 | if(condition){ 25 | action(); 26 | } 27 | 28 | // bad: 29 | if(condition){ 30 | action(); 31 | } 32 | ``` 33 | 34 | * No two lines should ever have more or less than 2 spaces difference in their indentation. Any number of mistakes in the above rules could lead to this, but one example would be: 35 | 36 | ```javascript 37 | // bad: 38 | transmogrify({ 39 | a: { 40 | b: function(){ 41 | } 42 | }}); 43 | ``` 44 | 45 | * use sublime's arrow collapsing as a guide. do the collapsing lines seem like they should be 'contained' by the line with an arrow on it? 46 | 47 | 48 | ### Variable names 49 | 50 | * A single descriptive word is best. 51 | 52 | ```javascript 53 | // good: 54 | var animals = ['cat', 'dog', 'fish']; 55 | 56 | // bad: 57 | var targetInputs = ['cat', 'dog', 'fish']; 58 | ``` 59 | 60 | * Collections such as arrays and maps should have plural noun variable names. 61 | 62 | ```javascript 63 | // good: 64 | var animals = ['cat', 'dog', 'fish']; 65 | 66 | // bad: 67 | var animalList = ['cat', 'dog', 'fish']; 68 | 69 | // bad: 70 | var animal = ['cat', 'dog', 'fish']; 71 | ``` 72 | 73 | * Name your variables after their purpose, not their structure 74 | 75 | ```javascript 76 | // good: 77 | var animals = ['cat', 'dog', 'fish']; 78 | 79 | // bad: 80 | var array = ['cat', 'dog', 'fish']; 81 | ``` 82 | 83 | 84 | ### Language constructs 85 | 86 | * Do not use `for...in` statements with the intent of iterating over a list of numeric keys. Use a for-with-semicolons statement in stead. 87 | 88 | ```javascript 89 | // good: 90 | var list = ['a', 'b', 'c'] 91 | for(var i = 0; i < list.length; i++){ 92 | alert(list[i]); 93 | } 94 | 95 | // bad: 96 | var list = ['a', 'b', 'c'] 97 | for(var i in list){ 98 | alert(list[i]); 99 | } 100 | ``` 101 | 102 | * Never omit braces for statement blocks (although they are technically optional). 103 | ```javascript 104 | // good: 105 | for(key in object){ 106 | alert(key); 107 | } 108 | 109 | // bad: 110 | for(key in object) 111 | alert(key); 112 | ``` 113 | 114 | * Always use `===` and `!==`, since `==` and `!=` will automatically convert types in ways you're unlikely to expect. 115 | 116 | ```javascript 117 | // good: 118 | 119 | // this comparison evaluates to false, because the number zero is not the same as the empty string. 120 | if(0 === ''){ 121 | alert('looks like they\'re equal'); 122 | } 123 | 124 | // bad: 125 | 126 | // This comparison evaluates to true, because after type coercion, zero and the empty string are equal. 127 | if(0 == ''){ 128 | alert('looks like they\'re equal'); 129 | } 130 | ``` 131 | 132 | * Don't use function statements for the entire first half of the course. They introduce a slew of subtle new rules to how the language behaves, and without a clear benefit. Once you and all your peers are expert level in the second half, you can start to use the more (needlessly) complicated option if you like. 133 | 134 | ```javascript 135 | // good: 136 | var go = function(){...}; 137 | 138 | // bad: 139 | function stop(){...}; 140 | ``` 141 | 142 | 143 | ### Semicolons 144 | 145 | * Don't forget semicolons at the end of lines 146 | 147 | ```javascript 148 | // good: 149 | alert('hi'); 150 | 151 | // bad: 152 | alert('hi') 153 | ``` 154 | 155 | * Semicolons are not required at the end of statements that include a block--i.e. `if`, `for`, `while`, etc. 156 | 157 | 158 | ```javascript 159 | // good: 160 | if(condition){ 161 | response(); 162 | } 163 | 164 | // bad: 165 | if(condition){ 166 | response(); 167 | }; 168 | ``` 169 | 170 | * Misleadingly, a function may be used at the end of a normal assignment statement, and would require a semicolon (even though it looks rather like the end of some statement block). 171 | 172 | ```javascript 173 | // good: 174 | var greet = function(){ 175 | alert('hi'); 176 | }; 177 | 178 | // bad: 179 | var greet = function(){ 180 | alert('hi'); 181 | } 182 | ``` 183 | 184 | # Supplemental reading 185 | 186 | ### Code density 187 | 188 | * Conserve line quantity by minimizing the number lines you write in. The more concisely your code is written, the more context can be seen in one screen. 189 | * Conserve line length by minimizing the amount of complexity you put on each line. Long lines are difficult to read. Rather than a character count limit, I recommend limiting the amount of complexity you put on a single line. Try to make it easily read in one glance. This goal is in conflict with the line quantity goal, so you must do your best to balance them. 190 | 191 | ### Comments 192 | 193 | * Provide comments any time you are confident it will make reading your code easier. 194 | * Be aware that comments come at some cost. They make a file longer and can drift out of sync with the code they annotate. 195 | * Comment on what code is attempting to do, not how it will achieve it. 196 | * A good comment is often less effective than a good variable name. 197 | 198 | 199 | ### Padding & additional whitespace 200 | 201 | * Generally, we don't care where you put extra spaces, provided they are not distracting. 202 | * You may use it as padding for visual clarity. If you do though, make sure it's balanced on both sides. 203 | 204 | ```javascript 205 | // optional: 206 | alert( "I chose to put visual padding around this string" ); 207 | 208 | // bad: 209 | alert( "I only put visual padding on one side of this string"); 210 | ``` 211 | 212 | * You may use it to align two similar lines, but it is not recommended. This pattern usually leads to unnecessary edits of many lines in your code every time you change a variable name. 213 | 214 | ```javascript 215 | // discouraged: 216 | var firstItem = getFirst (); 217 | var secondItem = getSecond(); 218 | ``` 219 | 220 | * Put `else` and `else if` statements on the same line as the ending curly brace for the preceding `if` block 221 | ```javascript 222 | // good: 223 | if(condition){ 224 | response(); 225 | }else{ 226 | otherResponse(); 227 | } 228 | 229 | // bad: 230 | if(condition){ 231 | response(); 232 | } 233 | else{ 234 | otherResponse(); 235 | } 236 | ``` 237 | 238 | 239 | 240 | ### Working with files 241 | 242 | * Do not end a file with any character other than a newline. 243 | * Don't use the -a or -m flags for `git commit` for the first half of the class, since they conceal what is actually happening (and do slightly different things than most people expect). 244 | 245 | ```shell 246 | # good: 247 | > git add . 248 | > git commit 249 | [save edits to the commit message file using the text editor that opens] 250 | 251 | # bad: 252 | > git commit -a 253 | [save edits to the commit message file using the text editor that opens] 254 | 255 | # bad: 256 | > git add . 257 | > git commit -m "updated algorithm" 258 | ``` 259 | 260 | 261 | ### Opening or closing too many blocks at once 262 | 263 | * The more blocks you open on a single line, the more your reader needs to remember about the context of what they are reading. Try to resolve your blocks early, and refactor. A good rule is to avoid closing more than two blocks on a single line--three in a pinch. 264 | 265 | ```javascript 266 | // avoid: 267 | _.ajax(url, {success: function(){ 268 | // ... 269 | }}); 270 | 271 | // prefer: 272 | _.ajax(url, { 273 | success: function(){ 274 | // ... 275 | } 276 | }); 277 | ``` 278 | 279 | 280 | ### Variable declaration 281 | 282 | * Use a new var statement for each line you declare a variable on. 283 | * Do not break variable declarations onto mutiple lines. 284 | * Use a new line for each variable declaration. 285 | * See http://benalman.com/news/2012/05/multiple-var-statements-javascript/ for more details 286 | 287 | ```javascript 288 | // good: 289 | var ape; 290 | var bat; 291 | 292 | // bad: 293 | var cat, 294 | dog 295 | 296 | // use sparingly: 297 | var eel, fly; 298 | ``` 299 | 300 | ### Capital letters in variable names 301 | 302 | * Some people choose to use capitalization of the first letter in their variable names to indicate that they contain a [class](http://en.wikipedia.org/wiki/Class_(computer_science\)). This capitalized variable might contain a function, a prototype, or some other construct that acts as a representative for the whole class. 303 | * Optionally, some people use a capital letter only on functions that are written to be run with the keyword `new`. 304 | * Do not use all-caps for any variables. Some people use this pattern to indicate an intended "constant" variable, but the language does not offer true constants, only mutable variables. 305 | 306 | 307 | ### Minutia 308 | 309 | * Don't rely on JavaScripts implicit global variables. If you are intending to write to the global scope, export things to `window.*` explicitly instead. 310 | 311 | ```javascript 312 | // good: 313 | var overwriteNumber = function(){ 314 | window.exported = Math.random(); 315 | }; 316 | 317 | // bad: 318 | var overwriteNumber = function(){ 319 | exported = Math.random(); 320 | }; 321 | ``` 322 | 323 | * For lists, put commas at the end of each newline, not at the beginning of each item in a list 324 | 325 | ```javascript 326 | // good: 327 | var animals = [ 328 | 'ape', 329 | 'bat', 330 | 'cat' 331 | ]; 332 | 333 | // bad: 334 | var animals = [ 335 | 'ape' 336 | , 'bat' 337 | , 'cat' 338 | ]; 339 | ``` 340 | 341 | * Avoid use of `switch` statements altogether. They are hard to outdent using the standard whitespace rules above, and are prone to error due to missing `break` statements. See [this article](http://ericleads.com/2012/12/switch-case-considered-harmful/) for more detail. 342 | 343 | * Prefer single quotes around JavaScript strings, rather than double quotes. Having a standard of any sort is preferable to a mix-and-match approach, and single quotes allow for easy embedding of HTML, which prefers double quotes around tag attributes. 344 | 345 | ```javascript 346 | // good: 347 | var dog = 'dog'; 348 | var cat = 'cat'; 349 | 350 | // acceptable: 351 | var dog = "dog"; 352 | var cat = "cat"; 353 | 354 | // bad: 355 | var dog = 'dog'; 356 | var cat = "cat"; 357 | ``` 358 | 359 | 360 | ### HTML 361 | 362 | * Do not use ids for html elements. Use a class instead. 363 | 364 | ```html 365 | 366 | 367 | 368 | 369 | 370 | ``` 371 | 372 | * Do not include a `type=text/javascript"` attribute on script tags 373 | 374 | ```html 375 | 376 | 377 | 378 | 379 | 380 | ``` 381 | -------------------------------------------------------------------------------- /server/TimesHistorian.js: -------------------------------------------------------------------------------- 1 | var movieFiles = ['A_Beautiful_Mind.srt', 2 | 'A_Clockwork_Orange.srt', 3 | 'A_Few_Good_Men.srt', 4 | 'Back_to_the_Future.srt', 5 | 'City_of_God.srt', 6 | 'Diehard.srt', 7 | 'Fight_Club.srt', 8 | 'Forrest_Gump.srt', 9 | 'Gladiator.srt', 10 | 'Good_Will_Hunting.srt', 11 | 'Inception.srt', 12 | 'Into_the_Wild.srt', 13 | 'Memento.srt', 14 | 'One_Flew_Over_the_Cuckoos_Nest.srt', 15 | 'Pulp_Fiction.srt', 16 | 'Raiders_of_the_Lost_Arc.srt', 17 | 'Reservoir_Dogs.srt', 18 | 'Return_of_the_King.srt', 19 | 'Saving_Private_Ryan.srt', 20 | 'Scarface.srt', 21 | 'Schindlers_List.srt', 22 | 'Se7en.srt', 23 | 'Terminator.srt', 24 | 'The_Dark_Knight.srt', 25 | 'The_Departed.srt', 26 | 'The_Empire_Strikes_Back.srt', 27 | 'The_Godfather.srt', 28 | 'The_Green_Mile.srt', 29 | 'The_Lion_King.srt', 30 | 'The_Matrix.srt', 31 | 'The_Princess_Bride.srt', 32 | 'The_Shawshank_Redemption.srt', 33 | 'The_Silence_of_the_Lambs.srt', 34 | 'Toy_Story.srt']; 35 | 36 | var fs = Npm.require('fs'); 37 | var parser = function() { 38 | var r = {}; 39 | r.fromSrt = function(r, e) { 40 | var n = e ? !0 : !1; 41 | r = r.replace(/\r/g, ""); 42 | var i = /(\d+)\n(\d{2}:\d{2}:\d{2},\d{3}) --> (\d{2}:\d{2}:\d{2},\d{3})/g; 43 | r = r.split(i), r.shift(); 44 | for (var a = [], d = 0; d < r.length; d += 4)a.push({ 45 | id: r[d].trim(), 46 | startTime: n ? t(r[d + 1].trim()) : r[d + 1].trim(), 47 | endTime: n ? t(r[d + 2].trim()) : r[d + 2].trim(), 48 | text: r[d + 3].trim() 49 | }); 50 | return a 51 | }, r.toSrt = function(r) { 52 | if (!r instanceof Array)return ""; 53 | for (var t = "", n = 0; n < r.length; n++) { 54 | var i = r[n]; 55 | isNaN(i.startTime) || isNaN(i.endTime) || (i.startTime = e(parseInt(i.startTime, 10)), i.endTime = e(parseInt(i.endTime, 10))), t += i.id + "\r\n", t += i.startTime + " --> " + i.endTime + "\r\n", t += i.text.replace("\n", "\r\n") + "\r\n\r\n" 56 | } 57 | return t 58 | }; 59 | var t = function(r) { 60 | var t = /(\d+):(\d{2}):(\d{2}),(\d{3})/, e = t.exec(r); 61 | if (null === e)return 0; 62 | for (var n = 1; 5 > n; n++)e[n] = parseInt(e[n], 10), isNaN(e[n]) && (e[n] = 0); 63 | return 36e5 * e[1] + 6e4 * e[2] + 1e3 * e[3] + e[4] 64 | }, e = function(r) { 65 | var t = [36e5, 6e4, 1e3], e = []; 66 | for (var n in t) { 67 | var i = (r / t[n] >> 0).toString(); 68 | i.length < 2 && (i = "0" + i), r %= t[n], e.push(i) 69 | } 70 | var a = r.toString(); 71 | if (a.length < 3)for (n = 0; n <= 3 - a.length; n++)a = "0" + a; 72 | return e.join(":") + "," + a 73 | }; 74 | return r 75 | }(); 76 | "object" == typeof exports && (module.exports = parser); 77 | var commonWords = Assets.getText('stopwords.txt'); 78 | 79 | var updateCollection = function(collection, roomId, updateObj){ 80 | //if (!collection.findOne(roomId)){ 81 | // collection.insert({ "_id": roomId, 82 | // "users": [], 83 | // "scoreBoard": [], 84 | // "answered": false, 85 | // "gameBoard": {"result": [], "choices": [], "chosen": ""}, 86 | // "roundInfo": {"roundNum": 0, "lastWinner": ""} 87 | // }); 88 | //}; 89 | collection.update({"_id": roomId}, updateObj); 90 | }; 91 | 92 | 93 | Meteor.methods({ 94 | getMovieData: function(roomID, winner) { 95 | // Reading from a file of common words to keep out of word cloud and putting into an object for quick lookup 96 | var commonWordsDic = {}; 97 | _.each(commonWords.split("\n"), function(word) { 98 | commonWordsDic[word] = true; 99 | }); 100 | 101 | // reading the movie directory and randomly selecting a file. We read that file using the srt parser and then 102 | // put the data into a Javascript dictionary 103 | randMovies = []; 104 | while (randMovies.length < 5) { 105 | var randN = Math.floor((Math.random() * movieFiles.length)); 106 | var randMovie = movieFiles[randN].split('.')[0]; 107 | if (randMovies.indexOf(randMovie) === -1) { 108 | randMovies.push(randMovie); 109 | } 110 | } 111 | 112 | var randN = Math.floor((Math.random() * randMovies.length)); 113 | var randMovieFile = randMovies[randN] + '.srt'; 114 | 115 | var srt = Assets.getText('Movies/' + randMovieFile); 116 | var data = parser.fromSrt(srt); 117 | 118 | // A function to read each line and count the usage of every word 119 | // We have to correct for common words and punctuation 120 | var data_convert = function(data, choices, chosen) { 121 | // initi storage object 122 | var tempObj = {}; 123 | 124 | // Helper function to create a new 'word' object 125 | var newMovie = function(text) { 126 | var result = {}; 127 | result['text'] = text; 128 | result['size'] = 1; 129 | tempObj[text] = result; 130 | }; 131 | 132 | // Function to normalize the words. There is still a lot to do here. 133 | var splitString = function(input) { 134 | var result = []; 135 | wordList = input.split(" "); 136 | _.each(wordList, function(word) { 137 | word = word.replace(/\./g, ""); 138 | word = word.replace(/\"/g, ""); 139 | word = word.replace(/\'/g, ""); 140 | word = word.replace(/\\/g, ""); 141 | word = word.replace(/\-/g, ""); 142 | word = word.replace(/\?/g, ""); 143 | word = word.replace(/\,/g, ""); 144 | word = word.replace(/\:/g, ""); 145 | word = word.replace(/\!/g, ""); 146 | word = word.replace(/\(/g, ""); 147 | word = word.replace(/\)/g, ""); 148 | word = word.replace(/\/g, ""); 150 | word = word.replace(/\//g, ""); 151 | word = word.replace('=', ""); 152 | word = word.replace('#', ""); 153 | word = word.replace('font', ""); 154 | word = word.replace('color', ""); 155 | word = word.replace('', ""); 156 | word = word.replace('', ""); 157 | word = word.replace(/[0-9]/g, ""); 158 | word = word.toLowerCase(); 159 | word = word.toLowerCase(); 160 | if (!commonWordsDic[word] && word != "") { 161 | result.push(word); 162 | } 163 | }); 164 | return result; 165 | }; 166 | 167 | // Getting a random 200 lines 168 | var randStart = Math.floor(Math.random() * (data.length - 201)); 169 | var randEnd = randStart + 200; 170 | 171 | // for each line 172 | for (var i = randStart; i < randEnd; i++) { 173 | var movieLine = data[i]; 174 | // for each word in the parsed string 175 | _.each(splitString(movieLine.text), function(word) { 176 | // if first occurance (not in tempObj) then create new word obj 177 | if (!tempObj[word]) { 178 | newMovie(word); 179 | // otherwise increment existing object 180 | } else { 181 | tempObj[word]['size']++; 182 | } 183 | }); 184 | } 185 | 186 | // transform return object to an array 187 | var resultArray = []; 188 | for (key in tempObj) { 189 | tempObj[key].size = tempObj[key].size * 5; 190 | Math.floor(Math.random() * (data.length - 100)); 191 | resultArray.push(tempObj[key]); 192 | } 193 | 194 | var temp = []; 195 | for (var i = 0; i < randMovies.length; i++){ 196 | temp.push({'text': randMovies[i].split('_').join(' ')}) 197 | } 198 | // At this point the data in the format that D3 word cloud expects so we stringify and write to file 199 | var stringResult = {'result': resultArray, 'choices': temp, 'chosen': chosen}; 200 | updateCollection(MovieRooms, roomID, {$set: {'gameBoard': stringResult, 'answered': false}}); 201 | // MovieRooms.update({"_id": roomID}, {$set: {'gameBoard': stringResult, 'answered': false}}); 202 | }; 203 | data_convert(data, randMovies, randMovieFile.split('.')[0]); 204 | return true; 205 | }, 206 | createMovieOrTimesRoom: function(collection, roomName, userId, username) { 207 | var returnRoom = collection.insert({ 208 | "users": [], 209 | "scoreBoard": [], 210 | "answered": false, 211 | "gameBoard": {"result": [], "choices": [], "chosen": ""}, 212 | "roundInfo": {"roundNum": 0, "lastWinner": ""}, 213 | "createdAt": new Date(), 214 | "createdBy": userId, 215 | "roomName" : roomName 216 | }, function(err, roomInserted) { 217 | returnRoom = roomInserted; 218 | }); 219 | Messages.insert({ 220 | createdById: userId, 221 | createdByName: username, 222 | createdAt: new Date(), 223 | roomId: returnRoom, 224 | messages: [] 225 | }, function (err, messageInserted) { 226 | if( err ) { 227 | console.log('error while creating doc in messages collection'); 228 | } 229 | }); 230 | 231 | return {room: returnRoom}; 232 | } 233 | }); 234 | 235 | Meteor.methods({ 236 | getTimesData: function(roomID){ 237 | 238 | // function to take a date and return it in the right string format 239 | var dateToString = function(inputDate){ 240 | var year = inputDate.getFullYear(); 241 | var month = ''+inputDate.getMonth(); 242 | if (month.length === 1){ 243 | month = '0' + month; 244 | } 245 | var day = ''+inputDate.getDate(); 246 | if (day.length === 1){ 247 | day = '0' + day 248 | } 249 | return ''+year+month+day; 250 | }; 251 | 252 | // generate a random date between start and end 253 | var genDate = function(start, end) { 254 | return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime())); 255 | }; 256 | 257 | // Get random decades for the choices 258 | var getRandDecades = function(chosenDate){ 259 | var result = []; 260 | var randN; 261 | var decades = ['1850','1860','1870','1880','1890','1900','1910','1920','1930','1940','1950','1960','1970','1980','1990']; 262 | var year = '' + chosenDate.getFullYear(); 263 | year = year.split(''); 264 | year[3] = '0'; 265 | result.push(year.join('')); 266 | while (result.length < 5){ 267 | randN = Math.floor((Math.random() * decades.length)); 268 | if (result.indexOf(decades[randN]) === -1){ 269 | result.push(decades[randN]); 270 | } 271 | } 272 | return result.sort(); 273 | }; 274 | // function to get the articles 275 | var getArticles = function(cb){ 276 | var startDate = new Date(1851, 8, 19); 277 | var endDate = new Date(1999,12,31); 278 | // getting random date 279 | var randDate = genDate(startDate, endDate); 280 | // converting to string 281 | var randomDate = dateToString(randDate); 282 | var parsedArticles = []; 283 | HTTP.get("http://api.nytimes.com/svc/search/v2/articlesearch.json?begin_date="+randomDate+"&end_date="+randomDate+"&api-key=654452f497823c3cdae2119e3bdd6b4f:9:71687614",{}, function(err, data){ 284 | for (var i = 0; i < data.data.response.docs.length; i++){ 285 | if (data.data.response.docs[i].lead_paragraph && parsedArticles.length < 5){ 286 | parsedArticles.push(data.data.response.docs[i].lead_paragraph); 287 | } 288 | } 289 | 290 | var temp = []; 291 | var decades = getRandDecades(randDate); 292 | for (var i = 0; i < decades.length; i++){ 293 | temp.push({'text': decades[i]}); 294 | } 295 | randDate = '' + randDate.getFullYear(); 296 | randDate = randDate.split(''); 297 | randDate[3] = '0'; 298 | randDate = randDate.join(''); 299 | var stringResult = {'result': parsedArticles, 'choices': temp, 'chosen': randDate}; 300 | // TimesHistorianRoom.update({"_id": roomID}, {$set: {'gameBoard': stringResult, 'answered': false}}); 301 | updateCollection(TimesHistorianRoom, roomID, {$set: {'gameBoard': stringResult, 'answered': false}}); 302 | cb(err, data); 303 | }); 304 | }; 305 | var result = Meteor.wrapAsync(getArticles) 306 | return result(); 307 | 308 | } 309 | }); 310 | -------------------------------------------------------------------------------- /client/router.js: -------------------------------------------------------------------------------- 1 | Router.configure({ 2 | layoutTemplate: 'layout' 3 | }); 4 | 5 | Router.route('/', function(){ 6 | this.render('home', {to: 'show'}); 7 | }); 8 | 9 | 10 | Router.route('/cardsagainstsobriety/:room', { 11 | onBeforeAction: function() { 12 | Session.set('currentRoomId', this.params.room); 13 | this.next(); 14 | }, 15 | waitOn: function () { 16 | // subscibe only to the messages for this room 17 | Meteor.subscribe('roomMessages', this.params.room); 18 | }, 19 | data: function () { 20 | var roomMessages = Messages.findOne({roomId: this.params.room}); 21 | return { 22 | messages : roomMessages 23 | } 24 | }, 25 | action: function () { 26 | this.render('cardsAgainstSobriety', {to: 'show'}); 27 | }, 28 | name: 'cardsagainstsobriety2', 29 | //see controller below 30 | controller: 'CASController' 31 | }); 32 | 33 | Router.route('/moviecloud/:room', { 34 | // this template will be rendered until the subscriptions are ready 35 | //loadingTemplate: 'layout', 36 | onBeforeAction: function() { 37 | Meteor.subscribe('MovieRooms', this.params.room); 38 | Meteor.subscribe('roomMessages', this.params.room); 39 | this.next(); 40 | }, 41 | waitOn: function () { 42 | // return one handle, a function, or an array 43 | //Meteor.subscribe('roomMessages', this.params.room); 44 | //Meteor.subscribe('MovieRooms', this.params.room); 45 | }, 46 | action: function () { 47 | this.render('timesHistorian', {to: 'show'}); 48 | serveGame('movieCloud', this.params.room); 49 | Session.set('currentRoomId', this.params.room); 50 | }, 51 | name: 'moviecloudroom' 52 | }); 53 | 54 | Router.route('/timeshistorian/:room', { 55 | // this template will be rendered until the subscriptions are ready 56 | //loadingTemplate: 'layout', 57 | onBeforeAction: function() { 58 | Meteor.subscribe('TimesHistorianRoom', this.params.room); 59 | Meteor.subscribe('roomMessages', this.params.room); 60 | this.next(); 61 | }, 62 | waitOn: function () { 63 | // return one handle, a function, or an array 64 | //Meteor.subscribe('roomMessages', this.params.room); 65 | //Meteor.subscribe('MovieRooms', this.params.room); 66 | }, 67 | action: function () { 68 | this.render('timesHistorian', {to: 'show'}); 69 | serveGame('timesHistorian', this.params.room); 70 | Session.set('currentRoomId', this.params.room); 71 | }, 72 | name: 'timeshistorianroom' 73 | }); 74 | 75 | CASController = RouteController.extend({ 76 | unload: function() { 77 | var roomId = Session.get('currentRoomId'); 78 | //remove user from CardsRoom when they click away from room 79 | var userId = Meteor.user()._id; 80 | var gameInformation = CardsRoom.update({_id: roomId}, {$pull: {users: {'_id': userId}}}); 81 | //TODO: remove user if they close webpage or browser (disconnect) 82 | } 83 | }); 84 | 85 | 86 | 87 | var renderSVG = function(svgData){ 88 | $('.movieSVG').empty(); 89 | var fill = d3.scale.category20(); 90 | d3.layout.cloud() 91 | .size([600, 300]) 92 | .words(svgData) 93 | .padding(5) 94 | .rotate(function() { return ~~(Math.random() * 2) * 90; }) 95 | .font("Impact") 96 | .fontSize(function(d) { return d.size; }) 97 | .on("end", draw) 98 | .start(); 99 | function draw(words) { 100 | d3.select('.movieSVG').append("svg") 101 | .attr("width", 900) 102 | .attr("height", 300) 103 | .style("padding-left",function(){return 150;}) 104 | .style("padding-top",function(){return 50;}) 105 | .style("padding-bottom",function(){return 100;}) 106 | .style("padding-right",function(){return 150;}) 107 | .append("g") 108 | .attr("transform", "translate(150,150)") 109 | .selectAll("text") 110 | .data(words) 111 | .enter().append("text") 112 | .style("font-size", function(d) { return d.size + "px"; }) 113 | .style("font-family", "Impact") 114 | .style("fill", function(d, i) { return fill(i); }) 115 | .attr("text-anchor", "middle") 116 | .attr("transform", function(d) { 117 | return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")"; 118 | }) 119 | .text(function(d) { return d.text; }); 120 | } 121 | }; 122 | 123 | 124 | 125 | // Word cloud layout by Jason Davies, http://www.jasondavies.com/word-cloud/ 126 | // Algorithm due to Jonathan Feinberg, http://static.mrfeinberg.com/bv_ch03.pdf 127 | (function() { 128 | function cloud() { 129 | var size = [256, 256], 130 | text = cloudText, 131 | font = cloudFont, 132 | fontSize = cloudFontSize, 133 | fontStyle = cloudFontNormal, 134 | fontWeight = cloudFontNormal, 135 | rotate = cloudRotate, 136 | padding = cloudPadding, 137 | spiral = archimedeanSpiral, 138 | words = [], 139 | timeInterval = Infinity, 140 | event = d3.dispatch("word", "end"), 141 | timer = null, 142 | cloud = {}; 143 | 144 | cloud.start = function() { 145 | var board = zeroArray((size[0] >> 5) * size[1]), 146 | bounds = null, 147 | n = words.length, 148 | i = -1, 149 | tags = [], 150 | data = words.map(function(d, i) { 151 | d.text = text.call(this, d, i); 152 | d.font = font.call(this, d, i); 153 | d.style = fontStyle.call(this, d, i); 154 | d.weight = fontWeight.call(this, d, i); 155 | d.rotate = rotate.call(this, d, i); 156 | d.size = ~~fontSize.call(this, d, i); 157 | d.padding = padding.call(this, d, i); 158 | return d; 159 | }).sort(function(a, b) { return b.size - a.size; }); 160 | 161 | if (timer) clearInterval(timer); 162 | timer = setInterval(step, 0); 163 | step(); 164 | 165 | return cloud; 166 | 167 | function step() { 168 | var start = +new Date, 169 | d; 170 | while (+new Date - start < timeInterval && ++i < n && timer) { 171 | d = data[i]; 172 | d.x = (size[0] * (Math.random() + .5)) >> 1; 173 | d.y = (size[1] * (Math.random() + .5)) >> 1; 174 | cloudSprite(d, data, i); 175 | if (d.hasText && place(board, d, bounds)) { 176 | tags.push(d); 177 | event.word(d); 178 | if (bounds) cloudBounds(bounds, d); 179 | else bounds = [{x: d.x + d.x0, y: d.y + d.y0}, {x: d.x + d.x1, y: d.y + d.y1}]; 180 | // Temporary hack 181 | d.x -= size[0] >> 1; 182 | d.y -= size[1] >> 1; 183 | } 184 | } 185 | if (i >= n) { 186 | cloud.stop(); 187 | event.end(tags, bounds); 188 | } 189 | } 190 | } 191 | 192 | cloud.stop = function() { 193 | if (timer) { 194 | clearInterval(timer); 195 | timer = null; 196 | } 197 | return cloud; 198 | }; 199 | 200 | cloud.timeInterval = function(x) { 201 | if (!arguments.length) return timeInterval; 202 | timeInterval = x == null ? Infinity : x; 203 | return cloud; 204 | }; 205 | 206 | function place(board, tag, bounds) { 207 | var perimeter = [{x: 0, y: 0}, {x: size[0], y: size[1]}], 208 | startX = tag.x, 209 | startY = tag.y, 210 | maxDelta = Math.sqrt(size[0] * size[0] + size[1] * size[1]), 211 | s = spiral(size), 212 | dt = Math.random() < .5 ? 1 : -1, 213 | t = -dt, 214 | dxdy, 215 | dx, 216 | dy; 217 | 218 | while (dxdy = s(t += dt)) { 219 | dx = ~~dxdy[0]; 220 | dy = ~~dxdy[1]; 221 | 222 | if (Math.min(dx, dy) > maxDelta) break; 223 | 224 | tag.x = startX + dx; 225 | tag.y = startY + dy; 226 | 227 | if (tag.x + tag.x0 < 0 || tag.y + tag.y0 < 0 || 228 | tag.x + tag.x1 > size[0] || tag.y + tag.y1 > size[1]) continue; 229 | // TODO only check for collisions within current bounds. 230 | if (!bounds || !cloudCollide(tag, board, size[0])) { 231 | if (!bounds || collideRects(tag, bounds)) { 232 | var sprite = tag.sprite, 233 | w = tag.width >> 5, 234 | sw = size[0] >> 5, 235 | lx = tag.x - (w << 4), 236 | sx = lx & 0x7f, 237 | msx = 32 - sx, 238 | h = tag.y1 - tag.y0, 239 | x = (tag.y + tag.y0) * sw + (lx >> 5), 240 | last; 241 | for (var j = 0; j < h; j++) { 242 | last = 0; 243 | for (var i = 0; i <= w; i++) { 244 | board[x + i] |= (last << msx) | (i < w ? (last = sprite[j * w + i]) >>> sx : 0); 245 | } 246 | x += sw; 247 | } 248 | delete tag.sprite; 249 | return true; 250 | } 251 | } 252 | } 253 | return false; 254 | } 255 | 256 | cloud.words = function(x) { 257 | if (!arguments.length) return words; 258 | words = x; 259 | return cloud; 260 | }; 261 | 262 | cloud.size = function(x) { 263 | if (!arguments.length) return size; 264 | size = [+x[0], +x[1]]; 265 | return cloud; 266 | }; 267 | 268 | cloud.font = function(x) { 269 | if (!arguments.length) return font; 270 | font = d3.functor(x); 271 | return cloud; 272 | }; 273 | 274 | cloud.fontStyle = function(x) { 275 | if (!arguments.length) return fontStyle; 276 | fontStyle = d3.functor(x); 277 | return cloud; 278 | }; 279 | 280 | cloud.fontWeight = function(x) { 281 | if (!arguments.length) return fontWeight; 282 | fontWeight = d3.functor(x); 283 | return cloud; 284 | }; 285 | 286 | cloud.rotate = function(x) { 287 | if (!arguments.length) return rotate; 288 | rotate = d3.functor(x); 289 | return cloud; 290 | }; 291 | 292 | cloud.text = function(x) { 293 | if (!arguments.length) return text; 294 | text = d3.functor(x); 295 | return cloud; 296 | }; 297 | 298 | cloud.spiral = function(x) { 299 | if (!arguments.length) return spiral; 300 | spiral = spirals[x + ""] || x; 301 | return cloud; 302 | }; 303 | 304 | cloud.fontSize = function(x) { 305 | if (!arguments.length) return fontSize; 306 | fontSize = d3.functor(x); 307 | return cloud; 308 | }; 309 | 310 | cloud.padding = function(x) { 311 | if (!arguments.length) return padding; 312 | padding = d3.functor(x); 313 | return cloud; 314 | }; 315 | 316 | return d3.rebind(cloud, event, "on"); 317 | } 318 | 319 | function cloudText(d) { 320 | return d.text; 321 | } 322 | 323 | function cloudFont() { 324 | return "serif"; 325 | } 326 | 327 | function cloudFontNormal() { 328 | return "normal"; 329 | } 330 | 331 | function cloudFontSize(d) { 332 | return Math.sqrt(d.value); 333 | } 334 | 335 | function cloudRotate() { 336 | return (~~(Math.random() * 6) - 3) * 30; 337 | } 338 | 339 | function cloudPadding() { 340 | return 1; 341 | } 342 | 343 | // Fetches a monochrome sprite bitmap for the specified text. 344 | // Load in batches for speed. 345 | function cloudSprite(d, data, di) { 346 | if (d.sprite) return; 347 | c.clearRect(0, 0, (cw << 5) / ratio, ch / ratio); 348 | var x = 0, 349 | y = 0, 350 | maxh = 0, 351 | n = data.length; 352 | --di; 353 | while (++di < n) { 354 | d = data[di]; 355 | c.save(); 356 | c.font = d.style + " " + d.weight + " " + ~~((d.size + 1) / ratio) + "px " + d.font; 357 | var w = c.measureText(d.text + "m").width * ratio, 358 | h = d.size << 1; 359 | if (d.rotate) { 360 | var sr = Math.sin(d.rotate * cloudRadians), 361 | cr = Math.cos(d.rotate * cloudRadians), 362 | wcr = w * cr, 363 | wsr = w * sr, 364 | hcr = h * cr, 365 | hsr = h * sr; 366 | w = (Math.max(Math.abs(wcr + hsr), Math.abs(wcr - hsr)) + 0x1f) >> 5 << 5; 367 | h = ~~Math.max(Math.abs(wsr + hcr), Math.abs(wsr - hcr)); 368 | } else { 369 | w = (w + 0x1f) >> 5 << 5; 370 | } 371 | if (h > maxh) maxh = h; 372 | if (x + w >= (cw << 5)) { 373 | x = 0; 374 | y += maxh; 375 | maxh = 0; 376 | } 377 | if (y + h >= ch) break; 378 | c.translate((x + (w >> 1)) / ratio, (y + (h >> 1)) / ratio); 379 | if (d.rotate) c.rotate(d.rotate * cloudRadians); 380 | c.fillText(d.text, 0, 0); 381 | if (d.padding) c.lineWidth = 2 * d.padding, c.strokeText(d.text, 0, 0); 382 | c.restore(); 383 | d.width = w; 384 | d.height = h; 385 | d.xoff = x; 386 | d.yoff = y; 387 | d.x1 = w >> 1; 388 | d.y1 = h >> 1; 389 | d.x0 = -d.x1; 390 | d.y0 = -d.y1; 391 | d.hasText = true; 392 | x += w; 393 | } 394 | var pixels = c.getImageData(0, 0, (cw << 5) / ratio, ch / ratio).data, 395 | sprite = []; 396 | while (--di >= 0) { 397 | d = data[di]; 398 | if (!d.hasText) continue; 399 | var w = d.width, 400 | w32 = w >> 5, 401 | h = d.y1 - d.y0; 402 | // Zero the buffer 403 | for (var i = 0; i < h * w32; i++) sprite[i] = 0; 404 | x = d.xoff; 405 | if (x == null) return; 406 | y = d.yoff; 407 | var seen = 0, 408 | seenRow = -1; 409 | for (var j = 0; j < h; j++) { 410 | for (var i = 0; i < w; i++) { 411 | var k = w32 * j + (i >> 5), 412 | m = pixels[((y + j) * (cw << 5) + (x + i)) << 2] ? 1 << (31 - (i % 32)) : 0; 413 | sprite[k] |= m; 414 | seen |= m; 415 | } 416 | if (seen) seenRow = j; 417 | else { 418 | d.y0++; 419 | h--; 420 | j--; 421 | y++; 422 | } 423 | } 424 | d.y1 = d.y0 + seenRow; 425 | d.sprite = sprite.slice(0, (d.y1 - d.y0) * w32); 426 | } 427 | } 428 | 429 | // Use mask-based collision detection. 430 | function cloudCollide(tag, board, sw) { 431 | sw >>= 5; 432 | var sprite = tag.sprite, 433 | w = tag.width >> 5, 434 | lx = tag.x - (w << 4), 435 | sx = lx & 0x7f, 436 | msx = 32 - sx, 437 | h = tag.y1 - tag.y0, 438 | x = (tag.y + tag.y0) * sw + (lx >> 5), 439 | last; 440 | for (var j = 0; j < h; j++) { 441 | last = 0; 442 | for (var i = 0; i <= w; i++) { 443 | if (((last << msx) | (i < w ? (last = sprite[j * w + i]) >>> sx : 0)) 444 | & board[x + i]) return true; 445 | } 446 | x += sw; 447 | } 448 | return false; 449 | } 450 | 451 | function cloudBounds(bounds, d) { 452 | var b0 = bounds[0], 453 | b1 = bounds[1]; 454 | if (d.x + d.x0 < b0.x) b0.x = d.x + d.x0; 455 | if (d.y + d.y0 < b0.y) b0.y = d.y + d.y0; 456 | if (d.x + d.x1 > b1.x) b1.x = d.x + d.x1; 457 | if (d.y + d.y1 > b1.y) b1.y = d.y + d.y1; 458 | } 459 | 460 | function collideRects(a, b) { 461 | return a.x + a.x1 > b[0].x && a.x + a.x0 < b[1].x && a.y + a.y1 > b[0].y && a.y + a.y0 < b[1].y; 462 | } 463 | 464 | function archimedeanSpiral(size) { 465 | var e = size[0] / size[1]; 466 | return function(t) { 467 | return [e * (t *= .1) * Math.cos(t), t * Math.sin(t)]; 468 | }; 469 | } 470 | 471 | function rectangularSpiral(size) { 472 | var dy = 4, 473 | dx = dy * size[0] / size[1], 474 | x = 0, 475 | y = 0; 476 | return function(t) { 477 | var sign = t < 0 ? -1 : 1; 478 | // See triangular numbers: T_n = n * (n + 1) / 2. 479 | switch ((Math.sqrt(1 + 4 * sign * t) - sign) & 3) { 480 | case 0: x += dx; break; 481 | case 1: y += dy; break; 482 | case 2: x -= dx; break; 483 | default: y -= dy; break; 484 | } 485 | return [x, y]; 486 | }; 487 | } 488 | 489 | // TODO reuse arrays? 490 | function zeroArray(n) { 491 | var a = [], 492 | i = -1; 493 | while (++i < n) a[i] = 0; 494 | return a; 495 | } 496 | 497 | var cloudRadians = Math.PI / 180, 498 | cw = 1 << 11 >> 5, 499 | ch = 1 << 11, 500 | canvas, 501 | ratio = 1; 502 | 503 | if (typeof document !== "undefined") { 504 | canvas = document.createElement("canvas"); 505 | canvas.width = 1; 506 | canvas.height = 1; 507 | ratio = Math.sqrt(canvas.getContext("2d").getImageData(0, 0, 1, 1).data.length >> 2); 508 | canvas.width = (cw << 5) / ratio; 509 | canvas.height = ch / ratio; 510 | } else { 511 | // Attempt to use node-canvas. 512 | canvas = new Canvas(cw << 5, ch); 513 | } 514 | 515 | var c = canvas.getContext("2d"), 516 | spirals = { 517 | archimedean: archimedeanSpiral, 518 | rectangular: rectangularSpiral 519 | }; 520 | c.fillStyle = c.strokeStyle = "red"; 521 | c.textAlign = "center"; 522 | 523 | if (typeof module === "object" && module.exports) module.exports = cloud; 524 | else (d3.layout || (d3.layout = {})).cloud = cloud; 525 | })(); 526 | 527 | 528 | var renderLines = function(articles){ 529 | $('.articles').empty(); 530 | for (var i = 0; i < articles.length; i++){ 531 | $('.articles').append(articles[i] + "

"); 532 | } 533 | }; 534 | 535 | 536 | function serveGame (gameName, roomID){ 537 | var gameData = { 538 | timesHistorian: { 539 | collectionName: 'TimesHistorianRoom', 540 | serverCall: 'getTimesData', 541 | renderFunc: renderLines, 542 | textObj: {prompt:'What decade is this from?', 543 | title:'Times Historian', 544 | subtitle:'You made it to Times Historian', 545 | button:'Get NYT data'} 546 | }, 547 | movieCloud: { 548 | collectionName: 'MovieRooms', 549 | serverCall: 'getMovieData', 550 | renderFunc: renderSVG, 551 | textObj: {prompt:'What Movie is this?', 552 | title:'Movie Cloud', 553 | subtitle:'You made it to Movie Cloud', 554 | button:'Get movie data'} 555 | } 556 | } 557 | var data = gameData[gameName] 558 | 559 | Template.timesHistorianHand.created = function () { 560 | // The created function and the ready function work together and 561 | // prevent the template from looking for data before it is loaded, 562 | // which would throw a non-breaking error in browser. 563 | this.subscriptions = [ 564 | Meteor.subscribe(data.collectionName) 565 | ]; 566 | collection = eval(data.collectionName); 567 | }; 568 | 569 | Template.timesHistorianHand.destroyed = function () { 570 | _.each(this.subscriptions, function (sub) { 571 | sub.stop(); 572 | }); 573 | }; 574 | 575 | 576 | Template.timesHistorianHand.helpers({ 577 | getOptions: function(){ 578 | return Session.get(roomID); 579 | }, 580 | ready: function () { 581 | return _.all(Template.instance().subscriptions, function (sub) { 582 | return sub.ready(); 583 | }); 584 | }, 585 | getText: function(){ 586 | return data.textObj; 587 | } 588 | }); 589 | 590 | Template.timesHistorian.helpers({ 591 | getText: function(){ 592 | return data.textObj; 593 | } 594 | }); 595 | 596 | var getAnswer = function(){ 597 | // helper function to get the answer 598 | return Session.get(roomID).gameBoard.chosen; 599 | }; 600 | 601 | // player-hand-view.html template event listeners 602 | Template.timesHistorian.events({ 603 | "click .timesButton": function (event) { 604 | // Create room populates the DB with the neccisarry document and doesn't 605 | // need to be called every time, only when you need to delete and recreate 606 | // document 607 | // If there is no game or if the current game is over 608 | if (!Session.get(roomID) || Session.get(roomID).answered){ 609 | // Calling a function on the server which will update the database 610 | Meteor.call(data.serverCall, roomID, Meteor.user().username, function(err, id){ 611 | // In the call back (which runs after the data is in the db), we set the 612 | // game data to session which triggers full stack reactivity, but we have 613 | // to manually call render SVG as it is not reactive 614 | Session.set(roomID, collection.findOne(roomID)); 615 | var y = collection.findOne(roomID); 616 | data.renderFunc(Session.get(roomID).gameBoard.result); 617 | }); 618 | } else { 619 | alert("The current game is in progress and there is no winnder yet!") 620 | } 621 | }, 622 | 623 | "click .card": function (event){ 624 | var updateScoreBoard = function(playerName, data){ 625 | // In order for Meteor to be able to use the data easily in the template 626 | // it needs to be in this format: 627 | // scoreboard = [{name: eddie, points: 2},{name: jonah, points: 1}] 628 | // this function gets the data into that format 629 | // This function would not exist if data looked like the following 630 | // {eddie: 2, jonah: 1} 631 | // But then we would have to convert data from that format to the 632 | // Meteoresque format every time the reactive data updates, which would be 633 | // inefficient. Full stack reactivity works best when the data is in teh right 634 | // format in the db. 635 | var exists = false; 636 | for (var i = 0; i < data.scoreBoard.length; i++){ 637 | if (data.scoreBoard[i].name === playerName){ 638 | exists = true; 639 | data.scoreBoard[i].points++; 640 | } 641 | } 642 | if (!exists){ 643 | data.scoreBoard.push({name: playerName, points: 1}); 644 | } 645 | return data; 646 | }; 647 | 648 | 649 | // Getting the data from Session 650 | // Rather than making several updates to the DB we 'check out' the data 651 | // and only make 1 update 652 | var data = Session.get(roomID); 653 | // Checking to see if it is the right answer 654 | if (this.text.split(' ').join('_')===getAnswer()){ 655 | // Checking to see if the round has already been won 656 | if (!data.answered){ 657 | // Getting username 658 | var me = Meteor.user().username; 659 | // Updating score board is verbose due to data format so logic is delegated 660 | data = updateScoreBoard(me, data); 661 | data.roundInfo.roundNum += 1; 662 | data.roundInfo.lastWinner = me; 663 | // Signalling that the round is over 664 | data.answered = true; 665 | // When updating you can't update _id so it is deleted from data object 666 | delete data['_id']; 667 | // Checking data back into db 668 | collection.update(roomID, {$set: data}); 669 | // updating the Session - This will get re-set to exactlyt he same when Meteor 670 | // triggers the update from the server, so essentially this is latency compensation 671 | Session.set(roomID,collection.findOne(roomID)); 672 | alert('You won!'); 673 | } else{ 674 | alert('You are right, but not the first one to answer') 675 | } 676 | } else{ 677 | // This is here to slow players down, i.e. you can't click all buttons without thinking 678 | alert('Wrong answer') 679 | } 680 | } 681 | }); 682 | }; 683 | 684 | Meteor.subscribe("TimesHistorianRoom"); 685 | Meteor.subscribe("MovieRooms"); 686 | 687 | //serveGame('movieCloud','BCDEFG'); 688 | // serveGame('timesHistorian','BCDEFG'); 689 | -------------------------------------------------------------------------------- /server/blackCards.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by ppp on 3/25/2015. 3 | */ 4 | BlackCards = [{"cardType": "Black", "text": "_? There's an app for that.", "expansion": "Base"}, 5 | {"cardType": "Black", "text": "Why can't I sleep at night?", "expansion": "Base"}, 6 | {"cardType": "Black", "text": "What's that smell?", "expansion": "Base"}, 7 | {"cardType": "Black", "text": "I got 99 problems but _ ain't one.", "expansion": "Base"}, 8 | {"cardType": "Black", "text": "Maybe she's born with it. Maybe it's _.", "expansion": "Base"}, 9 | {"cardType": "Black", "text": "What's the next Happy Meal© toy?", "expansion": "Base"}, 10 | { 11 | "cardType": "Black", 12 | "text": "Anthropologists have recently discovered a primitive tribe that worships _.", 13 | "expansion": "Base" 14 | }, 15 | { 16 | "cardType": "Black", 17 | "text": "It's a pity that kids these days are all getting involved with _.", 18 | "expansion": "Base" 19 | }, 20 | { 21 | "cardType": "Black", 22 | "text": "During Picasso's often-overlooked Brown Period, he produced hundreds of paintings of _.", 23 | "expansion": "Base" 24 | }, 25 | {"cardType": "Black", "text": "Alternative medicine is now embracing the curative powers of _.", "expansion": "Base"}, 26 | {"cardType": "Black", "text": "And the Academy Award for _ goes to _.", "expansion": "Base"}, 27 | {"cardType": "Black", "text": "What's that sound?", "expansion": "Base"}, 28 | {"cardType": "Black", "text": "What ended my last relationship?", "expansion": "Base"}, 29 | { 30 | "cardType": "Black", 31 | "text": "MTV's new reality show features eight washed-up celebrities living with _.", 32 | "expansion": "Base" 33 | }, 34 | {"cardType": "Black", "text": "I drink to forget _.", "expansion": "Base"}, 35 | { 36 | "cardType": "Black", 37 | "text": "I'm sorry professor, but I couldn't complete my homework because of _.", 38 | "expansion": "Base" 39 | }, 40 | {"cardType": "Black", "text": "What is Batman's guilty pleasure?", "expansion": "Base"}, 41 | { 42 | "cardType": "Black", 43 | "text": "This is the way the world ends
This is the way the world ends
Not with a bang but with _.", 44 | "expansion": "Base" 45 | }, 46 | {"cardType": "Black", "text": "What's a girl's best friend?", "expansion": "Base"}, 47 | {"cardType": "Black", "text": "TSA guidelines now prohibit _ on airplanes.", "expansion": "Base"}, 48 | {"cardType": "Black", "text": "_. That's how I want to die.", "expansion": "Base"}, 49 | {"cardType": "Black", "text": "For my next trick, I will pull _ out of _.", "expansion": "Base"}, 50 | { 51 | "cardType": "Black", 52 | "text": "In the new Disney Channel Original Movie, Hannah Montana struggles with _ for the first time.", 53 | "expansion": "Base" 54 | }, 55 | {"cardType": "Black", "text": "_ is a slippery slope that leads to _.", "expansion": "Base"}, 56 | {"cardType": "Black", "text": "What does Dick Cheney prefer?", "expansion": "Base"}, 57 | { 58 | "cardType": "Black", 59 | "text": "Dear Abby, I'm having some trouble with _ and would like your advice.", 60 | "expansion": "Base" 61 | }, 62 | {"cardType": "Black", "text": "Instead of coal, Santa now gives the bad children _.", "expansion": "Base"}, 63 | {"cardType": "Black", "text": "What's the most emo?", "expansion": "Base"}, 64 | { 65 | "cardType": "Black", 66 | "text": "In 1,000 years when paper money is but a distant memory, _ will be our currency.", 67 | "expansion": "Base" 68 | }, 69 | {"cardType": "Black", "text": "What's the next superhero/sidekick duo?", "expansion": "Base"}, 70 | { 71 | "cardType": "Black", 72 | "text": "In M. Night Shyamalan's new movie, Bruce Willis discovers that _ had really been _ all along.", 73 | "expansion": "Base" 74 | }, 75 | {"cardType": "Black", "text": "A romantic, candlelit dinner would be incomplete without _.", "expansion": "Base"}, 76 | {"cardType": "Black", "text": "_. Becha can't have just one!", "expansion": "Base"}, 77 | {"cardType": "Black", "text": "White people like _.", "expansion": "Base"}, 78 | {"cardType": "Black", "text": "_. High five, bro.", "expansion": "Base"}, 79 | {"cardType": "Black", "text": "Next from J.K. Rowling: Harry Potter and the Chamber of _.", "expansion": "Base"}, 80 | {"cardType": "Black", "text": "BILLY MAYS HERE FOR _.", "expansion": "Base"}, 81 | {"cardType": "Black", "text": "In a world ravaged by _, our only solace is _.", "expansion": "Base"}, 82 | {"cardType": "Black", "text": "War! What is it good for?", "expansion": "Base"}, 83 | {"cardType": "Black", "text": "During sex, I like to think about _.", "expansion": "Base"}, 84 | {"cardType": "Black", "text": "What are my parents hiding from me?", "expansion": "Base"}, 85 | {"cardType": "Black", "text": "What will always get you laid?", "expansion": "Base"}, 86 | { 87 | "cardType": "Black", 88 | "text": "In L.A. County Jail, word is you can trade 200 cigarettes for _.", 89 | "expansion": "Base" 90 | }, 91 | {"cardType": "Black", "text": "What did I bring back from Mexico?", "expansion": "Base"}, 92 | {"cardType": "Black", "text": "What don't you want to find in your Chinese food?", "expansion": "Base"}, 93 | { 94 | "cardType": "Black", 95 | "text": "What will I bring back in time to convince people that I am a powerful wizard?", 96 | "expansion": "Base" 97 | }, 98 | {"cardType": "Black", "text": "How am I maintaining my relationship status?", "expansion": "Base"}, 99 | {"cardType": "Black", "text": "_. It's a trap!", "expansion": "Base"}, 100 | {"cardType": "Black", "text": "Coming to Broadway this season, _: The Musical.", "expansion": "Base"}, 101 | { 102 | "cardType": "Black", 103 | "text": "While the United States raced the Soviet Union to the moon, the Mexican government funneled millions of pesos into research on _.", 104 | "expansion": "Base" 105 | }, 106 | { 107 | "cardType": "Black", 108 | "text": "After the earthquake, Sean Penn brought _ to the people of Haiti.", 109 | "expansion": "Base" 110 | }, 111 | {"cardType": "Black", "text": "Next on ESPN2, the World Series of _.", "expansion": "Base"}, 112 | {"cardType": "Black", "text": "Step 1: _. Step 2: _. Step 3: Profit.", "expansion": "Base"}, 113 | { 114 | "cardType": "Black", 115 | "text": "Rumor has it that Vladimir Putin's favorite dish is _ stuffed with _.", 116 | "expansion": "Base" 117 | }, 118 | {"cardType": "Black", "text": "But before I kill you, Mr. Bond, I must show you _.", "expansion": "Base"}, 119 | {"cardType": "Black", "text": "What gives me uncontrollable gas?", "expansion": "Base"}, 120 | {"cardType": "Black", "text": "What do old people smell like?", "expansion": "Base"}, 121 | {"cardType": "Black", "text": "The class field trip was completely ruined by _.", "expansion": "Base"}, 122 | {"cardType": "Black", "text": "When Pharaoh remained unmoved, Moses called down a Plague of _.", "expansion": "Base"}, 123 | {"cardType": "Black", "text": "What's my secret power?", "expansion": "Base"}, 124 | {"cardType": "Black", "text": "What's there a ton of in heaven?", "expansion": "Base"}, 125 | {"cardType": "Black", "text": "What would grandma find disturbing, yet oddly charming?", "expansion": "Base"}, 126 | {"cardType": "Black", "text": "I never truly understood _ until I encountered _.", "expansion": "Base"}, 127 | {"cardType": "Black", "text": "What did the U.S. airdrop to the children of Afghanistan?", "expansion": "Base"}, 128 | {"cardType": "Black", "text": "What helps Obama unwind?", "expansion": "Base"}, 129 | {"cardType": "Black", "text": "What did Vin Diesel eat for dinner?", "expansion": "Base"}, 130 | {"cardType": "Black", "text": "_: good to the last drop.", "expansion": "Base"}, 131 | {"cardType": "Black", "text": "Why am I sticky?", "expansion": "Base"}, 132 | {"cardType": "Black", "text": "What gets better with age?", "expansion": "Base"}, 133 | {"cardType": "Black", "text": "_: kid-tested, mother-approved.", "expansion": "Base"}, 134 | {"cardType": "Black", "text": "What's the crustiest?", "expansion": "Base"}, 135 | { 136 | "cardType": "Black", 137 | "text": "What's Teach for America using to inspire inner city students to succeed?", 138 | "expansion": "Base" 139 | }, 140 | { 141 | "cardType": "Black", 142 | "text": "Studies show that lab rats navigate mazes 50% faster after being exposed to _.", 143 | "expansion": "Base" 144 | }, 145 | { 146 | "cardType": "Black", 147 | "text": "Life for American Indians was forever changed when the White Man introduced them to _.", 148 | "expansion": "Base" 149 | }, 150 | {"cardType": "Black", "text": "Make a haiku.", "expansion": "Base"}, 151 | { 152 | "cardType": "Black", 153 | "text": "I do not know with what weapons World War III will be fought, but World War IV will be fought with _.", 154 | "expansion": "Base" 155 | }, 156 | {"cardType": "Black", "text": "Why do I hurt all over?", "expansion": "Base"}, 157 | {"cardType": "Black", "text": "What am I giving up for Lent?", "expansion": "Base"}, 158 | {"cardType": "Black", "text": "In Michael Jackson's final moments, he thought about _.", "expansion": "Base"}, 159 | { 160 | "cardType": "Black", 161 | "text": "In an attempt to reach a wider audience, the Smithsonian Museum of Natural History has opened an interactive exhibit on _.", 162 | "expansion": "Base" 163 | }, 164 | { 165 | "cardType": "Black", 166 | "text": "When I am President of the United States, I will create the Department of _.", 167 | "expansion": "Base" 168 | }, 169 | {"cardType": "Black", "text": "Lifetime© presents _, the story of _.", "expansion": "Base"}, 170 | { 171 | "cardType": "Black", 172 | "text": "When I am a billionaire, I shall erect a 50-foot statue to commemorate _.", 173 | "expansion": "Base" 174 | }, 175 | {"cardType": "Black", "text": "When I was tripping on acid, _ turned into _.", "expansion": "Base"}, 176 | {"cardType": "Black", "text": "That's right, I killed _. How, you ask? _.", "expansion": "Base"}, 177 | {"cardType": "Black", "text": "What's my anti-drug?", "expansion": "Base"}, 178 | {"cardType": "Black", "text": "_ + _ = _.", "expansion": "Base"}, 179 | {"cardType": "Black", "text": "What never fails to liven up the party?", "expansion": "Base"}, 180 | {"cardType": "Black", "text": "What's the new fad diet?", "expansion": "Base"}, 181 | { 182 | "cardType": "Black", 183 | "text": "Major League Baseball has banned _ for giving players an unfair advantage.", 184 | "expansion": "Base" 185 | }, 186 | {"cardType": "Black", "text": "My plan for world domination begins with _.", "expansion": "CAHe1"}, 187 | { 188 | "cardType": "Black", 189 | "text": "The CIA now interrogates enemy agents by repeatedly subjecting them to _.", 190 | "expansion": "CAHe1" 191 | }, 192 | { 193 | "cardType": "Black", 194 | "text": "Dear Sir or Madam, We regret to inform you that the Office of _ has denied your request for _", 195 | "expansion": "CAHe1" 196 | }, 197 | { 198 | "cardType": "Black", 199 | "text": "In Rome, there are whisperings that the Vatican has a secret room devoted to _.", 200 | "expansion": "CAHe1" 201 | }, 202 | {"cardType": "Black", "text": "Science will never explain _.", "expansion": "CAHe1"}, 203 | {"cardType": "Black", "text": "When all else fails, I can always masturbate to _.", "expansion": "CAHe1"}, 204 | { 205 | "cardType": "Black", 206 | "text": "I learned the hard way that you can't cheer up a grieving friend with _.", 207 | "expansion": "CAHe1" 208 | }, 209 | { 210 | "cardType": "Black", 211 | "text": "In its new tourism campaign, Detroit proudly proclaims that it has finally eliminated _.", 212 | "expansion": "CAHe1" 213 | }, 214 | {"cardType": "Black", "text": "An international tribunal has found _ guilty of _.", "expansion": "CAHe1"}, 215 | { 216 | "cardType": "Black", 217 | "text": "The socialist governments of Scandinavia have declared that access to _ is a basic human right.", 218 | "expansion": "CAHe1" 219 | }, 220 | { 221 | "cardType": "Black", 222 | "text": "In his new self-produced album, Kanye West raps over the sounds of _.", 223 | "expansion": "CAHe1" 224 | }, 225 | {"cardType": "Black", "text": "What's the gift that keeps on giving?", "expansion": "CAHe1"}, 226 | { 227 | "cardType": "Black", 228 | "text": "Next season on Man vs. Wild, Bear Grylls must survive in the depths of the Amazon with only _ and his wits.", 229 | "expansion": "CAHe1" 230 | }, 231 | {"cardType": "Black", "text": "When I pooped, what came out of my butt?", "expansion": "CAHe1"}, 232 | { 233 | "cardType": "Black", 234 | "text": "In the distant future, historians will agree that _ marked the beginning of America's decline.", 235 | "expansion": "CAHe1" 236 | }, 237 | {"cardType": "Black", "text": "In a pinch, _ can be a suitable substitute for _.", "expansion": "CAHe1"}, 238 | {"cardType": "Black", "text": "What has been making life difficult at the nudist colony?", "expansion": "CAHe1"}, 239 | {"cardType": "Black", "text": "Michael Bay's new three-hour action epic pits _ against _.", "expansion": "CAHe1"}, 240 | { 241 | "cardType": "Black", 242 | "text": "And I would have gotten away with it, too, if it hadn't been for _.", 243 | "expansion": "CAHe1" 244 | }, 245 | {"cardType": "Black", "text": "What brought the orgy to a grinding halt?", "expansion": "CAHe1"}, 246 | {"cardType": "Black", "text": "During his midlife crisis, my dad got really into _.", "expansion": "CAHe2"}, 247 | {"cardType": "Black", "text": "_ would be woefully incomplete without _.", "expansion": "CAHe2"}, 248 | {"cardType": "Black", "text": "My new favorite porn star is Joey "_" McGee.", "expansion": "CAHe2"}, 249 | { 250 | "cardType": "Black", 251 | "text": "Before I run for president, I must destroy all evidence of my involvement with _.", 252 | "expansion": "CAHe2" 253 | }, 254 | { 255 | "cardType": "Black", 256 | "text": "This is your captain speaking. Fasten your seatbelts and prepare for _.", 257 | "expansion": "CAHe2" 258 | }, 259 | { 260 | "cardType": "Black", 261 | "text": "In his newest and most difficult stunt, David Blaine must escape from _.", 262 | "expansion": "CAHe2" 263 | }, 264 | { 265 | "cardType": "Black", 266 | "text": "The Five Stages of Grief: denial, anger, bargaining, _, and acceptance.", 267 | "expansion": "CAHe2" 268 | }, 269 | { 270 | "cardType": "Black", 271 | "text": "My mom freaked out when she looked at my browser history and found _.com/_.", 272 | "expansion": "CAHe2" 273 | }, 274 | {"cardType": "Black", "text": "I went from _ to _, all thanks to _.", "expansion": "CAHe2"}, 275 | { 276 | "cardType": "Black", 277 | "text": "Members of New York's social elite are paying thousands of dollars just to experience _.", 278 | "expansion": "CAHe2" 279 | }, 280 | { 281 | "cardType": "Black", 282 | "text": "This month's Cosmo: "Spice up your sex life by bringing _ into the bedroom."", 283 | "expansion": "CAHe2" 284 | }, 285 | {"cardType": "Black", "text": "Little Miss Muffet Sat on a tuffet, Eating her curds and _.", "expansion": "CAHe2"}, 286 | {"cardType": "Black", "text": "If God didn't want us to enjoy _, he wouldn't have given us _.", "expansion": "CAHe2"}, 287 | {"cardType": "Black", "text": "My country, 'tis of thee, sweet land of _.", "expansion": "CAHe2"}, 288 | { 289 | "cardType": "Black", 290 | "text": "After months of debate, the Occupy Wall Street General Assembly could only agree on "More _!"", 291 | "expansion": "CAHe2" 292 | }, 293 | { 294 | "cardType": "Black", 295 | "text": "I spent my whole life working toward _, only to have it ruined by _.", 296 | "expansion": "CAHe2" 297 | }, 298 | {"cardType": "Black", "text": "Next time on Dr. Phil: How to talk to your child about _.", "expansion": "CAHe2"}, 299 | {"cardType": "Black", "text": "Only two things in life are certain: death and _.", "expansion": "CAHe2"}, 300 | { 301 | "cardType": "Black", 302 | "text": "Everyone down on the ground! We don't want to hurt anyone. We're just here for _.", 303 | "expansion": "CAHe2" 304 | }, 305 | { 306 | "cardType": "Black", 307 | "text": "The healing process began when I joined a support group for victims of _.", 308 | "expansion": "CAHe2" 309 | }, 310 | {"cardType": "Black", "text": "The votes are in, and the new high school mascot is _.", "expansion": "CAHe2"}, 311 | { 312 | "cardType": "Black", 313 | "text": "Charades was ruined for me forever when my mom had to act out _.", 314 | "expansion": "CAHe2" 315 | }, 316 | {"cardType": "Black", "text": "Before _, all we had was _.", "expansion": "CAHe2"}, 317 | {"cardType": "Black", "text": "Tonight on 20/20: What you don't know about _ could kill you.", "expansion": "CAHe2"}, 318 | { 319 | "cardType": "Black", 320 | "text": "You haven't truly lived until you've experienced _ and _ at the same time.", 321 | "expansion": "CAHe2" 322 | }, 323 | {"cardType": "Black", "text": "D&D 4.0 isn't real D&D because of the _.", "expansion": "CAHgrognards"}, 324 | {"cardType": "Black", "text": "It's a D&D retroclone with _ added.", "expansion": "CAHgrognards"}, 325 | {"cardType": "Black", "text": "Storygames aren't RPGs because of the _.", "expansion": "CAHgrognards"}, 326 | {"cardType": "Black", "text": "The Slayer's Guide to _.", "expansion": "CAHgrognards"}, 327 | {"cardType": "Black", "text": "Worst character concept ever: _, but with _.", "expansion": "CAHgrognards"}, 328 | {"cardType": "Black", "text": "Alightment: Chaotic _", "expansion": "CAHgrognards"}, 329 | {"cardType": "Black", "text": "It's a D&D retroclone with _ added.", "expansion": "CAHgrognards"}, 330 | {"cardType": "Black", "text": "What made the paladin fall? _", "expansion": "CAHgrognards"}, 331 | {"cardType": "Black", "text": "The portal leads to the quasi-elemental plane of _.", "expansion": "CAHgrognards"}, 332 | {"cardType": "Black", "text": "The Temple of Elemental _.", "expansion": "CAHgrognards"}, 333 | {"cardType": "Black", "text": "Pathfinder is basically D&D _ Edition.", "expansion": "CAHgrognards"}, 334 | {"cardType": "Black", "text": "_ : The Storytelling Game.", "expansion": "CAHgrognards"}, 335 | { 336 | "cardType": "Black", 337 | "text": "People are wondering why Steve Jackson published GURPS _.", 338 | "expansion": "CAHgrognards" 339 | }, 340 | {"cardType": "Black", "text": "Linear Fighter, Quadratic _.", "expansion": "CAHgrognards"}, 341 | {"cardType": "Black", "text": "You start with 1d4 _ points.", "expansion": "CAHgrognards"}, 342 | { 343 | "cardType": "Black", 344 | "text": "Back when I was 12 and I was just starting playing D&D, the game had _.", 345 | "expansion": "CAHgrognards" 346 | }, 347 | {"cardType": "Black", "text": "Big Eyes, Small _.", "expansion": "CAHgrognards"}, 348 | {"cardType": "Black", "text": "In the grim darkness of the future there is only _.", "expansion": "CAHgrognards"}, 349 | {"cardType": "Black", "text": "My innovative new RPG has a stat for _.", "expansion": "CAHgrognards"}, 350 | {"cardType": "Black", "text": "A true gamer has no problem with _.", "expansion": "CAHgrognards"}, 351 | { 352 | "cardType": "Black", 353 | "text": "Elminster cast a potent _ spell and then had sex with _.", 354 | "expansion": "CAHgrognards" 355 | }, 356 | {"cardType": "Black", "text": "The Deck of Many _.", "expansion": "CAHgrognards"}, 357 | {"cardType": "Black", "text": "You are all at a tavern when _ approach you.", "expansion": "CAHgrognards"}, 358 | { 359 | "cardType": "Black", 360 | "text": "For the convention I cosplayed as Sailor Moon, except with _.", 361 | "expansion": "CAHweeaboo" 362 | }, 363 | {"cardType": "Black", "text": "The worst part of Grave of the Fireflies is all the _.", "expansion": "CAHweeaboo"}, 364 | {"cardType": "Black", "text": "In the Evangelion remake, Shinji has to deal with _.", "expansion": "CAHweeaboo"}, 365 | {"cardType": "Black", "text": "Worst anime convention purchase ever? _.", "expansion": "CAHweeaboo"}, 366 | {"cardType": "Black", "text": "While powering up Vegeta screamed, _!", "expansion": "CAHweeaboo"}, 367 | {"cardType": "Black", "text": "You evaded my _ attack. Most impressive.", "expansion": "CAHweeaboo"}, 368 | {"cardType": "Black", "text": "I downloaded a doujin where _ got into _.", "expansion": "CAHweeaboo"}, 369 | { 370 | "cardType": "Black", 371 | "text": "The magical girl found out that the Power of Love is useless against _.", 372 | "expansion": "CAHweeaboo" 373 | }, 374 | { 375 | "cardType": "Black", 376 | "text": "The Japanese government has spent billions of yen researching _.", 377 | "expansion": "CAHweeaboo" 378 | }, 379 | {"cardType": "Black", "text": "In the dubbed version they changed _ into _.", "expansion": "CAHweeaboo"}, 380 | {"cardType": "Black", "text": "_ is Best Pony.", "expansion": "CAHweeaboo"}, 381 | {"cardType": "Black", "text": "The _ of Haruhi Suzumiya.", "expansion": "CAHweeaboo"}, 382 | { 383 | "cardType": "Black", 384 | "text": "The new thing in Akihabara is fetish cafes where you can see girls dressed up as _.", 385 | "expansion": "CAHweeaboo" 386 | }, 387 | {"cardType": "Black", "text": "Your drill can pierce _!", "expansion": "CAHweeaboo"}, 388 | {"cardType": "Black", "text": "Avatar: The Last _ bender.", "expansion": "CAHweeaboo"}, 389 | {"cardType": "Black", "text": "In the name of _ Sailor Moon will punish you!", "expansion": "CAHweeaboo"}, 390 | {"cardType": "Black", "text": "No harem anime is complete without _.", "expansion": "CAHweeaboo"}, 391 | {"cardType": "Black", "text": "My boyfriend's a _ now.", "expansion": "CAHweeaboo"}, 392 | {"cardType": "Black", "text": "The _ of _ has left me in despair!", "expansion": "CAHweeaboo"}, 393 | {"cardType": "Black", "text": "_.tumblr.com", "expansion": "CAHweeaboo"}, 394 | {"cardType": "Black", "text": "Somehow they made a cute mascot girl out of _.", "expansion": "CAHweeaboo"}, 395 | { 396 | "cardType": "Black", 397 | "text": "Haruko hit Naoto in the head with her bass guitar and _ came out.", 398 | "expansion": "CAHweeaboo" 399 | }, 400 | {"cardType": "Black", "text": "After blacking out during New year's Eve, I was awoken by _.", "expansion": "CAHxmas"}, 401 | { 402 | "cardType": "Black", 403 | "text": "This holiday season, Tim Allen must overcome his fear of _ to save Christmas.", 404 | "expansion": "CAHxmas" 405 | }, 406 | {"cardType": "Black", "text": "Jesus is _.", "expansion": "CAHxmas"}, 407 | { 408 | "cardType": "Black", 409 | "text": "Every Christmas, my uncle gets drunk and tells the story about _.", 410 | "expansion": "CAHxmas" 411 | }, 412 | {"cardType": "Black", "text": "What keeps me warm during the cold, cold, winter?", "expansion": "CAHxmas"}, 413 | { 414 | "cardType": "Black", 415 | "text": "On the third day of Christmas, my true love gave to me: three French hens, two turtle doves, and _.", 416 | "expansion": "CAHxmas" 417 | }, 418 | { 419 | "cardType": "Black", 420 | "text": "Wake up, America. Christmas is under attack by secular liberals and their _.", 421 | "expansion": "CAHxmas" 422 | }, 423 | {"cardType": "Black", "text": "We got the third rope, now where's the fourth?", "expansion": "NEIndy"}, 424 | {"cardType": "Black", "text": "Tonights main event, _ vs. _.", "expansion": "NEIndy"}, 425 | {"cardType": "Black", "text": "Tackle, Dropdown, _.", "expansion": "NEIndy"}, 426 | {"cardType": "Black", "text": "Christopher Daniels is late on his _.", "expansion": "NEIndy"}, 427 | {"cardType": "Black", "text": "Instead of booking _, they should have booked _.", "expansion": "NEIndy"}, 428 | {"cardType": "Black", "text": "Genius is 10% inspiration, 90% _.", "expansion": "NEIndy"}, 429 | {"cardType": "Black", "text": "They found _ in the dumpster behind _.", "expansion": "NEIndy"}, 430 | {"cardType": "Black", "text": "The best thing I ever got for Christmas was _.", "expansion": "NEIndy"}, 431 | {"cardType": "Black", "text": "There's no crying in _.", "expansion": "NEIndy"}, 432 | {"cardType": "Black", "text": "Mastodon! Pterodactyl! Triceratops! Sabretooth Tiger! _!", "expansion": "NEIndy"}, 433 | {"cardType": "Black", "text": "Don't eat the _.", "expansion": "NEIndy"}, 434 | {"cardType": "Black", "text": "He did _ with the _!?!", "expansion": "NEIndy"}, 435 | {"cardType": "Black", "text": "SOOOOO hot, want to touch the _.", "expansion": "NEIndy"}, 436 | {"cardType": "Black", "text": "Stop looking at me _!", "expansion": "NEIndy"}, 437 | {"cardType": "Black", "text": "I'm cuckoo for _ puffs.", "expansion": "NEIndy"}, 438 | {"cardType": "Black", "text": "Silly rabbit, _ are for kids.", "expansion": "NEIndy"}, 439 | {"cardType": "Black", "text": "Between love and madness lies _.", "expansion": "NSFH"}, 440 | { 441 | "cardType": "Black", 442 | "text": "Instead of chess, the Grim Reaper now gambles for your soul with a game of _.", 443 | "expansion": "NSFH" 444 | }, 445 | {"cardType": "Black", "text": "My father gave his life fighting to protect _ from _.", "expansion": "NSFH"}, 446 | {"cardType": "Black", "text": "Why is my throat sore?", "expansion": "NSFH"}, 447 | {"cardType": "Black", "text": "_ sparked a city-wide riot that only ended with _.", "expansion": "NSFH"}, 448 | { 449 | "cardType": "Black", 450 | "text": "I?m very sorry Mrs. Smith, but Little Billy has tested positive for _.", 451 | "expansion": "NSFH" 452 | }, 453 | {"cardType": "Black", "text": "Instead of beating them, Chris Brown now does _ to women.", "expansion": "NSFH"}, 454 | {"cardType": "Black", "text": "Instead of cutting, trendy young emo girls now engage in _.", "expansion": "NSFH"}, 455 | {"cardType": "Black", "text": "The definition of rock bottom is gambling away _.", "expansion": "NSFH"}, 456 | {"cardType": "Black", "text": "The Mayan prophecies really heralded the coming of _ in 2012.", "expansion": "NSFH"}, 457 | { 458 | "cardType": "Black", 459 | "text": "The next US election will be fought on the key issues of _ against _.", 460 | "expansion": "NSFH" 461 | }, 462 | {"cardType": "Black", "text": "When I was 10 I wrote to Santa wishing for _.", "expansion": "NSFH"}, 463 | {"cardType": "Black", "text": "Where or How I met my last signifigant other: _.", "expansion": "NSFH"}, 464 | {"cardType": "Black", "text": "_, Never leave home without it.", "expansion": "NSFH"}, 465 | {"cardType": "Black", "text": "_. This is my fetish.", "expansion": "NSFH"}, 466 | {"cardType": "Black", "text": "David Icke's newest conspiracy theory states that _ caused _.", "expansion": "NSFH"}, 467 | {"cardType": "Black", "text": "I did _ so you don't have to!", "expansion": "NSFH"}, 468 | {"cardType": "Black", "text": "I need your clothes, your bike, and _.", "expansion": "NSFH"}, 469 | { 470 | "cardType": "Black", 471 | "text": "In a new Cold War retro movie, the red menace tries to conquer the world through the cunning use of _.", 472 | "expansion": "NSFH" 473 | }, 474 | { 475 | "cardType": "Black", 476 | "text": "In college, our lecturer made us write a report comparing _ to _.", 477 | "expansion": "NSFH" 478 | }, 479 | { 480 | "cardType": "Black", 481 | "text": "In The Hangover part 3, those four guys have to deal with _, _, and _.", 482 | "expansion": "NSFH" 483 | }, 484 | {"cardType": "Black", "text": "My zombie survival kit includes food, water, and _.", "expansion": "NSFH"}, 485 | {"cardType": "Black", "text": "The way to a man's heart is through _.", "expansion": "NSFH"}, 486 | {"cardType": "Black", "text": "What was the theme of my second wedding?", "expansion": "NSFH"}, 487 | {"cardType": "Black", "text": "What's the newest Japanese craze to head West?", "expansion": "NSFH"}, 488 | {"cardType": "Black", "text": "Everybody loves _.", "expansion": "NSFH"}, 489 | {"cardType": "Black", "text": "I can only express myself through _.", "expansion": "NSFH"}, 490 | {"cardType": "Black", "text": "My new porn DVD was completely ruined by the inclusion of _", "expansion": "NSFH"}, 491 | {"cardType": "Black", "text": "My three wishes will be for _, _, and _.", "expansion": "NSFH"}, 492 | {"cardType": "Black", "text": "The latest horrifying school shooting was inspired by _.", "expansion": "NSFH"}, 493 | {"cardType": "Black", "text": "I got fired because of my not-so-secret obsession over _.", "expansion": "NSFH"}, 494 | {"cardType": "Black", "text": "My new favourite sexual position is _", "expansion": "NSFH"}, 495 | { 496 | "cardType": "Black", 497 | "text": "A successful job interview begins with a firm handshake and ends with _.", 498 | "expansion": "CAHe3" 499 | }, 500 | {"cardType": "Black", "text": "Lovin' you is easy 'cause you're _.", "expansion": "CAHe3"}, 501 | {"cardType": "Black", "text": "My life is ruled by a vicious cycle of _ and _.", "expansion": "CAHe3"}, 502 | { 503 | "cardType": "Black", 504 | "text": "The blind date was going horribly until we discovered our shared interest in _.", 505 | "expansion": "CAHe3" 506 | }, 507 | {"cardType": "Black", "text": "_. Awesome in theory, kind of a mess in practice.", "expansion": "CAHe3"}, 508 | {"cardType": "Black", "text": "I'm not like the rest of you. I'm too rich and busy for _.", "expansion": "CAHe3"}, 509 | { 510 | "cardType": "Black", 511 | "text": "In the seventh circle of Hell, sinners must endure _ for all eternity.", 512 | "expansion": "CAHe3" 513 | }, 514 | {"cardType": "Black", "text": "_: Hours of fun. Easy to use. Perfect for _!", "expansion": "CAHe3"}, 515 | {"cardType": "Black", "text": "What left this stain on my couch?", "expansion": "CAHe3"}, 516 | { 517 | "cardType": "Black", 518 | "text": "Call the law offices of Goldstein & Goldstein, because no one should have to tolerate _ in the workplace.", 519 | "expansion": "CAHe3" 520 | }, 521 | {"cardType": "Black", "text": "When you get right down to it, _ is just _.", "expansion": "CAHe3"}, 522 | { 523 | "cardType": "Black", 524 | "text": "Turns out that _-Man was neither the hero we needed nor wanted.", 525 | "expansion": "CAHe3" 526 | }, 527 | { 528 | "cardType": "Black", 529 | "text": "As part of his daily regimen, Anderson Cooper sets aside 15 minutes for _.", 530 | "expansion": "CAHe3" 531 | }, 532 | {"cardType": "Black", "text": "Money can't buy me love, but it can buy me _.", "expansion": "CAHe3"}, 533 | {"cardType": "Black", "text": "With enough time and pressure, _ will turn into _.", "expansion": "CAHe3"}, 534 | {"cardType": "Black", "text": "And what did you bring for show and tell?", "expansion": "CAHe3"}, 535 | { 536 | "cardType": "Black", 537 | "text": "During high school, I never really fit in until I found _ club.", 538 | "expansion": "CAHe3" 539 | }, 540 | {"cardType": "Black", "text": "Hey, baby, come back to my place and I'll show you _.", "expansion": "CAHe3"}, 541 | { 542 | "cardType": "Black", 543 | "text": "After months of practice with _, I think I'm finally ready for _.", 544 | "expansion": "CAHe3" 545 | }, 546 | { 547 | "cardType": "Black", 548 | "text": "To prepare for his upcoming role, Daniel Day-Lewis immersed himself in the world of _.", 549 | "expansion": "CAHe3" 550 | }, 551 | {"cardType": "Black", "text": "Finally! A service that delivers _ right to your door.", "expansion": "CAHe3"}, 552 | {"cardType": "Black", "text": "My gym teacher got fired for adding _ to the obstacle course.", "expansion": "CAHe3"}, 553 | {"cardType": "Black", "text": "Having problems with _? Try _!", "expansion": "CAHe3"}, 554 | { 555 | "cardType": "Black", 556 | "text": "As part of his contract, Prince won't perform without _ in his dressing room.", 557 | "expansion": "CAHe3" 558 | }, 559 | { 560 | "cardType": "Black", 561 | "text": "Listen, son. If you want to get involved with _, I won't stop you. Just steer clear of _.", 562 | "expansion": "CAHe3" 563 | }, 564 | {"cardType": "Black", "text": "I just met you and this is crazy, but here's _, so _ maybe", "expansion": "Image1"}, 565 | {"cardType": "Black", "text": "It's only _ if you get caught!", "expansion": "Image1"}, 566 | {"cardType": "Black", "text": "_: The Next Generation", "expansion": "Image1"}, 567 | {"cardType": "Black", "text": "Terminator 4: _", "expansion": "Image1"}, 568 | {"cardType": "Black", "text": "Disney presents _ on ice!", "expansion": "Image1"}, 569 | {"cardType": "Black", "text": "_. The other white meat.", "expansion": "Image1"}, 570 | {"cardType": "Black", "text": "A _ a day keeps the _ away.", "expansion": "Image1"}, 571 | {"cardType": "Black", "text": "I'm sweating like a _ at a _.", "expansion": "Image1"}, 572 | {"cardType": "Black", "text": "I love the smell of _ in the morning.", "expansion": "Image1"}, 573 | {"cardType": "Black", "text": "You're not gonna believe this, but _.", "expansion": "Image1"}, 574 | {"cardType": "Black", "text": "_. All the cool kids are doing it.", "expansion": "Image1"}, 575 | {"cardType": "Black", "text": "So I was _ in my cubicle at work, and suddenly _!", "expansion": "Image1"}, 576 | {"cardType": "Black", "text": "Baskin Robbins just added a 32nd flavor: _!", "expansion": "Image1"}, 577 | {"cardType": "Black", "text": "I can drive and ____ at the same time.", "expansion": "Image1"}, 578 | {"cardType": "Black", "text": "_ ain't nothin' to fuck wit'!", "expansion": "Image1"} 579 | ]; --------------------------------------------------------------------------------