├── .bowerrc
├── .editorconfig
├── .ember-cli
├── .gitignore
├── .jshintrc
├── .travis.yml
├── .watchmanconfig
├── README.md
├── app
├── app.js
├── components
│ ├── .gitkeep
│ ├── chat-app.js
│ ├── chat-message.js
│ ├── chat-messages.js
│ └── chat-username.js
├── controllers
│ └── .gitkeep
├── helpers
│ └── .gitkeep
├── index.html
├── models
│ └── .gitkeep
├── router.js
├── routes
│ ├── .gitkeep
│ └── index.js
├── services
│ ├── current-user.js
│ └── pusher.js
├── styles
│ ├── app.css
│ └── devices.min.css
└── templates
│ ├── application.hbs
│ ├── components
│ ├── .gitkeep
│ ├── chat-app.hbs
│ ├── chat-message.hbs
│ ├── chat-messages.hbs
│ └── chat-username.hbs
│ └── index.hbs
├── bower.json
├── config
└── environment.js
├── ember-cli-build.js
├── package.json
├── public
├── assets
│ └── images
│ │ ├── ember.png
│ │ ├── iPad_white_thumb.png
│ │ ├── pusher-logo-p.png
│ │ └── pusher-logo.png
├── crossdomain.xml
└── robots.txt
├── testem.json
├── tests
├── .jshintrc
├── helpers
│ ├── resolver.js
│ └── start-app.js
├── index.html
├── integration
│ └── components
│ │ ├── chat-app-test.js
│ │ ├── chat-message-test.js
│ │ ├── chat-messages-test.js
│ │ └── chat-username-test.js
├── test-helper.js
└── unit
│ ├── .gitkeep
│ ├── routes
│ └── index-test.js
│ └── services
│ ├── current-user-test.js
│ └── pusher-test.js
└── vendor
└── .gitkeep
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "bower_components",
3 | "analytics": false
4 | }
5 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 | end_of_line = lf
10 | charset = utf-8
11 | trim_trailing_whitespace = true
12 | insert_final_newline = true
13 | indent_style = space
14 | indent_size = 2
15 |
16 | [*.js]
17 | indent_style = space
18 | indent_size = 2
19 |
20 | [*.hbs]
21 | insert_final_newline = false
22 | indent_style = space
23 | indent_size = 2
24 |
25 | [*.css]
26 | indent_style = space
27 | indent_size = 2
28 |
29 | [*.html]
30 | indent_style = space
31 | indent_size = 2
32 |
33 | [*.{diff,md}]
34 | trim_trailing_whitespace = false
35 |
--------------------------------------------------------------------------------
/.ember-cli:
--------------------------------------------------------------------------------
1 | {
2 | /**
3 | Ember CLI sends analytics information by default. The data is completely
4 | anonymous, but there are times when you might want to disable this behavior.
5 |
6 | Setting `disableAnalytics` to true will prevent any data from being sent.
7 | */
8 | "disableAnalytics": false
9 | }
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 |
7 | # dependencies
8 | /node_modules
9 | /bower_components
10 |
11 | # misc
12 | /.sass-cache
13 | /connect.lock
14 | /coverage/*
15 | /libpeerconnection.log
16 | npm-debug.log
17 | testem.log
18 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "predef": [
3 | "document",
4 | "window",
5 | "-Promise"
6 | ],
7 | "browser": true,
8 | "boss": true,
9 | "curly": true,
10 | "debug": false,
11 | "devel": true,
12 | "eqeqeq": true,
13 | "evil": true,
14 | "forin": false,
15 | "immed": false,
16 | "laxbreak": false,
17 | "newcap": true,
18 | "noarg": true,
19 | "noempty": false,
20 | "nonew": false,
21 | "nomen": false,
22 | "onevar": false,
23 | "plusplus": false,
24 | "regexp": false,
25 | "undef": true,
26 | "sub": true,
27 | "strict": false,
28 | "white": false,
29 | "eqnull": true,
30 | "esnext": true,
31 | "unused": true
32 | }
33 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | ---
2 | language: node_js
3 | node_js:
4 | - "0.12"
5 |
6 | sudo: false
7 |
8 | cache:
9 | directories:
10 | - node_modules
11 |
12 | before_install:
13 | - export PATH=/usr/local/phantomjs-2.0.0/bin:$PATH
14 | - "npm config set spin false"
15 | - "npm install -g npm@^2"
16 |
17 | install:
18 | - npm install -g bower
19 | - npm install
20 | - bower install
21 |
22 | script:
23 | - npm test
24 |
--------------------------------------------------------------------------------
/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {
2 | "ignore_dirs": ["tmp"]
3 | }
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Pusher & EmberJS Chat
2 |
3 | This is an example EmberJS chat application built with Pusher.
4 |
5 | [You can see this app running on Heroku](http://emberjs-realtime-chat.herokuapp.com/).
6 |
7 | ## Prerequisites
8 |
9 | You will need the following things properly installed on your computer.
10 |
11 | * [Git](http://git-scm.com/)
12 | * [Node.js](http://nodejs.org/) (with NPM)
13 | * [Bower](http://bower.io/)
14 | * [Ember CLI](http://www.ember-cli.com/)
15 |
16 | ## Installation
17 |
18 | * `git clone git@github.com:pusher-community/emberjs-realtime-chat.git` this repository
19 | * `cd emberjs-realtime-chat`
20 | * `npm install`
21 | * `bower install`
22 |
23 | ## Running / Development
24 |
25 | * `ember server`
26 | * Visit your app at [http://localhost:4200](http://localhost:4200).
27 | * You'll need the [Sinatra Server](https://github.com/pusher-community/sinatra-realtime-server) running on port `4567` too.
28 |
29 | ## Further Reading / Useful Links
30 |
31 | * [ember.js](http://emberjs.com/)
32 | * [ember-cli](http://www.ember-cli.com/)
33 | * Development Browser Extensions
34 | * [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi)
35 | * [ember inspector for firefox](https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/)
36 |
37 |
--------------------------------------------------------------------------------
/app/app.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import Resolver from 'ember/resolver';
3 | import loadInitializers from 'ember/load-initializers';
4 | import config from './config/environment';
5 |
6 | var App;
7 |
8 | Ember.MODEL_FACTORY_INJECTIONS = true;
9 |
10 | App = Ember.Application.extend({
11 | modulePrefix: config.modulePrefix,
12 | podModulePrefix: config.podModulePrefix,
13 | Resolver: Resolver
14 | });
15 |
16 | loadInitializers(App, config.modulePrefix);
17 |
18 | export default App;
19 |
--------------------------------------------------------------------------------
/app/components/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pusher-community/emberjs-realtime-chat/ff53188a98ae9a977f38a15c277b5830e380b060/app/components/.gitkeep
--------------------------------------------------------------------------------
/app/components/chat-app.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | export default Ember.Component.extend({
4 | currentUserService: Ember.inject.service('current-user'),
5 | nameIsSet: Ember.computed('currentUserService.user', function() {
6 | return this.get('currentUserService').hasUser();
7 | })
8 | });
9 |
--------------------------------------------------------------------------------
/app/components/chat-message.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | export default Ember.Component.extend({
4 | timestamp: Ember.computed('message.time', function() {
5 | return strftime('%H:%M:%S %P', new Date(this.get('message').time));
6 | }),
7 | text: Ember.computed('message.text', function() {
8 | return he.decode(this.get('message').text);
9 | }),
10 | });
11 |
--------------------------------------------------------------------------------
/app/components/chat-messages.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import $ from 'jquery';
3 | import ENV from 'pusher-chat/config/environment';
4 |
5 | export default Ember.Component.extend({
6 | currentUserService: Ember.inject.service('current-user'),
7 | pusher: Ember.inject.service('pusher'),
8 | messages: ['Hi there!', 'Welcome to your chat app!'].map((message) => {
9 | return {
10 | username: 'pusher',
11 | time: new Date(),
12 | text: message,
13 | };
14 | }),
15 | messageObserver: Ember.observer('messages.length', function() {
16 | Ember.run.scheduleOnce('afterRender', function() {
17 | $("#message-list").scrollTop($("#message-list").height());
18 | });
19 | }),
20 | init() {
21 | this._super(...arguments);
22 |
23 | this.get('pusher').onMessage((data) => {
24 | this.get('messages').pushObject(data);
25 | });
26 | },
27 | actions: {
28 | newMessage() {
29 | const text = this.get('newMessage');
30 |
31 | if (!text) return;
32 |
33 | const username = this.get('currentUserService').get('user');
34 | const time = new Date();
35 |
36 | const urlBase = ENV.APP.SERVER_URL;
37 |
38 | $.post(`${urlBase}/messages`, { text, username, time });
39 |
40 | this.set('newMessage', '');
41 | }
42 | }
43 | });
44 |
--------------------------------------------------------------------------------
/app/components/chat-username.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | export default Ember.Component.extend({
4 | currentUserService: Ember.inject.service('current-user'),
5 | actions: {
6 | userSubmittedName() {
7 | this.get('currentUserService').setUser(this.get('userName'));
8 | },
9 | }
10 | });
11 |
--------------------------------------------------------------------------------
/app/controllers/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pusher-community/emberjs-realtime-chat/ff53188a98ae9a977f38a15c277b5830e380b060/app/controllers/.gitkeep
--------------------------------------------------------------------------------
/app/helpers/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pusher-community/emberjs-realtime-chat/ff53188a98ae9a977f38a15c277b5830e380b060/app/helpers/.gitkeep
--------------------------------------------------------------------------------
/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | PusherChat
7 |
8 |
9 |
10 | {{content-for 'head'}}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | {{content-for 'head-footer'}}
19 |
20 |
21 | {{content-for 'body'}}
22 |
23 |
24 |
25 |
26 | {{content-for 'body-footer'}}
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/models/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pusher-community/emberjs-realtime-chat/ff53188a98ae9a977f38a15c277b5830e380b060/app/models/.gitkeep
--------------------------------------------------------------------------------
/app/router.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import config from './config/environment';
3 |
4 | var Router = Ember.Router.extend({
5 | location: config.locationType
6 | });
7 |
8 | Router.map(function() {});
9 |
10 | export default Router;
11 |
--------------------------------------------------------------------------------
/app/routes/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pusher-community/emberjs-realtime-chat/ff53188a98ae9a977f38a15c277b5830e380b060/app/routes/.gitkeep
--------------------------------------------------------------------------------
/app/routes/index.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | export default Ember.Route.extend({
4 | });
5 |
--------------------------------------------------------------------------------
/app/services/current-user.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | export default Ember.Service.extend({
4 | user: null,
5 |
6 | setUser(username) {
7 | this.set('user', username);
8 | },
9 |
10 | hasUser() {
11 | return this.get('user') != undefined;
12 | }
13 | });
14 |
--------------------------------------------------------------------------------
/app/services/pusher.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import ENV from 'pusher-chat/config/environment';
3 |
4 | export default Ember.Service.extend({
5 | init() {
6 | this._super(...arguments);
7 |
8 | this.set('pusher', new Pusher(ENV.APP.PUSHER.key, {
9 | encrypted: true,
10 | }));
11 | },
12 |
13 | onMessage(fn) {
14 | const channel = this.get('pusher').subscribe('messages');
15 | channel.bind('new_message', fn);
16 | }
17 | });
18 |
--------------------------------------------------------------------------------
/app/styles/app.css:
--------------------------------------------------------------------------------
1 | body {
2 | height: 150vh;
3 | }
4 |
5 | .marvel-device.iphone6.silver {
6 | margin-top: -30px;
7 | }
8 |
9 | .marvel-device .screen {
10 | text-align: left;
11 | z-index: 3;
12 | color: inherit;
13 | -webkit-box-shadow: inherit;
14 | box-shadow: inherit;
15 | }
16 |
17 | .action-bar {
18 | width: 330px;
19 | }
20 |
21 | .option {
22 | width: 42px;
23 | padding-top: 8px;
24 | }
25 |
26 | span.emoji {
27 | font-family: initial;
28 | }
29 |
30 | #msg-input {
31 | width: 245px;
32 | }
33 |
34 | .chat-app {
35 | position: relative;
36 | height: 100%;
37 | padding-top: 0 !important;
38 | }
39 |
40 | .chat-app .action-bar {
41 | position: absolute;
42 | width: 100%;
43 | bottom: 0;
44 | }
45 |
46 | #try-it-out {
47 | width: 160px;
48 | font-weight: 200;
49 | padding: 10px 16px;
50 | vertical-align: bottom;
51 | }
52 |
53 | #message-list {
54 | height: 420px;
55 | overflow: scroll;
56 | }
57 |
58 | #input-name {
59 | width: 350px;
60 | }
61 |
62 | .chat-app .action-bar button.option.send-message {
63 | border-radius: 0;
64 | padding-top: 0;
65 | }
66 |
67 | input.input-message {
68 | display: inline-block;
69 | width: 87%;
70 | }
71 |
72 | input.input-message:focus,
73 | input.input-message:hover {
74 | outline: none;
75 | box-shadow: none;
76 | }
77 |
78 | .red-gradient-background {
79 | background: #fe845a;
80 | background-image: -webkit-linear-gradient(top, #fe845a, #d14e37);
81 | background-image: linear-gradient(to bottom, #fe845a, #d14e37);
82 | }
83 |
--------------------------------------------------------------------------------
/app/styles/devices.min.css:
--------------------------------------------------------------------------------
1 | .marvel-device{display:inline-block;position:relative}.marvel-device .screen{width:100%;position:relative;height:100%;color:white;z-index:2;text-align:center;display:block;-webkit-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 0 0 3px #111;box-shadow:0 0 0 3px #111}.marvel-device .top-bar,.marvel-device .bottom-bar{height:3px;background:black;width:100%;display:block}.marvel-device .middle-bar{width:3px;height:4px;top:0px;left:90px;background:black;position:absolute}.marvel-device.iphone6{width:375px;height:667px;padding:105px 24px;background:#d9dbdc;-webkit-border-radius:56px;border-radius:56px;-webkit-box-shadow:inset 0 0 3px 0 rgba(0,0,0,0.2);box-shadow:inset 0 0 3px 0 rgba(0,0,0,0.2)}.marvel-device.iphone6:before{width:calc(100% - 12px);height:calc(100% - 12px);position:absolute;top:6px;content:'';left:6px;-webkit-border-radius:50px;border-radius:50px;background:#f8f8f8;z-index:1}.marvel-device.iphone6:after{width:calc(100% - 16px);height:calc(100% - 16px);position:absolute;top:8px;content:'';left:8px;-webkit-border-radius:48px;border-radius:48px;-webkit-box-shadow:inset 0 0 3px 0 rgba(0,0,0,0.1),inset 0 0 6px 3px #fff;box-shadow:inset 0 0 3px 0 rgba(0,0,0,0.1),inset 0 0 6px 3px #fff;z-index:2}.marvel-device.iphone6 .home{-webkit-border-radius:100%;border-radius:100%;width:68px;height:68px;position:absolute;left:50%;margin-left:-34px;bottom:22px;z-index:3;background:#303233;background:-moz-linear-gradient(-45deg, #303233 0%, #b5b7b9 50%, #f0f2f2 69%, #303233 100%);background:-webkit-gradient(linear, left top, right bottom, color-stop(0%, #303233), color-stop(50%, #b5b7b9), color-stop(69%, #f0f2f2), color-stop(100%, #303233));background:-webkit-linear-gradient(-45deg, #303233 0%, #b5b7b9 50%, #f0f2f2 69%, #303233 100%);background:-o-linear-gradient(-45deg, #303233 0%, #b5b7b9 50%, #f0f2f2 69%, #303233 100%);background:-ms-linear-gradient(-45deg, #303233 0%, #b5b7b9 50%, #f0f2f2 69%, #303233 100%);background:linear-gradient(135deg, #303233 0%, #b5b7b9 50%, #f0f2f2 69%, #303233 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#303233', endColorstr='#303233',GradientType=1 )}.marvel-device.iphone6 .home:before{background:#f8f8f8;position:absolute;content:'';-webkit-border-radius:100%;border-radius:100%;width:calc(100% - 8px);height:calc(100% - 8px);top:4px;left:4px}.marvel-device.iphone6 .top-bar{height:14px;background:#bfbfc0;position:absolute;top:68px;left:0}.marvel-device.iphone6 .bottom-bar{height:14px;background:#bfbfc0;position:absolute;bottom:68px;left:0}.marvel-device.iphone6 .sleep{position:absolute;top:190px;right:-4px;width:4px;height:66px;-webkit-border-radius:0px 2px 2px 0px;border-radius:0px 2px 2px 0px;background:#d9dbdc}.marvel-device.iphone6 .volume{position:absolute;left:-4px;top:188px;z-index:0;height:66px;width:4px;-webkit-border-radius:2px 0px 0px 2px;border-radius:2px 0px 0px 2px;background:#d9dbdc}.marvel-device.iphone6 .volume:before{position:absolute;left:2px;top:-78px;height:40px;width:2px;-webkit-border-radius:2px 0px 0px 2px;border-radius:2px 0px 0px 2px;background:inherit;content:'';display:block}.marvel-device.iphone6 .volume:after{position:absolute;left:0px;top:82px;height:66px;width:4px;-webkit-border-radius:2px 0px 0px 2px;border-radius:2px 0px 0px 2px;background:inherit;content:'';display:block}.marvel-device.iphone6 .camera{background:#3c3d3d;width:12px;height:12px;position:absolute;top:24px;left:50%;margin-left:-6px;-webkit-border-radius:100%;border-radius:100%;z-index:3}.marvel-device.iphone6 .sensor{background:#3c3d3d;width:16px;height:16px;position:absolute;top:49px;left:134px;z-index:3;-webkit-border-radius:100%;border-radius:100%}.marvel-device.iphone6 .speaker{background:#292728;width:70px;height:6px;position:absolute;top:54px;left:50%;margin-left:-35px;-webkit-border-radius:6px;border-radius:6px;z-index:3}.marvel-device.iphone6.gold{background:#f9e7d3}.marvel-device.iphone6.gold .top-bar,.marvel-device.iphone6.gold .bottom-bar{background:white}.marvel-device.iphone6.gold .sleep,.marvel-device.iphone6.gold .volume{background:#f9e7d3}.marvel-device.iphone6.gold .home{background:#cebba9;background:-moz-linear-gradient(-45deg, #cebba9 0%, #f9e7d3 50%, #cebba9 100%);background:-webkit-gradient(linear, left top, right bottom, color-stop(0%, #cebba9), color-stop(50%, #f9e7d3), color-stop(100%, #cebba9));background:-webkit-linear-gradient(-45deg, #cebba9 0%, #f9e7d3 50%, #cebba9 100%);background:-o-linear-gradient(-45deg, #cebba9 0%, #f9e7d3 50%, #cebba9 100%);background:-ms-linear-gradient(-45deg, #cebba9 0%, #f9e7d3 50%, #cebba9 100%);background:linear-gradient(135deg, #cebba9 0%, #f9e7d3 50%, #cebba9 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#cebba9', endColorstr='#cebba9',GradientType=1 )}.marvel-device.iphone6.black{background:#464646;-webkit-box-shadow:inset 0 0 3px 0 rgba(0,0,0,0.7);box-shadow:inset 0 0 3px 0 rgba(0,0,0,0.7)}.marvel-device.iphone6.black:before{background:#080808}.marvel-device.iphone6.black:after{-webkit-box-shadow:inset 0 0 3px 0 rgba(0,0,0,0.1),inset 0 0 6px 3px #212121;box-shadow:inset 0 0 3px 0 rgba(0,0,0,0.1),inset 0 0 6px 3px #212121}.marvel-device.iphone6.black .top-bar,.marvel-device.iphone6.black .bottom-bar{background:#212121}.marvel-device.iphone6.black .volume,.marvel-device.iphone6.black .sleep{background:#464646}.marvel-device.iphone6.black .camera{background:#080808}.marvel-device.iphone6.black .home{background:#080808;background:-moz-linear-gradient(-45deg, #080808 0%, #464646 50%, #080808 100%);background:-webkit-gradient(linear, left top, right bottom, color-stop(0%, #080808), color-stop(50%, #464646), color-stop(100%, #080808));background:-webkit-linear-gradient(-45deg, #080808 0%, #464646 50%, #080808 100%);background:-o-linear-gradient(-45deg, #080808 0%, #464646 50%, #080808 100%);background:-ms-linear-gradient(-45deg, #080808 0%, #464646 50%, #080808 100%);background:linear-gradient(135deg, #080808 0%, #464646 50%, #080808 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#080808', endColorstr='#080808',GradientType=1 )}.marvel-device.iphone6.black .home:before{background:#080808}.marvel-device.iphone6.landscape{padding:24px 105px;height:375px;width:667px}.marvel-device.iphone6.landscape .sleep{top:100%;-webkit-border-radius:0px 0px 2px 2px;border-radius:0px 0px 2px 2px;right:190px;height:4px;width:66px}.marvel-device.iphone6.landscape .volume{width:66px;height:4px;top:-4px;left:calc(100% - 188px - 66px);-webkit-border-radius:2px 2px 0px 0px;border-radius:2px 2px 0px 0px}.marvel-device.iphone6.landscape .volume:before{width:40px;height:2px;top:2px;right:-78px;left:auto;-webkit-border-radius:2px 2px 0px 0px;border-radius:2px 2px 0px 0px}.marvel-device.iphone6.landscape .volume:after{left:-82px;width:66px;height:4px;top:0;-webkit-border-radius:2px 2px 0px 0px;border-radius:2px 2px 0px 0px}.marvel-device.iphone6.landscape .top-bar{width:14px;height:100%;left:calc(100% - 68px - 14px);top:0}.marvel-device.iphone6.landscape .bottom-bar{width:14px;height:100%;left:68px;top:0}.marvel-device.iphone6.landscape .home{top:50%;margin-top:-34px;margin-left:0;left:22px}.marvel-device.iphone6.landscape .sensor{top:134px;left:calc(100% - 49px - 16px)}.marvel-device.iphone6.landscape .speaker{height:70px;width:6px;left:calc(100% - 54px - 6px);top:50%;margin-left:0px;margin-top:-35px}.marvel-device.iphone6.landscape .camera{left:calc(100% - 32px);top:50%;margin-left:0px;margin-top:-5px}.marvel-device.iphone6plus{width:414px;height:736px;padding:112px 26px;background:#d9dbdc;-webkit-border-radius:56px;border-radius:56px;-webkit-box-shadow:inset 0 0 3px 0 rgba(0,0,0,0.2);box-shadow:inset 0 0 3px 0 rgba(0,0,0,0.2)}.marvel-device.iphone6plus:before{width:calc(100% - 12px);height:calc(100% - 12px);position:absolute;top:6px;content:'';left:6px;-webkit-border-radius:50px;border-radius:50px;background:#f8f8f8;z-index:1}.marvel-device.iphone6plus:after{width:calc(100% - 16px);height:calc(100% - 16px);position:absolute;top:8px;content:'';left:8px;-webkit-border-radius:48px;border-radius:48px;-webkit-box-shadow:inset 0 0 3px 0 rgba(0,0,0,0.1),inset 0 0 6px 3px #fff;box-shadow:inset 0 0 3px 0 rgba(0,0,0,0.1),inset 0 0 6px 3px #fff;z-index:2}.marvel-device.iphone6plus .home{-webkit-border-radius:100%;border-radius:100%;width:68px;height:68px;position:absolute;left:50%;margin-left:-34px;bottom:24px;z-index:3;background:#303233;background:-moz-linear-gradient(-45deg, #303233 0%, #b5b7b9 50%, #f0f2f2 69%, #303233 100%);background:-webkit-gradient(linear, left top, right bottom, color-stop(0%, #303233), color-stop(50%, #b5b7b9), color-stop(69%, #f0f2f2), color-stop(100%, #303233));background:-webkit-linear-gradient(-45deg, #303233 0%, #b5b7b9 50%, #f0f2f2 69%, #303233 100%);background:-o-linear-gradient(-45deg, #303233 0%, #b5b7b9 50%, #f0f2f2 69%, #303233 100%);background:-ms-linear-gradient(-45deg, #303233 0%, #b5b7b9 50%, #f0f2f2 69%, #303233 100%);background:linear-gradient(135deg, #303233 0%, #b5b7b9 50%, #f0f2f2 69%, #303233 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#303233', endColorstr='#303233',GradientType=1 )}.marvel-device.iphone6plus .home:before{background:#f8f8f8;position:absolute;content:'';-webkit-border-radius:100%;border-radius:100%;width:calc(100% - 8px);height:calc(100% - 8px);top:4px;left:4px}.marvel-device.iphone6plus .top-bar{height:14px;background:#bfbfc0;position:absolute;top:68px;left:0}.marvel-device.iphone6plus .bottom-bar{height:14px;background:#bfbfc0;position:absolute;bottom:68px;left:0}.marvel-device.iphone6plus .sleep{position:absolute;top:190px;right:-4px;width:4px;height:66px;-webkit-border-radius:0px 2px 2px 0px;border-radius:0px 2px 2px 0px;background:#d9dbdc}.marvel-device.iphone6plus .volume{position:absolute;left:-4px;top:188px;z-index:0;height:66px;width:4px;-webkit-border-radius:2px 0px 0px 2px;border-radius:2px 0px 0px 2px;background:#d9dbdc}.marvel-device.iphone6plus .volume:before{position:absolute;left:2px;top:-78px;height:40px;width:2px;-webkit-border-radius:2px 0px 0px 2px;border-radius:2px 0px 0px 2px;background:inherit;content:'';display:block}.marvel-device.iphone6plus .volume:after{position:absolute;left:0px;top:82px;height:66px;width:4px;-webkit-border-radius:2px 0px 0px 2px;border-radius:2px 0px 0px 2px;background:inherit;content:'';display:block}.marvel-device.iphone6plus .camera{background:#3c3d3d;width:12px;height:12px;position:absolute;top:29px;left:50%;margin-left:-6px;-webkit-border-radius:100%;border-radius:100%;z-index:3}.marvel-device.iphone6plus .sensor{background:#3c3d3d;width:16px;height:16px;position:absolute;top:54px;left:154px;z-index:3;-webkit-border-radius:100%;border-radius:100%}.marvel-device.iphone6plus .speaker{background:#292728;width:70px;height:6px;position:absolute;top:59px;left:50%;margin-left:-35px;-webkit-border-radius:6px;border-radius:6px;z-index:3}.marvel-device.iphone6plus.gold{background:#f9e7d3}.marvel-device.iphone6plus.gold .top-bar,.marvel-device.iphone6plus.gold .bottom-bar{background:white}.marvel-device.iphone6plus.gold .sleep,.marvel-device.iphone6plus.gold .volume{background:#f9e7d3}.marvel-device.iphone6plus.gold .home{background:#cebba9;background:-moz-linear-gradient(-45deg, #cebba9 0%, #f9e7d3 50%, #cebba9 100%);background:-webkit-gradient(linear, left top, right bottom, color-stop(0%, #cebba9), color-stop(50%, #f9e7d3), color-stop(100%, #cebba9));background:-webkit-linear-gradient(-45deg, #cebba9 0%, #f9e7d3 50%, #cebba9 100%);background:-o-linear-gradient(-45deg, #cebba9 0%, #f9e7d3 50%, #cebba9 100%);background:-ms-linear-gradient(-45deg, #cebba9 0%, #f9e7d3 50%, #cebba9 100%);background:linear-gradient(135deg, #cebba9 0%, #f9e7d3 50%, #cebba9 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#cebba9', endColorstr='#cebba9',GradientType=1 )}.marvel-device.iphone6plus.black{background:#464646;-webkit-box-shadow:inset 0 0 3px 0 rgba(0,0,0,0.7);box-shadow:inset 0 0 3px 0 rgba(0,0,0,0.7)}.marvel-device.iphone6plus.black:before{background:#080808}.marvel-device.iphone6plus.black:after{-webkit-box-shadow:inset 0 0 3px 0 rgba(0,0,0,0.1),inset 0 0 6px 3px #212121;box-shadow:inset 0 0 3px 0 rgba(0,0,0,0.1),inset 0 0 6px 3px #212121}.marvel-device.iphone6plus.black .top-bar,.marvel-device.iphone6plus.black .bottom-bar{background:#212121}.marvel-device.iphone6plus.black .volume,.marvel-device.iphone6plus.black .sleep{background:#464646}.marvel-device.iphone6plus.black .camera{background:#080808}.marvel-device.iphone6plus.black .home{background:#080808;background:-moz-linear-gradient(-45deg, #080808 0%, #464646 50%, #080808 100%);background:-webkit-gradient(linear, left top, right bottom, color-stop(0%, #080808), color-stop(50%, #464646), color-stop(100%, #080808));background:-webkit-linear-gradient(-45deg, #080808 0%, #464646 50%, #080808 100%);background:-o-linear-gradient(-45deg, #080808 0%, #464646 50%, #080808 100%);background:-ms-linear-gradient(-45deg, #080808 0%, #464646 50%, #080808 100%);background:linear-gradient(135deg, #080808 0%, #464646 50%, #080808 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#080808', endColorstr='#080808',GradientType=1 )}.marvel-device.iphone6plus.black .home:before{background:#080808}.marvel-device.iphone6plus.landscape{padding:26px 112px;height:414px;width:736px}.marvel-device.iphone6plus.landscape .sleep{top:100%;-webkit-border-radius:0px 0px 2px 2px;border-radius:0px 0px 2px 2px;right:190px;height:4px;width:66px}.marvel-device.iphone6plus.landscape .volume{width:66px;height:4px;top:-4px;left:calc(100% - 188px - 66px);-webkit-border-radius:2px 2px 0px 0px;border-radius:2px 2px 0px 0px}.marvel-device.iphone6plus.landscape .volume:before{width:40px;height:2px;top:2px;right:-78px;left:auto;-webkit-border-radius:2px 2px 0px 0px;border-radius:2px 2px 0px 0px}.marvel-device.iphone6plus.landscape .volume:after{left:-82px;width:66px;height:4px;top:0;-webkit-border-radius:2px 2px 0px 0px;border-radius:2px 2px 0px 0px}.marvel-device.iphone6plus.landscape .top-bar{width:14px;height:100%;left:calc(100% - 68px - 14px);top:0}.marvel-device.iphone6plus.landscape .bottom-bar{width:14px;height:100%;left:68px;top:0}.marvel-device.iphone6plus.landscape .home{top:50%;margin-top:-34px;margin-left:0;left:24px}.marvel-device.iphone6plus.landscape .sensor{top:154px;left:calc(100% - 54px - 16px)}.marvel-device.iphone6plus.landscape .speaker{height:70px;width:6px;left:calc(100% - 59px - 6px);top:50%;margin-left:0px;margin-top:-35px}.marvel-device.iphone6plus.landscape .camera{left:calc(100% - 29px);top:50%;margin-left:0px;margin-top:-5px}.marvel-device.iphone5s,.marvel-device.iphone5c{padding:105px 22px;background:#2c2b2c;width:320px;height:568px;-webkit-border-radius:50px;border-radius:50px}.marvel-device.iphone5s:before,.marvel-device.iphone5c:before{width:calc(100% - 8px);height:calc(100% - 8px);position:absolute;top:4px;content:'';left:4px;-webkit-border-radius:46px;border-radius:46px;background:#1e1e1e;z-index:1}.marvel-device.iphone5s .sleep,.marvel-device.iphone5c .sleep{position:absolute;top:-4px;right:60px;width:60px;height:4px;-webkit-border-radius:2px 2px 0px 0px;border-radius:2px 2px 0px 0px;background:#282727}.marvel-device.iphone5s .volume,.marvel-device.iphone5c .volume{position:absolute;left:-4px;top:180px;z-index:0;height:27px;width:4px;-webkit-border-radius:2px 0px 0px 2px;border-radius:2px 0px 0px 2px;background:#282727}.marvel-device.iphone5s .volume:before,.marvel-device.iphone5c .volume:before{position:absolute;left:0px;top:-75px;height:35px;width:4px;-webkit-border-radius:2px 0px 0px 2px;border-radius:2px 0px 0px 2px;background:inherit;content:'';display:block}.marvel-device.iphone5s .volume:after,.marvel-device.iphone5c .volume:after{position:absolute;left:0px;bottom:-64px;height:27px;width:4px;-webkit-border-radius:2px 0px 0px 2px;border-radius:2px 0px 0px 2px;background:inherit;content:'';display:block}.marvel-device.iphone5s .camera,.marvel-device.iphone5c .camera{background:#3c3d3d;width:10px;height:10px;position:absolute;top:32px;left:50%;margin-left:-5px;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;z-index:3}.marvel-device.iphone5s .sensor,.marvel-device.iphone5c .sensor{background:#3c3d3d;width:10px;height:10px;position:absolute;top:60px;left:160px;z-index:3;margin-left:-32px;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.marvel-device.iphone5s .speaker,.marvel-device.iphone5c .speaker{background:#292728;width:64px;height:10px;position:absolute;top:60px;left:50%;margin-left:-32px;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;z-index:3}.marvel-device.iphone5s.landscape,.marvel-device.iphone5c.landscape{padding:22px 105px;height:320px;width:568px}.marvel-device.iphone5s.landscape .sleep,.marvel-device.iphone5c.landscape .sleep{right:-4px;top:calc(100% - 120px);height:60px;width:4px;-webkit-border-radius:0px 2px 2px 0px;border-radius:0px 2px 2px 0px}.marvel-device.iphone5s.landscape .volume,.marvel-device.iphone5c.landscape .volume{width:27px;height:4px;top:-4px;left:calc(100% - 180px);-webkit-border-radius:2px 2px 0px 0px;border-radius:2px 2px 0px 0px}.marvel-device.iphone5s.landscape .volume:before,.marvel-device.iphone5c.landscape .volume:before{width:35px;height:4px;top:0px;right:-75px;left:auto;-webkit-border-radius:2px 2px 0px 0px;border-radius:2px 2px 0px 0px}.marvel-device.iphone5s.landscape .volume:after,.marvel-device.iphone5c.landscape .volume:after{bottom:0px;left:-64px;z-index:999;height:4px;width:27px;-webkit-border-radius:2px 2px 0px 0px;border-radius:2px 2px 0px 0px}.marvel-device.iphone5s.landscape .sensor,.marvel-device.iphone5c.landscape .sensor{top:160px;left:calc(100% - 60px);margin-left:0px;margin-top:-32px}.marvel-device.iphone5s.landscape .speaker,.marvel-device.iphone5c.landscape .speaker{height:64px;width:10px;left:calc(100% - 60px);top:50%;margin-left:0px;margin-top:-32px}.marvel-device.iphone5s.landscape .camera,.marvel-device.iphone5c.landscape .camera{left:calc(100% - 32px);top:50%;margin-left:0px;margin-top:-5px}.marvel-device.iphone5s .home{-moz-border-radius:36px;-webkit-border-radius:36px;border-radius:36px;width:68px;-webkit-box-shadow:inset 0 0 0 4px #2c2b2c;box-shadow:inset 0 0 0 4px #2c2b2c;height:68px;position:absolute;left:50%;margin-left:-34px;bottom:19px;z-index:3}.marvel-device.iphone5s .top-bar{top:70px;position:absolute;left:0}.marvel-device.iphone5s .bottom-bar{bottom:70px;position:absolute;left:0}.marvel-device.iphone5s.landscape .home{left:19px;bottom:50%;margin-bottom:-34px;margin-left:0px}.marvel-device.iphone5s.landscape .top-bar{left:70px;top:0px;width:3px;height:100%}.marvel-device.iphone5s.landscape .bottom-bar{right:70px;left:auto;bottom:0px;width:3px;height:100%}.marvel-device.iphone5s.silver{background:#bcbcbc}.marvel-device.iphone5s.silver:before{background:#fcfcfc}.marvel-device.iphone5s.silver .volume,.marvel-device.iphone5s.silver .sleep{background:#d6d6d6}.marvel-device.iphone5s.silver .top-bar,.marvel-device.iphone5s.silver .bottom-bar{background:#eaebec}.marvel-device.iphone5s.silver .home{-webkit-box-shadow:inset 0 0 0 4px #bcbcbc;box-shadow:inset 0 0 0 4px #bcbcbc}.marvel-device.iphone5s.gold{background:#f9e7d3}.marvel-device.iphone5s.gold:before{background:#fcfcfc}.marvel-device.iphone5s.gold .volume,.marvel-device.iphone5s.gold .sleep{background:#f9e7d3}.marvel-device.iphone5s.gold .top-bar,.marvel-device.iphone5s.gold .bottom-bar{background:white}.marvel-device.iphone5s.gold .home{-webkit-box-shadow:inset 0 0 0 4px #f9e7d3;box-shadow:inset 0 0 0 4px #f9e7d3}.marvel-device.iphone5c{background:white;-webkit-box-shadow:0 1px 2px 0 rgba(0,0,0,0.2);box-shadow:0 1px 2px 0 rgba(0,0,0,0.2)}.marvel-device.iphone5c .top-bar,.marvel-device.iphone5c .bottom-bar{display:none}.marvel-device.iphone5c .home{background:#242324;-moz-border-radius:36px;-webkit-border-radius:36px;border-radius:36px;width:68px;height:68px;z-index:3;position:absolute;left:50%;margin-left:-34px;bottom:19px}.marvel-device.iphone5c .home:after{width:20px;height:20px;border:1px solid rgba(255,255,255,0.1);-moz-border-radius:4px;-webkit-border-radius:4px;border-radius:4px;position:absolute;display:block;content:'';top:50%;left:50%;margin-top:-11px;margin-left:-11px}.marvel-device.iphone5c.landscape .home{left:19px;bottom:50%;margin-bottom:-34px;margin-left:0px}.marvel-device.iphone5c .volume,.marvel-device.iphone5c .sleep{background:#dddddd}.marvel-device.iphone5c.red{background:#f96b6c}.marvel-device.iphone5c.red .volume,.marvel-device.iphone5c.red .sleep{background:#ed5758}.marvel-device.iphone5c.yellow{background:#f2dc60}.marvel-device.iphone5c.yellow .volume,.marvel-device.iphone5c.yellow .sleep{background:#e5ce4c}.marvel-device.iphone5c.green{background:#97e563}.marvel-device.iphone5c.green .volume,.marvel-device.iphone5c.green .sleep{background:#85d94d}.marvel-device.iphone5c.blue{background:#33a2db}.marvel-device.iphone5c.blue .volume,.marvel-device.iphone5c.blue .sleep{background:#2694cd}.marvel-device.iphone4s{padding:129px 27px;width:320px;height:480px;background:#686868;-webkit-border-radius:54px;border-radius:54px}.marvel-device.iphone4s:before{content:'';width:calc(100% - 8px);height:calc(100% - 8px);position:absolute;top:4px;left:4px;z-index:1;-webkit-border-radius:50px;border-radius:50px;background:#1e1e1e}.marvel-device.iphone4s .top-bar{top:60px;position:absolute;left:0}.marvel-device.iphone4s .bottom-bar{bottom:90px;position:absolute;left:0}.marvel-device.iphone4s .camera{background:#3c3d3d;width:10px;height:10px;position:absolute;top:72px;left:134px;z-index:3;margin-left:-5px;-webkit-border-radius:100%;border-radius:100%}.marvel-device.iphone4s .speaker{background:#292728;width:64px;height:10px;position:absolute;top:72px;left:50%;z-index:3;margin-left:-32px;-webkit-border-radius:5px;border-radius:5px}.marvel-device.iphone4s .sensor{background:#292728;width:40px;height:10px;position:absolute;top:36px;left:50%;z-index:3;margin-left:-20px;-webkit-border-radius:5px;border-radius:5px}.marvel-device.iphone4s .home{background:#242324;-webkit-border-radius:100%;border-radius:100%;width:72px;height:72px;z-index:3;position:absolute;left:50%;margin-left:-36px;bottom:30px}.marvel-device.iphone4s .home:after{width:20px;height:20px;border:1px solid rgba(255,255,255,0.1);-webkit-border-radius:4px;border-radius:4px;position:absolute;display:block;content:'';top:50%;left:50%;margin-top:-11px;margin-left:-11px}.marvel-device.iphone4s .sleep{position:absolute;top:-4px;right:60px;width:60px;height:4px;-webkit-border-radius:2px 2px 0px 0px;border-radius:2px 2px 0px 0px;background:#4D4D4D}.marvel-device.iphone4s .volume{position:absolute;left:-4px;top:160px;height:27px;width:4px;-webkit-border-radius:2px 0px 0px 2px;border-radius:2px 0px 0px 2px;background:#4D4D4D}.marvel-device.iphone4s .volume:before{position:absolute;left:0px;top:-70px;height:35px;width:4px;-webkit-border-radius:2px 0px 0px 2px;border-radius:2px 0px 0px 2px;background:inherit;content:'';display:block}.marvel-device.iphone4s .volume:after{position:absolute;left:0px;bottom:-64px;height:27px;width:4px;-webkit-border-radius:2px 0px 0px 2px;border-radius:2px 0px 0px 2px;background:inherit;content:'';display:block}.marvel-device.iphone4s.landscape{padding:27px 129px;height:320px;width:480px}.marvel-device.iphone4s.landscape .bottom-bar{left:90px;bottom:0px;height:100%;width:3px}.marvel-device.iphone4s.landscape .top-bar{left:calc(100% - 60px);top:0px;height:100%;width:3px}.marvel-device.iphone4s.landscape .camera{top:134px;left:calc(100% - 72px);margin-left:0}.marvel-device.iphone4s.landscape .speaker{top:50%;margin-left:0;margin-top:-32px;left:calc(100% - 72px);width:10px;height:64px}.marvel-device.iphone4s.landscape .sensor{height:40px;width:10px;left:calc(100% - 36px);top:50%;margin-left:0;margin-top:-20px}.marvel-device.iphone4s.landscape .home{left:30px;bottom:50%;margin-left:0;margin-bottom:-36px}.marvel-device.iphone4s.landscape .sleep{height:60px;width:4px;right:-4px;top:calc(100% - 120px);-webkit-border-radius:0px 2px 2px 0px;border-radius:0px 2px 2px 0px}.marvel-device.iphone4s.landscape .volume{top:-4px;left:calc(100% - 187px);height:4px;width:27px;-webkit-border-radius:2px 2px 0px 0px;border-radius:2px 2px 0px 0px}.marvel-device.iphone4s.landscape .volume:before{right:-70px;left:auto;top:0px;width:35px;height:4px;-webkit-border-radius:2px 2px 0px 0px;border-radius:2px 2px 0px 0px}.marvel-device.iphone4s.landscape .volume:after{width:27px;height:4px;bottom:0px;left:-64px;-webkit-border-radius:2px 2px 0px 0px;border-radius:2px 2px 0px 0px}.marvel-device.iphone4s.silver{background:#bcbcbc}.marvel-device.iphone4s.silver:before{background:#fcfcfc}.marvel-device.iphone4s.silver .home{background:#fcfcfc;-webkit-box-shadow:inset 0 0 0 1px #bcbcbc;box-shadow:inset 0 0 0 1px #bcbcbc}.marvel-device.iphone4s.silver .home:after{border:1px solid rgba(0,0,0,0.2)}.marvel-device.iphone4s.silver .volume,.marvel-device.iphone4s.silver .sleep{background:#d6d6d6}.marvel-device.nexus5{padding:50px 15px 50px 15px;width:320px;height:568px;background:#1e1e1e;-webkit-border-radius:20px;border-radius:20px}.marvel-device.nexus5:before{-webkit-border-radius:600px / 50px;border-radius:600px / 50px;background:inherit;content:'';top:0;position:absolute;height:103.1%;width:calc(100% - 26px);top:50%;left:50%;-moz-transform:translateX(-50%) translateY(-50%);-webkit-transform:translateX(-50%) translateY(-50%);-o-transform:translateX(-50%) translateY(-50%);-ms-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%)}.marvel-device.nexus5 .top-bar{width:calc(100% - 8px);height:calc(100% - 6px);position:absolute;top:3px;left:4px;-webkit-border-radius:20px;border-radius:20px;background:#181818}.marvel-device.nexus5 .top-bar:before{-webkit-border-radius:600px / 50px;border-radius:600px / 50px;background:inherit;content:'';top:0;position:absolute;height:103.0%;width:calc(100% - 26px);top:50%;left:50%;-moz-transform:translateX(-50%) translateY(-50%);-webkit-transform:translateX(-50%) translateY(-50%);-o-transform:translateX(-50%) translateY(-50%);-ms-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%)}.marvel-device.nexus5 .bottom-bar{display:none}.marvel-device.nexus5 .sleep{width:3px;position:absolute;left:-3px;top:110px;height:100px;background:inherit;-webkit-border-radius:2px 0px 0px 2px;border-radius:2px 0px 0px 2px}.marvel-device.nexus5 .volume{width:3px;position:absolute;right:-3px;top:70px;height:45px;background:inherit;-webkit-border-radius:0px 2px 2px 0px;border-radius:0px 2px 2px 0px}.marvel-device.nexus5 .camera{background:#3c3d3d;width:10px;height:10px;position:absolute;top:18px;left:50%;z-index:3;margin-left:-5px;-webkit-border-radius:100%;border-radius:100%}.marvel-device.nexus5 .camera:before{background:#3c3d3d;width:6px;height:6px;content:'';display:block;position:absolute;top:2px;left:-100px;z-index:3;-webkit-border-radius:100%;border-radius:100%}.marvel-device.nexus5.landscape{padding:15px 50px 15px 50px;height:320px;width:568px}.marvel-device.nexus5.landscape:before{width:103.1%;height:calc(100% - 26px);-webkit-border-radius:50px / 600px;border-radius:50px / 600px}.marvel-device.nexus5.landscape .top-bar{left:3px;top:4px;height:calc(100% - 8px);width:calc(100% - 6px)}.marvel-device.nexus5.landscape .top-bar:before{width:103%;height:calc(100% - 26px);-webkit-border-radius:50px / 600px;border-radius:50px / 600px}.marvel-device.nexus5.landscape .sleep{height:3px;width:100px;left:calc(100% - 210px);top:-3px;-webkit-border-radius:2px 2px 0px 0px;border-radius:2px 2px 0px 0px}.marvel-device.nexus5.landscape .volume{height:3px;width:45px;right:70px;top:100%;-webkit-border-radius:0px 0px 2px 2px;border-radius:0px 0px 2px 2px}.marvel-device.nexus5.landscape .camera{top:50%;left:calc(100% - 18px);margin-left:0;margin-top:-5px}.marvel-device.nexus5.landscape .camera:before{top:-100px;left:2px}.marvel-device.s5{padding:60px 18px;-webkit-border-radius:42px;border-radius:42px;width:320px;height:568px;background:#bcbcbc}.marvel-device.s5:before,.marvel-device.s5:after{width:calc(100% - 52px);content:'';display:block;height:26px;background:inherit;position:absolute;-webkit-border-radius:500px / 40px;border-radius:500px / 40px;left:50%;-moz-transform:translateX(-50%);-webkit-transform:translateX(-50%);-o-transform:translateX(-50%);-ms-transform:translateX(-50%);transform:translateX(-50%)}.marvel-device.s5:before{top:-7px}.marvel-device.s5:after{bottom:-7px}.marvel-device.s5 .bottom-bar{display:none}.marvel-device.s5 .top-bar{-webkit-border-radius:37px;border-radius:37px;width:calc(100% - 10px);height:calc(100% - 10px);top:5px;left:5px;background:radial-gradient(rgba(0,0,0,0.02) 20%, transparent 60%) 0 0,radial-gradient(rgba(0,0,0,0.02) 20%, transparent 60%) 3px 3px;background-color:white;background-size:4px 4px;background-position:center;z-index:2;position:absolute}.marvel-device.s5 .top-bar:before,.marvel-device.s5 .top-bar:after{width:calc(100% - 48px);content:'';display:block;height:26px;background:inherit;position:absolute;-webkit-border-radius:500px / 40px;border-radius:500px / 40px;left:50%;-moz-transform:translateX(-50%);-webkit-transform:translateX(-50%);-o-transform:translateX(-50%);-ms-transform:translateX(-50%);transform:translateX(-50%)}.marvel-device.s5 .top-bar:before{top:-7px}.marvel-device.s5 .top-bar:after{bottom:-7px}.marvel-device.s5 .sleep{width:3px;position:absolute;left:-3px;top:100px;height:100px;background:#cecece;-webkit-border-radius:2px 0px 0px 2px;border-radius:2px 0px 0px 2px}.marvel-device.s5 .speaker{width:68px;height:8px;position:absolute;top:20px;display:block;z-index:3;left:50%;margin-left:-34px;background-color:#bcbcbc;background-position:top left;-webkit-border-radius:4px;border-radius:4px}.marvel-device.s5 .sensor{display:block;position:absolute;top:20px;right:110px;background:#3c3d3d;-moz-border-radius:100%;-webkit-border-radius:100%;border-radius:100%;width:8px;height:8px;z-index:3}.marvel-device.s5 .sensor:after{display:block;content:'';position:absolute;top:0px;right:12px;background:#3c3d3d;-moz-border-radius:100%;-webkit-border-radius:100%;border-radius:100%;width:8px;height:8px;z-index:3}.marvel-device.s5 .camera{display:block;position:absolute;top:24px;right:42px;background:black;-moz-border-radius:100%;-webkit-border-radius:100%;border-radius:100%;width:10px;height:10px;z-index:3}.marvel-device.s5 .camera:before{width:4px;height:4px;background:#3c3d3d;-moz-border-radius:100%;-webkit-border-radius:100%;border-radius:100%;position:absolute;content:'';top:50%;left:50%;margin-top:-2px;margin-left:-2px}.marvel-device.s5 .home{position:absolute;z-index:3;bottom:17px;left:50%;width:70px;height:20px;background:white;-webkit-border-radius:18px;border-radius:18px;display:block;margin-left:-35px;border:2px solid black}.marvel-device.s5.landscape{padding:18px 60px;height:320px;width:568px}.marvel-device.s5.landscape:before,.marvel-device.s5.landscape:after{height:calc(100% - 52px);width:26px;-webkit-border-radius:40px / 500px;border-radius:40px / 500px;-moz-transform:translateY(-50%);-webkit-transform:translateY(-50%);-o-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%)}.marvel-device.s5.landscape:before{top:50%;left:-7px}.marvel-device.s5.landscape:after{top:50%;left:auto;right:-7px}.marvel-device.s5.landscape .top-bar:before,.marvel-device.s5.landscape .top-bar:after{width:26px;height:calc(100% - 48px);-webkit-border-radius:40px / 500px;border-radius:40px / 500px;-moz-transform:translateY(-50%);-webkit-transform:translateY(-50%);-o-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%)}.marvel-device.s5.landscape .top-bar:before{right:-7px;top:50%;left:auto}.marvel-device.s5.landscape .top-bar:after{left:-7px;top:50%;right:auto}.marvel-device.s5.landscape .sleep{height:3px;width:100px;left:calc(100% - 200px);top:-3px;-webkit-border-radius:2px 2px 0px 0px;border-radius:2px 2px 0px 0px}.marvel-device.s5.landscape .speaker{height:68px;width:8px;left:calc(100% - 20px);top:50%;margin-left:0;margin-top:-34px}.marvel-device.s5.landscape .sensor{right:20px;top:calc(100% - 110px)}.marvel-device.s5.landscape .sensor:after{left:-12px;right:0px}.marvel-device.s5.landscape .camera{top:calc(100% - 42px);right:24px}.marvel-device.s5.landscape .home{width:20px;height:70px;bottom:50%;margin-bottom:-35px;margin-left:0;left:17px}.marvel-device.s5.black{background:#1e1e1e}.marvel-device.s5.black .speaker{background:black}.marvel-device.s5.black .sleep{background:#1e1e1e}.marvel-device.s5.black .top-bar{background:radial-gradient(rgba(0,0,0,0.05) 20%, transparent 60%) 0 0,radial-gradient(rgba(0,0,0,0.05) 20%, transparent 60%) 3px 3px;background-color:#2c2b2c;background-size:4px 4px}.marvel-device.s5.black .home{background:#2c2b2c}.marvel-device.lumia920{padding:80px 35px 125px 35px;background:#ffdd00;width:320px;height:533px;-moz-border-radius:40px / 3px;-webkit-border-radius:40px / 3px;border-radius:40px / 3px}.marvel-device.lumia920 .bottom-bar{display:none}.marvel-device.lumia920 .top-bar{width:calc(100% - 24px);height:calc(100% - 32px);position:absolute;top:16px;left:12px;-moz-border-radius:24px;-webkit-border-radius:24px;border-radius:24px;background:black;z-index:1}.marvel-device.lumia920 .top-bar:before{background:#1e1e1e;display:block;content:'';width:calc(100% - 4px);height:calc(100% - 4px);top:2px;left:2px;position:absolute;-moz-border-radius:22px;-webkit-border-radius:22px;border-radius:22px}.marvel-device.lumia920 .volume{width:3px;position:absolute;top:130px;height:100px;background:#1e1e1e;right:-3px;-webkit-border-radius:0px 2px 2px 0px;border-radius:0px 2px 2px 0px}.marvel-device.lumia920 .volume:before{width:3px;position:absolute;top:190px;content:'';display:block;height:50px;background:inherit;right:0px;-webkit-border-radius:0px 2px 2px 0px;border-radius:0px 2px 2px 0px}.marvel-device.lumia920 .volume:after{width:3px;position:absolute;top:460px;content:'';display:block;height:50px;background:inherit;right:0px;-webkit-border-radius:0px 2px 2px 0px;border-radius:0px 2px 2px 0px}.marvel-device.lumia920 .camera{background:#3c3d3d;width:10px;height:10px;position:absolute;top:34px;right:130px;z-index:5;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.marvel-device.lumia920 .speaker{background:#292728;width:64px;height:10px;position:absolute;top:38px;left:50%;margin-left:-32px;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;z-index:3}.marvel-device.lumia920.landscape{padding:35px 80px 35px 125px;height:320px;width:568px;-moz-border-radius:2px / 100px;-webkit-border-radius:2px / 100px;border-radius:2px / 100px}.marvel-device.lumia920.landscape .top-bar{height:calc(100% - 24px);width:calc(100% - 32px);left:16px;top:12px}.marvel-device.lumia920.landscape .volume{height:3px;right:130px;width:100px;top:100%;-webkit-border-radius:0px 0px 2px 2px;border-radius:0px 0px 2px 2px}.marvel-device.lumia920.landscape .volume:before{height:3px;right:190px;top:0px;width:50px;-webkit-border-radius:0px 0px 2px 2px;border-radius:0px 0px 2px 2px}.marvel-device.lumia920.landscape .volume:after{height:3px;right:430px;top:0px;width:50px;-webkit-border-radius:0px 0px 2px 2px;border-radius:0px 0px 2px 2px}.marvel-device.lumia920.landscape .camera{right:30px;top:calc(100% - 140px)}.marvel-device.lumia920.landscape .speaker{width:10px;height:64px;top:50%;margin-left:0;margin-top:-32px;left:calc(100% - 48px)}.marvel-device.lumia920.black{background:black}.marvel-device.lumia920.white{background:white;-webkit-box-shadow:0 1px 2px 0 rgba(0,0,0,0.2);box-shadow:0 1px 2px 0 rgba(0,0,0,0.2)}.marvel-device.lumia920.blue{background:#00acdd}.marvel-device.lumia920.red{background:#CC3E32}.marvel-device.htc-one{padding:72px 25px 100px 25px;width:320px;height:568px;background:#bebebe;-webkit-border-radius:34px;border-radius:34px}.marvel-device.htc-one:before{content:'';display:block;width:calc(100% - 4px);height:calc(100% - 4px);position:absolute;top:2px;left:2px;background:#adadad;-webkit-border-radius:32px;border-radius:32px}.marvel-device.htc-one:after{content:'';display:block;width:calc(100% - 8px);height:calc(100% - 8px);position:absolute;top:4px;left:4px;background:#eeeeee;-webkit-border-radius:30px;border-radius:30px}.marvel-device.htc-one .top-bar{width:calc(100% - 4px);height:635px;position:absolute;background:#424242;top:50px;z-index:1;left:2px}.marvel-device.htc-one .top-bar:before{content:'';position:absolute;width:calc(100% - 4px);height:100%;position:absolute;background:black;top:0px;z-index:1;left:2px}.marvel-device.htc-one .bottom-bar{display:none}.marvel-device.htc-one .speaker{height:16px;width:216px;display:block;position:absolute;top:22px;z-index:2;left:50%;margin-left:-108px;background:radial-gradient(#343434 25%, transparent 50%) 0 0,radial-gradient(#343434 25%, transparent 50%) 4px 4px;background-size:4px 4px;background-position:top left}.marvel-device.htc-one .speaker:after{content:'';height:16px;width:216px;display:block;position:absolute;top:676px;z-index:2;left:50%;margin-left:-108px;background:inherit}.marvel-device.htc-one .camera{display:block;position:absolute;top:18px;right:38px;background:#3c3d3d;-moz-border-radius:100%;-webkit-border-radius:100%;border-radius:100%;width:24px;height:24px;z-index:3}.marvel-device.htc-one .camera:before{width:8px;height:8px;background:black;-moz-border-radius:100%;-webkit-border-radius:100%;border-radius:100%;position:absolute;content:'';top:50%;left:50%;margin-top:-4px;margin-left:-4px}.marvel-device.htc-one .sensor{display:block;position:absolute;top:29px;left:60px;background:#3c3d3d;-moz-border-radius:100%;-webkit-border-radius:100%;border-radius:100%;width:8px;height:8px;z-index:3}.marvel-device.htc-one .sensor:after{display:block;content:'';position:absolute;top:0px;right:12px;background:#3c3d3d;-moz-border-radius:100%;-webkit-border-radius:100%;border-radius:100%;width:8px;height:8px;z-index:3}.marvel-device.htc-one.landscape{padding:25px 72px 25px 100px;height:320px;width:568px}.marvel-device.htc-one.landscape .top-bar{height:calc(100% - 4px);width:635px;left:calc(100% - 685px);top:2px}.marvel-device.htc-one.landscape .speaker{width:16px;height:216px;left:calc(100% - 38px);top:50%;margin-left:0px;margin-top:-108px}.marvel-device.htc-one.landscape .speaker:after{width:16px;height:216px;left:calc(100% - 692px);top:50%;margin-left:0;margin-top:-108px}.marvel-device.htc-one.landscape .camera{right:18px;top:calc(100% - 38px)}.marvel-device.htc-one.landscape .sensor{left:calc(100% - 29px);top:60px}.marvel-device.htc-one.landscape .sensor :after{right:0;top:-12px}.marvel-device.ipad{width:576px;height:768px;padding:90px 25px;background:#242324;-webkit-border-radius:44px;border-radius:44px}.marvel-device.ipad:before{width:calc(100% - 8px);height:calc(100% - 8px);position:absolute;content:'';display:block;top:4px;left:4px;-webkit-border-radius:40px;border-radius:40px;background:#1e1e1e}.marvel-device.ipad .camera{background:#3c3d3d;width:10px;height:10px;position:absolute;top:44px;left:50%;margin-left:-5px;-webkit-border-radius:100%;border-radius:100%}.marvel-device.ipad .top-bar,.marvel-device.ipad .bottom-bar{display:none}.marvel-device.ipad .home{background:#242324;-webkit-border-radius:36px;border-radius:36px;width:50px;height:50px;position:absolute;left:50%;margin-left:-25px;bottom:22px}.marvel-device.ipad .home:after{width:15px;height:15px;margin-top:-8px;margin-left:-8px;border:1px solid rgba(255,255,255,0.1);-webkit-border-radius:4px;border-radius:4px;position:absolute;display:block;content:'';top:50%;left:50%}.marvel-device.ipad.landscape{height:576px;width:768px;padding:25px 90px}.marvel-device.ipad.landscape .camera{left:calc(100% - 44px);top:50%;margin-left:0;margin-top:-5px}.marvel-device.ipad.landscape .home{top:50%;left:22px;margin-left:0;margin-top:-25px}.marvel-device.ipad.silver{background:#bcbcbc}.marvel-device.ipad.silver:before{background:#fcfcfc}.marvel-device.ipad.silver .home{background:#fcfcfc;-webkit-box-shadow:inset 0 0 0 1px #bcbcbc;box-shadow:inset 0 0 0 1px #bcbcbc}.marvel-device.ipad.silver .home:after{border:1px solid rgba(0,0,0,0.2)}
2 |
3 |
--------------------------------------------------------------------------------
/app/templates/application.hbs:
--------------------------------------------------------------------------------
1 |
22 |
23 | {{outlet}}
24 |
--------------------------------------------------------------------------------
/app/templates/components/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pusher-community/emberjs-realtime-chat/ff53188a98ae9a977f38a15c277b5830e380b060/app/templates/components/.gitkeep
--------------------------------------------------------------------------------
/app/templates/components/chat-app.hbs:
--------------------------------------------------------------------------------
1 | {{#if nameIsSet}}
2 | Welcome {{ userName }}
3 | {{#chat-messages}}{{/chat-messages}}
4 | {{else}}
5 |
6 | {{#chat-username}}{{/chat-username}}
7 |
8 | {{/if}}
9 |
--------------------------------------------------------------------------------
/app/templates/components/chat-message.hbs:
--------------------------------------------------------------------------------
1 |
2 |

3 |
4 |
5 |
6 | {{ message.username }}
7 | {{ timestamp }}
8 |
9 |
10 |
11 |
{{ text }}
12 |
13 |
--------------------------------------------------------------------------------
/app/templates/components/chat-messages.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Today
14 |
15 |
16 | {{#each messages as |message|}}
17 |
18 | {{#chat-message message=message}}{{/chat-message}}
19 |
20 | {{/each}}
21 |
22 |
23 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/templates/components/chat-username.hbs:
--------------------------------------------------------------------------------
1 | Enter your Twitter name and start chatting!
2 |
3 |
10 |
11 |
--------------------------------------------------------------------------------
/app/templates/index.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Realtime Chat with Pusher and Ember
6 |
7 | {{#chat-app}}{{/chat-app}}
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pusher-chat",
3 | "dependencies": {
4 | "ember": "2.1.0",
5 | "ember-cli-shims": "ember-cli/ember-cli-shims#0.0.3",
6 | "ember-cli-test-loader": "ember-cli-test-loader#0.1.3",
7 | "ember-data": "2.1.0",
8 | "ember-load-initializers": "ember-cli/ember-load-initializers#0.1.5",
9 | "ember-qunit": "0.4.9",
10 | "ember-qunit-notifications": "0.0.7",
11 | "ember-resolver": "~0.1.18",
12 | "jquery": "^1.11.3",
13 | "loader.js": "ember-cli/loader.js#3.2.1",
14 | "qunit": "~1.18.0",
15 | "pusher": "~3.0.0",
16 | "strftime": "~0.9.2",
17 | "he": "~0.5.0"
18 | },
19 | "resolutions": {
20 | "ember": "2.1.0"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/config/environment.js:
--------------------------------------------------------------------------------
1 | /* jshint node: true */
2 |
3 | module.exports = function(environment) {
4 | var ENV = {
5 | modulePrefix: 'pusher-chat',
6 | environment: environment,
7 | baseURL: '/',
8 | locationType: 'auto',
9 | EmberENV: {
10 | FEATURES: {
11 | // Here you can enable experimental features on an ember canary build
12 | // e.g. 'with-controller': true
13 | }
14 | },
15 |
16 | APP: {
17 | // Here you can pass flags/options to your application instance
18 | // when it is created
19 | PUSHER: {
20 | key: '6652ff76df277967f23e',
21 | },
22 | }
23 | };
24 |
25 | if (environment === 'development') {
26 | ENV.APP.SERVER_URL = 'http://localhost:4567';
27 | // ENV.APP.LOG_RESOLVER = true;
28 | // ENV.APP.LOG_ACTIVE_GENERATION = true;
29 | // ENV.APP.LOG_TRANSITIONS = true;
30 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
31 | // ENV.APP.LOG_VIEW_LOOKUPS = true;
32 | }
33 |
34 | if (environment === 'test') {
35 | // Testem prefers this...
36 | ENV.baseURL = '/';
37 | ENV.locationType = 'none';
38 |
39 | // keep test console output quieter
40 | ENV.APP.LOG_ACTIVE_GENERATION = false;
41 | ENV.APP.LOG_VIEW_LOOKUPS = false;
42 |
43 | ENV.APP.rootElement = '#ember-testing';
44 | }
45 |
46 | if (environment === 'production') {
47 | ENV.APP.SERVER_URL = 'https://pusher-chat-sinatra.herokuapp.com';
48 |
49 | }
50 |
51 | ENV['contentSecurityPolicy'] = {
52 | 'default-src': "'none'",
53 | 'script-src': "'self' https://stats.pusher.com/",
54 | 'connect-src': "'self' wss://ws.pusherapp.com/ http://localhost:4567/",
55 | 'img-src': "'self'",
56 | 'style-src': "'self' fonts.googleapis.com http://d3dhju7igb20wy.cloudfront.net/ 'unsafe-inline'",
57 | 'font-src': "'self' fonts.gstatic.com http://d3dhju7igb20wy.cloudfront.net/",
58 | 'media-src': "'self'",
59 | }
60 |
61 | return ENV;
62 | };
63 |
--------------------------------------------------------------------------------
/ember-cli-build.js:
--------------------------------------------------------------------------------
1 | /* global require, module */
2 | var EmberApp = require('ember-cli/lib/broccoli/ember-app');
3 |
4 | module.exports = function(defaults) {
5 | var app = new EmberApp(defaults, {
6 | // Add options here
7 | });
8 |
9 | app.import('bower_components/pusher/dist/pusher.min.js');
10 | app.import('bower_components/strftime/strftime-min.js');
11 | app.import('bower_components/he/he.js');
12 | app.import('app/styles/devices.min.css');
13 | // Use `app.import` to add additional libraries to the generated
14 | // output files.
15 | //
16 | // If you need to use different assets in different
17 | // environments, specify an object as the first parameter. That
18 | // object's keys should be the environment name and the values
19 | // should be the asset to use in that environment.
20 | //
21 | // If the library that you are including contains AMD or ES6
22 | // modules that you would like to import into your application
23 | // please specify an object with the list of modules as keys
24 | // along with the exports of each module as its value.
25 |
26 | return app.toTree();
27 | };
28 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pusher-chat",
3 | "version": "0.0.0",
4 | "description": "Small description for pusher-chat goes here",
5 | "private": true,
6 | "directories": {
7 | "doc": "doc",
8 | "test": "tests"
9 | },
10 | "scripts": {
11 | "build": "ember build",
12 | "start": "ember server",
13 | "test": "ember test"
14 | },
15 | "repository": "",
16 | "engines": {
17 | "node": ">= 0.10.0"
18 | },
19 | "author": "",
20 | "license": "MIT",
21 | "devDependencies": {
22 | "broccoli-asset-rev": "^2.1.2",
23 | "ember-cli": "1.13.8",
24 | "ember-cli-app-version": "0.5.0",
25 | "ember-cli-babel": "^5.1.3",
26 | "ember-cli-content-security-policy": "0.4.0",
27 | "ember-cli-dependency-checker": "^1.0.1",
28 | "ember-cli-htmlbars": "0.7.9",
29 | "ember-cli-htmlbars-inline-precompile": "^0.2.0",
30 | "ember-cli-ic-ajax": "0.2.1",
31 | "ember-cli-inject-live-reload": "^1.3.1",
32 | "ember-cli-qunit": "^1.0.0",
33 | "ember-cli-release": "0.2.3",
34 | "ember-cli-sri": "^1.0.3",
35 | "ember-cli-uglify": "^1.2.0",
36 | "ember-data": "1.13.8",
37 | "ember-disable-proxy-controllers": "^1.0.0",
38 | "ember-export-application-global": "^1.0.3"
39 | },
40 | "dependencies": {
41 | "strftime": "^0.9.2"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/public/assets/images/ember.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pusher-community/emberjs-realtime-chat/ff53188a98ae9a977f38a15c277b5830e380b060/public/assets/images/ember.png
--------------------------------------------------------------------------------
/public/assets/images/iPad_white_thumb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pusher-community/emberjs-realtime-chat/ff53188a98ae9a977f38a15c277b5830e380b060/public/assets/images/iPad_white_thumb.png
--------------------------------------------------------------------------------
/public/assets/images/pusher-logo-p.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pusher-community/emberjs-realtime-chat/ff53188a98ae9a977f38a15c277b5830e380b060/public/assets/images/pusher-logo-p.png
--------------------------------------------------------------------------------
/public/assets/images/pusher-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pusher-community/emberjs-realtime-chat/ff53188a98ae9a977f38a15c277b5830e380b060/public/assets/images/pusher-logo.png
--------------------------------------------------------------------------------
/public/crossdomain.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
15 |
16 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # http://www.robotstxt.org
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/testem.json:
--------------------------------------------------------------------------------
1 | {
2 | "framework": "qunit",
3 | "test_page": "tests/index.html?hidepassed",
4 | "disable_watching": true,
5 | "launch_in_ci": [
6 | "PhantomJS"
7 | ],
8 | "launch_in_dev": [
9 | "PhantomJS",
10 | "Chrome"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/tests/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "predef": [
3 | "document",
4 | "window",
5 | "location",
6 | "setTimeout",
7 | "$",
8 | "-Promise",
9 | "define",
10 | "console",
11 | "visit",
12 | "exists",
13 | "fillIn",
14 | "click",
15 | "keyEvent",
16 | "triggerEvent",
17 | "find",
18 | "findWithAssert",
19 | "wait",
20 | "DS",
21 | "andThen",
22 | "currentURL",
23 | "currentPath",
24 | "currentRouteName"
25 | ],
26 | "node": false,
27 | "browser": false,
28 | "boss": true,
29 | "curly": true,
30 | "debug": false,
31 | "devel": false,
32 | "eqeqeq": true,
33 | "evil": true,
34 | "forin": false,
35 | "immed": false,
36 | "laxbreak": false,
37 | "newcap": true,
38 | "noarg": true,
39 | "noempty": false,
40 | "nonew": false,
41 | "nomen": false,
42 | "onevar": false,
43 | "plusplus": false,
44 | "regexp": false,
45 | "undef": true,
46 | "sub": true,
47 | "strict": false,
48 | "white": false,
49 | "eqnull": true,
50 | "esnext": true,
51 | "unused": true
52 | }
53 |
--------------------------------------------------------------------------------
/tests/helpers/resolver.js:
--------------------------------------------------------------------------------
1 | import Resolver from 'ember/resolver';
2 | import config from '../../config/environment';
3 |
4 | var resolver = Resolver.create();
5 |
6 | resolver.namespace = {
7 | modulePrefix: config.modulePrefix,
8 | podModulePrefix: config.podModulePrefix
9 | };
10 |
11 | export default resolver;
12 |
--------------------------------------------------------------------------------
/tests/helpers/start-app.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import Application from '../../app';
3 | import config from '../../config/environment';
4 |
5 | export default function startApp(attrs) {
6 | var application;
7 |
8 | var attributes = Ember.merge({}, config.APP);
9 | attributes = Ember.merge(attributes, attrs); // use defaults, but you can override;
10 |
11 | Ember.run(function() {
12 | application = Application.create(attributes);
13 | application.setupForTesting();
14 | application.injectTestHelpers();
15 | });
16 |
17 | return application;
18 | }
19 |
--------------------------------------------------------------------------------
/tests/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | PusherChat Tests
7 |
8 |
9 |
10 | {{content-for 'head'}}
11 | {{content-for 'test-head'}}
12 |
13 |
14 |
15 |
16 |
17 | {{content-for 'head-footer'}}
18 | {{content-for 'test-head-footer'}}
19 |
20 |
21 |
22 | {{content-for 'body'}}
23 | {{content-for 'test-body'}}
24 |
25 |
26 |
27 |
28 |
29 |
30 | {{content-for 'body-footer'}}
31 | {{content-for 'test-body-footer'}}
32 |
33 |
34 |
--------------------------------------------------------------------------------
/tests/integration/components/chat-app-test.js:
--------------------------------------------------------------------------------
1 | import { moduleForComponent, test } from 'ember-qunit';
2 | import hbs from 'htmlbars-inline-precompile';
3 |
4 | moduleForComponent('chat-app', 'Integration | Component | chat app', {
5 | integration: true
6 | });
7 |
8 | test('it renders', function(assert) {
9 | assert.expect(2);
10 |
11 | // Set any properties with this.set('myProperty', 'value');
12 | // Handle any actions with this.on('myAction', function(val) { ... });
13 |
14 | this.render(hbs`{{chat-app}}`);
15 |
16 | assert.equal(this.$().text().trim(), '');
17 |
18 | // Template block usage:
19 | this.render(hbs`
20 | {{#chat-app}}
21 | template block text
22 | {{/chat-app}}
23 | `);
24 |
25 | assert.equal(this.$().text().trim(), 'template block text');
26 | });
27 |
--------------------------------------------------------------------------------
/tests/integration/components/chat-message-test.js:
--------------------------------------------------------------------------------
1 | import { moduleForComponent, test } from 'ember-qunit';
2 | import hbs from 'htmlbars-inline-precompile';
3 |
4 | moduleForComponent('chat-message', 'Integration | Component | chat message', {
5 | integration: true
6 | });
7 |
8 | test('it renders', function(assert) {
9 | assert.expect(2);
10 |
11 | // Set any properties with this.set('myProperty', 'value');
12 | // Handle any actions with this.on('myAction', function(val) { ... });
13 |
14 | this.render(hbs`{{chat-message}}`);
15 |
16 | assert.equal(this.$().text().trim(), '');
17 |
18 | // Template block usage:
19 | this.render(hbs`
20 | {{#chat-message}}
21 | template block text
22 | {{/chat-message}}
23 | `);
24 |
25 | assert.equal(this.$().text().trim(), 'template block text');
26 | });
27 |
--------------------------------------------------------------------------------
/tests/integration/components/chat-messages-test.js:
--------------------------------------------------------------------------------
1 | import { moduleForComponent, test } from 'ember-qunit';
2 | import hbs from 'htmlbars-inline-precompile';
3 |
4 | moduleForComponent('chat-messages', 'Integration | Component | chat messages', {
5 | integration: true
6 | });
7 |
8 | test('it renders', function(assert) {
9 | assert.expect(2);
10 |
11 | // Set any properties with this.set('myProperty', 'value');
12 | // Handle any actions with this.on('myAction', function(val) { ... });
13 |
14 | this.render(hbs`{{chat-messages}}`);
15 |
16 | assert.equal(this.$().text().trim(), '');
17 |
18 | // Template block usage:
19 | this.render(hbs`
20 | {{#chat-messages}}
21 | template block text
22 | {{/chat-messages}}
23 | `);
24 |
25 | assert.equal(this.$().text().trim(), 'template block text');
26 | });
27 |
--------------------------------------------------------------------------------
/tests/integration/components/chat-username-test.js:
--------------------------------------------------------------------------------
1 | import { moduleForComponent, test } from 'ember-qunit';
2 | import hbs from 'htmlbars-inline-precompile';
3 |
4 | moduleForComponent('chat-username', 'Integration | Component | chat username', {
5 | integration: true
6 | });
7 |
8 | test('it renders', function(assert) {
9 | assert.expect(2);
10 |
11 | // Set any properties with this.set('myProperty', 'value');
12 | // Handle any actions with this.on('myAction', function(val) { ... });
13 |
14 | this.render(hbs`{{chat-username}}`);
15 |
16 | assert.equal(this.$().text().trim(), '');
17 |
18 | // Template block usage:
19 | this.render(hbs`
20 | {{#chat-username}}
21 | template block text
22 | {{/chat-username}}
23 | `);
24 |
25 | assert.equal(this.$().text().trim(), 'template block text');
26 | });
27 |
--------------------------------------------------------------------------------
/tests/test-helper.js:
--------------------------------------------------------------------------------
1 | import resolver from './helpers/resolver';
2 | import {
3 | setResolver
4 | } from 'ember-qunit';
5 |
6 | setResolver(resolver);
7 |
--------------------------------------------------------------------------------
/tests/unit/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pusher-community/emberjs-realtime-chat/ff53188a98ae9a977f38a15c277b5830e380b060/tests/unit/.gitkeep
--------------------------------------------------------------------------------
/tests/unit/routes/index-test.js:
--------------------------------------------------------------------------------
1 | import { moduleFor, test } from 'ember-qunit';
2 |
3 | moduleFor('route:index', 'Unit | Route | index', {
4 | // Specify the other units that are required for this test.
5 | // needs: ['controller:foo']
6 | });
7 |
8 | test('it exists', function(assert) {
9 | var route = this.subject();
10 | assert.ok(route);
11 | });
12 |
--------------------------------------------------------------------------------
/tests/unit/services/current-user-test.js:
--------------------------------------------------------------------------------
1 | import { moduleFor, test } from 'ember-qunit';
2 |
3 | moduleFor('service:current-user', 'Unit | Service | current user', {
4 | // Specify the other units that are required for this test.
5 | // needs: ['service:foo']
6 | });
7 |
8 | // Replace this with your real tests.
9 | test('it exists', function(assert) {
10 | var service = this.subject();
11 | assert.ok(service);
12 | });
13 |
--------------------------------------------------------------------------------
/tests/unit/services/pusher-test.js:
--------------------------------------------------------------------------------
1 | import { moduleFor, test } from 'ember-qunit';
2 |
3 | moduleFor('service:pusher', 'Unit | Service | pusher', {
4 | // Specify the other units that are required for this test.
5 | // needs: ['service:foo']
6 | });
7 |
8 | // Replace this with your real tests.
9 | test('it exists', function(assert) {
10 | var service = this.subject();
11 | assert.ok(service);
12 | });
13 |
--------------------------------------------------------------------------------
/vendor/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pusher-community/emberjs-realtime-chat/ff53188a98ae9a977f38a15c277b5830e380b060/vendor/.gitkeep
--------------------------------------------------------------------------------