Merge branch 'locator' of git://github.com/tangosource/diaspora into tangosource-locator
This commit is contained in:
commit
6b1fdaafc0
29 changed files with 345 additions and 10 deletions
1
Gemfile
1
Gemfile
|
|
@ -197,4 +197,5 @@ group :development, :test do
|
||||||
|
|
||||||
# Jasmine (client side application tests (JS))
|
# Jasmine (client side application tests (JS))
|
||||||
gem 'jasmine', '1.3.2'
|
gem 'jasmine', '1.3.2'
|
||||||
|
gem 'sinon-rails', '1.4.2.1'
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -357,6 +357,8 @@ GEM
|
||||||
rack (~> 1.3, >= 1.3.6)
|
rack (~> 1.3, >= 1.3.6)
|
||||||
rack-protection (~> 1.2)
|
rack-protection (~> 1.2)
|
||||||
tilt (~> 1.3, >= 1.3.3)
|
tilt (~> 1.3, >= 1.3.3)
|
||||||
|
sinon-rails (1.4.2.1)
|
||||||
|
railties (>= 3.1)
|
||||||
slim (1.3.8)
|
slim (1.3.8)
|
||||||
temple (~> 0.6.3)
|
temple (~> 0.6.3)
|
||||||
tilt (~> 1.3.3)
|
tilt (~> 1.3.3)
|
||||||
|
|
@ -470,6 +472,7 @@ DEPENDENCIES
|
||||||
selenium-webdriver (= 2.32.1)
|
selenium-webdriver (= 2.32.1)
|
||||||
sidekiq (= 2.11.1)
|
sidekiq (= 2.11.1)
|
||||||
sinatra (= 1.3.3)
|
sinatra (= 1.3.3)
|
||||||
|
sinon-rails (= 1.4.2.1)
|
||||||
slim (= 1.3.8)
|
slim (= 1.3.8)
|
||||||
spork (= 1.0.0rc3)
|
spork (= 1.0.0rc3)
|
||||||
timecop (= 0.6.1)
|
timecop (= 0.6.1)
|
||||||
|
|
|
||||||
BIN
app/assets/images/icons/marker.png
Normal file
BIN
app/assets/images/icons/marker.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 969 B |
|
|
@ -7,7 +7,8 @@ app.views.Content = app.views.Base.extend({
|
||||||
return _.extend(this.defaultPresenter(), {
|
return _.extend(this.defaultPresenter(), {
|
||||||
text : app.helpers.textFormatter(this.model.get("text"), this.model),
|
text : app.helpers.textFormatter(this.model.get("text"), this.model),
|
||||||
largePhoto : this.largePhoto(),
|
largePhoto : this.largePhoto(),
|
||||||
smallPhotos : this.smallPhotos()
|
smallPhotos : this.smallPhotos(),
|
||||||
|
location: this.location()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -34,12 +35,16 @@ app.views.Content = app.views.Base.extend({
|
||||||
$(evt.currentTarget).hide();
|
$(evt.currentTarget).hide();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
location: function(){
|
||||||
|
var address = this.model.get('address')? this.model.get('address') : '';
|
||||||
|
return address;
|
||||||
|
},
|
||||||
|
|
||||||
collapseOversized : function() {
|
collapseOversized : function() {
|
||||||
var collHeight = 200
|
var collHeight = 200
|
||||||
, elem = this.$(".collapsible")
|
, elem = this.$(".collapsible")
|
||||||
, oembed = elem.find(".oembed")
|
, oembed = elem.find(".oembed")
|
||||||
, addHeight = 0;
|
, addHeight = 0;
|
||||||
|
|
||||||
if($.trim(oembed.html()) != "") {
|
if($.trim(oembed.html()) != "") {
|
||||||
addHeight = oembed.height();
|
addHeight = oembed.height();
|
||||||
}
|
}
|
||||||
|
|
@ -100,4 +105,4 @@ app.views.OEmbed = app.views.Base.extend({
|
||||||
insertHTML.attr("src", insertHTML.attr("src") + paramSeparator + "autoplay=1");
|
insertHTML.attr("src", insertHTML.attr("src") + paramSeparator + "autoplay=1");
|
||||||
this.$el.html(insertHTML);
|
this.$el.html(insertHTML);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
|
||||||
3
app/assets/javascripts/app/views/location_stream.js
Normal file
3
app/assets/javascripts/app/views/location_stream.js
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
app.views.LocationStream = app.views.Content.extend({
|
||||||
|
templateName: "status-message-location"
|
||||||
|
});
|
||||||
25
app/assets/javascripts/app/views/location_view.js
Normal file
25
app/assets/javascripts/app/views/location_view.js
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
app.views.Location = Backbone.View.extend({
|
||||||
|
|
||||||
|
el: "#location",
|
||||||
|
|
||||||
|
initialize: function(){
|
||||||
|
this.render();
|
||||||
|
this.getLocation();
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function(){
|
||||||
|
$(this.el).append('<img alt="delete location" src="/assets/ajax-loader.gif">');
|
||||||
|
},
|
||||||
|
|
||||||
|
getLocation: function(e){
|
||||||
|
element = this.el;
|
||||||
|
|
||||||
|
locator = new OSM.Locator();
|
||||||
|
locator.getAddress(function(address, latlng){
|
||||||
|
$(element).html('<input id="location_address" value="' + address + '"/>');
|
||||||
|
$('#location_coords').val(latlng.latitude + "," + latlng.longitude);
|
||||||
|
$(element).append('<a id="hide_location"><img alt="delete location" src="/assets/deletelabel.png"></a>');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
@ -23,7 +23,10 @@ app.views.Publisher = Backbone.View.extend(_.extend(
|
||||||
"click .post_preview_button" : "createPostPreview",
|
"click .post_preview_button" : "createPostPreview",
|
||||||
"click .service_icon": "toggleService",
|
"click .service_icon": "toggleService",
|
||||||
"textchange #status_message_fake_text": "handleTextchange",
|
"textchange #status_message_fake_text": "handleTextchange",
|
||||||
"click .dropdown .dropdown_list li": "toggleAspect"
|
"click .dropdown .dropdown_list li": "toggleAspect",
|
||||||
|
"click #locator" : "showLocation",
|
||||||
|
"click #hide_location" : "destroyLocation",
|
||||||
|
"keypress #location_address" : "avoidEnter"
|
||||||
},
|
},
|
||||||
|
|
||||||
tooltipSelector: ".service_icon",
|
tooltipSelector: ".service_icon",
|
||||||
|
|
@ -79,7 +82,9 @@ app.views.Publisher = Backbone.View.extend(_.extend(
|
||||||
},
|
},
|
||||||
"aspect_ids" : serializedForm["aspect_ids[]"],
|
"aspect_ids" : serializedForm["aspect_ids[]"],
|
||||||
"photos" : serializedForm["photos[]"],
|
"photos" : serializedForm["photos[]"],
|
||||||
"services" : serializedForm["services[]"]
|
"services" : serializedForm["services[]"],
|
||||||
|
"location_address" : $("#location_address").val(),
|
||||||
|
"location_coords" : serializedForm["location[coords]"]
|
||||||
}, {
|
}, {
|
||||||
url : "/status_messages",
|
url : "/status_messages",
|
||||||
success : function() {
|
success : function() {
|
||||||
|
|
@ -94,6 +99,30 @@ app.views.Publisher = Backbone.View.extend(_.extend(
|
||||||
|
|
||||||
// clear state
|
// clear state
|
||||||
this.clear();
|
this.clear();
|
||||||
|
|
||||||
|
// clear location
|
||||||
|
this.destroyLocation();
|
||||||
|
},
|
||||||
|
|
||||||
|
// creates the location
|
||||||
|
showLocation: function(){
|
||||||
|
if($('#location').length == 0){
|
||||||
|
$('#publisher_textarea_wrapper').after('<div id="location"></div>');
|
||||||
|
app.views.location = new app.views.Location();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// destroys the location
|
||||||
|
destroyLocation: function(){
|
||||||
|
if(app.views.location){
|
||||||
|
app.views.location.remove();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// avoid submitting form when pressing Enter key
|
||||||
|
avoidEnter: function(evt){
|
||||||
|
if(evt.keyCode == 13)
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
createPostPreview : function(evt) {
|
createPostPreview : function(evt) {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@ app.views.StreamPost = app.views.Post.extend({
|
||||||
".likes" : "likesInfoView",
|
".likes" : "likesInfoView",
|
||||||
".comments" : "commentStreamView",
|
".comments" : "commentStreamView",
|
||||||
".post-content" : "postContentView",
|
".post-content" : "postContentView",
|
||||||
".oembed" : "oEmbedView"
|
".oembed" : "oEmbedView",
|
||||||
|
".status-message-location" : "postLocationStreamView"
|
||||||
},
|
},
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
|
|
@ -47,6 +48,10 @@ app.views.StreamPost = app.views.Post.extend({
|
||||||
return new postClass({ model : this.model })
|
return new postClass({ model : this.model })
|
||||||
},
|
},
|
||||||
|
|
||||||
|
postLocationStreamView : function(){
|
||||||
|
return new app.views.LocationStream({ model : this.model});
|
||||||
|
},
|
||||||
|
|
||||||
removeNsfwShield: function(evt){
|
removeNsfwShield: function(evt){
|
||||||
if(evt){ evt.preventDefault(); }
|
if(evt){ evt.preventDefault(); }
|
||||||
this.model.set({nsfw : false})
|
this.model.set({nsfw : false})
|
||||||
|
|
|
||||||
|
|
@ -10,3 +10,4 @@
|
||||||
//= require profile
|
//= require profile
|
||||||
//= require people
|
//= require people
|
||||||
//= require photos
|
//= require photos
|
||||||
|
//= require sinon
|
||||||
|
|
|
||||||
|
|
@ -42,3 +42,4 @@
|
||||||
//= require bootstrap-popover
|
//= require bootstrap-popover
|
||||||
//= require bootstrap-dropdown
|
//= require bootstrap-dropdown
|
||||||
//= require bootstrap-scrollspy-custom
|
//= require bootstrap-scrollspy-custom
|
||||||
|
//= require osmlocator
|
||||||
|
|
|
||||||
23
app/assets/javascripts/osmlocator.js
Normal file
23
app/assets/javascripts/osmlocator.js
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
OSM = {};
|
||||||
|
|
||||||
|
OSM.Locator = function(){
|
||||||
|
|
||||||
|
var geolocalize = function(callback){
|
||||||
|
navigator.geolocation.getCurrentPosition(function(position) {
|
||||||
|
lat=position.coords.latitude;
|
||||||
|
lon=position.coords.longitude;
|
||||||
|
var display_name =$.getJSON("http://nominatim.openstreetmap.org/reverse?format=json&lat="+lat+"&lon="+lon+"&addressdetails=3", function(data){
|
||||||
|
return callback(data.display_name, position.coords);
|
||||||
|
});
|
||||||
|
},errorGettingPosition);
|
||||||
|
};
|
||||||
|
|
||||||
|
function errorGettingPosition(err) {
|
||||||
|
$("#location").remove();
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
getAddress: geolocalize
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -573,9 +573,22 @@ ul.as-selections
|
||||||
& > .likes, & > .comments
|
& > .likes, & > .comments
|
||||||
:margin-right 15px
|
:margin-right 15px
|
||||||
|
|
||||||
|
.status-message-location
|
||||||
|
.near-from
|
||||||
|
:font-size smaller
|
||||||
|
:color #aaa
|
||||||
|
:width 100%
|
||||||
|
:float left
|
||||||
|
.address
|
||||||
|
:font-size 11px
|
||||||
|
:color #bbb
|
||||||
|
|
||||||
.stream_element .post-content .reshare
|
.stream_element .post-content .reshare
|
||||||
:border-left 2px solid #ddd
|
:border-left 2px solid #ddd
|
||||||
|
|
||||||
|
.stream_element.loaded .media .bd .feedback
|
||||||
|
:clear both
|
||||||
|
|
||||||
form.new_comment
|
form.new_comment
|
||||||
input
|
input
|
||||||
:display none
|
:display none
|
||||||
|
|
@ -697,6 +710,8 @@ form p.checkbox_select
|
||||||
:height 100%
|
:height 100%
|
||||||
:width 100%
|
:width 100%
|
||||||
:cursor pointer
|
:cursor pointer
|
||||||
|
img
|
||||||
|
:margin-right 20px
|
||||||
|
|
||||||
#publisher
|
#publisher
|
||||||
:z-index 1
|
:z-index 1
|
||||||
|
|
@ -876,6 +891,8 @@ form p.checkbox_select
|
||||||
:position absolute
|
:position absolute
|
||||||
:bottom 0
|
:bottom 0
|
||||||
:right 35px
|
:right 35px
|
||||||
|
:width 430px
|
||||||
|
:left 5px
|
||||||
:padding 0
|
:padding 0
|
||||||
|
|
||||||
li
|
li
|
||||||
|
|
@ -3120,3 +3137,46 @@ body
|
||||||
:bottom 3px solid #3f8fba !important
|
:bottom 3px solid #3f8fba !important
|
||||||
:background
|
:background
|
||||||
:color #e8f7ff
|
:color #e8f7ff
|
||||||
|
|
||||||
|
#publisher-images
|
||||||
|
#locator
|
||||||
|
:bottom 1px !important
|
||||||
|
:display inline-block
|
||||||
|
:margin 0
|
||||||
|
:position absolute !important
|
||||||
|
:right 30px
|
||||||
|
:cursor pointer
|
||||||
|
img
|
||||||
|
:padding-top 2px
|
||||||
|
@include opacity(0.4)
|
||||||
|
&:hover
|
||||||
|
:color #666
|
||||||
|
:cursor pointer
|
||||||
|
img
|
||||||
|
@include opacity(0.8)
|
||||||
|
.btn
|
||||||
|
:height 19px
|
||||||
|
:width 19px
|
||||||
|
|
||||||
|
#location
|
||||||
|
:border 1px solid #999
|
||||||
|
:height 20px
|
||||||
|
#location_address
|
||||||
|
:border none
|
||||||
|
:color #aaa
|
||||||
|
:height 10px
|
||||||
|
:width 430px
|
||||||
|
:float left
|
||||||
|
a#hide_location
|
||||||
|
:position absolute
|
||||||
|
:right 22px
|
||||||
|
:filter alpha(opacity=30)
|
||||||
|
:-moz-opacity 0.3
|
||||||
|
:-khtml-opacity 0.3
|
||||||
|
:opacity 0.3
|
||||||
|
:z-index 5
|
||||||
|
a#hide_location:hover
|
||||||
|
@include opacity(0)
|
||||||
|
:-khtml-opacity 1
|
||||||
|
:opacity 1
|
||||||
|
:cursor pointer
|
||||||
|
|
|
||||||
5
app/assets/templates/status-message-location_tpl.jst.hbs
Normal file
5
app/assets/templates/status-message-location_tpl.jst.hbs
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
{{#if location}}
|
||||||
|
<div class='near-from'>
|
||||||
|
{{ t "publisher.near_from" location=location}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
@ -55,6 +55,7 @@
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<div class="post-content"> </div>
|
<div class="post-content"> </div>
|
||||||
|
<div class="status-message-location"> </div>
|
||||||
|
|
||||||
<div class="feedback"> </div>
|
<div class="feedback"> </div>
|
||||||
<div class="likes"> </div>
|
<div class="likes"> </div>
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ class StatusMessagesController < ApplicationController
|
||||||
services = [*params[:services]].compact
|
services = [*params[:services]].compact
|
||||||
|
|
||||||
@status_message = current_user.build_post(:status_message, params[:status_message])
|
@status_message = current_user.build_post(:status_message, params[:status_message])
|
||||||
|
@status_message.build_location(:address => params[:location_address], :coordinates => params[:location_coords]) if params[:location_address].present?
|
||||||
@status_message.attach_photos_by_ids(params[:photos])
|
@status_message.attach_photos_by_ids(params[:photos])
|
||||||
|
|
||||||
if @status_message.save
|
if @status_message.save
|
||||||
|
|
|
||||||
12
app/models/location.rb
Normal file
12
app/models/location.rb
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
class Location < ActiveRecord::Base
|
||||||
|
|
||||||
|
before_validation :split_coords, :on => :create
|
||||||
|
|
||||||
|
attr_accessor :coordinates
|
||||||
|
|
||||||
|
belongs_to :status_message
|
||||||
|
|
||||||
|
def split_coords
|
||||||
|
coordinates.present? ? (self.lat, self.lng = coordinates.split(',')) : false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -66,6 +66,10 @@ class Post < ActiveRecord::Base
|
||||||
def mentioned_people; []; end
|
def mentioned_people; []; end
|
||||||
def photos; []; end
|
def photos; []; end
|
||||||
|
|
||||||
|
#prevents error when trying to access @post.address in a post different than Reshare and StatusMessage types;
|
||||||
|
#check PostPresenter
|
||||||
|
def address
|
||||||
|
end
|
||||||
|
|
||||||
def self.excluding_blocks(user)
|
def self.excluding_blocks(user)
|
||||||
people = user.blocks.map{|b| b.person_id}
|
people = user.blocks.map{|b| b.person_id}
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,10 @@ class Reshare < Post
|
||||||
current
|
current
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def address
|
||||||
|
absolute_root.location.try(:address)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def after_parse
|
def after_parse
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ class StatusMessage < Post
|
||||||
|
|
||||||
has_many :photos, :dependent => :destroy, :foreign_key => :status_message_guid, :primary_key => :guid
|
has_many :photos, :dependent => :destroy, :foreign_key => :status_message_guid, :primary_key => :guid
|
||||||
|
|
||||||
|
has_one :location
|
||||||
|
|
||||||
# a StatusMessage is federated before its photos are so presence_of_content() fails erroneously if no text is present
|
# a StatusMessage is federated before its photos are so presence_of_content() fails erroneously if no text is present
|
||||||
# therefore, we put the validation in a before_destory callback instead of a validation
|
# therefore, we put the validation in a before_destory callback instead of a validation
|
||||||
before_destroy :presence_of_content
|
before_destroy :presence_of_content
|
||||||
|
|
@ -164,6 +166,10 @@ class StatusMessage < Post
|
||||||
self.oembed_url = urls.find{ |url| !TRUSTED_OEMBED_PROVIDERS.find(url).nil? }
|
self.oembed_url = urls.find{ |url| !TRUSTED_OEMBED_PROVIDERS.find(url).nil? }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def address
|
||||||
|
location.try(:address)
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
def presence_of_content
|
def presence_of_content
|
||||||
unless text_and_photos_blank?
|
unless text_and_photos_blank?
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ class PostPresenter
|
||||||
:title => title,
|
:title => title,
|
||||||
:next_post => next_post_path,
|
:next_post => next_post_path,
|
||||||
:previous_post => previous_post_path,
|
:previous_post => previous_post_path,
|
||||||
|
:address => @post.address,
|
||||||
|
|
||||||
:interactions => {
|
:interactions => {
|
||||||
:likes => [user_like].compact,
|
:likes => [user_like].compact,
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,12 @@
|
||||||
= status.text_area :fake_text, :rows => 2, :value => h(publisher_formatted_text), :tabindex => 1, :placeholder => "#{t('contacts.index.start_a_conversation')}..."
|
= status.text_area :fake_text, :rows => 2, :value => h(publisher_formatted_text), :tabindex => 1, :placeholder => "#{t('contacts.index.start_a_conversation')}..."
|
||||||
= status.hidden_field :text, :value => h(publisher_hidden_text), :class => 'clear_on_submit'
|
= status.hidden_field :text, :value => h(publisher_hidden_text), :class => 'clear_on_submit'
|
||||||
|
|
||||||
#file-upload{:title => t('.upload_photos')}
|
#publisher-images
|
||||||
= image_tag 'icons/camera.png', :alt => t('.upload_photos').titleize
|
#locator.btn{:title => t('.get_location')}
|
||||||
|
= image_tag 'icons/marker.png', :alt => t('.get_location').titleize, :class => 'publisher_image'
|
||||||
|
#file-upload.btn{:title => t('.upload_photos')}
|
||||||
|
= image_tag 'icons/camera.png', :alt => t('.upload_photos').titleize, :class => 'publisher_image'
|
||||||
|
= hidden_field :location, :coords
|
||||||
|
|
||||||
- if publisher_public
|
- if publisher_public
|
||||||
= hidden_field_tag 'aspect_ids[]', "public"
|
= hidden_field_tag 'aspect_ids[]', "public"
|
||||||
|
|
|
||||||
|
|
@ -794,6 +794,7 @@ en:
|
||||||
make_public: "make public"
|
make_public: "make public"
|
||||||
all: "all"
|
all: "all"
|
||||||
upload_photos: "Upload photos"
|
upload_photos: "Upload photos"
|
||||||
|
get_location: "Get your location"
|
||||||
all_contacts: "all contacts"
|
all_contacts: "all contacts"
|
||||||
share_with: "share with"
|
share_with: "share with"
|
||||||
whats_on_your_mind: "What's on your mind?"
|
whats_on_your_mind: "What's on your mind?"
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ en:
|
||||||
at_least_one_aspect: "You must publish to at least one aspect"
|
at_least_one_aspect: "You must publish to at least one aspect"
|
||||||
limited: "Limited - your post will only be seen by people you are sharing with"
|
limited: "Limited - your post will only be seen by people you are sharing with"
|
||||||
public: "Public - your post will be visible to everyone and found by search engines"
|
public: "Public - your post will be visible to everyone and found by search engines"
|
||||||
|
near_from: "Near from: <%= location %>"
|
||||||
infinite_scroll:
|
infinite_scroll:
|
||||||
no_more: "No more posts."
|
no_more: "No more posts."
|
||||||
no_more_contacts: "No more contacts."
|
no_more_contacts: "No more contacts."
|
||||||
|
|
|
||||||
12
db/migrate/20120405170105_create_locations.rb
Normal file
12
db/migrate/20120405170105_create_locations.rb
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
class CreateLocations < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
create_table :locations do |t|
|
||||||
|
t.string :address
|
||||||
|
t.string :lat
|
||||||
|
t.string :lng
|
||||||
|
t.integer :status_message_id
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -151,6 +151,15 @@ ActiveRecord::Schema.define(:version => 20130404211624) do
|
||||||
add_index "likes", ["target_id", "author_id", "target_type"], :name => "index_likes_on_target_id_and_author_id_and_target_type", :unique => true
|
add_index "likes", ["target_id", "author_id", "target_type"], :name => "index_likes_on_target_id_and_author_id_and_target_type", :unique => true
|
||||||
add_index "likes", ["target_id"], :name => "index_likes_on_post_id"
|
add_index "likes", ["target_id"], :name => "index_likes_on_post_id"
|
||||||
|
|
||||||
|
create_table "locations", :force => true do |t|
|
||||||
|
t.string "address"
|
||||||
|
t.string "lat"
|
||||||
|
t.string "lng"
|
||||||
|
t.integer "status_message_id"
|
||||||
|
t.datetime "created_at", :null => false
|
||||||
|
t.datetime "updated_at", :null => false
|
||||||
|
end
|
||||||
|
|
||||||
create_table "mentions", :force => true do |t|
|
create_table "mentions", :force => true do |t|
|
||||||
t.integer "post_id", :null => false
|
t.integer "post_id", :null => false
|
||||||
t.integer "person_id", :null => false
|
t.integer "person_id", :null => false
|
||||||
|
|
|
||||||
17
spec/javascripts/app/views/location_view_spec.js
Normal file
17
spec/javascripts/app/views/location_view_spec.js
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
describe("app.views.Location", function(){
|
||||||
|
beforeEach(function(){
|
||||||
|
OSM = {};
|
||||||
|
OSM.Locator = function(){return { getAddress:function(){}}};
|
||||||
|
|
||||||
|
this.view = new app.views.Location();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("When it gets instantiated", function(){
|
||||||
|
it("creates #location_address", function(){
|
||||||
|
|
||||||
|
expect($("#location_address")).toBeTruthy();
|
||||||
|
expect($("#location_coords")).toBeTruthy();
|
||||||
|
expect($("#hide_location")).toBeTruthy();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -292,4 +292,66 @@ describe("app.views.Publisher", function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
context("locator", function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
// should be jasmine helper
|
||||||
|
loginAs({name: "alice", avatar : {small : "http://avatar.com/photo.jpg"}});
|
||||||
|
|
||||||
|
spec.loadFixture("aspects_index");
|
||||||
|
this.view = new app.views.Publisher();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('#showLocation', function(){
|
||||||
|
it("Show location", function(){
|
||||||
|
|
||||||
|
// inserts location to the DOM; it is the location's view element
|
||||||
|
setFixtures('<div id="publisher_textarea_wrapper"></div>');
|
||||||
|
|
||||||
|
// creates a fake Locator
|
||||||
|
OSM = {};
|
||||||
|
OSM.Locator = function(){return { getAddress:function(){}}};
|
||||||
|
|
||||||
|
// validates there is not location
|
||||||
|
expect($("#location").length).toBe(0);
|
||||||
|
|
||||||
|
// this should create a new location
|
||||||
|
this.view.showLocation();
|
||||||
|
|
||||||
|
// validates there is one location created
|
||||||
|
expect($("#location").length).toBe(1);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#destroyLocation', function(){
|
||||||
|
it("Destroy location if exists", function(){
|
||||||
|
|
||||||
|
// inserts location to the DOM; it is the location's view element
|
||||||
|
setFixtures('<div id="location"></div>');
|
||||||
|
|
||||||
|
// creates a new Location view with the #location element
|
||||||
|
app.views.Location = new Backbone.View({el:"#location"});
|
||||||
|
|
||||||
|
// creates the mock
|
||||||
|
app.views.location = sinon.mock(app.views.Location).object;
|
||||||
|
|
||||||
|
// calls the destroy function and test the expected result
|
||||||
|
this.view.destroyLocation();
|
||||||
|
expect($("#location").length).toBe(0);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#avoidEnter', function(){
|
||||||
|
it("Avoid submitting the form when pressing enter", function(){
|
||||||
|
// simulates the event object
|
||||||
|
evt = {};
|
||||||
|
evt.keyCode = 13;
|
||||||
|
|
||||||
|
// should return false in order to avoid the form submition
|
||||||
|
expect(this.view.avoidEnter(evt)).toBeFalsy();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
|
||||||
24
spec/javascripts/osmlocator-spec.js
Normal file
24
spec/javascripts/osmlocator-spec.js
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
describe("Locator", function(){
|
||||||
|
navigator.geolocation['getCurrentPosition'] = function(myCallback){
|
||||||
|
lat = 1;
|
||||||
|
lon = 2;
|
||||||
|
position = { coords: { latitude: lat, longitude: lon} }
|
||||||
|
return myCallback(position);
|
||||||
|
};
|
||||||
|
|
||||||
|
$.getJSON = function(url, myCallback){
|
||||||
|
if(url == "http://nominatim.openstreetmap.org/reverse?format=json&lat=1&lon=2&addressdetails=3")
|
||||||
|
{
|
||||||
|
return myCallback({ display_name: 'locator address' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var osmlocator = new OSM.Locator();
|
||||||
|
|
||||||
|
it("should return address, latitude, and longitude using getAddress method", function(){
|
||||||
|
osmlocator.getAddress(function(display_name, coordinates){
|
||||||
|
expect(display_name, 'locator address')
|
||||||
|
expect(coordinates, { latitude: 1, longitude: 2 })
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
15
spec/models/location_spec.rb
Normal file
15
spec/models/location_spec.rb
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Location do
|
||||||
|
describe 'before validation' do
|
||||||
|
it 'should create new location when it has coordinates' do
|
||||||
|
location = Location.new(coordinates:'1,2')
|
||||||
|
location.save.should be true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should not create new location when it does not have coordinates' do
|
||||||
|
location = Location.new()
|
||||||
|
location.save.should be false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Reference in a new issue