├── .gitignore
├── .meteor
├── .gitignore
├── release
├── platforms
├── cordova-plugins
├── .finished-upgraders
├── .id
├── packages
└── versions
├── scss.json
├── client
├── initializers
│ └── autoform.js
├── styles
│ ├── utils.scss
│ ├── lib
│ │ ├── mixins.scss
│ │ └── variables.scss
│ ├── components
│ │ ├── account.scss
│ │ ├── main-slides.scss
│ │ └── fixed-buttons.scss
│ ├── modules
│ │ ├── modal.scss
│ │ ├── input-bottom.scss
│ │ ├── header.scss
│ │ ├── button.scss
│ │ ├── ea-button.scss
│ │ ├── img-preview.scss
│ │ ├── guest-list.scss
│ │ └── list-items.scss
│ ├── layout.scss
│ └── base.scss
├── views
│ ├── events
│ │ ├── _activity-edit.html
│ │ ├── create-activity.js
│ │ ├── show
│ │ │ ├── rsvp.html
│ │ │ ├── photos.html
│ │ │ ├── wall.js
│ │ │ ├── host.js
│ │ │ ├── wall.html
│ │ │ ├── guest-list.js
│ │ │ ├── host.html
│ │ │ ├── details.js
│ │ │ ├── details.html
│ │ │ └── guest-list.html
│ │ ├── _list.js
│ │ ├── account
│ │ │ ├── account-input.html
│ │ │ └── account-input.js
│ │ ├── _edit.js
│ │ ├── rsvp.html
│ │ ├── _list.html
│ │ ├── create-activity.html
│ │ ├── account.html
│ │ ├── index.html
│ │ ├── _edit.html
│ │ ├── account.js
│ │ ├── index.js
│ │ ├── show.js
│ │ └── show.html
│ ├── index.js
│ ├── index.html
│ └── layouts
│ │ └── application.html
├── components
│ ├── swiper-slides
│ │ ├── swiper-slides.html
│ │ └── swiper-slides.js
│ └── modal
│ │ └── modal.js
├── lib
│ ├── helpers.js
│ ├── formaldehyde.js
│ └── ramjet.js
└── swiper
│ └── swiper.css
├── public
└── images
│ └── 57.png
├── methods
└── users.js
├── controllers
├── lib
│ └── application.js
├── index.js
└── events
│ ├── show.js
│ └── index.js
├── routes.js
├── server
├── publications.js
└── seeds.js
├── mobile-config.js
├── collections
├── events.js
├── lib
│ └── schemas.js
└── users.js
└── index.scss
/.gitignore:
--------------------------------------------------------------------------------
1 | settings.json
2 |
--------------------------------------------------------------------------------
/.meteor/.gitignore:
--------------------------------------------------------------------------------
1 | local
2 |
--------------------------------------------------------------------------------
/.meteor/release:
--------------------------------------------------------------------------------
1 | METEOR@1.1.0.2
2 |
--------------------------------------------------------------------------------
/.meteor/platforms:
--------------------------------------------------------------------------------
1 | browser
2 | ios
3 | server
4 |
--------------------------------------------------------------------------------
/scss.json:
--------------------------------------------------------------------------------
1 | {
2 | "useIndex": true
3 | }
4 |
--------------------------------------------------------------------------------
/client/initializers/autoform.js:
--------------------------------------------------------------------------------
1 | AutoForm.setDefaultTemplate('materialize');
2 |
--------------------------------------------------------------------------------
/client/styles/utils.scss:
--------------------------------------------------------------------------------
1 | .horizontal-padded {
2 | @include horizontal-padded;
3 | }
4 |
--------------------------------------------------------------------------------
/public/images/57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/poetic/meteor-events/master/public/images/57.png
--------------------------------------------------------------------------------
/.meteor/cordova-plugins:
--------------------------------------------------------------------------------
1 | org.apache.cordova.inappbrowser@0.6.0
2 | org.apache.cordova.statusbar@0.1.10
3 |
--------------------------------------------------------------------------------
/client/views/events/_activity-edit.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/client/views/events/create-activity.js:
--------------------------------------------------------------------------------
1 | Template.CreateActivity.events({
2 | 'click .back': Modal.closeModal
3 | });
4 |
--------------------------------------------------------------------------------
/client/views/events/show/rsvp.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/client/views/events/show/photos.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/client/views/index.js:
--------------------------------------------------------------------------------
1 | Template.Index.events({
2 | 'click li': function(){
3 | Router.go('events.index', { user_id: this._id });
4 | }
5 | });
6 |
--------------------------------------------------------------------------------
/client/styles/lib/mixins.scss:
--------------------------------------------------------------------------------
1 | @mixin horizontal-padded {
2 | padding-left: 10px;
3 | padding-right: 10px;
4 | box-sizing: border-box;
5 | }
6 |
7 |
--------------------------------------------------------------------------------
/methods/users.js:
--------------------------------------------------------------------------------
1 | Meteor.methods({
2 | addComment: function(eventId, comment){
3 | Events.update({ _id: eventId }, { $push: { comments: comment } });
4 | }
5 | });
6 |
--------------------------------------------------------------------------------
/client/styles/lib/variables.scss:
--------------------------------------------------------------------------------
1 | $black: #000000;
2 | $lightgray: #D3D3D3;
3 | $gray: #808080;
4 | $white: #FFFFFF;
5 | $green: #008000;
6 | $red: #FF0000;
7 | $status-height: 23px;
8 | $header-height: 40px;
9 |
--------------------------------------------------------------------------------
/controllers/lib/application.js:
--------------------------------------------------------------------------------
1 | ApplicationController = RouteController.extend({
2 | layoutTemplate: 'ApplicationLayout',
3 | notFoundTemplate: 'notFoundLayout',
4 | loadingTemplate: 'loadingLayout'
5 | });
6 |
--------------------------------------------------------------------------------
/client/views/events/_list.js:
--------------------------------------------------------------------------------
1 | Template._eventsList.events({
2 | 'click li': function(){
3 | var userId = Router.current().params.user_id;
4 |
5 | Router.go('events.show', { user_id: userId, event_id: this._id });
6 | }
7 | });
8 |
--------------------------------------------------------------------------------
/client/components/swiper-slides/swiper-slides.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{> UI.contentBlock}}
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/client/styles/components/account.scss:
--------------------------------------------------------------------------------
1 | #account {
2 | .title {
3 | font-weight: bold;
4 | border-bottom: 1px solid $gray;
5 | margin-bottom: 20px;
6 | margin-top: 32px;
7 | padding-bottom: 10px;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/routes.js:
--------------------------------------------------------------------------------
1 | Router.route('/', {
2 | name: 'index'
3 | });
4 |
5 | Router.route('/users/:user_id/events', {
6 | name: 'events.index'
7 | });
8 |
9 | Router.route('/users/:user_id/events/:event_id', {
10 | name: 'events.show'
11 | });
12 |
--------------------------------------------------------------------------------
/client/views/index.html:
--------------------------------------------------------------------------------
1 |
2 | Select a user:
3 |
4 |
5 | {{#each users}}
6 | {{profile.fullName}}
7 | {{/each}}
8 |
9 |
10 | {{> loginButtons}}
11 |
12 |
--------------------------------------------------------------------------------
/controllers/index.js:
--------------------------------------------------------------------------------
1 | IndexController = ApplicationController.extend({
2 | waitOn: function(){
3 | Meteor.subscribe('users');
4 | },
5 |
6 | data: {
7 | users: function(){
8 | return Meteor.users.find();
9 | }
10 | }
11 | });
12 |
--------------------------------------------------------------------------------
/client/styles/modules/modal.scss:
--------------------------------------------------------------------------------
1 | .modal-full {
2 | display: none;
3 | top: 0;
4 | position: absolute;
5 | background-color: white;
6 | z-index: 100;
7 | width: 100%;
8 | height: 100%;
9 | }
10 |
--------------------------------------------------------------------------------
/client/styles/components/main-slides.scss:
--------------------------------------------------------------------------------
1 | .swiper-slide-main {
2 | .swiper-slide {
3 | overflow-y: scroll;
4 | -webkit-overflow-scrolling: touch;
5 |
6 | .swiper-slide-scrollable {
7 | margin: 0;
8 | height: 100.2%;
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/server/publications.js:
--------------------------------------------------------------------------------
1 | Meteor.publish('users', function(){
2 | return Meteor.users.find();
3 | });
4 |
5 | Meteor.publish('events', function(){
6 | return Events.find();
7 | });
8 |
9 | Meteor.publish('event', function(eventId){
10 | return Events.find({ _id: eventId });
11 | });
12 |
--------------------------------------------------------------------------------
/.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/styles/modules/input-bottom.scss:
--------------------------------------------------------------------------------
1 | .form-bottom {
2 | border-top: 1px solid lightgray;
3 | background-color: white;
4 | position: fixed;
5 | bottom: 0;
6 | width: 100%;
7 |
8 | .form-bottom-input {
9 | padding-left: 10px;
10 | margin-bottom: 0;
11 | height: 50px;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/client/views/events/show/wall.js:
--------------------------------------------------------------------------------
1 | Template.EventsShowWall.helpers({
2 | timeAgoPosted: function(){
3 | return moment(this.created).fromNow();
4 | },
5 |
6 | username: function(){
7 | var user = Meteor.users.findOne({ '_id': this.user })
8 |
9 | if (user) { return user.profile.fullName }
10 | }
11 | });
12 |
--------------------------------------------------------------------------------
/client/views/events/account/account-input.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.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 | 1wv7fg61nhcof01rhqsd
8 |
--------------------------------------------------------------------------------
/client/styles/components/fixed-buttons.scss:
--------------------------------------------------------------------------------
1 | #acct-btn-wrapper {
2 | position: fixed;
3 | bottom: 15px;
4 | left: 15px;
5 | margin-bottom: 0;
6 |
7 | i {
8 | font-size: 30px;
9 | }
10 | }
11 |
12 | #add-event-btn-wrapper {
13 | position: fixed;
14 | bottom: 15px;
15 | right: 15px;
16 | margin-bottom: 0;
17 |
18 | i {
19 | font-size: 30px;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/client/styles/modules/header.scss:
--------------------------------------------------------------------------------
1 | .header {
2 | text-align: center;
3 | border-bottom: 1px $gray solid;
4 |
5 | .header-left {
6 | display: inline-block;
7 | position: absolute;
8 | left: 10px;
9 | }
10 | .header-middle {
11 | font-weight: bold;
12 | }
13 | .header-right {
14 | display: inline-block;
15 | position: absolute;
16 | right: 10px;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/client/views/events/_edit.js:
--------------------------------------------------------------------------------
1 | Template._eventsEdit.events({
2 | 'submit form': function(event) {
3 | stopEvent(event);
4 |
5 | // TODO: save event
6 | },
7 |
8 | 'click #add-an-activity': Modal.openModal.bind(null, '#createActivity'),
9 |
10 | 'click .close': function(event){
11 | stopEvent(event);
12 |
13 | ParamManager.setParam('edit-form', null);
14 | }
15 | });
16 |
17 |
--------------------------------------------------------------------------------
/client/views/events/account/account-input.js:
--------------------------------------------------------------------------------
1 | /**
2 | * params:
3 | * model: {Hash}
4 | * name: {String}
5 | */
6 |
7 | Template.AccountInput.helpers({
8 | _label: function() {
9 | var defalutLabel = s.underscored(this.name).replace(/_/g, ' ').toUpperCase();
10 | return this.label || defalutLabel;
11 | },
12 | _value: function() {
13 | return this.model[this.name];
14 | },
15 | });
16 |
--------------------------------------------------------------------------------
/client/styles/modules/button.scss:
--------------------------------------------------------------------------------
1 | .btn-bottom-wrapper {
2 | position: absolute;
3 | bottom: 0px;
4 | width: 100%;
5 |
6 | .btn-full {
7 | margin-bottom: 10px;
8 | }
9 | }
10 |
11 | .btn-full {
12 | width: 100%;
13 |
14 | // NOTE: the following should be the default
15 | background-color: $white;
16 | color: $black;
17 | border: solid 1px black;
18 | border-radius: 5px;
19 | }
20 |
--------------------------------------------------------------------------------
/client/components/modal/modal.js:
--------------------------------------------------------------------------------
1 | Modal = {
2 | // TODO: add an option for animation
3 | openModal: function(selector) {
4 | $(selector).css('display', 'block');
5 | $(selector).velocity({top: 0}, "swing");
6 | return false;
7 | },
8 | closeModal: function(event, template) {
9 | var modal = $(template.firstNode);
10 | modal.velocity({top: '100%'}, "swing", function() {
11 | modal.css('display', 'none');
12 | });
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/client/styles/modules/ea-button.scss:
--------------------------------------------------------------------------------
1 | .ea-btn {
2 | width: 100%;
3 | box-sizing: border-box;
4 | border: 1px solid $black;
5 | border-radius: 5px;
6 | font-weight: bold;
7 | font-size: 1.2rem;
8 | text-transform: uppercase;
9 | background-color: white;
10 | color: $black;
11 | box-shadow: none;
12 |
13 | &:hover {
14 | background-color: white;
15 | box-shadow: none;
16 | }
17 |
18 | i {
19 | font-size: 3rem;
20 | padding-left: 5px;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/client/views/events/rsvp.html:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
--------------------------------------------------------------------------------
/client/views/events/show/host.js:
--------------------------------------------------------------------------------
1 | Template.EventsShowHost.events({
2 | 'click .mdi-communication-email': function(event){
3 | stopEvent(event);
4 | window.open('mailto:' + this.email, '_system');
5 | },
6 |
7 | 'click .mdi-communication-phone': function(event){
8 | stopEvent(event);
9 | location.href = 'tel:' + this.phone;
10 | },
11 |
12 | 'click .mdi-communication-message': function(event){
13 | stopEvent(event);
14 | window.open('sms:' + this.phone, '_system');
15 | },
16 | });
17 |
--------------------------------------------------------------------------------
/mobile-config.js:
--------------------------------------------------------------------------------
1 | App.setPreference('Orientation', 'portrait');
2 | App.setPreference('StatusBarBackgroundColor', '#000000');
3 | App.setPreference('StatusBarOverlaysWebView', false);
4 | App.setPreference('StatusBarStyle', 'lightcontent');
5 |
6 | App.accessRule('*://lorempixel.com/*');
7 | App.accessRule('*.google.com/*');
8 | App.accessRule('*.googleapis.com/*');
9 | App.accessRule('*.gstatic.com/*');
10 | App.accessRule('mailto:*', { launchExternal: true });
11 | App.accessRule('sms:*', { launchExternal: true });
12 |
--------------------------------------------------------------------------------
/client/views/events/show/wall.html:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
--------------------------------------------------------------------------------
/client/styles/layout.scss:
--------------------------------------------------------------------------------
1 | #content {
2 | position: relative;
3 | }
4 |
5 | .navbar {
6 | height: 55px;
7 | background-color: $black;
8 | color: $gray;
9 | border-bottom: 1px solid $gray;
10 | text-align: center;
11 |
12 | .swiper-slide {
13 | width: 40%;
14 | height: 55px;
15 | line-height: 55px;
16 | }
17 |
18 | .swiper-slide-active {
19 | color: $white;
20 | }
21 | }
22 |
23 | .slide-container {
24 | margin: 0;
25 | }
26 |
27 | .header {
28 | height: $header-height;
29 | line-height: $header-height;
30 | }
31 |
--------------------------------------------------------------------------------
/client/styles/modules/img-preview.scss:
--------------------------------------------------------------------------------
1 | .img-preview {
2 | color: white;
3 | }
4 |
5 | .img-preview-inner {
6 | width: 100%;
7 | height: 100px;
8 | position: relative;
9 |
10 | .img-preview-top-center {
11 | padding: 0 10px 0 10px;
12 | position: absolute;
13 | left: 50%;
14 | margin: 0 0 0 -24px;
15 | }
16 |
17 | .img-preview-top-right {
18 | position: absolute;
19 | right: 0;
20 | top: 0;
21 | margin: 10px;
22 | }
23 |
24 | .img-preview-title {
25 | position: absolute;
26 | margin: 10px;
27 | bottom: 0;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/client/views/events/_list.html:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
--------------------------------------------------------------------------------
/client/views/layouts/application.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | EventAssist
7 |
8 |
9 |
10 |
11 |
12 |
13 | {{> yield}}
14 |
15 |
16 |
17 |
18 | {{> yield}}
19 |
20 |
21 |
22 | {{> yield}}
23 |
24 |
--------------------------------------------------------------------------------
/.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 |
7 | meteor-platform
8 | iron:router
9 | materialize:materialize
10 | fourseven:scss
11 | digilord:faker
12 | dburles:collection-helpers
13 | underscore
14 | aldeed:simple-schema
15 | aldeed:collection2
16 | msavin:mongol
17 | momentjs:moment
18 | velocityjs:velocityjs
19 | dburles:google-maps
20 | reactive-dict
21 | accounts-ui
22 | accounts-password
23 | underscorestring:underscore.string
24 | aldeed:autoform
25 | gildaspk:autoform-materialize
26 | fastclick
27 |
--------------------------------------------------------------------------------
/client/views/events/show/guest-list.js:
--------------------------------------------------------------------------------
1 | Template.EventsShowGuestList.helpers({
2 | yesGuests: function(){
3 | var guests = this.currentEvent().guests;
4 | return guests.filter(function(guest){ return guest.attending });
5 | },
6 |
7 | noGuests: function(){
8 | var guests = this.currentEvent().guests;
9 | return guests.filter(function(guest){ return !guest.attending });
10 | },
11 |
12 | noReplyGuests: function(){
13 | var guests = this.currentEvent().guests;
14 | return guests.filter(function(guest){ return !guest.replied });
15 | },
16 |
17 | notYetInvitedGuests: function(){
18 | var guests = this.currentEvent().guests;
19 | return guests.filter(function(guest){ return !guest.invited });
20 | },
21 | });
22 |
--------------------------------------------------------------------------------
/collections/events.js:
--------------------------------------------------------------------------------
1 | Events = new Mongo.Collection('events');
2 |
3 | Events.attachSchema(new SimpleSchema({
4 | user: {
5 | type: String,
6 | },
7 | coverPhoto: {
8 | type: String,
9 | },
10 | title: {
11 | type: String
12 | },
13 | description: {
14 | type: String
15 | },
16 | isPlusOne: {
17 | type: Boolean
18 | },
19 | hosts: {
20 | type: [HostSchema],
21 | defaultValue: [],
22 | },
23 | activities: {
24 | type: [ActivitySchema],
25 | defaultValue: [],
26 | },
27 | comments: {
28 | type: [CommentSchema],
29 | defaultValue: [],
30 | },
31 | guests: {
32 | type: [GuestSchema],
33 | defaultValue: [],
34 | },
35 | }));
36 |
37 | Events.helpers({
38 | eventStartTime: function(){
39 | var eventStartTime = _.first(_.sortBy(this.activities, 'startTime')).startTime;
40 |
41 | return moment(eventStartTime).format('dddd, MMM D YYYY, h:mm a');
42 | },
43 | });
44 |
--------------------------------------------------------------------------------
/controllers/events/show.js:
--------------------------------------------------------------------------------
1 | EventsShowController = ApplicationController.extend({
2 | waitOn: function(){
3 | var eventId = Router.current().params.event_id;
4 |
5 | Meteor.subscribe('users');
6 | Meteor.subscribe('event', eventId);
7 | },
8 |
9 | data: function(){
10 | var eventId = this.params.event_id;
11 | var routeParams = this.params.query;
12 | var initialSlide;
13 |
14 | if (_.has(routeParams, 'slides')) {
15 | initialSlide = routeParams.slides;
16 | }
17 |
18 | return {
19 | currentEvent: function(){
20 | return Events.findOne(eventId);
21 | },
22 | plannerNavOptions: {
23 | centeredSlides: true,
24 | slidesPerView: 'auto',
25 | slideToClickedSlide: true,
26 | initialSlide: initialSlide || 2,
27 | },
28 | plannerSlideOptions: {
29 | initialSlide: initialSlide || 2,
30 | threshold: 10,
31 | },
32 | };
33 | },
34 | });
35 |
--------------------------------------------------------------------------------
/client/views/events/create-activity.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 | {{#autoForm id="eventForm" schema="ActivitySchema" type="insert"}}
13 |
14 | {{> afQuickField name='title'}}
15 | {{> afQuickField name='startTime'}}
16 | {{> afQuickField name='endTime'}}
17 | {{> afQuickField name='address'}}
18 | {{> afQuickField name='attire'}}
19 | {{> afQuickField name='transit'}}
20 |
21 |
22 |
26 |
27 | {{/autoForm}}
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/client/views/events/show/host.html:
--------------------------------------------------------------------------------
1 |
2 |
27 |
28 |
--------------------------------------------------------------------------------
/client/styles/base.scss:
--------------------------------------------------------------------------------
1 | html {
2 | font-size: 0.75rem;
3 | }
4 |
5 | html, body {
6 | margin: 0;
7 | padding: 0;
8 | }
9 |
10 | h1 {
11 | font-size: 2.25rem;
12 | line-height: 30px;
13 | }
14 |
15 | h2 {
16 | font-size: 1.6rem;
17 | line-height: 25px;
18 | }
19 |
20 | h3 {
21 | font-size: 1.5rem;
22 | line-height: 20px;
23 | }
24 |
25 | h5 {
26 | font-size: 0.9rem;
27 | line-height: 15px;
28 | }
29 |
30 | button {
31 | padding: 0 0 0 3px;
32 |
33 | i {
34 | font-size: 2.5rem;
35 | vertical-align: middle;
36 | }
37 | }
38 |
39 | i {
40 | font-size: 2rem;
41 | vertical-align: bottom;
42 | &:before {
43 | vertical-align: bottom;
44 | }
45 | }
46 |
47 | a {
48 | color: $black;
49 | }
50 |
51 | input[type=text], input[type=password], input[type=email], input[type=url], input[type=time], input[type=date], input[type=datetime-local], input[type=tel], input[type=number], input[type=search], textarea.materialize-textarea {
52 | @include horizontal-padded
53 | }
54 |
--------------------------------------------------------------------------------
/client/styles/modules/guest-list.scss:
--------------------------------------------------------------------------------
1 | .guest-list-header {
2 | padding: 20px 10px 10px 10px;
3 |
4 | .guest-list-header-title {
5 | margin: 0;
6 | text-align: center;
7 | margin-bottom: 20px;
8 | }
9 | }
10 |
11 | .guest-list {
12 | margin: 0;
13 |
14 | .guest-list-header {
15 | position: relative;
16 | padding: 5px 0 5px 10px;
17 | background: $lightgray;
18 | color: $white;
19 | text-transform: uppercase;
20 |
21 | &.yes {
22 | border-bottom: 2px solid $green;
23 | }
24 |
25 | &.no {
26 | border-bottom: 2px solid $red;
27 | }
28 | }
29 |
30 | .guest-list-heading {
31 | margin: 0;
32 | font-weight: bold;
33 | }
34 |
35 | .guest-list-heading-right {
36 | position: absolute;
37 | right: 11px;
38 | }
39 | }
40 |
41 | .guest-list-preview-container {
42 | padding: 20px 10px 10px 10px;
43 |
44 | .guest-list-invite-link {
45 | margin: 0 0 20px 0;
46 | text-align: center;
47 | text-decoration: underline;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/client/views/events/account.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
15 |
16 |
YOUR DETAILS
17 |
18 | {{#autoForm id="accountForm" collection="Meteor.users" type="update" doc=user}}
19 | {{> afQuickField name='profile.fullName'}}
20 | {{> afQuickField name='profile.email'}}
21 | {{> afQuickField name='profile.secondaryEmail'}}
22 | {{> afQuickField name='profile.phone'}}
23 |
24 |
28 |
29 | {{/autoForm}}
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/client/views/events/index.html:
--------------------------------------------------------------------------------
1 |
2 | {{#if Template.subscriptionsReady}}
3 | {{#swiperSlides
4 | options=navOptions param='slides' classNames='navbar'
5 | }}
6 | RSVPs
7 | Upcoming
8 | Past Events
9 | {{/swiperSlides}}
10 |
11 | {{#swiperSlides
12 | options=slideOptions param='slides' classNames='swiper-slide-main'
13 | }}
14 | {{> EventsRsvp}}
15 | {{> _eventsList eventsList=upcomingEvents}}
16 | {{> _eventsList eventsList=pastEvents}}
17 | {{/swiperSlides}}
18 | {{/if}}
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | {{> Account}}
28 | {{> _eventsEdit}}
29 | {{> CreateActivity}}
30 |
31 |
--------------------------------------------------------------------------------
/index.scss:
--------------------------------------------------------------------------------
1 | // This file is auto generated by the scss package
2 | // New .scss and .sass files will be automatically '@import'ed at the bottom
3 | // Existing content in the file will not be touched
4 | // When deleting a .scss or .sass file you must manually delete it from here
5 |
6 | @import "client/styles/lib/variables.scss";
7 | @import "client/styles/lib/mixins.scss";
8 |
9 | @import "client/styles/components/fixed-buttons.scss";
10 | @import "client/styles/components/main-slides.scss";
11 |
12 | @import "client/styles/base.scss";
13 | @import "client/styles/layout.scss";
14 | @import "client/styles/modules/list-items.scss";
15 | @import "client/styles/modules/img-preview.scss";
16 | @import "client/styles/modules/guest-list.scss";
17 | @import "client/styles/modules/ea-button.scss";
18 | @import "client/styles/modules/input-bottom.scss";
19 |
20 | @import "client/styles/utils.scss";
21 | @import "client/styles/modules/button.scss";
22 |
23 | @import "client/styles/components/fixed-buttons.scss";
24 | @import "client/styles/components/account.scss";
25 |
26 | @import "client/styles/modules/header.scss";
27 | @import "client/styles/modules/modal.scss";
--------------------------------------------------------------------------------
/client/views/events/_edit.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 | {{#autoForm id="eventForm" collection="Events" type="insert"}}
12 |
13 | {{> afQuickField name='title'}}
14 | {{> afQuickField name='description'}}
15 |
16 |
17 | {{> afQuickField name='isPlusOne'}}
18 |
19 |
20 |
24 |
25 | {{#if activities}}
26 |
30 | {{/if}}
31 |
32 | {{/autoForm}}
33 |
34 |
35 |
--------------------------------------------------------------------------------
/client/views/events/account.js:
--------------------------------------------------------------------------------
1 | Template.Account.onRendered(function(){
2 | if(Router.current().params['show-account']) {
3 | $(this.firstNode).css('display', 'block').css('top', 0);
4 | }
5 | });
6 |
7 | Template.Account.helpers({
8 | user: function() {
9 | return Meteor.user();
10 | },
11 | });
12 |
13 | Template.Account.events({
14 | 'submit form': function(event) {
15 | stopEvent(event);
16 |
17 | var accountBtn = $('#acct-btn');
18 | var accountPage = $('#account');
19 |
20 | ramjet.transform(accountPage[0], accountBtn[0], {
21 | //easing: ramjet.easeInOut,
22 | duration: 250,
23 | });
24 |
25 | accountPage.css('display', 'none');
26 |
27 | //Meteor.users.update(Meteor.userId(), {
28 | //$set: {
29 | //'profile.fullName': event.target.fullName.value,
30 | //'profile.email': event.target.email.value,
31 | //'profile.secondaryEmail': event.target.secondaryEmail.value,
32 | //'profile.phone': event.target.phone.value
33 | //}
34 | //});
35 |
36 | //closeAccount();
37 |
38 | //return false;
39 | },
40 |
41 | 'click .close': function(event){
42 | stopEvent(event);
43 |
44 | transformClose({
45 | from: $('#account'),
46 | to: $('#acct-btn')
47 | });
48 | }
49 | });
50 |
--------------------------------------------------------------------------------
/controllers/events/index.js:
--------------------------------------------------------------------------------
1 | EventsIndexController = ApplicationController.extend({
2 | waitOn: function(){
3 | Meteor.subscribe('events');
4 | },
5 |
6 | data: function(){
7 | var userId = this.params.user_id;
8 | var routeParams = this.params.query;
9 | var initialSlide;
10 |
11 | if (_.has(routeParams, 'slides')) {
12 | initialSlide = routeParams.slides;
13 | }
14 |
15 | return {
16 | upcomingEvents: function(){
17 | return Events.find(
18 | { $or: [{ guests: { $in: [userId] } }, { user: userId }] },
19 | { sort: { 'activities.startTime': 1 } }
20 | );
21 | },
22 | pastEvents: function(){
23 | return Events.find(
24 | { $or: [{ guests: { $in: [userId] } }, { user: userId }] },
25 | { sort: { 'activities.startTime': 1 } }
26 | );
27 | },
28 | rsvps: function(){
29 | return [];
30 | },
31 | navOptions: {
32 | centeredSlides: true,
33 | slidesPerView: 'auto',
34 | slideToClickedSlide: true,
35 | touchRatio: 0.5,
36 | initialSlide: initialSlide || 1,
37 | runCallbacksOnInit: false,
38 | },
39 | slideOptions: {
40 | initialSlide: initialSlide || 1,
41 | runCallbacksOnInit: false,
42 | },
43 | };
44 | }
45 | });
46 |
--------------------------------------------------------------------------------
/client/views/events/index.js:
--------------------------------------------------------------------------------
1 | Template.EventsIndex.created = function(){
2 | this.subscribe('events');
3 | };
4 |
5 | Template.EventsIndex.rendered = function(){
6 | Meteor.setTimeout(function(){
7 | setSlideHeight.call(this);
8 |
9 | var swiper1 = this.$('.swiper-container')[0].swiper;
10 | var swiper2 = this.$('.swiper-container')[1].swiper;
11 |
12 | swiper1.params.control = swiper2;
13 | swiper2.params.control = swiper1;
14 | }.bind(this), 1000);
15 |
16 | registerEditFormParam();
17 | };
18 |
19 | Template.EventsIndex.destroyed = function(){
20 | deregisterParam('edit-form');
21 | };
22 |
23 | Template.EventsIndex.events({
24 | 'click #acct-btn-wrapper': function(event) {
25 | stopEvent(event);
26 |
27 | transformOpen({
28 | from: $('#acct-btn'),
29 | to: $('#account')
30 | });
31 | },
32 |
33 | 'click #add-event-btn-wrapper': function(event){
34 | stopEvent(event);
35 |
36 | ParamManager.setParam('edit-form', true);
37 | },
38 | });
39 |
40 | function setSlideHeight (){
41 | var windowHeight = $(window).height();
42 | var navHeight = $('.navbar').height();
43 |
44 | // take off 1 extra pixel to prevent outer template from scrolling
45 | var availableHeight = windowHeight - (navHeight + 1);
46 |
47 | this.$('.swiper-slide-main').height(availableHeight);
48 | };
49 |
--------------------------------------------------------------------------------
/client/components/swiper-slides/swiper-slides.js:
--------------------------------------------------------------------------------
1 | Template.swiperSlides.rendered = function(){
2 | setInitialParamValue.call(this);
3 |
4 | var options = this.data.options;
5 | var defaults = { onTransitionEnd: onTransitionEnd.bind(this) };
6 | var swiperOptions = _.extend(options, defaults);
7 |
8 | this.$('.swiper-container').swiper(swiperOptions);
9 | };
10 |
11 | Template.swiperSlides.destroyed = function(){
12 | var param = this.data.param;
13 |
14 | if (ParamManager.isRegistered(param)) {
15 | ParamManager.DeRegisterParam(param);
16 | }
17 | };
18 |
19 | function onTransitionEnd (event){
20 | var param = this.data.param;
21 | var currentSlide = event.activeIndex.toString();
22 |
23 | if (ParamManager.getParam(param) !== currentSlide) {
24 | ParamManager.setParam(param, currentSlide);
25 | }
26 | };
27 |
28 | function setInitialParamValue (){
29 | var param = this.data.param;
30 | var initialSlide = this.data.options.initialSlide;
31 | var routeParams = Router.current().params.query;
32 |
33 | if (!ParamManager.isRegistered(param)) {
34 | ParamManager.RegisterParam(param, onParamChange.bind(this));
35 | }
36 |
37 | if (routeParams[param] !== initialSlide) {
38 | ParamManager.setParam(param, initialSlide, true);
39 | }
40 | };
41 |
42 | function onParamChange (index){
43 | var swiper = this.$('.swiper-container')[0].swiper;
44 |
45 | if (swiper) { swiper.slideTo(index) }
46 | };
47 |
48 |
--------------------------------------------------------------------------------
/client/views/events/show/details.js:
--------------------------------------------------------------------------------
1 | Template.EventsShowDetails.created = function(){
2 | this.state = new ReactiveDict();
3 | };
4 |
5 | Template.EventsShowDetails.rendered = function(){
6 | this.state.set('mapWidth', $('.li-lrg-banner').width());
7 | };
8 |
9 | Template.EventsShowDetails.events({
10 | 'click .li-lrg-touch-footer': function(event){
11 | stopEvent(event);
12 |
13 | var touchArea = event.target;
14 | var activityCard = $(touchArea).parent();
15 | var arrow = activityCard.find('.arrow');
16 |
17 | // remove 40px vertical padding from total height because it opens too far
18 | var totalCardHeight = activityCard[0].scrollHeight - 40;
19 |
20 | if (!arrow.hasClass('expanded')) {
21 | arrow.addClass('expanded');
22 |
23 | $.Velocity(arrow, { rotateZ: '180deg' }, { duration: 200 });
24 | $.Velocity(activityCard,
25 | { height: totalCardHeight }, { duration: 200 }
26 | );
27 | } else {
28 | arrow.removeClass('expanded');
29 |
30 | arrow.velocity('reverse');
31 | activityCard.velocity('reverse');
32 | }
33 | },
34 | });
35 |
36 | Template.EventsShowDetails.helpers({
37 | formattedDate: function(){
38 | return moment(this.startTime).format('dddd MMM D');
39 | },
40 |
41 | formattedStartTime: function(){
42 | return moment(this.startTime).format('h:mm a');
43 | },
44 |
45 | formattedEndTime: function(){
46 | return moment(this.endTime).format('h:mm a');
47 | },
48 |
49 | staticMapUrl: function(address){
50 | var mapHeight = '100';
51 | var mapWidth = Template.instance().state.get('mapWidth');
52 | var encodedAddress = encodeFullAddress(this.address);
53 |
54 | if (mapWidth) {
55 | return buildMapUrl(encodedAddress, {
56 | width: mapWidth, height: mapHeight
57 | });
58 | }
59 | }
60 | });
61 |
--------------------------------------------------------------------------------
/client/views/events/show/details.html:
--------------------------------------------------------------------------------
1 |
2 |
48 |
49 |
--------------------------------------------------------------------------------
/client/lib/helpers.js:
--------------------------------------------------------------------------------
1 | ParamManager = Meteor.Poetic.ParamManager;
2 | Settings = Meteor.settings;
3 |
4 | stopEvent = function(event){
5 | event.preventDefault();
6 | event.stopPropagation();
7 | };
8 |
9 | encodeFullAddress = function(address){
10 | var addressParts = [
11 | address.address1, address.city,
12 | address.state, address.zipCode
13 | ];
14 |
15 | if (address.address2) {
16 | addressParts.splice(1, 0, address.address2);
17 | }
18 |
19 | return encodeURIComponent(addressParts.join(' '));
20 | };
21 |
22 | buildMapUrl = function(encodedAddress, dimensions){
23 | var url = Settings.public.googleApiUrl +
24 | '?markers=' + encodedAddress +
25 | '&size=' + dimensions.width + 'x' + dimensions.height +
26 | '&key=' + Settings.public.googleApiKey;
27 |
28 | return url;
29 | };
30 |
31 | transformOpen = function(els){
32 | els.to.css('display', 'initial');
33 |
34 | ramjet.transform(els.from[0], els.to[0], {
35 | duration: 250,
36 | done: function(){
37 | els.to.css('display', 'initial')
38 | },
39 | });
40 |
41 | els.to.css('display', 'none');
42 | };
43 |
44 | transformClose = function(els){
45 | ramjet.transform(els.from[0], els.to[0], {
46 | duration: 250
47 | });
48 |
49 | els.from.css('display', 'none');
50 | };
51 |
52 | deregisterParam = function(param){
53 | if (ParamManager.isRegistered(param)) {
54 | ParamManager.DeRegisterParam(param);
55 | }
56 | };
57 |
58 | registerEditFormParam = function(){
59 | if (!ParamManager.isRegistered('edit-form')) {
60 | ParamManager.RegisterParam('edit-form', function(value){
61 |
62 | if (value) {
63 | transformOpen({
64 | from: $('#event-edit-btn'), to: $('#events-edit')
65 | });
66 | } else {
67 | if ($('#events-edit').css('display') !== 'none') {
68 | transformClose({
69 | from: $('#events-edit'), to: $('#event-edit-btn')
70 | });
71 | }
72 | }
73 | });
74 | }
75 | };
76 |
--------------------------------------------------------------------------------
/.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 | aldeed:autoform@5.1.2
6 | aldeed:collection2@2.3.3
7 | aldeed:simple-schema@1.3.2
8 | aldeed:template-extension@3.4.3
9 | autoupdate@1.2.1
10 | babrahams:editable-json@0.4.3
11 | base64@1.0.3
12 | binary-heap@1.0.3
13 | blaze@2.1.2
14 | blaze-tools@1.0.3
15 | boilerplate-generator@1.0.3
16 | callback-hook@1.0.3
17 | check@1.0.5
18 | coffeescript@1.0.6
19 | dburles:collection-helpers@1.0.3
20 | dburles:google-maps@1.0.8
21 | dburles:mongo-collection-instances@0.3.3
22 | ddp@1.1.0
23 | deps@1.0.7
24 | digilord:faker@1.0.6
25 | ejson@1.0.6
26 | email@1.0.6
27 | fastclick@1.0.3
28 | fourseven:scss@2.1.1
29 | geojson-utils@1.0.3
30 | gildaspk:autoform-materialize@0.0.18
31 | gwendall:session-json@0.1.7
32 | html-tools@1.0.4
33 | htmljs@1.0.4
34 | http@1.1.0
35 | id-map@1.0.3
36 | iron:controller@1.0.7
37 | iron:core@1.0.7
38 | iron:dynamic-template@1.0.7
39 | iron:layout@1.0.7
40 | iron:location@1.0.7
41 | iron:middleware-stack@1.0.7
42 | iron:router@1.0.7
43 | iron:url@1.0.7
44 | jquery@1.11.3_2
45 | json@1.0.3
46 | lai:collection-extensions@0.1.3
47 | launch-screen@1.0.2
48 | less@1.0.14
49 | livedata@1.0.13
50 | localstorage@1.0.3
51 | logging@1.0.7
52 | materialize:materialize@0.96.1
53 | meteor@1.1.6
54 | meteor-platform@1.2.2
55 | meteortoys:toykit@0.3.4
56 | minifiers@1.1.5
57 | minimongo@1.0.8
58 | mobile-status-bar@1.0.3
59 | momentjs:moment@2.10.3
60 | mongo@1.1.0
61 | msavin:mongol@1.0.22
62 | npm-bcrypt@0.7.8_2
63 | observe-sequence@1.0.6
64 | ordered-dict@1.0.3
65 | random@1.0.3
66 | reactive-dict@1.1.0
67 | reactive-var@1.0.5
68 | reload@1.1.3
69 | retry@1.0.3
70 | routepolicy@1.0.5
71 | service-configuration@1.0.4
72 | session@1.1.0
73 | sha@1.0.3
74 | spacebars@1.0.6
75 | spacebars-compiler@1.0.6
76 | srp@1.0.3
77 | templating@1.1.1
78 | tracker@1.0.7
79 | ui@1.0.6
80 | underscore@1.0.3
81 | underscorestring:underscore.string@3.0.3
82 | url@1.0.4
83 | velocityjs:velocityjs@1.2.1
84 | webapp@1.2.0
85 | webapp-hashing@1.0.3
86 |
--------------------------------------------------------------------------------
/collections/lib/schemas.js:
--------------------------------------------------------------------------------
1 | Schemas = {};
2 |
3 | HostSchema = new SimpleSchema({
4 | name: {
5 | type: String
6 | },
7 | role: {
8 | type: String
9 | },
10 | email: {
11 | type: String
12 | },
13 | phone: {
14 | type: String
15 | }
16 | });
17 |
18 | AddressSchema = new SimpleSchema({
19 | locationName: {
20 | type: String
21 | },
22 | address1: {
23 | type: String
24 | },
25 | address2: {
26 | type: String
27 | },
28 | city: {
29 | type: String
30 | },
31 | state: {
32 | type: String
33 | },
34 | zipCode: {
35 | type: String
36 | }
37 | });
38 |
39 | ActivitySchema = new SimpleSchema({
40 | title: {
41 | type: String
42 | },
43 | startTime: {
44 | type: Date
45 | },
46 | endTime: {
47 | type: Date
48 | },
49 | timeZone: {
50 | type: String
51 | },
52 | address: {
53 | type: AddressSchema
54 | },
55 | attire: {
56 | type: String
57 | },
58 | transit: {
59 | type: String
60 | },
61 | notes: {
62 | type: String
63 | },
64 | //media: {
65 | //??
66 | //}
67 | });
68 |
69 | CommentSchema = new SimpleSchema({
70 | user: {
71 | type: String,
72 | optional: true,
73 | },
74 | body: {
75 | type: String
76 | },
77 | created: {
78 | type: Date
79 | },
80 | imageUrl: {
81 | type: String,
82 | optional: true
83 | },
84 | });
85 |
86 | GuestSchema = new SimpleSchema({
87 | name: {
88 | type: String
89 | },
90 | email: {
91 | type: String
92 | },
93 | phone: {
94 | type: String
95 | },
96 | invited: {
97 | type: Boolean
98 | },
99 | attending: {
100 | type: Boolean
101 | },
102 | plusOne: {
103 | type: Boolean
104 | },
105 | replied: {
106 | type: Boolean
107 | },
108 | mailStatus: {
109 | type: String
110 | },
111 | // initially a guest may not have a user account because all we may have
112 | // is a name and phone number. when the guest becomes a user this field will
113 | // be populated and the event will be added to the user's events array.
114 | guest: {
115 | type: String
116 | },
117 | });
118 |
--------------------------------------------------------------------------------
/collections/users.js:
--------------------------------------------------------------------------------
1 | Schemas.UserProfileSchema = new SimpleSchema({
2 | fullName: {
3 | type: String
4 | },
5 | //email: {
6 | //type: String,
7 | //regEx: SimpleSchema.RegEx.Email
8 | //},
9 |
10 | secondaryEmail: {
11 | type: String,
12 | regEx: SimpleSchema.RegEx.Email,
13 | optional: true
14 | },
15 | phone: {
16 | type: String,
17 | optional: true
18 | },
19 | events: {
20 | type: [String],
21 | defaultValue: [],
22 | },
23 | });
24 |
25 | Meteor.users.attachSchema(new SimpleSchema({
26 | username: {
27 | type: String,
28 | regEx: /^[a-z0-9A-Z_]{3,15}$/,
29 | optional: true
30 | },
31 | emails: {
32 | type: [Object],
33 | // this must be optional if you also use other login services like facebook,
34 | // but if you use only accounts-password, then it can be required
35 | optional: true
36 | },
37 | "emails.$.address": {
38 | type: String,
39 | regEx: SimpleSchema.RegEx.Email,
40 | optional: true
41 | },
42 | "emails.$.verified": {
43 | type: Boolean,
44 | optional: true
45 | },
46 | createdAt: {
47 | type: Date,
48 | optional: true
49 | },
50 | profile: {
51 | type: Schemas.UserProfileSchema,
52 | optional: true
53 | },
54 | services: {
55 | type: Object,
56 | optional: true,
57 | blackbox: true
58 | },
59 | // // Add `roles` to your schema if you use the meteor-roles package.
60 | // // Option 1: Object type
61 | // // If you specify that type as Object, you must also specify the
62 | // // `Roles.GLOBAL_GROUP` group whenever you add a user to a role.
63 | // // Example:
64 | // // Roles.addUsersToRoles(userId, ["admin"], Roles.GLOBAL_GROUP);
65 | // // You can't mix and match adding with and without a group since
66 | // // you will fail validation in some cases.
67 | // roles: {
68 | // type: Object,
69 | // optional: true,
70 | // blackbox: true
71 | // },
72 | // // Option 2: [String] type
73 | // // If you are sure you will never need to use role groups, then
74 | // // you can specify [String] as the type
75 | // roles: {
76 | // type: [String],
77 | // optional: true
78 | // }
79 | }));
80 |
81 |
--------------------------------------------------------------------------------
/client/views/events/show.js:
--------------------------------------------------------------------------------
1 | Template.EventsShow.created = function(){
2 | this.subscribe('event');
3 | };
4 |
5 | Template.EventsShow.rendered = function(){
6 | Meteor.setTimeout(function(){
7 | setEventsShowHeight.call(this);
8 | setCommentFieldPosition.call(this);
9 |
10 | var swiper1 = this.$('.swiper-container')[0].swiper;
11 | var swiper2 = this.$('.swiper-container')[1].swiper;
12 |
13 | swiper1.params.control = swiper2;
14 | swiper2.params.control = swiper1;
15 | }.bind(this), 1000);
16 |
17 | registerEditFormParam();
18 | };
19 |
20 | Template.EventsShow.destroyed = function(){
21 | deregisterParam('edit-form');
22 | };
23 |
24 | Template.EventsShow.helpers({
25 | isPlanner: function(){
26 | if (this.currentEvent()) {
27 | return Router.current().params.user_id === this.currentEvent().user;
28 | }
29 | },
30 | });
31 |
32 | Template.EventsShow.events({
33 | 'submit #new-post': function(event){
34 | stopEvent(event);
35 |
36 | var routeParams = Router.current().params;
37 | var eventId = routeParams.event_id;
38 |
39 | var comment = {
40 | user: routeParams.user_id,
41 | body: event.target.comment.value,
42 | created: new Date(),
43 | };
44 |
45 | Meteor.call('addComment', eventId, comment);
46 | scrollToBottom();
47 |
48 | event.target.comment.value = '';
49 | },
50 |
51 | 'click #back-arrow': function(){
52 | stopEvent(event);
53 | var userId = Router.current().params.user_id;
54 |
55 | Router.go('events.index', { user_id: userId });
56 | },
57 |
58 | 'click #edit-event-btn-wrapper': function(event){
59 | stopEvent(event);
60 |
61 | ParamManager.setParam('edit-form', true);
62 | },
63 | });
64 |
65 | function setEventsShowHeight (){
66 | var windowHeight = $(window).height();
67 | var occupied = (
68 | $('.img-preview-inner').height() + ($('.navbar').height() + 1)
69 | );
70 | var availableHeight = windowHeight - occupied;
71 |
72 | this.$('.swiper-slide-main').height(availableHeight);
73 | };
74 |
75 | function setCommentFieldPosition (){
76 | var windowWidth = $(window).width();
77 | var leftOffset = windowWidth * 2;
78 |
79 | this.$('.form-bottom').css({ left: leftOffset });
80 | };
81 |
82 | function scrollToBottom (){
83 | var newPost = $('.wall-slide li').last();
84 |
85 | newPost.velocity('scroll', { container: $('.wall-slide') });
86 | };
87 |
88 |
89 |
--------------------------------------------------------------------------------
/client/views/events/show.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
9 |
10 |
11 |
{{currentEvent.title}}
12 |
13 |
14 |
15 | {{#if Template.subscriptionsReady}}
16 | {{#if isPlanner}}
17 | {{#swiperSlides
18 | options=plannerNavOptions param='slides' classNames='navbar'
19 | }}
20 | Host
21 | Details
22 | The Wall
23 | Guest List
24 | {{/swiperSlides}}
25 |
26 | {{#swiperSlides
27 | options=plannerSlideOptions param='slides' classNames='swiper-slide-main'
28 | }}
29 | {{> EventsShowHost}}
30 | {{> EventsShowDetails}}
31 | {{> EventsShowWall}}
32 | {{> EventsShowGuestList}}
33 |
34 |
41 | {{/swiperSlides}}
42 |
43 | {{else}}
44 |
45 | {{#swiperSlides
46 | options=plannerNavOptions param='slides' classNames='nav-slides nav-slides-show'
47 | }}
48 | Host
49 | Details
50 | The Wall
51 | Photos
52 | RSVP
53 | {{/swiperSlides}}
54 |
55 | {{#swiperSlides
56 | options=plannerSlideOptions param='slides' classNames='swiper-slide-main'
57 | }}
58 | {{> EventsShowHost}}
59 | {{> EventsShowDetails}}
60 | {{> EventsShowWall}}
61 | {{> EventsShowPhotos}}
62 | {{> EventsShowRsvp}}
63 |
64 |
71 | {{/swiperSlides}}
72 | {{/if}}
73 | {{/if}}
74 |
75 | {{> _eventsEdit currentEvent=currentEvent}}
76 | {{> _eventsActivityEdit currentActivity=currentActivity}}
77 |
78 |
--------------------------------------------------------------------------------
/client/views/events/show/guest-list.html:
--------------------------------------------------------------------------------
1 |
2 |
95 |
96 |
--------------------------------------------------------------------------------
/client/styles/modules/list-items.scss:
--------------------------------------------------------------------------------
1 | .li-xl {
2 | position: relative;
3 | width: 100%;
4 | height: 275px;
5 |
6 | .li-xl-footer {
7 | position: absolute;
8 | bottom: 10px;
9 | color: $white;
10 | padding: 0 10px 0 10px;
11 | }
12 |
13 | .li-xl-footer-title {
14 | margin: 0 0 10px 0;
15 | }
16 |
17 | .li-xl-footer-subtitle {
18 | margin: 0;
19 | }
20 | }
21 |
22 | .li-lrg {
23 | position: relative;
24 | padding: 20px 0 20px 0;
25 | border-bottom: 1px solid $lightgray;
26 | overflow-y: hidden;
27 | height: 124px;
28 |
29 | .li-lrg-preview {
30 | padding: 0 10px 20px 10px;
31 | }
32 |
33 | .li-lrg-preview-heading {
34 | font-weight: bold;
35 | margin: 0 0 5px 0;
36 | }
37 |
38 | .li-lrg-preview-subheading {
39 | margin: 0 0 20px 0;
40 | }
41 |
42 | .li-lrg-preview-offset {
43 | margin-left: 10px;
44 | color: $gray;
45 | }
46 |
47 | .li-lrg-preview-items {
48 | margin-right: 25px;
49 |
50 | .li-lrg-preview-item {
51 | margin: 0;
52 | }
53 | }
54 |
55 | .li-lrg-preview-bottom-right {
56 | position: absolute;
57 | right: 10px;
58 | top: 118px;
59 | }
60 |
61 | .li-lrg-touch-footer {
62 | position: absolute;
63 | top: 110px;
64 | height: 75px;
65 | width: 100%;
66 | z-index: 9;
67 | }
68 |
69 | .li-lrg-banner {
70 | border-top: 1px solid lightgray;
71 | border-bottom: 1px solid lightgray;
72 | height: 100px;
73 | width: 100%;
74 | }
75 |
76 | .li-lrg-more-details {
77 | padding: 20px 10px 0 10px;
78 |
79 | .detail {
80 | margin: 0 0 20px 0;
81 |
82 | &:last-child {
83 | margin-bottom: 0;
84 | }
85 | }
86 |
87 | .detail-heading {
88 | margin: 0 0 5px 0;
89 | font-weight: bold;
90 | color: $gray;
91 | text-transform: uppercase;
92 | }
93 |
94 | .detail-text {
95 | margin: 0;
96 | }
97 | }
98 | }
99 |
100 | .li-med {
101 | padding: 20px 10px 20px 10px;
102 | border-bottom: 1px solid lightgray;
103 |
104 | .li-med-heading {
105 | margin: 0;
106 | }
107 |
108 | .li-med-subheading {
109 | text-transform: capitalize;
110 | }
111 |
112 | .li-med-line-item {
113 | position: relative;
114 |
115 | .li-med-line-item-right {
116 | position: absolute;
117 | right: 0;
118 | top: -11px;
119 | }
120 | }
121 |
122 | .li-med-line-item-text {
123 | margin-right: 60px;
124 |
125 | &.no-bottom-margin {
126 | margin-bottom: 0;
127 | }
128 | }
129 | }
130 |
131 | .li-sml {
132 | padding: 10px 10px 0 10px;
133 |
134 | .li-sml-heading {
135 | font-weight: bold;
136 | text-transform: uppercase;
137 | margin-bottom: 5px;
138 |
139 | .li-sml-subheading {
140 | font-weight: normal;
141 | text-transform: initial;
142 | color: $gray;
143 | margin-left: 5px;
144 | }
145 | }
146 |
147 | .li-sml-text {
148 | margin: 0;
149 | }
150 |
151 | &:last-child {
152 | padding-bottom: 62px;
153 | }
154 | }
155 |
156 | .li-tiny {
157 | position: relative;
158 | padding-left: 10px;
159 | border-bottom: 1px solid $lightgray;
160 |
161 | .li-tiny-text {
162 | position: relative;
163 | margin: 10px 0 10px 0;
164 | }
165 |
166 | .li-tiny-right {
167 | position: absolute;
168 | color: $lightgray;
169 | }
170 |
171 | .right-text {
172 | right: 12px;
173 | text-transform: capitalize;
174 | }
175 |
176 | .right-icon {
177 | right: 5px;
178 | top: -9px;
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/client/lib/formaldehyde.js:
--------------------------------------------------------------------------------
1 | if(Meteor.isClient){
2 | // Check if the Meteor.Poetic object is already populated... if we use this as a normal namespace
3 | // Some other module may have already declared it.. if not then it needs to be instantiated as an empty
4 | // object
5 | if (typeof Meteor.Poetic === "undefined") {
6 | Meteor.Poetic = {};
7 | }
8 | else{
9 | console.log("Meteor.Poetic already exhists");
10 | }
11 | // Register the ParamManager under the Meteor.Poetic namespace
12 | Meteor.Poetic.ParamManager = (function(){
13 |
14 | // Interface is the object that will be returned.. any function or variable added to this will be available
15 | // in the global scope Meteor.Poetic.ParamManager.[methodName]
16 |
17 | var Interface = {};
18 |
19 | // this will be an array of all param objects. This is private and should remain so.
20 | // A param object will follow the structure such as
21 | // {
22 | // param: TheParameterName,
23 | // value: TheValueOfThisParam,
24 | // callback: TheFunctionToCallIfThisValueChanges
25 | // }
26 |
27 | var Params = [];
28 |
29 | // RegisterParamFunction is the method to register your parameter with a callback function.
30 | // based on the model above for params objects your call to this should look like
31 | //
32 | // Meteor.Poetic.ParamManager.RegisterParamFunction('user', function(value){console.log(value);});
33 | //
34 | // this will successfully console.log the new value when the value of user changes.
35 | // your function will most likely impliment some logic based on the value passed back.
36 |
37 | Interface.RegisterParam = function(paramName, callback){
38 | // TODO add a test to check if paramName or callback are null and throw and error
39 | Params[Params.length] = {
40 | param: paramName, // save the parameter name passed
41 | value: null,
42 | callback: function(){
43 | callback(getParameterByName(paramName));
44 | } // the user can maintain scope without using function.bind()
45 | }
46 | }
47 |
48 | // This allows you to deregister a param callback by paramname
49 | Interface.DeRegisterParam = function(paramName){
50 | // iterate through the params object and find a match to the paramName passed then remove it
51 | for(var i = 0; i < Params.length; i++){
52 | if(Params[i].param === paramName){
53 | Params.splice(i, 1);
54 | i = Params.length;
55 | }
56 | }
57 | }
58 |
59 | Interface.isRegistered = function(paramName){
60 | return Params.some(function(param){ return param.param === paramName });
61 | }
62 |
63 | // build URL is the interface set to trigger any callbacks whose state may have changed from the current set call
64 | // This is orientated around a direct javascript call. A template (html) linking method should be added that calls based
65 | // on multiple param values being changed in one link.
66 |
67 | function buildUrl(param, value, replaceState){
68 | var path = location.href.split('?')[0]; // get the URL currently in state
69 | var paramStrings = [];
70 |
71 | Params.forEach(function(urlParam){
72 | if (param === urlParam.param) {
73 | if (value !== null) {
74 | paramStrings.push(urlParam.param + '=' + value);
75 | }
76 | } else {
77 | if (urlParam.value) {
78 | paramStrings.push(urlParam.param + '=' + urlParam.value);
79 | }
80 | }
81 | });
82 |
83 | var queryString = '?' + paramStrings.join('&');
84 |
85 | if (replaceState) {
86 | history.replaceState({}, "OptionalTitle", path + queryString); // store a new state in memory with the built url
87 | } else {
88 | history.pushState({}, "OptionalTitle", path + queryString); // store a new state in memory with the built url
89 | }
90 | }
91 |
92 | // this function needs to push the state of the window, build a new URL based on values passed and then trigger any
93 | // callback functions that are rigistered to the change in url replace is a third optional arguement to update params
94 | // but not push the state forward.
95 | Interface.setParam = function(param, value, replace){
96 | buildUrl(param, value, replace); // pass the values to build a new url
97 | document.dispatchEvent(ChangedURL); // url has changed fire an event to trigger callbacks
98 | }
99 |
100 | // allow the user an easy interface to find or poll for the value of param. This shouldn't ever be needed if the que
101 | // Set and Link methods are used properly, but it does allow for restful principles and easier debugging.
102 | Interface.getParam = function(param){
103 | return getParameterByName(param);
104 | }
105 |
106 | // ChangedURL is the event that will be listened to for url changes. Because of this you should always use
107 | // the paramManager to update the parameter url or ELSE your function will NOT be called.
108 |
109 | var ChangedURL = new Event('urlchange');
110 |
111 | // executeCallbacks will iterate through every object in the params array and execute its callback.
112 | // It will check its current value and the value in the URL if these values are the same then its callback
113 | // won't be called. If the values are different then the url was Changed so then it will run its callback and then
114 | // update its internal value to the new value
115 | function executeCallbacks(){
116 | Params.forEach(function(curParam, i){
117 | var curVal = getParameterByName(curParam.param);
118 | if(curParam.value !== curVal){
119 | curParam.callback(curVal);
120 | curParam.value = curVal;
121 | }
122 | });
123 | }
124 |
125 | // when the document is ready register our callbacks function on the urlchange event.
126 | window.onload = function(){
127 | document.addEventListener('urlchange', function(event){
128 | executeCallbacks();
129 | }, false);
130 | // register forward backwards and directly typed urls to fire this event.
131 | window.onpopstate = function(){
132 | document.dispatchEvent(ChangedURL);
133 | }
134 | document.dispatchEvent(ChangedURL);
135 | };
136 |
137 | // simple function that returns the value of a query param by name from the current url.
138 | function getParameterByName(name) {
139 | name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
140 | var regex = new RegExp("[\\?&]" + name + "=([^]*)"),
141 | results = regex.exec(location.search);
142 | return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
143 | }
144 | return Interface;
145 | })();
146 | }
147 |
148 |
--------------------------------------------------------------------------------
/server/seeds.js:
--------------------------------------------------------------------------------
1 | Meteor.startup(function(){
2 | Meteor.users.remove({})
3 | Events.remove({})
4 |
5 | var user1, user2, user3, user4, user5, user6;
6 | var events = [];
7 |
8 | if (!Meteor.users.find().count()) {
9 | user1 = Accounts.createUser({
10 | email: faker.internet.email(),
11 | password: faker.internet.password(),
12 | profile: {
13 | fullName: faker.name.findName(),
14 | secondaryEmail: faker.internet.email(),
15 | phone: faker.phone.phoneNumber(),
16 | },
17 | });
18 |
19 | user2 = Accounts.createUser({
20 | email: faker.internet.email(),
21 | password: faker.internet.password(),
22 | profile: {
23 | fullName: faker.name.findName(),
24 | secondaryEmail: faker.internet.email(),
25 | phone: faker.phone.phoneNumber(),
26 | },
27 | });
28 |
29 | user3 = Accounts.createUser({
30 | email: faker.internet.email(),
31 | password: faker.internet.password(),
32 | profile: {
33 | fullName: faker.name.findName(),
34 | secondaryEmail: faker.internet.email(),
35 | phone: faker.phone.phoneNumber(),
36 | },
37 | });
38 |
39 | user4 = Accounts.createUser({
40 | email: faker.internet.email(),
41 | password: faker.internet.password(),
42 | profile: {
43 | fullName: faker.name.findName(),
44 | secondaryEmail: faker.internet.email(),
45 | phone: faker.phone.phoneNumber(),
46 | },
47 | });
48 |
49 | user5 = Accounts.createUser({
50 | email: faker.internet.email(),
51 | password: faker.internet.password(),
52 | profile: {
53 | fullName: faker.name.findName(),
54 | secondaryEmail: faker.internet.email(),
55 | phone: faker.phone.phoneNumber(),
56 | },
57 | });
58 |
59 | user6 = Accounts.createUser({
60 | email: faker.internet.email(),
61 | password: faker.internet.password(),
62 | profile: {
63 | fullName: faker.name.findName(),
64 | secondaryEmail: faker.internet.email(),
65 | phone: faker.phone.phoneNumber(),
66 | },
67 | });
68 | }
69 |
70 | if (!Events.find().count()) {
71 | for (var i = 0; i < 10; i++) {
72 | events[i] = Events.insert({
73 | user: user1,
74 | coverPhoto: faker.image.image(),
75 | title: faker.name.findName(),
76 | description: faker.lorem.sentences(),
77 | isPlusOne: true,
78 |
79 | hosts: [{
80 | name: faker.name.findName(),
81 | role: faker.hacker.adjective(),
82 | email: faker.internet.email(),
83 | phone: faker.phone.phoneNumber(),
84 | }, {
85 | name: faker.name.findName(),
86 | role: faker.hacker.adjective(),
87 | email: faker.internet.email(),
88 | phone: faker.phone.phoneNumber(),
89 | }],
90 |
91 | activities: [{
92 | title: faker.name.findName(),
93 | startTime: faker.date.past(),
94 | endTime: faker.date.future(),
95 | timeZone: 'central',
96 |
97 | address: {
98 | locationName: faker.company.companyName(),
99 | address1: faker.address.streetAddress(),
100 | address2: faker.address.secondaryAddress(),
101 | city: faker.address.city(),
102 | state: faker.address.state(),
103 | zipCode: faker.address.zipCode(),
104 | },
105 |
106 | attire: faker.lorem.sentence(),
107 | transit: faker.lorem.sentence(),
108 | notes: faker.lorem.sentence(),
109 | }, {
110 | title: faker.name.findName(),
111 | startTime: faker.date.past(),
112 | endTime: faker.date.future(),
113 | timeZone: 'central',
114 |
115 | address: {
116 | locationName: faker.company.companyName(),
117 | address1: faker.address.streetAddress(),
118 | address2: faker.address.secondaryAddress(),
119 | city: faker.address.city(),
120 | state: faker.address.state(),
121 | zipCode: faker.address.zipCode(),
122 | },
123 |
124 | attire: faker.lorem.sentence(),
125 | transit: faker.lorem.sentence(),
126 | notes: faker.lorem.sentence(),
127 | }, {
128 | title: faker.name.findName(),
129 | startTime: faker.date.past(),
130 | endTime: faker.date.future(),
131 | timeZone: 'central',
132 |
133 | address: {
134 | locationName: faker.company.companyName(),
135 | address1: faker.address.streetAddress(),
136 | address2: faker.address.secondaryAddress(),
137 | city: faker.address.city(),
138 | state: faker.address.state(),
139 | zipCode: faker.address.zipCode(),
140 | },
141 |
142 | attire: faker.lorem.sentence(),
143 | transit: faker.lorem.sentence(),
144 | notes: faker.lorem.sentence(),
145 | }],
146 |
147 | comments: [{
148 | user: user1,
149 | body: faker.lorem.sentence(),
150 | imageUrl: faker.image.imageUrl(),
151 | created: new Date(),
152 | }, {
153 | user: user2,
154 | body: faker.lorem.sentence(),
155 | imageUrl: faker.image.imageUrl(),
156 | created: new Date(),
157 | }, {
158 | user: user1,
159 | body: faker.lorem.sentence(),
160 | imageUrl: faker.image.imageUrl(),
161 | created: new Date(),
162 | }, {
163 | user: user3,
164 | body: faker.lorem.sentence(),
165 | imageUrl: faker.image.imageUrl(),
166 | created: new Date(),
167 | }],
168 | });
169 | };
170 | }
171 |
172 | var auser2, auser3, auser4, auser5, auser6;
173 |
174 | if (Events.find().count()) {
175 | auser2 = Meteor.users.findOne({ _id: user2 })
176 | auser3 = Meteor.users.findOne({ _id: user3 })
177 | auser4 = Meteor.users.findOne({ _id: user4 })
178 | auser5 = Meteor.users.findOne({ _id: user5 })
179 | auser6 = Meteor.users.findOne({ _id: user6 })
180 | }
181 |
182 | if (Events.find().count()) {
183 | Events.update({_id: events[0]}, {$push: {guests: {$each: [{
184 | name: auser2.profile.fullName,
185 | email: auser2.emails[0].address,
186 | phone: auser2.profile.phone,
187 | invited: true,
188 | attending: true,
189 | plusOne: true,
190 | replied: true,
191 | mailStatus: 'opened',
192 | guest: user2,
193 | }, {
194 | name: auser3.profile.fullName,
195 | email: auser3.emails[0].address,
196 | phone: auser3.profile.phone,
197 | invited: false,
198 | attending: false,
199 | plusOne: false,
200 | replied: false,
201 | mailStatus: 'bounced',
202 | guest: user3,
203 | }, {
204 | name: auser5.profile.fullName,
205 | email: auser5.emails[0].address,
206 | phone: auser5.profile.phone,
207 | invited: true,
208 | attending: true,
209 | plusOne: true,
210 | replied: true,
211 | mailStatus: 'opened',
212 | guest: user5,
213 | }, {
214 | name: auser4.profile.fullName,
215 | email: auser4.emails[0].address,
216 | phone: auser4.profile.phone,
217 | invited: false,
218 | attending: false,
219 | plusOne: false,
220 | replied: false,
221 | mailStatus: 'bounced',
222 | guest: user4,
223 | }]}}});
224 |
225 | Events.update({_id: events[1]}, {$push: {guests: {$each: [{
226 | name: auser4.profile.fullName,
227 | email: auser4.emails[0].address,
228 | phone: auser4.profile.phone,
229 | invited: false,
230 | attending: false,
231 | plusOne: false,
232 | replied: false,
233 | mailStatus: 'bounced',
234 | guest: user4,
235 | }, {
236 | name: auser5.profile.fullName,
237 | email: auser5.emails[0].address,
238 | phone: auser5.profile.phone,
239 | invited: true,
240 | attending: true,
241 | plusOne: true,
242 | replied: true,
243 | mailStatus: 'opened',
244 | guest: user5,
245 | }]}}})
246 | }
247 |
248 | if (Meteor.users.find().count() && Events.find().count()) {
249 | Meteor.users.update({ _id: user2 }, { $push: { 'profile.events': { $each: [events[0]] } } })
250 | Meteor.users.update({ _id: user3 }, { $push: { 'profile.events': { $each: [events[0]] } } })
251 | Meteor.users.update({ _id: user4 }, { $push: { 'profile.events': { $each: [events[0], events[1]] } } })
252 | Meteor.users.update({ _id: user5 }, { $push: { 'profile.events': { $each: [events[0], events[1]] } } })
253 | }
254 | });
255 |
--------------------------------------------------------------------------------
/client/swiper/swiper.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Swiper 3.0.6
3 | * Most modern mobile touch slider and framework with hardware accelerated transitions
4 | *
5 | * http://www.idangero.us/swiper/
6 | *
7 | * Copyright 2015, Vladimir Kharlampidi
8 | * The iDangero.us
9 | * http://www.idangero.us/
10 | *
11 | * Licensed under MIT
12 | *
13 | * Released on: March 27, 2015
14 | */
15 | .swiper-container {
16 | margin: 0 auto;
17 | position: relative;
18 | overflow: hidden;
19 | /* Fix of Webkit flickering */
20 | z-index: 1;
21 | }
22 | .swiper-container-no-flexbox .swiper-slide {
23 | float: left;
24 | }
25 | .swiper-container-vertical > .swiper-wrapper {
26 | -webkit-box-orient: vertical;
27 | -moz-box-orient: vertical;
28 | -ms-flex-direction: column;
29 | -webkit-flex-direction: column;
30 | flex-direction: column;
31 | }
32 | .swiper-wrapper {
33 | position: relative;
34 | width: 100%;
35 | height: 100%;
36 | z-index: 1;
37 | display: -webkit-box;
38 | display: -moz-box;
39 | display: -ms-flexbox;
40 | display: -webkit-flex;
41 | display: flex;
42 | -webkit-transform-style: preserve-3d;
43 | -moz-transform-style: preserve-3d;
44 | -ms-transform-style: preserve-3d;
45 | transform-style: preserve-3d;
46 | -webkit-transition-property: -webkit-transform;
47 | -moz-transition-property: -moz-transform;
48 | -o-transition-property: -o-transform;
49 | -ms-transition-property: -ms-transform;
50 | transition-property: transform;
51 | -webkit-box-sizing: content-box;
52 | -moz-box-sizing: content-box;
53 | box-sizing: content-box;
54 | }
55 | .swiper-container-android .swiper-slide,
56 | .swiper-wrapper {
57 | -webkit-transform: translate3d(0px, 0, 0);
58 | -moz-transform: translate3d(0px, 0, 0);
59 | -o-transform: translate(0px, 0px);
60 | -ms-transform: translate3d(0px, 0, 0);
61 | transform: translate3d(0px, 0, 0);
62 | }
63 | .swiper-container-multirow > .swiper-wrapper {
64 | -webkit-box-lines: multiple;
65 | -moz-box-lines: multiple;
66 | -ms-fles-wrap: wrap;
67 | -webkit-flex-wrap: wrap;
68 | flex-wrap: wrap;
69 | }
70 | .swiper-container-free-mode > .swiper-wrapper {
71 | -webkit-transition-timing-function: ease-out;
72 | -moz-transition-timing-function: ease-out;
73 | -ms-transition-timing-function: ease-out;
74 | -o-transition-timing-function: ease-out;
75 | transition-timing-function: ease-out;
76 | margin: 0 auto;
77 | }
78 | .swiper-slide {
79 | -webkit-transform-style: preserve-3d;
80 | -moz-transform-style: preserve-3d;
81 | -ms-transform-style: preserve-3d;
82 | transform-style: preserve-3d;
83 | -webkit-flex-shrink: 0;
84 | -ms-flex: 0 0 auto;
85 | flex-shrink: 0;
86 | width: 100%;
87 | height: 100%;
88 | position: relative;
89 | }
90 | /* a11y */
91 | .swiper-container .swiper-notification {
92 | position: absolute;
93 | left: 0;
94 | top: 0;
95 | pointer-events: none;
96 | opacity: 0;
97 | z-index: -1000;
98 | }
99 | /* IE10 Windows Phone 8 Fixes */
100 | .swiper-wp8-horizontal {
101 | -ms-touch-action: pan-y;
102 | touch-action: pan-y;
103 | }
104 | .swiper-wp8-vertical {
105 | -ms-touch-action: pan-x;
106 | touch-action: pan-x;
107 | }
108 | /* Arrows */
109 | .swiper-button-prev,
110 | .swiper-button-next {
111 | position: absolute;
112 | top: 50%;
113 | width: 27px;
114 | height: 44px;
115 | margin-top: -22px;
116 | z-index: 10;
117 | cursor: pointer;
118 | -moz-background-size: 27px 44px;
119 | -webkit-background-size: 27px 44px;
120 | background-size: 27px 44px;
121 | background-position: center;
122 | background-repeat: no-repeat;
123 | }
124 | .swiper-button-prev.swiper-button-disabled,
125 | .swiper-button-next.swiper-button-disabled {
126 | opacity: 0.35;
127 | cursor: auto;
128 | pointer-events: none;
129 | }
130 | .swiper-button-prev,
131 | .swiper-container-rtl .swiper-button-next {
132 | background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M0%2C22L22%2C0l2.1%2C2.1L4.2%2C22l19.9%2C19.9L22%2C44L0%2C22L0%2C22L0%2C22z'%20fill%3D'%23007aff'%2F%3E%3C%2Fsvg%3E");
133 | left: 10px;
134 | right: auto;
135 | }
136 | .swiper-button-prev.swiper-button-black,
137 | .swiper-container-rtl .swiper-button-next.swiper-button-black {
138 | background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M0%2C22L22%2C0l2.1%2C2.1L4.2%2C22l19.9%2C19.9L22%2C44L0%2C22L0%2C22L0%2C22z'%20fill%3D'%23000000'%2F%3E%3C%2Fsvg%3E");
139 | }
140 | .swiper-button-prev.swiper-button-white,
141 | .swiper-container-rtl .swiper-button-next.swiper-button-white {
142 | background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M0%2C22L22%2C0l2.1%2C2.1L4.2%2C22l19.9%2C19.9L22%2C44L0%2C22L0%2C22L0%2C22z'%20fill%3D'%23ffffff'%2F%3E%3C%2Fsvg%3E");
143 | }
144 | .swiper-button-next,
145 | .swiper-container-rtl .swiper-button-prev {
146 | background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M27%2C22L27%2C22L5%2C44l-2.1-2.1L22.8%2C22L2.9%2C2.1L5%2C0L27%2C22L27%2C22z'%20fill%3D'%23007aff'%2F%3E%3C%2Fsvg%3E");
147 | right: 10px;
148 | left: auto;
149 | }
150 | .swiper-button-next.swiper-button-black,
151 | .swiper-container-rtl .swiper-button-prev.swiper-button-black {
152 | background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M27%2C22L27%2C22L5%2C44l-2.1-2.1L22.8%2C22L2.9%2C2.1L5%2C0L27%2C22L27%2C22z'%20fill%3D'%23000000'%2F%3E%3C%2Fsvg%3E");
153 | }
154 | .swiper-button-next.swiper-button-white,
155 | .swiper-container-rtl .swiper-button-prev.swiper-button-white {
156 | background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M27%2C22L27%2C22L5%2C44l-2.1-2.1L22.8%2C22L2.9%2C2.1L5%2C0L27%2C22L27%2C22z'%20fill%3D'%23ffffff'%2F%3E%3C%2Fsvg%3E");
157 | }
158 | /* Pagination Styles */
159 | .swiper-pagination {
160 | position: absolute;
161 | text-align: center;
162 | -webkit-transition: 300ms;
163 | -moz-transition: 300ms;
164 | -o-transition: 300ms;
165 | transition: 300ms;
166 | -webkit-transform: translate3d(0, 0, 0);
167 | -ms-transform: translate3d(0, 0, 0);
168 | -o-transform: translate3d(0, 0, 0);
169 | transform: translate3d(0, 0, 0);
170 | z-index: 10;
171 | }
172 | .swiper-pagination.swiper-pagination-hidden {
173 | opacity: 0;
174 | }
175 | .swiper-pagination-bullet {
176 | width: 8px;
177 | height: 8px;
178 | display: inline-block;
179 | border-radius: 100%;
180 | background: #000;
181 | opacity: 0.2;
182 | }
183 | .swiper-pagination-clickable .swiper-pagination-bullet {
184 | cursor: pointer;
185 | }
186 | .swiper-pagination-white .swiper-pagination-bullet {
187 | background: #fff;
188 | }
189 | .swiper-pagination-bullet-active {
190 | opacity: 1;
191 | background: #007aff;
192 | }
193 | .swiper-pagination-white .swiper-pagination-bullet-active {
194 | background: #fff;
195 | }
196 | .swiper-pagination-black .swiper-pagination-bullet-active {
197 | background: #000;
198 | }
199 | .swiper-container-vertical > .swiper-pagination {
200 | right: 10px;
201 | top: 50%;
202 | -webkit-transform: translate3d(0px, -50%, 0);
203 | -moz-transform: translate3d(0px, -50%, 0);
204 | -o-transform: translate(0px, -50%);
205 | -ms-transform: translate3d(0px, -50%, 0);
206 | transform: translate3d(0px, -50%, 0);
207 | }
208 | .swiper-container-vertical > .swiper-pagination .swiper-pagination-bullet {
209 | margin: 5px 0;
210 | display: block;
211 | }
212 | .swiper-container-horizontal > .swiper-pagination {
213 | bottom: 10px;
214 | left: 0;
215 | width: 100%;
216 | }
217 | .swiper-container-horizontal > .swiper-pagination .swiper-pagination-bullet {
218 | margin: 0 5px;
219 | }
220 | /* 3D Container */
221 | .swiper-container-3d {
222 | -webkit-perspective: 1200px;
223 | -moz-perspective: 1200px;
224 | -o-perspective: 1200px;
225 | perspective: 1200px;
226 | }
227 | .swiper-container-3d .swiper-wrapper,
228 | .swiper-container-3d .swiper-slide,
229 | .swiper-container-3d .swiper-slide-shadow-left,
230 | .swiper-container-3d .swiper-slide-shadow-right,
231 | .swiper-container-3d .swiper-slide-shadow-top,
232 | .swiper-container-3d .swiper-slide-shadow-bottom,
233 | .swiper-container-3d .swiper-cube-shadow {
234 | -webkit-transform-style: preserve-3d;
235 | -moz-transform-style: preserve-3d;
236 | -ms-transform-style: preserve-3d;
237 | transform-style: preserve-3d;
238 | }
239 | .swiper-container-3d .swiper-slide-shadow-left,
240 | .swiper-container-3d .swiper-slide-shadow-right,
241 | .swiper-container-3d .swiper-slide-shadow-top,
242 | .swiper-container-3d .swiper-slide-shadow-bottom {
243 | position: absolute;
244 | left: 0;
245 | top: 0;
246 | width: 100%;
247 | height: 100%;
248 | pointer-events: none;
249 | z-index: 10;
250 | }
251 | .swiper-container-3d .swiper-slide-shadow-left {
252 | background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, 0.5)), to(rgba(0, 0, 0, 0)));
253 | /* Safari 4+, Chrome */
254 | background-image: -webkit-linear-gradient(right, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
255 | /* Chrome 10+, Safari 5.1+, iOS 5+ */
256 | background-image: -moz-linear-gradient(right, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
257 | /* Firefox 3.6-15 */
258 | background-image: -o-linear-gradient(right, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
259 | /* Opera 11.10-12.00 */
260 | background-image: linear-gradient(to left, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
261 | /* Firefox 16+, IE10, Opera 12.50+ */
262 | }
263 | .swiper-container-3d .swiper-slide-shadow-right {
264 | background-image: -webkit-gradient(linear, right top, left top, from(rgba(0, 0, 0, 0.5)), to(rgba(0, 0, 0, 0)));
265 | /* Safari 4+, Chrome */
266 | background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
267 | /* Chrome 10+, Safari 5.1+, iOS 5+ */
268 | background-image: -moz-linear-gradient(left, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
269 | /* Firefox 3.6-15 */
270 | background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
271 | /* Opera 11.10-12.00 */
272 | background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
273 | /* Firefox 16+, IE10, Opera 12.50+ */
274 | }
275 | .swiper-container-3d .swiper-slide-shadow-top {
276 | background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0.5)), to(rgba(0, 0, 0, 0)));
277 | /* Safari 4+, Chrome */
278 | background-image: -webkit-linear-gradient(bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
279 | /* Chrome 10+, Safari 5.1+, iOS 5+ */
280 | background-image: -moz-linear-gradient(bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
281 | /* Firefox 3.6-15 */
282 | background-image: -o-linear-gradient(bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
283 | /* Opera 11.10-12.00 */
284 | background-image: linear-gradient(to top, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
285 | /* Firefox 16+, IE10, Opera 12.50+ */
286 | }
287 | .swiper-container-3d .swiper-slide-shadow-bottom {
288 | background-image: -webkit-gradient(linear, left bottom, left top, from(rgba(0, 0, 0, 0.5)), to(rgba(0, 0, 0, 0)));
289 | /* Safari 4+, Chrome */
290 | background-image: -webkit-linear-gradient(top, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
291 | /* Chrome 10+, Safari 5.1+, iOS 5+ */
292 | background-image: -moz-linear-gradient(top, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
293 | /* Firefox 3.6-15 */
294 | background-image: -o-linear-gradient(top, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
295 | /* Opera 11.10-12.00 */
296 | background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
297 | /* Firefox 16+, IE10, Opera 12.50+ */
298 | }
299 | /* Coverflow */
300 | .swiper-container-coverflow .swiper-wrapper {
301 | /* Windows 8 IE 10 fix */
302 | -ms-perspective: 1200px;
303 | }
304 | /* Fade */
305 | .swiper-container-fade.swiper-container-free-mode .swiper-slide {
306 | -webkit-transition-timing-function: ease-out;
307 | -moz-transition-timing-function: ease-out;
308 | -ms-transition-timing-function: ease-out;
309 | -o-transition-timing-function: ease-out;
310 | transition-timing-function: ease-out;
311 | }
312 | .swiper-container-fade .swiper-slide {
313 | pointer-events: none;
314 | }
315 | .swiper-container-fade .swiper-slide-active {
316 | pointer-events: auto;
317 | }
318 | /* Cube */
319 | .swiper-container-cube {
320 | overflow: visible;
321 | }
322 | .swiper-container-cube .swiper-slide {
323 | pointer-events: none;
324 | visibility: hidden;
325 | -webkit-transform-origin: 0 0;
326 | -moz-transform-origin: 0 0;
327 | -ms-transform-origin: 0 0;
328 | transform-origin: 0 0;
329 | -webkit-backface-visibility: hidden;
330 | -moz-backface-visibility: hidden;
331 | -ms-backface-visibility: hidden;
332 | backface-visibility: hidden;
333 | width: 100%;
334 | height: 100%;
335 | }
336 | .swiper-container-cube.swiper-container-rtl .swiper-slide {
337 | -webkit-transform-origin: 100% 0;
338 | -moz-transform-origin: 100% 0;
339 | -ms-transform-origin: 100% 0;
340 | transform-origin: 100% 0;
341 | }
342 | .swiper-container-cube .swiper-slide-active,
343 | .swiper-container-cube .swiper-slide-next,
344 | .swiper-container-cube .swiper-slide-prev,
345 | .swiper-container-cube .swiper-slide-next + .swiper-slide {
346 | pointer-events: auto;
347 | visibility: visible;
348 | }
349 | .swiper-container-cube .swiper-cube-shadow {
350 | position: absolute;
351 | left: 0;
352 | bottom: 0px;
353 | width: 100%;
354 | height: 100%;
355 | background: #000;
356 | opacity: 0.6;
357 | -webkit-filter: blur(50px);
358 | filter: blur(50px);
359 | }
360 | .swiper-container-cube.swiper-container-vertical .swiper-cube-shadow {
361 | z-index: 0;
362 | }
363 | /* Scrollbar */
364 | .swiper-scrollbar {
365 | border-radius: 10px;
366 | position: relative;
367 | -ms-touch-action: none;
368 | background: rgba(0, 0, 0, 0.1);
369 | }
370 | .swiper-container-horizontal > .swiper-scrollbar {
371 | position: absolute;
372 | left: 1%;
373 | bottom: 3px;
374 | z-index: 50;
375 | height: 5px;
376 | width: 98%;
377 | }
378 | .swiper-container-vertical > .swiper-scrollbar {
379 | position: absolute;
380 | right: 3px;
381 | top: 1%;
382 | z-index: 50;
383 | width: 5px;
384 | height: 98%;
385 | }
386 | .swiper-scrollbar-drag {
387 | height: 100%;
388 | width: 100%;
389 | position: relative;
390 | background: rgba(0, 0, 0, 0.5);
391 | border-radius: 10px;
392 | left: 0;
393 | top: 0;
394 | }
395 | .swiper-scrollbar-cursor-drag {
396 | cursor: move;
397 | }
398 | /* Preloader */
399 | .swiper-lazy-preloader {
400 | width: 42px;
401 | height: 42px;
402 | position: absolute;
403 | left: 50%;
404 | top: 50%;
405 | margin-left: -21px;
406 | margin-top: -21px;
407 | z-index: 10;
408 | -webkit-transform-origin: 50%;
409 | -moz-transform-origin: 50%;
410 | transform-origin: 50%;
411 | -webkit-animation: swiper-preloader-spin 1s steps(12, end) infinite;
412 | -moz-animation: swiper-preloader-spin 1s steps(12, end) infinite;
413 | animation: swiper-preloader-spin 1s steps(12, end) infinite;
414 | }
415 | .swiper-lazy-preloader:after {
416 | display: block;
417 | content: "";
418 | width: 100%;
419 | height: 100%;
420 | background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20viewBox%3D'0%200%20120%20120'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20xmlns%3Axlink%3D'http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink'%3E%3Cdefs%3E%3Cline%20id%3D'l'%20x1%3D'60'%20x2%3D'60'%20y1%3D'7'%20y2%3D'27'%20stroke%3D'%236c6c6c'%20stroke-width%3D'11'%20stroke-linecap%3D'round'%2F%3E%3C%2Fdefs%3E%3Cg%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(30%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(60%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(90%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(120%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(150%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.37'%20transform%3D'rotate(180%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.46'%20transform%3D'rotate(210%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.56'%20transform%3D'rotate(240%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.66'%20transform%3D'rotate(270%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.75'%20transform%3D'rotate(300%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.85'%20transform%3D'rotate(330%2060%2C60)'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
421 | background-position: 50%;
422 | -webkit-background-size: 100%;
423 | background-size: 100%;
424 | background-repeat: no-repeat;
425 | }
426 | .swiper-lazy-preloader-white:after {
427 | background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20viewBox%3D'0%200%20120%20120'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20xmlns%3Axlink%3D'http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink'%3E%3Cdefs%3E%3Cline%20id%3D'l'%20x1%3D'60'%20x2%3D'60'%20y1%3D'7'%20y2%3D'27'%20stroke%3D'%23fff'%20stroke-width%3D'11'%20stroke-linecap%3D'round'%2F%3E%3C%2Fdefs%3E%3Cg%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(30%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(60%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(90%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(120%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(150%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.37'%20transform%3D'rotate(180%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.46'%20transform%3D'rotate(210%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.56'%20transform%3D'rotate(240%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.66'%20transform%3D'rotate(270%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.75'%20transform%3D'rotate(300%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.85'%20transform%3D'rotate(330%2060%2C60)'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
428 | }
429 | @-webkit-keyframes swiper-preloader-spin {
430 | 100% {
431 | -webkit-transform: rotate(360deg);
432 | }
433 | }
434 | @keyframes swiper-preloader-spin {
435 | 100% {
436 | transform: rotate(360deg);
437 | }
438 | }
439 |
--------------------------------------------------------------------------------
/client/lib/ramjet.js:
--------------------------------------------------------------------------------
1 | (function (global, factory) {
2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3 | typeof define === 'function' && define.amd ? define(factory) :
4 | global.ramjet = factory()
5 | }(this, function () { 'use strict';
6 |
7 | // for the sake of Safari, may it burn in hell
8 | var BLACKLIST = ['length', 'parentRule'];
9 |
10 | var styleKeys = undefined;
11 |
12 | if (typeof CSS2Properties !== 'undefined') {
13 | // why hello Firefox
14 | styleKeys = Object.keys(CSS2Properties.prototype);
15 | } else {
16 | styleKeys = Object.keys(document.createElement('div').style).filter(function (k) {
17 | return ! ~BLACKLIST.indexOf(k);
18 | });
19 | }
20 |
21 | var utils_styleKeys = styleKeys;
22 |
23 | var svgns = 'http://www.w3.org/2000/svg';
24 | var svg = document.createElementNS(svgns, 'svg');
25 |
26 | svg.style.position = 'fixed';
27 | svg.style.top = svg.style.left = '0';
28 | svg.style.width = svg.style.height = '100%';
29 | svg.style.overflow = 'visible';
30 | svg.style.pointerEvents = 'none';
31 | svg.setAttribute('class', 'mogrify-svg');
32 |
33 | var appendedSvg = false;
34 |
35 | function appendSvg() {
36 | document.body.appendChild(svg);
37 | appendedSvg = true;
38 | }
39 |
40 | function cloneNode(node) {
41 | var clone = node.cloneNode();
42 |
43 | var style = undefined;
44 | var len = undefined;
45 | var i = undefined;
46 |
47 | var attr = undefined;
48 |
49 | if (node.nodeType === 1) {
50 | style = window.getComputedStyle(node);
51 |
52 | utils_styleKeys.forEach(function (prop) {
53 | clone.style[prop] = style[prop];
54 | });
55 |
56 | len = node.childNodes.length;
57 | for (i = 0; i < len; i += 1) {
58 | clone.appendChild(cloneNode(node.childNodes[i]));
59 | }
60 | }
61 |
62 | return clone;
63 | }
64 |
65 | function wrapNode(node) {
66 | var isSvg = node.namespaceURI === svgns;
67 |
68 | var _node$getBoundingClientRect = node.getBoundingClientRect();
69 |
70 | var left = _node$getBoundingClientRect.left;
71 | var right = _node$getBoundingClientRect.right;
72 | var top = _node$getBoundingClientRect.top;
73 | var bottom = _node$getBoundingClientRect.bottom;
74 |
75 | var style = window.getComputedStyle(node);
76 |
77 | var clone = cloneNode(node);
78 |
79 | var wrapper = {
80 | node: node, clone: clone, isSvg: isSvg,
81 | cx: (left + right) / 2,
82 | cy: (top + bottom) / 2,
83 | width: right - left,
84 | height: bottom - top,
85 | transform: null,
86 | borderRadius: null
87 | };
88 |
89 | if (isSvg) {
90 | var ctm = node.getScreenCTM();
91 | wrapper.transform = 'matrix(' + [ctm.a, ctm.b, ctm.c, ctm.d, ctm.e, ctm.f].join(',') + ')';
92 | wrapper.borderRadius = [0, 0, 0, 0];
93 |
94 | svg.appendChild(clone);
95 | } else {
96 | var offsetParent = node.offsetParent || document.body;
97 | var offsetParentStyle = window.getComputedStyle(offsetParent);
98 | var offsetParentBcr = offsetParent.getBoundingClientRect();
99 |
100 | clone.style.position = 'absolute';
101 | clone.style.top = top - parseInt(style.marginTop, 10) - (offsetParentBcr.top - parseInt(offsetParentStyle.marginTop, 10)) + 'px';
102 | clone.style.left = left - parseInt(style.marginLeft, 10) - (offsetParentBcr.left - parseInt(offsetParentStyle.marginLeft, 10)) + 'px';
103 |
104 | wrapper.transform = ''; // TODO...?
105 | wrapper.borderRadius = [parseFloat(style.borderTopLeftRadius), parseFloat(style.borderTopRightRadius), parseFloat(style.borderBottomRightRadius), parseFloat(style.borderBottomLeftRadius)];
106 |
107 | node.parentNode.appendChild(clone);
108 | }
109 |
110 | return wrapper;
111 | }
112 |
113 | function hideNode(node) {
114 | node.__ramjetOriginalTransition__ = node.style.transition;
115 | node.style.transition = '';
116 |
117 | node.style.opacity = 0;
118 | }
119 |
120 | function showNode(node) {
121 | node.style.transition = '';
122 | node.style.opacity = 1;
123 |
124 | if (node.__ramjetOriginalTransition__) {
125 | setTimeout(function () {
126 | node.style.transition = node.__ramjetOriginalTransition__;
127 | });
128 | }
129 | }
130 |
131 | var utils_getTransform = getTransform;
132 |
133 | function getTransform(isSvg, cx, cy, dx, dy, dsx, dsy, t) {
134 | var transform = isSvg ? "translate(" + cx + " " + cy + ") scale(" + (1 + t * dsx) + " " + (1 + t * dsy) + ") translate(" + -cx + " " + -cy + ") translate(" + t * dx + " " + t * dy + ")" : "translate(" + t * dx + "px," + t * dy + "px) scale(" + (1 + t * dsx) + "," + (1 + t * dsy) + ")";
135 |
136 | return transform;
137 | }
138 |
139 | var utils_getBorderRadius = getBorderRadius;
140 |
141 | function getBorderRadius(a, b, dsx, dsy, t) {
142 | var sx = 1 + t * dsx;
143 | var sy = 1 + t * dsy;
144 |
145 | return a.map(function (from, i) {
146 | var to = b[i];
147 |
148 | var rx = (from + t * (to - from)) / sx;
149 | var ry = (from + t * (to - from)) / sy;
150 |
151 | return "" + rx + "px " + ry + "px";
152 | });
153 | }
154 |
155 | function linear(pos) {
156 | return pos;
157 | }
158 |
159 | function easeIn(pos) {
160 | return Math.pow(pos, 3);
161 | }
162 |
163 | function easeOut(pos) {
164 | return Math.pow(pos - 1, 3) + 1;
165 | }
166 |
167 | function easeInOut(pos) {
168 | if ((pos /= 0.5) < 1) {
169 | return 0.5 * Math.pow(pos, 3);
170 | }
171 |
172 | return 0.5 * (Math.pow(pos - 2, 3) + 2);
173 | }
174 |
175 | var rAF = window.requestAnimationFrame || window.webkitRequestAnimationFrame || function (fn) {
176 | return setTimeout(fn, 16);
177 | };
178 |
179 | var utils_rAF = rAF;
180 |
181 | function transformers_TimerTransformer___classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
182 |
183 | var transformers_TimerTransformer__TimerTransformer = function TimerTransformer(from, to, options) {
184 | transformers_TimerTransformer___classCallCheck(this, transformers_TimerTransformer__TimerTransformer);
185 |
186 | var dx = to.cx - from.cx;
187 | var dy = to.cy - from.cy;
188 |
189 | var dsxf = to.width / from.width - 1;
190 | var dsyf = to.height / from.height - 1;
191 |
192 | var dsxt = from.width / to.width - 1;
193 | var dsyt = from.height / to.height - 1;
194 |
195 | var startTime = Date.now();
196 | var duration = options.duration || 400;
197 | var easing = options.easing || linear;
198 |
199 | function tick() {
200 | var timeNow = Date.now();
201 | var elapsed = timeNow - startTime;
202 |
203 | if (elapsed > duration) {
204 | from.clone.parentNode.removeChild(from.clone);
205 | to.clone.parentNode.removeChild(to.clone);
206 |
207 | if (options.done) {
208 | options.done();
209 | }
210 |
211 | return;
212 | }
213 |
214 | var t = easing(elapsed / duration);
215 |
216 | // opacity
217 | from.clone.style.opacity = 1 - t;
218 | to.clone.style.opacity = t;
219 |
220 | // border radius
221 | var borderRadius = utils_getBorderRadius(from.borderRadius, to.borderRadius, t);
222 | from.clone.style.borderTopLeftRadius = to.clone.style.borderTopLeftRadius = borderRadius[0];
223 | from.clone.style.borderTopRightRadius = to.clone.style.borderTopRightRadius = borderRadius[1];
224 | from.clone.style.borderBottomRightRadius = to.clone.style.borderBottomRightRadius = borderRadius[2];
225 | from.clone.style.borderBottomLeftRadius = to.clone.style.borderBottomLeftRadius = borderRadius[3];
226 |
227 | var cx = from.cx + dx * t;
228 | var cy = from.cy + dy * t;
229 |
230 | var fromTransform = utils_getTransform(from.isSvg, cx, cy, dx, dy, dsxf, dsyf, t) + ' ' + from.transform;
231 | var toTransform = utils_getTransform(to.isSvg, cx, cy, -dx, -dy, dsxt, dsyt, 1 - t) + ' ' + to.transform;
232 |
233 | if (from.isSvg) {
234 | from.clone.setAttribute('transform', fromTransform);
235 | } else {
236 | from.clone.style.transform = from.clone.style.webkitTransform = from.clone.style.msTransform = fromTransform;
237 | }
238 |
239 | if (to.isSvg) {
240 | to.clone.setAttribute('transform', toTransform);
241 | } else {
242 | to.clone.style.transform = to.clone.style.webkitTransform = to.clone.style.msTransform = toTransform;
243 | }
244 |
245 | utils_rAF(tick);
246 | }
247 |
248 | tick();
249 | };
250 |
251 | var transformers_TimerTransformer = transformers_TimerTransformer__TimerTransformer;
252 |
253 | var div = document.createElement('div');
254 |
255 | var keyframesSupported = true;
256 | var TRANSFORM = undefined;
257 | var KEYFRAMES = undefined;
258 | var ANIMATION_DIRECTION = undefined;
259 | var ANIMATION_DURATION = undefined;
260 | var ANIMATION_ITERATION_COUNT = undefined;
261 | var ANIMATION_NAME = undefined;
262 | var ANIMATION_TIMING_FUNCTION = undefined;
263 | var ANIMATION_END = undefined;
264 |
265 | // We have to browser-sniff for IE11, because it was apparently written
266 | // by a barrel of stoned monkeys - http://jsfiddle.net/rich_harris/oquLu2qL/
267 |
268 | // http://stackoverflow.com/questions/17907445/how-to-detect-ie11
269 | var isIe11 = !window.ActiveXObject && 'ActiveXObject' in window;
270 |
271 | if (!isIe11 && ('transform' in div.style || 'webkitTransform' in div.style) && ('animation' in div.style || 'webkitAnimation' in div.style)) {
272 | keyframesSupported = true;
273 |
274 | TRANSFORM = 'transform' in div.style ? 'transform' : '-webkit-transform';
275 |
276 | if ('animation' in div.style) {
277 | KEYFRAMES = '@keyframes';
278 |
279 | ANIMATION_DIRECTION = 'animationDirection';
280 | ANIMATION_DURATION = 'animationDuration';
281 | ANIMATION_ITERATION_COUNT = 'animationIterationCount';
282 | ANIMATION_NAME = 'animationName';
283 | ANIMATION_TIMING_FUNCTION = 'animationTimingFunction';
284 |
285 | ANIMATION_END = 'animationend';
286 | } else {
287 | KEYFRAMES = '@-webkit-keyframes';
288 |
289 | ANIMATION_DIRECTION = 'webkitAnimationDirection';
290 | ANIMATION_DURATION = 'webkitAnimationDuration';
291 | ANIMATION_ITERATION_COUNT = 'webkitAnimationIterationCount';
292 | ANIMATION_NAME = 'webkitAnimationName';
293 | ANIMATION_TIMING_FUNCTION = 'webkitAnimationTimingFunction';
294 |
295 | ANIMATION_END = 'webkitAnimationEnd';
296 | }
297 | } else {
298 | keyframesSupported = false;
299 | }
300 |
301 | function transformers_KeyframeTransformer___classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
302 |
303 | var transformers_KeyframeTransformer__KeyframeTransformer = function KeyframeTransformer(from, to, options) {
304 | transformers_KeyframeTransformer___classCallCheck(this, transformers_KeyframeTransformer__KeyframeTransformer);
305 |
306 | var _getKeyframes = getKeyframes(from, to, options);
307 |
308 | var fromKeyframes = _getKeyframes.fromKeyframes;
309 | var toKeyframes = _getKeyframes.toKeyframes;
310 |
311 | var fromId = '_' + ~ ~(Math.random() * 1000000);
312 | var toId = '_' + ~ ~(Math.random() * 1000000);
313 |
314 | var css = '' + KEYFRAMES + ' ' + fromId + ' { ' + fromKeyframes + ' } ' + KEYFRAMES + ' ' + toId + ' { ' + toKeyframes + ' }';
315 | var dispose = addCss(css);
316 |
317 | from.clone.style[ANIMATION_DIRECTION] = 'alternate';
318 | from.clone.style[ANIMATION_DURATION] = '' + options.duration / 1000 + 's';
319 | from.clone.style[ANIMATION_ITERATION_COUNT] = 1;
320 | from.clone.style[ANIMATION_NAME] = fromId;
321 | from.clone.style[ANIMATION_TIMING_FUNCTION] = 'linear';
322 |
323 | to.clone.style[ANIMATION_DIRECTION] = 'alternate';
324 | to.clone.style[ANIMATION_DURATION] = '' + options.duration / 1000 + 's';
325 | to.clone.style[ANIMATION_ITERATION_COUNT] = 1;
326 | to.clone.style[ANIMATION_NAME] = toId;
327 | to.clone.style[ANIMATION_TIMING_FUNCTION] = 'linear';
328 |
329 | var fromDone = undefined;
330 | var toDone = undefined;
331 |
332 | function done() {
333 | if (fromDone && toDone) {
334 | from.clone.parentNode.removeChild(from.clone);
335 | to.clone.parentNode.removeChild(to.clone);
336 |
337 | if (options.done) options.done();
338 |
339 | dispose();
340 | }
341 | }
342 |
343 | from.clone.addEventListener(ANIMATION_END, function () {
344 | fromDone = true;
345 | done();
346 | });
347 |
348 | to.clone.addEventListener(ANIMATION_END, function () {
349 | toDone = true;
350 | done();
351 | });
352 | };
353 |
354 | var transformers_KeyframeTransformer = transformers_KeyframeTransformer__KeyframeTransformer;
355 |
356 | function addCss(css) {
357 | var styleElement = document.createElement('style');
358 | styleElement.type = 'text/css';
359 |
360 | var head = document.getElementsByTagName('head')[0];
361 |
362 | // Internet Exploder won't let you use styleSheet.innerHTML - we have to
363 | // use styleSheet.cssText instead
364 | var styleSheet = styleElement.styleSheet;
365 |
366 | if (styleSheet) {
367 | styleSheet.cssText = css;
368 | } else {
369 | styleElement.innerHTML = css;
370 | }
371 |
372 | head.appendChild(styleElement);
373 |
374 | return function () {
375 | return head.removeChild(styleElement);
376 | };
377 | }
378 |
379 | function getKeyframes(from, to, options) {
380 | var dx = to.cx - from.cx;
381 | var dy = to.cy - from.cy;
382 |
383 | var dsxf = to.width / from.width - 1;
384 | var dsyf = to.height / from.height - 1;
385 |
386 | var dsxt = from.width / to.width - 1;
387 | var dsyt = from.height / to.height - 1;
388 |
389 | var easing = options.easing || linear;
390 |
391 | var numFrames = options.duration / 50; // one keyframe per 50ms is probably enough... this may prove not to be the case though
392 |
393 | var fromKeyframes = [];
394 | var toKeyframes = [];
395 | var i;
396 |
397 | function addKeyframes(pc, t) {
398 | var cx = from.cx + dx * t;
399 | var cy = from.cy + dy * t;
400 |
401 | var fromBorderRadius = utils_getBorderRadius(from.borderRadius, to.borderRadius, dsxf, dsyf, t);
402 | var toBorderRadius = utils_getBorderRadius(to.borderRadius, from.borderRadius, dsxt, dsyt, 1 - t);
403 |
404 | var fromTransform = utils_getTransform(false, cx, cy, dx, dy, dsxf, dsyf, t) + ' ' + from.transform;
405 | var toTransform = utils_getTransform(false, cx, cy, -dx, -dy, dsxt, dsyt, 1 - t) + ' ' + to.transform;
406 |
407 | fromKeyframes.push('\n\t\t\t' + pc + '% {\n\t\t\t\topacity: ' + (1 - t) + ';\n\t\t\t\tborder-top-left-radius: ' + fromBorderRadius[0] + ';\n\t\t\t\tborder-top-right-radius: ' + fromBorderRadius[1] + ';\n\t\t\t\tborder-bottom-right-radius: ' + fromBorderRadius[2] + ';\n\t\t\t\tborder-bottom-left-radius: ' + fromBorderRadius[3] + ';\n\t\t\t\t' + TRANSFORM + ': ' + fromTransform + ';\n\t\t\t}');
408 |
409 | toKeyframes.push('\n\t\t\t' + pc + '% {\n\t\t\t\topacity: ' + t + ';\n\t\t\t\tborder-top-left-radius: ' + toBorderRadius[0] + ';\n\t\t\t\tborder-top-right-radius: ' + toBorderRadius[1] + ';\n\t\t\t\tborder-bottom-right-radius: ' + toBorderRadius[2] + ';\n\t\t\t\tborder-bottom-left-radius: ' + toBorderRadius[3] + ';\n\t\t\t\t' + TRANSFORM + ': ' + toTransform + ';\n\t\t\t}');
410 | }
411 |
412 | for (i = 0; i < numFrames; i += 1) {
413 | var pc = 100 * (i / numFrames);
414 | var t = easing(i / numFrames);
415 |
416 | addKeyframes(pc, t);
417 | }
418 |
419 | addKeyframes(100, 1);
420 |
421 | fromKeyframes = fromKeyframes.join('\n');
422 | toKeyframes = toKeyframes.join('\n');
423 |
424 | return { fromKeyframes: fromKeyframes, toKeyframes: toKeyframes };
425 | }
426 |
427 | var ramjet = {
428 | transform: function (fromNode, toNode) {
429 | var options = arguments[2] === undefined ? {} : arguments[2];
430 |
431 | if (typeof options === 'function') {
432 | options = { done: options };
433 | }
434 |
435 | if (!('duration' in options)) {
436 | options.duration = 400;
437 | }
438 |
439 | var from = wrapNode(fromNode);
440 | var to = wrapNode(toNode);
441 |
442 | if (from.isSvg || to.isSvg && !appendedSvg) {
443 | appendSvg();
444 | }
445 |
446 | if (!keyframesSupported || options.useTimer || from.isSvg || to.isSvg) {
447 | return new transformers_TimerTransformer(from, to, options);
448 | } else {
449 | return new transformers_KeyframeTransformer(from, to, options);
450 | }
451 | },
452 |
453 | hide: function () {
454 | for (var _len = arguments.length, nodes = Array(_len), _key = 0; _key < _len; _key++) {
455 | nodes[_key] = arguments[_key];
456 | }
457 |
458 | nodes.forEach(hideNode);
459 | },
460 |
461 | show: function () {
462 | for (var _len2 = arguments.length, nodes = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
463 | nodes[_key2] = arguments[_key2];
464 | }
465 |
466 | nodes.forEach(showNode);
467 | },
468 |
469 | // expose some basic easing functions
470 | linear: linear, easeIn: easeIn, easeOut: easeOut, easeInOut: easeInOut
471 | };
472 |
473 | return ramjet;
474 |
475 | }));
476 |
--------------------------------------------------------------------------------