Merge branch 'release/0.5.0.0-RC'

This commit is contained in:
Jonne Haß 2015-05-03 15:45:49 +02:00
commit f8b3836d1f
1336 changed files with 31612 additions and 34450 deletions

17
.editorconfig Normal file
View file

@ -0,0 +1,17 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = space
indent_size = 2
[{Gemfile,Rakefile,Guardfile,Procfile}]
trim_trailing_whitespace = true
[*.{js,hbs,rb,rake,ru,erb,haml,scss,sh,md}]
trim_trailing_whitespace = true
[*.yml]
trim_trailing_whitespace = false

View file

@ -1,2 +1,2 @@
port: 3000
formation: web=1,sidekiq=0
formation: xmpp=0,web=1,sidekiq=0

14
.gitignore vendored
View file

@ -1,3 +1,7 @@
# xmpp certificates, keys and user data
config/vines/*.crt
config/vines/*.key
#trademark sillyness
app/views/home/_show.*
app/views/terms/terms.*
@ -10,6 +14,7 @@ config/heroku.yml
config/initializers/secret_token.rb
config/redis.conf
config/deploy_config.yml
config/schedule.rb
.bundle
vendor/bundle/
vendor/cache/
@ -29,6 +34,11 @@ spec/fixtures/*.y*ml
spec/fixtures/*.fixture.*
coverage/
xml_locales/
public/404.html
public/422.html
public/500.html
# Sprites
app/assets/images/branding-*.png
app/assets/images/icons-*.png
app/assets/images/social_media_logos-*.png
@ -58,7 +68,6 @@ tmp/
*.swp
*~
*#
bin/*
nbproject
patches-*
capybara-*.html
@ -69,3 +78,6 @@ dump.rdb
#IDE
diaspora.iml
# Dolphin's directory's preferences files
*.directory

9
.hound.yml Normal file
View file

@ -0,0 +1,9 @@
java_script:
enabled: true
config_file: config/.jshint.json
ignore_file: config/.jshint_ignore
ruby:
enabled: true
config_file: .rubocop.yml
scss:
enabled: false

1
.jshintignore Symbolic link
View file

@ -0,0 +1 @@
config/.jshint_ignore

1
.jshintrc Symbolic link
View file

@ -0,0 +1 @@
config/.jshint.json

15
.pairs
View file

@ -1,15 +0,0 @@
pairs:
dg: Daniel Grippi; daniel
rs: Raphael Sofaer; raphael
iz: Ilya Zhitomirskiy; ilya
ms: Maxwell Salzberg; maxwell
dh: Dan Hansen; ohaibbq
sm: Sarah Mei; sarah
mjs: Michael Sofaer; michael
jd: Jeff Dickey; dickeytk
dc: Dennis Collinson
tf: Tim Frazer
kf: Kevin Fitzpatrick
email:
prefix: pair
domain: joindiaspora.com

View file

@ -1 +0,0 @@
export NEW_HOTNESS=yessir

4
.powrc
View file

@ -1,4 +0,0 @@
if [ -f "$rvm_path/scripts/rvm" ] && [ -f ".rvmrc" ]; then
source "$rvm_path/scripts/rvm"
source ".rvmrc"
fi

1
.rspec
View file

@ -3,4 +3,3 @@
--color
--tag ~performance
--order random
--drb

130
.rubocop.yml Normal file
View file

@ -0,0 +1,130 @@
AllCops:
RunRailsCops: true
# Commonly used screens these days easily fit more than 80 characters.
Metrics/LineLength:
Max: 120
# Too short methods lead to extraction of single-use methods, which can make
# the code easier to read (by naming things), but can also clutter the class
Metrics/MethodLength:
Max: 20
# The guiding principle of classes is SRP, SRP can't be accurately measured by LoC
Metrics/ClassLength:
Max: 1500
# No space makes the method definition shorter and differentiates
# from a regular assignment.
Style/SpaceAroundEqualsInParameterDefault:
EnforcedStyle: no_space
# Single quotes being faster is hardly measurable and only affects parse time.
# Enforcing double quotes reduces the times where you need to change them
# when introducing an interpolation. Use single quotes only if their semantics
# are needed.
Style/StringLiterals:
EnforcedStyle: double_quotes
# We do not need to support Ruby 1.9, so this is good to use.
Style/SymbolArray:
Enabled: true
# Most readable form.
Style/AlignHash:
EnforcedHashRocketStyle: table
EnforcedColonStyle: table
# Mixing the styles looks just silly.
# REVIEW: Enable once https://github.com/bbatsov/rubocop/commit/760ce1ed2cf10beda5e163f934c03a6fb6daa38e
# is released.
#Style/HashSyntax:
# EnforcedStyle: ruby19_no_mixed_keys
# has_key? and has_value? are far more readable than key? and value?
Style/DeprecatedHashMethods:
Enabled: false
# String#% is by far the least verbose and only object oriented variant.
Style/FormatString:
EnforcedStyle: percent
Style/CollectionMethods:
Enabled: true
PreferredMethods:
# inject seems more common in the community.
reduce: "inject"
# Either allow this style or don't. Marking it as safe with parenthesis
# is silly. Let's try to live without them for now.
Style/ParenthesesAroundCondition:
AllowSafeAssignment: false
Lint/AssignmentInCondition:
AllowSafeAssignment: false
# A specialized exception class will take one or more arguments and construct the message from it.
# So both variants make sense.
Style/RaiseArgs:
Enabled: false
# Fail is an alias of raise. Avoid aliases, it's more cognitive load for no gain.
# The argument that fail should be used to abort the program is wrong too,
# there's Kernel#abort for that.
Style/SignalException:
EnforcedStyle: only_raise
# Suppressing exceptions can be perfectly fine, and be it to avoid to
# explicitly type nil into the rescue since that's what you want to return,
# or suppressing LoadError for optional dependencies
Lint/HandleExceptions:
Enabled: false
Style/SpaceInsideBlockBraces:
# The space here provides no real gain in readability while consuming
# horizontal space that could be used for a better parameter name.
# Also {| differentiates better from a hash than { | does.
SpaceBeforeBlockParameters: false
# No trailing space differentiates better from the block:
# foo} means hash, foo } means block.
Style/SpaceInsideHashLiteralBraces:
EnforcedStyle: no_space
# { ... } for multi-line blocks is okay, follow Weirichs rule instead:
# https://web.archive.org/web/20140221124509/http://onestepback.org/index.cgi/Tech/Ruby/BraceVsDoEnd.rdoc
Style/Blocks:
Enabled: false
# do / end blocks should be used for side effects,
# methods that run a block for side effects and have
# a useful return value are rare, assign the return
# value to a local variable for those cases.
Style/MethodCalledOnDoEndBlock:
Enabled: true
# Enforcing the names of variables? To single letter ones? Just no.
Style/SingleLineBlockParams:
Enabled: false
# Shadowing outer local variables with block parameters is often useful
# to not reinvent a new name for the same thing, it highlights the relation
# between the outer variable and the parameter. The cases where it's actually
# confusing are rare, and usually bad for other reasons already, for example
# because the method is too long.
Lint/ShadowingOuterLocalVariable:
Enabled: false
# Check with yard instead.
Style/Documentation:
Enabled: false
# This is just silly. Calling the argument `other` in all cases makes no sense.
Style/OpMethod:
Enabled: false
# There are valid cases, for example debugging Cucumber steps,
# also they'll fail CI anyway
Lint/Debugger:
Enabled: false

View file

@ -1 +1 @@
2.0
2.1

View file

@ -1,12 +1,8 @@
branches:
only:
- 'master'
- 'develop'
language: ruby
rvm:
- 2.0.0
- 1.9.3
- 2.1
- 2.0
env:
- DB=postgres BUILD_TYPE=other
@ -14,13 +10,22 @@ env:
- DB=postgres BUILD_TYPE=cucumber
- DB=mysql BUILD_TYPE=cucumber
sudo: false
cache:
bundler: true
directories:
- app/assets/images
branches:
only:
- 'master'
- 'develop'
before_install: gem install bundler
bundler_args: "--without development production heroku --jobs 3 --retry 3"
bundler_args: "--without development production heroku"
script: "./script/ci/build.sh"
addons:
firefox: "26.0"
notifications:
irc:
channels:

View file

@ -1,3 +1,219 @@
# 0.5.0.0
## Major Sidekiq update
This release includes a major upgrade of the background processing system Sidekiq. To upgrade cleanly:
1. Stop diaspora*
2. Run `RAILS_ENV=production bundle exec sidekiq` and wait 5-10 minutes, then stop it again (hit `CTRL+C`)
3. Do a normal upgrade of diaspora*
4. Start diaspora*
## Rails 4 - Manual action required
Please edit `config/initializers/secret_token.rb`, replacing `secret_token` with
`secret_key_base`.
```ruby
# Old
Rails.application.config.secret_token = '***********...'
# New
Diaspora::Application.config.secret_key_base = '*************...'
```
You also need to take care to set `RAILS_ENV` and to clear the cache while precompiling assets: `RAILS_ENV=production bundle exec rake tmp:cache:clear assets:precompile`
## Supported Ruby versions
This release drops official support for the Ruby 1.9 series. This means we will no longer test against this Ruby version or take care to choose libraries
that work with it. However that doesn't mean we won't accept patches that improve running diaspora* on it.
At the same time we adopt support for the Ruby 2.1 series and recommend running on the latest Ruby version of that branch. We continue to support the Ruby 2.0
series and run our comprehensive test suite against it.
## Change in defaults.yml
The default for including jQuery from a CDN has changed. If you want to continue to include it from a CDN, please explicitly set the `jquery_cdn` setting to `true` in diaspora.yml.
## Change in database.yml
For MySQL databases, replace `charset: utf8` with `encoding: utf8mb4` and change `collation` from `utf8_bin` to `utf8mb4_bin` in the file `config/database.yml`.
This is enables full UTF8 support (4bytes characters), including standard emoji characters.
See `database.yml.example` for reference.
Please make sure to stop Diaspora prior running this migration!
## Experimental chat feature
This release adds experimental integration with XMPP for real-time chat. Please see [our wiki](https://wiki.diasporafoundation.org/Vines) for further informations.
## Change in statistics.json schema
The way services are shown in the `statistics.json` route is changing. The keys relating to showing whether services are enabled or not are moving to their own container as `"services": {....}`, instead of having them all in the root level of the JSON.
The keys will still be available in the root level within the 0.5 release. The old keys will be removed in the 0.6 release.
## New maintenance feature to automatically expire inactive accounts
Removing of old inactive users can now be done automatically by background processing. The amount of inactivity is set by `after_days`. A warning email will be sent to the user and after an additional `warn_days`, the account will be automatically closed.
This maintenance is not enabled by default. Podmins can enable it by for example copying over the new settings under `settings.maintenance` to their `diaspora.yml` file and setting it enabled. The default setting is to expire accounts that have been inactive for 2 years (no login).
## Camo integration to proxy external assets
It is now possible to enable an automatic proxying of external assets, for example images embedded via Markdown or OpenGraph thumbnails loaded from insecure third party servers through a [Camo proxy](https://github.com/atmos/camo).
This is disabled by default since it requires the installation of additional packages and might cause some traffic. Check the [wiki page](https://wiki.diasporafoundation.org/Installation/Camo) for more information and detailed installation instructions.
## Paypal unhosted button and currency
Podmins can now set the currency for donations, and use an unhosted button if they can't use
a hosted one. Note: you need to **copy the new settings from diaspora.yml.example to your
diaspora.yml file**. The existing settings from 0.4.x and before will not work any more.
## Custom splash page changes
diaspora* no longer adds a `div.container` to wrap custom splash pages. This adds the ability for podmins to write home pages using Bootstrap's fluid design. Podmins who added a custom splash page in `app/views/home/_show.{html,mobile}.haml` need to wrap the contents into a `div.container` to keep the old design. You will find updated examples [in our wiki](https://wiki.diasporafoundation.org/Custom_splash_page).
## Refactor
* Redesign contacts page [#5153](https://github.com/diaspora/diaspora/pull/5153)
* Improve profile page design on mobile [#5084](https://github.com/diaspora/diaspora/pull/5084)
* Port test suite to RSpec 3 [#5170](https://github.com/diaspora/diaspora/pull/5170)
* Port tag stream to Bootstrap [#5138](https://github.com/diaspora/diaspora/pull/5138)
* Consolidate migrations, if you need a migration prior 2013, checkout the latest release in the 0.4.x series first [#5173](https://github.com/diaspora/diaspora/pull/5173)
* Add tests for mobile sign up [#5185](https://github.com/diaspora/diaspora/pull/5185)
* Display new conversation form on conversations/index [#5178](https://github.com/diaspora/diaspora/pull/5178)
* Port profile page to Backbone [#5180](https://github.com/diaspora/diaspora/pull/5180)
* Pull punycode.js from rails-assets.org [#5263](https://github.com/diaspora/diaspora/pull/5263)
* Redesign profile page and port to Bootstrap [#4657](https://github.com/diaspora/diaspora/pull/4657)
* Unify stream selection links in the left sidebar [#5271](https://github.com/diaspora/diaspora/pull/5271)
* Refactor schema of statistics.json regarding services [#5296](https://github.com/diaspora/diaspora/pull/5296)
* Pull jquery.idle-timer.js from rails-assets.org [#5310](https://github.com/diaspora/diaspora/pull/5310)
* Pull jquery.placeholder.js from rails-assets.org [#5299](https://github.com/diaspora/diaspora/pull/5299)
* Pull jquery.textchange.js from rails-assets.org [#5297](https://github.com/diaspora/diaspora/pull/5297)
* Pull jquery.hotkeys.js from rails-assets.org [#5368](https://github.com/diaspora/diaspora/pull/5368)
* Reduce amount of useless background job retries and pull public posts when missing [#5209](https://github.com/diaspora/diaspora/pull/5209)
* Updated Weekly User Stats admin page to show data for the most recent week including reversing the order of the weeks in the drop down to show the most recent. [#5331](https://github.com/diaspora/diaspora/pull/5331)
* Convert some cukes to RSpec tests [#5289](https://github.com/diaspora/diaspora/pull/5289)
* Hidden overflow for long names on tag pages [#5279](https://github.com/diaspora/diaspora/pull/5279)
* Always reshare absolute root of a post [#5276](https://github.com/diaspora/diaspora/pull/5276)
* Convert remaining SASS stylesheets to SCSS [#5342](https://github.com/diaspora/diaspora/pull/5342)
* Update rack-protection [#5403](https://github.com/diaspora/diaspora/pull/5403)
* Cleanup diaspora.yml [#5426](https://github.com/diaspora/diaspora/pull/5426)
* Replace `opengraph_parser` with `open_graph_reader` [#5462](https://github.com/diaspora/diaspora/pull/5462)
* Make sure conversations without any visibilities left are deleted [#5478](https://github.com/diaspora/diaspora/pull/5478)
* Change tooltip for delete button in conversations view [#5477](https://github.com/diaspora/diaspora/pull/5477)
* Replace a modifier-rescue with a specific rescue [#5491](https://github.com/diaspora/diaspora/pull/5491)
* Port contacts page to backbone [#5473](https://github.com/diaspora/diaspora/pull/5473)
* Replace CSS vendor prefixes automatically [#5532](https://github.com/diaspora/diaspora/pull/5532)
* Use sentence case consistently throughout UI [#5588](https://github.com/diaspora/diaspora/pull/5588)
* Hide sign up button when registrations are disabled [#5612](https://github.com/diaspora/diaspora/pull/5612)
* Standardize capitalization throughout the UI [#5588](https://github.com/diaspora/diaspora/pull/5588)
* Display photos on the profile page as thumbnails [#5521](https://github.com/diaspora/diaspora/pull/5521)
* Unify not connected pages (sign in, sign up, forgot password) [#5391](https://github.com/diaspora/diaspora/pull/5391)
* Port remaining stream pages to Bootstrap [#5715](https://github.com/diaspora/diaspora/pull/5715)
* Port notification dropdown to Backbone [#5707](https://github.com/diaspora/diaspora/pull/5707) [#5761](https://github.com/diaspora/diaspora/pull/5761)
* Add rounded corners for avatars [#5733](https://github.com/diaspora/diaspora/pull/5733)
* Move registration form to a partial [#5764](https://github.com/diaspora/diaspora/pull/5764)
* Add tests for liking and unliking posts [#5741](https://github.com/diaspora/diaspora/pull/5741)
* Rewrite slide effect in conversations as css transition for better performance [#5776](https://github.com/diaspora/diaspora/pull/5776)
* Various cleanups and improvements in the frontend code [#5781](https://github.com/diaspora/diaspora/pull/5781) [#5769](https://github.com/diaspora/diaspora/pull/5769) [#5763](https://github.com/diaspora/diaspora/pull/5763) [#5762](https://github.com/diaspora/diaspora/pull/5762) [#5758](https://github.com/diaspora/diaspora/pull/5758) [#5755](https://github.com/diaspora/diaspora/pull/5755) [#5747](https://github.com/diaspora/diaspora/pull/5747) [#5734](https://github.com/diaspora/diaspora/pull/5734) [#5786](https://github.com/diaspora/diaspora/pull/5786) [#5768](https://github.com/diaspora/diaspora/pull/5798)
* Add specs and validations to the role model [#5792](https://github.com/diaspora/diaspora/pull/5792)
* Replace 'Make something' text by diaspora ball logo on registration page [#5743](https://github.com/diaspora/diaspora/pull/5743)
## Bug fixes
* orca cannot see 'Add Contact' button [#5158](https://github.com/diaspora/diaspora/pull/5158)
* Move submit button to the right in conversations view [#4960](https://github.com/diaspora/diaspora/pull/4960)
* Handle long URLs and titles in OpenGraph descriptions [#5208](https://github.com/diaspora/diaspora/pull/5208)
* Fix deformed getting started popover [#5227](https://github.com/diaspora/diaspora/pull/5227)
* Use correct locale for invitation subject [#5232](https://github.com/diaspora/diaspora/pull/5232)
* Initial support for IDN emails
* Fix services settings reported by statistics.json [#5256](https://github.com/diaspora/diaspora/pull/5256)
* Only collapse empty comment box [#5328](https://github.com/diaspora/diaspora/pull/5328)
* Fix pagination for people/guid/contacts [#5304](https://github.com/diaspora/diaspora/pull/5304)
* Fix poll creation on Bootstrap pages [#5334](https://github.com/diaspora/diaspora/pull/5334)
* Show error message on invalid reset password attempt [#5325](https://github.com/diaspora/diaspora/pull/5325)
* Fix translations on mobile password reset pages [#5318](https://github.com/diaspora/diaspora/pull/5318)
* Handle unset user agent when signing out [#5316](https://github.com/diaspora/diaspora/pull/5316)
* More robust URL parsing for oEmbed and OpenGraph [#5347](https://github.com/diaspora/diaspora/pull/5347)
* Fix Publisher doesn't expand while uploading images [#3098](https://github.com/diaspora/diaspora/issues/3098)
* Drop unneeded and too open crossdomain.xml
* Fix hidden aspect dropdown on getting started page [#5407](https://github.com/diaspora/diaspora/pulls/5407)
* Fix a few issues on Bootstrap pages [#5401](https://github.com/diaspora/diaspora/pull/5401)
* Improve handling of the `more` link on mobile stream pages [#5400](https://github.com/diaspora/diaspora/pull/5400)
* Fix prefilling publisher after getting started [#5442](https://github.com/diaspora/diaspora/pull/5442)
* Fix overflow in profile sidebar [#5450](https://github.com/diaspora/diaspora/pull/5450)
* Fix code overflow in SPV and improve styling for code tags [#5422](https://github.com/diaspora/diaspora/pull/5422)
* Correctly validate if local recipients actually want to receive a conversation [#5449](https://github.com/diaspora/diaspora/pull/5449)
* Improve consistency of poll answer ordering [#5471](https://github.com/diaspora/diaspora/pull/5471)
* Fix broken aspect selectbox on asynchronous search results [#5488](https://github.com/diaspora/diaspora/pull/5488)
* Replace %{third_party_tools} by the appropriate hyperlink in tags FAQ [#5509](https://github.com/diaspora/diaspora/pull/5509)
* Repair downloading the profile image from Facebook [#5493](https://github.com/diaspora/diaspora/pull/5493)
* Fix localization of post and comment timestamps on mobile [#5482](https://github.com/diaspora/diaspora/issues/5482)
* Fix mobile JS loading to quieten errors. Fixes also service buttons on mobile bookmarklet.
* Don't error out when adding a too long location to the profile [#5614](https://github.com/diaspora/diaspora/pull/5614)
* Correctly decrease unread count for conversations [#5646](https://github.com/diaspora/diaspora/pull/5646)
* Fix automatic scroll for conversations [#5646](https://github.com/diaspora/diaspora/pull/5646)
* Fix missing translation on privacy settings page [#5671](https://github.com/diaspora/diaspora/pull/5671)
* Fix code overflow for the mobile website [#5675](https://github.com/diaspora/diaspora/pull/5675)
* Strip Unicode format characters prior post processing [#5680](https://github.com/diaspora/diaspora/pull/5680)
* Disable email notifications for closed user accounts [#5640](https://github.com/diaspora/diaspora/pull/5640)
* Total user statistic no longer includes closed accounts [#5041](https://github.com/diaspora/diaspora/pull/5041)
* Don't add a space when rendering a mention [#5711](https://github.com/diaspora/diaspora/pull/5711)
* Fix flickering hovercards [#5714](https://github.com/diaspora/diaspora/pull/5714) [#5876](https://github.com/diaspora/diaspora/pull/5876)
* Improved stripping markdown in post titles [#5730](https://github.com/diaspora/diaspora/pull/5730)
* Remove border from reply form for conversations [#5744](https://github.com/diaspora/diaspora/pull/5744)
* Fix overflow for headings, blockquotes and other elements [#5731](https://github.com/diaspora/diaspora/pull/5731)
* Correct photo count on profile page [#5751](https://github.com/diaspora/diaspora/pull/5751)
* Fix mobile sign up from an invitation [#5754](https://github.com/diaspora/diaspora/pull/5754)
* Set max-width for tag following button on tag page [#5752](https://github.com/diaspora/diaspora/pull/5752)
* Display error messages for failed password change [#5580](https://github.com/diaspora/diaspora/pull/5580)
* Display correct error message for too long tags [#5783](https://github.com/diaspora/diaspora/pull/5783)
* Fix displaying reshares in the stream on mobile [#5790](https://github.com/diaspora/diaspora/pull/5790)
* Remove bottom margin from lists that are the last element of a post. [#5721](https://github.com/diaspora/diaspora/pull/5721)
* Fix pagination design on conversations page [#5791](https://github.com/diaspora/diaspora/pull/5791)
* Prevent inserting posts into the wrong stream [#5838](https://github.com/diaspora/diaspora/pull/5838)
* Update help section [#5857](https://github.com/diaspora/diaspora/pull/5857) [#5859](https://github.com/diaspora/diaspora/pull/5859)
* Fix asset precompilation check in script/server [#5863](https://github.com/diaspora/diaspora/pull/5863)
* Convert MySQL databases to utf8mb4 [#5530](https://github.com/diaspora/diaspora/pull/5530) [#5624](https://github.com/diaspora/diaspora/pull/5624) [#5865](https://github.com/diaspora/diaspora/pull/5865)
* Don't upcase labels on mobile sign up/sign in [#5872](https://github.com/diaspora/diaspora/pull/5872)
## Features
* Don't pull jQuery from a CDN by default [#5105](https://github.com/diaspora/diaspora/pull/5105)
* Better character limit message [#5151](https://github.com/diaspora/diaspora/pull/5151)
* Remember whether a AccountDeletion was performed [#5156](https://github.com/diaspora/diaspora/pull/5156)
* Increased the number of notifications shown in drop down bar to 15 [#5129](https://github.com/diaspora/diaspora/pull/5129)
* Increase possible captcha length [#5169](https://github.com/diaspora/diaspora/pull/5169)
* Display visibility icon in publisher aspects dropdown [#4982](https://github.com/diaspora/diaspora/pull/4982)
* Add a link to the reported comment in the admin panel [#5337](https://github.com/diaspora/diaspora/pull/5337)
* Strip search query from leading and trailing whitespace [#5317](https://github.com/diaspora/diaspora/pull/5317)
* Add the "network" key to statistics.json and set it to "Diaspora" [#5308](https://github.com/diaspora/diaspora/pull/5308)
* Infinite scrolling in the notifications dropdown [#5237](https://github.com/diaspora/diaspora/pull/5237)
* Maintenance feature to automatically expire inactive accounts [#5288](https://github.com/diaspora/diaspora/pull/5288)
* Add LibreJS markers to JavaScript [5320](https://github.com/diaspora/diaspora/pull/5320)
* Ask for confirmation when leaving a submittable publisher [#5309](https://github.com/diaspora/diaspora/pull/5309)
* Allow page-specific styling via individual CSS classes [#5282](https://github.com/diaspora/diaspora/pull/5282)
* Change diaspora logo in the header on hover [#5355](https://github.com/diaspora/diaspora/pull/5355)
* Display diaspora handle in search results [#5419](https://github.com/diaspora/diaspora/pull/5419)
* Show a message on the ignored users page when there are none [#5434](https://github.com/diaspora/diaspora/pull/5434)
* Truncate too long OpenGraph descriptions [#5387](https://github.com/diaspora/diaspora/pull/5387)
* Make the source code URL configurable [#5410](https://github.com/diaspora/diaspora/pull/5410)
* Prefill publisher on the tag pages [#5442](https://github.com/diaspora/diaspora/pull/5442)
* Don't include the content of non-public posts into notification mails [#5494](https://github.com/diaspora/diaspora/pull/5494)
* Allow to set unhosted button and currency for paypal donation [#5452](https://github.com/diaspora/diaspora/pull/5452)
* Add followed tags in the mobile menu [#5468](https://github.com/diaspora/diaspora/pull/5468)
* Replace Pagedown with markdown-it [#5526](https://github.com/diaspora/diaspora/pull/5526)
* Do not truncate notification emails anymore [#4342](https://github.com/diaspora/diaspora/issues/4342)
* Allows users to export their data in gzipped JSON format from their user settings page [#5499](https://github.com/diaspora/diaspora/pull/5499)
* Strip EXIF data from newly uploaded images [#5510](https://github.com/diaspora/diaspora/pull/5510)
* Hide user setting if the community spotlight is not enabled on the pod [#5562](https://github.com/diaspora/diaspora/pull/5562)
* Add HTML view for pod statistics [#5464](https://github.com/diaspora/diaspora/pull/5464)
* Added/Moved hide, block user, report and delete button in SPV [#5547](https://github.com/diaspora/diaspora/pull/5547)
* Added keyboard shortcuts r(reshare), m(expand Post), o(open first link in post) [#5602](https://github.com/diaspora/diaspora/pull/5602)
* Added dropdown to add/remove people from/to aspects in mobile view [#5594](https://github.com/diaspora/diaspora/pull/5594)
* Dynamically compute minimum and maximum valid year for birthday field [#5639](https://github.com/diaspora/diaspora/pull/5639)
* Show hovercard on mentions [#5652](https://github.com/diaspora/diaspora/pull/5652)
* Make help sections linkable [#5667](https://github.com/diaspora/diaspora/pull/5667)
* Add invitation link to contacts page [#5655](https://github.com/diaspora/diaspora/pull/5655)
* Add year to notifications page [#5676](https://github.com/diaspora/diaspora/pull/5676)
* Give admins the ability to lock & unlock accounts [#5643](https://github.com/diaspora/diaspora/pull/5643)
* Add reshares to the stream view immediately [#5699](https://github.com/diaspora/diaspora/pull/5699)
* Update and improve help section [#5665](https://github.com/diaspora/diaspora/pull/5665), [#5706](https://github.com/diaspora/diaspora/pull/5706)
* Expose participation controls in the stream view [#5511](https://github.com/diaspora/diaspora/pull/5511)
* Reimplement photo export [#5685](https://github.com/diaspora/diaspora/pull/5685)
* Add participation controls in the single post view [#5722](https://github.com/diaspora/diaspora/pull/5722)
* Display polls on reshares [#5782](https://github.com/diaspora/diaspora/pull/5782)
* Remove footer from stream pages [#5816](https://github.com/diaspora/diaspora/pull/5816)
# 0.4.1.3
* Update Redcarped, fixes [OSVDB-120415](http://osvdb.org/show/osvdb/120415).
@ -19,7 +235,7 @@ This release brings a new ToS feature that allows pods to easily display to user
terms:
enable: true
When enabled, the footer and sidebar will have a link to terms page, and signup will have a disclaimer indicating that creating an account means the user accepts the terms of use.
When enabled, the footer and sidebar will have a link to terms page, and sign up will have a disclaimer indicating that creating an account means the user accepts the terms of use.
While the project itself doesn't restrict what kind of terms pods run on, we realize not all podmins want to spend time writing them from scratch. Thus there is a basic ToS template included that will be used unless a custom one available.
@ -134,7 +350,7 @@ Read more in [#4249](https://github.com/diaspora/diaspora/pull/4249) and [#4883]
* Reorder and reword items on user settings page [#4912](https://github.com/diaspora/diaspora/pull/4912)
* SPV: Improve padding and interaction counts [#4426](https://github.com/diaspora/diaspora/pull/4426)
* Remove auto 'mark as read' for notifications [#4810](https://github.com/diaspora/diaspora/pull/4810)
* Improve set read/unread in notifications dropdown [#4869](https://github.com/diaspora/diaspora/pull/4869)
* Improve set read/unread in notifications dropdown [#4869](https://github.com/diaspora/diaspora/pull/4869)
* Refactor publisher: trigger events for certain actions, introduce 'disabled' state [#4932](https://github.com/diaspora/diaspora/pull/4932)
## Bug fixes
@ -1022,7 +1238,7 @@ The new configuration system allows all possible settings to be overriden by env
### Environment variable changes:
#### deprectated
#### deprecated
* REDISTOGO_URL in favour of REDIS_URL or ENVIRONMENT_REDIS
@ -1081,4 +1297,3 @@ The single-post view will also be revamped/reverted, but that didn't make it int
## Cleanup in maintenance scripts and automated build environment

285
Gemfile
View file

@ -1,218 +1,275 @@
source 'https://rubygems.org'
source "https://rubygems.org"
gem 'rails', '3.2.20'
gem "rails", "4.2.1"
# Legacy Rails features, remove me!
# caches_page
gem "actionpack-action_caching"
gem "actionpack-page_caching"
# responders (class level)
gem "responders", "2.1.0"
# Appserver
gem 'unicorn', '4.8.3', :require => false
gem "unicorn", "4.8.3", require: false
# API and JSON
gem 'acts_as_api', '0.4.2'
gem 'json', '1.8.1'
gem "acts_as_api", "0.4.2"
gem "json", "1.8.2"
# Authentication
gem 'devise', '3.2.4'
gem 'devise_lastseenable', '0.0.4'
gem "devise", "3.4.1"
gem "devise_lastseenable", "0.0.4"
gem "devise-token_authenticatable", "~> 0.3.0"
# Captcha
gem 'galetahub-simple_captcha', '0.1.5', :require => 'simple_captcha'
gem "simple_captcha2", "0.3.4", require: "simple_captcha"
# Background processing
gem 'sidekiq', '2.17.7'
gem 'sinatra', '1.3.3'
gem "sidekiq", "3.3.3"
gem "sinatra", "1.4.6"
# Scheduled processing
gem "sidetiq", "0.6.3"
# Compression
gem "uglifier", "2.7.1"
# Configuration
gem 'configurate', '0.0.8'
gem "configurate", "0.2.0"
# Cross-origin resource sharing
gem 'rack-cors', '0.2.9', :require => 'rack/cors'
gem "rack-cors", "0.3.1", require: "rack/cors"
# CSS
gem "bootstrap-sass", "2.3.2.2"
gem "compass-rails", "2.0.4"
gem "sass-rails", "5.0.1"
gem "autoprefixer-rails", "5.1.7.1"
# Database
ENV['DB'] ||= 'mysql'
ENV["DB"] ||= "mysql"
gem 'mysql2', '0.3.16' if ENV['DB'] == 'all' || ENV['DB'] == 'mysql'
gem 'pg', '0.17.1' if ENV['DB'] == 'all' || ENV['DB'] == 'postgres'
gem "mysql2", "0.3.18" if ENV["DB"] == "all" || ENV["DB"] == "mysql"
gem "pg", "0.18.1" if ENV["DB"] == "all" || ENV["DB"] == "postgres"
gem 'activerecord-import', '0.3.1'
gem 'foreigner', '1.6.1'
gem "activerecord-import", "0.7.0"
# File uploading
gem 'carrierwave', '0.10.0'
gem 'fog', '1.22.1'
gem 'mini_magick', '3.7.0'
gem 'remotipart', '1.2.1'
gem "carrierwave", "0.10.0"
gem "fog", "1.28.0"
gem "mini_magick", "4.2.0"
gem "remotipart", "1.2.1"
# GUID generation
gem 'uuid', '2.3.7'
gem "uuid", "2.3.7"
# Icons
gem "entypo-rails", "2.2.2"
# JavaScript
gem "backbone-on-rails", "1.1.2"
gem "handlebars_assets", "0.20.1"
gem "jquery-rails", "3.1.2"
gem "js_image_paths", "0.0.2"
gem "js-routes", "1.0.0"
source "https://rails-assets.org" do
gem "rails-assets-jquery", "1.11.1" # Should be kept in sync with jquery-rails
gem "rails-assets-markdown-it", "4.2.0"
gem "rails-assets-markdown-it-hashtag", "0.3.0"
gem "rails-assets-markdown-it-diaspora-mention", "0.3.0"
gem "rails-assets-markdown-it-sanitizer", "0.3.0"
gem "rails-assets-markdown-it--markdown-it-for-inline", "0.1.0"
gem "rails-assets-markdown-it-sub", "1.0.0"
gem "rails-assets-markdown-it-sup", "1.0.0"
# jQuery plugins
gem "rails-assets-jeresig--jquery.hotkeys", "0.2.0"
gem "rails-assets-jquery-idletimer", "1.0.1"
gem "rails-assets-jquery-placeholder", "2.1.1"
gem "rails-assets-jquery-textchange", "0.2.3"
gem "rails-assets-perfect-scrollbar", "0.5.9"
end
# Localization
gem 'http_accept_language', '1.0.2'
gem 'i18n-inflector-rails', '1.0.7'
gem 'rails-i18n', '0.7.4'
gem "http_accept_language", "2.0.5"
gem "i18n-inflector-rails", "1.0.7"
gem "rails-i18n", "4.0.4"
# Mail
gem 'markerb', '1.0.2'
gem 'messagebus_ruby_api', '1.0.3'
gem "markerb", "1.0.2"
gem "messagebus_ruby_api", "1.0.3"
# Parsing
gem 'nokogiri', '1.6.1'
gem 'rails_autolink', '1.1.5'
gem 'redcarpet', '3.2.3'
gem 'roxml', '3.1.6'
gem 'ruby-oembed', '0.8.9'
gem 'opengraph_parser', '0.2.3'
# Please remove when migrating to Rails 4
gem 'strong_parameters', '0.2.3'
gem "nokogiri", "1.6.6.2"
gem "redcarpet", "3.2.3"
gem "twitter-text", "1.11.0"
gem "roxml", "3.1.6"
gem "ruby-oembed", "0.8.12"
gem "open_graph_reader", "0.5.0"
# Services
gem 'omniauth', '1.2.1'
gem 'omniauth-facebook', '1.6.0'
gem 'omniauth-tumblr', '1.1'
gem 'omniauth-twitter', '1.0.1'
gem 'twitter', '4.8.1'
gem 'omniauth-wordpress','0.2.1'
gem "omniauth", "1.2.2"
gem "omniauth-facebook", "1.6.0"
gem "omniauth-tumblr", "1.1"
gem "omniauth-twitter", "1.0.1"
gem "twitter", "4.8.1"
gem "omniauth-wordpress", "0.2.1"
# Serializers
gem "active_model_serializers", "0.9.3"
# XMPP chat dependencies
gem "diaspora-vines", "~> 0.1.27"
gem "rails-assets-diaspora_jsxc", "~> 0.1.1", source: "https://rails-assets.org"
# Tags
gem 'acts-as-taggable-on', '3.2.6'
gem "acts-as-taggable-on", "3.5.0"
# URIs and HTTP
gem 'addressable', '2.3.6', :require => 'addressable/uri'
gem 'faraday', '0.8.9'
gem 'faraday_middleware', '0.9.0'
gem 'typhoeus', '0.6.8'
gem "addressable", "2.3.7", require: "addressable/uri"
gem "faraday", "0.9.1"
gem "faraday_middleware", "0.9.1"
gem "faraday-cookie_jar", "0.0.6"
gem "typhoeus", "0.7.1"
# Views
gem 'gon', '5.0.4'
gem 'haml', '4.0.5'
gem 'mobile-fu', '1.2.2'
gem 'will_paginate', '3.0.5'
gem 'rails-timeago', '2.4.0'
gem "gon", "5.2.3"
gem "haml", "4.0.6"
gem "mobile-fu", "1.3.1"
gem "will_paginate", "3.0.7"
gem "rails-timeago", "2.11.0"
# Workarounds
# https://github.com/rubyzip/rubyzip#important-note
gem 'zip-zip'
gem "zip-zip"
### GROUPS ####
# Prevent occasions where minitest is not bundled in
# packaged versions of ruby. See following issues/prs:
# https://github.com/gitlabhq/gitlabhq/issues/3826
# https://github.com/gitlabhq/gitlabhq/pull/3852
# https://github.com/discourse/discourse/pull/238
gem "minitest"
group :assets do
# Windows and OSX have an execjs compatible runtime built-in, Linux users should
# install Node.js or use "therubyracer".
#
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
# Icons
gem 'entypo-rails', '2.2.1'
# CSS
gem 'bootstrap-sass', '2.2.2.0'
gem 'compass-rails', '1.1.7'
gem 'sass-rails', '3.2.6'
# Compression
gem 'uglifier', '2.5.0'
# JavaScript
gem 'backbone-on-rails', '1.1.1'
gem 'handlebars_assets', '0.12.0'
gem 'jquery-rails', '3.0.4'
# Windows and OSX have an execjs compatible runtime built-in, Linux users should
# install Node.js or use 'therubyracer'.
#
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
# gem 'therubyracer', :platform => :ruby
end
group :production do # we don't install these on travis to speed up test runs
# gem "therubyracer", :platform => :ruby
group :production do # we don"t install these on travis to speed up test runs
# Administration
gem 'rails_admin', '0.4.9'
gem "rails_admin", "0.6.7"
# Analytics
gem 'rack-google-analytics', '0.14.0', :require => 'rack/google-analytics'
gem 'rack-piwik', '0.2.2', :require => 'rack/piwik'
gem "rack-google-analytics", "1.2.0"
gem "rack-piwik", "0.3.0", require: "rack/piwik"
# Click-jacking protection
gem 'rack-protection', '1.2'
gem "rack-protection", "1.5.3"
# Process management
gem 'foreman', '0.62'
gem "foreman", "0.62"
# Redirects
gem 'rack-rewrite', '1.5.0', :require => false
gem 'rack-ssl', '1.3.3', :require => 'rack/ssl'
gem "rack-rewrite", "1.5.1", require: false
gem "rack-ssl", "1.4.1", require: "rack/ssl"
# Third party asset hosting
gem 'asset_sync', '1.0.0', :require => false
gem "asset_sync", "1.1.0", require: false
end
group :development do
# Automatic test runs
gem 'guard-cucumber', '1.4.1'
gem 'guard-rspec', '4.2.9'
gem 'rb-fsevent', '0.9.4', :require => false
gem 'rb-inotify', '0.9.4', :require => false
gem "guard-cucumber", "1.5.4"
gem "guard-jshintrb", "1.1.1"
gem "guard-rspec", "4.5.0"
gem "guard-rubocop", "1.2.0"
gem "guard", "2.12.5", require: false
gem "rb-fsevent", "0.9.4", require: false
gem "rb-inotify", "0.9.5", require: false
# Linters
gem "jshintrb", "0.3.0"
gem "rubocop", "0.29.1"
# Preloading environment
gem 'guard-spork', '1.5.1'
gem 'spork', '1.0.0rc4'
gem "spring", "1.3.3"
gem "spring-commands-rspec", "1.0.4"
gem "spring-commands-cucumber", "1.0.1"
# Debugging
gem "pry"
gem "pry-debundle"
gem "pry-byebug"
end
group :test do
# RSpec (unit tests, some integration tests)
gem 'fixture_builder', '0.3.6'
gem 'fuubar', '1.3.3'
gem 'rspec-instafail', '0.2.4', :require => false
gem 'test_after_commit', '0.2.3'
gem "fixture_builder", "0.3.6"
gem "fuubar", "2.0.0"
gem "rspec-instafail", "0.2.6", require: false
gem "test_after_commit", "0.4.1"
# Cucumber (integration tests)
gem 'capybara', '2.2.1'
gem 'database_cleaner', '1.3.0'
gem 'selenium-webdriver', '2.42.0'
gem "capybara", "2.4.4"
gem "database_cleaner" , "1.4.1"
gem "selenium-webdriver", "2.45.0"
# General helpers
gem 'factory_girl_rails', '4.4.1'
gem 'timecop', '0.7.1'
gem 'webmock', '1.18.0', :require => false
gem "factory_girl_rails", "4.5.0"
gem "timecop", "0.7.3"
gem "webmock", "1.20.4", require: false
gem "shoulda-matchers", "2.8.0", require: false
end
group :development, :test do
# RSpec (unit tests, some integration tests)
gem "rspec-rails", '2.14.2'
gem "rspec-rails", "3.2.1"
# Cucumber (integration tests)
gem 'cucumber-rails', '1.4.1', :require => false
gem "cucumber-rails", "1.4.2", require: false
# Jasmine (client side application tests (JS))
gem 'jasmine', '1.3.2'
gem 'sinon-rails', '1.9.0'
gem "jasmine", "2.2.0"
gem "jasmine-jquery-rails", "2.0.3"
gem "rails-assets-jasmine-ajax", "3.1.0", source: "https://rails-assets.org"
gem "sinon-rails", "1.10.3"
end

File diff suppressed because it is too large Load diff

View file

@ -1,38 +1,45 @@
# A sample Guardfile
# More info at https://github.com/guard/guard#readme
# also, http://asciicasts.com/episodes/264-guard
guard 'rspec', :all_on_start => false, :all_after_pass => false do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { "spec" }
guard :rspec, cmd: "bin/spring rspec", all_on_start: false, all_after_pass: false do
watch(/^spec\/.+_spec\.rb$/)
watch(/^lib\/(.+)\.rb$/) {|m| "spec/lib/#{m[1]}_spec.rb" }
watch(/spec\/spec_helper.rb/) { "spec" }
# Rails example
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
watch(/^spec\/.+_spec\.rb$/)
watch(/^app\/(.+)\.rb$/) {|m| "spec/#{m[1]}_spec.rb" }
watch(/^lib\/(.+)\.rb$/) {|m| "spec/lib/#{m[1]}_spec.rb" }
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) {|m|
["spec/routing/#{m[1]}_routing_spec.rb",
"spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb",
"spec/acceptance/#{m[1]}_spec.rb"]
}
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
watch('spec/spec_helper.rb') { "spec" }
watch('config/routes.rb') { "spec/routing" }
watch('app/controllers/application_controller.rb') { "spec/controllers" }
watch("spec/spec_helper.rb") { "spec" }
watch("config/routes.rb") { "spec/routing" }
watch("app/controllers/application_controller.rb") { "spec/controllers" }
# Capybara request specs
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) {|m| "spec/requests/#{m[1]}_spec.rb" }
end
guard 'spork', :cucumber_env => { 'RAILS_ENV' => 'test' }, :rspec_env => { 'RAILS_ENV' => 'test' }, :all_on_start => false, :all_after_pass => false, :wait => 70 do
watch('config/application.rb')
watch('config/environment.rb')
watch(%r{^config/environments/.+\.rb$})
watch(%r{^config/initializers/.+\.rb$})
watch('Gemfile')
watch('Gemfile.lock')
watch('spec/spec_helper.rb') { :rspec }
watch('test/test_helper.rb') { :test_unit }
watch(%r{features/support/}) { :cucumber }
guard(:cucumber,
command_prefix: "bin/spring",
bundler: false,
all_on_start: false,
all_after_pass: false) do
watch(/^features\/.+\.feature$/)
watch(%r{^features/support/.+$}) { "features" }
watch(%r{^features/step_definitions/(.+)_steps\.rb$}) {|m|
Dir[File.join("**/#{m[1]}.feature")][0] || "features"
}
end
guard 'cucumber', :all_on_start => false, :all_after_pass => false do
watch(%r{^features/.+\.feature$})
watch(%r{^features/support/.+$}) { 'features' }
watch(%r{^features/step_definitions/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'features' }
guard :rubocop, all_on_start: false, keep_failed: false do
watch(/(?:app|config|db|lib|features|spec)\/.+\.rb$/)
watch(/(config.ru|Gemfile|Guardfile|Rakefile)$/)
end
guard :jshintrb do
watch(/^app\/assets\/javascripts\/.+\.js$/)
watch(/^lib\/assets\/javascripts\/.+\.js$/)
watch(/^spec\/javascripts\/.+\.js$/)
end

View file

@ -1,2 +1,3 @@
web: bundle exec unicorn_rails -c config/unicorn.rb -p $PORT
sidekiq: bundle exec sidekiq
web: bin/bundle exec unicorn_rails -c config/unicorn.rb -p $PORT
sidekiq: bin/bundle exec sidekiq
xmpp: bin/bundle exec vines start

View file

@ -1,5 +1,5 @@
# diaspora*
### a privacy aware, distributed, open source social network
### a privacy-aware, distributed, open source social network
**master:** [![Build Status master](https://secure.travis-ci.org/diaspora/diaspora.png?branch=master)](http://travis-ci.org/diaspora/diaspora)
**develop:** [![Build Status develop](https://secure.travis-ci.org/diaspora/diaspora.png?branch=develop)](http://travis-ci.org/diaspora/diaspora) |

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 B

0
app/assets/images/facebox/loading.gif Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 255 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

0
app/assets/images/icons/menu.png Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 262 B

After

Width:  |  Height:  |  Size: 262 B

View file

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
//= require_self
//= require_tree ./helpers
@ -12,6 +14,8 @@
//= require_tree ./collections
//= require_tree ./views
//= require perfect-scrollbar
var app = {
collections: {},
models: {},
@ -31,13 +35,11 @@ var app = {
events: _.extend({}, Backbone.Events),
user: function(userAttrs) {
if(userAttrs) { return this._user = new app.models.User(userAttrs) }
return this._user || false
},
baseImageUrl: function(baseUrl){
if(baseUrl) { return this._baseImageUrl = baseUrl }
return this._baseImageUrl || "assets/"
if(userAttrs) {
this._user = new app.models.User(userAttrs);
return this._user;
}
return this._user || false;
},
initialize: function() {
@ -53,25 +55,25 @@ var app = {
},
hasPreload : function(prop) {
return !!(window.gon.preloads && window.gon.preloads[prop]) //returning boolean variable so that parsePreloads, which cleans up properly is used instead
return !!(window.gon.preloads && window.gon.preloads[prop]); //returning boolean variable so that parsePreloads, which cleans up properly is used instead
},
setPreload : function(prop, val) {
window.gon.preloads = window.gon.preloads || {}
window.gon.preloads[prop] = val
window.gon.preloads = window.gon.preloads || {};
window.gon.preloads[prop] = val;
},
parsePreload : function(prop) {
if(!app.hasPreload(prop)) { return }
var preload = window.gon.preloads[prop]
delete window.gon.preloads[prop] //prevent dirty state across navigates
var preload = window.gon.preloads[prop];
delete window.gon.preloads[prop]; //prevent dirty state across navigates
return(preload)
return(preload);
},
setupDummyPreloads: function() {
if (window.gon == undefined) {
if (window.gon === undefined) {
window.gon = {preloads:{}};
}
},
@ -89,8 +91,8 @@ var app = {
},
setupFacebox: function() {
$.facebox.settings.closeImage = app.baseImageUrl()+'facebox/closelabel.png';
$.facebox.settings.loadingImage = app.baseImageUrl()+'facebox/loading.gif';
$.facebox.settings.closeImage = ImagePaths.get('facebox/closelabel.png');
$.facebox.settings.loadingImage = ImagePaths.get('facebox/loading.gif');
$.facebox.settings.opacity = 0.75;
},
@ -102,14 +104,13 @@ var app = {
evt.preventDefault();
var link = $(this);
$(".stream_title").text(link.text())
app.router.navigate(link.attr("href").substring(1) ,true)
$(".stream_title").text(link.text());
app.router.navigate(link.attr("href").substring(1) ,true);
});
},
setupGlobalViews: function() {
app.hovercard = new app.views.Hovercard();
app.aspectMembershipsBlueprint = new app.views.AspectMembershipBlueprint();
$('.aspect_membership_dropdown').each(function(){
new app.views.AspectMembership({el: this});
});
@ -119,7 +120,7 @@ var app = {
/* mixpanel wrapper function */
instrument : function(type, name, object, callback) {
if(!window.mixpanel) { return }
window.mixpanel[type](name, object, callback)
window.mixpanel[type](name, object, callback);
},
setupDisabledLinks: function() {
@ -132,3 +133,4 @@ var app = {
$(function() {
app.initialize();
});
// @license-end

View file

@ -0,0 +1,6 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.collections.AspectMemberships = Backbone.Collection.extend({
model: app.models.AspectMembership
});
// @license-end

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.collections.Aspects = Backbone.Collection.extend({
model: app.models.Aspect,
@ -23,4 +25,5 @@ app.collections.Aspects = Backbone.Collection.extend({
var separator = Diaspora.I18n.t("comma") + ' ';
return this.selectedAspects('name').join(separator).replace(/,\s([^,]+)$/, ' ' + Diaspora.I18n.t("and") + ' $1') || Diaspora.I18n.t("my_aspects");
}
})
});
// @license-end

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.collections.Comments = Backbone.Collection.extend({
model: app.models.Comment,
url: function() { return _.result(this.post, 'url') + '/comments'; },
@ -14,11 +16,12 @@ app.collections.Comments = Backbone.Collection.extend({
var deferred = comment.save({}, {
url: '/posts/'+this.post.id+'/comments',
success: function() {
comment.set({author: app.currentUser.toJSON(), parent: self.post })
self.add(comment)
comment.set({author: app.currentUser.toJSON(), parent: self.post });
self.add(comment);
}
});
return deferred;
}
});
// @license-end

View file

@ -0,0 +1,21 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.collections.Contacts = Backbone.Collection.extend({
model: app.models.Contact,
comparator : function(con1, con2) {
if( !con1.person || !con2.person ) return 1;
if(app.aspect) {
var inAspect1 = con1.inAspect(app.aspect.get('id'));
var inAspect2 = con2.inAspect(app.aspect.get('id'));
if( inAspect1 && !inAspect2 ) return -1;
if( !inAspect1 && inAspect2 ) return 1;
}
var n1 = con1.person.get('name');
var n2 = con2.person.get('name');
return n1.localeCompare(n2);
}
});
// @license-end

View file

@ -1,7 +1,10 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.collections.Likes = Backbone.Collection.extend({
model: app.models.Like,
initialize : function(models, options) {
this.url = "/posts/" + options.post.id + "/likes" //not delegating to post.url() because when it is in a stream collection it delegates to that url
this.url = "/posts/" + options.post.id + "/likes"; //not delegating to post.url() because when it is in a stream collection it delegates to that url
}
});
// @license-end

View file

@ -1,8 +1,10 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.collections.Photos = Backbone.Collection.extend({
url : "/photos",
model: function(attrs, options) {
var modelClass = app.models.Photo
var modelClass = app.models.Photo;
return new modelClass(attrs, options);
},
@ -10,3 +12,4 @@ app.collections.Photos = Backbone.Collection.extend({
return resp.photos;
}
});
// @license-end

View file

@ -1,4 +1,8 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.collections.Posts = Backbone.Collection.extend({
model: app.models.Post,
url : "/posts"
});
// @license-end

View file

@ -1,4 +1,8 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.collections.Reshares = Backbone.Collection.extend({
model: app.models.Reshare,
url : "/reshares"
});
// @license-end

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.collections.TagFollowings = Backbone.Collection.extend({
model: app.models.TagFollowing,
@ -17,3 +19,5 @@ app.collections.TagFollowings = Backbone.Collection.extend({
}
});
// @license-end

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
(function(){
app.helpers.dateFormatter = {
parse:function (dateString) {
@ -12,5 +14,6 @@
return timestamp || 0;
}
}
};
})();
// @license-end

View file

@ -0,0 +1,95 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
(function() {
app.helpers.txtDirection = {
setCssFor: function(str, on_element) {
if( this.isRTL(str) ) {
$(on_element).css('direction', 'rtl');
} else {
$(on_element).css('direction', 'ltr');
}
},
classFor: function(str) {
if( this.isRTL(str) ) return 'rtl';
return 'ltr';
},
isRTL: function(str) {
if(typeof str !== "string" || str.length < 1) {
return false;
}
var charCode = this._fixedCharCodeAt(str, 0);
if(charCode >= 1536 && charCode <= 1791) // Sarabic, Persian, ...
return true;
else if(charCode >= 65136 && charCode <= 65279) // Arabic present 1
return true;
else if(charCode >= 64336 && charCode <= 65023) // Arabic present 2
return true;
else if(charCode>=1424 && charCode<=1535) // Hebrew
return true;
else if(charCode>=64256 && charCode<=64335) // Hebrew present
return true;
else if(charCode>=68096 && charCode<=68184) // Kharoshthi
return true;
else if(charCode>=67840 && charCode<=67871) // Phoenician
return true;
else if(charCode>=1792 && charCode<=1871) // Syriac
return true;
else if(charCode>=1920 && charCode<=1983) // Thaana
return true;
else if(charCode>=1984 && charCode<=2047) // NKo
return true;
else if(charCode>=11568 && charCode<=11647) // Tifinagh
return true;
return false;
},
// source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt
_fixedCharCodeAt: function(str, idx) {
str += '';
var code,
end = str.length;
var surrogatePairs = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
while ((surrogatePairs.exec(str)) != null) {
var li = surrogatePairs.lastIndex;
if (li - 2 < idx) {
idx++;
}
else {
break;
}
}
if (idx >= end || idx < 0) {
return NaN;
}
code = str.charCodeAt(idx);
var hi, low;
if (0xD800 <= code && code <= 0xDBFF) {
hi = code;
low = str.charCodeAt(idx+1);
// Go one further, since one of the "characters" is part of a surrogate pair
return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
}
return code;
}
};
})();
// @license-end

View file

@ -1,26 +1,67 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
Handlebars.registerHelper('t', function(scope, values) {
return Diaspora.I18n.t(scope, values.hash)
return Diaspora.I18n.t(scope, values.hash);
});
Handlebars.registerHelper('txtDirClass', function(str) {
return app.helpers.txtDirection.classFor(str);
});
Handlebars.registerHelper('imageUrl', function(path){
return app.baseImageUrl() + path;
return ImagePaths.get(path);
});
Handlebars.registerHelper('linkToPerson', function(context, block) {
Handlebars.registerHelper('urlTo', function(path_helper, id, data){
if( !data ) {
// only one argument given to helper, mangle parameters
data = id;
return Routes[path_helper+'_path'](data.hash);
}
return Routes[path_helper+'_path'](id, data.hash);
});
Handlebars.registerHelper('linkToAuthor', function(context, block) {
if( !context ) context = this;
var html = "<a href=\"/people/" + context.guid + "\" class=\"author-name ";
html += Handlebars.helpers.hovercardable(context);
html += "\">";
html += block.fn(context);
html += "</a>";
return html
return html;
});
Handlebars.registerHelper('linkToPerson', function(context, block) {
if( !context ) context = this;
var html = "<a href=\"/people/" + context.guid + "\" class=\"name\">";
html += block.fn(context);
html += "</a>";
return html;
});
// relationship indicator for profile page
Handlebars.registerHelper('sharingMessage', function(person) {
var i18n_scope = 'people.helper.is_not_sharing';
var icon = "circle";
if( person.is_sharing ) {
i18n_scope = 'people.helper.is_sharing';
icon = "entypo check";
}
var title = Diaspora.I18n.t(i18n_scope, {name: person.name});
var html = '<span class="sharing_message_container" title="'+title+'" data-placement="bottom">'+
' <i id="sharing_message" class="'+icon+'"></i>'+
'</span>';
return html;
});
// allow hovercards for users that are not the current user.
// returns the html class name used to trigger hovercards.
Handlebars.registerHelper('hovercardable', function(person) {
if( app.currentUser.get('guid') != person.guid ) {
if( app.currentUser.get('guid') !== person.guid ) {
return 'hovercardable';
}
return '';
@ -29,18 +70,65 @@ Handlebars.registerHelper('hovercardable', function(person) {
Handlebars.registerHelper('personImage', function(person, size, imageClass) {
/* we return here if person.avatar is blank, because this happens when a
* user is unauthenticated. we don't know why this happens... */
if( _.isUndefined(person.avatar) ) { return }
if( !person.avatar &&
!(person.profile && person.profile.avatar) ) return;
var avatar = person.avatar || person.profile.avatar;
var name = ( person.name ) ? person.name : 'avatar';
size = ( !_.isString(size) ) ? "small" : size;
imageClass = ( !_.isString(imageClass) ) ? size : imageClass;
return _.template('<img src="<%= src %>" class="avatar <%= img_class %>" title="<%= title %>" />', {
'src': person.avatar[size],
return _.template('<img src="<%= src %>" class="avatar <%= img_class %>" title="<%= title %>" alt="<%= title %>" />')({
'src': avatar[size],
'img_class': imageClass,
'title': _.escape(person.name)
'title': _.escape(name)
});
});
Handlebars.registerHelper('localTime', function(timestamp) {
return new Date(timestamp).toLocaleString();
});
});
Handlebars.registerHelper('fmtTags', function(tags) {
var links = _.map(tags, function(tag) {
return '<a class="tag" href="' + Routes.tag_path(tag) + '">' +
' #' + tag +
'</a>';
}).join(' ');
return new Handlebars.SafeString(links);
});
Handlebars.registerHelper('fmtText', function(text) {
return new Handlebars.SafeString(app.helpers.textFormatter(text));
});
Handlebars.registerHelper('isCurrentPage', function(path_helper, id, options){
var currentPage = "/"+Backbone.history.fragment;
if (currentPage === Handlebars.helpers.urlTo(path_helper, id, options.data)) {
return options.fn(this);
} else {
return options.inverse(this);
}
});
Handlebars.registerHelper('isCurrentProfilePage', function(id, diaspora_handle, options){
var username = diaspora_handle.split("@")[0];
return Handlebars.helpers.isCurrentPage('person', id, options) ||
Handlebars.helpers.isCurrentPage('user_profile', username, options);
});
Handlebars.registerHelper('aspectMembershipIndicator', function(contact,in_aspect) {
if(!app.aspect || !app.aspect.get('id')) return '<div class="aspect_membership_dropdown placeholder"></div>';
var html = '<i class="entypo ';
if( in_aspect === 'in_aspect' ) {
html += 'circled-cross contact_remove-from-aspect" ';
html += 'title="' + Diaspora.I18n.t('contacts.remove_contact') + '" ';
} else {
html += 'circled-plus contact_add-to-aspect" ';
html += 'title="' + Diaspora.I18n.t('contacts.add_contact') + '" ';
}
html += '></i>';
return html;
});
// @license-end

View file

@ -1,4 +1,7 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
/* we need to wrap this in a document ready to ensure JST is accessible */
$(function(){
Handlebars.registerPartial('status-message', JST['status-message_tpl'])
Handlebars.registerPartial('status-message', HandlebarsTemplates['status-message_tpl']);
});
// @license-end

View file

@ -1,14 +1,17 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
(function(){
app.helpers.oEmbed = {
html : function (o_embed_cache) {
if (!o_embed_cache) { return "" }
var data = o_embed_cache.data;
if (data.type == "photo") {
return '<img src="' + data.url + '" width="' + data.width + '" height="' + data.height + '" />'
if (data.type === "photo") {
return '<img src="' + data.url + '" width="' + data.width + '" height="' + data.height + '" />';
} else {
return data.html || ""
return data.html || "";
}
}
}
})();
};
})();
// @license-end

View file

@ -1,8 +1,12 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
(function(){
app.helpers.openGraph = {
html : function (open_graph_cache) {
if (!open_graph_cache) { return "" }
return '<img src="' + open_graph_cache.image + '" />'
}
}
if (!open_graph_cache) { return ""; }
return '<img src="' + open_graph_cache.image + '" />';
},
};
})();
// @license-end

View file

@ -1,144 +1,66 @@
// cache url regex globally, for direct acces when testing
$(function() {
Diaspora.url_regex = /(^|\s)\b((?:(?:https?|ftp):(?:\/{1,3})|www\.)(?:[^"<>\)\s]|\(([^\s()<>]+|(\([^\s()<>]+\)))\))+)(?=\s|$)/gi;
});
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
(function(){
//make it so I take text and mentions rather than the modelapp.helpers.textFormatter(
var textFormatter = function textFormatter(text, model) {
var mentions = model.get("mentioned_people");
app.helpers.textFormatter = function(text, mentions) {
mentions = mentions ? mentions : [];
return textFormatter.mentionify(
textFormatter.hashtagify(
textFormatter.markdownify(text)
), mentions
)
};
var md = window.markdownit({
breaks: true,
html: true,
linkify: true,
typographer: true
});
textFormatter.markdownify = function markdownify(text){
var converter = Markdown.getSanitizingConverter();
var inlinePlugin = window.markdownitForInline;
md.use(inlinePlugin, "utf8_symbols", "text", function (tokens, idx) {
tokens[idx].content = tokens[idx].content.replace(/<->/g, "↔")
.replace(/<-/g, "←")
.replace(/->/g, "→")
.replace(/<3/g, "♥");
});
// punycode non-ascii chars in urls
converter.hooks.chain("preConversion", function(text) {
// add < > around plain urls, effectively making them "autolinks"
text = text.replace(Diaspora.url_regex, function() {
var url = arguments[2];
if( url.match(/^[^\w]/) ) return url; // evil witchcraft, noop
return arguments[1]+"<"+url+">";
});
// process links
// regex copied from: https://code.google.com/p/pagedown/source/browse/Markdown.Converter.js#1198 (and slightly expanded)
var linkRegex = /(\[.*\]:\s)?(<|\()((?:(https?|ftp):\/\/[^\/'">\s]|www)[^'">\s]+?)([>\)]{1,2})/gi;
text = text.replace(linkRegex, function() {
var unicodeUrl = arguments[3];
var urlSuffix = arguments[5];
unicodeUrl = ( unicodeUrl.match(/^www/) ) ? ('http://' + unicodeUrl) : unicodeUrl;
// handle parentheses, especially in case the link ends with ')'
if( urlSuffix.indexOf(')') != -1 && urlSuffix.indexOf('>') != -1 ) {
unicodeUrl += ')';
urlSuffix = '>';
}
// url*DE*code as much as possible
try {
while( unicodeUrl.indexOf("%") !== -1 && unicodeUrl != decodeURI(unicodeUrl) ) {
unicodeUrl = decodeURI(unicodeUrl);
}
}
catch(e){}
// markdown doesn't like '(' or ')' anywhere, except where it wants
var workingUrl = unicodeUrl.replace(/\(/, "%28").replace(/\)/, "%29");
var addr = parse_url(unicodeUrl);
if( !addr.host ) addr.host = ""; // must not be 'undefined'
var asciiUrl = // rebuild the url
(!addr.scheme ? '' : addr.scheme +
( (addr.scheme.toLowerCase()=="mailto") ? ':' : '://')) +
(!addr.user ? '' : addr.user +
(!addr.pass ? '' : ':'+addr.pass) + '@') +
punycode.toASCII(addr.host) +
(!addr.port ? '' : ':' + addr.port) +
(!addr.path ? '' : encodeURI(addr.path) ) +
(!addr.query ? '' : '?' + encodeURI(addr.query) ) +
(!addr.fragment ? '' : '#' + encodeURI(addr.fragment) );
if( !arguments[1] || arguments[1] == "") { // inline link
if(arguments[2] == "<") return "["+workingUrl+"]("+asciiUrl+")"; // without link text
else return arguments[2]+asciiUrl+urlSuffix; // with link text
} else { // reference style link
return arguments[1]+asciiUrl;
md.use(inlinePlugin, "link_new_window_and_missing_http", "link_open", function (tokens, idx) {
tokens[idx].attrs.forEach(function(attribute, index, array) {
if( attribute[0] === "href" ) {
array[index][1] = attribute[1].replace(/^www\./, "http://www.");
}
});
return text;
tokens[idx].attrPush([ "target", "_blank" ]);
});
// make nice little utf-8 symbols
converter.hooks.chain("preConversion", function(text) {
var input_strings = [
"<->", "->", "<-",
"(c)", "(r)", "(tm)",
"<3"
];
var output_symbols = [
"↔", "→", "←",
"©", "®", "™",
"♥"
];
// quote function from: http://stackoverflow.com/a/494122
var quote = function(str) {
return str.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
};
_.each(input_strings, function(str, idx) {
var r = new RegExp(quote(str), "gi");
text = text.replace(r, output_symbols[idx]);
});
return text;
var hashtagPlugin = window.markdownitHashtag;
md.use(hashtagPlugin, {
// compare tag_text_regexp in app/models/acts_as_taggable_on-tag.rb
hashtagRegExp: "[" + PosixBracketExpressions.alnum + "_\\-]+|<3",
// compare tag_strings in lib/diaspora/taggabe.rb
preceding: "^|\\s"
});
converter.hooks.chain("postConversion", function (text) {
return text.replace(/(\"(?:(?:http|https):\/\/)?[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(?:\/\S*)?\")(\>)/g, '$1 target="_blank">')
var mentionPlugin = window.markdownitDiasporaMention;
md.use(mentionPlugin, {
mentions: mentions,
allowHovercards: true,
currentUserId: app.currentUser.get("guid")
});
return converter.makeHtml(text)
var subPlugin = window.markdownitSub;
md.use(subPlugin);
var supPlugin = window.markdownitSup;
md.use(supPlugin);
var sanitizerPlugin = window.markdownitSanitizer;
md.use(sanitizerPlugin);
// xmpp: should behave like mailto:
md.linkify.add("xmpp:","mailto:");
// mumble:// should behave like http://:
md.linkify.add("mumble:","http:");
md.linkify.set({ fuzzyLink: false });
// Bootstrap table markup
md.renderer.rules.table_open = function () { return "<table class=\"table table-striped\">\n"; };
return md.render(text);
};
textFormatter.hashtagify = function hashtagify(text){
var utf8WordCharcters =/(<a[^>]*>.*?<\/a>)|(\s|^|>)#([\u0080-\uFFFF|\w|-]+|&lt;3)/g;
return text.replace(utf8WordCharcters, function(result, linkTag, preceeder, tagText) {
if(linkTag)
return linkTag;
else
return preceeder + "<a href='/tags/" + tagText.toLowerCase() +
"' class='tag'>#" + tagText + "</a>";
});
};
textFormatter.mentionify = function mentionify(text, mentions) {
var mentionRegex = /@\{([^;]+); ([^\}]+)\}/g
return text.replace(mentionRegex, function(mentionText, fullName, diasporaId) {
var person = _.find(mentions, function(person){
return (diasporaId == person.diaspora_id || person.handle) //jquery.mentionsInput gives us person.handle
})
if(person) {
var url = person.url || "/people/" + person.guid //jquery.mentionsInput gives us person.url
, personText = "<a href='" + url + "' class='mention'>" + fullName + "</a>"
} else {
personText = fullName;
}
return personText
})
}
app.helpers.textFormatter = textFormatter;
})();
// @license-end

View file

@ -0,0 +1,9 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
(function(){
app.helpers.timeago = function(el) {
el.find('time.timeago').each(function(i,e) {
$(e).attr('title', new Date($(e).attr('datetime')).toLocaleString());
}).timeago().tooltip();
};
})();
// @license-end

View file

@ -0,0 +1,14 @@
(function() {
app.helpers.truncate = function(passedString, length) {
if (passedString === null || passedString === undefined) {
return passedString;
}
if (passedString.length > length) {
var lastBlank = passedString.lastIndexOf(' ', length);
var trimstring = passedString.substring(0, Math.min(length, lastBlank));
return new Handlebars.SafeString(trimstring + " ...");
}
return new Handlebars.SafeString(passedString);
};
})();

View file

@ -1,3 +1,4 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
// Mixin to provide date formatting and "createdAt" method
// other attributes can be accessed by calling this.timeOf("timestamp-field")
@ -14,3 +15,5 @@ app.models.formatDateMixin = {
}
};
// @license-end

View file

@ -1,5 +1,9 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.models.Aspect = Backbone.Model.extend({
toggleSelected: function(){
this.set({'selected' : !this.get('selected')}, {async: false});
}
});
// @license-end

View file

@ -1,7 +1,11 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
/**
* this model represents the assignment of an aspect to a person.
* (only valid for the context of the current user)
*/
app.models.AspectMembership = Backbone.Model.extend({
urlRoot: "/aspect_memberships"
});
});
// @license-end

View file

@ -1,3 +1,7 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.models.Block = Backbone.Model.extend({
urlRoot : "/blocks"
});
// @license-end

View file

@ -1,3 +1,7 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.models.Comment = Backbone.Model.extend({
urlRoot: "/comments"
});
// @license-end

View file

@ -0,0 +1,13 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.models.Contact = Backbone.Model.extend({
initialize : function() {
this.aspect_memberships = new app.collections.AspectMemberships(this.get('aspect_memberships'));
if( this.get('person') ) this.person = new app.models.Person(this.get('person'));
},
inAspect : function(id) {
return this.aspect_memberships.any(function(membership){ return membership.get('aspect').id === id; });
}
});
// @license-end

View file

@ -1 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.models.Like = Backbone.Model.extend({ });
// @license-end

View file

@ -0,0 +1,53 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.models.Person = Backbone.Model.extend({
url: function() {
return Routes.person_path(this.get('guid'));
},
initialize: function() {
if( this.get('profile') )
this.profile = new app.models.Profile(this.get('profile'));
},
isSharing: function() {
var rel = this.get('relationship');
return (rel === 'mutual' || rel === 'sharing');
},
isReceiving: function() {
var rel = this.get('relationship');
return (rel === 'mutual' || rel === 'receiving');
},
isMutual: function() {
return (this.get('relationship') === 'mutual');
},
isBlocked: function() {
return (this.get('relationship') === 'blocked');
},
block: function() {
var self = this;
var block = new app.models.Block({block: {person_id: this.id}});
// return the jqXHR with Promise interface
return block.save()
.done(function() { app.events.trigger('person:block:'+self.id); });
},
unblock: function() {
var self = this;
if( !this.get('block') ) {
var def = $.Deferred();
return def.reject();
}
var block = new app.models.Block({id: this.get('block').id});
return block.destroy()
.done(function() { app.events.trigger('person:unblock:'+self.id); });
}
});
// @license-end

View file

@ -1,6 +1,10 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.models.Photo = Backbone.Model.extend(_.extend({}, app.models.formatDateMixin, {
urlRoot : "/photos",
initialize : function() {},
}));
// @license-end

View file

@ -1,5 +1,9 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.models.PollParticipation = Backbone.Model.extend({
urlRoot: function(){
return '/posts/' + this.get('post_id') + "/poll_participations";
}
});
// @license-end

View file

@ -1,18 +1,20 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.models.Post = Backbone.Model.extend(_.extend({}, app.models.formatDateMixin, {
urlRoot : "/posts",
initialize : function() {
this.interactions = new app.models.Post.Interactions(_.extend({post : this}, this.get("interactions")))
this.delegateToInteractions()
this.interactions = new app.models.Post.Interactions(_.extend({post : this}, this.get("interactions")));
this.delegateToInteractions();
},
delegateToInteractions : function(){
this.comments = this.interactions.comments
this.likes = this.interactions.likes
this.comments = this.interactions.comments;
this.likes = this.interactions.likes;
this.comment = function(){
this.interactions.comment.apply(this.interactions, arguments)
}
this.interactions.comment.apply(this.interactions, arguments);
};
},
interactedAt : function() {
@ -20,11 +22,12 @@ app.models.Post = Backbone.Model.extend(_.extend({}, app.models.formatDateMixin,
},
reshare : function(){
return this._reshare = this._reshare || new app.models.Reshare({root_guid : this.get("guid")});
this._reshare = this._reshare || new app.models.Reshare({root_guid : this.get("guid")});
return this._reshare;
},
reshareAuthor : function(){
return this.get("author")
return this.get("author");
},
blockAuthor: function() {
@ -36,7 +39,7 @@ app.models.Post = Backbone.Model.extend(_.extend({}, app.models.formatDateMixin,
},
toggleFavorite : function(options){
this.set({favorite : !this.get("favorite")})
this.set({favorite : !this.get("favorite")});
/* guard against attempting to save a model that a user doesn't own */
if(options.save){ this.save() }
@ -44,27 +47,29 @@ app.models.Post = Backbone.Model.extend(_.extend({}, app.models.formatDateMixin,
headline : function() {
var headline = this.get("text").trim()
, newlineIdx = headline.indexOf("\n")
return (newlineIdx > 0 ) ? headline.substr(0, newlineIdx) : headline
, newlineIdx = headline.indexOf("\n");
return (newlineIdx > 0 ) ? headline.substr(0, newlineIdx) : headline;
},
body : function(){
var body = this.get("text").trim()
, newlineIdx = body.indexOf("\n")
return (newlineIdx > 0 ) ? body.substr(newlineIdx+1, body.length) : ""
, newlineIdx = body.indexOf("\n");
return (newlineIdx > 0 ) ? body.substr(newlineIdx+1, body.length) : "";
},
//returns a promise
preloadOrFetch : function(){
var action = app.hasPreload("post") ? this.set(app.parsePreload("post")) : this.fetch()
return $.when(action)
var action = app.hasPreload("post") ? this.set(app.parsePreload("post")) : this.fetch();
return $.when(action);
},
hasPhotos : function(){
return this.get("photos") && this.get("photos").length > 0
return this.get("photos") && this.get("photos").length > 0;
},
hasText : function(){
return $.trim(this.get("text")) !== ""
return $.trim(this.get("text")) !== "";
}
}));
// @license-end

View file

@ -1,136 +1,139 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
//require ../post
app.models.Post.Interactions = Backbone.Model.extend({
url : function(){
return this.post.url() + "/interactions"
return this.post.url() + "/interactions";
},
initialize : function(options){
this.post = options.post
this.comments = new app.collections.Comments(this.get("comments"), {post : this.post})
this.post = options.post;
this.comments = new app.collections.Comments(this.get("comments"), {post : this.post});
this.likes = new app.collections.Likes(this.get("likes"), {post : this.post});
this.reshares = new app.collections.Reshares(this.get("reshares"), {post : this.post});
},
parse : function(resp){
this.comments.reset(resp.comments)
this.likes.reset(resp.likes)
this.reshares.reset(resp.reshares)
this.comments.reset(resp.comments);
this.likes.reset(resp.likes);
this.reshares.reset(resp.reshares);
var comments = this.comments
, likes = this.likes
, reshares = this.reshares
, reshares = this.reshares;
return {
comments : comments,
likes : likes,
reshares : reshares,
fetched : true
}
};
},
likesCount : function(){
return (this.get("fetched") ? this.likes.models.length : this.get("likes_count") )
return (this.get("fetched") ? this.likes.models.length : this.get("likes_count") );
},
resharesCount : function(){
return this.get("fetched") ? this.reshares.models.length : this.get("reshares_count")
return this.get("fetched") ? this.reshares.models.length : this.get("reshares_count");
},
commentsCount : function(){
return this.get("fetched") ? this.comments.models.length : this.get("comments_count")
return this.get("fetched") ? this.comments.models.length : this.get("comments_count");
},
userLike : function(){
return this.likes.select(function(like){ return like.get("author").guid == app.currentUser.get("guid")})[0]
return this.likes.select(function(like){ return like.get("author").guid === app.currentUser.get("guid")})[0];
},
userReshare : function(){
return this.reshares.select(function(reshare){
return reshare.get("author") && reshare.get("author").guid == app.currentUser.get("guid")})[0]
return reshare.get("author") && reshare.get("author").guid === app.currentUser.get("guid")})[0];
},
toggleLike : function() {
if(this.userLike()) {
this.unlike()
this.unlike();
} else {
this.like()
this.like();
}
},
like : function() {
var self = this;
this.likes.create({}, {success : function(){
self.trigger("change")
self.set({"likes_count" : self.get("likes_count") + 1})
}})
self.trigger("change");
self.set({"likes_count" : self.get("likes_count") + 1});
}});
app.instrument("track", "Like")
app.instrument("track", "Like");
},
unlike : function() {
var self = this;
this.userLike().destroy({success : function(model, resp) {
self.trigger('change')
self.set({"likes_count" : self.get("likes_count") - 1})
this.userLike().destroy({success : function() {
self.trigger('change');
self.set({"likes_count" : self.get("likes_count") - 1});
}});
app.instrument("track", "Unlike")
app.instrument("track", "Unlike");
},
comment : function (text) {
var self = this;
this.comments.make(text).fail(function () {
flash = new Diaspora.Widgets.FlashMessages;
var flash = new Diaspora.Widgets.FlashMessages();
flash.render({
success: false,
notice: Diaspora.I18n.t("failed_to_post_message")
});
}).done(function() {
self.trigger('change') //updates after sync
self.trigger('change'); //updates after sync
});
this.trigger("change") //updates count in an eager manner
this.trigger("change"); //updates count in an eager manner
app.instrument("track", "Comment")
app.instrument("track", "Comment");
},
reshare : function(){
var interactions = this
, reshare = this.post.reshare()
, flash = new Diaspora.Widgets.FlashMessages;
, flash = new Diaspora.Widgets.FlashMessages();
reshare.save({}, {
success : function(resp){
reshare.save()
.done(function(reshare) {
flash.render({
success: true,
notice: Diaspora.I18n.t("reshares.successful")
});
},
error: function(resp){
interactions.reshares.add(reshare);
if (app.stream && /^\/(?:stream|activity|aspects)/.test(app.stream.basePath())) {
app.stream.addNow(reshare);
}
interactions.trigger("change");
})
.fail(function(){
flash.render({
success: false,
notice: Diaspora.I18n.t("reshares.duplicate")
});
}
}).done(function(){
interactions.reshares.add(reshare)
}).done(function(){
interactions.trigger("change")
});
app.instrument("track", "Reshare")
app.instrument("track", "Reshare");
},
userCanReshare : function(){
var isReshare = this.post.get("post_type") == "Reshare"
var isReshare = this.post.get("post_type") === "Reshare"
, rootExists = (isReshare ? this.post.get("root") : true)
, publicPost = this.post.get("public")
, userIsNotAuthor = this.post.get("author").diaspora_id != app.currentUser.get("diaspora_id")
, userIsNotRootAuthor = rootExists && (isReshare ? this.post.get("root").author.diaspora_id != app.currentUser.get("diaspora_id") : true)
, userIsNotAuthor = this.post.get("author").diaspora_id !== app.currentUser.get("diaspora_id")
, userIsNotRootAuthor = rootExists && (isReshare ? this.post.get("root").author.diaspora_id !== app.currentUser.get("diaspora_id") : true)
, notReshared = !this.userReshare();
return publicPost && app.currentUser.authenticated() && userIsNotAuthor && userIsNotRootAuthor && notReshared;
}
});
// @license-end

View file

@ -1,20 +1,24 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.models.Profile = Backbone.Model.extend({
urlRoot : "/profiles"
}, {
preloadOrFetch : function(id){
return app.hasPreload("person") ? this.preload() : this.findByGuid(id)
return app.hasPreload("person") ? this.preload() : this.findByGuid(id);
},
preload : function(){
var person = new app.models.Profile(app.parsePreload("person"))
person.deferred = $.when(true)
return person
var person = new app.models.Profile(app.parsePreload("person"));
person.deferred = $.when(true);
return person;
},
findByGuid : function(personId){
var person = new app.models.Profile({ id : personId})
person.deferred = person.fetch()
return person
var person = new app.models.Profile({ id : personId});
person.deferred = person.fetch();
return person;
}
});
// @license-end

View file

@ -1,4 +1,8 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.models.Report = Backbone.Model.extend({
urlRoot: '/report',
type: 'POST'
});
// @license-end

View file

@ -1,16 +1,20 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.models.Reshare = app.models.Post.extend({
urlRoot : "/reshares",
rootPost : function(){
this._rootPost = this._rootPost || new app.models.Post(this.get("root"));
return this._rootPost
return this._rootPost;
},
reshare : function(){
return this.rootPost().reshare()
return this.rootPost().reshare();
},
reshareAuthor : function(){
return this.rootPost().reshareAuthor()
return this.rootPost().reshareAuthor();
}
});
// @license-end

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.models.StatusMessage = app.models.Post.extend({
url : function(){
return this.isNew() ? '/status_messages' : '/posts/' + this.get("id");
@ -15,6 +17,8 @@ app.models.StatusMessage = app.models.Post.extend({
photos : this.photos && this.photos.pluck("id"),
services : this.get("services"),
poll : this.get("poll")
}
};
}
});
// @license-end

View file

@ -1,49 +1,54 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
//= require ../collections/posts
//= require ../collections/photos
app.models.Stream = Backbone.Collection.extend({
initialize : function(models, options){
var collectionClass = options && options.collection || app.collections.Posts;
var collectionClass = app.collections.Posts;
if( options ) {
options.collection && (collectionClass = options.collection);
options.basePath && (this.streamPath = options.basePath);
}
this.items = new collectionClass([], this.collectionOptions());
},
collectionOptions :function(){
var order = this.sortOrder();
return { comparator : function(item) { return -item[order](); } }
return { comparator : function(item) { return -item[order](); } };
},
url : function(){
return _.any(this.items.models) ? this.timeFilteredPath() : this.basePath()
return _.any(this.items.models) ? this.timeFilteredPath() : this.basePath();
},
_fetchOpts: function(opts) {
var defaultOpts = {
remove: false // tell backbone to keep existing items in the collection
};
return _.extend({}, defaultOpts, opts);
return _.extend({ url: this.url() }, defaultOpts, opts);
},
fetch: function() {
if( this.isFetching() ) return false;
var url = this.url();
this.deferred = this.items.fetch(this._fetchOpts({url : url}))
this.deferred = this.items.fetch( this._fetchOpts() )
.done(_.bind(this.triggerFetchedEvents, this));
},
isFetching : function() {
return (this.deferred && this.deferred.state() == "pending");
return (this.deferred && this.deferred.state() === "pending");
},
triggerFetchedEvents : function(resp){
this.trigger("fetched", this);
// all loaded?
var respItems = this.items.parse(resp);
if(respItems && (respItems.author || respItems.length == 0)) {
if(respItems && (respItems.author || respItems.length === 0)) {
this.trigger("allItemsLoaded", this);
}
},
basePath : function(){
return document.location.pathname;
return this.streamPath || document.location.pathname;
},
timeFilteredPath : function(){
@ -52,11 +57,11 @@ app.models.Stream = Backbone.Collection.extend({
maxTime: function(){
var lastPost = _.last(this.items.models);
return lastPost[this.sortOrder()]()
return lastPost[this.sortOrder()]();
},
sortOrder : function() {
return this.basePath().match(/activity/) ? "interactedAt" : "createdAt"
return this.basePath().match(/activity/) ? "interactedAt" : "createdAt";
},
/* This function is for adding a large number of posts one by one.
@ -66,7 +71,7 @@ app.models.Stream = Backbone.Collection.extend({
* stream for the changes to take effect in the infinite stream view
*/
add : function(models){
this.items.add(models)
this.items.add(models);
},
/* This function is for adding a single post. It immediately triggers
@ -79,12 +84,13 @@ app.models.Stream = Backbone.Collection.extend({
},
preloadOrFetch : function(){ //hai, plz test me THNX
return $.when(app.hasPreload("stream") ? this.preload() : this.fetch())
return $.when(app.hasPreload("stream") ? this.preload() : this.fetch());
},
preload : function(){
this.items.reset(app.parsePreload("stream"))
this.deferred = $.when(true)
this.trigger("fetched")
this.items.reset(app.parsePreload("stream"));
this.deferred = $.when(true);
this.trigger("fetched");
}
});
// @license-end

View file

@ -1,7 +1,9 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.models.StreamAspects = app.models.Stream.extend({
url : function(){
return _.any(this.items.models) ? this.timeFilteredPath() : this.basePath()
return _.any(this.items.models) ? this.timeFilteredPath() : this.basePath();
},
initialize : function(models, options){
@ -19,6 +21,15 @@ app.models.StreamAspects = app.models.Stream.extend({
var url = this.url();
var ids = this.aspects_ids;
this.deferred = this.items.fetch(this._fetchOpts({url : url, data : { 'a_ids': ids }}))
.done(_.bind(this.triggerFetchedEvents, this));
.done(_.bind(this.fetchDone, this));
},
fetchDone: function() {
this.triggerFetchedEvents();
if (app.aspects) {
app.aspects.trigger('aspectStreamFetched');
}
}
});
// @license-end

View file

@ -1,3 +1,7 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.models.TagFollowing = Backbone.Model.extend({
urlRoot: "/tag_followings"
});
// @license-end

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.models.User = Backbone.Model.extend({
toggleNsfwState : function() {
if(!app.currentUser.authenticated()){ return false }
@ -10,14 +12,16 @@ app.models.User = Backbone.Model.extend({
},
expProfileUrl : function(){
return "/people/" + app.currentUser.get("guid") + "?ex=true"
return "/people/" + app.currentUser.get("guid") + "?ex=true";
},
isServiceConfigured : function(providerName) {
return _.include(this.get("configured_services"), providerName)
return _.include(this.get("configured_services"), providerName);
},
isAuthorOf: function(model) {
return this.authenticated() && model.get("author").id == this.id;
return this.authenticated() && model.get("author").id === this.id;
}
});
// @license-end

View file

@ -0,0 +1,75 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.pages.Contacts = Backbone.View.extend({
el: "#contacts_container",
events: {
"click #contacts_visibility_toggle" : "toggleContactVisibility",
"click #chat_privilege_toggle" : "toggleChatPrivilege",
"click #change_aspect_name" : "showAspectNameForm",
"keyup #contact_list_search" : "searchContactList"
},
initialize: function(opts) {
this.visibility_toggle = $("#contacts_visibility_toggle .entypo");
this.chat_toggle = $("#chat_privilege_toggle .entypo");
this.stream = opts.stream;
this.stream.render();
$("#people_stream.contacts .header .entypo").tooltip({ 'placement': 'bottom'});
$(document).on('ajax:success', 'form.edit_aspect', this.updateAspectName);
},
toggleChatPrivilege: function() {
if (this.chat_toggle.hasClass("enabled")) {
this.chat_toggle.tooltip("destroy")
.removeClass("enabled")
.removeAttr("data-original-title")
.attr("title", Diaspora.I18n.t("contacts.aspect_chat_is_not_enabled"))
.tooltip({'placement': 'bottom'});
} else {
this.chat_toggle.tooltip("destroy")
.addClass("enabled")
.removeAttr("data-original-title")
.attr("title", Diaspora.I18n.t("contacts.aspect_chat_is_enabled"))
.tooltip({'placement': 'bottom'});
}
},
toggleContactVisibility: function() {
if (this.visibility_toggle.hasClass("lock-open")) {
this.visibility_toggle.removeClass("lock-open")
.addClass("lock")
.tooltip("destroy")
.removeAttr("data-original-title")
.attr("title", Diaspora.I18n.t("contacts.aspect_list_is_not_visible"))
.tooltip({'placement': 'bottom'});
}
else {
this.visibility_toggle.removeClass("lock")
.addClass("lock-open")
.tooltip("destroy")
.removeAttr("data-original-title")
.attr("title", Diaspora.I18n.t("contacts.aspect_list_is_visible"))
.tooltip({'placement': 'bottom'});
}
},
showAspectNameForm: function() {
$(".header > h3").hide();
$(".header > #aspect_name_form").show();
},
updateAspectName: function(evt,data){
$(".header #aspect_name").text(data['name']);
$("#aspect_nav [data-aspect-id='"+data['id']+"'] .name").text(data['name']);
$(".header > #aspect_name_form").hide();
$(".header > h3").show();
},
searchContactList: function(e) {
this.stream.search($(e.target).val());
}
});
// @license-end

View file

@ -0,0 +1,130 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.pages.Profile = app.views.Base.extend({
events: {
'click #block_user_button': 'blockPerson',
'click #unblock_user_button': 'unblockPerson'
},
subviews: {
'#profile': 'sidebarView',
'.profile_header': 'headerView',
'#main_stream': 'streamView'
},
tooltipSelector: '.profile_button .profile-header-icon, .sharing_message_container',
initialize: function(opts) {
if( !this.model ) {
this._populateModel(opts);
}
if( app.hasPreload('photos') )
this.photos = app.parsePreload('photos'); // we don't interact with it, so no model
if( app.hasPreload('contacts') )
this.contacts = app.parsePreload('contacts'); // we don't interact with it, so no model
this.streamCollection = _.has(opts, 'streamCollection') ? opts.streamCollection : null;
this.streamViewClass = _.has(opts, 'streamView') ? opts.streamView : null;
this.model.on('change', this.render, this);
this.model.on('sync', this._done, this);
// bind to global events
var id = this.model.get('id');
app.events.on('person:block:'+id, this.reload, this);
app.events.on('person:unblock:'+id, this.reload, this);
app.events.on('aspect:create', this.reload, this);
app.events.on('aspect_membership:update', this.reload, this);
},
_populateModel: function(opts) {
if( app.hasPreload('person') ) {
this.model = new app.models.Person(app.parsePreload('person'));
} else if(opts && opts.person_id) {
this.model = new app.models.Person({guid: opts.person_id});
this.model.fetch();
} else {
throw new Error("unable to load person");
}
},
sidebarView: function() {
if( !this.model.has('profile') ) return false;
return new app.views.ProfileSidebar({
model: this.model,
});
},
headerView: function() {
if( !this.model.has('profile') ) return false;
return new app.views.ProfileHeader({
model: this.model,
photos: this.photos,
contacts: this.contacts
});
},
streamView: function() {
if( !this.model.has('profile') ) return false;
if( this.model.isBlocked() ) {
$('#main_stream').empty().html(
'<div class="dull">'+
Diaspora.I18n.t('profile.ignoring', {name: this.model.get('name')}) +
'</div>');
return false;
}
// a collection is set, this means we want to view photos
var route = this.streamCollection ? 'person_photos_path' : 'person_stream_path';
var view = this.streamViewClass ? this.streamViewClass : app.views.Stream;
app.stream = new app.models.Stream(null, {
basePath: Routes[route](app.page.model.get('guid')),
collection: this.streamCollection
});
app.stream.fetch();
if( this.model.get('is_own_profile') ) {
app.publisher = new app.views.Publisher({collection : app.stream.items});
}
return new view({model: app.stream});
},
blockPerson: function() {
if( !confirm(Diaspora.I18n.t('ignore_user')) ) return;
var block = this.model.block();
block.fail(function() {
Diaspora.page.flashMessages.render({
success: false,
notice: Diaspora.I18n.t('ignore_failed')
});
});
return false;
},
unblockPerson: function() {
var block = this.model.unblock();
block.fail(function() {
Diaspora.page.flashMessages.render({
success: false,
notice: Diaspora.I18.t('unblock_failed')
});
});
return false;
},
reload: function() {
this.$('#profile').addClass('loading');
this.model.fetch();
},
_done: function() {
this.$('#profile').removeClass('loading');
}
});
// @license-end

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.pages.SinglePostViewer = app.views.Base.extend({
templateName: "single-post-viewer",
@ -9,8 +11,8 @@ app.pages.SinglePostViewer = app.views.Base.extend({
initialize : function(options) {
this.model = new app.models.Post({ id : options.id });
this.model.preloadOrFetch().done(_.bind(this.initViews, this));
this.model.interactions.fetch() //async, yo, might want to throttle this later.
this.setupLightbox()
this.model.interactions.fetch(); //async, yo, might want to throttle this later.
this.setupLightbox();
},
setupLightbox : function(){
@ -31,10 +33,11 @@ app.pages.SinglePostViewer = app.views.Base.extend({
postRenderTemplate : function() {
if(this.model.get("title")){
// formats title to html...
var html_title = app.helpers.textFormatter(this.model.get("title"), this.model);
var html_title = app.helpers.textFormatter(this.model.get("title"), this.model.get("mentioned_people"));
//... and converts html to plain text
document.title = $('<div>').html(html_title).text();
}
},
});
// @license-end

View file

@ -1,6 +1,12 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.Router = Backbone.Router.extend({
routes: {
"help/:section": "help",
"help/": "help",
"help": "help",
"contacts": "contacts",
"conversations": "conversations",
//new hotness
"posts/:id": "singlePost",
@ -19,11 +25,12 @@ app.Router = Backbone.Router.extend({
"followed_tags": "followed_tags",
"tags/:name": "followed_tags",
"people/:id/photos": "photos",
"people/:id/contacts": "profile",
"people/:id": "stream",
"u/:name": "stream"
"people/:id": "profile",
"u/:name": "profile"
},
initialize: function() {
// To support encoded linefeeds (%0A) we need to specify
// our own internal router.route call with the correct regexp.
@ -31,10 +38,26 @@ app.Router = Backbone.Router.extend({
this.route(/^bookmarklet(?:\?(.*))?/, "bookmarklet");
},
help: function() {
help: function(section) {
app.help = new app.views.Help();
$("#help").prepend(app.help.el);
app.help.render();
app.help.render(section);
},
contacts: function() {
app.aspect = new app.models.Aspect(gon.preloads.aspect);
app.contacts = new app.collections.Contacts(app.parsePreload('contacts'));
var stream = new app.views.ContactStream({
collection: app.contacts,
el: $('.stream.contacts #contact_stream'),
});
app.page = new app.pages.Contacts({stream: stream});
},
conversations: function() {
app.conversations = new app.views.Conversations();
},
singlePost : function(id) {
@ -42,14 +65,19 @@ app.Router = Backbone.Router.extend({
},
renderPage : function(pageConstructor){
app.page && app.page.unbind && app.page.unbind() //old page might mutate global events $(document).keypress, so unbind before creating
app.page = pageConstructor() //create new page after the world is clean (like that will ever happen)
$("#container").html(app.page.render().el)
app.page && app.page.unbind && app.page.unbind(); //old page might mutate global events $(document).keypress, so unbind before creating
app.page = pageConstructor(); //create new page after the world is clean (like that will ever happen)
app.page.render();
if( !$.contains(document, app.page.el) ) {
// view element isn't already attached to the DOM, insert it
$("#container").empty().append(app.page.el);
}
},
//below here is oldness
stream : function(page) {
stream : function() {
app.stream = new app.models.Stream();
app.stream.fetch();
app.page = new app.views.Stream({model : app.stream});
@ -59,13 +87,18 @@ app.Router = Backbone.Router.extend({
$("#main_stream").html(app.page.render().el);
$('#selected_aspect_contacts .content').html(streamFacesView.render().el);
this.hideInactiveStreamLists();
this._hideInactiveStreamLists();
},
photos : function() {
app.photos = new app.models.Stream([], {collection: app.collections.Photos});
app.page = new app.views.Photos({model : app.photos});
$("#main_stream").html(app.page.render().el);
photos : function(guid) {
this.renderPage(function() {
return new app.pages.Profile({
person_id: guid,
el: $('body > .container-fluid'),
streamCollection: app.collections.Photos,
streamView: app.views.Photos
});
});
},
followed_tags : function(name) {
@ -82,9 +115,10 @@ app.Router = Backbone.Router.extend({
var followedTagsAction = new app.views.TagFollowingAction(
{tagText: decodeURIComponent(name).toLowerCase()}
);
$("#author_info").prepend(followedTagsAction.render().el)
$("#author_info").prepend(followedTagsAction.render().el);
app.tags = new app.views.Tags({tagName: name});
}
this.hideInactiveStreamLists();
this._hideInactiveStreamLists();
},
aspects : function(){
@ -107,22 +141,28 @@ app.Router = Backbone.Router.extend({
$("#main_stream").html(app.page.render().el);
$('#selected_aspect_contacts .content').html(streamFacesView.render().el);
this.hideInactiveStreamLists();
this._hideInactiveStreamLists();
},
hideInactiveStreamLists: function() {
if(this.aspects_list && Backbone.history.fragment != "aspects")
_hideInactiveStreamLists: function() {
if(this.aspects_list && Backbone.history.fragment !== "aspects")
this.aspects_list.hideAspectsList();
if(this.followedTagsView && Backbone.history.fragment != "followed_tags")
if(this.followedTagsView && Backbone.history.fragment !== "followed_tags")
this.followedTagsView.hideFollowedTags();
},
bookmarklet: function() {
var contents = (window.gon) ? gon.preloads.bookmarklet : {}
var contents = (window.gon) ? gon.preloads.bookmarklet : {};
app.bookmarklet = new app.views.Bookmarklet(
_.extend({}, {el: $('#bookmarklet')}, contents)
).render();
},
profile: function() {
this.renderPage(function() { return new app.pages.Profile({
el: $('body > .container-fluid')
}); });
}
});
// @license-end

View file

@ -1,11 +1,13 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.Base = Backbone.View.extend({
initialize : function(options) {
initialize : function() {
this.setupRenderEvents();
},
presenter : function(){
return this.defaultPresenter()
return this.defaultPresenter();
},
setupRenderEvents : function(){
@ -16,7 +18,7 @@ app.views.Base = Backbone.View.extend({
},
defaultPresenter : function(){
var modelJson = this.model && this.model.attributes ? _.clone(this.model.attributes) : {}
var modelJson = this.model && this.model.attributes ? _.clone(this.model.attributes) : {};
return _.extend(modelJson, {
current_user : app.currentUser.attributes,
@ -25,24 +27,29 @@ app.views.Base = Backbone.View.extend({
},
render : function() {
this.renderTemplate()
this.renderSubviews()
this.renderPluginWidgets()
this.removeTooltips()
this.renderTemplate();
this.renderSubviews();
this.renderPluginWidgets();
this.removeTooltips();
return this
return this;
},
renderTemplate : function(){
var presenter = _.isFunction(this.presenter) ? this.presenter() : this.presenter
this.template = JST[this.templateName+"_tpl"]
var presenter = _.isFunction(this.presenter) ? this.presenter() : this.presenter;
this.template = HandlebarsTemplates[this.templateName+"_tpl"];
if(!this.template) {
console.log(this.templateName ? ("no template for " + this.templateName) : "no templateName specified")
console.log(this.templateName ? ("no template for " + this.templateName) : "no templateName specified");
return;
}
this.$el
.html(this.template(presenter))
.attr("data-template", _.last(this.templateName.split("/")));
// add avatar fallback if it can't be loaded
this.$el.find(this.avatars.selector).error(this.avatars.fallback);
this.postRenderTemplate();
},
@ -51,12 +58,12 @@ app.views.Base = Backbone.View.extend({
renderSubviews : function(){
var self = this;
_.each(this.subviews, function(property, selector){
var view = _.isFunction(self[property]) ? self[property]() : self[property]
var view = _.isFunction(self[property]) ? self[property]() : self[property];
if(view) {
self.$(selector).html(view.render().el)
self.$(selector).html(view.render().el);
view.delegateEvents();
}
})
});
},
renderPluginWidgets : function() {
@ -69,16 +76,16 @@ app.views.Base = Backbone.View.extend({
},
setFormAttrs : function(){
this.model.set(_.inject(this.formAttrs, _.bind(setValueFromField, this), {}))
function setValueFromField(memo, attribute, selector){
if(attribute.slice("-2") === "[]") {
memo[attribute.slice(0, attribute.length - 2)] = _.pluck(this.$el.find(selector).serializeArray(), "value")
memo[attribute.slice(0, attribute.length - 2)] = _.pluck(this.$el.find(selector).serializeArray(), "value");
} else {
memo[attribute] = this.$el.find(selector).val() || this.$el.find(selector).text();
}
return memo
return memo;
}
this.model.set(_.inject(this.formAttrs, _.bind(setValueFromField, this), {}));
},
report: function(evt) {
@ -97,13 +104,13 @@ app.views.Base = Backbone.View.extend({
var report = new app.models.Report();
report.save(data, {
success: function(model, response) {
success: function() {
Diaspora.page.flashMessages.render({
success: true,
notice: Diaspora.I18n.t('report.status.created')
});
},
error: function(model, response) {
error: function() {
Diaspora.page.flashMessages.render({
success: false,
notice: Diaspora.I18n.t('report.status.exists')
@ -118,12 +125,14 @@ app.views.Base = Backbone.View.extend({
var url = this.model.urlRoot + '/' + this.model.id;
if (confirm(Diaspora.I18n.t("confirm_dialog"))) {
this.$el.addClass('deleting');
this.model.destroy({ url: url })
.done(function() {
self.remove();
})
.fail(function() {
var flash = new Diaspora.Widgets.FlashMessages;
self.$el.removeClass('deleting');
var flash = new Diaspora.Widgets.FlashMessages();
flash.render({
success: false,
notice: Diaspora.I18n.t('failed_to_remove')
@ -131,6 +140,13 @@ app.views.Base = Backbone.View.extend({
});
}
},
avatars: {
fallback: function() {
$(this).attr("src", ImagePaths.get("user/default.png"));
},
selector: "img.avatar"
}
});
app.views.StaticContentView = app.views.Base.extend({
@ -146,3 +162,4 @@ app.views.StaticContentView = app.views.Base.extend({
return this.data;
},
});
// @license-end

View file

@ -1,161 +0,0 @@
/**
* this view lets the user (de-)select aspect memberships in the context
* of another users profile or the contact page.
*
* updates to the list of aspects are immediately propagated to the server, and
* the results are dislpayed as flash messages.
*/
app.views.AspectMembershipBlueprint = Backbone.View.extend({
initialize: function() {
// attach event handler, removing any previous instances
var selector = '.dropdown.aspect_membership .dropdown_list > li';
$('body')
.off('click', selector)
.on('click', selector, _.bind(this._clickHandler, this));
this.list_item = null;
this.dropdown = null;
},
// decide what to do when clicked
// -> addMembership
// -> removeMembership
_clickHandler: function(evt) {
this.list_item = $(evt.target);
this.dropdown = this.list_item.parent();
this.list_item.addClass('loading');
if( this.list_item.is('.selected') ) {
var membership_id = this.list_item.data('membership_id');
this.removeMembership(membership_id);
} else {
var aspect_id = this.list_item.data('aspect_id');
var person_id = this.dropdown.data('person_id');
this.addMembership(person_id, aspect_id);
}
return false; // stop the event
},
// return the (short) name of the person associated with the current dropdown
_name: function() {
return this.dropdown.data('person-short-name');
},
// create a membership for the given person in the given aspect
addMembership: function(person_id, aspect_id) {
var aspect_membership = new app.models.AspectMembership({
'person_id': person_id,
'aspect_id': aspect_id
});
aspect_membership.on('sync', this._successSaveCb, this);
aspect_membership.on('error', function() {
this._displayError('aspect_dropdown.error');
}, this);
aspect_membership.save();
},
_successSaveCb: function(aspect_membership) {
var aspect_id = aspect_membership.get('aspect_id');
var membership_id = aspect_membership.get('id');
var li = this.dropdown.find('li[data-aspect_id="'+aspect_id+'"]');
// the user didn't have this person in any aspects before, congratulate them
// on their newly found friendship ;)
if( this.dropdown.find('li.selected').length == 0 ) {
var msg = Diaspora.I18n.t('aspect_dropdown.started_sharing_with', { 'name': this._name() });
Diaspora.page.flashMessages.render({ 'success':true, 'notice':msg });
}
li.attr('data-membership_id', membership_id) // just to be sure...
.data('membership_id', membership_id)
.addClass('selected');
this.updateSummary();
this._done();
},
// show an error flash msg
_displayError: function(msg_id) {
this._done();
this.dropdown.removeClass('active'); // close the dropdown
var msg = Diaspora.I18n.t(msg_id, { 'name': this._name() });
Diaspora.page.flashMessages.render({ 'success':false, 'notice':msg });
},
// remove the membership with the given id
removeMembership: function(membership_id) {
var aspect_membership = new app.models.AspectMembership({
'id': membership_id
});
aspect_membership.on('sync', this._successDestroyCb, this);
aspect_membership.on('error', function() {
this._displayError('aspect_dropdown.error_remove');
}, this);
aspect_membership.destroy();
},
_successDestroyCb: function(aspect_membership) {
var membership_id = aspect_membership.get('id');
var li = this.dropdown.find('li[data-membership_id="'+membership_id+'"]');
li.removeAttr('data-membership_id')
.removeData('membership_id')
.removeClass('selected');
// we just removed the last aspect, inform the user with a flash message
// that he is no longer sharing with that person
if( this.dropdown.find('li.selected').length == 0 ) {
var msg = Diaspora.I18n.t('aspect_dropdown.stopped_sharing_with', { 'name': this._name() });
Diaspora.page.flashMessages.render({ 'success':true, 'notice':msg });
}
this.updateSummary();
this._done();
},
// cleanup tasks after aspect selection
_done: function() {
if( this.list_item ) {
this.list_item.removeClass('loading');
}
},
// refresh the button text to reflect the current aspect selection status
updateSummary: function() {
var btn = this.dropdown.parents('div.aspect_membership').find('.button.toggle');
var aspects_cnt = this.dropdown.find('li.selected').length;
var txt;
if( aspects_cnt == 0 ) {
btn.removeClass('in_aspects');
txt = Diaspora.I18n.t('aspect_dropdown.toggle.zero');
} else {
btn.addClass('in_aspects');
txt = this._pluralSummaryTxt(aspects_cnt);
}
btn.text(txt + ' ▼');
},
_pluralSummaryTxt: function(cnt) {
var all_aspects_cnt = this.dropdown.find('li').length;
if( cnt == 1 ) {
return this.dropdown.find('li.selected').first().text();
}
if( cnt == all_aspects_cnt ) {
return Diaspora.I18n.t('aspect_dropdown.all_aspects');
}
return Diaspora.I18n.t('aspect_dropdown.toggle', { 'count':cnt.toString() });
}
});

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
//= require ./aspects_dropdown_view
/**
@ -10,7 +12,8 @@
app.views.AspectMembership = app.views.AspectsDropdown.extend({
events: {
"click ul.aspect_membership.dropdown-menu > li.aspect_selector": "_clickHandler"
"click ul.aspect_membership.dropdown-menu > li.aspect_selector": "_clickHandler",
"keypress ul.aspect_membership.dropdown-menu > li.aspect_selector": "_clickHandler"
},
initialize: function() {
@ -22,6 +25,7 @@ app.views.AspectMembership = app.views.AspectsDropdown.extend({
// -> addMembership
// -> removeMembership
_clickHandler: function(evt) {
var promise = null;
this.list_item = $(evt.target).closest('li.aspect_selector');
this.dropdown = this.list_item.parent();
@ -29,13 +33,18 @@ app.views.AspectMembership = app.views.AspectsDropdown.extend({
if( this.list_item.is('.selected') ) {
var membership_id = this.list_item.data('membership_id');
this.removeMembership(membership_id);
promise = this.removeMembership(membership_id);
} else {
var aspect_id = this.list_item.data('aspect_id');
var person_id = this.dropdown.data('person_id');
this.addMembership(person_id, aspect_id);
promise = this.addMembership(person_id, aspect_id);
}
promise && promise.always(function() {
// trigger a global event
app.events.trigger('aspect_membership:update');
});
return false; // stop the event
},
@ -56,7 +65,7 @@ app.views.AspectMembership = app.views.AspectsDropdown.extend({
this._displayError('aspect_dropdown.error');
}, this);
aspect_membership.save();
return aspect_membership.save();
},
_successSaveCb: function(aspect_membership) {
@ -66,7 +75,7 @@ app.views.AspectMembership = app.views.AspectsDropdown.extend({
// the user didn't have this person in any aspects before, congratulate them
// on their newly found friendship ;)
if( this.dropdown.find('li.selected').length == 0 ) {
if( this.dropdown.find('li.selected').length === 0 ) {
var msg = Diaspora.I18n.t('aspect_dropdown.started_sharing_with', { 'name': this._name() });
Diaspora.page.flashMessages.render({ 'success':true, 'notice':msg });
}
@ -98,7 +107,7 @@ app.views.AspectMembership = app.views.AspectsDropdown.extend({
this._displayError('aspect_dropdown.error_remove');
}, this);
aspect_membership.destroy();
return aspect_membership.destroy();
},
_successDestroyCb: function(aspect_membership) {
@ -111,7 +120,7 @@ app.views.AspectMembership = app.views.AspectsDropdown.extend({
// we just removed the last aspect, inform the user with a flash message
// that he is no longer sharing with that person
if( this.dropdown.find('li.selected').length == 0 ) {
if( this.dropdown.find('li.selected').length === 0 ) {
var msg = Diaspora.I18n.t('aspect_dropdown.stopped_sharing_with', { 'name': this._name() });
Diaspora.page.flashMessages.render({ 'success':true, 'notice':msg });
}
@ -132,3 +141,5 @@ app.views.AspectMembership = app.views.AspectsDropdown.extend({
this._updateButton('green');
},
});
// @license-end

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.Aspect = app.views.Base.extend({
templateName: "aspect",
@ -6,19 +8,20 @@ app.views.Aspect = app.views.Base.extend({
className: 'hoverable',
events: {
'click .icons-check_yes_ok+a': 'toggleAspect'
'click .entypo.check+a': 'toggleAspect'
},
toggleAspect: function(evt) {
if (evt) { evt.preventDefault(); };
if (evt) { evt.preventDefault(); }
this.model.toggleSelected();
this.$el.find('.icons-check_yes_ok').toggleClass('selected');
app.router.aspects_stream();
},
presenter : function() {
return _.extend(this.defaultPresenter(), {
aspect : this.model
})
});
}
});
// @license-end

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
/*
* Aspects view for the publishers aspect dropdown and the aspect membership dropdown.
*/
@ -14,12 +16,11 @@ app.views.AspectsDropdown = app.views.Base.extend({
// select aspects in the dropdown by a given list of ids
_selectAspects: function(ids) {
this.$('.dropdown-menu > li').each(function(){
this.$('.dropdown-menu > li').each(function() {
var el = $(this);
if(_.contains(ids, el.data('aspect_id'))){
if (_.contains(ids, el.data('aspect_id'))) {
el.addClass('selected');
}
else {
} else {
el.removeClass('selected');
}
});
@ -31,16 +32,14 @@ app.views.AspectsDropdown = app.views.Base.extend({
selectedAspects = this.$(".dropdown-menu > li.selected").length,
buttonText;
if(selectedAspects == 0){
if (selectedAspects === 0) {
button.removeClass(inAspectClass).addClass('btn-default');
buttonText = Diaspora.I18n.t("aspect_dropdown.select_aspects");
}
else{
} else {
button.removeClass('btn-default').addClass(inAspectClass);
if(selectedAspects == 1){
if (selectedAspects === 1) {
buttonText = this.$(".dropdown-menu > li.selected .text").first().text();
}
else{
} else {
buttonText = Diaspora.I18n.t("aspect_dropdown.toggle", { count: selectedAspects.toString() });
}
}
@ -48,3 +47,5 @@ app.views.AspectsDropdown = app.views.Base.extend({
button.find('.text').text(buttonText);
}
});
// @license-end

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.AspectsList = app.views.Base.extend({
templateName: 'aspects-list',
@ -10,6 +12,7 @@ app.views.AspectsList = app.views.Base.extend({
initialize: function() {
this.collection.on('change', this.toggleSelector, this);
this.collection.on('change', this.updateStreamTitle, this);
this.collection.on('aspectStreamFetched', this.updateAspectList, this);
},
postRenderTemplate: function() {
@ -26,19 +29,12 @@ app.views.AspectsList = app.views.Base.extend({
},
toggleAll: function(evt) {
if (evt) { evt.preventDefault(); };
if (evt) { evt.preventDefault(); }
var aspects = this.$('li:not(:last)')
if (this.collection.allSelected()) {
this.collection.deselectAll();
aspects.each(function(i){
$(this).find('.icons-check_yes_ok').removeClass('selected');
});
} else {
this.collection.selectAll();
aspects.each(function(i){
$(this).find('.icons-check_yes_ok').addClass('selected');
});
}
this.toggleSelector();
@ -58,7 +54,19 @@ app.views.AspectsList = app.views.Base.extend({
$('.stream_title').text(this.collection.toSentence());
},
updateAspectList: function() {
this.collection.each(function(aspect) {
var element = this.$("li[data-aspect_id="+aspect.get('id')+"]");
if (aspect.get('selected')) {
element.find('.entypo.check').addClass('selected');
} else {
element.find('.entypo.check').removeClass('selected');
}
});
},
hideAspectsList: function() {
this.$el.empty();
},
})
});
// @license-end

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.Bookmarklet = Backbone.View.extend({
separator: ' - ',
@ -28,18 +30,20 @@ app.views.Bookmarklet = Backbone.View.extend({
return contents;
},
_postSubmit: function(evt) {
_postSubmit: function() {
this.$('h4').text(Diaspora.I18n.t('bookmarklet.post_submit'));
},
_postSuccess: function(evt) {
_postSuccess: function() {
this.$('h4').text(Diaspora.I18n.t('bookmarklet.post_success'));
app.publisher.close();
this.$("#publisher").addClass("hidden");
_.delay(window.close, 2000);
},
_postError: function(evt) {
_postError: function() {
this.$('h4').text(Diaspora.I18n.t('bookmarklet.post_something'));
}
});
// @license-end

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.CommentStream = app.views.Base.extend({
templateName: "comment-stream",
@ -37,7 +39,7 @@ app.views.CommentStream = app.views.Base.extend({
moreCommentsCount : (this.model.interactions.commentsCount() - 3),
showExpandCommentsLink : (this.model.interactions.commentsCount() > 3),
commentsCount : this.model.interactions.commentsCount()
})
});
},
createComment: function(evt) {
@ -55,8 +57,8 @@ app.views.CommentStream = app.views.Base.extend({
},
keyDownOnCommentBox: function(evt) {
if(evt.keyCode == 13 && evt.ctrlKey) {
this.$("form").submit()
if(evt.keyCode === 13 && evt.ctrlKey) {
this.$("form").submit();
return false;
}
},
@ -64,14 +66,14 @@ app.views.CommentStream = app.views.Base.extend({
appendComment: function(comment) {
// Set the post as the comment's parent, so we can check
// on post ownership in the Comment view.
comment.set({parent : this.model.toJSON()})
comment.set({parent : this.model.toJSON()});
this.$(".comments").append(new app.views.Comment({
model: comment
}).render().el);
},
commentTextareaFocused: function(evt){
commentTextareaFocused: function(){
this.$("form").removeClass('hidden').addClass("open");
},
@ -81,18 +83,18 @@ app.views.CommentStream = app.views.Base.extend({
expandComments: function(evt){
if(evt){ evt.preventDefault(); }
self = this;
var self = this;
this.model.comments.fetch({
success : function(resp){
self.model.set({
comments : resp.models,
all_comments_loaded : true
})
});
self.model.trigger("commentsExpanded", self)
self.model.trigger("commentsExpanded", self);
}
});
}
});
// @license-end

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
//= require ./content_view
app.views.Comment = app.views.Content.extend({
templateName: "comment",
@ -11,27 +13,27 @@ app.views.Comment = app.views.Content.extend({
},
initialize : function(options){
this.templateName = options.templateName || this.templateName
this.model.on("change", this.render, this)
this.templateName = options.templateName || this.templateName;
this.model.on("change", this.render, this);
},
presenter : function() {
return _.extend(this.defaultPresenter(), {
canRemove: this.canRemove(),
text : app.helpers.textFormatter(this.model.get("text"), this.model)
})
text : app.helpers.textFormatter(this.model.get("text"))
});
},
ownComment : function() {
return app.currentUser.authenticated() && this.model.get("author").diaspora_id == app.currentUser.get("diaspora_id")
return app.currentUser.authenticated() && this.model.get("author").diaspora_id === app.currentUser.get("diaspora_id");
},
postOwner : function() {
return app.currentUser.authenticated() && this.model.get("parent").author.diaspora_id == app.currentUser.get("diaspora_id")
return app.currentUser.authenticated() && this.model.get("parent").author.diaspora_id === app.currentUser.get("diaspora_id");
},
canRemove : function() {
return app.currentUser.authenticated() && (this.ownComment() || this.postOwner())
return app.currentUser.authenticated() && (this.ownComment() || this.postOwner());
}
});
@ -39,3 +41,4 @@ app.views.ExpandedComment = app.views.Comment.extend({
postRenderTemplate : function(){
}
});
// @license-end

View file

@ -0,0 +1,77 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.ContactStream = Backbone.View.extend({
initialize: function() {
this.itemCount = 0;
this.perPage = 25;
this.query = '';
this.resultList = this.collection.toArray();
var throttledScroll = _.throttle(_.bind(this.infScroll, this), 200);
$(window).scroll(throttledScroll);
this.on('renderContacts', this.renderContacts, this);
},
render: function() {
if( _.isEmpty(this.resultList) ) {
var content = document.createDocumentFragment();
content = '<div id="no_contacts" class="well">' +
' <h4>' +
Diaspora.I18n.t('contacts.search_no_results') +
' </h4>' +
'</div>';
this.$el.html(content);
} else {
this.$el.html('');
this.renderContacts();
}
},
renderContacts: function() {
this.$el.addClass("loading");
var content = document.createDocumentFragment();
_.rest(_.first(this.resultList , this.itemCount + this.perPage), this.itemCount).forEach( function(item) {
var view = new app.views.Contact({model: item});
content.appendChild(view.render().el);
});
var size = _.size(this.resultList);
if( this.itemCount + this.perPage >= size ){
this.itemCount = size;
this.off('renderContacts');
} else {
this.itemCount += this.perPage;
}
this.$el.append(content);
this.$el.removeClass("loading");
},
search: function(query) {
query = query.trim();
if( query || this.query ) {
this.off('renderContacts');
this.on('renderContacts', this.renderContacts, this);
this.itemCount = 0;
if( query ) {
this.query = query;
var regex = new RegExp(query,'i');
this.resultList = this.collection.filter(function(contact) {
return regex.test(contact.get('person').name) ||
regex.test(contact.get('person').diaspora_id);
});
} else {
this.resultList = this.collection.toArray();
this.query = '';
}
this.render();
}
},
infScroll: function() {
if( this.$el.hasClass('loading') ) return;
var distanceTop = $(window).height() + $(window).scrollTop(),
distanceBottom = $(document).height() - distanceTop;
if(distanceBottom < 300) this.trigger('renderContacts');
}
});
// @license-end

View file

@ -0,0 +1,71 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.Contact = app.views.Base.extend({
templateName: 'contact',
events: {
"click .contact_add-to-aspect" : "addContactToAspect",
"click .contact_remove-from-aspect" : "removeContactFromAspect"
},
tooltipSelector: '.contact_add-to-aspect, .contact_remove-from-aspect',
presenter: function() {
return _.extend(this.defaultPresenter(), {
person_id : this.model.get('person_id'),
person : this.model.get('person'),
in_aspect: (app.aspect && this.model.inAspect(app.aspect.get('id'))) ? 'in_aspect' : '',
});
},
postRenderTemplate: function() {
var self = this;
var dropdownEl = this.$('.aspect_membership_dropdown.placeholder');
if( dropdownEl.length === 0 ) {
return;
}
// TODO render me client side!!!
var href = this.model.person.url() + '/aspect_membership_button?size=small';
$.get(href, function(resp) {
dropdownEl.html(resp);
new app.views.AspectMembership({el: $('.aspect_dropdown',dropdownEl)});
// UGLY (re-)attach the facebox
self.$('a[rel*=facebox]').facebox();
});
},
addContactToAspect: function(){
var self = this;
this.model.aspect_memberships.create({
'person_id': this.model.get('person_id'),
'aspect_id': app.aspect.get('id')
},{
success: function(){
self.render();
},
error: function(){
var msg = Diaspora.I18n.t('contacts.error_add', { 'name': self.model.get('person').name });
Diaspora.page.flashMessages.render({ 'success':false, 'notice':msg });
}
});
},
removeContactFromAspect: function(){
var self = this;
this.model.aspect_memberships
.find(function(membership){ return membership.get('aspect').id === app.aspect.id; })
.destroy({
success: function(){
self.render();
},
error: function(){
var msg = Diaspora.I18n.t('contacts.error_remove', { 'name': self.model.get('person').name });
Diaspora.page.flashMessages.render({ 'success':false, 'notice':msg });
}
});
}
});
// @license-end

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.Content = app.views.Base.extend({
events: {
"click .expander": "expandPost"
@ -5,7 +7,7 @@ app.views.Content = app.views.Base.extend({
presenter : function(){
return _.extend(this.defaultPresenter(), {
text : app.helpers.textFormatter(this.model.get("text"), this.model),
text : app.helpers.textFormatter(this.model.get("text"), this.model.get("mentioned_people")),
largePhoto : this.largePhoto(),
smallPhotos : this.smallPhotos(),
location: this.location()
@ -14,13 +16,13 @@ app.views.Content = app.views.Base.extend({
largePhoto : function() {
var photos = this.model.get("photos")
if(!photos || photos.length == 0) { return }
return photos[0]
var photos = this.model.get("photos");
if(!photos || photos.length === 0) { return }
return photos[0];
},
smallPhotos : function() {
var photos = this.model.get("photos")
var photos = this.model.get("photos");
if(!photos || photos.length < 2) { return }
photos.splice(0, 1); // remove first photo as it is already shown as largePhoto
return photos;
@ -47,10 +49,10 @@ app.views.Content = app.views.Base.extend({
, oembed = elem.find(".oembed")
, opengraph = elem.find(".opengraph")
, addHeight = 0;
if($.trim(oembed.html()) != "") {
if($.trim(oembed.html()) !== "") {
addHeight += oembed.height();
}
if($.trim(opengraph.html()) != "") {
if($.trim(opengraph.html()) !== "") {
addHeight += opengraph.height();
}
@ -69,7 +71,7 @@ app.views.Content = app.views.Base.extend({
},
postRenderTemplate : function(){
_.defer(_.bind(this.collapseOversized, this))
_.defer(_.bind(this.collapseOversized, this));
}
});
@ -93,26 +95,55 @@ app.views.OEmbed = app.views.Base.extend({
},
presenter:function () {
o_embed_cache = this.model.get("o_embed_cache")
var o_embed_cache = this.model.get("o_embed_cache");
if(o_embed_cache) {
typemodel = { rich: false, photo: false, video: false, link: false }
typemodel[o_embed_cache.data.type] = true
o_embed_cache.data.types = typemodel
var typemodel = { rich: false, photo: false, video: false, link: false };
typemodel[o_embed_cache.data.type] = true;
o_embed_cache.data.types = typemodel;
}
return _.extend(this.defaultPresenter(), {
o_embed_html : app.helpers.oEmbed.html(o_embed_cache)
})
});
},
showOembedContent : function (evt) {
if( $(evt.target).is('a') ) return;
var clickedThumb = false;
if ($(evt.target).hasClass(".thumb")) {
clickedThumb = $(evt.target);
} else {
clickedThumb = $(evt.target).parent(".thumb");
}
var insertHTML = $(app.helpers.oEmbed.html(this.model.get("o_embed_cache")));
var paramSeparator = ( /\?/.test(insertHTML.attr("src")) ) ? "&" : "?";
insertHTML.attr("src", insertHTML.attr("src") + paramSeparator + "autoplay=1&wmode=opaque");
if (clickedThumb) {
insertHTML.attr("width", clickedThumb.width());
insertHTML.attr("height", clickedThumb.height());
}
this.$el.html(insertHTML);
}
});
app.views.OpenGraph = app.views.Base.extend({
templateName : "opengraph"
templateName : "opengraph",
initialize: function() {
this.truncateDescription();
},
truncateDescription: function() {
// truncate opengraph description to 250 for stream view
if(this.model.has('open_graph_cache')) {
var ogdesc = this.model.get('open_graph_cache');
ogdesc.description = app.helpers.truncate(ogdesc.description, 250);
}
}
});
app.views.SPVOpenGraph = app.views.OpenGraph.extend({
truncateDescription: function () {
// override with nothing
}
});
// @license-end

View file

@ -0,0 +1,31 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.ConversationsForm = Backbone.View.extend({
initialize: function(opts) {
this.contacts = _.has(opts, 'contacts') ? opts.contacts : null;
this.prefill = [];
if (_.has(opts, 'prefillName') && _.has(opts, 'prefillValue')) {
this.prefill = [{name : opts.prefillName,
value : opts.prefillValue}];
}
this.autocompleteInput = $("#contact_autocomplete");
this.prepareAutocomplete(this.contacts);
},
prepareAutocomplete: function(data){
this.autocompleteInput.autoSuggest(data, {
selectedItemProp: "name",
searchObjProps: "name",
asHtmlID: "contact_ids",
retrieveLimit: 10,
minChars: 1,
keyDelay: 0,
startText: '',
emptyText: Diaspora.I18n.t('no_results'),
preFill: this.prefill
}).focus();
}
});
// @license-end

View file

@ -0,0 +1,49 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.Conversations = Backbone.View.extend({
el: "#conversations_container",
events: {
"conversation:loaded" : "setupConversation"
},
initialize: function() {
if($('#conversation_new:visible').length > 0) {
new app.views.ConversationsForm({contacts: gon.contacts});
}
this.setupConversation();
},
setupConversation: function() {
app.helpers.timeago($(this.el));
$('.control-icons a').tooltip({placement: 'bottom'});
var conv = $('.conversation-wrapper .stream_element.selected'),
cBadge = $('#conversations_badge .badge_count');
if(conv.hasClass('unread') ){
var unreadCount = parseInt(conv.find('.unread_message_count').text(), 10);
if(cBadge.text() !== '') {
cBadge.text().replace(/\d+/, function(num){
num = parseInt(num, 10) - unreadCount;
if(num > 0) {
cBadge.text(num);
} else {
cBadge.text(0).addClass('hidden');
}
});
}
conv.removeClass('unread');
conv.find('.unread_message_count').remove();
var pos = $('#first_unread').offset().top - 50;
$("html").animate({scrollTop:pos});
} else {
$("html").animate({scrollTop:0});
}
}
});
// @license-end

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.FaqQuestionView = app.views.Base.extend({
templateName: "faq_question",
@ -26,12 +28,14 @@ app.views.FaqQuestionView = app.views.Base.extend({
},
toggled: function(e) {
el = $(e.target);
parent = el.parents('.question');
var el = $(e.target);
var parent = el.parents('.question');
parent.children('.answer').toggle();
parent.toggleClass('opened').toggleClass('collapsed');
e.preventDefault();
},
});
});
// @license-end

View file

@ -1,7 +1,11 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
//=require "./feedback_view"
app.views.FeedbackActions = app.views.Feedback.extend({
id : "user-controls",
templateName : "feedback-actions",
events: {},
initialize: function(){}
});
});
// @license-end

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.Feedback = app.views.Base.extend({
templateName: "feedback",
@ -6,18 +8,21 @@ app.views.Feedback = app.views.Base.extend({
events: {
"click .like" : "toggleLike",
"click .reshare" : "resharePost",
"click .post_report" : "report"
"click .post_report" : "report",
"click .block_user" : "blockUser",
"click .hide_post" : "hidePost",
},
tooltipSelector : ".label",
initialize : function() {
this.model.interactions.on('change', this.render, this);
this.initViews && this.initViews() // I don't know why this was failing with $.noop... :(
this.initViews && this.initViews(); // I don't know why this was failing with $.noop... :(
},
presenter : function() {
var interactions = this.model.interactions
var interactions = this.model.interactions;
return _.extend(this.defaultPresenter(),{
commentsCount : interactions.commentsCount(),
@ -26,7 +31,7 @@ app.views.Feedback = app.views.Base.extend({
userCanReshare : interactions.userCanReshare(),
userLike : interactions.userLike(),
userReshare : interactions.userReshare()
})
});
},
toggleLike: function(evt) {
@ -38,5 +43,45 @@ app.views.Feedback = app.views.Base.extend({
if(evt) { evt.preventDefault(); }
if(!window.confirm(Diaspora.I18n.t("reshares.post", {name: this.model.reshareAuthor().name}))) { return }
this.model.interactions.reshare();
}
},
blockUser: function(evt) {
if(evt) { evt.preventDefault(); }
if(!confirm(Diaspora.I18n.t('ignore_user'))) { return; }
this.model.blockAuthor()
.done(function() {
// return to stream
document.location.href = "/stream";
})
.fail(function() {
Diaspora.page.flashMessages.render({
success: false,
notice: Diaspora.I18n.t('hide_post_failed')
});
});
},
hidePost : function(evt) {
if(evt) { evt.preventDefault(); }
if(!confirm(Diaspora.I18n.t('hide_post'))) { return; }
$.ajax({
url : "/share_visibilities/42",
type : "PUT",
data : {
post_id : this.model.id
}
}).done(function() {
// return to stream
document.location.href = "/stream";
})
.fail(function() {
Diaspora.page.flashMessages.render({
success: false,
notice: Diaspora.I18n.t('ignore_post_failed')
});
});
},
});
// @license-end

View file

@ -1,45 +1,53 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.Header = app.views.Base.extend({
templateName : "header",
templateName: "header",
className : "dark-header",
className: "dark-header",
events : {
"click ul.dropdown li:first-child" : "toggleDropdown",
events: {
"click ul.dropdown li:first-child": "toggleUserDropdown",
"focusin #q": "toggleSearchActive",
"focusout #q": "toggleSearchActive"
},
initialize : function(options) {
$(document.body).click($.proxy(this.hideDropdown, this));
initialize: function(){
$(document.body).click($.proxy(this.hideUserDropdown, this));
return this;
},
menuElement : function() {
return this.$("ul.dropdown");
postRenderTemplate: function(){
new app.views.Notifications({ el: '#notification_dropdown' });
new app.views.NotificationDropdown({ el: '#notification_badge' });
new app.views.Search({ el: '#header-search-form' });
},
toggleDropdown : function(evt) {
menuElement: function(){ return this.$("ul.dropdown"); },
toggleUserDropdown: function(evt){
if(evt){ evt.preventDefault(); }
this.menuElement().toggleClass("active");
if($.browser.msie) {
if($.browser.msie){
this.$("header").toggleClass('ie-user-menu-active');
}
},
hideDropdown : function(evt) {
if(this.menuElement().hasClass("active") && !$(evt.target).parents("#user_menu").length) {
hideUserDropdown: function(evt){
if(this.menuElement().hasClass("active") && !$(evt.target).parents("#user_menu").length){
this.menuElement().removeClass("active");
}
},
toggleSearchActive: function(ev) {
toggleSearchActive: function(ev){
// jQuery produces two events for focus/blur (for bubbling)
// don't rely on which event arrives first, by allowing for both variants
var is_active = (_.indexOf(['focus','focusin'], ev.type) != -1);
var is_active = (_.indexOf(['focus','focusin'], ev.type) !== -1);
$(ev.target).toggleClass('active', is_active);
return false;
}
});
// @license-end

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.HelpSectionView = app.views.StaticContentView.extend({
events: {
@ -24,8 +26,8 @@ app.views.HelpSectionView = app.views.StaticContentView.extend({
},
toggled: function(e) {
el = $(e.target);
parent = el.parents('.question');
var el = $(e.target);
var parent = el.parents('.question');
parent.children('.answer.hideable').toggle();
parent.toggleClass('opened').toggleClass('collapsed');
@ -52,3 +54,5 @@ app.views.HelpSectionView = app.views.StaticContentView.extend({
},
});
// @license-end

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.Help = app.views.StaticContentView.extend({
templateName : "help",
@ -6,23 +8,39 @@ app.views.Help = app.views.StaticContentView.extend({
"click .faq-link-getting-help" : "gettingHelp",
"click .faq-link-sharing" : "sharing",
"click .faq-link-posts-and-posting" : "postsAndPosting",
"click .faq-link-tags": "tags",
"click .faq-link-keyboard-shortcuts" : "keyboardShortcuts",
"click .faq-link-chat" : "chat"
},
initialize : function(options) {
initialize : function() {
this.GETTING_HELP_SUBS = {
getting_started_a: { tutorial_series: this.linkHtml("http://diasporafoundation.org/getting_started/sign_up", Diaspora.I18n.t( 'getting_started_tutorial' )) },
get_support_a_website: { link: this.linkHtml("https://diasporafoundation.org/", Diaspora.I18n.t( 'foundation_website' ))},
get_support_a_tutorials: { tutorials: this.linkHtml("https://diasporafoundation.org/tutorials", Diaspora.I18n.t( 'tutorials' ))},
get_support_a_wiki: { link: this.linkHtml("https://wiki.diasporafoundation.org/Special:Search", Diaspora.I18n.t( 'wiki' ))},
get_support_a_irc: { irc: this.linkHtml("https://wiki.diasporafoundation.org/How_We_Communicate#IRC", Diaspora.I18n.t( 'irc' ))},
get_support_a_hashtag: { question: this.linkHtml("/tags/question", "#question")},
get_support_a_faq: { faq: this.linkHtml("https://wiki.diasporafoundation.org/FAQ_for_users", Diaspora.I18n.t( 'faq' ))},
get_support_a_hashtag: { question: this.linkHtml("/tags/question", "#question")}
};
this.POSTS_AND_POSTING_SUBS = {
format_text_a: {
markdown: this.linkHtml("http://diasporafoundation.org/formatting", Diaspora.I18n.t( 'markdown' )),
here: this.linkHtml("http://daringfireball.net/projects/markdown/syntax", Diaspora.I18n.t( 'here' )),
here: this.linkHtml("http://daringfireball.net/projects/markdown/syntax", Diaspora.I18n.t( 'here' ))
}
};
this.TAGS_SUBS = {
filter_tags_a: {
third_party_tools: this.linkHtml("https://wiki.diasporafoundation.org/Tools_to_use_with_Diaspora", Diaspora.I18n.t( 'third_party_tools' ))
}
};
this.CHAT_SUBS = {
add_contact_roster_a: {
toggle_privilege: this.getChatIcons(),
contacts_page: this.linkHtml(Routes.contacts_path(), Diaspora.I18n.t('chat.contacts_page'))
}
};
@ -43,19 +61,24 @@ app.views.Help = app.views.StaticContentView.extend({
title_tags: Diaspora.I18n.t( 'tags.title' ),
title_keyboard_shortcuts: Diaspora.I18n.t( 'keyboard_shortcuts.title' ),
title_miscellaneous: Diaspora.I18n.t( 'miscellaneous.title' ),
}
title_chat: Diaspora.I18n.t( 'chat.title' ),
chat_enabled: this.chatEnabled()
};
return this;
},
render: function(){
var section = app.views.Base.prototype.render.apply(this, arguments);
render: function(section){
var html = app.views.Base.prototype.render.apply(this, arguments);
// After render actions
this.resetMenu(true);
this.renderStaticSection("getting_help", "faq_getting_help", this.GETTING_HELP_SUBS);
return section;
var elTarget = this.findSection(section);
if(elTarget !== null){ $(elTarget).click(); }
return html;
},
showItems: function(el) {
@ -98,8 +121,12 @@ app.views.Help = app.views.StaticContentView.extend({
menuClicked: function(e) {
this.resetMenu();
$(e.target).hide();
$(e.target).next().show();
var data = $(e.target).data('section');
app.router.navigate('help/' + data);
},
clearItems: function() {
@ -115,8 +142,8 @@ app.views.Help = app.views.StaticContentView.extend({
renderStaticSection: function(section, template, subs) {
this.clearItems();
data = $.extend(Diaspora.I18n.resolve(section), { className: section });
help_section = new app.views.HelpSectionView({
var data = $.extend(Diaspora.I18n.resolve(section), { className: section });
var help_section = new app.views.HelpSectionView({
template: template,
data: data,
subs: subs
@ -124,6 +151,18 @@ app.views.Help = app.views.StaticContentView.extend({
this.$('#faq').append(help_section.render().el);
},
/**
* Returns The section title whose data-section property equals the given query
* Returns null if nothing found
* @param data Value for the data-section to find
* @returns {jQuery}
*/
findSection: function(data){
var res = this.$('a[data-section=' + data + ']');
if(res.length === 0){ return null; }
return res;
},
gettingHelp: function(e) {
this.renderStaticSection("getting_help", "faq_getting_help", this.GETTING_HELP_SUBS);
this.menuClicked(e);
@ -145,6 +184,13 @@ app.views.Help = app.views.StaticContentView.extend({
e.preventDefault();
},
tags: function(e) {
this.renderStaticSection("tags", "faq_tags", this.TAGS_SUBS);
this.menuClicked(e);
e.preventDefault();
},
keyboardShortcuts: function(e) {
this.renderStaticSection("keyboard_shortcuts", "faq_keyboard_shortcuts", {});
this.menuClicked(e);
@ -152,7 +198,27 @@ app.views.Help = app.views.StaticContentView.extend({
e.preventDefault();
},
chat: function(e){
this.renderStaticSection("chat", "faq_chat", this.CHAT_SUBS);
this.menuClicked(e);
e.preventDefault();
},
linkHtml: function(url, text) {
return "<a href=\"" + url + "\" target=\"_blank\">" + text + "</a>";
},
chatEnabled: function(){
return gon.chatEnabled;
},
getChatIcons: function(){
return '<div class="help-chat-icons">' +
' <i class="entypo lock-open"></i>' +
' <i class="entypo chat"></i>' +
' <i class="entypo trash"></i>' +
'</div>';
}
});
// @license-end

View file

@ -1,3 +1,4 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.Hovercard = app.views.Base.extend({
templateName: 'hovercard',
@ -14,8 +15,8 @@ app.views.Hovercard = app.views.Base.extend({
.on('mouseenter', '.hovercardable', _.bind(this._mouseenterHandler, this))
.on('mouseleave', '.hovercardable', _.bind(this._mouseleaveHandler, this));
this.show_me = false;
this.parent = null; // current 'hovercarable' element that caused HC to appear
this.showMe = false;
this.parent = null; // current 'hovercardable' element that caused HC to appear
// cache some element references
this.avatar = this.$('.avatar');
@ -28,7 +29,7 @@ app.views.Hovercard = app.views.Base.extend({
},
postRenderTemplate: function() {
this.$el.appendTo($('body'))
this.$el.appendTo($('body'));
},
deactivate: function() {
@ -40,7 +41,7 @@ app.views.Hovercard = app.views.Base.extend({
},
_mouseenterHandler: function(event) {
if( this.active == false ||
if( this.active === false ||
$.contains(this.el, event.target) ) { return false; }
var el = $(event.target);
@ -48,28 +49,31 @@ app.views.Hovercard = app.views.Base.extend({
el = el.parents('a');
}
if( el.attr('href').indexOf('/people') == -1 ) {
if( el.attr('href').indexOf('/people') === -1 ) {
// can't fetch data from that URL, aborting
return false;
}
this.show_me = true;
this.showMe = true;
this.showHovercardOn(el);
return false;
},
_mouseleaveHandler: function(event) {
if( this.active == false ||
$.contains(this.el, event.relatedTarget) ) { return false; }
this.showMe = false;
if( this.active === false ||
$.contains(this.el, event.relatedTarget) ) { return false; }
if( this.mouseIsOverElement(this.parent, event) ||
this.mouseIsOverElement(this.$el, event) ) { return false; }
this.show_me = false;
if( this.$el.is(':visible') ) {
this.$el.fadeOut('fast');
} else {
this.$el.hide();
}
this.dropdown_container.empty();
this.dropdown_container.unbind().empty();
return false;
},
@ -77,7 +81,7 @@ app.views.Hovercard = app.views.Base.extend({
var el = $(element);
var hc = this.$el;
if( !this.show_me ) {
if( !this.showMe ) {
// mouse has left element
return;
}
@ -94,11 +98,15 @@ app.views.Hovercard = app.views.Base.extend({
var self = this;
$.get(href, function(person){
if( !person || person.length == 0 ) {
if( !person || person.length === 0 ) {
throw new Error("received data is not a person object");
}
self._populateHovercardWith(person);
if( !self.showMe ) {
// mouse has left element
return;
}
self.$el.fadeIn('fast');
});
},
@ -119,15 +127,13 @@ app.views.Hovercard = app.views.Base.extend({
})) );
// set aspect dropdown
// TODO render me client side!!!
var href = this.href();
href += "/aspect_membership_button";
if(gon.bootstrap == true){
href += "?bootstrap=true";
}
$.get(href, function(response) {
self.dropdown_container.html(response);
});
var aspect_membership = new app.views.AspectMembership({el: self.dropdown_container});
new app.views.AspectMembership({el: self.dropdown_container});
},
_positionHovercard: function() {
@ -138,5 +144,14 @@ app.views.Hovercard = app.views.Base.extend({
top: p_pos.top + p_height - 25,
left: p_pos.left
});
}
},
mouseIsOverElement: function(element, event) {
var elPos = element.offset();
return event.pageX >= elPos.left &&
event.pageX <= elPos.left + element.width() &&
event.pageY >= elPos.top &&
event.pageY <= elPos.top + element.height();
},
});
// @license-end

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
// Abstract Infinite Scroll View Super Class
// Requires:
// a stream model, assigned to this.stream
@ -32,7 +34,7 @@ app.views.InfScroll = app.views.Base.extend({
createPostView : function(post){
var postView = new this.postClass({ model: post, stream: this.stream });
if (this.collection.at(0).id == post.id) {
if (this.collection.at(0).id === post.id) {
// post is first in collection - insert view at top of the list
this.postViews.unshift(postView);
} else {
@ -44,7 +46,7 @@ app.views.InfScroll = app.views.Base.extend({
// called for every item inserted in this.collection
addPostView : function(post) {
var el = this.createPostView(post).render().el;
if (this.collection.at(0).id == post.id) {
if (this.collection.at(0).id === post.id) {
this.prependedPosts.insertBefore(el, this.prependedPosts.firstChild);
} else {
this.appendedPosts.appendChild(el);
@ -56,7 +58,7 @@ app.views.InfScroll = app.views.Base.extend({
},
renderTemplate : function(){
this.renderInitialPosts()
this.renderInitialPosts();
},
renderInitialPosts : function(){
@ -64,7 +66,7 @@ app.views.InfScroll = app.views.Base.extend({
var els = document.createDocumentFragment();
this.stream.items.each(_.bind(function(post){
els.appendChild(this.createPostView(post).render().el);
}, this))
}, this));
this.$el.html(els);
},
@ -76,7 +78,7 @@ app.views.InfScroll = app.views.Base.extend({
},
showLoader: function(){
$("#paginate .loader").removeClass("hidden")
$("#paginate .loader").removeClass("hidden");
},
finishedAdding: function() {
@ -105,3 +107,4 @@ app.views.InfScroll = app.views.Base.extend({
}
}
});
// @license-end

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.LikesInfo = app.views.Base.extend({
templateName : "likes-info",
@ -17,11 +19,12 @@ app.views.LikesInfo = app.views.Base.extend({
likes : this.model.interactions.likes.toJSON(),
likesCount : this.model.interactions.likesCount(),
likes_fetched : this.model.interactions.get("fetched"),
})
});
},
showAvatars : function(evt){
if(evt) { evt.preventDefault() }
this.model.interactions.fetch()
this.model.interactions.fetch();
}
});
// @license-end

View file

@ -1,3 +1,7 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.LocationStream = app.views.Content.extend({
templateName: "status-message-location"
});
// @license-end

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.Location = Backbone.View.extend({
el: "#location",
@ -8,18 +10,18 @@ app.views.Location = Backbone.View.extend({
},
render: function(){
$(this.el).append('<img alt="delete location" src="/assets/ajax-loader.gif">');
$(this.el).append('<img alt="ajax-loader" src="'+ImagePaths.get('ajax-loader.gif')+'">');
},
getLocation: function(e){
element = this.el;
getLocation: function(){
var element = this.el;
locator = new OSM.Locator();
var locator = new OSM.Locator();
locator.getAddress(function(address, latlng){
$(element).html('<input id="location_address" type="text" class="input-block-level" value="' + address + '"/>');
$('#location_coords').val(latlng.latitude + "," + latlng.longitude);
$(element).append('<a id="hide_location"><img alt="delete location" src="/assets/deletelabel.png"></a>');
});
},
});
// @license-end

View file

@ -0,0 +1,118 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.NotificationDropdown = app.views.Base.extend({
events:{
"click #notifications-badge": "toggleDropdown"
},
initialize: function(){
$(document.body).click($.proxy(this.hideDropdown, this));
this.notifications = [];
this.perPage = 5;
this.hasMoreNotifs = true;
this.badge = this.$el;
this.dropdown = $('#notification_dropdown');
this.dropdownNotifications = this.dropdown.find('.notifications');
this.ajaxLoader = this.dropdown.find('.ajax_loader');
},
toggleDropdown: function(evt){
evt.preventDefault();
evt.stopPropagation();
if(this.dropdownShowing()){ this.hideDropdown(evt); }
else{ this.showDropdown(); }
},
dropdownShowing: function(){
return this.dropdown.css('display') === 'block';
},
showDropdown: function(){
this.resetParams();
this.ajaxLoader.show();
this.badge.addClass('active');
this.dropdown.css('display', 'block');
this.dropdownNotifications.addClass('loading');
this.getNotifications();
},
hideDropdown: function(evt){
var inDropdown = $(evt.target).parents().is(this.dropdown);
var inHovercard = $.contains(app.hovercard.el, evt.target);
if(!inDropdown && !inHovercard && this.dropdownShowing()){
this.badge.removeClass('active');
this.dropdown.css('display', 'none');
this.dropdownNotifications.perfectScrollbar('destroy');
}
},
dropdownScroll: function(){
var isLoading = ($('.loading').length === 1);
if (this.isBottom() && this.hasMoreNotifs && !isLoading){
this.dropdownNotifications.addClass('loading');
this.getNotifications();
}
},
getParams: function(){
if(this.notifications.length === 0){ return{ per_page: 10, page: 1 }; }
else{ return{ per_page: this.perPage, page: this.nextPage }; }
},
resetParams: function(){
this.notifications.length = 0;
this.hasMoreNotifs = true;
delete this.nextPage;
},
isBottom: function(){
var bottom = this.dropdownNotifications.prop('scrollHeight') - this.dropdownNotifications.height();
var currentPosition = this.dropdownNotifications.scrollTop();
return currentPosition + 50 >= bottom;
},
getNotifications: function(){
var self = this;
$.getJSON(Routes.notifications_path(this.getParams()), function(notifications){
$.each(notifications, function(){ self.notifications.push(this); });
self.hasMoreNotifs = notifications.length >= self.perPage;
if(self.nextPage){ self.nextPage++; }
else { self.nextPage = 3; }
self.renderNotifications();
});
},
hideAjaxLoader: function(){
var self = this;
this.ajaxLoader.find('img').fadeTo(200, 0, function(){
self.ajaxLoader.hide(300, function(){
self.ajaxLoader.find('img').css('opacity', 1);
});
});
},
renderNotifications: function(){
var self = this;
this.dropdownNotifications.find('.media.stream_element').remove();
$.each(self.notifications, function(index, notifications){
$.each(notifications, function(index, notification){
if($.inArray(notification, notifications) === -1){
var node = self.dropdownNotifications.append(notification.note_html);
$(node).find('.unread-toggle .entypo').tooltip('destroy').tooltip();
}
});
});
this.hideAjaxLoader();
app.helpers.timeago(this.dropdownNotifications);
this.dropdownNotifications.perfectScrollbar('destroy').perfectScrollbar();
this.dropdownNotifications.removeClass('loading');
this.dropdownNotifications.scroll(function(){
self.dropdownScroll();
});
}
});
// @license-end

View file

@ -1,40 +1,35 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.Notifications = Backbone.View.extend({
events: {
"click .unread-toggle" : "toggleUnread"
"click .unread-toggle" : "toggleUnread",
"click #mark_all_read_link": "markAllRead"
},
initialize: function() {
Diaspora.page.header.notifications.setUpNotificationPage(this);
$(".unread-toggle .entypo").tooltip();
app.helpers.timeago($(document));
},
toggleUnread: function(evt) {
note = $(evt.target).closest(".stream_element");
unread = note.hasClass("unread");
var note = $(evt.target).closest(".stream_element");
var unread = note.hasClass("unread");
if (unread) {
this.setRead(note.data("guid"));
}
else {
this.setUnread(note.data("guid"));
}
if (unread){ this.setRead(note.data("guid")); }
else { this.setUnread(note.data("guid")); }
},
setRead: function(guid) {
$.ajax({
url: "/notifications/" + guid,
data: { set_unread: false },
type: "PUT",
context: this,
success: this.clickSuccess
});
},
getAllUnread: function(){ return $('.media.stream_element.unread'); },
setUnread: function(guid) {
setRead: function(guid) { this.setUnreadStatus(guid, false); },
setUnread: function(guid){ this.setUnreadStatus(guid, true); },
setUnreadStatus: function(guid, state){
$.ajax({
url: "/notifications/" + guid,
data: { set_unread: true },
data: { set_unread: state },
type: "PUT",
context: this,
success: this.clickSuccess
@ -42,51 +37,58 @@ app.views.Notifications = Backbone.View.extend({
},
clickSuccess: function(data) {
type = $('.stream_element[data-guid=' + data["guid"] + ']').data('type');
var type = $('.stream_element[data-guid=' + data["guid"] + ']').data('type');
this.updateView(data["guid"], type, data["unread"]);
},
markAllRead: function(evt){
if(evt) { evt.preventDefault(); }
var self = this;
this.getAllUnread().each(function(i, el){
self.setRead($(el).data("guid"));
});
},
updateView: function(guid, type, unread) {
change = unread ? 1 : -1;
all_notes = $('ul.nav > li:eq(0) .badge');
type_notes = $('ul.nav > li[data-type=' + type + '] .badge');
header_badge = $('#notification_badge .badge_count');
var change = unread ? 1 : -1,
all_notes = $('ul.nav > li:eq(0) .badge'),
type_notes = $('ul.nav > li[data-type=' + type + '] .badge'),
header_badge = $('#notification_badge .badge_count'),
note = $('.stream_element[data-guid=' + guid + ']'),
markAllReadLink = $('a#mark_all_read_link'),
translationKey = unread ? 'notifications.mark_read' : 'notifications.mark_unread';
note = $('.stream_element[data-guid=' + guid + ']');
if(unread) {
note.removeClass("read").addClass("unread");
$(".unread-toggle .entypo", note)
if(unread){ note.removeClass("read").addClass("unread"); }
else { note.removeClass("unread").addClass("read"); }
$(".unread-toggle .entypo", note)
.tooltip('destroy')
.removeAttr("data-original-title")
.attr('title',Diaspora.I18n.t('notifications.mark_read'))
.attr('title',Diaspora.I18n.t(translationKey))
.tooltip();
}
else {
note.removeClass("unread").addClass("read");
$(".unread-toggle .entypo", note)
.tooltip('destroy')
.removeAttr("data-original-title")
.attr('title',Diaspora.I18n.t('notifications.mark_unread'))
.tooltip();
}
all_notes.text( function(i,text) { return parseInt(text) + change });
type_notes.text( function(i,text) { return parseInt(text) + change });
header_badge.text( function(i,text) { return parseInt(text) + change });
if(all_notes.text()>0){
all_notes.addClass('badge-important').removeClass('badge-default');
} else {
all_notes.removeClass('badge-important').addClass('badge-default');
}
if(type_notes.text()>0){
type_notes.addClass('badge-important').removeClass('badge-default');
} else {
type_notes.removeClass('badge-important').addClass('badge-default');
}
if(header_badge.text()>0){
[all_notes, type_notes, header_badge].forEach(function(element){
element.text(function(i, text){
return parseInt(text) + change });
});
[all_notes, type_notes].forEach(function(badge) {
if(badge.text() > 0) {
badge.addClass('badge-important').removeClass('badge-default');
}
else {
badge.removeClass('badge-important').addClass('badge-default');
}
});
if(header_badge.text() > 0){
header_badge.removeClass('hidden');
} else {
markAllReadLink.removeClass('disabled');
}
else{
header_badge.addClass('hidden');
markAllReadLink.addClass('disabled');
}
}
});
// @license-end

View file

@ -1,3 +1,5 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.Photo = app.views.Base.extend({
templateName: "photo",
@ -8,11 +10,10 @@ app.views.Photo = app.views.Base.extend({
"click .remove_post": "destroyModel"
},
tooltipSelector : ".block_user, .delete",
tooltipSelector : ".control-icons a",
initialize : function() {
$(this.el).attr("id", this.model.get("guid"));
this.model.bind('remove', this.remove, this);
return this;
},
@ -22,3 +23,5 @@ app.views.Photo = app.views.Base.extend({
});
}
});
// @license-end

View file

@ -1,7 +1,10 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.PhotoViewer = app.views.Base.extend({
templateName : "photo-viewer",
presenter : function(){
return { photos : this.model.get("photos") } //json array of attributes, not backbone models, yet.
return { photos : this.model.get("photos") }; //json array of attributes, not backbone models, yet.
}
});
});
// @license-end

View file

@ -1,13 +1,15 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.Photos = app.views.InfScroll.extend({
initialize : function(options) {
initialize : function() {
this.stream = this.model;
this.collection = this.stream.items;
// viable for extraction
this.stream.fetch();
this.setupLightbox()
this.setupInfiniteScroll()
this.setupLightbox();
this.setupInfiniteScroll();
},
postClass : app.views.Photo,
@ -21,3 +23,4 @@ app.views.Photos = app.views.InfScroll.extend({
$(this.el).delegate("a.photo-link", "click", this.lightbox.lightboxImageClicked);
}
});
// @license-end

Some files were not shown because too many files have changed in this diff Show more