├── .gitignore ├── Gruntfile.js ├── README.bubbling.md ├── README.localization.md ├── README.md ├── build.sh ├── config.js.example ├── contrib └── apache │ └── buddycloud-apache-virtual-host ├── css ├── FollowerList.css ├── LoginSidebar.css ├── MetadataPane.css ├── PostStream.css ├── SubscribedChannelsList.css ├── UserMenu.css ├── anon.css ├── bubbling.css ├── button.css ├── channel.css ├── channelDetails.css ├── common.css ├── discover.css ├── dropzone.css ├── edit.css ├── fonts │ ├── Nunito SIL OFL Font License.txt │ ├── Nunito-Regular-webfont.eot │ ├── Nunito-Regular-webfont.svg │ ├── Nunito-Regular-webfont.ttf │ └── Nunito-Regular-webfont.woff ├── form.css ├── grid.css ├── helpers.css ├── homepage.css ├── lightSidebar.css ├── login.css ├── main.css ├── mobile │ └── test.css ├── normalize.css ├── overlay.css ├── popup.css ├── reset.css ├── sidebar.css ├── singlePost.css ├── spinner.css ├── startpage.css └── stylus.css ├── favicon.ico ├── img ├── Clouds_by_Felix_Niklas.jpg ├── bc-icon.png ├── beagle.png ├── buddycloud-logo-white.png ├── buddycloud-logo.png ├── buddycloud-logo.svg ├── buddycloud.png ├── facebook_16.png ├── firefox-manifest-icon-128.png ├── globe.png ├── glyphicons_062_paperclip.png ├── google_16.png ├── info.png ├── lock-small.png ├── magnifier.png ├── media-download.png ├── minus.png ├── new.png ├── padlock.png ├── personal-50px.jpg ├── personal-75px.jpg ├── plus.png ├── poweredby.png ├── reddit_16.png ├── settings_dropdown.png ├── spinner.gif ├── spinner.png ├── spinner_yellow.png ├── spritemap.png ├── spritemap@2x.png ├── test_image.jpg ├── topic-50px.jpg ├── topic-75px.jpg ├── tumblr_16.png ├── twitter_16.png └── unlock-small.png ├── index.html ├── js ├── app │ ├── Router.js │ ├── main.js │ ├── models │ │ ├── Channel.js │ │ ├── ChannelFollowers.js │ │ ├── ChannelFollowersRequests.js │ │ ├── ChannelItems.js │ │ ├── ChannelMetadata.js │ │ ├── CollectionBase.js │ │ ├── ContentSearch.js │ │ ├── ContentSearchResult.js │ │ ├── Discover.js │ │ ├── DiscoverCollection.js │ │ ├── Item.js │ │ ├── MetadataSearch.js │ │ ├── MetadataSearchResult.js │ │ ├── ModelBase.js │ │ ├── PostNotifications.js │ │ ├── Preferences.js │ │ ├── Search.js │ │ ├── SidebarInfo.js │ │ ├── SidebarInfoCollection.js │ │ ├── SimilarChannels.js │ │ ├── SubscribedChannels.js │ │ ├── Sync.js │ │ ├── User.js │ │ ├── UserCredentials.js │ │ └── db │ │ │ ├── ChannelMetadataDB.js │ │ │ ├── PostsDB.js │ │ │ └── SidebarInfoDB.js │ ├── util │ │ ├── animations.js │ │ ├── api.js │ │ ├── autoResize.js │ │ ├── avatarFallback.js │ │ ├── dateUtils.js │ │ ├── embedlify.js │ │ ├── indexedDB.js │ │ ├── linkify.js │ │ ├── mediaFallback.js │ │ ├── mediaServer.js │ │ ├── spinner.js │ │ └── url.js │ └── views │ │ ├── content │ │ ├── AbstractEditStream.js │ │ ├── AbstractExploreView.js │ │ ├── AnonBar.js │ │ ├── AnonChannelOverlay.js │ │ ├── ChannelDetails.js │ │ ├── ChannelHeader.js │ │ ├── ChannelList.js │ │ ├── ChannelListDetails.js │ │ ├── ChannelNotifications.js │ │ ├── ChannelPage.js │ │ ├── ChannelStream.js │ │ ├── ChannelView.js │ │ ├── CreateChannelPage.js │ │ ├── CreateChannelStream.js │ │ ├── DiscoverView.js │ │ ├── EditChannelPage.js │ │ ├── EditChannelStream.js │ │ ├── EditChannelView.js │ │ ├── ErrorPage.js │ │ ├── ExplorePage.js │ │ ├── ForbiddenPage.js │ │ ├── PostView.js │ │ ├── PreferencesPage.js │ │ ├── PreferencesStream.js │ │ ├── PreferencesView.js │ │ ├── SearchBar.js │ │ └── SearchView.js │ │ ├── overlay │ │ ├── DiscoverOverlay.js │ │ └── WelcomePage.js │ │ └── sidebar │ │ ├── ActionBar.js │ │ ├── Channels.js │ │ ├── PersonalChannel.js │ │ └── SidebarPage.js └── vendor │ ├── IndexedDBShim.min.js │ ├── backbone-indexeddb.js │ ├── backbone.js │ ├── dropzone.js │ ├── jasmine │ ├── MIT.LICENSE │ ├── jasmine-html.js │ ├── jasmine.css │ └── jasmine.js │ ├── jquery.cookie.js │ ├── jquery.embedly.js │ ├── jquery.js │ ├── l10n-browser.js │ ├── l10n.js │ ├── modernizr.js │ ├── pollymer.js │ ├── require.js │ ├── text.js │ ├── timeago.js │ └── underscore.js ├── locales ├── data.de.properties ├── data.en.properties ├── data.es.properties ├── data.fr.properties ├── data.fy.properties ├── data.id.properties ├── data.nl-NL.properties ├── data.properties ├── data.pt.properties ├── data.sv.properties └── data.tr-TR.properties ├── manifest.webapp ├── package.json ├── prototypes ├── Firefox OS mockups │ ├── Firefox OS screen mockup.graffle │ ├── Firefox OS screen mockup.png │ ├── home-screen.png │ └── posts-screen.png ├── anon.html ├── avatars │ ├── anon.png │ ├── channel1.jpg │ ├── channel2.jpg │ ├── channel3.jpg │ ├── channel4.jpg │ ├── channel5.jpg │ ├── channel6.jpg │ ├── channel7.jpg │ ├── channel8.jpg │ ├── topic.png │ ├── user1.jpg │ ├── user10.jpg │ ├── user11.jpg │ ├── user12.jpg │ ├── user13.jpg │ ├── user1_big.jpg │ ├── user2.jpg │ ├── user2_big.jpg │ ├── user3.jpg │ ├── user4.jpg │ ├── user5.jpg │ ├── user6.jpg │ ├── user7.jpg │ ├── user8.jpg │ └── user9.jpg ├── discover.html ├── editChannel.html ├── landingPage.html ├── loading.html ├── preferences.html ├── private.html ├── searchResults.html ├── singlePost.html └── streams.html ├── spec ├── Channel.spec.js ├── ChannelFollowers.spec.js ├── ChannelItems.spec.js ├── ChannelMetadata.spec.js ├── Discover.spec.js ├── Item.spec.js ├── PostNotifications.spec.js ├── Preferences.spec.js ├── Search.spec.js ├── SimilarChannels.spec.js ├── SubscribedChannels.spec.js ├── User.spec.js ├── UserCredentials.spec.js ├── config.js ├── runner.js ├── testacular-runner.js ├── util_api.spec.js ├── util_avatarFallback.spec.js └── util_linkify.spec.js ├── templates ├── content │ ├── anonBar.html │ ├── anonOverlay.html │ ├── channelDetails.html │ ├── channelList.html │ ├── channelListDetails.html │ ├── createChannel.html │ ├── discover.html │ ├── editChannel.html │ ├── embed.html │ ├── error.html │ ├── forbidden.html │ ├── header.html │ ├── mediaFallback.html │ ├── notification.html │ ├── post.html │ ├── preferences.html │ ├── private.html │ ├── searchBar.html │ ├── searchResults.html │ └── stream.html ├── overlay │ ├── discover.html │ ├── footer.html │ ├── loading.html │ └── welcome.html └── sidebar │ ├── actionBar.html │ ├── channel.html │ ├── channels.html │ └── personalChannel.html ├── test.html └── testacular.conf.js /.gitignore: -------------------------------------------------------------------------------- 1 | .c9revisions 2 | /new buddycloud.tmproj 3 | *.swp 4 | locales/wti-config.wti 5 | .DS_Store 6 | css/style.min.css 7 | js/app.min.js* 8 | 9 | # https://github.com/github/gitignore/blob/master/Node.gitignore 10 | lib-cov 11 | *.seed 12 | *.log 13 | *.csv 14 | *.dat 15 | *.out 16 | *.pid 17 | *.gz 18 | 19 | pids 20 | logs 21 | results 22 | 23 | npm-debug.log 24 | node_modules 25 | 26 | .project 27 | .settings 28 | config.js 29 | -------------------------------------------------------------------------------- /README.bubbling.md: -------------------------------------------------------------------------------- 1 | buddycloud bubbles: buddycloud sorts the most interesting information for you 2 | ----------------------------------------------------------------------------- 3 | 4 | Bubbling is unique to buddycloud and defines which events cause a user 5 | or channel to float upwards to the top of the channel list. Users subscribe 6 | to hundreds of channels. We want to **show the user the most important information with the least 7 | amount of scrolling**. 8 | 9 | Channels bubble up according to the following sorting: 10 | 11 | (Never sorted by post counts) 12 | 13 | - the owner's channel should be pinned to the top of the channel list 14 | (and scroll with all channels) 15 | - 1st: channels with unread @mentions - sorted from newest to oldest (where am I mentioned?) 16 | - 2nd: channels with unread private messages - sorted from newest to oldest (did someone try to contact me?) 17 | - 3th: channels with unread channel posts - most recent posts to oldest (show new new posts higher up the list) 18 | - 4th: sorted from most recent posts to oldest 19 | - 5th: tie breaker - compare alphabetically (for example a new user with pre-defined channels) 20 | 21 | ``` 22 | sort(channelA, channelB): 23 | if (channelA.hasMentions and !channelB.hasMentions): 24 | return -1 25 | if (!channelA.hasMentions and channelB.hasMentions): 26 | return 1 27 | if (channelA.hasMentions and channelB.hasMentions): 28 | return channelB.lastMention - channelA.lastMention 29 | 30 | if (channelA.hasPrivate and !channelB.hasPrivate): 31 | return -1 32 | if (!channelA.hasPrivate and channelB.hasPrivate): 33 | return 1 34 | if (channelA.hasPrivate and channelB.hasPrivate): 35 | return channelB.lastPrivate - channelA.lastPrivate 36 | 37 | if (channelA.hasPost and !channelB.hasPost): 38 | return -1 39 | if (!channelA.hasPost and channelB.hasPost): 40 | return 1 41 | if (channelA.hasPost and channelB.hasPost): 42 | return channelB.lastPost - channelA.lastPost 43 | 44 | if (channelA.lastPost != channelB.lastPost): 45 | return channelB.lastPost - channelA.lastPost 46 | 47 | return channelA.name - channelB.name 48 | 49 | ``` 50 | 51 | So for example our hypothetical user might have their list of channels 52 | in the following order: 53 | 54 | - name@domain.com (top becasue they are name@domain.com) 55 | - coffee@domain.com (mentioned in a post - they really know their 56 | coffee (or code)) 57 | - java@domain.com (also mentioned here) 58 | - barristas@domain.com (users replied to name@domain.com's posting) 59 | - mom@family.com (a private message from mom) 60 | - dad@family.com (a private messgae from dad that was sent before mom 61 | sent hers) 62 | - team@work.com (a very new channel post) 63 | - swimgroup@sports.com (an older channel post) 64 | - news-updats@bbc.co.uk (nothing unread, but most recently accessed) 65 | - boring-channel@boring-domain.com (nothing unread, but also not read 66 | for a long time) 67 | - even-more-boring@boring-domain.com (not read for even longer than 68 | boring-channel@boring-domain.com) 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | buddycloud webclient 2 | ==================== 3 | 4 | This is the buddycloud web software for [buddycloud](http://buddycloud.com/). 5 | This software uses the the buddycloud [HTTP API](https://github.com/buddycloud/buddycloud-http-api) 6 | 7 | The code is based on 8 | * [jQuery](http://jquery.com/) 9 | * [Underscore](http://underscorejs.org/) 10 | * [Backbone.js](http://backbonejs.org/) 11 | * [Jasmine](https://jasmine.github.io/) for unit testing 12 | 13 | Setup 14 | ===== 15 | 16 | Setting up your webclient dev environment 17 | ``` 18 | git clone https://github.com/buddycloud/webclient.git 19 | cd webclient 20 | cp /config.js.example /config.js 21 | # to work against the buddycloud demo server: 22 | edit /config.js and set the baseURL to https://demo.buddycoud.org/api 23 | edit /config.js and set the homeDomain to buddycoud.org 24 | # install node.js 25 | npm install 26 | npm install -g grunt-cli 27 | grunt build 28 | grunt default 29 | browse to http://localhost:3000 30 | ``` 31 | 32 | Building the compressed JavaScript and CSS files 33 | ================================================ 34 | 35 | ``` 36 | cd webclient 37 | npm install 38 | grunt build 39 | ``` 40 | 41 | License and Copyright 42 | ===================== 43 | This code is Apache 2 licensed and copyright buddycloud. 44 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | 4 | # Install needed modules 5 | npm install 6 | 7 | # Create config file 8 | cp config.js.example config.js 9 | 10 | # Build 11 | ./node_modules/.bin/grunt build 12 | 13 | # Copies target result to target folder 14 | mkdir -p target 15 | rsync -av . target/ --exclude=target --exclude=debian \ 16 | --exclude=.gitignore --exclude=node_modules 17 | rm -rf target/js/app target/css/main.css 18 | 19 | -------------------------------------------------------------------------------- /config.js.example: -------------------------------------------------------------------------------- 1 | define({ 2 | // Where to find your API server (defaults to the buddycloud dev server - set 3 | // to your own server. 4 | // Note: Browsers do not permit using self signed certificates for AJAX requests 5 | baseUrl: 'https://demo.buddycloud.org/api/', 6 | 7 | // Domain that you your users live on. 8 | // For example, if your users' IDs look like user@example.com, then you would 9 | // set the homeDomain to example.com. 10 | homeDomain: 'buddycloud.org', 11 | 12 | // To enable oEmbed support (link previews), sign up on http://embed.ly (they 13 | // have a free plan). 14 | embedlyKey: '', 15 | 16 | // If you see "some content is unencrypted" messages while browsing, change 17 | // embedlySecure to "true". 18 | embedlySecure: true, 19 | 20 | // If you are a buddycloud developer you can optionally specify a port number 21 | // to run the server on (defaults to 3000). Safely ignored by everyone else. 22 | // port: 3000, 23 | }) 24 | -------------------------------------------------------------------------------- /contrib/apache/buddycloud-apache-virtual-host: -------------------------------------------------------------------------------- 1 | 2 | # push any HTTP requests to HTTPS 3 | ServerName EXAMPLE.COM 4 | RewriteEngine On 5 | RewriteCond %{HTTPS} off 6 | RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} 7 | 8 | 9 | 10 | ServerName EXAMPLE.COM 11 | DocumentRoot /usr/share/buddycloud-webclient 12 | LogLevel alert 13 | ErrorLog /var/log/apache2/EXAMPLE.COM-error.log 14 | CustomLog /var/log/apache2/EXAMPLE.COM-access.log combined 15 | SSLEngine On 16 | SSLCertificateFile /etc/apache2/certs/EXAMPLE.COM.pem 17 | SSLCertificateKeyFile /etc/apache2/certs/EXAMPLE.COM.key 18 | SSLCertificateChainFile /etc/apache2/certs/sub.class1.server.ca.crt 19 | # from https://www.startssl.com/certs/sub.class1.server.ca.crt 20 | 21 | # redirect everything except static content 22 | RewriteEngine On 23 | RewriteCond %{REQUEST_URI} !^/js/ 24 | RewriteCond %{REQUEST_URI} !^/css/ 25 | RewriteCond %{REQUEST_URI} !^/img/ 26 | RewriteCond %{REQUEST_URI} !^/locales/ 27 | RewriteCond %{REQUEST_URI} !^/config\.js$ 28 | RewriteCond %{REQUEST_URI} !^/favicon\.ico$ 29 | RewriteCond %{REQUEST_URI} !^/api/ 30 | RewriteCond %{REQUEST_URI} !^(.*)\.html$ 31 | RewriteRule ^(.*)$ /index.html 32 | 33 | ProxyPass /api/ http://localhost:9123/ 34 | ProxyPassReverse /api/ http://localhost:9123/ 35 | 36 | # Enable sensible caching 37 | FileETag None 38 | ExpiresActive On 39 | ExpiresDefault "access plus 1 seconds" 40 | ExpiresByType text/html "access plus 1 days" 41 | ExpiresByType image/jpeg "access plus 1 days" 42 | ExpiresByType image/png "access plus 1 days" 43 | ExpiresByType text/css "access plus 1 days" 44 | ExpiresByType application/javascript "access plus 1 days" 45 | 46 | # For Firefox OS Manifest file serving 47 | AddType application/x-web-app-manifest+json .webapp 48 | 49 | -------------------------------------------------------------------------------- /css/FollowerList.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Denis Washington 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | .follower-list h2 { 18 | margin: 0px; 19 | font-size: 90%; 20 | text-transform: uppercase; 21 | color: #a9a79e; 22 | border-bottom: 1px solid #fbf8ea; 23 | } 24 | 25 | .follower-list h2:after { 26 | display: block; 27 | border-bottom: 1px solid #d6d3c7; 28 | content: ''; 29 | } 30 | 31 | .follower-list ul { 32 | margin-left: 0; 33 | padding-left: 0; 34 | } 35 | 36 | .follower-list a { 37 | text-decoration: none; 38 | } 39 | 40 | .follower-list li { 41 | clear: left; 42 | margin-bottom: 16px; 43 | overflow-x: hidden; 44 | list-style: none; 45 | } 46 | 47 | .follower-list li .avatar { 48 | float: left; 49 | width: 32px; 50 | height: 32px; 51 | border-radius: 6px; 52 | } 53 | 54 | .follower-list li .text { 55 | margin-left: 40px; 56 | line-height: 90%; 57 | } 58 | 59 | .follower-list li .name { 60 | font-weight: bold; 61 | } 62 | 63 | .follower-list li .id { 64 | font-size: 80%; 65 | color: #444; 66 | } 67 | 68 | .follower-list li:hover { 69 | background: #ffffff; 70 | border-radius: 4px; 71 | } 72 | 73 | -------------------------------------------------------------------------------- /css/LoginSidebar.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Denis Washington 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | .login-sidebar a { 18 | font-weight: bold; 19 | color: inherit; 20 | } 21 | 22 | .login-sidebar p { 23 | margin-top: 0; 24 | font-size: 110%; 25 | } 26 | 27 | .login-sidebar input { 28 | display: block; 29 | margin-top: 8px; 30 | } 31 | 32 | .login-sidebar input[type=text], .login-sidebar input[type=password] { 33 | width: 100%; 34 | padding: 4px; 35 | box-sizing: border-box; 36 | -moz-box-sizing: border-box; 37 | border-radius: 4px; 38 | border: 1px solid white; 39 | box-shadow: 0 0 4px 1px #d6d3c7; 40 | background-image: -webkit-linear-gradient(top, #f5f3ec, white); 41 | background-image: -moz-linear-gradient(top, #f5f3ec, white); 42 | font-size: 120%; 43 | } 44 | 45 | .login-sidebar input[type=submit] { 46 | margin-right: 0; 47 | margin-left: auto; 48 | padding: 4px 12px 4px 12px; 49 | border: 1px solid white; 50 | border-radius: 4px; 51 | box-shadow: 0 0 4px 1px #d6d3c7; 52 | background-color: #5d859d; 53 | color: white; 54 | font-weight: bold; 55 | } -------------------------------------------------------------------------------- /css/MetadataPane.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Denis Washington 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | .metadata-pane { 18 | padding: 8px; 19 | background-image: -webkit-linear-gradient(top, #fbf8ea, #efecdf); 20 | background-image: -moz-linear-gradient(top, #fbf8ea, #efecdf); 21 | background-image: -ms-linear-gradient(top, #fbf8ea, #efecdf); 22 | background-image: -o-linear-gradient(top, #fbf8ea, #efecdf); 23 | background-image: linear-gradient(to bottom, #fbf8ea, #efecdf); 24 | } 25 | 26 | .metadata-pane .avatar { 27 | display: block; 28 | float: left; 29 | width: 64px; 30 | height: 64px; 31 | margin-right: 8px; 32 | border-radius: 8px; 33 | } 34 | 35 | .metadata-pane h1 { 36 | margin: 0px; 37 | font-size: 200%; 38 | font-weight: normal; 39 | } 40 | 41 | .metadata-pane p { 42 | margin-top: 0; 43 | margin-left: 72px; 44 | font-size: 120%; 45 | } 46 | 47 | .metadata-pane .id { 48 | font-size: 50%; 49 | color: #a9a79e; 50 | } 51 | -------------------------------------------------------------------------------- /css/PostStream.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Denis Washington 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | .post-stream { 18 | background-color: white; 19 | } 20 | 21 | .post-stream .loading { 22 | text-align: center; 23 | padding: 8px; 24 | } 25 | 26 | .post-stream .new-topic { 27 | padding: 14px 13px 13px; 28 | position: relative; 29 | overflow: hidden; 30 | display: block; 31 | } 32 | 33 | .post-stream textarea { 34 | width: 100%; 35 | margin: 0; 36 | padding: 5px; 37 | box-sizing: border-box; 38 | -webkit-box-sizing: border-box; 39 | -moz-box-sizing: border-box; 40 | -ms-box-sizing: border-box; 41 | -o-box-sizing: border-box; 42 | resize: none; 43 | white-space: pre-wrap; 44 | word-wrap: break-word; 45 | border: 1px solid rgba(0, 0, 0, 0.13); 46 | font: 13px/19.5px sans-serif; 47 | outline: 0; 48 | z-index: 2; 49 | } 50 | 51 | .post-stream .new-topic .controls, .post-stream .new-comment .controls { 52 | text-align: right; 53 | } 54 | 55 | .post-stream .new-topic textarea { 56 | height: 84px; 57 | } 58 | 59 | .post-stream .new-comment { 60 | margin: 0 0 0 59px; 61 | } 62 | 63 | .post-stream .new-comment textarea { 64 | height: 48px; 65 | } 66 | 67 | .post-stream .thread { 68 | padding: 13px; 69 | border-top: 1px solid #d6d3c7; 70 | } 71 | 72 | .post-stream .comment { 73 | margin-top: 2em; 74 | margin-left: 16px; 75 | font-size: 90%; 76 | } 77 | 78 | .post-stream .avatar { 79 | float: left; 80 | margin-right: 8px; 81 | } 82 | 83 | .post-stream .thread .avatar { 84 | width: 48px; 85 | height: 48px; 86 | border-radius: 8px; 87 | } 88 | 89 | .post-stream .comment .avatar { 90 | width: 32px; 91 | height: 32px; 92 | border-radius: 6px; 93 | } 94 | 95 | .post-stream .title { 96 | font-size: 120%; 97 | line-height: 110%; 98 | margin: 0 0 0.5em 0; 99 | } 100 | 101 | .post-stream .author-id { 102 | font-size: 75%; 103 | font-weight: normal; 104 | color: #444; 105 | } 106 | 107 | .post-stream .content { 108 | font-family: inherit; 109 | white-space: pre-wrap; 110 | } 111 | 112 | .post-stream .thread .content { 113 | margin-top: 0; 114 | margin-left: 56px; 115 | clear: none; 116 | } 117 | 118 | .post-stream .comment .content { 119 | margin-left: 40px; 120 | } 121 | -------------------------------------------------------------------------------- /css/SubscribedChannelsList.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Denis Washington 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | .subscribedchannels-list h2 { 18 | margin: 0px; 19 | font-size: 90%; 20 | text-transform: uppercase; 21 | color: #a9a79e; 22 | border-bottom: 1px solid #fbf8ea; 23 | } 24 | 25 | .subscribedchannels-list h2:after { 26 | display: block; 27 | border-bottom: 1px solid #d6d3c7; 28 | content: ''; 29 | } 30 | 31 | .subscribedchannels-list ul { 32 | margin-left: 0; 33 | padding-left: 0; 34 | } 35 | 36 | .subscribedchannels-list a { 37 | text-decoration: none; 38 | } 39 | 40 | .subscribedchannels-list li { 41 | clear: left; 42 | margin-bottom: 16px; 43 | overflow-x: hidden; 44 | list-style: none; 45 | } 46 | 47 | .subscribedchannels-list li .avatar { 48 | float: left; 49 | width: 32px; 50 | height: 32px; 51 | border-radius: 6px; 52 | } 53 | 54 | .subscribedchannels-list li .text { 55 | margin-left: 40px; 56 | line-height: 90%; 57 | } 58 | 59 | .subscribedchannels-list li .name { 60 | font-weight: bold; 61 | } 62 | 63 | .subscribedchannels-list li .id { 64 | font-size: 80%; 65 | color: #444; 66 | } 67 | 68 | .subscribedchannels-list li:hover { 69 | background: #ffffff; 70 | border-radius: 4px; 71 | } 72 | 73 | -------------------------------------------------------------------------------- /css/UserMenu.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Denis Washington 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | .user-menu .avatar { 18 | float: left; 19 | width: 32px; 20 | height: 32px; 21 | margin-right: 8px; 22 | } 23 | 24 | .user-menu .username { 25 | line-height: 32px; 26 | vertical-align: center; 27 | font-weight: bold; 28 | margin-right: 12px; 29 | } 30 | 31 | .user-menu button { 32 | padding: 4px 12px 4px 12px; 33 | border: 1px solid white; 34 | border-radius: 4px; 35 | box-shadow: 0 0 4px 1px #2f5873; 36 | background-color: #efecdf; 37 | color: black; 38 | font-size: inherit; 39 | font-weight: bold; 40 | } -------------------------------------------------------------------------------- /css/anon.css: -------------------------------------------------------------------------------- 1 | .content.anonView { 2 | left: 0; 3 | } 4 | 5 | .anonView .channelView { 6 | width: auto; 7 | height: 100%; 8 | } 9 | 10 | .anonView .channelHeader { 11 | padding: 53px 21px 13px; 12 | } 13 | 14 | .anonView #poweredby { 15 | top: 66px; 16 | margin: 0; 17 | } 18 | 19 | .anonView .navigation { 20 | position: absolute; 21 | top: 0; 22 | z-index: 2; 23 | } 24 | 25 | /* specific to the prototypes until we implement the share options */ 26 | .anonView .share .popup { 27 | left: -53px; 28 | } 29 | 30 | .navigation { 31 | padding: 0 0 13px; 32 | position: relative; 33 | width: 580px; 34 | -moz-box-sizing: border-box; 35 | box-sizing: border-box; 36 | } 37 | .navigation .buddycloud123 { 38 | margin: 3px 0 0 5px; 39 | width: 71px; 40 | height: 18px; 41 | font: 0/0 a; 42 | color: transparent; 43 | vertical-align: top; 44 | background: url("/img/buddycloud.png") no-repeat center; 45 | display: inline-block; 46 | } 47 | .userbar .joinBuddycloud { 48 | width: 580px; 49 | margin: -237px 0 26px; 50 | border-radius: 5px; 51 | opacity: 0; 52 | -webkit-transition: margin 500ms, opacity 500ms; 53 | -moz-transition: margin 500ms, opacity 500ms; 54 | -ms-transition: margin 500ms, opacity 500ms; 55 | -o-transition: margin 500ms, opacity 500ms; 56 | transition: margin 500ms, opacity 500ms; 57 | } 58 | .userbar.action-open .joinBuddycloud { 59 | margin-top: 13px; 60 | margin-bottom: 0; 61 | opacity: 1; 62 | } -------------------------------------------------------------------------------- /css/bubbling.css: -------------------------------------------------------------------------------- 1 | .bubbleHolder { 2 | height: 0; 3 | overflow: visible; 4 | position: relative; 5 | z-index: 100; 6 | -webkit-transition: height 1s; 7 | -moz-transition: height 1s; 8 | -ms-transition: height 1s; 9 | -o-transition: height 1s; 10 | transition: height 1s; 11 | } 12 | .bubbleHolder.rainbowBubble { 13 | -webkit-transition: height 500ms linear; 14 | -moz-transition: height 500ms linear; 15 | -ms-transition: height 500ms linear; 16 | -o-transition: height 500ms linear; 17 | transition: height 500ms linear; 18 | } 19 | .bubbleHolder .channel { 20 | position: absolute; 21 | -webkit-transition: top 1s; 22 | -moz-transition: top 1s; 23 | -ms-transition: top 1s; 24 | -o-transition: top 1s; 25 | transition: top 1s; 26 | } 27 | .rainbowBubble .channel { 28 | -webkit-transition: top 500ms cubic-bezier(0.340, 0.975, 0.585, 1.355), left 500ms linear; 29 | -moz-transition: top 500ms cubic-bezier(0.340, 0.975, 0.585, 1.355), left 500ms linear; 30 | -ms-transition: top 500ms cubic-bezier(0.340, 0.975, 0.585, 1.355), left 500ms linear; 31 | -o-transition: top 500ms cubic-bezier(0.340, 0.975, 0.585, 1.355), left 500ms linear; 32 | transition: top 500ms cubic-bezier(0.340, 0.975, 0.585, 1.355), left 500ms linear; 33 | } 34 | .startingArea { 35 | -webkit-transition: height 1s; 36 | -moz-transition: height 1s; 37 | -ms-transition: height 1s; 38 | -o-transition: height 1s; 39 | transition: height 1s; 40 | } -------------------------------------------------------------------------------- /css/common.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Denis Washington 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | body { 18 | margin: 0px; 19 | font-family: Helvetica, sans-serif; 20 | font-size: 13px; 21 | background-color: #efecdf; 22 | color: #132b36; 23 | } 24 | 25 | a { 26 | color: #e28c35; 27 | text-decoration: none; 28 | } 29 | 30 | a:hover { 31 | text-decoration: underline; 32 | } 33 | 34 | .avatar { 35 | border: 1px solid #e1dbc3; 36 | } 37 | 38 | .bordered { 39 | border: 1px solid #fbf8ea; 40 | box-shadow: 0 0 4px 0 #9c9a91; 41 | border-radius: 6px; 42 | padding: 8px; 43 | } 44 | 45 | #toolbar { 46 | -moz-box-sizing: border-box; 47 | box-sizing: border-box; 48 | width: 100%; 49 | padding: 4px; 50 | box-shadow: 0 0 8px 0 black; 51 | background-color: #6993aa; 52 | background-image: -webkit-linear-gradient(top, #6993aa, #2f5873); 53 | background-image: -moz-linear-gradient(top, #6993aa, #2f5873); 54 | background-image: -ms-linear-gradient(top, #6993aa, #2f5873); 55 | background-image: -o-linear-gradient(top, #6993aa, #2f5873); 56 | background-image: linear-gradient(to bottom, #6993aa, #2f5873); 57 | color: white; 58 | font-size: 80%; 59 | } 60 | 61 | #toolbar a { 62 | font-weight: bold; 63 | color: white; 64 | text-decoration: none; 65 | } 66 | 67 | #toolbar a:hover { 68 | text-decoration: underline; 69 | } 70 | 71 | #toolbar-right { 72 | float: right; 73 | } 74 | 75 | #toolbar:after { 76 | display: block; 77 | clear: right; 78 | content: ''; 79 | } 80 | 81 | #main { 82 | position: relative; 83 | margin: 16px; 84 | } 85 | 86 | #content { 87 | position: absolute; 88 | margin: 0 16px 0 16px; 89 | padding: 0; 90 | left: 20em; 91 | right: 20em; 92 | } 93 | 94 | #left, #right { 95 | position: absolute; 96 | width: 20em; 97 | box-sizing: border-box; 98 | } 99 | 100 | #left { 101 | left: 0; 102 | } 103 | 104 | #right { 105 | right: 0; 106 | } 107 | 108 | -------------------------------------------------------------------------------- /css/fonts/Nunito-Regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/css/fonts/Nunito-Regular-webfont.eot -------------------------------------------------------------------------------- /css/fonts/Nunito-Regular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/css/fonts/Nunito-Regular-webfont.ttf -------------------------------------------------------------------------------- /css/fonts/Nunito-Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/css/fonts/Nunito-Regular-webfont.woff -------------------------------------------------------------------------------- /css/grid.css: -------------------------------------------------------------------------------- 1 | /* one element is 377px wide */ 2 | /* gutter is 34px */ 3 | 4 | .span-1,.span-2,.span-3,.span-4 { 5 | float: left; 6 | margin-right: 34px; 7 | } 8 | .span-1:last-child,.span-2:last-child,.span-3:last-child,.span-4:last-child { 9 | margin-right: 0; 10 | } 11 | .span-1 { 12 | width: 377px; 13 | } 14 | .span-2 { 15 | width: 788px; 16 | } 17 | .span-3 { 18 | width: 1199px; 19 | } 20 | .span-4 { 21 | width: 1610px; 22 | } -------------------------------------------------------------------------------- /css/helpers.css: -------------------------------------------------------------------------------- 1 | .clearfix:after { 2 | content: ""; 3 | clear: both; 4 | display: block; 5 | height: 0; 6 | } 7 | .floatLeft { 8 | float: left; 9 | } 10 | .floatRight { 11 | float: right; 12 | } 13 | 14 | 15 | .trim { 16 | white-space: nowrap; 17 | overflow: hidden; 18 | text-overflow: ellipsis; 19 | display: block; 20 | } 21 | 22 | 23 | /* social */ 24 | .social { 25 | width: 16px; 26 | height: 16px; 27 | display: inline-block; 28 | font: 0/0 a; 29 | color: transparent; 30 | vertical-align: top; 31 | background-position: center; 32 | background-repeat: no-repeat; 33 | } 34 | .facebook { background-image: url(/img/facebook_16.png); } 35 | .twitter { background-image: url(/img/twitter_16.png); } 36 | .gplus { background-image: url(/img/google_16.png); } 37 | .reddit { background-image: url(/img/reddit_16.png); } 38 | .tumblr { background-image: url(/img/tumblr_16.png); } 39 | 40 | /* flex box helpers */ 41 | .centered, .justify, .horizontal { 42 | display: -webkit-box; 43 | display: -moz-box; 44 | display: -ms-box; 45 | display: -o-box; 46 | display: box; 47 | } 48 | .stretchWidth { 49 | width: 100%; 50 | } 51 | .stretchHeight { 52 | height: 100%; 53 | } 54 | .centered { 55 | -webkit-box-align: center; 56 | -moz-box-align: center; 57 | -ms-box-align: center; 58 | -o-box-align: center; 59 | box-align: center; 60 | -webkit-box-pack: center; 61 | -moz-box-pack: center; 62 | -ms-box-pack: center; 63 | -o-box-pack: center; 64 | box-pack: center; 65 | } 66 | .start { 67 | -webkit-box-align: start; 68 | -moz-box-align: start; 69 | -ms-box-align: start; 70 | -o-box-align: start; 71 | box-align: start; 72 | } 73 | .justify, .horizontal { 74 | width: 100%; 75 | -webkit-box-orient: horizontal; 76 | -moz-box-orient: horizontal; 77 | -ms-box-orient: horizontal; 78 | -o-box-orient: horizontal; 79 | box-orient: horizontal; 80 | } 81 | .vertical { 82 | -webkit-box-orient: vertical; 83 | -moz-box-orient: vertical; 84 | -ms-box-orient: vertical; 85 | -o-box-orient: vertical; 86 | box-orient: vertical; 87 | } 88 | .justify { 89 | -webkit-box-pack: justify; 90 | -moz-box-pack: justify; 91 | -ms-box-pack: justify; 92 | -o-box-pack: justify; 93 | box-pack: justify; 94 | -webkit-box-align: center; 95 | -moz-box-align: center; 96 | -ms-box-align: center; 97 | -o-box-align: center; 98 | box-align: center; 99 | -webkit-box-lines: multiple; 100 | -moz-box-lines: multiple; 101 | -ms-box-lines: multiple; 102 | -o-box-lines: multiple; 103 | box-lines: multiple; 104 | } 105 | .flex, 106 | .fit > * { 107 | -webkit-box-flex: 1; 108 | -moz-box-flex: 1; 109 | -ms-box-flex: 1; 110 | -o-box-flex: 1; 111 | box-flex: 1; 112 | } 113 | .noSelect { 114 | -webkit-user-select: none; 115 | -moz-user-select: none; 116 | -ms-user-select: none; 117 | -o-user-select: none; 118 | user-select: none; 119 | } -------------------------------------------------------------------------------- /css/homepage.css: -------------------------------------------------------------------------------- 1 | .content.homepage { 2 | left: 0; 3 | background: white; 4 | } 5 | .content.homepage:before { 6 | content: ""; 7 | height: 530px; 8 | width: 100%; 9 | background: white url(/img/Clouds_by_Felix_Niklas.jpg) no-repeat bottom; 10 | background-size: cover; 11 | position: absolute; 12 | } 13 | 14 | .homepage .formHolder { 15 | position: relative; 16 | } 17 | 18 | .homepage form { 19 | right: 1px; 20 | top: 28px; 21 | padding: 13px 3px 13px 13px; 22 | background: white; 23 | position: absolute; 24 | border-radius: 3px 0 3px 3px; 25 | box-shadow: 26 | 0 1px 2px rgba(0,0,0,.55) inset, 27 | 0 0 0 1px rgba(0,0,0,.4), 28 | 0 2px rgba(255,255,255,.13); 29 | display: none; 30 | } 31 | .homepage form.login { 32 | padding: 13px; 33 | right: 52px; 34 | } 35 | .homepage form.register { width: 480px; } 36 | .homepage .showLogin form.login { display: block; } 37 | .homepage .showRegister form.register { display: block; } 38 | 39 | .homepage .middle { 40 | width: 788px; 41 | margin: 0 auto; 42 | position: relative; 43 | } 44 | 45 | .discoverChannels h1 { 46 | color: rgba(0,0,0,.13); 47 | text-shadow: none; 48 | } 49 | .discoverChannels h2 { 50 | color: rgba(0,0,0,.55); 51 | text-shadow: none; 52 | } 53 | 54 | .hero { 55 | margin: 34px 0 55px; 56 | height: 377px; 57 | border-radius: 5px; 58 | color: white; 59 | text-shadow: 0 1px 3px black; 60 | } 61 | .discoverChannels .hero h1 { 62 | color: inherit; 63 | text-shadow: inherit; 64 | font-size: 72px; 65 | line-height: 1.3; 66 | } 67 | .hero p { 68 | color: inherit; 69 | font-size: 18px; 70 | text-shadow: inherit; 71 | } 72 | 73 | footer { 74 | padding: 21px; 75 | background: hsl(0,0%,95%); 76 | border-top: 1px solid hsl(0,0%,90%); 77 | } 78 | footer p { 79 | color: #333; 80 | text-align: center; 81 | } -------------------------------------------------------------------------------- /css/lightSidebar.css: -------------------------------------------------------------------------------- 1 | .sidebar { 2 | background: hsl(202,0%,100%); 3 | } 4 | .channel .info .owner, 5 | .channel .info .status, 6 | .sidebar button { 7 | color: hsl(0,0%,21%); 8 | } 9 | .channel .info .status { 10 | opacity: 0.65; 11 | } 12 | .personal.channel, 13 | .personal.channel.selected, 14 | .sidebar button { 15 | background: hsl(202,0%,95%); 16 | } 17 | .personal.channel, 18 | .sidebar button { 19 | border: none; 20 | box-shadow: none; 21 | } 22 | .sidebar nav { 23 | box-shadow: 24 | 0 1px hsl(202,0%,80%); 25 | } 26 | .channel.selected { 27 | background: none; 28 | box-shadow: none; 29 | } 30 | .channel .avatar { 31 | box-shadow: none; 32 | } 33 | .content { 34 | box-shadow: 35 | -1px 0 rgba(0,0,0,.21), 36 | -2px 0 rgba(0,0,0,.05); 37 | } -------------------------------------------------------------------------------- /css/login.css: -------------------------------------------------------------------------------- 1 | .left[type=submit] { 2 | float: none; 3 | margin-top: 8px; 4 | } 5 | 6 | form.login > div, 7 | form.register > div { 8 | margin-bottom: 5px; 9 | } 10 | 11 | .join.button { 12 | margin: 0 12px 13px; 13 | right: 13px; 14 | position: absolute; 15 | } 16 | .action-open .join.button { 17 | display: none; 18 | } 19 | 20 | .subview-login .email { display: none; } -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | @import "normalize.css"; 2 | @import "reset.css"; 3 | @import "helpers.css"; 4 | @import "grid.css"; 5 | @import "button.css"; 6 | @import "form.css"; 7 | @import "sidebar.css"; 8 | @import "channel.css"; 9 | @import "channelDetails.css"; 10 | @import "dropzone.css"; 11 | @import "overlay.css"; 12 | @import "edit.css"; 13 | @import "startpage.css"; 14 | @import "discover.css"; 15 | @import "bubbling.css"; 16 | @import "singlePost.css"; 17 | @import "anon.css"; 18 | @import "popup.css"; 19 | @import "homepage.css"; 20 | @import "login.css"; 21 | @import "spinner.css"; 22 | /*@import "lightSidebar.css";*/ 23 | 24 | pre,textarea { 25 | margin: 0; 26 | padding: 0; 27 | outline: 0; 28 | border: 0; 29 | } 30 | 31 | @font-face { 32 | font-family: 'Nunito'; 33 | src: url("fonts/Nunito-Regular-webfont.eot"); 34 | src: url("fonts/Nunito-Regular-webfont.eot?#iefix") format('embedded-opentype'),url("fonts/Nunito-Regular-webfont.woff") format('woff'),url("fonts/Nunito-Regular-webfont.ttf") format('truetype'),url("fonts/Nunito-Regular-webfont.svg#NunitoRegular") format('svg'); 35 | font-weight: normal; 36 | font-style: normal; 37 | } 38 | 39 | html { 40 | height: 100%; 41 | overflow-y: hidden; 42 | overflow-x: auto; 43 | font: 13px/17px "Helvetica Neue",Helvetica,Arial,sans-serif; 44 | } 45 | body { 46 | width: 100%; 47 | height: 100%; 48 | overflow-y: hidden; 49 | overflow-x: auto; 50 | background: hsl(0,0%,95%); 51 | -webkit-text-size-adjust: none; 52 | } 53 | h1 { 54 | font-size: 34px; 55 | font-family: "Nunito",sans-serif; 56 | } 57 | h2 { 58 | font-size: 26px; 59 | font-family: "Nunito",sans-serif; 60 | } 61 | h3 { 62 | font-size: 18px; 63 | font-family: "Nunito",sans-serif; 64 | } 65 | h4 { 66 | width: 100%; 67 | font-size: 14px; 68 | font-weight: bold; 69 | color: rgba(0,0,0,0.89); 70 | white-space: nowrap; 71 | overflow: hidden; 72 | text-overflow: ellipsis; 73 | } 74 | h5 { 75 | margin-bottom: 5px; 76 | font-size: 12px; 77 | font-weight: bold; 78 | } 79 | ul { 80 | list-style-type: none; 81 | } 82 | p.error { 83 | color: #AC1D21; 84 | display: none 85 | } 86 | a { 87 | color: hsl(81, 53%, 44%); 88 | text-decoration: none; 89 | } 90 | a:hover, a:visited:hover { 91 | text-decoration: underline; 92 | } 93 | a.internal, a:visited.internal { 94 | padding-left: 12px; 95 | background: url("/img/beagle.png") no-repeat left 4px; 96 | } 97 | .hint { color: #bbb; } 98 | table td:not(:last-child) { 99 | padding-right: 13px; 100 | } 101 | 102 | .loader { 103 | /* bug in firefox where it doesnt start from the top */ 104 | vertical-align: top; 105 | } 106 | .throbber { 107 | font-size: 2em; 108 | color: rgba(0,0,0,.55); 109 | position: relative; 110 | opacity: 0; 111 | -webkit-animation: throb 1s linear infinite alternate; 112 | } 113 | 114 | @-webkit-keyframes throb { 115 | to { opacity: 1; } 116 | } 117 | -------------------------------------------------------------------------------- /css/mobile/test.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } 49 | 50 | /*mobile related stuff*/ 51 | 52 | * { 53 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif !important; 54 | } 55 | 56 | .register { 57 | display: none; 58 | } 59 | 60 | .login { 61 | display: none; 62 | } 63 | 64 | .hero { 65 | text-align: center; 66 | } 67 | 68 | .hero h1 { 69 | font-size: 30px; 70 | } 71 | 72 | .local h1 { 73 | display: none; 74 | } 75 | 76 | .mostActive { 77 | margin-top: 10px; 78 | border: solid black 1px; 79 | } 80 | 81 | .popular { 82 | border: solid black 1px; 83 | } 84 | 85 | h2 { 86 | font-size: 20px; 87 | text-align: center; 88 | } 89 | 90 | .channel { 91 | background-color: #C4CED1; 92 | border: solid black 1px; 93 | padding: 10px; 94 | } 95 | 96 | /*channel view*/ 97 | 98 | .home { 99 | display: none; 100 | } 101 | 102 | .close { 103 | display: none; 104 | } 105 | 106 | .join { 107 | display: none; 108 | } 109 | 110 | .channelHeader a { 111 | display: none; 112 | } 113 | 114 | .left h2 { 115 | display: none; 116 | } 117 | 118 | 119 | 120 | 121 | .posts .post { 122 | background-color: #C4CED1; 123 | border: solid black 1px; 124 | } 125 | 126 | .opener { 127 | background-color: #C4CED1; 128 | } 129 | 130 | .opener p { 131 | margin: 10px; 132 | } 133 | 134 | .comment { 135 | padding-left: 15px; 136 | } 137 | 138 | .comment .avatar { 139 | float: left; 140 | } 141 | 142 | .comment { 143 | background-color: #EFECDF; 144 | border: solid black 1px; 145 | } 146 | 147 | .comment p { 148 | margin: 10px; 149 | margin-top: 25px; 150 | } 151 | 152 | .usermeta { 153 | float: right; 154 | margin-right: 10px; 155 | } 156 | 157 | .name { 158 | font-weight: bold; 159 | } -------------------------------------------------------------------------------- /css/popup.css: -------------------------------------------------------------------------------- 1 | .popup { 2 | position: absolute; 3 | z-index: 1; 4 | } 5 | .popup ul { 6 | border: 1px solid hsl(0,0%,83%); 7 | border-radius: 3px; 8 | box-shadow: 9 | 0 1px rgba(0,0,0,.03), 10 | 0 2px rgba(0,0,0,.02), 11 | 0 3px rgba(0,0,0,.01); 12 | } 13 | .popup li { 14 | padding: 8px 21px 5px 21px; 15 | text-align: left; 16 | white-space: nowrap; 17 | color: #666; 18 | border-bottom: 1px solid hsl(0,0%,93%); 19 | background: white; 20 | cursor: pointer; 21 | } 22 | .popup li a { 23 | color: inherit; 24 | text-decoration: inherit; 25 | } 26 | .popup li:first-child { 27 | border-radius: 1px 1px 0 0; 28 | } 29 | .popup li:last-child { 30 | padding-bottom: 8px; 31 | border-bottom: none; 32 | border-radius: 0 0 1px 1px; 33 | } 34 | .popup li:only-child { 35 | border-radius: 1px; 36 | } 37 | .popup .arrow { 38 | position: relative; 39 | width: 13px; 40 | height: 10px; 41 | margin: 0 auto -1px; 42 | overflow: hidden; 43 | } 44 | .popup .arrow:before { 45 | content: ""; 46 | position: absolute; 47 | width: 10px; 48 | height: 10px; 49 | left: 1px; 50 | top: 6px; 51 | border: 1px solid hsl(0,0%,83%); 52 | background: white; 53 | -webkit-transform: rotate(45deg); 54 | -moz-transform: rotate(45deg); 55 | -ms-transform: rotate(45deg); 56 | -o-transform: rotate(45deg); 57 | transform: rotate(45deg); 58 | } -------------------------------------------------------------------------------- /css/reset.css: -------------------------------------------------------------------------------- 1 | body,div,span,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,code,del,dfn,em,img,q,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,dialog,figure,footer,header,hgroup,nav,section { 2 | margin: 0; 3 | padding: 0; 4 | border: 0; 5 | font-weight: inherit; 6 | font-style: inherit; 7 | font-size: 100%; 8 | font-family: inherit; 9 | vertical-align: baseline; 10 | } 11 | article,aside,dialog,figure,footer,header,hgroup,nav,section { 12 | display: block; 13 | } 14 | body { 15 | line-height: 1.5; 16 | } 17 | table { 18 | border-collapse: separate; 19 | border-spacing: 0; 20 | } 21 | caption,th,td { 22 | text-align: left; 23 | font-weight: normal; 24 | float: none; 25 | } 26 | table,th,td { 27 | vertical-align: middle; 28 | } 29 | blockquote:before,blockquote:after,q:before,q:after { 30 | content: '' 31 | } 32 | blockquote,q { 33 | quotes: "" ""; 34 | } 35 | a img { 36 | border: none; 37 | } 38 | :focus { 39 | outline: 0; 40 | } -------------------------------------------------------------------------------- /css/singlePost.css: -------------------------------------------------------------------------------- 1 | .content.singlePost { 2 | min-width: 0; 3 | left: 0; 4 | box-shadow: none; 5 | } 6 | 7 | .singlePost .stream p { 8 | display: inline; 9 | } 10 | 11 | .singlePost .channelView { 12 | margin-right: 0; 13 | padding: 0; 14 | float: none; 15 | position: relative; 16 | /* 'hook in' when closing is finished */ 17 | -webkit-transition: top 0 500ms; 18 | -moz-transition: top 0 500ms; 19 | -ms-transition: top 0 500ms; 20 | -o-transition: top 0 500ms; 21 | transition: top 0 500ms; 22 | } 23 | .singlePost.action-pressed .channelView, 24 | .singlePost.action-open .channelView { 25 | -webkit-transition: top 0; 26 | -moz-transition: top 0; 27 | -ms-transition: top 0; 28 | -o-transition: top 0; 29 | transition: top 0; 30 | } 31 | 32 | .singlePost .topics:only-of-type article { 33 | box-shadow: 34 | 0 1px rgba(0,0,0,.01), 35 | 0 2px rgba(0,0,0,.02), 36 | 0 3px rgba(0,0,0,.03); 37 | } 38 | .singlePost.action-open .topics:only-of-type article { 39 | box-shadow: 40 | 0 -2px rgba(0,0,0,.03) inset, 41 | 0 1px rgba(0,0,0,.01), 42 | 0 2px rgba(0,0,0,.02), 43 | 0 3px rgba(0,0,0,.03), 44 | 0 4px rgba(0,0,0,.03), 45 | 0 5px rgba(0,0,0,.02), 46 | 0 6px rgba(0,0,0,.01); 47 | } 48 | 49 | .joinBuddycloud { 50 | height: 211px; 51 | width: 554px; 52 | margin: -213px 12px 0; 53 | padding: 21px; 54 | background: hsl(0,0%,90%); 55 | border: 1px solid rgba(0,0,0,.13); 56 | border-radius: 0 0 5px 5px; 57 | box-shadow: 0 0 5px rgba(0,0,0,.08) inset; 58 | -moz-box-sizing: border-box; 59 | box-sizing: border-box; 60 | -webkit-transition: margin 500ms; 61 | -moz-transition: margin 500ms; 62 | -ms-transition: margin 500ms; 63 | -o-transition: margin 500ms; 64 | transition: margin 500ms; 65 | } 66 | .action-open .joinBuddycloud { 67 | margin-top: -1px; 68 | } 69 | .joinBuddycloud .horizontal > div { 70 | margin-right: 8px; 71 | } 72 | .joinBuddycloud h3 { 73 | margin: 13px 0 8px; 74 | color: #333; 75 | } -------------------------------------------------------------------------------- /css/spinner.css: -------------------------------------------------------------------------------- 1 | .spinner { 2 | position: relative; 3 | width: 21px; 4 | height: 21px; 5 | margin-right: 6px; 6 | background: url(/img/spinner.png); 7 | display: inline-block; 8 | vertical-align: middle; 9 | -webkit-animation: spin 2.1s linear infinite; 10 | -moz-animation: spin 2.1s linear infinite; 11 | -ms-animation: spin 2.1s linear infinite; 12 | -o-animation: spin 2.1s linear infinite; 13 | animation: spin 2.1s linear infinite; 14 | } 15 | .spinner.yellow { 16 | width: 75px; 17 | height: 75px; 18 | margin: 0; 19 | background: url(/img/spinner_yellow.png) no-repeat center; 20 | } 21 | 22 | @-webkit-keyframes spin { 23 | to { -webkit-transform: rotate(360deg) translateZ(0); } 24 | } 25 | @-moz-keyframes spin { 26 | to { -moz-transform: rotate(360deg); } 27 | } 28 | @-ms-keyframes spin { 29 | to { -ms-transform: rotate(360deg); } 30 | } 31 | @-o-keyframes spin { 32 | to { -o-transform: rotate(360deg); } 33 | } 34 | @keyframes spin { 35 | to { transform: rotate(360deg); } 36 | } -------------------------------------------------------------------------------- /css/startpage.css: -------------------------------------------------------------------------------- 1 | body.startpage { 2 | background: #eee url("/img/images/Coffee_by_Felix_Niklas.jpg") no-repeat; 3 | background: #eee url("/img/images/logo.png") no-repeat center center; 4 | -webkit-background-size: cover; 5 | -moz-background-size: cover; 6 | -ms-background-size: cover; 7 | -o-background-size: cover; 8 | background-size: cover; 9 | overflow: auto; 10 | } 11 | .startpage header { 12 | border-bottom: 1px solid rgba(0,0,0,0.21); 13 | box-shadow: 0 1px rgba(255,255,255,0.34); 14 | } 15 | .startpage header .holder { 16 | height: 50px; 17 | } 18 | .startpage header h1 { 19 | padding-right: 21px; 20 | float: left; 21 | } 22 | .startpage header h1 a { 23 | color: #000; 24 | } 25 | .startpage header nav { 26 | margin-top: 11px; 27 | } 28 | .startpage header nav input[type="submit"] { 29 | margin-top: 23px; 30 | } 31 | .startpage header .search { 32 | float: left; 33 | margin: 13px 21px; 34 | border: 1px solid rgba(0,0,0,0.21); 35 | box-shadow: 0 1px rgba(255,255,255,0.89); 36 | } 37 | .startpage header form { 38 | margin-top: -23px; 39 | } 40 | .startpage header form div { 41 | margin-left: 13px; 42 | float: left; 43 | } 44 | .startpage h5 { 45 | font-size: 34px; 46 | text-align: center; 47 | margin: 55px 21px; 48 | } 49 | .startpage .content { 50 | z-index: -1; 51 | } 52 | .startpage .userbar { 53 | min-width: 100%; 54 | max-width: 100% 55 | } 56 | .startpage .holder { 57 | padding: 13px 21px; 58 | } 59 | .startpage section { 60 | width: 431px; 61 | margin: 13px 0; 62 | padding: 13px 21px; 63 | background: rgba(255,255,255,0.55); 64 | border-radius: 3px; 65 | border: 1px solid rgba(0,0,0,0.21); 66 | box-shadow: 0 1px rgba(255,255,255,0.21) inset,0 0 0 1px rgba(255,255,255,0.13) inset,0 2px 0 -1px rgba(0,0,0,0.08),0 4px 5px -1px rgba(0,0,0,0.13); 67 | } 68 | .channelType { 69 | margin: 5px 0; 70 | padding: 0 5px; 71 | border-radius: 8px; 72 | background-color: #f00; 73 | } 74 | .channelType.personal { 75 | background-color: #008000; 76 | } 77 | .stats .channelType { 78 | float: right; 79 | } 80 | .stats .channel .owner,.stats .channel .info,.stats .channel .status { 81 | color: #000; 82 | } 83 | .about p,.about ul { 84 | padding-top: 10px; 85 | } 86 | .about ul { 87 | padding-left: 16px; 88 | list-style-type: circle; 89 | } 90 | .about p { 91 | white-space: pre-wrap; 92 | break-word: break-word; 93 | } -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/favicon.ico -------------------------------------------------------------------------------- /img/Clouds_by_Felix_Niklas.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/Clouds_by_Felix_Niklas.jpg -------------------------------------------------------------------------------- /img/bc-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/bc-icon.png -------------------------------------------------------------------------------- /img/beagle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/beagle.png -------------------------------------------------------------------------------- /img/buddycloud-logo-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/buddycloud-logo-white.png -------------------------------------------------------------------------------- /img/buddycloud-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/buddycloud-logo.png -------------------------------------------------------------------------------- /img/buddycloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/buddycloud.png -------------------------------------------------------------------------------- /img/facebook_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/facebook_16.png -------------------------------------------------------------------------------- /img/firefox-manifest-icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/firefox-manifest-icon-128.png -------------------------------------------------------------------------------- /img/globe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/globe.png -------------------------------------------------------------------------------- /img/glyphicons_062_paperclip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/glyphicons_062_paperclip.png -------------------------------------------------------------------------------- /img/google_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/google_16.png -------------------------------------------------------------------------------- /img/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/info.png -------------------------------------------------------------------------------- /img/lock-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/lock-small.png -------------------------------------------------------------------------------- /img/magnifier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/magnifier.png -------------------------------------------------------------------------------- /img/media-download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/media-download.png -------------------------------------------------------------------------------- /img/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/minus.png -------------------------------------------------------------------------------- /img/new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/new.png -------------------------------------------------------------------------------- /img/padlock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/padlock.png -------------------------------------------------------------------------------- /img/personal-50px.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/personal-50px.jpg -------------------------------------------------------------------------------- /img/personal-75px.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/personal-75px.jpg -------------------------------------------------------------------------------- /img/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/plus.png -------------------------------------------------------------------------------- /img/poweredby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/poweredby.png -------------------------------------------------------------------------------- /img/reddit_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/reddit_16.png -------------------------------------------------------------------------------- /img/settings_dropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/settings_dropdown.png -------------------------------------------------------------------------------- /img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/spinner.gif -------------------------------------------------------------------------------- /img/spinner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/spinner.png -------------------------------------------------------------------------------- /img/spinner_yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/spinner_yellow.png -------------------------------------------------------------------------------- /img/spritemap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/spritemap.png -------------------------------------------------------------------------------- /img/spritemap@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/spritemap@2x.png -------------------------------------------------------------------------------- /img/test_image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/test_image.jpg -------------------------------------------------------------------------------- /img/topic-50px.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/topic-50px.jpg -------------------------------------------------------------------------------- /img/topic-75px.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/topic-75px.jpg -------------------------------------------------------------------------------- /img/tumblr_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/tumblr_16.png -------------------------------------------------------------------------------- /img/twitter_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/twitter_16.png -------------------------------------------------------------------------------- /img/unlock-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/img/unlock-small.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | buddycloud 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 24 |
25 |
26 | 27 | -------------------------------------------------------------------------------- /js/app/models/Channel.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var ChannelFollowers = require('models/ChannelFollowers'); 19 | var ModelBase = require('models/ModelBase'); 20 | var SimilarChannels = require('models/SimilarChannels'); 21 | 22 | var Channel = ModelBase.extend({ 23 | constructor: function(name) { 24 | ModelBase.call(this); 25 | this.name = name; 26 | this.similarChannels = new SimilarChannels(name); 27 | this.followers = new ChannelFollowers(name); 28 | }, 29 | 30 | fetch: function(options) { 31 | options = _.extend(options || {}, { 32 | complete: this._triggerFetchCallback() 33 | }); 34 | this.followers.fetch(options); 35 | this.similarChannels.fetch(options); 36 | }, 37 | 38 | _triggerFetchCallback: function() { 39 | var self = this; 40 | var fetched = []; 41 | return function(model) { 42 | fetched.push(model); 43 | if (_.include(fetched, self.followers) && 44 | _.include(fetched, self.similarChannels)) { 45 | self.trigger('fetch'); 46 | } 47 | }; 48 | } 49 | }); 50 | 51 | return Channel; 52 | }); 53 | -------------------------------------------------------------------------------- /js/app/models/ChannelFollowersRequests.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var api = require('util/api'); 19 | var ModelBase = require('models/ModelBase'); 20 | 21 | var ChannelFollowersRequests = ModelBase.extend({ 22 | constructor: function(channel) { 23 | ModelBase.call(this); 24 | this.channel = channel; 25 | }, 26 | 27 | url: function() { 28 | return api.url(this.channel, 'subscribers', 'posts', 'approve'); 29 | }, 30 | 31 | pending: function() { 32 | return _.where(this.attributes, {subscription: 'pending'}); 33 | }, 34 | 35 | // These are workarounds resultant by server issues 36 | parse: function(resp, xhr) { 37 | if (typeof(resp) === 'string') { 38 | return {}; 39 | } 40 | 41 | return resp; 42 | }, 43 | 44 | sync: function(method, model, options) { 45 | if (method === 'update' || method === 'create') { 46 | // Always POST only changed attributes 47 | var changed = model.changedAttributes(); 48 | if (changed) { 49 | options.data = JSON.stringify([changed] || [{}]); 50 | options.contentType = 'application/json'; 51 | options.dataType = 'text'; 52 | method = 'create'; 53 | } 54 | } 55 | var sync = Backbone.ajaxSync ? Backbone.ajaxSync : Backbone.sync; 56 | sync.call(this, method, model, options); 57 | } 58 | }); 59 | 60 | return ChannelFollowersRequests; 61 | }); 62 | -------------------------------------------------------------------------------- /js/app/models/CollectionBase.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var Backbone = require('backbone'); 19 | 20 | var CollectionBase = Backbone.Collection.extend({ 21 | fetch: function(options) { 22 | if (options && options.credentials) { 23 | options.credentials.addAuthorizationToAjaxOptions(options); 24 | } 25 | Backbone.Collection.prototype.fetch.call(this, options); 26 | }, 27 | 28 | save: function(attributes, options) { 29 | if (options && options.credentials) { 30 | options.credentials.addAuthorizationToAjaxOptions(options); 31 | } 32 | Backbone.Collection.prototype.save.call(this, attributes, options); 33 | }, 34 | 35 | create: function(attributes, options) { 36 | if (options && options.credentials) { 37 | options.credentials.addAuthorizationToAjaxOptions(options); 38 | } 39 | return Backbone.Collection.prototype.create.call(this, attributes, options); 40 | }, 41 | 42 | _adjustOptions: function(options) { 43 | if (options && options.credentials) { 44 | return _.extend(options, credentials.asAjaxOptions()); 45 | } else { 46 | return options; 47 | } 48 | }, 49 | 50 | sync: function(method, model, options) { 51 | var sync = Backbone.ajaxSync ? Backbone.ajaxSync : Backbone.sync; 52 | sync.call(this, method, model, options); 53 | } 54 | }); 55 | 56 | return CollectionBase; 57 | }); 58 | -------------------------------------------------------------------------------- /js/app/models/ContentSearch.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var api = require('util/api'); 19 | var CollectionBase = require('models/CollectionBase'); 20 | var ContentSearchResult = require('models/ContentSearchResult'); 21 | 22 | var ContentSearch = CollectionBase.extend({ 23 | model: ContentSearchResult, 24 | 25 | constructor: function() { 26 | CollectionBase.call(this); 27 | }, 28 | 29 | url: function() { 30 | return api.url('search'); 31 | }, 32 | 33 | doSearch: function(query, callback) { 34 | if (query.q) { 35 | query = _.extend({type: 'content', max: 5}, query); 36 | this.fetch({data: query, success: callback}); 37 | } 38 | }, 39 | 40 | parse: function(resp, xhr) { 41 | if (typeof(resp) === 'object' && resp.rsm && resp.items) { 42 | this.index = resp.rsm.index; 43 | this.count = resp.rsm.count; 44 | return resp.items; 45 | } else { 46 | return resp; 47 | } 48 | } 49 | }); 50 | 51 | return ContentSearch; 52 | }); 53 | -------------------------------------------------------------------------------- /js/app/models/ContentSearchResult.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var api = require('util/api') 19 | , ModelBase = require('models/ModelBase') 20 | 21 | var ContentSearchResult = ModelBase.extend({ 22 | authorAvatarUrl: function(size) { 23 | return api.avatarUrl(this.author(), size) 24 | }, 25 | 26 | author: function() { 27 | return this.get('author') 28 | }, 29 | 30 | content: function() { 31 | return this.get('content') 32 | }, 33 | 34 | published: function() { 35 | return this.get('published') 36 | }, 37 | 38 | updated: function() { 39 | return this.get('updated') || this.get('published') 40 | }, 41 | 42 | parentJID: function() { 43 | return this.get('parent_simpleid') 44 | } 45 | }); 46 | 47 | return ContentSearchResult 48 | }); 49 | -------------------------------------------------------------------------------- /js/app/models/Discover.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var api = require('util/api'); 19 | var ModelBase = require('models/ModelBase'); 20 | var DiscoverCollection = require('models/DiscoverCollection'); 21 | 22 | var Discover = ModelBase.extend({ 23 | constructor: function(username) { 24 | ModelBase.call(this); 25 | this.username = username; 26 | this.mostActive = new DiscoverCollection(api.url('most_active')); 27 | this.recommendations = new DiscoverCollection(api.url('recommendations')); 28 | }, 29 | 30 | doDiscover: function() { 31 | var callback = this._triggerDiscoverCallback(); 32 | this.listenTo(this.mostActive, 'reset', callback); 33 | this.mostActive.fetch({data: {max:5}, reset: true}); 34 | 35 | this.listenTo(this.recommendations, 'reset', callback); 36 | this.recommendations.fetch({data: {max:5, user: this.username}, reset: true}); 37 | }, 38 | 39 | _triggerDiscoverCallback: function() { 40 | var self = this; 41 | var fetched = []; 42 | return function(model) { 43 | fetched.push(model); 44 | if (_.include(fetched, self.mostActive) && 45 | _.include(fetched, self.recommendations)) { 46 | self.trigger('fetch'); 47 | } 48 | } 49 | } 50 | }); 51 | 52 | return Discover; 53 | }); 54 | -------------------------------------------------------------------------------- /js/app/models/DiscoverCollection.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var CollectionBase = require('models/CollectionBase'); 19 | var MetadataSearchResult = require('models/MetadataSearchResult'); 20 | 21 | var DiscoverCollection = CollectionBase.extend({ 22 | model: MetadataSearchResult, 23 | 24 | constructor: function(discoverUrl) { 25 | CollectionBase.call(this); 26 | this.discoverUrl = discoverUrl; 27 | }, 28 | 29 | url: function() { 30 | return this.discoverUrl; 31 | }, 32 | 33 | parse: function(resp, xhr) { 34 | if (typeof(resp) === 'object' && resp.rsm && resp.items) { 35 | this.index = resp.rsm.index; 36 | this.count = resp.rsm.count; 37 | return resp.items; 38 | } else { 39 | return resp; 40 | } 41 | } 42 | }); 43 | 44 | return DiscoverCollection; 45 | }); 46 | -------------------------------------------------------------------------------- /js/app/models/Item.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var api = require('util/api'); 19 | var ModelBase = require('models/ModelBase'); 20 | 21 | var Item = ModelBase.extend({ 22 | 23 | initialize: function() { 24 | this._initializeComments(); 25 | this._defineGetter('author', function() { 26 | var author = this.get('author'); 27 | if (author) { 28 | if (author.indexOf('acct:') !== -1) { 29 | return author.slice('acct:'.length); 30 | } else { 31 | return author; 32 | } 33 | } 34 | }); 35 | this._defineGetter('source', function() { 36 | var source = this.get('source'); 37 | return source ? source.split('/', 2)[0] : undefined; 38 | }); 39 | this._defineGetter('content', function() { 40 | return this.get('content') || ''; 41 | }); 42 | this._defineGetter('media', function() { 43 | return this.get('media') || ''; 44 | }); 45 | this._defineGetter('updated', function() { 46 | return this.get('updated') || this.published; 47 | }); 48 | this._defineGetter('replyTo'); 49 | this._defineGetter('published'); 50 | this._defineGetter('id'); 51 | }, 52 | 53 | _initializeComments: function() { 54 | var comments = []; 55 | _.each(this.attributes.comments || [], function(comment) { 56 | comments.push(new Item(comment)); 57 | }); 58 | this.comments = comments; 59 | delete this.attributes.comments; 60 | }, 61 | 62 | _defineGetter: function(name, getter) { 63 | getter = getter ? _.bind(getter, this) : _.bind(this.get, this, name); 64 | Object.defineProperty(this, name, {get: getter}); 65 | }, 66 | 67 | isPost: function() { 68 | return !this.replyTo; 69 | }, 70 | 71 | isComment: function() { 72 | return !this.isPost(); 73 | }, 74 | 75 | deleteComment: function(id) { 76 | for (var i in this.comments) { 77 | if (this.comments[i].id === id) { 78 | this.comments.splice(i, 1); 79 | } 80 | } 81 | }, 82 | 83 | authorAvatarUrl: function(size) { 84 | return api.avatarUrl(this.author, size); 85 | } 86 | }); 87 | 88 | return Item; 89 | }); 90 | -------------------------------------------------------------------------------- /js/app/models/MetadataSearch.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var api = require('util/api'); 19 | var CollectionBase = require('models/CollectionBase'); 20 | var MetadataSearchResult = require('models/MetadataSearchResult'); 21 | 22 | var MetadataSearch = CollectionBase.extend({ 23 | model: MetadataSearchResult, 24 | 25 | constructor: function() { 26 | CollectionBase.call(this); 27 | }, 28 | 29 | url: function() { 30 | return api.url('search'); 31 | }, 32 | 33 | doSearch: function(query, callback) { 34 | if (query.q) { 35 | query = _.extend({type: 'metadata', max: 5}, query); 36 | this.fetch({data: query, success: callback}); 37 | } 38 | }, 39 | 40 | parse: function(resp, xhr) { 41 | if (typeof(resp) === 'object' && resp.rsm && resp.items) { 42 | this.index = resp.rsm.index; 43 | this.count = resp.rsm.count; 44 | return resp.items; 45 | } else { 46 | return resp; 47 | } 48 | } 49 | }); 50 | 51 | return MetadataSearch; 52 | }); 53 | -------------------------------------------------------------------------------- /js/app/models/MetadataSearchResult.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var api = require('util/api'); 19 | var ModelBase = require('models/ModelBase'); 20 | 21 | var MetadataSearchResult = ModelBase.extend({ 22 | jid: function() { 23 | return this.get('jid'); 24 | }, 25 | 26 | jidAvatarUrl: function(size) { 27 | return api.avatarUrl(this.jid(), size); 28 | }, 29 | 30 | title: function() { 31 | return this.get('title'); 32 | }, 33 | 34 | channelType: function() { 35 | return this.get('channelType'); 36 | }, 37 | 38 | description: function() { 39 | return this.get('description'); 40 | }, 41 | 42 | published: function() { 43 | return this.get('published'); 44 | }, 45 | 46 | defaultAffiliation: function() { 47 | return this.get('defaultAffiliation'); 48 | } 49 | }); 50 | 51 | return MetadataSearchResult; 52 | }); 53 | -------------------------------------------------------------------------------- /js/app/models/ModelBase.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var Backbone = require('backbone'); 19 | 20 | var ModelBase = Backbone.Model.extend({ 21 | fetch: function(options) { 22 | if (options && options.credentials) { 23 | options.credentials.addAuthorizationToAjaxOptions(options); 24 | } 25 | Backbone.Model.prototype.fetch.call(this, options); 26 | }, 27 | 28 | save: function(attributes, options) { 29 | if (options && options.credentials) { 30 | options.credentials.addAuthorizationToAjaxOptions(options); 31 | } 32 | Backbone.Model.prototype.save.call(this, attributes, options); 33 | }, 34 | 35 | sync: function(method, model, options) { 36 | var sync = Backbone.ajaxSync ? Backbone.ajaxSync : Backbone.sync; 37 | sync.call(this, method, model, options); 38 | } 39 | }); 40 | 41 | return ModelBase; 42 | }); 43 | -------------------------------------------------------------------------------- /js/app/models/Preferences.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var api = require('util/api'); 19 | var ModelBase = require('models/ModelBase'); 20 | 21 | var Preferences = ModelBase.extend({ 22 | 23 | url: function() { 24 | return api.url('notification_settings'); 25 | }, 26 | 27 | parse: function(resp, xhr) { 28 | // FIXME: only handling one email 29 | resp = resp[0] || {} 30 | 31 | if (!resp.target) { 32 | // Workaround for old accounts (without email) 33 | this.trigger('change') 34 | } 35 | 36 | return resp 37 | }, 38 | 39 | sync: function(method, model, options) { 40 | if (method === 'update') { 41 | // always POST 42 | method = 'create'; 43 | } 44 | var sync = Backbone.ajaxSync ? Backbone.ajaxSync : Backbone.sync; 45 | sync.call(this, method, model, options); 46 | } 47 | }); 48 | 49 | return Preferences; 50 | }); 51 | -------------------------------------------------------------------------------- /js/app/models/Search.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var api = require('util/api'); 19 | var ModelBase = require('models/ModelBase'); 20 | var MetadataSearch = require('models/MetadataSearch'); 21 | var ContentSearch = require('models/ContentSearch'); 22 | 23 | var Search = ModelBase.extend({ 24 | constructor: function() { 25 | ModelBase.call(this); 26 | this.channels = new MetadataSearch(); 27 | this.posts = new ContentSearch(); 28 | }, 29 | 30 | doSearch: function(params) { 31 | var callback = this._triggerSearchCallback(); 32 | this.channels.doSearch(params, callback); 33 | this.posts.doSearch(params, callback); 34 | }, 35 | 36 | _triggerSearchCallback: function() { 37 | var self = this; 38 | var fetched = []; 39 | return function(model) { 40 | fetched.push(model); 41 | if (_.include(fetched, self.channels) && 42 | _.include(fetched, self.posts)) { 43 | self.trigger('fetch'); 44 | } 45 | } 46 | } 47 | }); 48 | 49 | return Search; 50 | }); 51 | -------------------------------------------------------------------------------- /js/app/models/SidebarInfo.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var Backbone = require('backbone'); 19 | var SidebarInfoDB = require('models/db/SidebarInfoDB'); 20 | require('backbone-indexeddb'); 21 | 22 | var SidebarInfo = Backbone.Model.extend({ 23 | database: SidebarInfoDB, 24 | storeName: SidebarInfoDB.id 25 | }); 26 | 27 | return SidebarInfo; 28 | }); 29 | -------------------------------------------------------------------------------- /js/app/models/SimilarChannels.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var api = require('util/api'); 19 | var CollectionBase = require('models/CollectionBase'); 20 | var MetadataSearchResult = require('models/MetadataSearchResult'); 21 | 22 | var SimilarChannels = CollectionBase.extend({ 23 | model: MetadataSearchResult, 24 | 25 | constructor: function(channel) { 26 | this.channel = channel; 27 | CollectionBase.call(this); 28 | }, 29 | 30 | url: function() { 31 | return api.url(this.channel, 'similar'); 32 | }, 33 | 34 | parse: function(resp, xhr) { 35 | if (typeof(resp) === 'object' && resp.rsm && resp.items) { 36 | this.index = resp.rsm.index; 37 | this.count = resp.rsm.count; 38 | return resp.items; 39 | } else { 40 | return resp; 41 | } 42 | }, 43 | 44 | usernames: function() { 45 | var usernames = []; 46 | _.each(this.models, function(model) { 47 | usernames.push(model.jid()); 48 | }); 49 | 50 | return usernames; 51 | } 52 | }); 53 | 54 | return SimilarChannels; 55 | }); 56 | -------------------------------------------------------------------------------- /js/app/models/db/ChannelMetadataDB.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var ChannelMetadataDB = { 19 | id: 'channel-metadata', 20 | migrations: [ 21 | { 22 | version: '1.1', 23 | migrate: function(transaction, next) { 24 | var store; 25 | 26 | if (!transaction.db.objectStoreNames.contains('channel-metadata')) { 27 | store = transaction.db.createObjectStore('channel-metadata'); 28 | } else { 29 | store = transaction.objectStore('channel-metadata'); 30 | } 31 | 32 | store.createIndex('channelIndex', 'channel', { unique: true }); 33 | 34 | next(); 35 | } 36 | } 37 | ] 38 | }; 39 | 40 | return ChannelMetadataDB; 41 | }); 42 | -------------------------------------------------------------------------------- /js/app/models/db/PostsDB.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var PostsDB = { 19 | id: 'posts', 20 | migrations: [ 21 | { 22 | version: '1.1', 23 | migrate: function(transaction, next) { 24 | var store; 25 | 26 | if (!transaction.db.objectStoreNames.contains('posts')) { 27 | store = transaction.db.createObjectStore('posts'); 28 | } else { 29 | store = transaction.objectStore('posts'); 30 | } 31 | 32 | store.createIndex('sourceIndex', 'source'); 33 | 34 | next(); 35 | } 36 | } 37 | ] 38 | }; 39 | 40 | return PostsDB; 41 | }); 42 | -------------------------------------------------------------------------------- /js/app/models/db/SidebarInfoDB.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var SidebarInfoDB = { 19 | id: 'sidebar-info', 20 | migrations: [ 21 | { 22 | version: '1.0', 23 | migrate: function(transaction, next) { 24 | if (transaction.db.objectStoreNames.contains('sidebar-info')) { 25 | var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB 26 | var dbreq = indexedDB.deleteDatabase('sidebar-info') 27 | } 28 | 29 | next() 30 | } 31 | }, 32 | { 33 | version: '1.1', 34 | migrate: function(transaction, next) { 35 | var store 36 | 37 | if (!transaction.db.objectStoreNames.contains('sidebar-info')) { 38 | store = transaction.db.createObjectStore('sidebar-info') 39 | } else { 40 | store = transaction.objectStore('sidebar-info') 41 | } 42 | 43 | store.createIndex('userIndex', 'user', { unique: false }) 44 | 45 | next(); 46 | } 47 | } 48 | ] 49 | }; 50 | 51 | return SidebarInfoDB; 52 | }); 53 | -------------------------------------------------------------------------------- /js/app/util/animations.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var transition = Modernizr.prefixed('transition'); 19 | var transform = Modernizr.prefixed('transform'); 20 | var transEndEventNames = { 21 | 'WebkitTransition' : 'webkitTransitionEnd', 22 | 'MozTransition' : 'transitionend', 23 | 'OTransition' : 'oTransitionEnd', 24 | 'msTransition' : 'msTransitionEnd', // maybe? 25 | 'transition' : 'transitionend' 26 | } 27 | 28 | function transitionsEndEvent() { 29 | return transEndEventNames[transition]; 30 | } 31 | 32 | // (C) WebReflection (As It Is) - Mit Style 33 | // redraw function to make sure that transitions are triggered 34 | // ideally an Object.defineProperty 35 | // unfortunately some engine will complain 36 | // if used against DOM objects 37 | document.redraw = function() { 38 | // clientHeight returns 0 if not present in the DOM 39 | // e.g. document.removeChild(document.documentElement); 40 | // also some crazy guy may swap runtime the whole HTML element 41 | // this is why the documentElement should be always discovered 42 | // and if present, it should be a node of the document 43 | // In all these cases the returned value is true 44 | // otherwise what is there cannot really be considered as painted 45 | return !!(document.documentElement || 0).clientHeight; 46 | }; 47 | 48 | return { 49 | transitionsEndEvent: transitionsEndEvent 50 | } 51 | 52 | }); -------------------------------------------------------------------------------- /js/app/util/api.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 18 | define(function(require) { 19 | var config = require('config'); 20 | 21 | function url() { 22 | var components = _.toArray(arguments); 23 | components.unshift(config.baseUrl); 24 | return components.join('/'); 25 | } 26 | 27 | function rootUrl() { 28 | return url(''); 29 | } 30 | 31 | function mediaUrl(channel, id, height, width) { 32 | var ret = id ? url(channel, 'media', id) : url(channel, 'media'); 33 | if (height && width) { 34 | ret += '?maxheight=' + height + '&maxwidth=' + width; 35 | } 36 | return ret; 37 | } 38 | 39 | function avatarUrl(channel, size) { 40 | return mediaUrl(channel, 'avatar', size, size); 41 | } 42 | 43 | return { 44 | url: url, 45 | rootUrl: rootUrl, 46 | mediaUrl: mediaUrl, 47 | avatarUrl: avatarUrl 48 | }; 49 | }); 50 | -------------------------------------------------------------------------------- /js/app/util/autoResize.js: -------------------------------------------------------------------------------- 1 | define(['jquery'], function($) { 2 | 3 | // thanks to http://www.alistapart.com/articles/expanding-text-areas-made-elegant/ 4 | $.fn.autoResize = function() { 5 | 6 | this.filter('.expandingArea').each(function() { 7 | 8 | var $this = $(this); 9 | var $textarea = $this.find('textarea'); 10 | var $span = $this.find('span'); 11 | var textarea = $textarea[0]; 12 | var span = $span[0]; 13 | 14 | if (textarea.addEventListener) { 15 | textarea.addEventListener('input', function() { 16 | span.textContent = textarea.value; 17 | }, false); 18 | span.textContent = textarea.value; 19 | } else if (textarea.attachEvent) { 20 | // IE8 compatibility 21 | textarea.attachEvent('onpropertychange', function() { 22 | span.innerText = textarea.value; 23 | }); 24 | span.innerText = textarea.value; 25 | } 26 | // Enable extra CSS 27 | $this.addClass('active'); 28 | 29 | }); 30 | } 31 | }); -------------------------------------------------------------------------------- /js/app/util/avatarFallback.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var $ = require('jquery'); 19 | 20 | function avatarFallback(avatarElements, type, size) { 21 | var fallbackImage; 22 | $(avatarElements).one('error', function(event) { 23 | type = event.target.dataset['type'] || type; 24 | fallbackImage = 'img/' + type + '-' + size + 'px.jpg'; 25 | event.target.src = fallbackImage; 26 | }); 27 | } 28 | 29 | return avatarFallback; 30 | }); 31 | -------------------------------------------------------------------------------- /js/app/util/dateUtils.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | function toUTC(date) { 19 | var utc = new Date(date.getUTCFullYear(), 20 | date.getUTCMonth(), 21 | date.getUTCDate(), 22 | date.getUTCHours(), 23 | date.getUTCMinutes(), 24 | date.getUTCSeconds()); 25 | 26 | return utc; 27 | } 28 | 29 | function utcDate(isoDate) { 30 | return toUTC(new Date(isoDate)); 31 | } 32 | 33 | function toUTCDate(date) { 34 | return toUTC(date); 35 | } 36 | 37 | function earliestTime() { 38 | return new Date(1970, 0, 1).toISOString(); 39 | } 40 | 41 | function now() { 42 | return new Date(); 43 | } 44 | 45 | function lastWeekDate() { 46 | var weekInMillis = 7*(24*60*(60*1000)); 47 | var now = toUTCDate(new Date()); 48 | return now - weekInMillis; 49 | } 50 | 51 | return { 52 | toUTC: toUTC, 53 | utcDate: utcDate, 54 | toUTCDate: toUTCDate, 55 | earliestTime: earliestTime, 56 | now: now, 57 | lastWeekDate: lastWeekDate 58 | }; 59 | }); 60 | -------------------------------------------------------------------------------- /js/app/util/embedlify.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var _ = require('underscore'); 19 | var config = require('config'); 20 | var template = require('text!templates/content/embed.html'); 21 | 22 | function embedlify(success) { 23 | return { 24 | maxWidth: 400, 25 | key: config.embedlyKey, 26 | secure: config.embedlySecure, 27 | success: function(oembed, dict) { 28 | // If is not a link or if the link has an image 29 | if (oembed.type !== 'link' || oembed.thumbnail_url) { 30 | var html = _.template(template, { 31 | maxWidth: 400, 32 | url: oembed.url || dict.url, 33 | title: (oembed.type !== 'photo') ? (oembed.title || dict.url) : undefined, 34 | img: (oembed.type === 'photo') ? oembed.url : oembed.thumbnail_url, 35 | width: (oembed.type === 'photo') ? oembed.width : oembed.thumbnail_width, 36 | html: oembed.html, 37 | description: oembed.description 38 | }); 39 | success(dict.node, html); 40 | } 41 | } 42 | }; 43 | } 44 | 45 | return embedlify; 46 | }); 47 | -------------------------------------------------------------------------------- /js/app/util/indexedDB.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | function isSuppported() { 19 | window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; 20 | return window.indexedDB ? true : false; 21 | } 22 | 23 | return { 24 | isSuppported: isSuppported 25 | }; 26 | }); 27 | -------------------------------------------------------------------------------- /js/app/util/mediaFallback.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var template = require('text!templates/content/mediaFallback.html'); 19 | 20 | function mediaFallback(imgElements) { 21 | $(imgElements).one('error', function(event) { 22 | var $target = $(event.target); 23 | var url = $target.parent().attr('href'); 24 | var $mediaDiv = $target.parents('.media'); 25 | $mediaDiv.html(_.template(template, { 26 | title: "Click to download the file", 27 | url: url, 28 | img: 'img/media-download.png' 29 | })); 30 | }); 31 | } 32 | 33 | return mediaFallback; 34 | }); 35 | -------------------------------------------------------------------------------- /js/app/util/mediaServer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 18 | define(function(require) { 19 | var $ = require('jquery'); 20 | var api = require('util/api'); 21 | 22 | function buildFormData(file) { 23 | var formData = new FormData(); 24 | formData.append('data', file); 25 | formData.append('content-type', file.type); 26 | if (file.name) { 27 | formData.append('filename', file.name); 28 | } 29 | 30 | return formData; 31 | } 32 | 33 | function sendUploadFileRequest(formData, method, url, authHeader) { 34 | var options = { 35 | type: method, 36 | url: url, 37 | crossDomain: true, 38 | data: formData, 39 | xhrFields: {withCredentials: true}, 40 | contentType: false, 41 | processData: false, 42 | beforeSend: function(xhr) { 43 | xhr.setRequestHeader('Authorization', authHeader); 44 | } 45 | }; 46 | 47 | return $.ajax(options); 48 | } 49 | 50 | function uploadMedia(file, channel, authHeader) { 51 | var formData = buildFormData(file); 52 | var url = api.mediaUrl(channel); 53 | return sendUploadFileRequest(formData, 'POST', url, authHeader); 54 | } 55 | 56 | function uploadAvatar(file, channel, authHeader) { 57 | var formData = buildFormData(file); 58 | var url = api.avatarUrl(channel); 59 | return sendUploadFileRequest(formData, 'PUT', url, authHeader); 60 | } 61 | 62 | return { 63 | uploadMedia: uploadMedia, 64 | uploadAvatar: uploadAvatar, 65 | sendUploadFileRequest: sendUploadFileRequest, 66 | buildFormData: buildFormData 67 | }; 68 | }); 69 | -------------------------------------------------------------------------------- /js/app/util/spinner.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var $ = require('jquery'); 19 | var SPINNER = ''; 20 | 21 | function append(element) { 22 | var spinner = $(SPINNER); 23 | element.append(spinner.el); 24 | } 25 | 26 | function remove(element) { 27 | element.remove('.loading'); 28 | } 29 | 30 | function replace(element) { 31 | element.html(SPINNER); 32 | } 33 | 34 | return { 35 | append: append, 36 | remove: remove, 37 | replace: replace 38 | }; 39 | }); 40 | -------------------------------------------------------------------------------- /js/app/util/url.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 18 | define(function(require) { 19 | var Backbone = require('backbone'); 20 | 21 | function getQueryVariable(variable) { 22 | var search = Backbone.history.location.search; 23 | if (!search || search == '') { 24 | return undefined; 25 | } 26 | var query = search.substring(1); 27 | var vars = query.split("&"); 28 | for (var i = 0; i < vars.length; i++) { 29 | var pair = vars[i].split("="); 30 | if (pair[0] == variable) { 31 | return unescape(pair[1]); 32 | } 33 | } 34 | return undefined; 35 | } 36 | 37 | return { 38 | getQueryVariable: getQueryVariable 39 | }; 40 | }); 41 | -------------------------------------------------------------------------------- /js/app/views/content/AbstractEditStream.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var Backbone = require('backbone'); 19 | 20 | var AbstractEditStream = Backbone.View.extend({ 21 | className: 'stream clearfix', 22 | 23 | _initialize: function() { 24 | this.fields = 25 | { 26 | 'channel_title': 'title', 27 | 'channel_description': 'description', 28 | 'channel_public_access': 'access_model', 29 | 'channel_default_role': 'default_affiliation', 30 | 'channel_status': 'status' 31 | }; 32 | }, 33 | 34 | _check: function(element, value) { 35 | if (element) { 36 | element.attr('checked', value); 37 | } 38 | }, 39 | 40 | _setFields: function(model) { 41 | this._setTextFields(model); 42 | this._setAccessModel(model); 43 | this._setDefaultRole(model); 44 | }, 45 | 46 | _save: function(model, options) { 47 | // Set fields 48 | this._setFields(model); 49 | 50 | // Save 51 | options = options || {}; 52 | options.credentials = this.options.user.credentials; 53 | model.save(null, options); 54 | }, 55 | 56 | _setTextFields: function(model) { 57 | // FIXME not all fields are handled by HTTP API 58 | // var textFields = ['channel_title', 'channel_description', 'channel_status', 'channel_location']; 59 | var textFields = ['channel_title', 'channel_description', 'channel_status']; 60 | for (var i = 0; i < textFields.length; i++) { 61 | var content = this.$('#' + textFields[i]).val(); 62 | model.set(this.fields[textFields[i]], content, {silent: true}); 63 | } 64 | }, 65 | 66 | _isChecked: function(element) { 67 | return element ? element.prop('checked') : false; 68 | }, 69 | 70 | _setAccessModel: function(model) { 71 | var accessField = this.fields['channel_public_access']; 72 | if (this._isChecked(this.$('#channel_public_access'))) { 73 | model.set(accessField, 'open', {silent: true}); 74 | } else { 75 | model.set(accessField, 'authorize', {silent: true}); 76 | } 77 | }, 78 | 79 | _setDefaultRole: function(model) { 80 | var defaultRoleField = this.fields['channel_default_role']; 81 | if (this.$('#channel_default_role').val() == 'followerPlus') { 82 | model.set(defaultRoleField, 'publisher', {silent: true}); 83 | } else if (this.$('#channel_default_role').val() == 'follower') { 84 | model.set(defaultRoleField, 'member', {silent: true}); 85 | } 86 | } 87 | }); 88 | 89 | return AbstractEditStream; 90 | }); 91 | -------------------------------------------------------------------------------- /js/app/views/content/AbstractExploreView.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var Backbone = require('backbone'); 19 | var avatarFallback = require('util/avatarFallback'); 20 | var Events = Backbone.Events; 21 | 22 | var AbstractExploreView = Backbone.View.extend({ 23 | 24 | _render: function() { 25 | avatarFallback(this.$('.avatar'), undefined, 50); 26 | this._renderButtons(); 27 | }, 28 | 29 | _defineGetter: function(name, getter) { 30 | getter.bind(this); 31 | Object.defineProperty(this, name, {get: getter}); 32 | }, 33 | 34 | _renderButtons: function() { 35 | var self = this; 36 | this.$('.channel').each(function() { 37 | var jid = $(this).attr('id'); 38 | if (self._follows(jid)) { 39 | $(this).find('.follow').removeClass('callToAction').addClass('disabled'); 40 | } 41 | }); 42 | }, 43 | 44 | _follows: function(jid) { 45 | var followedChannels = this.options.user.subscribedChannels.channels(); 46 | return _.include(followedChannels, jid); 47 | }, 48 | 49 | _channelDefaultAffiliation: function(jid) { 50 | for (var i = 0; i < this.channels.length; i++) { 51 | if (this.channels[i].jid() === jid) { 52 | return this.channels[i].defaultAffiliation(); 53 | } 54 | } 55 | 56 | return null; 57 | }, 58 | 59 | _follow: function(event) { 60 | var $channel = $(event.currentTarget).parent(); 61 | var jid = $channel.attr('id'); 62 | var role = this._channelDefaultAffiliation(jid); 63 | var credentials = this.options.user.credentials; 64 | 65 | if (jid && role && credentials) { 66 | var animationClassName = 'rainbow'; 67 | var offset = $channel.offset(); 68 | 69 | // Subscribe 70 | this.options.user.subscribedChannels.subscribe(jid, ['posts'], role, credentials, {offset: offset, animationClass: animationClassName}); 71 | 72 | // Disable button 73 | $channel.find('.follow').removeClass('callToAction').addClass('disabled'); 74 | } 75 | } 76 | }); 77 | 78 | return AbstractExploreView; 79 | }); 80 | -------------------------------------------------------------------------------- /js/app/views/content/AnonBar.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var Backbone = require('backbone'); 19 | var Events = Backbone.Events; 20 | var l10nBrowser = require('l10n-browser'); 21 | var localTemplate; 22 | var template = require('text!templates/content/anonBar.html'); 23 | 24 | var AnonBar = Backbone.View.extend({ 25 | className: 'navigation clearfix', 26 | 27 | events: {'click .findChannels': 'explore', 28 | 'click .home': 'home'}, 29 | 30 | initialize: function() { 31 | if (!localTemplate) localTemplate = l10nBrowser.localiseHTML(template, {}); 32 | this.render(); 33 | }, 34 | 35 | explore: function() { 36 | Events.trigger('navigate', 'explore'); 37 | }, 38 | 39 | home: function() { 40 | Events.trigger('navigate', '/'); 41 | }, 42 | 43 | render: function() { 44 | this.$el.html(_.template(localTemplate)); 45 | } 46 | }); 47 | 48 | return AnonBar; 49 | }); 50 | -------------------------------------------------------------------------------- /js/app/views/content/AnonChannelOverlay.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var animations = require('util/animations'); 19 | var Backbone = require('backbone'); 20 | var Events = Backbone.Events; 21 | var l10nBrowser = require('l10n-browser'); 22 | var localTemplate; 23 | var template = require('text!templates/content/anonOverlay.html'); 24 | 25 | var AnonChannelOverlay = Backbone.View.extend({ 26 | className: 'overlay', 27 | 28 | events: {'click .close': '_removeOverlay', 29 | 'submit form.login': 'login'}, 30 | 31 | initialize: function() { 32 | if (!localTemplate) localTemplate = l10nBrowser.localiseHTML(template, {}); 33 | this.listenTo(this.model, 'loginSuccess', this._successfullLogin); 34 | this.listenTo(this.model, 'loginError', this._invalidLogin); 35 | }, 36 | 37 | destroy: function() { 38 | this.remove(); 39 | }, 40 | 41 | render: function() { 42 | this.$el.html(_.template(localTemplate)); 43 | }, 44 | 45 | _invalidLogin: function(message) { 46 | $('.error').text(message).show(); 47 | this._enableButton(); 48 | }, 49 | 50 | _successfullLogin: function() { 51 | $('.content').removeClass('anonView'); 52 | 53 | // Remove overlay 54 | this._removeOverlay(function() { 55 | Events.trigger('navigate', 'home'); 56 | }); 57 | }, 58 | 59 | _disableButton: function() { 60 | this.$('#auth_submit').attr('disabled', true); 61 | }, 62 | 63 | _enableButton: function() { 64 | this.$('#auth_submit').attr('disabled', false); 65 | }, 66 | 67 | login: function(event) { 68 | event.preventDefault(); 69 | this._disableButton(); 70 | var username = $('#auth_name').val().toLowerCase(); // Ensure lower case 71 | var password = $('#auth_pwd').val(); 72 | var permanent = $('#store_local').is(':checked'); 73 | 74 | var loginInfo = {'username': username, 'password': password}; 75 | this.model.login(loginInfo, {'permanent': permanent}); 76 | }, 77 | 78 | _removeOverlay: function(callback) { 79 | var self = this; 80 | this.$('.modal').one(animations.transitionsEndEvent(), function() { 81 | self.$el.hide(); 82 | self.destroy(); 83 | if (callback) { 84 | callback(); 85 | } 86 | }); 87 | this.$el.removeClass('visible'); 88 | } 89 | }); 90 | 91 | return AnonChannelOverlay; 92 | }); 93 | -------------------------------------------------------------------------------- /js/app/views/content/ChannelNotifications.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var api = require('util/api'); 19 | var avatarFallback = require('util/avatarFallback'); 20 | var Backbone = require('backbone'); 21 | var ChannelFollowersRequests = require('models/ChannelFollowersRequests'); 22 | var template = require('text!templates/content/notification.html'); 23 | 24 | var ChannelNotifications = Backbone.View.extend({ 25 | className: 'notificationsList', 26 | 27 | events: {'click .positive': '_allow', 28 | 'click .negative': '_deny'}, 29 | 30 | initialize: function() { 31 | this.model = new ChannelFollowersRequests(this.options.channel); 32 | this.listenTo(this.model, 'change', this.render); 33 | this.model.fetch({ 34 | credentials: this.options.user.credentials 35 | }); 36 | }, 37 | 38 | render: function() { 39 | var pending = this.model.pending(); 40 | this.$el.html(_.template(template, { 41 | pending: pending, 42 | api: api 43 | })); 44 | 45 | if (pending.length > 0) { 46 | this.$el.find('.notification').addClass('visible'); 47 | avatarFallback(this.$('.avatar'), 'personal', 75); 48 | } 49 | }, 50 | 51 | _allow: function(event) { 52 | var $target = $(event.target).closest('.notification'); 53 | var jid = $target.get(0).id.split('_')[1]; 54 | 55 | this.listenTo(this.model, 'sync', function() { 56 | $target.addClass('log granted'); 57 | }); 58 | this._setAction(jid, 'subscribed'); 59 | }, 60 | 61 | _deny: function() { 62 | var $target = $(event.target).closest('.notification'); 63 | var jid = $target.get(0).id.split('_')[1]; 64 | 65 | this.listenTo(this.model, 'sync', function() { 66 | $target.addClass('log denied'); 67 | }); 68 | this._setAction(jid, 'none'); 69 | }, 70 | 71 | _setAction: function(jid, subscription) { 72 | this.model.set({'jid': jid, 'subscription': subscription}, {silent: true}); 73 | this.model.save(null, {credentials: this.options.user.credentials}); 74 | } 75 | }); 76 | 77 | return ChannelNotifications; 78 | }); 79 | -------------------------------------------------------------------------------- /js/app/views/content/ChannelPage.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var AnonChannelOverlay = require('views/content/AnonChannelOverlay'); 19 | var Backbone = require('backbone'); 20 | var ChannelView = require('views/content/ChannelView'); 21 | 22 | var ChannelPage = Backbone.View.extend({ 23 | className: 'channelView clearfix', 24 | 25 | initialize: function() { 26 | this.view = new ChannelView({ 27 | channel: this.options.channel, 28 | user: this.options.user 29 | }); 30 | 31 | this.render(); 32 | }, 33 | 34 | render: function() { 35 | this.view.render(); 36 | var $content = $('.content'); 37 | 38 | if (this.options.user.isAnonymous()) { 39 | this._renderAnonPage($content); 40 | } else { 41 | $content.html(this.view.el); 42 | $content.removeClass('full'); 43 | } 44 | }, 45 | 46 | _renderAnonPage: function($content) { 47 | this._renderOverlay(); 48 | 49 | var $center = $('
'); 50 | $center.html(this.view.el); 51 | 52 | $content.addClass('anonView'); 53 | $content.html(this.overlay.el); 54 | $content.append($center); 55 | }, 56 | 57 | _renderOverlay: function() { 58 | this.overlay = new AnonChannelOverlay({model: this.options.user}); 59 | this.overlay.render(); 60 | }, 61 | 62 | destroy: function() { 63 | if (this.overlay) { 64 | this.overlay.remove(); 65 | } 66 | this.view.destroy(); 67 | this.remove(); 68 | } 69 | }); 70 | 71 | return ChannelPage; 72 | }); 73 | -------------------------------------------------------------------------------- /js/app/views/content/ChannelView.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var animations = require('util/animations'); 19 | var AnonBar = require('views/content/AnonBar'); 20 | var Backbone = require('backbone'); 21 | var ChannelHeader = require('views/content/ChannelHeader'); 22 | var ChannelStream = require('views/content/ChannelStream'); 23 | var ChannelDetails = require('views/content/ChannelDetails'); 24 | 25 | var ChannelView = Backbone.View.extend({ 26 | className: 'channelView clearfix', 27 | 28 | events: {'click .join': '_showOverlay'}, 29 | 30 | initialize: function() { 31 | this.header = new ChannelHeader({ 32 | channel: this.options.channel, 33 | user: this.options.user 34 | }); 35 | this.stream = new ChannelStream({ 36 | channel: this.options.channel, 37 | user: this.options.user 38 | }); 39 | this.details = new ChannelDetails({ 40 | channel: this.options.channel, 41 | user: this.options.user 42 | }); 43 | 44 | if (this.options.user.isAnonymous()) { 45 | this.bar = new AnonBar(); 46 | } 47 | }, 48 | 49 | _showOverlay: function() { 50 | var $overlay = $('.overlay'); 51 | $overlay.show(); 52 | document.redraw(); 53 | $overlay.addClass('visible'); 54 | $overlay.find('input').first().focus(); 55 | }, 56 | 57 | render: function() { 58 | // This was necessary to replicate the same structure of prototype 59 | var $centered = $('
'); 60 | 61 | $centered.append(this.stream.el); 62 | $centered.append(this.details.el); 63 | this.$el.append(this.header.el); 64 | this.$el.append($centered); 65 | 66 | if (this.options.user.isAnonymous()) { 67 | this.$el.addClass('centered vertical start'); 68 | this.$el.prepend(this.bar.el); 69 | } 70 | }, 71 | 72 | destroy: function() { 73 | if (this.bar) { 74 | this.bar.remove(); 75 | } 76 | this.header.destroy(); 77 | this.stream.destroy(); 78 | this.details.destroy(); 79 | } 80 | }); 81 | 82 | return ChannelView; 83 | }); 84 | -------------------------------------------------------------------------------- /js/app/views/content/CreateChannelPage.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var Backbone = require('backbone'); 19 | var CreateChannelStream = require('views/content/CreateChannelStream'); 20 | 21 | var CreateChannelPage = Backbone.View.extend({ 22 | className: 'channelView', 23 | 24 | initialize: function() { 25 | this.view = new CreateChannelStream({ 26 | user: this.options.user 27 | }); 28 | 29 | this.render(); 30 | }, 31 | 32 | render: function() { 33 | var $content = $('.content'); 34 | $content.html(this.view.el); 35 | $content.removeClass('full'); 36 | }, 37 | 38 | destroy: function() { 39 | this.remove(); 40 | this.view.remove(); 41 | } 42 | }); 43 | 44 | return CreateChannelPage; 45 | }); 46 | -------------------------------------------------------------------------------- /js/app/views/content/DiscoverView.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var AbstractExploreView = require('views/content/AbstractExploreView'); 19 | var Backbone = require('backbone'); 20 | var Events = Backbone.Events; 21 | var l10nBrowser = require('l10n-browser'); 22 | var localTemplate; 23 | var spinner = require('util/spinner'); 24 | var template = require('text!templates/content/discover.html'); 25 | 26 | var DiscoverView = AbstractExploreView.extend({ 27 | className: 'discoverChannels clearfix', 28 | 29 | events: { 30 | 'click .callToAction': '_follow', 31 | 'click .info,.avatar': '_redirect' 32 | }, 33 | 34 | initialize: function() { 35 | if (!localTemplate) localTemplate = l10nBrowser.localiseHTML(template, {}); 36 | this._defineGetter('channels', function() { 37 | return _.union(this.model.mostActive.models, this.model.recommendations.models); 38 | }); 39 | 40 | this.listenTo(this.model, 'fetch', this.render); 41 | this.model.doDiscover(); 42 | spinner.replace(this.$el); 43 | }, 44 | 45 | render: function() { 46 | this.$el.html(_.template(localTemplate, { 47 | mostActive: this.model.mostActive.models, 48 | recommended: this.model.recommendations.models 49 | })); 50 | this._render(); 51 | }, 52 | 53 | _redirect: function(event) { 54 | var jid = this.$(event.currentTarget).parent().attr('id'); 55 | Events.trigger('navigate', jid); 56 | } 57 | }); 58 | 59 | return DiscoverView; 60 | }); 61 | -------------------------------------------------------------------------------- /js/app/views/content/EditChannelPage.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var Backbone = require('backbone'); 19 | var Events = Backbone.Events; 20 | var EditChannelView = require('views/content/EditChannelView'); 21 | 22 | var EditChannelPage = Backbone.View.extend({ 23 | className: 'channelView', 24 | 25 | initialize: function() { 26 | if (this._isOwner()) { 27 | this.model = this.options.user.metadata(this.options.channel); 28 | this.view = new EditChannelView({ 29 | model: this.model, 30 | user: this.options.user 31 | }); 32 | this.render(); 33 | } else { 34 | Events.trigger('forbidden'); 35 | } 36 | }, 37 | 38 | _isOwner: function() { 39 | var channel = this.options.channel; 40 | return this.options.user.subscribedChannels.isOwner(channel); 41 | }, 42 | 43 | render: function() { 44 | var $content = $('.content'); 45 | $content.html(this.view.el); 46 | $content.removeClass('full'); 47 | }, 48 | 49 | destroy: function() { 50 | if (this.view && this.model) { 51 | this.model.unbind('change', this.render, this); 52 | this.view.destroy(); 53 | this.remove(); 54 | } 55 | } 56 | }); 57 | 58 | return EditChannelPage; 59 | }); 60 | -------------------------------------------------------------------------------- /js/app/views/content/EditChannelView.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var Backbone = require('backbone'); 19 | var ChannelHeader = require('views/content/ChannelHeader'); 20 | var EditChannelStream = require('views/content/EditChannelStream'); 21 | 22 | var EditChannelView = Backbone.View.extend({ 23 | className: 'channelView', 24 | 25 | initialize: function() { 26 | this.header = new ChannelHeader({ 27 | model: this.model, 28 | user: this.options.user, 29 | edit: true 30 | }); 31 | this.stream = new EditChannelStream({ 32 | model: this.model, 33 | user: this.options.user 34 | }); 35 | this.render(); 36 | }, 37 | 38 | destroy: function() { 39 | this.header.destroy(); 40 | this.stream.destroy(); 41 | this.remove(); 42 | }, 43 | 44 | render: function() { 45 | this.$el.append(this.header.el); 46 | this.$el.append(this.stream.el); 47 | } 48 | }); 49 | 50 | return EditChannelView; 51 | }); 52 | -------------------------------------------------------------------------------- /js/app/views/content/ErrorPage.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Denis Washington 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var Backbone = require('backbone'); 19 | var template = require('text!templates/content/error.html') 20 | 21 | var ErrorPage = Backbone.View.extend({ 22 | className: 'channelView clearfix', 23 | 24 | initialize: function() { 25 | this.render(); 26 | }, 27 | 28 | render: function() { 29 | $('.content').html(_.template(template, { 30 | domain: this.options.domain, 31 | error: this.options.error 32 | })); 33 | }, 34 | 35 | destroy: function() { 36 | this.remove(); 37 | } 38 | }); 39 | 40 | return ErrorPage; 41 | }); 42 | -------------------------------------------------------------------------------- /js/app/views/content/ExplorePage.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var Backbone = require('backbone'); 19 | var Discover = require('models/Discover'); 20 | var DiscoverView = require('views/content/DiscoverView'); 21 | var Events = Backbone.Events; 22 | var Search = require('models/Search'); 23 | var SearchBar = require('views/content/SearchBar'); 24 | var SearchView = require('views/content/SearchView'); 25 | var spinner = require('util/spinner'); 26 | 27 | var ExplorePage = Backbone.View.extend({ 28 | className: 'discoverPage clearfix', 29 | 30 | initialize: function() { 31 | this._initDiscover(); 32 | this._initSearch(); 33 | this.render(); 34 | }, 35 | 36 | _initDiscover: function() { 37 | this.discoverModel = new Discover(this.options.user.username()); 38 | this.discoverView = new DiscoverView({model: this.discoverModel, user: this.options.user}); 39 | }, 40 | 41 | _initSearch: function() { 42 | this.searchModel = new Search(); 43 | this.searchbar = new SearchBar({model: this.searchModel}); 44 | this.searchView = new SearchView({model: this.searchModel, user: this.options.user}); 45 | Events.on('startSearch', this._removeDiscover, this); 46 | }, 47 | 48 | _removeDiscover: function() { 49 | if (this.discoverView) { 50 | this.discoverView.remove(); 51 | } 52 | 53 | this.$el.append(this.searchView.el); 54 | }, 55 | 56 | render: function() { 57 | this.searchbar.render(); 58 | this.$el.append(this.searchbar.el); 59 | this.$el.append(this.discoverView.el); 60 | 61 | var $content = $('.content'); 62 | $content.html(this.el); 63 | $content.removeClass('full'); 64 | }, 65 | 66 | destroy: function() { 67 | this.searchbar.remove(); 68 | if (this.discoverView) { 69 | this.discoverView.remove(); 70 | } 71 | if (this.searchView) { 72 | this.searchView.remove(); 73 | } 74 | this.remove(); 75 | } 76 | }); 77 | 78 | return ExplorePage; 79 | }); 80 | -------------------------------------------------------------------------------- /js/app/views/content/ForbiddenPage.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Denis Washington 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var Backbone = require('backbone'); 19 | var template = require('text!templates/content/forbidden.html'); 20 | 21 | var ForbiddenPage = Backbone.View.extend({ 22 | className: 'channelView clearfix', 23 | 24 | initialize: function() { 25 | this.render(); 26 | }, 27 | 28 | render: function() { 29 | $('.content').html(_.template(template)); 30 | }, 31 | 32 | destroy: function() { 33 | this.remove(); 34 | } 35 | }); 36 | 37 | return ForbiddenPage; 38 | }); 39 | -------------------------------------------------------------------------------- /js/app/views/content/PreferencesPage.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var Backbone = require('backbone'); 19 | var PreferencesView = require('views/content/PreferencesView'); 20 | 21 | var PreferencesPage = Backbone.View.extend({ 22 | className: 'channelView', 23 | 24 | initialize: function() { 25 | this.view = new PreferencesView({ 26 | user: this.options.user 27 | }); 28 | this.render(); 29 | }, 30 | 31 | render: function() { 32 | var $content = $('.content'); 33 | $content.html(this.view.el); 34 | $content.removeClass('full'); 35 | }, 36 | 37 | destroy: function() { 38 | this.view.remove(); 39 | this.remove(); 40 | } 41 | }); 42 | 43 | return PreferencesPage; 44 | }); 45 | -------------------------------------------------------------------------------- /js/app/views/content/PreferencesView.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var Backbone = require('backbone'); 19 | var ChannelHeader = require('views/content/ChannelHeader'); 20 | var PreferencesStream = require('views/content/PreferencesStream'); 21 | 22 | var PreferencesView = Backbone.View.extend({ 23 | className: 'channelView', 24 | 25 | initialize: function() { 26 | var channel = this.options.user.username(); 27 | this.metadata = this.options.user.metadata(channel); 28 | this.header = new ChannelHeader({ 29 | model: this.metadata, 30 | user: this.options.user, 31 | edit: true 32 | }); 33 | this.stream = new PreferencesStream({ 34 | user: this.options.user 35 | }); 36 | this.render(); 37 | }, 38 | 39 | render: function() { 40 | this.$el.append(this.header.el); 41 | this.$el.append(this.stream.el); 42 | } 43 | }); 44 | 45 | return PreferencesView; 46 | }); 47 | -------------------------------------------------------------------------------- /js/app/views/content/SearchBar.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var Backbone = require('backbone'); 19 | var Events = Backbone.Events; 20 | var l10n = require('l10n'); 21 | var template = require('text!templates/content/searchBar.html') 22 | 23 | var SearchBar = Backbone.View.extend({ 24 | className: 'searchbar', 25 | 26 | events: {'submit form': 'search'}, 27 | 28 | search: function(e) { 29 | var q = this.$el.find('input[type=search]').val(); 30 | e.preventDefault(); 31 | this.model.doSearch({q: q}); 32 | Events.trigger('startSearch'); 33 | }, 34 | 35 | render: function() { 36 | this.$el.html(_.template(template, {l: l10n.get})); 37 | } 38 | }); 39 | 40 | return SearchBar; 41 | }); 42 | -------------------------------------------------------------------------------- /js/app/views/content/SearchView.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var AbstractExploreView = require('views/content/AbstractExploreView'); 19 | var Backbone = require('backbone'); 20 | var Events = Backbone.Events; 21 | var spinner = require('util/spinner'); 22 | var l10nBrowser = require('l10n-browser'); 23 | var template = require('text!templates/content/searchResults.html') 24 | var localTemplate; 25 | 26 | var SearchView = AbstractExploreView.extend({ 27 | className: 'discoverChannels clearfix', 28 | 29 | events: { 30 | 'click .callToAction': '_follow', 31 | 'click #channel_avatar, #channel_info': '_redirectChannel', 32 | 'click .post': '_redirectPost' 33 | }, 34 | 35 | initialize: function() { 36 | if (!localTemplate) localTemplate = l10nBrowser.localiseHTML(template, {}); 37 | this._defineGetter('channels', function() { 38 | return this.model.channels.models; 39 | }); 40 | this.listenTo(this.model, 'fetch', this.render); 41 | spinner.replace(this.$el); 42 | }, 43 | 44 | render: function() { 45 | this.$el.html(_.template(localTemplate, { 46 | channels: this.channels, 47 | posts: this.model.posts.models 48 | })); 49 | this._render(); 50 | }, 51 | 52 | _redirectChannel: function(event) { 53 | var jid = this.$(event.currentTarget).parent().attr('id'); 54 | Events.trigger('navigate', jid); 55 | }, 56 | 57 | _redirectPost: function(event) { 58 | var jid = this.$(event.currentTarget).attr('id'); 59 | Events.trigger('navigate', jid); 60 | } 61 | }); 62 | 63 | return SearchView; 64 | }); 65 | -------------------------------------------------------------------------------- /js/app/views/sidebar/ActionBar.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var Backbone = require('backbone'); 19 | var l10nBrowser = require('l10n-browser'); 20 | var template = require('text!templates/sidebar/actionBar.html') 21 | var Events = Backbone.Events; 22 | var localTemplate; 23 | 24 | var ActionBar = Backbone.View.extend({ 25 | className: 'actionBar clearfix', 26 | events: {'click .discover': '_navigate'}, 27 | 28 | initialize: function() { 29 | if (!localTemplate) localTemplate = l10nBrowser.localiseHTML(template, {}); 30 | }, 31 | 32 | render: function() { 33 | this.$el.html(_.template(localTemplate)); 34 | 35 | return this; 36 | }, 37 | 38 | _navigate: function() { 39 | Events.trigger('navigate', 'explore'); 40 | } 41 | }); 42 | 43 | return ActionBar; 44 | }); 45 | -------------------------------------------------------------------------------- /js/app/views/sidebar/SidebarPage.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var ActionBar = require('views/sidebar/ActionBar'); 19 | var Backbone = require('backbone'); 20 | var Channels = require('views/sidebar/Channels'); 21 | var PersonalChannel = require('views/sidebar/PersonalChannel'); 22 | 23 | var SidebarPage = Backbone.View.extend({ 24 | className: 'sidebar hidden', 25 | 26 | initialize: function() { 27 | this.personalChannel = new PersonalChannel({model: this.model}); 28 | this.actionBar = new ActionBar(); 29 | this.channels = new Channels({model: this.model}); 30 | this.render(); 31 | }, 32 | 33 | render: function() { 34 | var $sidebar = $('.sidebar'); 35 | $sidebar.append(this.personalChannel.el); 36 | $sidebar.append(this.actionBar.render().el); 37 | $sidebar.append(this.channels.el); 38 | $sidebar.removeClass('hidden'); 39 | }, 40 | 41 | destroy: function() { 42 | $('.sidebar').addClass('hidden'); 43 | this.personalChannel.remove(); 44 | this.actionBar.remove(); 45 | this.channels.remove(); 46 | this.remove(); 47 | }, 48 | 49 | selectChannel: function(channel) { 50 | this.personalChannel.selectChannel(channel); 51 | this.channels.selectChannel(channel); 52 | }, 53 | 54 | unSelectChannel: function() { 55 | this.personalChannel.selectChannel(''); 56 | this.channels.selectChannel(''); 57 | } 58 | }); 59 | 60 | return SidebarPage; 61 | }); 62 | -------------------------------------------------------------------------------- /js/vendor/jasmine/MIT.LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008-2011 Pivotal Labs 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /js/vendor/jquery.cookie.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery Cookie Plugin v1.3.1 3 | * https://github.com/carhartl/jquery-cookie 4 | * 5 | * Copyright 2013 Klaus Hartl 6 | * Released under the MIT license 7 | */ 8 | (function (factory) { 9 | if (typeof define === 'function' && define.amd) { 10 | // AMD. Register as anonymous module. 11 | define(['jquery'], factory); 12 | } else { 13 | // Browser globals. 14 | factory(jQuery); 15 | } 16 | }(function ($) { 17 | 18 | var pluses = /\+/g; 19 | 20 | function raw(s) { 21 | return s; 22 | } 23 | 24 | function decoded(s) { 25 | return decodeURIComponent(s.replace(pluses, ' ')); 26 | } 27 | 28 | function converted(s) { 29 | if (s.indexOf('"') === 0) { 30 | // This is a quoted cookie as according to RFC2068, unescape 31 | s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); 32 | } 33 | try { 34 | return config.json ? JSON.parse(s) : s; 35 | } catch(er) {} 36 | } 37 | 38 | var config = $.cookie = function (key, value, options) { 39 | 40 | // write 41 | if (value !== undefined) { 42 | options = $.extend({}, config.defaults, options); 43 | 44 | if (typeof options.expires === 'number') { 45 | var days = options.expires, t = options.expires = new Date(); 46 | t.setDate(t.getDate() + days); 47 | } 48 | 49 | value = config.json ? JSON.stringify(value) : String(value); 50 | 51 | return (document.cookie = [ 52 | config.raw ? key : encodeURIComponent(key), 53 | '=', 54 | config.raw ? value : encodeURIComponent(value), 55 | options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE 56 | options.path ? '; path=' + options.path : '', 57 | options.domain ? '; domain=' + options.domain : '', 58 | options.secure ? '; secure' : '' 59 | ].join('')); 60 | } 61 | 62 | // read 63 | var decode = config.raw ? raw : decoded; 64 | var cookies = document.cookie.split('; '); 65 | var result = key ? undefined : {}; 66 | for (var i = 0, l = cookies.length; i < l; i++) { 67 | var parts = cookies[i].split('='); 68 | var name = decode(parts.shift()); 69 | var cookie = decode(parts.join('=')); 70 | 71 | if (key && key === name) { 72 | result = converted(cookie); 73 | break; 74 | } 75 | 76 | if (!key) { 77 | result[name] = converted(cookie); 78 | } 79 | } 80 | 81 | return result; 82 | }; 83 | 84 | config.defaults = {}; 85 | 86 | $.removeCookie = function (key, options) { 87 | if ($.cookie(key) !== undefined) { 88 | // Must not alter options, thus extending a fresh object... 89 | $.cookie(key, '', $.extend({}, options, { expires: -1 })); 90 | return true; 91 | } 92 | return false; 93 | }; 94 | 95 | })); -------------------------------------------------------------------------------- /locales/data.properties: -------------------------------------------------------------------------------- 1 | #encoding: UTF-8 2 | # entries in alphabetical order by locale. 3 | 4 | [*] 5 | @import url(data.en.properties) 6 | 7 | [de] 8 | @import url(data.de.properties) 9 | 10 | [es] 11 | @import url(data.es.properties) 12 | 13 | [fr] 14 | @import url(data.fr.properties) 15 | 16 | [fy] 17 | @import url(data.fy.properties) 18 | 19 | [id] 20 | @import url(data.id.properties) 21 | 22 | [nl-NL] 23 | @import url(data.nl-NL.properties) 24 | 25 | [pt] 26 | @import url(data.pt.properties) 27 | 28 | [sv] 29 | @import url(data.sv.properties) 30 | 31 | [tr-TR] 32 | @import url(data.tr-TR.properties) 33 | 34 | -------------------------------------------------------------------------------- /manifest.webapp: -------------------------------------------------------------------------------- 1 | { 2 | "name": "buddycloud", 3 | "description": "buddycloud is a distributed social network that let's you share, communicate and update those around you.", 4 | "launch_path": "/", 5 | "icons": { 6 | "128": "/img/firefox-manifest-icon-128.png" 7 | }, 8 | "developer": { 9 | "name": "Buddycloud Dev Team", 10 | "url": "http://buddycloud.org" 11 | }, 12 | "default_locale": "en" 13 | } 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "buddycloud-webclient", 3 | "version": "0.0.1", 4 | "author": "buddycloud", 5 | "description": "Web interface for the buddycloud network", 6 | "license": "Apache 2.0", 7 | "devDependencies": { 8 | "grunt": "~0.4.0", 9 | "grunt-cli": "~0.1.9", 10 | "grunt-contrib-connect": "~0.1.0", 11 | "grunt-contrib-watch": "~0.1.0", 12 | "gruntacular": "~0.1.0", 13 | "express": "~3.1.0" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git://github.com/buddycloud/buddycloud-webclient.git" 18 | }, 19 | "engine": { 20 | "node": ">=0.8" 21 | }, 22 | "dependencies": { 23 | "requirejs": "~2.1.6", 24 | "color": "~0.4.4" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /prototypes/Firefox OS mockups/Firefox OS screen mockup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/Firefox OS mockups/Firefox OS screen mockup.png -------------------------------------------------------------------------------- /prototypes/Firefox OS mockups/home-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/Firefox OS mockups/home-screen.png -------------------------------------------------------------------------------- /prototypes/Firefox OS mockups/posts-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/Firefox OS mockups/posts-screen.png -------------------------------------------------------------------------------- /prototypes/avatars/anon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/anon.png -------------------------------------------------------------------------------- /prototypes/avatars/channel1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/channel1.jpg -------------------------------------------------------------------------------- /prototypes/avatars/channel2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/channel2.jpg -------------------------------------------------------------------------------- /prototypes/avatars/channel3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/channel3.jpg -------------------------------------------------------------------------------- /prototypes/avatars/channel4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/channel4.jpg -------------------------------------------------------------------------------- /prototypes/avatars/channel5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/channel5.jpg -------------------------------------------------------------------------------- /prototypes/avatars/channel6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/channel6.jpg -------------------------------------------------------------------------------- /prototypes/avatars/channel7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/channel7.jpg -------------------------------------------------------------------------------- /prototypes/avatars/channel8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/channel8.jpg -------------------------------------------------------------------------------- /prototypes/avatars/topic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/topic.png -------------------------------------------------------------------------------- /prototypes/avatars/user1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/user1.jpg -------------------------------------------------------------------------------- /prototypes/avatars/user10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/user10.jpg -------------------------------------------------------------------------------- /prototypes/avatars/user11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/user11.jpg -------------------------------------------------------------------------------- /prototypes/avatars/user12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/user12.jpg -------------------------------------------------------------------------------- /prototypes/avatars/user13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/user13.jpg -------------------------------------------------------------------------------- /prototypes/avatars/user1_big.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/user1_big.jpg -------------------------------------------------------------------------------- /prototypes/avatars/user2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/user2.jpg -------------------------------------------------------------------------------- /prototypes/avatars/user2_big.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/user2_big.jpg -------------------------------------------------------------------------------- /prototypes/avatars/user3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/user3.jpg -------------------------------------------------------------------------------- /prototypes/avatars/user4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/user4.jpg -------------------------------------------------------------------------------- /prototypes/avatars/user5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/user5.jpg -------------------------------------------------------------------------------- /prototypes/avatars/user6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/user6.jpg -------------------------------------------------------------------------------- /prototypes/avatars/user7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/user7.jpg -------------------------------------------------------------------------------- /prototypes/avatars/user8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/user8.jpg -------------------------------------------------------------------------------- /prototypes/avatars/user9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/webclient/d5e364660c2f378e7d19ac943d74f69ac59aa47e/prototypes/avatars/user9.jpg -------------------------------------------------------------------------------- /prototypes/loading.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Welcome Page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 |
18 |
19 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /spec/Channel.spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var Channel = require('models/Channel'); 19 | var ChannelFollowers = require('models/ChannelFollowers'); 20 | var SimilarChannels = require('models/SimilarChannels'); 21 | 22 | describe('Channel', function() { 23 | var channel; 24 | 25 | beforeEach(function() { 26 | channel = new Channel('eve@example.com'); 27 | }); 28 | 29 | it('should initialize submodels correctly', function() { 30 | expect(channel.name).toBe('eve@example.com'); 31 | expect(channel.followers instanceof ChannelFollowers).toBeTruthy(); 32 | expect(channel.similarChannels instanceof SimilarChannels).toBeTruthy(); 33 | expect(channel.followers.channel).toBe(channel.name); 34 | expect(channel.similarChannels.channel).toBe(channel.name); 35 | }); 36 | 37 | describe('fetch()', function() { 38 | it('should call fetch() on each submodel', function() { 39 | spyOn(channel.followers, 'fetch'); 40 | spyOn(channel.similarChannels, 'fetch'); 41 | channel.fetch(); 42 | expect(channel.followers.fetch).toHaveBeenCalled(); 43 | expect(channel.similarChannels.fetch).toHaveBeenCalled(); 44 | }); 45 | 46 | it('should trigger "fetch" if all submodels are fetched', function() { 47 | spyOn(channel.followers, 'fetch').andCallFake(function(options) { 48 | options.complete(channel.followers); 49 | }); 50 | spyOn(channel.similarChannels, 'fetch').andCallFake(function(options) { 51 | options.complete(channel.similarChannels); 52 | }); 53 | spyOn(channel, 'trigger'); 54 | channel.fetch(); 55 | expect(channel.trigger).toHaveBeenCalledWith('fetch'); 56 | }); 57 | }); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /spec/ChannelMetadata.spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var ChannelMetadata = require('models/ChannelMetadata'); 19 | 20 | describe('ChannelMetadata', function() { 21 | var metadata; 22 | 23 | beforeEach(function() { 24 | metadata = new ChannelMetadata('alice@example.com'); 25 | metadata.set({ 26 | title: 'Alice', 27 | description: 'Your favorite persona', 28 | creation_date: '2012-01-01', 29 | channel_type: 'personal', 30 | access_model: 'authorize' 31 | }); 32 | }); 33 | 34 | it('should initialize correctly', function() { 35 | expect(metadata.channel).toBe('alice@example.com'); 36 | }); 37 | 38 | it('should have URL //metadata/posts', function() { 39 | var url = 'https://example.com/alice@example.com/metadata/posts'; 40 | expect(metadata.url()).toBe(url); 41 | }); 42 | 43 | it('should have a function for each metadata attribute', function() { 44 | expect(metadata.title()).toBe('Alice'); 45 | expect(metadata.description()).toBe('Your favorite persona'); 46 | expect(metadata.creationDate()).toBe('2012-01-01'); 47 | expect(metadata.channelType()).toBe('personal'); 48 | expect(metadata.accessModel()).toBe('authorize'); 49 | }); 50 | 51 | describe('avatarUrl()', function() { 52 | it('should return //media/avatar', function() { 53 | var url = 'https://example.com/alice@example.com/media/avatar'; 54 | expect(metadata.avatarUrl()).toBe(url); 55 | }); 56 | }); 57 | }); 58 | 59 | }); 60 | -------------------------------------------------------------------------------- /spec/Discover.spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var DiscoverCollection = require('models/DiscoverCollection'); 19 | var Discover = require('models/Discover'); 20 | 21 | describe('Discover', function() { 22 | var discover; 23 | 24 | beforeEach(function() { 25 | discover = new Discover('alice@example.com'); 26 | }); 27 | 28 | it('should initialize submodels correctly', function() { 29 | expect(discover.mostActive instanceof DiscoverCollection).toBeTruthy(); 30 | expect(discover.recommendations instanceof DiscoverCollection).toBeTruthy(); 31 | }); 32 | 33 | describe('doDiscover()', function() { 34 | it('should call fetch() on each submodel', function() { 35 | spyOn(discover.mostActive, 'fetch'); 36 | spyOn(discover.recommendations, 'fetch'); 37 | discover.doDiscover(); 38 | expect(discover.mostActive.fetch).toHaveBeenCalled(); 39 | expect(discover.recommendations.fetch).toHaveBeenCalled(); 40 | }); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /spec/PostNotifications.spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var PostNotifications = require('models/PostNotifications'); 19 | 20 | describe('PostNotifications', function() { 21 | var notifications; 22 | 23 | beforeEach(function() { 24 | notifications = new PostNotifications; 25 | }); 26 | 27 | it('should have URL /notifications/posts', function() { 28 | var url = 'https://example.com/notifications/posts'; 29 | expect(notifications.url()).toBe(url); 30 | }); 31 | 32 | describe('fetch()', function() { 33 | it('should include "Accept: application/json" header', function() { 34 | spyOn(notifications._request, 'on').andCallFake(function(e, f) { 35 | expect(e).toBe('finished'); 36 | }); 37 | spyOn(notifications._request, 'start').andCallFake(function(method, url, headers) { 38 | expect(method).toBe('GET'); 39 | expect(url).toBe('https://example.com/notifications/posts'); 40 | expect(headers['Accept']).toBe('application/json'); 41 | }); 42 | notifications.fetch(); 43 | }); 44 | }); 45 | }); 46 | 47 | }); 48 | -------------------------------------------------------------------------------- /spec/Preferences.spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var Preferences = require('models/Preferences'); 19 | 20 | describe('Preferences', function() { 21 | var preferences; 22 | 23 | beforeEach(function() { 24 | preferences = new Preferences(); 25 | preferences.set({ 26 | 'target': 'alice@example.com', 27 | 'postAfterMe': 'true', 28 | 'postMentionedMe': 'false', 29 | 'postOnMyChannel': 'false', 30 | 'postOnSubscribedChannel': 'false', 31 | 'followMyChannel': 'true', 32 | 'followRequest': 'true' 33 | }); 34 | }); 35 | 36 | it('should have URL /notifications', function() { 37 | var url = 'https://example.com/notifications'; 38 | expect(preferences.url()).toBe(url); 39 | }); 40 | 41 | describe('email()', function() { 42 | it('should return user email', function() { 43 | var email = preferences.target(); 44 | expect(email).toBe('alice@example.com'); 45 | }); 46 | }); 47 | 48 | describe('newFollowers()', function() { 49 | it('should return correct boolean value', function() { 50 | var newFollowers = preferences.newFollowers(); 51 | expect(newFollowers).toBe(true); 52 | }); 53 | }); 54 | 55 | describe('mentions()', function() { 56 | it('should return correct boolean value', function() { 57 | var mentions = preferences.mentions(); 58 | expect(mentions).toBe(false); 59 | }); 60 | }); 61 | 62 | describe('ownChannel()', function() { 63 | it('should return correct boolean value', function() { 64 | var ownChannel = preferences.ownChannel(); 65 | expect(ownChannel).toBe(false); 66 | }); 67 | }); 68 | 69 | describe('followedChannels()', function() { 70 | it('should return correct boolean value', function() { 71 | var followedChannels = preferences.followedChannels(); 72 | expect(followedChannels).toBe(false); 73 | }); 74 | }); 75 | 76 | describe('threads()', function() { 77 | it('should return correct boolean value', function() { 78 | var threads = preferences.threads(); 79 | expect(threads).toBe(true); 80 | }); 81 | }); 82 | }); 83 | 84 | }); 85 | -------------------------------------------------------------------------------- /spec/Search.spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var MetadataSearch = require('models/MetadataSearch'); 19 | var ContentSearch = require('models/ContentSearch'); 20 | var Search = require('models/Search'); 21 | 22 | describe('Search', function() { 23 | var search; 24 | 25 | beforeEach(function() { 26 | search = new Search(); 27 | }); 28 | 29 | it('should initialize submodels correctly', function() { 30 | expect(search.channels instanceof MetadataSearch).toBeTruthy(); 31 | expect(search.posts instanceof ContentSearch).toBeTruthy(); 32 | }); 33 | 34 | describe('doSearch()', function() { 35 | it('should call doSearch() on each submodel', function() { 36 | spyOn(search.channels, 'doSearch'); 37 | spyOn(search.posts, 'doSearch'); 38 | search.doSearch({q: 'test'}); 39 | expect(search.channels.doSearch).toHaveBeenCalled(); 40 | expect(search.posts.doSearch).toHaveBeenCalled(); 41 | }); 42 | 43 | it('should trigger "fetch" if all submodels are fetched', function() { 44 | spyOn(search.channels, 'fetch').andCallFake(function(options) { 45 | options.success(search.channels); 46 | }); 47 | spyOn(search.posts, 'fetch').andCallFake(function(options) { 48 | options.success(search.posts); 49 | }); 50 | spyOn(search, 'trigger'); 51 | search.doSearch({q: 'test'}); 52 | expect(search.trigger).toHaveBeenCalledWith('fetch'); 53 | }); 54 | }); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /spec/SimilarChannels.spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var SimilarChannels = require('models/SimilarChannels'); 19 | var MetadataSearchResult = require('models/MetadataSearchResult'); 20 | 21 | describe('SimilarChannels', function() { 22 | var similarChannels; 23 | var similarChannelsData = [{ 24 | 'jid': 'alice@example.com', 25 | 'title': 'Alice\'s channel', 26 | 'channelType': 'personal', 27 | 'description': 'Alice\'s channel description', 28 | 'published': '2012-01-01', 29 | 'defaultAffiliation': 'member' 30 | }, { 31 | 'jid': 'bob@example.com', 32 | 'title': 'Bob\'s channel', 33 | 'channelType': 'personal', 34 | 'description': 'Bob\'s channel description', 35 | 'published': '2012-01-02', 36 | 'defaultAffiliation': 'publisher' 37 | }, { 38 | 'jid': 'lounge@topics.example.com', 39 | 'title': 'Lounge channel', 40 | 'channelType': 'topic', 41 | 'description': 'Lounge channel description', 42 | 'published': '2012-01-03', 43 | 'defaultAffiliation': 'publisher' 44 | }]; 45 | 46 | beforeEach(function() { 47 | similarChannels = new SimilarChannels('ron@example.com'); 48 | similarChannels.reset(similarChannels.parse(similarChannelsData)); 49 | }); 50 | 51 | it('should initialize correctly', function() { 52 | expect(similarChannels.channel).toBe('ron@example.com'); 53 | }); 54 | 55 | it('should have URL //similar', function() { 56 | var url = 'https://example.com/ron@example.com/similar'; 57 | expect(similarChannels.url()).toBe(url); 58 | }); 59 | 60 | describe('usernames()', function() { 61 | it('should return similar channels jids', function() { 62 | var usernames = similarChannels.usernames(); 63 | expect(usernames.length).toBe(3); 64 | expect(usernames).toContain('alice@example.com'); 65 | expect(usernames).toContain('bob@example.com'); 66 | expect(usernames).toContain('lounge@topics.example.com'); 67 | }); 68 | }); 69 | }); 70 | 71 | }); 72 | -------------------------------------------------------------------------------- /spec/SubscribedChannels.spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var SubscribedChannels = require('models/SubscribedChannels'); 19 | 20 | describe('SubscribedChannels', function() { 21 | var subscribedChannels; 22 | 23 | beforeEach(function() { 24 | subscribedChannels = new SubscribedChannels(); 25 | subscribedChannels.set({ 26 | 'alice@example.com/posts': 'owner', 27 | 'alice@example.com/status': 'owner', 28 | 'bob@example.com/geo': 'publisher', 29 | 'ron@example.com/status': 'publisher', 30 | 'alice@example2.com/posts': 'member' 31 | }); 32 | }); 33 | 34 | it('should have URL //subscribers/', function() { 35 | var url = 'https://example.com/subscribed'; 36 | expect(subscribedChannels.url()).toBe(url); 37 | }); 38 | 39 | describe('channels()', function() { 40 | it('should return subscribedChannels without affiliations', function() { 41 | var channels = subscribedChannels.channels(); 42 | expect(channels.length).toBe(4); 43 | expect(channels).toContain('alice@example.com'); 44 | expect(channels).toContain('alice@example2.com'); 45 | expect(channels).toContain('bob@example.com'); 46 | expect(channels).toContain('ron@example.com'); 47 | }); 48 | }); 49 | 50 | describe('parse()', function() { 51 | it('should return only the channels that refers to posts node', function() { 52 | var fixedChannels = subscribedChannels.parse({ 53 | 'alice@example.com/posts': 'owner', 54 | 'alice@example.com/status': 'owner', 55 | 'bob@example.com/geo': 'publisher', 56 | 'ron@example.com/status': 'publisher', 57 | 'alice@example2.com/posts': 'member' 58 | }); 59 | expect(fixedChannels['alice@example.com/posts']).toBe('owner'); 60 | expect(fixedChannels['alice@example.com/statis']).toBe(undefined); 61 | expect(fixedChannels['bob@example.com/geo']).toBe(undefined); 62 | expect(fixedChannels['ron@example.com/status']).toBe(undefined); 63 | expect(fixedChannels['alice@example2.com/posts']).toBe('member'); 64 | }); 65 | }); 66 | }); 67 | 68 | }); 69 | -------------------------------------------------------------------------------- /spec/config.js: -------------------------------------------------------------------------------- 1 | define({ 2 | baseUrl: 'https://example.com', 3 | homeDomain: 'example.com' 4 | }); 5 | -------------------------------------------------------------------------------- /spec/runner.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | requirejs.config({ 18 | baseUrl: 'js/vendor', 19 | paths: { 20 | 'config': '../../spec/config', 21 | 'spec': '../../spec', 22 | 'models': '../app/models', 23 | 'util': '../app/util' 24 | } 25 | }); 26 | 27 | define(function(require) { 28 | require('spec/Channel.spec'); 29 | require('spec/ChannelFollowers.spec'); 30 | require('spec/ChannelItems.spec'); 31 | require('spec/ChannelMetadata.spec'); 32 | require('spec/Discover.spec'); 33 | require('spec/Item.spec'); 34 | require('spec/PostNotifications.spec'); 35 | require('spec/Search.spec'); 36 | require('spec/SimilarChannels.spec'); 37 | require('spec/SubscribedChannels.spec'); 38 | require('spec/User.spec'); 39 | require('spec/UserCredentials.spec'); 40 | require('spec/util_api.spec'); 41 | require('spec/util_avatarFallback.spec'); 42 | require('spec/util_linkify.spec'); 43 | 44 | jasmine.getEnv().addReporter(new jasmine.HtmlReporter()); 45 | jasmine.getEnv().execute(); 46 | }); 47 | -------------------------------------------------------------------------------- /spec/testacular-runner.js: -------------------------------------------------------------------------------- 1 | requirejs.config({ 2 | baseUrl: '/base/js/vendor', 3 | paths: { 4 | 'config': '../../spec/config', 5 | 'spec': '../../spec', 6 | 'models': '../app/models', 7 | 'util': '../app/util' 8 | } 9 | }); 10 | 11 | require([ 12 | 'spec/Item.spec', 13 | 'spec/Channel.spec', 14 | 'spec/ChannelFollowers.spec', 15 | 'spec/ChannelItems.spec', 16 | 'spec/ChannelMetadata.spec', 17 | 'spec/Discover.spec', 18 | 'spec/Item.spec', 19 | 'spec/PostNotifications.spec', 20 | 'spec/Search.spec', 21 | 'spec/SimilarChannels.spec', 22 | 'spec/SubscribedChannels.spec', 23 | 'spec/User.spec', 24 | 'spec/UserCredentials.spec', 25 | 'spec/util_api.spec', 26 | 'spec/util_avatarFallback.spec', 27 | 'spec/util_linkify.spec' 28 | ], function() { 29 | window.__testacular__.start(); 30 | }); 31 | 32 | -------------------------------------------------------------------------------- /spec/util_api.spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var api = require('util/api'); 19 | 20 | describe('util/api', function() { 21 | describe('url()', function() { 22 | it('should return the API root plus supplied components', function() { 23 | var url = api.url('foo', 'bar', 'baz'); 24 | expect(url).toBe('https://example.com/foo/bar/baz'); 25 | }); 26 | }); 27 | 28 | describe('rootUrl()', function() { 29 | it('should return the API root with trailing slash', function() { 30 | expect(api.rootUrl()).toBe('https://example.com/'); 31 | }); 32 | }); 33 | 34 | describe('avatarUrl()', function() { 35 | it('should return the URL for a channel\'s avatar', function() { 36 | var url = api.avatarUrl('alice@example.com'); 37 | expect(url).toBe('https://example.com/alice@example.com/media/avatar'); 38 | }); 39 | }); 40 | }); 41 | 42 | }); 43 | -------------------------------------------------------------------------------- /spec/util_avatarFallback.spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | define(function(require) { 18 | var $ = require('jquery'); 19 | var avatarFallback = require('util/avatarFallback'); 20 | 21 | describe('util/avatarFallback', function() { 22 | var image; 23 | 24 | beforeEach(function() { 25 | image = new Image; 26 | }); 27 | 28 | it('should set fallback URL on image if avatar fails to load', function() { 29 | avatarFallback(image, 'personal', 50); 30 | $(image).trigger('error'); 31 | expect(image.src).toContain('img/personal-50px.jpg'); 32 | }); 33 | 34 | it('should not change the passed image before an "error" event', function() { 35 | avatarFallback(image, 'personal', 50); 36 | expect(image.src).toBe(''); 37 | }); 38 | 39 | it('should accept jQuery selectors', function() { 40 | var image2 = new Image; 41 | avatarFallback($([image, image2]), 'topic', 75); 42 | $(image).trigger('error'); 43 | $(image2).trigger('error'); 44 | expect(image.src).toContain('img/topic-75px.jpg'); 45 | expect(image2.src).toContain('img/topic-75px.jpg'); 46 | }); 47 | }); 48 | 49 | }); 50 | -------------------------------------------------------------------------------- /templates/content/anonBar.html: -------------------------------------------------------------------------------- 1 | 6 | 11 | -------------------------------------------------------------------------------- /templates/content/anonOverlay.html: -------------------------------------------------------------------------------- 1 | 36 | -------------------------------------------------------------------------------- /templates/content/channelDetails.html: -------------------------------------------------------------------------------- 1 |
2 |

Info

3 | 4 |
5 |
6 |
7 |

8 | Channel address: 9 | <%= metadata.channel %> 10 |

11 |

12 | This channel is: 13 | 14 |

15 |

16 | Broadcasting for: 17 | 18 |

19 |
20 |
21 | -------------------------------------------------------------------------------- /templates/content/channelList.html: -------------------------------------------------------------------------------- 1 |

<%- title %> <%= channels.length %>

2 |
3 | <% _.each(channels, function(channel, index) { %> 4 | <% if (index < 8) { %> 5 | 6 | <% } else { %> 7 | 8 | <% } %> 9 | <% }); %> 10 | <% if (channels.length > 8) { %> 11 |
show all
12 | <% } %> 13 |
14 | -------------------------------------------------------------------------------- /templates/content/channelListDetails.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

<%- channel %>

6 |
<%- role %>
7 |
8 |
9 |
Change role
10 | 16 |
17 |
18 |
    19 |
  • read your channel
  • 20 |
  • write comments & messages
  • 21 |
  • post new topics
  • 22 |
  • approve new followers
  • 23 |
  • set user's roles
  • 24 |
  • delete posts
  • 25 |
26 |
27 |
28 |
    29 |
  • read your channel
  • 30 |
  • write comments & messages
  • 31 |
  • post new topics
  • 32 |
33 |
34 |
35 |
    36 |
  • read your channel
  • 37 |
38 |
39 |
40 |
41 |
42 |
Ban User
43 |
44 |
    45 |
  • Remove channel access.
  • 46 |
47 |
48 |
49 |
50 |
Change role
51 |
Ban User
52 |
53 |
54 |
Cancel
55 |
Ok
56 |
57 |
58 |
Change role
59 |
60 |
61 |
62 | -------------------------------------------------------------------------------- /templates/content/createChannel.html: -------------------------------------------------------------------------------- 1 |
2 |

Create Channel

3 | 10 |
11 | 12 | 13 |
14 |
15 | 16 | 17 |
18 |
19 | 20 | 21 |
22 |
23 | 24 |
25 | 26 |
27 |
28 |
29 | 30 |
31 | 35 | 36 | 37 | can only read your channel 38 | 39 | 40 | can read and write your channel 41 | 42 | 43 |
44 |
45 |
46 | 50 | powered by buddycloud 51 | -------------------------------------------------------------------------------- /templates/content/editChannel.html: -------------------------------------------------------------------------------- 1 |
2 |

Edit channel

3 | 10 |
11 | 12 | 13 |
14 |
15 | 16 | 17 |
18 | 24 | 30 |
31 | 32 |
33 | 34 |
35 |
36 |
37 | 38 |
39 | 43 | 44 | 45 | can read and post messages in your channel 46 | 47 | 48 | can only read your channel 49 | 50 | 51 |
52 |
53 |
54 | 62 | -------------------------------------------------------------------------------- /templates/content/embed.html: -------------------------------------------------------------------------------- 1 |
2 | <% if (title) { %> 3 | <%- title %> 4 | <% } 5 | if (html) { print(html); } 6 | else if (img) { %> 7 | 8 | alt="<%- description %>"<% } %>/> 10 | 11 | <% } 12 | if (description && description.length > 0) { %> 13 |
<%- description %>
14 | <% } %> 15 |
16 | -------------------------------------------------------------------------------- /templates/content/error.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

<%- domain %> API server error.

4 | <% if (error.status && error.statusText) { %> 5 |

<%- error.status %> - <%- error.statusText %>

6 | <% } %> 7 |
8 |
-------------------------------------------------------------------------------- /templates/content/forbidden.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Forbidden

4 |
5 |
-------------------------------------------------------------------------------- /templates/content/header.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 |
6 |

<%- metadata.title() %>

7 |
<%- metadata.description() %>
8 |
9 | 13 | powered by buddycloud 14 | -------------------------------------------------------------------------------- /templates/content/mediaFallback.html: -------------------------------------------------------------------------------- 1 |
2 | <% if (title) { %> 3 | <%- title %> 4 | <% } %> 5 | <% if (img) { %> 6 | 7 | 8 | 9 | <% } %> 10 |
11 | -------------------------------------------------------------------------------- /templates/content/notification.html: -------------------------------------------------------------------------------- 1 | <% _.each(pending, function(obj, index) { %> 2 |
3 |
4 | 5 |
6 | <%- obj.jid.split('@')[0] %>@<%- obj.jid.split('@')[1] %> 7 |

wants to follow this channel.

8 | started following this channel. 9 | was denied to follow this channel. 10 |
11 |
12 |
Deny
13 |
Allow
14 |
15 |
16 |
17 | <% }); %> -------------------------------------------------------------------------------- /templates/content/preferences.html: -------------------------------------------------------------------------------- 1 |
2 |

Preferences

3 | 7 |
8 | 9 |
10 |
11 | 12 | 13 |
14 |
15 | 16 | 17 |
18 |
19 | 20 | 21 |
22 |
23 | 24 | 25 |
26 |
27 | 28 | 29 |
30 |
31 |
32 |
33 | 41 | -------------------------------------------------------------------------------- /templates/content/private.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

This channel is private. You have to follow it to see it's content.

5 |
6 |
7 |
8 |
9 |
10 | 13 | 14 |

15 |
16 |
17 |
18 |
19 | 22 | 23 |

24 |
25 |
26 |
27 | 30 | 31 |

32 |
33 |
34 |
35 | 38 | 39 |

40 |
41 |
42 |
43 | 46 | 47 |

48 |
49 |
50 |
51 |
-------------------------------------------------------------------------------- /templates/content/searchBar.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | -------------------------------------------------------------------------------- /templates/content/searchResults.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Channels

5 | <% if (channels) { %> 6 |
7 | <% _.each(channels, function(channel) { %> 8 |
9 | 10 |
11 | <%- channel.title() %> 12 | <%- channel.description() %> 13 |
14 | 15 |
16 | <% }); %> 17 |
18 | <% } %> 19 |
20 |
21 |

Posts

22 | <% if (posts) { %> 23 |
24 | <% _.each(posts, function(post) { %> 25 |
26 |
27 | 28 |
29 |
<%- post.author().split('@', 2)[0] %>
30 |
<%- post.author() %>
31 |
32 | 35 |

<%- post.content() %>

36 |
37 |
38 | <% }); %> 39 |
40 | <% } %> 41 |
42 |
43 |
44 | -------------------------------------------------------------------------------- /templates/content/stream.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |

6 |
7 |
8 | 9 |
10 |
11 |
ctrl + enter to post or drop a file here
12 |
Post
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |

22 | 23 |

24 | -------------------------------------------------------------------------------- /templates/overlay/discover.html: -------------------------------------------------------------------------------- 1 |
2 |

Local

3 |
4 |

Most active

5 | <% if (localMostActive) { %> 6 |
7 | <% _.each(localMostActive, function(channel) { %> 8 |
9 | 10 |
11 | <%- channel.title() %> 12 | <%- channel.description() %> 13 |
14 |
15 | <% }); %> 16 |
17 | <% } %> 18 |
19 | 35 |
36 |
37 |

Global

38 |
39 |

Most Active

40 | <% if (mostActive) { %> 41 |
42 | <% _.each(mostActive, function(channel) { %> 43 |
44 | 45 |
46 | <%- channel.title() %> 47 | <%- channel.description() %> 48 |
49 |
50 | <% }); %> 51 |
52 | <% } %> 53 |
54 | 70 |
71 | 72 | -------------------------------------------------------------------------------- /templates/overlay/footer.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | buddycloud is Open Source and built by amazing people from all over the world. 4 |

5 |
6 | -------------------------------------------------------------------------------- /templates/overlay/loading.html: -------------------------------------------------------------------------------- 1 |
2 |

loading <%= jid %>...

3 |
4 | -------------------------------------------------------------------------------- /templates/overlay/welcome.html: -------------------------------------------------------------------------------- 1 |
2 | 6 |
7 | 11 |
12 |
13 |
14 | 15 | 16 |
17 |
18 | 19 | 20 |
21 | 25 |
26 |

27 | 28 |
29 | 47 |
48 |
49 |
50 |

buddycloud

51 |

Share, discover and communicate.

52 |
53 | -------------------------------------------------------------------------------- /templates/sidebar/actionBar.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /templates/sidebar/channel.html: -------------------------------------------------------------------------------- 1 |
3 |
4 | 6 | 7 |
8 |
9 | <%- metadata.title() %> 10 | <%- metadata.description() || '' %> 11 |
12 |
13 | -------------------------------------------------------------------------------- /templates/sidebar/channels.html: -------------------------------------------------------------------------------- 1 |
2 | <% _.each(metadatas, function(metadata, index) { %> 3 |
5 |
6 | 8 | 9 | 10 |
11 |
12 | <%- metadata.title() %> 13 | <%- metadata.description() %> 14 |
15 |
16 | <% }); %> 17 |
18 | -------------------------------------------------------------------------------- /templates/sidebar/personalChannel.html: -------------------------------------------------------------------------------- 1 | 13 |
14 | 24 |
25 | -------------------------------------------------------------------------------- /test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | buddycloud webclient specs 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /testacular.conf.js: -------------------------------------------------------------------------------- 1 | // Testacular configuration 2 | // Generated on Wed Jan 09 2013 12:38:39 GMT+0100 (CET) 3 | 4 | 5 | // base path, that will be used to resolve files and exclude 6 | basePath = ''; 7 | 8 | 9 | // list of files / patterns to load in the browser 10 | files = [ 11 | JASMINE, 12 | JASMINE_ADAPTER, 13 | REQUIRE, 14 | REQUIRE_ADAPTER, 15 | {pattern: 'js/**/*.js', included: false}, 16 | {pattern: 'spec/config.js', included: false}, 17 | {pattern: 'spec/*.spec.js', included: false}, 18 | 'spec/testacular-runner.js' 19 | ]; 20 | 21 | 22 | // list of files to exclude 23 | exclude = [ 24 | 'js/app/main.js', 25 | 'spec/runner.js' 26 | ]; 27 | 28 | 29 | // test results reporter to use 30 | // possible values: 'dots', 'progress', 'junit' 31 | reporters = ['progress']; 32 | 33 | 34 | // web server port 35 | port = 8080; 36 | 37 | 38 | // cli runner port 39 | runnerPort = 9100; 40 | 41 | 42 | // enable / disable colors in the output (reporters and logs) 43 | colors = true; 44 | 45 | 46 | // level of logging 47 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 48 | logLevel = LOG_INFO; 49 | 50 | 51 | // enable / disable watching file and executing tests whenever any file changes 52 | autoWatch = true; 53 | 54 | 55 | // Start these browsers, currently available: 56 | // - Chrome 57 | // - ChromeCanary 58 | // - Firefox 59 | // - Opera 60 | // - Safari (only Mac) 61 | // - PhantomJS 62 | // - IE (only Windows) 63 | browsers = ['PhantomJS']; 64 | 65 | 66 | // If browser does not capture in given timeout [ms], kill it 67 | captureTimeout = 5000; 68 | 69 | 70 | // Continuous Integration mode 71 | // if true, it capture browsers, run tests and exit 72 | singleRun = false; 73 | --------------------------------------------------------------------------------