├── .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 |
4 |
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 |
2 |
9 |
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 |
2 |
3 |
4 |
5 | {{text}}
6 |
7 |
8 | {{expansion}}
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/client/cardsagainstsobriety/views/cards/black_card.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{text}}
6 |
7 |
8 | {{expansion}}
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/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 |
2 |
3 |
4 |
5 | {{text}}
6 |
7 |
8 | {{expansion}}
9 |
10 |
11 |
12 |
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 |
2 |
3 |
ScoreBoard
4 | {{> leadeboard}}
5 |
6 |
7 |
8 |
9 |
10 | {{#each players}}
11 | {{>playerScore}}
12 | {{/each}}
13 |
Last Winner
14 | {{lastWinner}}
15 | Round Number
16 | {{roundNum}}
17 |
18 |
19 |
20 |
21 |
22 | {{name}}: {{points}}
23 |
24 |
--------------------------------------------------------------------------------
/client/timeshistorian/timesHistorianHand.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{#if ready}}
5 |
6 |
7 |
{{getText.prompt}}
8 |
9 | {{#each getOptions.gameBoard.choices}}
10 |
11 | {{> whiteTimesCard}}
12 |
13 | {{/each}}
14 |
15 |
16 |
17 | {{/if}}
18 |
19 |
--------------------------------------------------------------------------------
/client/chat/chat.html:
--------------------------------------------------------------------------------
1 |
2 | Talk Smack
3 | {{> chatBox}}
4 |
5 |
6 |
7 |
8 | {{#each messages.messages}}
9 | {{>chatMessage}}
10 | {{/each}}
11 |
12 |
13 | Send Chat
14 |
15 |
16 |
17 |
18 |
{{createdByName}}:
{{message}}
19 |
20 |
--------------------------------------------------------------------------------
/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 |
2 |
3 |
4 |
5 |
6 | {{>timesHistorianHand}}
7 |
8 |
9 |
{{getText.title}}
10 |
11 |
{{getText.subtitle}}
12 |
{{getText.button}}
13 |
14 | {{> scoreBoard}}
15 |
16 |
17 | {{#if currentUser}}
18 | {{> mainBox}}
19 | {{/if}}
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/_.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 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
11 | {{> loginButtons}}
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | {{#if currentUser}}
22 | {{> yield region="show"}}
23 | {{/if}}
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/client/cardsagainstsobriety/views/player-hand-view.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Your Cards: Choose wisely. Or, not. Whatever.
5 |
6 |
Your Score: {{currentUser.score}}
7 |
8 |
DEAL
9 |
CLEAR
10 |
Your Hand
11 |
12 | {{#each playsHand}}
13 |
14 | {{> whiteCard}}
15 |
16 | {{/each}}
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/client/cardsagainstsobriety/cardsagainstsobriety.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
20 |
21 | {{#if currentUser}}
22 | {{> mainBox}}
23 | {{/if}}
24 |
25 |
26 |
27 |
28 |
29 | {{#if currentUser}}
30 | {{> playerHand}}
31 | {{else}}
32 |
35 | {{/if}}
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/_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 | [](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 |
2 |
3 |
4 |
5 |
6 |
Currently there are {{numPlayers}} players in the room.
7 |
8 | {{#each users}}
9 |
10 | {{username}} - Score: {{score}}
11 |
12 | {{/each}}
13 |
14 |
15 |
16 |
17 |
18 | {{#if winnerChosen}}
19 |
Let's play another, you smarmy wench
20 | {{else}}
21 | {{#if allCardsPlayed}}
22 |
ROUND OVER
23 | {{else}}
24 |
Number of cards -
25 |
26 |
27 | In play: {{cardsPlayed}}
28 |
29 |
30 | Left to be played: {{cardsLeft}}
31 |
32 | {{/if}}
33 |
34 | {{/if}}
35 |
36 |
37 |
38 |
39 |
40 |
45 |
46 | {{#if currentUser.judge}}
47 |
You're the Motha Fuckin' JUDGE
48 | {{/if}}
49 |
50 | {{#if winnerChosen}}
51 |
So somebody finds this sick shit funny..
52 | {{/if}}
53 |
54 |
55 |
56 |
57 |
58 |
59 | {{#each question}}
60 | {{> blackCard}}
61 | {{/each}}
62 |
63 |
64 |
65 |
66 |
67 | {{#each answers}}
68 |
69 | {{> whiteCard}}
70 |
71 |
72 |
73 |
74 |
75 |
76 | {{/each}}
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/client/home/home.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Cards Against Sobriety
5 |
Available Rooms – Click to Ennter
6 |
7 | {{#each loadCardRooms}}
8 |
9 | {{room.roomName}}
10 | {{#if roomOwner }}
11 |
14 | {{/if}}
15 |
16 | {{/each}}
17 |
18 |
19 |
Or Create Your Own Room
20 |
Create Room
21 |
22 |
Give your new room a name
23 |
24 |
Create Room
25 |
or Cancel
26 |
27 |
28 |
29 |
30 |
31 |
32 |
Movie Cloud
33 |
Available Rooms – Click to Ennter
34 |
35 | {{#each loadMovieRooms}}
36 |
37 | {{room.roomName}}
38 | {{#if roomOwner }}
39 |
42 | {{/if}}
43 |
44 | {{/each}}
45 |
46 |
47 |
Or Create Your Own Room
48 |
Create Room
49 |
50 |
Give your new room a name
51 |
52 |
Create Room
53 |
or Cancel
54 |
55 |
56 |
57 |
Times Historian
58 |
Available Rooms – Click to Ennter
59 |
60 | {{#each loadTimesRooms}}
61 |
62 | {{room.roomName}}
63 | {{#if roomOwner }}
64 |
67 | {{/if}}
68 |
69 | {{/each}}
70 |
71 |
72 |
Or Create Your Own Room
73 |
Create Room
74 |
75 |
Give your new room a name
76 |
77 |
Create Room
78 |
or Cancel
79 |
80 |
81 |
82 |
83 |
84 | {{> timesHistorian}}
85 |
86 |
--------------------------------------------------------------------------------
/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 | ];
--------------------------------------------------------------------------------