Move Javascript to the asset pipeline

* Move all Diaspora-specific javascripts to app/assets/javascripts
* Move all vendored javascripts to vendor/assets/javascripts
* Add the appropriate Sprockets require directives to make sure
  everything gets included in the right order
* Remove Jammit dependencies
* Fix all templates that were using Jammit's include_javascripts helper
* Add handlebars_assets gem for compiling Handlebars templates
* Move all Handlebars templates to app/assets/templates and rename
  from .handlebars to .jst.hbs (this is to keep them in the same
  global JST namespace that they were in under Jammit)
* Add public/assets to .gitignore since these files can and should
  be re-generated by Heroku or Capistrano during each deploy
* Fix a few Handlebars templates that were looking for images in the
  wrong location (I'm sure there are others, but it's late)
* Configure application.rb to precompile all javascript and css assets
  that were compiled by Jammit in the Rails 3.0 code
This commit is contained in:
Steven Hancock
2012-03-22 05:44:35 -07:00
committed by Maxwell Salzberg
parent 9dffb426d4
commit 1aa0b15c8c
175 changed files with 213 additions and 102 deletions

View File

@@ -1,46 +0,0 @@
var app = {
collections: {},
models: {},
helpers: {},
views: {},
pages: {},
forms: {},
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 || ""
},
initialize: function() {
app.router = new app.Router();
app.currentUser = app.user(window.current_user_attributes) || new app.models.User()
if(app.currentUser.authenticated()){
app.header = new app.views.Header;
$("header").prepend(app.header.el);
app.header.render();
}
Backbone.history.start({pushState: true});
// there's probably a better way to do this...
$("a[rel=backbone]").bind("click", function(evt){
evt.preventDefault();
var link = $(this);
$(".stream_title").text(link.text())
app.router.navigate(link.attr("href").substring(1) ,true)
})
}
};
$(function() {
app.initialize();
});

View File

@@ -1,7 +0,0 @@
app.collections.Comments = Backbone.Collection.extend({
model: app.models.Comment,
initialize : function(models, options) {
this.url = "/posts/" + options.post.id + "/comments" //not delegating to post.url() because when it is in a stream collection it delegates to that url
}
});

View File

@@ -1,7 +0,0 @@
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
}
});

View File

@@ -1,7 +0,0 @@
app.collections.Participations = Backbone.Collection.extend({
model: app.models.Participation,
initialize : function(models, options) {
this.url = "/posts/" + options.post.id + "/participations" //not delegating to post.url() because when it is in a stream collection it delegates to that url
}
});

View File

@@ -1,12 +0,0 @@
app.collections.Photos = Backbone.Collection.extend({
url : "/photos",
model: function(attrs, options) {
var modelClass = app.models.Photo
return new modelClass(attrs, options);
},
parse: function(resp){
return resp.photos;
}
});

View File

@@ -1,12 +0,0 @@
app.collections.Posts = Backbone.Collection.extend({
url : "/posts",
model: function(attrs, options) {
var modelClass = app.models[attrs.post_type] || app.models.Post
return new modelClass(attrs, options);
},
parse: function(resp){
return resp.posts;
}
});

View File

@@ -1,35 +0,0 @@
(function(){
var dateFormatter = function dateFormatter() {
};
dateFormatter.parse = function(date_string) {
var timestamp = new Date(date_string).getTime();
if (isNaN(timestamp)) {
timestamp = dateFormatter.parseISO8601UTC(date_string);
}
return timestamp;
},
dateFormatter.parseISO8601UTC = function(date_string) {
var iso8601_utc_pattern = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(.(\d{3}))?Z$/;
var time_components = date_string.match(iso8601_utc_pattern);
var timestamp = 0;
if (time_components != null) {
if (time_components[8] == undefined) {
time_components[8] = 0;
}
timestamp = Date.UTC(time_components[1], time_components[2] - 1, time_components[3],
time_components[4], time_components[5], time_components[6],
time_components[8]);
}
return timestamp;
},
app.helpers.dateFormatter = dateFormatter;
})();

View File

@@ -1,26 +0,0 @@
Handlebars.registerHelper('t', function(scope, values) {
return Diaspora.I18n.t(scope, values.hash)
})
Handlebars.registerHelper('imageUrl', function(path){
return app.baseImageUrl() + path;
})
Handlebars.registerHelper('linkToPerson', function(context, block) {
var html = "<a href=\"/people/" + context.guid + "\" class=\"author-name\">";
html+= block.fn(context);
html+= "</a>";
return html
})
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(typeof(person.avatar) == "undefined") { return }
size = (typeof(size) != "string" ? "small" : size);
imageClass = (typeof(imageClass) != "string" ? size : imageClass);
return "<img src=\"" + person.avatar[size] +"\" class=\"avatar " + imageClass + "\" title=\"" + person.name +"\" />";
})

View File

@@ -1,4 +0,0 @@
/* we need to wrap this in a document ready to ensure JST is accessible */
$(function(){
Handlebars.registerPartial('status-message', JST['status-message'])
});

View File

@@ -1,69 +0,0 @@
(function(){
var textFormatter = function textFormatter(model) {
var text = model.get("text");
var mentions = model.get("mentioned_people");
return textFormatter.mentionify(
textFormatter.hashtagify(
textFormatter.markdownify(text)
), mentions
)
};
textFormatter.markdownify = function markdownify(text){
var converter = Markdown.getSanitizingConverter();
// punycode non-ascii chars in urls
converter.hooks.chain("preConversion", function(text) {
// add < > around plain urls, effectively making them "autolinks"
var urlRegex = /(^|\s)\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/gi;
text = text.replace(urlRegex, function(wholematch, space, url) {
return space+"<"+url+">";
});
// process links
var linkRegex = /(\[.*\]:\s)?(<|\()(((https?|ftp):\/{1,3})([^'">\s]+))(>|\))/gi;
text = text.replace(linkRegex, function() {
var protocol = arguments[4];
var unicodeUrl = arguments[6];
var asciiUrl = protocol+punycode.toASCII(unicodeUrl);
if( !arguments[1] || arguments[1] == "") { // inline link
if(arguments[2] == "<") return "["+protocol+unicodeUrl+"]("+asciiUrl+")"; // without link text
else return arguments[2]+asciiUrl+arguments[7]; // with link text
} else { // reference style link
return arguments[1]+asciiUrl;
}
});
return text;
});
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">')
});
return converter.makeHtml(text)
};
textFormatter.hashtagify = function hashtagify(text){
var utf8WordCharcters =/(\s|^|>)#([\u0080-\uFFFF|\w|-]+|&lt;3)/g
return text.replace(utf8WordCharcters, function(hashtag, preceeder, tagText) {
return preceeder + "<a href='/tags/" + tagText + "' 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 person.diaspora_id == diasporaId
})
return person ? "<a href='/people/" + person.guid + "' class='mention'>" + fullName + "</a>" : fullName;
})
}
app.helpers.textFormatter = textFormatter;
})();

View File

@@ -1,3 +0,0 @@
app.models.Block = Backbone.Model.extend({
urlRoot : "/blocks"
});

View File

@@ -1,3 +0,0 @@
app.models.Comment = Backbone.Model.extend({
urlRoot: "/comments"
});

View File

@@ -1 +0,0 @@
app.models.Like = Backbone.Model.extend({ });

View File

@@ -1 +0,0 @@
app.models.Participation = Backbone.Model.extend({ });

View File

@@ -1,14 +0,0 @@
app.models.Photo = Backbone.Model.extend({
urlRoot : "/photos",
initialize : function() {},
createdAt : function() {
return this.timeOf("created_at");
},
timeOf: function(field) {
return app.helpers.dateFormatter.parse(this.get(field)) / 1000;
},
});

View File

@@ -1,69 +0,0 @@
app.models.Photos = Backbone.Model.extend({
initialize : function(){
this.photos = new app.collections.Photos([], this.photoOptions());
},
photoOptions :function(){
var order = this.sortOrder();
return {
comparator : function(photo) { return -photo[order](); }
}
},
url : function() {
return _.any(this.photos.models) ? this.timeFilteredPath() : this.basePath()
},
_fetching : false,
fetch : function(){
if(this._fetching) { return false; }
var self = this;
// we're fetching the collection... there is probably a better way to do this
self._fetching = true;
this.photos
.fetch({
add : true,
url : self.url()
})
.done(
function(resp){
// we're done fetching... there is probably a better way to handle this
self._fetching = false;
self.trigger("fetched", self);
// all loaded?
if(resp.photos && resp.photos.length == 0) {
self.trigger("allPostsLoaded", self);
}
}
);
return this;
},
basePath : function(){
return document.location.pathname;
},
timeFilteredPath : function(){
return this.basePath() + "?max_time=" + this.maxTime();
},
maxTime: function(){
var lastPost = _.last(this.photos.models);
return lastPost[this.sortOrder()]()
},
sortOrder : function() {
return "createdAt";
},
add : function(models){
this.photos.add(models)
}
});

View File

@@ -1,108 +0,0 @@
app.models.Post = Backbone.Model.extend({
urlRoot : "/posts",
initialize : function() {
this.setupCollections();
this.bind("change", this.setupCollections, this)
},
setupCollections: function() {
this.comments = new app.collections.Comments(this.get("comments") || this.get("last_three_comments"), {post : this});
this.likes = this.likes || new app.collections.Likes([], {post : this}); // load in the user like initially
this.participations = this.participations || new app.collections.Participations([], {post : this}); // load in the user like initially
},
createdAt : function() {
return this.timeOf("created_at");
},
interactedAt : function() {
return this.timeOf("interacted_at");
},
timeOf: function(field) {
return app.helpers.dateFormatter.parse(this.get(field)) / 1000;
},
createReshareUrl : "/reshares",
reshare : function(){
return this._reshare = this._reshare || new app.models.Reshare({root_guid : this.get("guid")});
},
reshareAuthor : function(){
return this.get("author")
},
toggleFollow : function() {
var userParticipation = this.get("user_participation");
if(userParticipation) {
this.unfollow();
} else {
this.follow();
}
},
follow : function() {
var self = this;
this.participations.create({}, {success : function(resp){
self.set(resp)
self.trigger('interacted', self)
}});
},
unfollow : function() {
var self = this;
var participationModel = new app.models.Participation(this.get("user_participation"));
participationModel.url = this.participations.url + "/" + participationModel.id;
participationModel.destroy({success : function(model, resp){
self.set(resp);
self.trigger('interacted', this)
}});
},
toggleLike : function() {
var userLike = this.get("user_like")
if(userLike) {
this.unlike()
} else {
this.like()
}
},
like : function() {
var self = this;
this.likes.create({}, {success : function(resp){
self.set(resp)
self.trigger('interacted', self)
}});
},
unlike : function() {
var self = this;
var likeModel = new app.models.Like(this.get("user_like"));
likeModel.url = this.likes.url + "/" + likeModel.id;
likeModel.destroy({success : function(model, resp) {
self.set(resp);
self.trigger('interacted', this)
}});
}
}, {
frameMoods : [
"Day"
],
legacyTemplateNames : [
"status-with-photo-backdrop",
"note",
"rich-media",
"multi-photo",
"photo-backdrop",
"activity-streams-photo",
"status"
]
});

View File

@@ -1,14 +0,0 @@
app.models.Reshare = app.models.Post.extend({
rootPost : function(){
this._rootPost = this._rootPost || new app.models.Post(this.get("root"));
return this._rootPost
},
reshare : function(){
return this.rootPost().reshare()
},
reshareAuthor : function(){
return this.rootPost().reshareAuthor()
}
});

View File

@@ -1,24 +0,0 @@
app.models.StatusMessage = app.models.Post.extend({
url : function(){
return this.isNew() ? '/status_messages' : '/posts/' + this.get("id");
},
defaults : {
'post_type' : 'StatusMessage',
'author' : app.currentUser ? app.currentUser.attributes : {}
},
toJSON : function(){
return {
status_message : _.clone(this.attributes),
aspect_ids : this.get("aspect_ids") && this.get("aspect_ids").split(","),
photos : this.photos && this.photos.pluck("id"),
services : mungeServices(this.get("services"))
}
function mungeServices (values) {
if(!values) { return; }
return values.length > 1 ? values : [values]
}
}
});

View File

@@ -1,67 +0,0 @@
app.models.Stream = Backbone.Collection.extend({
initialize : function(){
this.posts = new app.collections.Posts([], this.postOptions());
},
postOptions :function(){
var order = this.sortOrder();
return {
comparator : function(post) { return -post[order](); }
}
},
url : function(){
return _.any(this.posts.models) ? this.timeFilteredPath() : this.basePath()
},
_fetching : false,
fetch: function() {
if(this._fetching) { return false; }
var self = this
// we're fetching the collection... there is probably a better way to do this
self._fetching = true;
this.posts
.fetch({
add : true,
url : self.url()
})
.done(
function(resp){
// we're done fetching... there is probably a better way to handle this
self._fetching = false;
self.trigger("fetched", self);
// all loaded?
if(resp.posts && (resp.posts.author || resp.posts.length == 0)) {
self.trigger("allPostsLoaded", self);
}
}
)
return this;
},
basePath : function(){
return document.location.pathname;
},
timeFilteredPath : function(){
return this.basePath() + "?max_time=" + this.maxTime();
},
maxTime: function(){
var lastPost = _.last(this.posts.models);
return lastPost[this.sortOrder()]()
},
sortOrder : function() {
return this.basePath().match(/activity/) ? "interactedAt" : "createdAt"
},
add : function(models){
this.posts.add(models)
}
});

View File

@@ -1,11 +0,0 @@
app.models.User = Backbone.Model.extend({
toggleNsfwState : function() {
if(!app.currentUser.authenticated()){ return false }
this.set({showNsfw : !this.get("showNsfw")});
this.trigger("nsfwChanged");
},
authenticated : function() {
return !!this.id;
}
});

View File

@@ -1,97 +0,0 @@
app.pages.PostViewer = app.views.Base.extend({
templateName: "post-viewer",
subviews : {
"#post-content" : "postView",
"#post-nav" : "navView",
"#post-interactions" : "interactionsView",
"#header-container" : "authorView"
},
initialize : function(options) {
this.model = new app.models.Post({ id : options.id });
this.model.fetch().success(_.bind(this.initViews, this));
this.prepIdleHooks();
$(document).bind("keypress", _.bind(this.commentAnywhere, this))
$(document).bind("keypress", _.bind(this.invokePane, this))
$(document).bind("keyup", _.bind(this.closePane, this))
},
initViews : function() {
/* init view */
this.authorView = new app.views.PostViewerAuthor({ model : this.model });
this.interactionsView = new app.views.PostViewerInteractions({ model : this.model });
this.navView = new app.views.PostViewerNav({ model : this.model });
this.postView = app.views.Post.showFactory(this.model)
this.render();
},
prepIdleHooks : function () {
$.idleTimer(3000);
$(document).bind("idle.idleTimer", function(){
$("body").addClass('idle');
});
$(document).bind("active.idleTimer", function(){
$("body").removeClass('idle');
});
},
postRenderTemplate : function() {
/* set the document title */
console.log(this.model)
document.title = this.model.get("title");
this.bindNavHooks();
},
bindNavHooks : function() {
/* navagation hooks */
var nextPostLocation = this.model.get("next_post");
var previousPostLocation = this.model.get("previous_post");
$(document).keydown(function(evt){
/* prevent nav from happening if the user is using the arrow
* keys to navigate through their comment text */
if($(evt.target).is("textarea")) { return }
switch(evt.keyCode) {
case 37:
navigate(nextPostLocation); break;
case 39:
navigate(previousPostLocation); break;
default:
break;
}
})
function navigate(loc) {
loc ? window.location = loc : null
}
},
commentAnywhere : function(evt) {
/* ignore enter, space bar, arrow keys */
if(_.include([13, 32, 37, 38, 39, 40], evt.keyCode)) { return }
this.interactionsView.invokePane();
$('#new-post-comment textarea').focus();
},
invokePane : function(evt) {
if(evt.keyCode != 32) { return }
this.interactionsView.invokePane();
},
closePane : function(evt) {
if(evt.keyCode != 27) { return }
this.interactionsView.hidePane();
}
});

View File

@@ -1,57 +0,0 @@
app.Router = Backbone.Router.extend({
routes: {
"activity": "stream",
"stream": "stream",
"participate": "stream",
"explore": "stream",
"aspects:query": "stream",
"commented": "stream",
"liked": "stream",
"mentions": "stream",
"people/:id": "stream",
"people/:id/photos": "photos",
"u/:name": "stream",
"followed_tags": "stream",
"tags/:name": "stream",
"posts/new" : "newPost",
"posts/:id": "singlePost",
"p/:id": "singlePost",
"framer": "framer"
},
stream : function() {
app.stream = new app.models.Stream();
app.page = new app.views.Stream({model : app.stream});
app.publisher = app.publisher || new app.views.Publisher({collection : app.stream.posts});
var streamFacesView = new app.views.StreamFaces({collection : app.stream.posts});
$("#main_stream").html(app.page.render().el);
$('#selected_aspect_contacts .content').html(streamFacesView.render().el);
},
photos : function() {
app.photos = new app.models.Photos();
app.page = new app.views.Photos({model : app.photos});
$("#main_stream").html(app.page.render().el);
},
newPost : function(){
var page = new app.pages.PostNew();
$("#container").html(page.render().el)
},
framer : function(){
var page = new app.pages.Framer();
$("#container").html(page.render().el)
},
singlePost : function(id) {
var page = new app.pages.PostViewer({ id: id });
$("#container").html(page.el);
}
});

View File

@@ -1,3 +0,0 @@
<a href="{{object_url}}" class="stream-photo-link">
<img src="{{image_url}}" data-small-photo="{{image_url}}" data-full-photo="{{image_url}}" class="stream-photo" />
</a>

View File

@@ -1,30 +0,0 @@
{{#unless all_comments_loaded}}
<div class="show_comments comment {{#unless showExpandCommentsLink}} hidden {{/unless}}">
<div class="media">
<a href="/posts/{{id}}/comments" class="toggle_post_comments">
{{t "stream.more_comments" count=moreCommentsCount}}
</a>
</div>
</div>
{{/unless}}
<div class="comments"> </div>
{{#if loggedIn}}
<div class="comment no-border media new_comment_form_wrapper {{#unless comments_count}} hidden {{/unless}}">
{{#with current_user}}
<a href="/people/{{guid}}" class="img">
{{{personImage this}}}
</a>
{{/with}}
<div class="bd">
<form accept-charset="UTF-8" action="/posts/{{id}}/comments" class="new_comment" id="new_comment_on_{{id}}" method="post">
<textarea class="comment_box" id="comment_text_on_{{id}}" name="text" rows="2" placeholder="{{t "stream.comment"}}" />
<div class="submit_button">
<input class="button creation" id="comment_submit_{{id}}" name="commit" type="submit" value="{{t "stream.comment"}}" />
</div>
</form>
</div>
</div>
{{/if}}

View File

@@ -1,27 +0,0 @@
{{#if canRemove}}
<div class="controls">
<a href="#" class="delete comment_delete" title="{{t "delete"}}">
<img alt="Deletelabel" src="{{imageUrl "deletelabel.png"}}" />
<a/>
</div>
{{/if}}
<div class="img">
{{#linkToPerson author}}
{{{personImage this "small" "small"}}}
{{/linkToPerson}}
</div>
<div class="bd">
<a href="/people/{{author.guid}}" class="author author-name">
{{author.name}}
</a>
<div class="collapsible comment-content">
{{{text}}}
</div>
<div class="info">
<time class="timeago" datetime="{{created_at}}"/>
</div>
</div>

View File

@@ -1,43 +0,0 @@
<span class="post_scope grey">
{{#if public}}
{{t "stream.public"}}
{{else}}
{{t "stream.limited"}}
{{/if}}
{{#if provider_display_name}}
<strong>
via {{provider_display_name}}
</strong>
{{/if}}
</span>
<a href="#" class="participate_action" rel='nofollow'>
{{#if user_participation}}
{{t "stream.unfollow"}}
{{else}}
{{t "stream.follow"}}
{{/if}}
</a>
·
<a href="#" class="like_action" rel='nofollow'>
{{#if user_like}}
{{t "stream.unlike"}}
{{else}}
{{t "stream.like"}}
{{/if}}
</a>
·
{{#if userCanReshare}}
<a href="#" class="reshare_action" rel='nofollow'>
{{t "stream.reshare"}}
</a>
·
{{/if}}
<a href="#" class="focus_comment_textarea" rel="nofollow">
{{t "stream.comment"}}
</a>

View File

@@ -1,117 +0,0 @@
<div class="container" style="position:relative;">
<a href="/stream">
<img alt="Logo_small" class="diaspora_header_logo" height="38px" width="65px" src="{{imageUrl "header-logo.png"}}" />
</a>
<span class="header-nav">
<span>
<a href="/stream">
{{t "my_stream"}}
</a>
</span>
<span>
<a href="/activity">
{{t "my_activity"}}
</a>
</span>
</span>
<div id="nav_badges">
<div class="badge" id="notification_badge">
<a href="/notifications" title="{{t "header.notifications"}}">
<img alt="{{t "header.notifications"}}" id="notification-flag" src="{{imageUrl "icons/notifications_grey.png"}}" />
<div class="badge_count {{#unless current_user.notifications_count}} hidden {{/unless}}">
{{current_user.notifications_count}}
</div>
</a>
</div>
<div class="badge" id="message_inbox_badge">
<a href="/conversations" title="{{t "header.messages"}}">
<img alt="{{t "header.messages"}}" src="{{imageUrl "icons/mail_grey.png"}}" />
<div class="badge_count {{#unless current_user.unread_messages_count}} hidden {{/unless}}">
{{current_user.unread_messages_count}}
</div>
</a>
</div>
<div id="notification_dropdown">
<div class="header">
<div class="right">
<a href="#" id="mark_all_read_link">
{{t "header.mark_all_as_read"}}
</a>
|
<a href="/notifications" id="view_all_notifications">
{{t "header.view_all"}}
</a>
</div>
<h4>
{{t "header.recent_notifications"}}
</h4>
</div>
<div class="notifications">
<div class="ajax_loader">
<img alt="Ajax-loader" src="{{imageUrl "ajax-loader.gif"}}">
</div>
</div>
</div>
</div>
<div id="hovercard_container">
<div id="hovercard">
<img class="avatar">
<h4>
<a class="person"></a>
</h4>
<p class="handle"></p>
<div id="hovercard_dropdown_container"></div>
<div class="hovercard_footer">
<div class="footer_container">
<div class="hashtags"></div>
</div>
</div>
</div>
</div>
<ul class="dropdown" id="user_menu">
<li>
<div class="right">
</div>
<img alt="{{current_user.name}}" class="avatar" src="{{current_user.avatar.small}}" title="{{current_user.name}}" />
<a href="#">{{current_user.name}}</a>
</li>
<li><a href="/people/{{current_user.guid}}">{{t "header.profile"}}</a></li>
<li><a href="/contacts">{{t "header.contacts"}}</a></li>
<li><a href="/user/edit">{{t "header.settings"}}</a></li>
{{#if current_user.admin}}
<li><a href="/admins/user_search">{{t "header.admin"}}</a></li>
{{/if}}
<li><a href="/users/sign_out">{{t "header.log_out"}}</a></li>
</ul>
<div id="global_search">
<form accept-charset="UTF-8" action="/people" class="search_form" method="get">
<input name="utf8" type="hidden" value="✓">
<input id="q" name="q" placeholder="{{t "header.search"}}" results="5" type="search" autocomplete="off" class="ac_input">
</form>
</div>
<div id="lightbox">
<div id="lightbox-content">
<a href="#" id="lightbox-close-link">[x] close</a>
<img id="lightbox-image">
<div id="lightbox-imageset"></div>
</div>
</div>
<div id="lightbox-backdrop"></div>
</div>

View File

@@ -1,28 +0,0 @@
{{#if likes_count}}
<div class="comment">
<div class="media">
<span class="img">
<img alt="Heart" src="{{imageUrl "heart.png"}}" />
</span>
<div class="bd">
{{#unless likes.length}}
<a href="#" class="expand_likes grey">
{{t "stream.likes" count=likes_count}}
</a>
{{else}}
{{#each likes}}
{{#with attributes.author}}
<a href="/people/{{guid}}">
<img src="{{avatar.small}}" class="avatar micro" title="{{name}}"/>
</a>
{{/with}}
{{/each}}
{{/unless}}
</div>
</div>
</div>
{{/if}}

View File

@@ -1,3 +0,0 @@
<a href="#" class="photo-link">
<img src="{{sizes.large}}" class="photo big_photo" data-small-photo="{{sizes.small}}" data-full-photo="{{sizes.large}}" rel="lightbox">
</a>

View File

@@ -1,10 +0,0 @@
<!-- header to be extracted -->
<div class="header">
<div id="header-container"> </div>
</div>
<div id="post-content"> </div>
<div id="post-nav"> </div>
<div id="post-interactions"> </div>

View File

@@ -1,25 +0,0 @@
<div class="img">
{{#linkToPerson author}}
{{{personImage this "small"}}}
{{/linkToPerson}}
</div>
<div class="bd">
{{#linkToPerson author}}
{{name}}
{{/linkToPerson}}
{{#if root}}
<i class="icon-retweet"></i>
{{#linkToPerson root.author}}
{{name}}
{{/linkToPerson}}
{{/if}}
<div class="post-time">
<time datetime="{{created_at}}" />
{{#unless public}}
<i class="icon-lock"> </i>
{{/unless}}
</div>
</div>

View File

@@ -1,15 +0,0 @@
<div class="img">
{{#linkToPerson author}}
{{{personImage this "small" "small"}}}
{{/linkToPerson}}
</div>
<div class="bd">
{{#linkToPerson author}}
{{name}}
{{/linkToPerson}}
<div class="comment-content">
{{{text}}}
</div>
</div>

View File

@@ -1,7 +0,0 @@
<div class="photoset">
<div class="img-bounding-box">
<a href="{{object_url}}" class="stream-photo-link">
<img src="{{image_url}}" data-small-photo="{{image_url}}" data-full-photo="{{image_url}}" class="stream-photo" />
</a>
</div>
</div>

View File

@@ -1,9 +0,0 @@
{{{text}}}
<div class="photoset">
{{#each photos}}
<div class="img-bounding-box">
<img src="{{sizes.large}}" />
</div>
{{/each}}
</div>

View File

@@ -1,3 +0,0 @@
<div class="note-content">
{{{text}}}
</div>

View File

@@ -1,4 +0,0 @@
{{#each photos}}
<div class="photo-fill" style="background-image: url({{sizes.large}})"> </div>
<img src="{{sizes.large}}" />
{{/each}}

View File

@@ -1,11 +0,0 @@
<div class="rich-media-container">
<div class="rich-media-container2">
{{{o_embed_cache.data.html}}}
<br>
<br>
{{{text}}}
</div>
</div>

View File

@@ -1,9 +0,0 @@
{{#each photos}}
<div class="photo-fill" style="background-image: url({{sizes.large}})">
<div class="darken">
<div class="darken-content">
{{{../text}}}
</div>
</div>
</div>
{{/each}}

View File

@@ -1,66 +0,0 @@
<a href="#" rel="auth-required" class="label like" title="{{#if user_like}} {{t "viewer.unlike"}} {{else}} {{t "viewer.like"}} {{/if}}">
{{#if user_like}}
<i class="icon-heart icon-red"></i>
{{else}}
<i class="icon-heart icon-white"></i>
{{/if}}
{{likes_count}}
</a>
<a href="#" rel="auth-required" class="label follow" title="{{#if user_participation}} {{t "viewer.stop_following_post"}} {{else}} {{t "viewer.follow_post"}} {{/if}}">
{{#if user_participation}}
<i class="icon-plus icon-green"></i>
{{else}}
<i class="icon-plus icon-white"></i>
{{/if}}
{{participations_count}}
</a>
{{#if userCanReshare}}
<a href="#" rel="auth-required" class="label reshare" title="{{#if user_reshare}} {{t "viewer.reshared"}} {{else}} {{t "viewer.reshare"}} {{/if}}">
{{#if user_reshare}}
<i class="icon-retweet icon-blue"></i>
{{else}}
<i class="icon-retweet icon-white"></i>
{{/if}}
{{reshares_count}}
</a>
{{else}}
<a class="label reshare-viewonly" title="{{#if user_reshare}} {{t "viewer.reshared"}} {{else}} {{t "viewer.reshare"}} {{/if}}">
{{#if user_reshare}}
<i class="icon-retweet icon-blue"></i>
{{else}}
<i class="icon-retweet icon-white"></i>
{{/if}}
{{reshares_count}}
</a>
{{/if}}
<a href="#" class="label comment" rel="invoke-interaction-pane" title="{{t "viewer.comment"}}">
<i class="icon-comment icon-white"></i>
{{comments_count}}
</a>
<!-- this acts as a dock underlay -->
<div id="post-info-sneaky" class="passive">
<div id="post-info-container-sneaky">
<a href="#" rel="invoke-interaction-pane" class="invoker">
<img src="/images/up-tick-inset.png" class="info-tick"/>
<a href="/" title="{{t "header.home"}}" class="home-button">
<i class="icon-home icon-white"></i>
</a>
</a>
</div>
</div>
<!-- this closes an open interaction pane -->
<div id="close-reactions-pane">
<div id="close-reactions-pane-container">
<a href="#" rel="hide-interaction-pane" class="invoker">
<a href="/" title="{{t "header.home"}}" class="home-button">
<i class="icon-home icon-white"></i>
</a>
</a>
</div>
</div>

View File

@@ -1,11 +0,0 @@
<div id="post-feedback"> </div>
<div id="post-info" style="display:none;">
<div id="post-info-container">
<img src="/images/down-tick-inset.png" class="info-tick"/>
<div id="post-reactions"> </div>
<div id="new-post-comment"> </div>
</div>
</div>

View File

@@ -1,11 +0,0 @@
<a href="#" class="nav-arrow left" id="forward">
<div class="nav-arrow-inner">
<img src="/images/arrow-left.png" />
</div>
</a>
<a href="#" class="nav-arrow right" id="back">
<div class="nav-arrow-inner">
<img src="/images/arrow-right.png" />
</div>
</a>

View File

@@ -1,8 +0,0 @@
<div id="new-post-comment-container">
<form class="form-inline">
<textarea class="new-comment-text" id="new-comment-text" placeholder="{{t "stream.comment"}}"></textarea>
<button type="submit" class="btn btn-small">
{{t "stream.comment"}}
</button>
</form>
</div>

View File

@@ -1,53 +0,0 @@
{{# if likes}}
<div id="post-likes">
<div class="well media">
<div class="img">
<i class="icon-heart icon-red"></i>
</div>
<div class="bd">
{{#each likes}}
{{#linkToPerson author}}
{{{personImage this "small" "micro"}}}
{{/linkToPerson}}
{{/each}}
</div>
</div>
</div>
{{/if}}
{{# if participations}}
<div id="post-follows">
<div class="well media">
<div class="img">
<i class="icon-plus icon-green"></i>
</div>
<div class="bd">
{{#each participations}}
{{#linkToPerson author}}
{{{personImage this "small" "micro"}}}
{{/linkToPerson}}
{{/each}}
</div>
</div>
</div>
{{/if}}
{{# if reshares}}
<div id="post-reshares">
<div class="well media">
<div class="img">
<i class="icon-retweet icon-blue"></i>
</div>
<div class="bd">
{{#each reshares}}
{{#linkToPerson author}}
{{{personImage this "small" "micro"}}}
{{/linkToPerson}}
{{/each}}
</div>
</div>
</div>
{{/if}}
<div id="post-comments"> </div>

View File

@@ -1,46 +0,0 @@
<div class="reshare">
{{#if root}}
<div class="media">
{{#with root}}
<a href="/people/{{author.guid}}" class="img">
<img src="{{author.avatar.small}}" class="avatar" />
</a>
{{/with}}
{{#with root}}
<div class="bd">
<div>
<a href="/people/{{author.guid}}" class="author">
{{author.name}}
</a>
<span class="details grey">
-
<a href="/posts/{{id}}">
<time class="timeago" datetime="{{created_at}}"/>
</a>
{{#if reshares_count}}
-
{{t "stream.reshares" count=reshares_count}}
{{/if}}
</span>
</div>
{{/with}}
{{> status-message}}
</div>
</div>
{{else}}
<p>
{{t "stream.original_post_deleted"}}
</p>
{{/if}}
</div>

View File

@@ -1 +0,0 @@
<span class=text>{{ text }}</span>

View File

@@ -1,31 +0,0 @@
{{#if largePhoto}}
<div class="photo_attachments">
<a href="#" class="stream-photo-link">
{{#with largePhoto}}
<img src="{{sizes.large}}" class="stream-photo big_stream_photo" data-small-photo="{{sizes.small}}" data-full-photo="{{sizes.large}}" rel="lightbox">
{{/with}}
</a>
{{#each smallPhotos}}
<a href="#" class="stream-photo-link">
<img src="{{sizes.small}}" class="stream-photo thumb_small" data-small-photo="{{sizes.small}}" data-full-photo="{{sizes.large}}" rel="lightbox">
</a>
{{/each}}
{{/if}}
<div class="collapsible">
{{{text}}}
{{#if o_embed_cache}}
<div class="oembed">
{{#if o_embed_cache.data.thumbnail_url}}
<div class="thumb">
<img src="{{o_embed_cache.data.thumbnail_url}}" />
<div class="video-overlay"></div>
</div>
{{else}}
{{{o_embed_html}}}
{{/if}}
</div>
{{/if}}
</div>

View File

@@ -1,80 +0,0 @@
<div class="media">
{{#if current_user}}
<div class="controls">
{{#unless authorIsCurrentUser}}
<a href="#" rel=nofollow>
<img src="{{imageUrl "ignoreuser.png"}}"" alt="Ignoreuser" class="block_user control_icon" title="{{t "ignore"}}" />
</a>
<a href="#" rel=nofollow>
<img src="{{imageUrl "deletelabel.png"}}" class="delete control_icon hide_post" title="{{t "stream.hide"}}" />
</a>
{{else}}
<a href="#" rel=nofollow>
<img src="{{imageUrl "deletelabel.png"}}" class="delete control_icon remove_post" title="{{t "delete"}}" />
</a>
{{/unless}}
</div>
{{/if}}
{{#with author}}
<a href="/people/{{guid}}" class="img">
{{{personImage this}}}
</a>
{{/with}}
<div class="bd">
<div>
{{#with author}}
<a href="/people/{{guid}}" class="author">
{{name}}
</a>
{{/with}}
<span class="details grey">
-
<a href="/posts/{{id}}">
<time class="timeago" datetime="{{created_at}}" />
</a>
{{#if reshares_count}}
-
{{t "stream.reshares" count=reshares_count}}
{{/if}}
</span>
</div>
{{#if showPost}}
{{#if nsfw}}
<div class="nsfw_off">
<strong>
#NSFW
</strong>
|
<a href="#" class="toggle_nsfw_state">
{{t "stream.hide_nsfw_posts"}}
</a>
</div>
{{/if}}
<div class="post-content"> </div>
<div class="feedback"> </div>
<div class="likes"> </div>
<div class="comments"> </div>
{{else}}
<div class="nsfw-shield">
<strong>
#NSFW
</strong>
|
<a href="#" class="show_nsfw_post">
{{t "stream.show_nsfw_post"}}
</a>
|
<a href="#" class="toggle_nsfw_state">
{{t "stream.show_nsfw_posts"}}
</a>
</div>
{{/if}}
</div>

View File

@@ -1,5 +0,0 @@
{{#people}}
{{#linkToPerson this}}
{{{personImage this "small"}}}
{{/linkToPerson}}
{{/people}}

View File

@@ -1,70 +0,0 @@
app.views.Base = Backbone.View.extend({
initialize : function(options) {
this.setupRenderEvents();
},
presenter : function(){
return this.defaultPresenter()
},
setupRenderEvents : function(){
if(this.model) {
//this should be in streamobjects view
this.model.bind('remove', this.remove, this);
}
// this line is too generic. we usually only want to re-render on
// feedback changes as the post content, author, and time do not change.
//
// this.model.bind('change', this.render, this);
},
defaultPresenter : function(){
var modelJson = this.model ? _.clone(this.model.attributes) : {}
return _.extend(modelJson, {
current_user : app.currentUser.attributes,
loggedIn : app.currentUser.authenticated()
});
},
render : function() {
this.renderTemplate()
this.renderSubviews()
this.renderPluginWidgets()
this.removeTooltips()
return this
},
renderTemplate : function(){
var presenter = _.isFunction(this.presenter) ? this.presenter() : this.presenter
this.template = JST[this.templateName]
$(this.el)
.html(this.template(presenter))
.attr("data-template", _.last(this.templateName.split("/")));
this.postRenderTemplate();
},
postRenderTemplate : $.noop, //hella callbax yo
renderSubviews : function(){
var self = this;
_.each(this.subviews, function(property, selector){
var view = _.isFunction(self[property]) ? self[property]() : self[property]
if(view) {
self.$(selector).html(view.render().el)
view.delegateEvents();
}
})
},
renderPluginWidgets : function() {
this.$(this.tooltipSelector).twipsy();
this.$("time").timeago();
},
removeTooltips : function() {
$(".twipsy").remove();
}
});

View File

@@ -1,79 +0,0 @@
app.views.CommentStream = app.views.Base.extend({
templateName: "comment-stream",
className : "comment_stream",
events: {
"submit form": "createComment",
"focus .comment_box": "commentTextareaFocused",
"click .toggle_post_comments": "expandComments"
},
initialize: function(options) {
this.commentTemplate = options.commentTemplate;
this.setupBindings();
},
setupBindings: function() {
this.model.comments.bind('add', this.appendComment, this);
this.model.bind("commentsExpanded", this.render, this);
},
postRenderTemplate : function() {
this.$("textarea").placeholder();
this.model.comments.each(this.appendComment, this);
// add autoexpanders to new comment textarea
this.$("textarea").autoResize({'extraSpace' : 10});
},
presenter: function(){
return _.extend(this.defaultPresenter(), {
moreCommentsCount : (this.model.get("comments_count") - 3),
showExpandCommentsLink : (this.model.get("comments_count") > 3)
})
},
createComment: function(evt) {
if(evt){ evt.preventDefault(); }
this.model.comments.create({
"text" : this.$(".comment_box").val()
});
this.$(".comment_box").val("")
return this;
},
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()})
this.$(".comments").append(new app.views.Comment({
model: comment
}).render().el);
},
commentTextareaFocused: function(evt){
this.$("form").removeClass('hidden').addClass("open");
},
expandComments: function(evt){
if(evt){ evt.preventDefault(); }
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)
}
});
}
});

View File

@@ -1,31 +0,0 @@
app.views.Comment = app.views.Content.extend({
templateName: "comment",
className : "comment media",
events : function() {
return _.extend(app.views.Content.prototype.events, {
"click .comment_delete": "destroyModel"
});
},
presenter : function() {
return _.extend(this.defaultPresenter(), {
canRemove: this.canRemove(),
text : app.helpers.textFormatter(this.model)
})
},
ownComment : function() {
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")
},
canRemove : function() {
return app.currentUser.authenticated() && (this.ownComment() || this.postOwner())
}
});

View File

@@ -1,69 +0,0 @@
app.views.Content = app.views.StreamObject.extend({
events: {
"click .oembed .thumb": "showOembedContent",
"click .expander": "expandPost"
},
presenter : function(){
return _.extend(this.defaultPresenter(), {
text : app.helpers.textFormatter(this.model),
o_embed_html : this.embedHTML(),
largePhoto : this.largePhoto(),
smallPhotos : this.smallPhotos()
});
},
embedHTML: function(){
if(!this.model.get("o_embed_cache")) { return ""; }
var data = this.model.get("o_embed_cache").data;
if(data.type == "photo") {
return '<img src="'+data.url+'" width="'+data.width+'" height="'+data.height+'" />';
} else {
return data.html || ""
}
},
largePhoto : function() {
var photos = this.model.get("photos")
if(!photos || photos.length == 0) { return }
return photos[0]
},
smallPhotos : function() {
var photos = this.model.get("photos")
if(!photos || photos.length < 2) { return }
return photos.slice(1,8)
},
showOembedContent: function() {
var oembed = $(this.el).find(".oembed");
var insertHTML = $( this.embedHTML() );
var paramSeparator = ( /\?/.test(insertHTML.attr("src")) ) ? "&" : "?";
insertHTML.attr("src", insertHTML.attr("src") + paramSeparator + "autoplay=1");
oembed.html( insertHTML );
},
expandPost: function(evt) {
var el = $(this.el).find('.collapsible');
el.removeClass('collapsed').addClass('opened');
el.animate({'height':el.data('orig-height')}, 550, function() {
el.css('height','auto');
});
$(evt.currentTarget).hide();
}
});
app.views.StatusMessage = app.views.Content.extend({
templateName : "status-message"
});
app.views.Reshare = app.views.Content.extend({
templateName : "reshare"
});
app.views.ActivityStreams__Photo = app.views.Content.extend({
templateName : "activity-streams-photo"
});

View File

@@ -1,58 +0,0 @@
app.views.Feedback = app.views.Base.extend({
templateName: "feedback",
className : "info",
events: {
"click .like_action" : "toggleLike",
"click .participate_action" : "toggleFollow",
"click .reshare_action" : "resharePost"
},
initialize : function() {
this.model.bind('interacted', this.render, this);
},
presenter : function() {
return _.extend(this.defaultPresenter(), {
userCanReshare : this.userCanReshare()
})
},
toggleFollow : function(evt) {
if(evt) { evt.preventDefault(); }
this.model.toggleFollow();
},
toggleLike: function(evt) {
if(evt) { evt.preventDefault(); }
this.model.toggleLike();
},
resharePost : function(evt) {
if(evt) { evt.preventDefault(); }
if(!window.confirm("Reshare " + this.model.reshareAuthor().name + "'s post?")) { return }
var reshare = this.model.reshare()
var model = this.model
reshare.save({}, {
url: this.model.createReshareUrl,
success : function(resp){
app.stream && app.stream.add(reshare);
model.trigger("interacted")
}
});
},
userCanReshare : function() {
var isReshare = this.model.get("post_type") == "Reshare"
var rootExists = (isReshare ? this.model.get("root") : true)
var publicPost = this.model.get("public");
var userIsNotAuthor = this.model.get("author").diaspora_id != app.currentUser.get("diaspora_id");
var userIsNotRootAuthor = rootExists && (isReshare ? this.model.get("root").author.diaspora_id != app.currentUser.get("diaspora_id") : true)
return publicPost && app.currentUser.authenticated() && userIsNotAuthor && userIsNotRootAuthor;
}
});

View File

@@ -1,35 +0,0 @@
app.views.Header = app.views.Base.extend({
templateName : "header",
className : "dark-header",
events : {
"click ul.dropdown li:first-child" : "toggleDropdown"
},
initialize : function(options) {
$(document.body).click($.proxy(this.hideDropdown, this));
return this;
},
menuElement : function() {
return this.$("ul.dropdown");
},
toggleDropdown : function(evt) {
if(evt){ evt.preventDefault(); }
this.menuElement().toggleClass("active");
if($.browser.msie) {
this.$("header").toggleClass('ie-user-menu-active');
}
},
hideDropdown : function(evt) {
if(this.menuElement().hasClass("active") && !$(evt.target).parents("#user_menu").length) {
this.menuElement().removeClass("active");
}
}
});

View File

@@ -1,31 +0,0 @@
app.views.LikesInfo = app.views.StreamObject.extend({
templateName : "likes-info",
events : {
"click .expand_likes" : "showAvatars"
},
tooltipSelector : ".avatar",
initialize : function() {
this.model.bind('expandedLikes', this.render, this)
},
presenter : function() {
return _.extend(this.defaultPresenter(), {
likes : this.model.likes.models
})
},
showAvatars : function(evt){
if(evt) { evt.preventDefault() }
var self = this;
this.model.likes.fetch()
.done(function(resp){
// set like attribute and like collection
self.model.set({likes : self.model.likes.reset(resp)})
self.model.trigger("expandedLikes")
})
}
});

View File

@@ -1,13 +0,0 @@
app.views.Photo = app.views.StreamObject.extend({
templateName: "photo",
className : "photo loaded",
initialize : function() {
$(this.el).attr("id", this.model.get("guid"));
this.model.bind('remove', this.remove, this);
return this;
}
});

View File

@@ -1,60 +0,0 @@
app.views.Photos = Backbone.View.extend({
events : {},
initialize : function(options) {
this.photos = this.model;
this.collection = this.model.photos;
this.setupEvents();
this.setupLightbox();
},
setupEvents : function(){
this.photos.bind("fetched", this.removeLoader, this)
this.collection.bind("add", this.addPhoto, this);
},
addPhoto : function(photo) {
var photoView = new app.views.Photo({ model: photo });
$(this.el)[
(this.collection.at(0).id == photo.id)
? "prepend"
: "append"
](photoView.render().el);
return this;
},
render : function(evt) {
if(evt) {evt.preventDefault(); }
if(this.model.fetch()) {
this.appendLoader();
};
return this;
},
appendLoader: function(){
$("#paginate").html($("<img>", {
src : "/images/static-loader.png",
"class" : "loader"
}));
},
removeLoader: function() {
$("#paginate").empty();
},
setupLightbox : function(){
this.lightbox = Diaspora.BaseWidget.instantiate("Lightbox");
this.lightbox.set({
imageParent: '#main_stream',
imageSelector: 'img.photo'
});
$(this.el).delegate("a.photo-link", "click", this.lightbox.lightboxImageClicked);
},
});

View File

@@ -1,8 +0,0 @@
app.views.PostViewerAuthor = app.views.Base.extend({
id : "post-author",
className : "media",
templateName: "post-viewer/author"
});

View File

@@ -1,40 +0,0 @@
app.views.PostViewerFeedback = app.views.Feedback.extend({
id : "user-controls",
className : "",
templateName: "post-viewer/feedback",
events : {
"click *[rel='auth-required']" : "requireAuth",
"click .like" : "toggleLike",
"click .follow" : "toggleFollow",
"click .reshare" : "resharePost",
"click *[rel='invoke-interaction-pane']" : "invokePane",
"click *[rel='hide-interaction-pane']" : "hidePane"
},
tooltipSelector : ".label, .home-button",
postRenderTemplate : function() {
this.sneakyVisiblity()
},
sneakyVisiblity : function() {
if(!$("#post-info").is(":visible")) {
this.$("#post-info-sneaky").removeClass('passive')
}
},
invokePane : function(evt){ this.trigger("invokePane") },
hidePane : function(evt){ this.trigger("hidePane") },
requireAuth : function(evt) {
if( app.currentUser.authenticated() ) { return }
alert("you must be logged in to do that!")
return false;
}
});

View File

@@ -1,56 +0,0 @@
app.views.PostViewerInteractions = app.views.Base.extend({
className : "",
subviews : {
"#post-feedback" : "feedbackView",
"#post-reactions" : "reactionsView",
"#new-post-comment" : "newCommentView"
},
templateName: "post-viewer/interactions",
initialize : function() {
this.initViews();
this.feedbackView && this.feedbackView.bind("invokePane", this.invokePane, this)
this.feedbackView && this.feedbackView.bind("hidePane", this.hidePane, this)
},
initViews : function() {
this.reactionsView = new app.views.PostViewerReactions({ model : this.model })
/* subviews that require user */
this.feedbackView = new app.views.PostViewerFeedback({ model : this.model })
if(app.currentUser.authenticated()) {
this.newCommentView = new app.views.PostViewerNewComment({ model : this.model })
}
},
togglePane : function(evt) {
if(evt) { evt.preventDefault() }
$("#post-interactions").toggleClass("active")
this.$("#post-info").slideToggle(300)
this.removeTooltips()
},
invokePane : function() {
if(!this.$("#post-info").is(":visible")) {
this.$("#post-info-sneaky").addClass("passive")
this.togglePane()
}
},
hidePane : function() {
if(this.$("#post-info").is(":visible")) {
/* it takes about 400ms for the pane to hide. we need to keep
* the sneaky hidden until the slide is complete */
setTimeout(function(){
this.$("#post-info-sneaky").removeClass("passive")
}, 400)
this.togglePane()
}
}
});

View File

@@ -1,30 +0,0 @@
app.views.PostViewerNav = app.views.Base.extend({
templateName: "post-viewer/nav",
events : {
"click a" : "pjax"
},
postRenderTemplate : function() {
var mappings = {"#forward" : "next_post",
"#back" : "previous_post"};
_.each(mappings, function(attribute, selector){
this.setArrow(this.$(selector), this.model.get(attribute))
}, this);
},
setArrow : function(arrow, loc) {
loc ? arrow.attr('href', loc) : arrow.remove()
},
pjax : function(evt) {
if(evt) { evt.preventDefault(); }
var link;
evt.target.tagName != "A" ? link = $(evt.target).closest("a") : link = $(evt.target)
app.router.navigate(link.attr("href").substring(1), true)
}
});

View File

@@ -1,45 +0,0 @@
app.views.PostViewerNewComment = app.views.Base.extend({
templateName: "post-viewer/new-comment",
events : {
"click button" : "createComment",
"focus textarea" : "scrollToBottom"
},
scrollableArea : "#post-reactions",
postRenderTemplate : function() {
this.$("textarea").placeholder();
this.$("textarea").autoResize({'extraSpace' : 0});
},
createComment: function(evt) {
if(evt){ evt.preventDefault(); }
var self = this;
this.toggleFormState()
this.model.comments.create({
"text" : this.$("textarea").val()
}, {success : _.bind(self.clearAndReactivateForm, self)});
},
clearAndReactivateForm : function() {
this.model.trigger("interacted")
this.toggleFormState()
this.$("textarea").val("")
.css('height', '18px')
.focus()
},
toggleFormState : function() {
this.$("form").children().toggleClass('disabled')
},
scrollToBottom : function() {
$(this.scrollableArea).scrollTop($(this.scrollableArea).prop("scrollHeight"))
}
});

View File

@@ -1,34 +0,0 @@
app.views.PostViewerReactions = app.views.Base.extend({
className : "",
templateName: "post-viewer/reactions",
tooltipSelector : ".avatar",
initialize : function() {
this.model.bind('interacted', this.render, this);
},
postRenderTemplate : function() {
this.populateComments()
},
/* copy pasta from commentStream */
populateComments : function() {
this.model.comments.each(this.appendComment, this)
},
/* copy pasta from commentStream */
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()})
this.$("#post-comments").append(new app.views.Comment({
model: comment,
className : "post-comment media"
}).render().el);
}
});

View File

@@ -1,44 +0,0 @@
app.views.Post = app.views.StreamObject.extend({
presenter : function() {
return _.extend(this.defaultPresenter(), {
authorIsCurrentUser : this.authorIsCurrentUser(),
showPost : this.showPost(),
text : app.helpers.textFormatter(this.model)
})
},
authorIsCurrentUser : function() {
return app.currentUser.authenticated() && this.model.get("author").id == app.user().id
},
showPost : function() {
return (app.currentUser.get("showNsfw")) || !this.model.get("nsfw")
}
}, { //static methods below
showFactory : function(model) {
var frameName = model.get("frame_name");
if(_.include(app.models.Post.legacyTemplateNames, frameName)){
return legacyShow(model)
} else {
return new app.views.Post[frameName]({
model : model
})
}
function legacyShow(model) {
return new app.views.Post.Legacy({
model : model,
className : frameName + " post loaded",
templateName : "post-viewer/content/" + frameName
});
}
}
});
app.views.Post.Legacy = app.views.Post.extend({
initialize : function(options) {
this.templateName = options.templateName || this.templateName
}
})

View File

@@ -1,100 +0,0 @@
//this file is the scary no-no-zone bad-touch of our backbone code.
//after re-writing/eliminating the existing Publisher let's re-write
//this with PANACHE! <333 Dennis
app.views.Publisher = Backbone.View.extend({
el : "#publisher",
events : {
"focus textarea" : "open",
"click #hide_publisher" : "clear",
"submit form" : "createStatusMessage"
},
initialize : function(){
this.collection = this.collection //takes a Posts collection
return this;
},
createStatusMessage : function(evt) {
if(evt){ evt.preventDefault(); }
var serializedForm = $(evt.target).closest("form").serializeObject();
// lulz this code should be killed.
var statusMessage = new app.models.Post();
statusMessage.save({
"status_message" : {
"text" : serializedForm["status_message[text]"]
},
"aspect_ids" : serializedForm["aspect_ids[]"],
"photos" : serializedForm["photos[]"],
"services" : serializedForm["services[]"]
}, {
url : "/status_messages",
success : function() {
if(app.publisher) {
$(app.publisher.el).trigger('ajax:success');
}
if(app.stream) {
app.stream.posts.add(statusMessage.toJSON());
}
}
});
// clear state
this.clear();
},
clear : function() {
this.$('textarea').val("");
this.$('#publisher_textarea_wrapper').removeClass("with_attachments");
// remove photos
this.$("#photodropzone").find('li').remove();
this.$("input[name='photos[]']").remove();
// close publishing area (CSS)
this.close();
Publisher.clear()
return this;
},
open : function() {
$(this.el).removeClass('closed');
this.$("#publisher_textarea_wrapper").addClass('active');
this.$("textarea.ac_input").css('min-height', '42px');
return this;
},
close : function() {
$(this.el).addClass("closed");
this.$("#publisher_textarea_wrapper").removeClass("active");
this.$("textarea.ac_input").css('min-height', '');
return this;
}
});
// jQuery helper for serializing a <form> into JSON
$.fn.serializeObject = function()
{
var o = {};
var a = this.serializeArray();
$.each(a, function() {
if (o[this.name] !== undefined) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
return o;
};

View File

@@ -1,29 +0,0 @@
app.views.StreamFaces = app.views.Base.extend({
templateName : "stream-faces",
className : "stream-faces",
tooltipSelector : ".avatar",
initialize : function(){
this.updatePeople()
app.stream.posts.bind("add", this.updatePeople, this)
},
presenter : function() {
return {people : this.people}
},
updatePeople : function(){
if(this.people && this.people.length >= 15) { return }
this.people = _(this.collection.models).chain()
.map(function(post){ return post.get("author") })
.compact()
.uniq(false, function(person){ return person.id })
.value()
.slice(0,15);
this.render();
}
});

View File

@@ -1,19 +0,0 @@
app.views.StreamObject = app.views.Base.extend({
destroyModel: function(evt) {
if (evt) {
evt.preventDefault();
}
if (!confirm(Diaspora.I18n.t("confirm_dialog"))) {
return
}
this.model.destroy();
this.slideAndRemove();
},
slideAndRemove : function() {
$(this.el).slideUp(400, function() {
$(this).remove();
});
}
});

View File

@@ -1,119 +0,0 @@
app.views.Stream = Backbone.View.extend({
events: {
"click #paginate": "render"
},
initialize: function(options) {
this.stream = this.model
this.collection = this.model.posts
this.setupEvents()
this.setupInfiniteScroll()
this.setupLightbox()
this.postViews = []
},
setupEvents : function(){
this.stream.bind("fetched", this.removeLoader, this)
this.stream.bind("fetched", this.postRender, this)
this.stream.bind("allPostsLoaded", this.unbindInfScroll, this)
this.collection.bind("add", this.addPost, this);
if(window.app.user()) {
app.user().bind("nsfwChanged", function() {
_.map(this.postViews, function(view){ view.render() })
}, this)
}
},
addPost : function(post) {
var postView = new app.views.StreamPost({ model: post });
$(this.el)[
(this.collection.at(0).id == post.id)
? "prepend"
: "append"
](postView.render().el);
this.postViews.push(postView)
return this;
},
unbindInfScroll : function() {
$(window).unbind("scroll");
},
render : function(evt) {
if(evt) { evt.preventDefault(); }
// fetch more posts from the stream model
if(this.stream.fetch()) {
this.appendLoader()
};
return this;
},
postRender : function() {
// collapse long posts
var collHeight = 420,
collElem = $(this.el).find(".collapsible");
_.each(collElem, function(elem) {
var elem = $(elem),
oembed = elem.find(".oembed"),
addHeight = 0;
if( $.trim(oembed.html()) != "" ) {
addHeight = oembed.height();
}
// only collapse if height exceeds collHeight+20%
if( elem.height() > ((collHeight*1.2)+addHeight) && !elem.is(".opened") ) {
elem.data("orig-height", elem.height() );
elem
.height( Math.max(collHeight, addHeight) )
.addClass("collapsed")
.append(
$('<div />')
.addClass('expander')
.text( Diaspora.I18n.t("show_more") )
);
}
});
},
appendLoader: function(){
$("#paginate").html($("<img>", {
src : "/images/static-loader.png",
"class" : "loader"
}));
},
removeLoader: function() {
$("#paginate").empty();
},
setupLightbox : function(){
this.lightbox = Diaspora.BaseWidget.instantiate("Lightbox");
$(this.el).delegate("a.stream-photo-link", "click", this.lightbox.lightboxImageClicked);
},
setupInfiniteScroll : function() {
var throttledScroll = _.throttle($.proxy(this.infScroll, this), 200);
$(window).scroll(throttledScroll);
},
infScroll : function() {
var $window = $(window);
var distFromTop = $window.height() + $window.scrollTop();
var distFromBottom = $(document).height() - distFromTop;
var bufferPx = 500;
if(distFromBottom < bufferPx) {
this.render();
}
return this;
},
});

View File

@@ -1,29 +0,0 @@
/* Copyright (c) 2010-2011, Diaspora Inc. This file is
* licensed under the Affero General Public License version 3 or later. See
* the COPYRIGHT file.
*/
function toggleAspectTitle(){
$("#aspect_name_title").toggleClass('hidden');
$("#aspect_name_edit").toggleClass('hidden');
}
function updateAspectName(new_name) {
$('#aspect_name_title .name').html(new_name);
$('input#aspect_name').val(new_name);
}
function updatePageAspectName( an_id, new_name) {
$('ul#aspect_nav [data-guid="'+an_id+'"]').html(new_name);
}
$(document).ready(function() {
$('#rename_aspect_link').live('click', function(){
toggleAspectTitle();
});
$('form.edit_aspect').live('ajax:success', function(evt, data, status, xhr) {
updateAspectName(data['name']);
updatePageAspectName( data['id'], data['name'] );
toggleAspectTitle();
});
});

View File

@@ -1,18 +0,0 @@
/* Copyright (c) 2010-2011, Diaspora Inc. This file is
* licensed under the Affero General Public License version 3 or later. See
* the COPYRIGHT file.
*/
$(document).ready(function() {
$('#aspect_nav.left_nav .all_aspects .sub_nav').sortable({
items: "li[data-aspect_id]",
update: function(event, ui) {
var order = $(this).sortable("toArray", {attribute: "data-aspect_id"}),
obj = { 'reorder_aspects': order, '_method': 'put' };
$.ajax('/user', { type: 'post', dataType: 'script', data: obj });
},
revert: true,
helper: 'clone'
});
});

View File

@@ -1,61 +0,0 @@
// Copyright (c) 2010-2011, Diaspora Inc. This file is
// licensed under the Affero General Public License version 3 or later. See
// the COPYRIGHT file.
var AspectsDropdown = {
updateNumber: function(dropdown, personId, number, inAspectClass){
var button = dropdown.parents(".dropdown").children('.button.toggle'),
selectedAspects = dropdown.children(".selected").length,
allAspects = dropdown.children().length,
replacement;
if (number == 0) {
button.removeClass(inAspectClass);
if( dropdown.closest('#publisher').length ) {
replacement = Diaspora.I18n.t("aspect_dropdown.select_aspects");
} else {
replacement = Diaspora.I18n.t("aspect_dropdown.add_to_aspect");
/* flash message prompt */
var message = Diaspora.I18n.t("aspect_dropdown.stopped_sharing_with", {name: dropdown.data('person-short-name')});
Diaspora.page.flashMessages.render({success: true, notice: message});
}
}else if (selectedAspects == allAspects) {
replacement = Diaspora.I18n.t('aspect_dropdown.all_aspects');
}else if (number == 1) {
button.addClass(inAspectClass);
replacement = dropdown.find(".selected").first().text();
/* flash message prompt */
if( dropdown.closest('#publisher').length == 0 ) {
var message = Diaspora.I18n.t("aspect_dropdown.started_sharing_with", {name: dropdown.data('person-short-name')});
Diaspora.page.flashMessages.render({success: true, notice: message});
}
}else {
replacement = Diaspora.I18n.t('aspect_dropdown.toggle', { count: number.toString()})
}
button.text(replacement + ' ▼');
},
toggleCheckbox: function(check) {
if(!check.hasClass('radio')){
var selectedAspects = check.closest(".dropdown").find("li.radio");
AspectsDropdown.uncheckGroup(selectedAspects);
}
check.toggleClass('selected');
},
toggleRadio: function(check) {
var selectedAspects = check.closest(".dropdown").find("li");
AspectsDropdown.uncheckGroup(selectedAspects);
AspectsDropdown.toggleCheckbox(check);
},
uncheckGroup: function(elements){
$.each(elements, function(index, value) {
$(value).removeClass('selected');
});
}
};

View File

@@ -1,98 +0,0 @@
// Copyright (c) 2010-2011, Diaspora Inc. This file is
// licensed under the Affero General Public License version 3 or later. See
// the COPYRIGHT file.
var ContactEdit = {
init: function(){
$.extend(ContactEdit, AspectsDropdown);
$('.dropdown.aspect_membership .dropdown_list > li, .dropdown.inviter .dropdown_list > li').live('click', function(evt){
ContactEdit.processClick($(this), evt);
});
},
updateNumber: function(dropdown, personId, number){
var button = dropdown.parents(".dropdown").children('.button.toggle'),
replacement;
if (number == 0) {
button.removeClass("in_aspects");
replacement = Diaspora.I18n.t("aspect_dropdown.toggle.zero");
}else if (number == 1) {
button.addClass("in_aspects");
replacement = dropdown.find(".selected").first().text();
}else if (number < 3) {
replacement = Diaspora.I18n.t('aspect_dropdown.toggle.few', { count: number.toString()})
}else if (number > 3) {
replacement = Diaspora.I18n.t('aspect_dropdown.toggle.many', { count: number.toString()})
}else {
//the above one are a tautology, but I want to have them here once for once we figure out a neat way i18n them
replacement = Diaspora.I18n.t('aspect_dropdown.toggle.other', { count: number.toString()})
ContactEdit.toggleAspectMembership(li, evt);
}
},
inviteFriend: function(li, evt) {
$.post('/services/inviter/facebook.json', {
"aspect_id" : li.data("aspect_id"),
"uid" : li.parent().data("service_uid")
}, function(data){
ContactEdit.processSuccess(li, evt, data);
});
},
processSuccess: function(element, evt, data) {
element.removeClass('loading')
if (data.url != undefined) {
window.location = data.url;
} else {
element.toggleClass("selected");
Diaspora.widgets.flashes.render({'success':true, 'notice':data.message});
}
},
processClick: function(li, evt){
var dropdown = li.closest('.dropdown');
li.addClass('loading');
if (dropdown.hasClass('inviter')) {
ContactEdit.inviteFriend(li, evt);
dropdown.html('sending, please wait...');
}
else {
ContactEdit.toggleAspectMembership(li, evt);
}
},
toggleAspectMembership: function(li, evt) {
var button = li.find('.button'),
dropdown = li.closest('.dropdown'),
dropdownList = li.parent('.dropdown_list');
if(button.hasClass('disabled') || li.hasClass('newItem')){ return; }
var selected = li.hasClass("selected"),
routedId = selected ? "/42" : "";
$.post("/aspect_memberships" + routedId + ".json", {
"aspect_id": li.data("aspect_id"),
"person_id": li.parent().data("person_id"),
"_method": (selected) ? "DELETE" : "POST"
}, function(aspectMembership) {
ContactEdit.toggleCheckbox(li);
ContactEdit.updateNumber(li.closest(".dropdown_list"), li.parent().data("person_id"), aspectMembership.aspect_ids.length, 'in_aspects');
Diaspora.page.publish("aspectDropdown/updated", [li.parent().data("person_id"), li.parents(".dropdown").parent(".right").html()]);
})
.error(function() {
var message = Diaspora.I18n.t("aspect_dropdown.error", {name: dropdownList.data('person-short-name')});
Diaspora.page.flashMessages.render({success: false, notice: message});
dropdown.removeClass('active');
})
.complete(function() {
li.removeClass("loading");
});
}
};
$(document).ready(function(){
ContactEdit.init();
});

View File

@@ -1,82 +0,0 @@
/* Copyright (c) 2010-2011, Diaspora Inc. This file is
* licensed under the Affero General Public License version 3 or later. See
* the COPYRIGHT file.
*/
var List = {
initialize: function() {
$(".contact_list_search").live("keyup", function(e) {
var search = $(this);
var list = $(".contacts", ".searchable");
var query = new RegExp(search.val(),'i');
$("> .contact", list).each( function(idx, element) {
element = $(element);
if( !element.find(".name").text().match(query) ) {
element.addClass('hidden');
} else {
element.removeClass('hidden');
}
});
});
},
disconnectUser: function(contact_id){
$.ajax({
url: "/contacts/" + contact_id,
type: "DELETE",
success: function(){
if( $('.searchable').length == 1){
$('.searchable .contact[data-contact_id='+contact_id+']').fadeOut(200);
} else if($('#aspects_list').length == 1) {
$.facebox.close();
};
}
});
},
runDelayedSearch: function( searchTerm ) {
$.getJSON('/people/refresh_search',
{ q: searchTerm },
List.handleSearchRefresh
);
},
handleSearchRefresh: function( data ) {
var streamEl = $("#people_stream.stream");
var string = data.search_html || $("<p>", {
text : Diaspora.I18n.t("people.not_found")
});
streamEl.html(string);
},
startSearchDelay: function (theSearch) {
setTimeout( "List.runDelayedSearch('" + theSearch + "')", 10000);
}
};
$(document).ready(function() {
$('.added').bind('ajax:loading', function() {
var $this = $(this);
$this.addClass('disabled');
$this.fadeTo(200,0.4);
});
$('.added').bind('hover',
function() {
var $this = $(this)
$this.addClass("remove");
$this.children("img").attr("src","/images/icons/monotone_close_exit_delete.png");
},
function() {
var $this = $(this)
$this.removeClass("remove");
$this.children("img").attr("src","/images/icons/monotone_check_yes.png");
});
List.initialize();
});

View File

@@ -1,95 +0,0 @@
/* Copyright (c) 2010-2011, Diaspora Inc. This file is
* licensed under the Affero General Public License version 3 or later. See
* the COPYRIGHT file.
*/
(function() {
var Diaspora = {
Pages: {},
Widgets: {}
};
Diaspora.EventBroker = {
extend: function(Klass) {
var whatToExtend = (typeof Klass === "function") ? Klass.prototype : Klass;
$.extend(whatToExtend, {
eventsContainer: $({}),
publish: function(eventName, args) {
var eventNames = eventName.split(" ");
for(eventName in eventNames) {
this.eventsContainer.trigger(eventNames[eventName], args);
}
},
subscribe: function(eventName, callback, context) {
var eventNames = eventName.split(" ");
for(eventName in eventNames) {
this.eventsContainer.bind(eventNames[eventName], $.proxy(callback, context));
}
}
});
return whatToExtend;
}
};
Diaspora.BaseWidget = {
instantiate: function(Widget, element) {
$.extend(Diaspora.Widgets[Widget].prototype, Diaspora.EventBroker.extend(Diaspora.BaseWidget));
var widget = new Diaspora.Widgets[Widget](),
args = Array.prototype.slice.call(arguments, 1);
widget.publish("widget/ready", args);
return widget;
},
globalSubscribe: function(eventName, callback, context) {
Diaspora.page.subscribe(eventName, callback, context);
},
globalPublish: function(eventName, args) {
Diaspora.page.publish(eventName, args);
}
};
Diaspora.BasePage = function(body) {
$.extend(this, Diaspora.BaseWidget);
$.extend(this, {
directionDetector: this.instantiate("DirectionDetector"),
events: function() { return Diaspora.page.eventsContainer.data("events"); },
flashMessages: this.instantiate("FlashMessages"),
header: this.instantiate("Header", body.find("header")),
hoverCard: this.instantiate("HoverCard", body.find("#hovercard")),
timeAgo: this.instantiate("TimeAgo")
});
};
Diaspora.instantiatePage = function() {
if (typeof Diaspora.Pages[Diaspora.Page] === "undefined") {
Diaspora.page = Diaspora.EventBroker.extend(Diaspora.BaseWidget);
} else {
var Page = Diaspora.Pages[Diaspora.Page];
$.extend(Page.prototype, Diaspora.EventBroker.extend(Diaspora.BaseWidget));
Diaspora.page = new Page();
}
if(!$.mobile)//why does this need this?
$.extend(Diaspora.page, new Diaspora.BasePage($(document.body)));
Diaspora.page.publish("page/ready", [$(document.body)])
};
// temp hack to check if backbone is enabled for the page
Diaspora.backboneEnabled = function(){
return window.app && window.app.stream !== undefined;
}
window.Diaspora = Diaspora;
})();
$(Diaspora.instantiatePage);

File diff suppressed because it is too large Load Diff

View File

@@ -1,25 +0,0 @@
/* Copyright (c) 2010-2011, Diaspora Inc. This file is
* licensed under the Affero General Public License version 3 or later. See
* the COPYRIGHT file.
*/
var FriendFinder = {
initialize: function() {
$('.contact_list .button').click(function(){
$this = $(this);
var uid = $this.parents('li').attr("uid");
$this.parents('ul').children("#options_"+uid).slideToggle(function(){
if($this.text() == 'Done'){
$this.text($this.attr('old-text'));
} else {
$this.attr('old-text', $this.text());
$this.text('Done');
}
$(this).toggleClass('hidden');
});
});
}
};
$(document).ready(FriendFinder.initialize);

View File

@@ -1,30 +0,0 @@
Diaspora.Alert = {
faceboxTemplate:
'<div id="diaspora_alert">' +
'<div class="span-12 last">' +
'<div id="facebox_header">' +
'<h4>' +
'<%= title %>' +
'</h4>' +
'</div>' +
'<%= content %>' +
'</div>' +
'</div>',
show: function(title, content) {
$(_.template(this.faceboxTemplate, {
title: title,
content: content
})).appendTo(document.body);
$.facebox({
div: "#diaspora_alert"
}, "diaspora_alert");
}
};
$(function() {
$(document).bind("close.facebox", function() {
$("#diaspora_alert").remove();
});
});

View File

@@ -1,39 +0,0 @@
/* Copyright (c) 2010-2011, Diaspora Inc. This file is
* licensed under the Affero General Public License version 3 or later. See
* the COPYRIGHT file.
*/
Diaspora.I18n = {
language: "en",
locale: {},
loadLocale: function(locale, language) {
this.locale = locale;
this.language = language;
rule = this.t('pluralization_rule');
if (rule === "")
rule = 'function (n) { return n == 1 ? "one" : "other" }';
eval("this.pluralizationKey = "+rule);
},
t: function(item, views) {
var items = item.split("."),
translatedMessage,
nextNamespace;
while(nextNamespace = items.shift()) {
translatedMessage = (translatedMessage)
? translatedMessage[nextNamespace]
: this.locale[nextNamespace];
if(typeof translatedMessage === "undefined") {
return "";
}
}
if(views && typeof views.count !== "undefined") {
translatedMessage = translatedMessage[this.pluralizationKey(views.count)];
}
return _.template(translatedMessage, views || {});
}
};

View File

@@ -1,3 +0,0 @@
document.createElement('header');
document.createElement('footer');

View File

@@ -1,93 +0,0 @@
/* Copyright (c) 2010-2011, Diaspora Inc. This file is
* licensed under the Affero General Public License version 3 or later. See
* the COPYRIGHT file.
*/
$(document).ready(function(){
$('a.conversation').live('click', function(){
$.getScript(this.href, function() {
Diaspora.page.directionDetector.updateBinds();
});
history.pushState(null, "", this.href);
var conv = $(this).children('.stream_element'),
cBadge = $("#message_inbox_badge").children(".badge_count");
if(conv.hasClass('unread') ){
conv.removeClass('unread');
}
if(cBadge.html() !== null) {
cBadge.html().replace(/\d+/, function(num){
num = parseInt(num);
cBadge.html(parseInt(num)-1);
if(num == 1) {
cBadge.addClass("hidden");
}
});
}
jQuery("abbr.timeago").timeago();
return false;
});
$(window).bind("popstate", function(){
if (location.href.match(/conversations\/\d+/) !== null) {
$.getScript(location.href, function() {
Diaspora.page.directionDetector.updateBinds();
});
return false;
}
});
resize();
$(window).resize(function(){
resize();
});
$('#conversation_inbox .stream').infinitescroll({
navSelector : ".pagination",
// selector for the paged navigation (it will be hidden)
nextSelector : ".pagination a.next_page",
// selector for the NEXT link (to page 2)
itemSelector : "#conversation_inbox .conversation",
// selector for all items you'll retrieve
localMode: true,
debug: false,
donetext: "no more.",
loadingText: "",
loadingImg: '/images/ajax-loader.gif'
}, function(){
$('.conversation', '.stream').bind('mousedown', function(){
bindIt($(this));
});
});
// kill scroll binding
$(window).unbind('.infscr');
// hook up the manual click guy.
$('a.next_page').click(function(){
$(document).trigger('retrieve.infscr');
return false;
});
// remove the paginator when we're done.
$(document).ajaxError(function(e,xhr,opt){
if (xhr.status == 404) { $('a.next_page').remove(); }
});
$('#reply_to_conversation').live('click', function(evt) {
evt.preventDefault();
$('html, body').animate({scrollTop:$(window).height()}, 'medium', function(){
$('#message_text').focus();
});
});
});
var resize = function(){
var inboxSidebar = $('#conversation_inbox'),
inboxSidebarOffset = inboxSidebar.offset().top,
windowHeight = $(window).height();
inboxSidebar.css('height', windowHeight - inboxSidebarOffset);
};

View File

@@ -1,763 +0,0 @@
/*
* Autocomplete - jQuery plugin 1.1pre
*
* Copyright (c) 2007 Dylan Verheul, Dan G. Switzer, Anjesh Tuladhar, Jörn Zaefferer
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
* Revision: $Id: jquery.autocomplete.js 5785 2008-07-12 10:37:33Z joern.zaefferer $
* Modified by Diaspora
*/
;(function($) {
$.fn.extend({
autocomplete: function(urlOrData, options) {
var isUrl = typeof urlOrData == "string";
options = $.extend({}, $.Autocompleter.defaults, {
url: isUrl ? urlOrData : null,
data: isUrl ? null : urlOrData,
delay: isUrl ? $.Autocompleter.defaults.delay : 10,
max: options && !options.scroll ? 10 : 150
}, options);
// if highlight is set to false, replace it with a do-nothing function
options.highlight = options.highlight || function(value) { return value; };
// if the formatMatch option is not specified, then use formatItem for backwards compatibility
options.formatMatch = options.formatMatch || options.formatItem;
return this.each(function() {
new $.Autocompleter(this, options);
});
},
result: function(handler) {
return this.bind("result", handler);
},
search: function(handler) {
return this.trigger("search", [handler]);
},
flushCache: function() {
return this.trigger("flushCache");
},
setOptions: function(options){
return this.trigger("setOptions", [options]);
},
unautocomplete: function() {
return this.trigger("unautocomplete");
}
});
$.Autocompleter = function(input, options) {
var KEY = KEYCODES;
// Create $ object for input element
var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);
var timeout;
var previousValue = "";
var cache = $.Autocompleter.Cache(options);
var hasFocus = 0;
var lastKeyPressCode;
var config = {
mouseDownOnSelect: false
};
var select = $.Autocompleter.Select(options, input, selectCurrent, config);
var blockSubmit;
// prevent form submit in opera when selecting with return key
$.browser.opera && $(input.form).bind("submit.autocomplete", function() {
if (blockSubmit) {
blockSubmit = false;
return false;
}
});
// only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all
$input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) {
// track last key pressed
lastKeyPressCode = event.keyCode;
switch(event.keyCode) {
case KEY.LEFT:
case KEY.RIGHT:
if( options.disableRightAndLeft && select.visible()){
event.preventDefault();
}
break;
case KEY.UP:
if ( select.visible() ) {
event.preventDefault();
select.prev();
} else {
onChange(0, true);
}
break;
case KEY.DOWN:
if ( select.visible() ) {
event.preventDefault();
select.next();
} else {
onChange(0, true);
}
break;
case KEY.PAGEUP:
if ( select.visible() ) {
event.preventDefault();
select.pageUp();
} else {
onChange(0, true);
}
break;
case KEY.PAGEDOWN:
if ( select.visible() ) {
event.preventDefault();
select.pageDown();
} else {
onChange(0, true);
}
break;
// matches also semicolon
case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
case KEY.TAB:
case KEY.RETURN:
if( selectCurrent() ) {
// stop default to prevent a form submit, Opera needs special handling
event.preventDefault();
blockSubmit = true;
return false;
}
break;
case KEY.ESC:
select.hide();
break;
default:
options.onLetterTyped(event, $input);
clearTimeout(timeout);
timeout = setTimeout(onChange, options.delay);
break;
}
}).focus(function(){
// track whether the field has focus, we shouldn't process any
// results if the field no longer has focus
hasFocus++;
}).blur(function() {
hasFocus = 0;
if (!config.mouseDownOnSelect) {
hideResults();
}
}).click(function() {
// show select when clicking in a focused field
if ( hasFocus++ > 1 && !select.visible() ) {
onChange(0, true);
}
}).bind("search", function() {
// TODO why not just specifying both arguments?
var fn = (arguments.length > 1) ? arguments[1] : null;
function findValueCallback(q, data) {
var result;
if( data && data.length ) {
for (var i=0; i < data.length; i++) {
if( data[i].result.toLowerCase() == q.toLowerCase() ) {
result = data[i];
break;
}
}
}
if( typeof fn == "function" ) fn(result);
else $input.trigger("result", result && [result.data, result.value]);
}
$.each(trimWords($input.val()), function(i, value) {
request(value, findValueCallback, findValueCallback);
});
}).bind("flushCache", function() {
cache.flush();
}).bind("setOptions", function() {
$.extend(options, arguments[1]);
// if we've updated the data, repopulate
if ( "data" in arguments[1] )
cache.populate();
}).bind("unautocomplete", function() {
select.unbind();
$input.unbind();
$(input.form).unbind(".autocomplete");
});
function selectCurrent() {
var selected = select.selected();
if( !selected )
return false;
var v = selected.result;
previousValue = v;
if ( options.multiple ) {
var words = trimWords($input.val());
if ( words.length > 1 ) {
v = words.slice(0, words.length - 1).join( options.multipleSeparator ) + options.multipleSeparator + v;
}
v += options.multipleSeparator;
}
hideResultsNow();
options.onSelect($input, selected.data, selected.value);
return true;
}
function onChange(crap, skipPrevCheck) {
if( lastKeyPressCode == KEY.DEL ) {
select.hide();
return;
}
var currentValue = $input.val();
if ( !skipPrevCheck && currentValue == previousValue )
return;
previousValue = currentValue;
currentValue = options.searchTermFromValue(currentValue, $input[0].selectionStart);
if ( currentValue.length >= options.minChars) {
$input.addClass(options.loadingClass);
if (!options.matchCase)
currentValue = currentValue.toLowerCase();
request(currentValue, receiveData, hideResultsNow);
} else {
stopLoading();
select.hide();
}
};
function trimWords(value) {
if ( !value ) {
return [""];
}
var words = value.split( options.multipleSeparator );
var result = [];
$.each(words, function(i, value) {
if ( $.trim(value) )
result[i] = $.trim(value);
});
return result;
}
// fills in the input box w/the first match (assumed to be the best match)
// q: the term entered
// sValue: the first matching result
function autoFill(q, sValue){
// autofill in the complete box w/the first match as long as the user hasn't entered in more data
// if the last user key pressed was backspace, don't autofill
if( options.autoFill && (options.lastWord($input.val(), null, options.multiple).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {
// fill in the value (keep the case the user has typed)
$input.val($input.val() + sValue.substring(options.lastWord(previousValue, null, options.multiple).length));
// select the portion of the value not typed by the user (so the next character will erase)
$.Autocompleter.Selection(input, previousValue.length, previousValue.length + sValue.length);
}
};
function hideResults() {
clearTimeout(timeout);
timeout = setTimeout(hideResultsNow, 200);
};
function hideResultsNow() {
select.hide();
clearTimeout(timeout);
stopLoading();
if (options.mustMatch) {
// call search and run callback
$input.search(
function (result){
// if no value found, clear the input box
if( !result ) {
if (options.multiple) {
var words = trimWords($input.val()).slice(0, -1);
$input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );
}
else
$input.val( "" );
}
}
);
}
};
function receiveData(q, data) {
if ( data && data.length && hasFocus ) {
stopLoading();
select.display(data, q);
autoFill(q, data[0].value);
select.show();
} else {
hideResultsNow();
}
};
function request(term, success, failure) {
if (!options.matchCase)
term = term.toLowerCase();
var data = cache.load(term);
// recieve the cached data
if (data && data.length) {
success(term, data);
// if an AJAX url has been supplied, try loading the data now
} else if( (typeof options.url == "string") && (options.url.length > 0) ){
var extraParams = {
timestamp: +new Date()
};
$.each(options.extraParams, function(key, param) {
extraParams[key] = typeof param == "function" ? param() : param;
});
$.ajax({
// try to leverage ajaxQueue plugin to abort previous requests
mode: "abort",
// limit abortion to this input
port: "autocomplete" + input.name,
dataType: options.dataType,
url: options.url,
data: $.extend({
q: options.lastWord(term, null, options.multiple),
limit: options.max
}, extraParams),
success: function(data) {
var parsed = options.parse && options.parse(data) || parse(data);
cache.add(term, parsed);
success(term, parsed);
}
});
} else {
// if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
select.emptyList();
failure(term);
}
};
function parse(data) {
var parsed = [];
var rows = data.split("\n");
for (var i=0; i < rows.length; i++) {
var row = $.trim(rows[i]);
if (row) {
row = row.split("|");
parsed[parsed.length] = {
data: row,
value: row[0],
result: options.formatResult && options.formatResult(row, row[0]) || row[0]
};
}
}
return parsed;
};
function stopLoading() {
$input.removeClass(options.loadingClass);
};
};
$.Autocompleter.defaults = {
onLetterTyped : function(event){},
lastWord : function(value, crap, multiple) {
if ( !multiple )
return value;
var words = trimWords(value);
return words[words.length - 1];
},
inputClass: "ac_input",
resultsClass: "ac_results",
loadingClass: "ac_loading",
onSelect: function(input, data, formatted){
if (select.visible())
// position cursor at end of input field
$.Autocompleter.Selection(input, input.value.length, input.value.length);
input.val(formatted);
},
minChars: 1,
delay: 400,
matchCase: false,
matchSubset: true,
matchContains: false,
cacheLength: 10,
max: 100,
mustMatch: false,
extraParams: {},
selectFirst: true,
formatItem: function(row) { return row[0]; },
selectionChanged : function(newItem) {},
formatMatch: null,
autoFill: false,
width: 0,
multiple: false,
multipleSeparator: ", ",
disableRightAndLeft: false,
highlight: function(value, term) {
return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
},
scroll: true,
scrollHeight: 180
};
$.Autocompleter.defaults.searchTermFromValue = $.Autocompleter.defaults.lastWord;
$.Autocompleter.Cache = function(options) {
var data = {};
var length = 0;
function matchSubset(s, sub) {
if (!options.matchCase)
s = s.toLowerCase();
var i = s.indexOf(sub);
if (options.matchContains == "word"){
i = s.toLowerCase().search("\\b" + sub.toLowerCase());
}
if (i == -1) return false;
return i == 0 || options.matchContains;
};
function add(q, value) {
if (length > options.cacheLength){
flush();
}
if (!data[q]){
length++;
}
data[q] = value;
}
function populate(){
if( !options.data ) return false;
// track the matches
var stMatchSets = {},
nullData = 0;
// no url was specified, we need to adjust the cache length to make sure it fits the local data store
if( !options.url ) options.cacheLength = 1;
// track all options for minChars = 0
stMatchSets[""] = [];
// loop through the array and create a lookup structure
for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
var rawValue = options.data[i];
// if rawValue is a string, make an array otherwise just reference the array
rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
var value = options.formatMatch(rawValue, i+1, options.data.length);
if ( value === false )
continue;
var firstChar = value.charAt(0).toLowerCase();
// if no lookup array for this character exists, look it up now
if( !stMatchSets[firstChar] )
stMatchSets[firstChar] = [];
// if the match is a string
var row = {
value: value,
data: rawValue,
result: options.formatResult && options.formatResult(rawValue) || value
};
// push the current match into the set list
stMatchSets[firstChar].push(row);
// keep track of minChars zero items
if ( nullData++ < options.max ) {
stMatchSets[""].push(row);
}
};
// add the data items to the cache
$.each(stMatchSets, function(i, value) {
// increase the cache size
options.cacheLength++;
// add to the cache
add(i, value);
});
}
// populate any existing data
setTimeout(populate, 25);
function flush(){
data = {};
length = 0;
}
return {
flush: flush,
add: add,
populate: populate,
load: function(q) {
if (!options.cacheLength || !length)
return null;
/*
* if dealing w/local data and matchContains than we must make sure
* to loop through all the data collections looking for matches
*/
if( !options.url && options.matchContains ){
// track all matches
var csub = [];
// loop through all the data grids for matches
for( var k in data ){
// don't search through the stMatchSets[""] (minChars: 0) cache
// this prevents duplicates
if( k.length > 0 ){
var c = data[k];
$.each(c, function(i, x) {
// if we've got a match, add it to the array
if (matchSubset(x.value, q)) {
csub.push(x);
}
});
}
}
return csub;
} else
// if the exact item exists, use it
if (data[q]){
return data[q];
} else
if (options.matchSubset) {
for (var i = q.length - 1; i >= options.minChars; i--) {
var c = data[q.substr(0, i)];
if (c) {
var csub = [];
$.each(c, function(i, x) {
if (matchSubset(x.value, q)) {
csub[csub.length] = x;
}
});
return csub;
}
}
}
return null;
}
};
};
$.Autocompleter.Select = function (options, input, select, config) {
var CLASSES = {
ACTIVE: "ac_over"
};
var listItems,
active = -1,
data,
term = "",
needsInit = true,
element,
list;
// Create results
function init() {
if (!needsInit)
return;
element = $("<div/>")
.hide()
.addClass(options.resultsClass)
.css("position", "absolute")
.appendTo(document.body);
list = $("<ul/>").appendTo(element).mouseover( function(event) {
if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
$(target(event)).addClass(CLASSES.ACTIVE);
}
}).click(function(event) {
$(target(event)).addClass(CLASSES.ACTIVE);
select();
// TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
input.focus();
return false;
}).mousedown(function() {
config.mouseDownOnSelect = true;
}).mouseup(function() {
config.mouseDownOnSelect = false;
});
if( options.width > 0 )
element.css("width", options.width);
needsInit = false;
}
function target(event) {
var element = event.target;
while(element && element.tagName != "LI")
element = element.parentNode;
// more fun with IE, sometimes event.target is empty, just ignore it then
if(!element)
return [];
return element;
}
function moveSelect(step) {
listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
movePosition(step);
var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
if(options.scroll) {
var offset = 0;
listItems.slice(0, active).each(function() {
offset += this.offsetHeight;
});
if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) {
list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight());
} else if(offset < list.scrollTop()) {
list.scrollTop(offset);
}
}
options.selectionChanged(activeItem);
};
function movePosition(step) {
active += step;
if (active < 0) {
active = listItems.size() - 1;
} else if (active >= listItems.size()) {
active = 0;
}
}
function limitNumberOfItems(available) {
return options.max && options.max < available
? options.max
: available;
}
function fillList() {
list.empty();
var max = limitNumberOfItems(data.length);
for (var i=0; i < max; i++) {
if (!data[i])
continue;
var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);
if ( formatted === false )
continue;
var li = $("<li/>").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0];
$.data(li, "ac_data", data[i]);
}
listItems = list.find("li");
if ( options.selectFirst ) {
listItems.slice(0, 1).addClass(CLASSES.ACTIVE);
active = 0;
}
// apply bgiframe if available
if ( $.fn.bgiframe )
list.bgiframe();
}
return {
display: function(d, q) {
init();
data = d;
term = q;
fillList();
},
next: function() {
moveSelect(1);
},
prev: function() {
moveSelect(-1);
},
pageUp: function() {
if (active != 0 && active - 8 < 0) {
moveSelect( -active );
} else {
moveSelect(-8);
}
},
pageDown: function() {
if (active != listItems.size() - 1 && active + 8 > listItems.size()) {
moveSelect( listItems.size() - 1 - active );
} else {
moveSelect(8);
}
},
hide: function() {
element && element.hide();
listItems && listItems.removeClass(CLASSES.ACTIVE);
active = -1;
},
visible : function() {
return element && element.is(":visible");
},
current: function() {
return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);
},
show: function() {
var offset = $(input).offset();
element.css({
width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(),
top: offset.top + input.offsetHeight,
left: offset.left
}).show();
if(options.scroll) {
list.scrollTop(0);
list.css({
maxHeight: options.scrollHeight,
overflow: 'auto'
});
if($.browser.msie && typeof document.body.style.maxHeight === "undefined") {
var listHeight = 0;
listItems.each(function() {
listHeight += this.offsetHeight;
});
var scrollbarsVisible = listHeight > options.scrollHeight;
list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight );
if (!scrollbarsVisible) {
// IE doesn't recalculate width when scrollbar disappears
listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );
}
}
}
},
selected: function() {
var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);
return selected && selected.length && $.data(selected[0], "ac_data");
},
emptyList: function (){
list && list.empty();
},
unbind: function() {
element && element.remove();
}
};
};
$.Autocompleter.Selection = function(field, start, end) {
if( field.createTextRange ){
var selRange = field.createTextRange();
selRange.collapse(true);
selRange.moveStart("character", start);
selRange.moveEnd("character", end);
selRange.select();
} else if( field.setSelectionRange ){
field.setSelectionRange(start, end);
} else {
if( field.selectionStart ){
field.selectionStart = start;
field.selectionEnd = end;
}
}
field.focus();
};
})(jQuery);

View File

@@ -1,621 +0,0 @@
/*!
// Infinite Scroll jQuery plugin
// copyright Paul Irish, licensed GPL & MIT
// version 2.0b1.110420
// home and docs: http://www.infinite-scroll.com
// Modified by Diaspora:
// A few callbacks were made options and generateInstanceID was make jquery 162 compatible
*/
; (function ($) {
/* Define 'infinitescroll' function
---------------------------------------------------*/
$.fn.infinitescroll = function infscr(options, callback) {
// grab each selector option and see if any fail.
function areSelectorsValid(opts) {
var debug = $.fn.infinitescroll._debug;
for (var key in opts) {
if (key.indexOf && key.indexOf('Selector') > -1 && $(opts[key]).length === 0) {
debug('Your ' + key + ' found no elements.');
return false;
}
return true;
}
}
// find the number to increment in the path.
function determinePath(path) {
if ($.isFunction(opts.pathParse)) {
debug('pathParse');
return [path];
} else if (path.match(/^(.*?)\b2\b(.*?$)/)) {
path = path.match(/^(.*?)\b2\b(.*?$)/).slice(1);
// if there is any 2 in the url at all.
} else if (path.match(/^(.*?)2(.*?$)/)) {
// page= is used in django:
// http://www.infinite-scroll.com/changelog/comment-page-1/#comment-127
if (path.match(/^(.*?page=)2(\/.*|$)/)) {
path = path.match(/^(.*?page=)2(\/.*|$)/).slice(1);
return path;
}
path = path.match(/^(.*?)2(.*?$)/).slice(1);
} else {
// page= is used in drupal too but second page is page=1 not page=2:
// thx Jerod Fritz, vladikoff
if (path.match(/^(.*?page=)1(\/.*|$)/)) {
path = path.match(/^(.*?page=)1(\/.*|$)/).slice(1);
return path;
} else {
debug('Sorry, we couldn\'t parse your Next (Previous Posts) URL. Verify your the css selector points to the correct A tag. If you still get this error: yell, scream, and kindly ask for help at infinite-scroll.com.');
props.isInvalidPage = true; //prevent it from running on this page.
}
}
debug('determinePath',path);
return path;
}
// Calculate internal height (used for local scroll)
function hiddenHeight(element) {
var height = 0;
$(element).children().each(function () {
height = height + $(this).outerHeight(false);
});
return height;
}
//Generate InstanceID based on random data (to give consistent but different ID's)
function generateInstanceID(element) {
var $element = $(element)
var number = $element.length + $element.html().length
if($element.attr("class") !== undefined){
number += $element.attr("class").length
}
if($element.attr("id") !== undefined){
number += $element.attr("id").length
}
opts.infid = number;
}
// if options is a string, use as a command
if (typeof options=='string') {
var command = options,
argument = callback,
validCommand = (command == 'pause' || command == 'destroy' || command == 'retrieve' || command == 'binding'),
debug = $.fn.infinitescroll._debug;
argument = argument || null;
command = (validCommand) ? $.fn.infinitescroll[command](argument) : debug('Invalid command');
return false;
}
// lets get started.
var opts = $.infinitescroll.opts = $.extend({}, $.infinitescroll.defaults, options),
props = $.infinitescroll, // shorthand
innerContainerHeight, box, frag, desturl, pause, error, errorStatus, method, result;
callback = $.fn.infinitescroll._callback = callback || function () { },
debug = $.fn.infinitescroll._debug,
error = $.fn.infinitescroll._error,
pause = $.fn.infinitescroll.pause,
destroy = $.fn.infinitescroll.destroy,
binding = $.fn.infinitescroll.binding;
// if selectors from opts aren't valid, return false
if (!areSelectorsValid(opts)) { return false; }
opts.container = opts.container || document.documentElement;
// contentSelector we'll use for our ajax call
opts.contentSelector = opts.contentSelector || this;
// Generate unique instance ID
opts.infid = (opts.infid == 0) ? generateInstanceID(opts.contentSelector) : opts.infid;
// loadMsgSelector - if we want to place the load message in a specific selector, defaulted to the contentSelector
opts.loadMsgSelector = opts.loadMsgSelector || opts.contentSelector;
// get the relative URL - everything past the domain name.
var relurl = /(.*?\/\/).*?(\/.*)/,
path = $(opts.nextSelector).attr('href');
if (!path) { debug('Navigation selector not found'); return; }
// set the path to be a relative URL from root.
opts.path = determinePath(path);
// define loading msg
props.loadingMsg = $('<div id="infscr-loading" style="text-align: center;"><img alt="Loading..." src="' +
opts.loadingImg + '" /><div>' + opts.loadingText + '</div></div>');
// preload the image
(new Image()).src = opts.loadingImg;
//Check if its HTML (window scroll) and set innerContainerHeight
opts.binder = (opts.container.nodeName == "HTML") ? $(window) : $(opts.container);
innerContainerHeight = (opts.container.nodeName == "HTML") ? $(document).height() : innerContainerHeight = hiddenHeight(opts.container);
debug('Scrolling in: ',(opts.container.nodeName == "HTML") ? 'window' : opts.container);
// distance from nav links to bottom
// computed as: height of the document + top offset of container - top offset of nav link
opts.pixelsFromNavToBottom = innerContainerHeight +
(opts.container == document.documentElement ? 0 : $(opts.container).offset().top) -
$(opts.navSelector).offset().top;
// set up our bindings
// bind scroll handler to element (if its a local scroll) or window
binding('bind');
opts.binder.trigger('smartscroll.infscr.' + opts.infid); // trigger the event, in case it's a short page
return this;
} // end of $.fn.infinitescroll()
/* Defaults and read-only properties object
---------------------------------------------------*/
$.infinitescroll = {
defaults: {
debug: false,
binder: $(window),
preload: false,
nextSelector: "div.navigation a:first",
loadingImg: "http://www.infinite-scroll.com/loading.gif",
loadingText: "<em>Loading the next set of posts...</em>",
donetext: "<em>Congratulations, you've reached the end of the internet.</em>",
navSelector: "div.navigation",
contentSelector: null, // not really a selector. :) it's whatever the method was called on..
loadMsgSelector: null,
loadingMsgRevealSpeed: 'fast', // controls how fast you want the loading message to come in, ex: 'fast', 'slow', 200 (milliseconds)
extraScrollPx: 150,
itemSelector: "div.post",
animate: false,
pathParse: undefined,
dataType: 'html',
appendCallback: true,
bufferPx: 40,
orientation: 'height',
errorCallback: function () { },
currPage: 1,
infid: 0, //Instance ID (Generated at setup)
isDuringAjax: false,
isInvalidPage: false,
isDestroyed: false,
isDone: false, // for when it goes all the way through the archive.
isPaused: false,
container: undefined, //If left undefined uses window scroll, set as container for local scroll
pixelsFromNavToBottom: undefined,
path: undefined
},
loadingImg: undefined,
loadingMsg: undefined,
currDOMChunk: null // defined in setup()'s load()
};
/* Methods + Commands
---------------------------------------------------*/
// Console log wrapper.
$.fn.infinitescroll._debug = function infscr_debug() {
if ($.infinitescroll.opts.debug) {
return window.console && console.log.call(console, arguments);
}
}
// shortcut function for...getting shortcuts
$.fn.infinitescroll._shorthand = function infscr_shorthand() {
// someone should write this, and it would rule
};
// Near Bottom (isNearBottom)
$.fn.infinitescroll._nearbottom = function infscr_nearbottom() {
// replace with shorthand function
var opts = $.infinitescroll.opts,
debug = $.fn.infinitescroll._debug,
hiddenHeight = $.fn.infinitescroll._hiddenheight;
// distance remaining in the scroll
// computed as: document height - distance already scroll - viewport height - buffer
if (opts.container.nodeName == "HTML") {
var pixelsFromWindowBottomToBottom = 0
+ $(document).height()
// have to do this bs because safari doesnt report a scrollTop on the html element
- ($(opts.container).scrollTop() || $(opts.container.ownerDocument.body).scrollTop())
- $(window).height();
}
else {
var pixelsFromWindowBottomToBottom = 0
+ hiddenHeight(opts.container) - $(opts.container).scrollTop() - $(opts.container).height();
}
debug('math:', pixelsFromWindowBottomToBottom, opts.pixelsFromNavToBottom);
// if distance remaining in the scroll (including buffer) is less than the orignal nav to bottom....
return (pixelsFromWindowBottomToBottom - opts.bufferPx < opts.pixelsFromNavToBottom);
}
// Setup function (infscrSetup)
$.fn.infinitescroll._setup = function infscr_setup() {
// replace with shorthand function
var props = $.infinitescroll,
opts = $.infinitescroll.opts,
isNearBottom = $.fn.infinitescroll._nearbottom,
kickOffAjax = $.fn.infinitescroll.retrieve;
if (opts.isDuringAjax || opts.isInvalidPage || opts.isDone || opts.isDestroyed || opts.isPaused) return;
if (!isNearBottom(opts, props)) return;
kickOffAjax();
};
// Ajax function (kickOffAjax)
$.fn.infinitescroll.retrieve = function infscr_retrieve() {
// replace with shorthand function
var props = $.infinitescroll,
opts = props.opts,
debug = $.fn.infinitescroll._debug,
loadCallback = $.fn.infinitescroll._loadcallback,
error = $.fn.infinitescroll._error,
path = opts.path, // get this
box, frag, desturl, method, condition;
// we dont want to fire the ajax multiple times
opts.isDuringAjax = true;
// show the loading message quickly
// then hide the previous/next links after we're
// sure the loading message was visible
props.loadingMsg.appendTo(opts.loadMsgSelector).show();
$(opts.navSelector).hide();
// increment the URL bit. e.g. /page/3/
opts.currPage++;
debug('heading into ajax', path);
// if we're dealing with a table we can't use DIVs
box = $(opts.contentSelector).is('table') ? $('<tbody/>') : $('<div/>');
// INSERT DEBUG ERROR FOR invalid desturl
desturl = ($.isFunction(opts.pathParse)) ? opts.pathParse(path.join('2'), opts.currPage) : desturl = path.join(opts.currPage);
// desturl = path.join(opts.currPage);
// create switch parameter for append / callback
// MAKE SURE CALLBACK EXISTS???
method = (opts.dataType == 'html' || opts.dataType == 'json') ? opts.dataType : 'html+callback';
if (opts.appendCallback && opts.dataType == 'html') method += '+callback';
switch (method) {
case 'html+callback':
debug('Using HTML via .load() method');
box.load(desturl + ' ' + opts.itemSelector, null, function(jqXHR,textStatus) {
loadCallback(box,jqXHR.responseText);
});
break;
case 'html':
case 'json':
debug('Using '+(method.toUpperCase())+' via $.ajax() method');
$.ajax({
// params
url: desturl,
dataType: opts.dataType,
complete: function _infscrAjax(jqXHR,textStatus) {
condition = (typeof(jqXHR.isResolved) !== 'undefined') ? (jqXHR.isResolved()) : (textStatus === "success" || textStatus === "notmodified");
(condition) ? loadCallback(box,jqXHR.responseText) : error([404]);
}
});
break;
}
};
// Load callback
$.fn.infinitescroll._loadcallback = function infscr_loadcallback(box,data) {
// replace with shorthand function
var props = $.infinitescroll,
opts = $.infinitescroll.opts,
error = $.fn.infinitescroll._error,
showDoneMsg = $.fn.infinitescroll._donemsg,
callback = $.fn.infinitescroll._callback, // GLOBAL OBJECT FOR CALLBACK
result, frag;
result = (opts.isDone) ? 'done' : (!opts.appendCallback) ? 'no-append' : 'append';
switch (result) {
case 'done':
showDoneMsg();
return false;
break;
case 'no-append':
if (opts.dataType == 'html') {
data = '<div>'+data+'</div>';
data = $(data).find(opts.itemSelector);
};
break;
case 'append':
var children = box.children();
// if it didn't return anything
if (children.length == 0 || children.hasClass('error404')) {
// trigger a 404 error so we can quit.
return error([404]);
}
// use a documentFragment because it works when content is going into a table or UL
frag = document.createDocumentFragment();
while (box[0].firstChild) {
frag.appendChild(box[0].firstChild);
}
$(opts.contentSelector)[0].appendChild(frag);
// previously, we would pass in the new DOM element as context for the callback
// however we're now using a documentfragment, which doesnt havent parents or children,
// so the context is the contentContainer guy, and we pass in an array
// of the elements collected as the first argument.
data = children.get();
break;
}
// fadeout currently makes the <em>'d text ugly in IE6
props.loadingMsg.hide();
// smooth scroll to ease in the new content
if (opts.animate) {
var scrollTo = $(window).scrollTop() + $('#infscr-loading').height() + opts.extraScrollPx + 'px';
$('html,body').animate({ scrollTop: scrollTo }, 800, function () { opts.isDuringAjax = false; });
}
if (!opts.animate) opts.isDuringAjax = false; // once the call is done, we can allow it again.
callback.call($(opts.contentSelector)[0], data);
};
// Show done message.
$.fn.infinitescroll._donemsg = function infscr_donemsg() {
// replace with shorthand function
var props = $.infinitescroll,
opts = $.infinitescroll.opts;
props.loadingMsg
.find('img')
.hide()
.parent()
.find('div').html(opts.donetext).animate({ opacity: 1 }, 2000, function () {
$(this).parent().fadeOut('normal');
});
// user provided callback when done
opts.errorCallback();
}
// Pause function
$.fn.infinitescroll.pause = function infscr_pause(pause) {
// if pauseValue is not 'pause' or 'resume', toggle it's value
var debug = $.fn.infinitescroll._debug,
opts = $.infinitescroll.opts;
if (pause !== 'pause' && pause !== 'resume' && pause !== 'toggle' && pause !== null) {
debug('Invalid argument. Toggling pause value instead');
};
pause = (pause && (pause == 'pause' || pause == 'resume')) ? pause : 'toggle';
switch (pause) {
case 'pause':
opts.isPaused = true;
break;
case 'resume':
opts.isPaused = false;
break;
case 'toggle':
opts.isPaused = !opts.isPaused;
break;
}
debug('Paused',opts.isPaused);
return false;
}
// Error function
$.fn.infinitescroll._error = function infscr_error(xhr) {
// replace with shorthand function
var opts = $.infinitescroll.opts,
binder = (opts.container.nodeName == "HTML") ? $(window) : $(opts.container),
debug = $.fn.infinitescroll._debug,
showDoneMsg = $.fn.infinitescroll._donemsg,
error = (!opts.isDone && xhr == 404) ? 'end' : (opts.isDestroyed && xhr == 302) ? 'destroy' : 'unknown';
switch (error) {
case 'end':
// die if we're out of pages.
debug('Page not found. Self-destructing...');
showDoneMsg();
opts.isDone = true;
opts.currPage = 1; // if you need to go back to this instance
opts.isPaused = false;
binder.unbind('smartscroll.infscr.' + opts.infid);
break;
case 'destroy':
// die if destroyed.
debug('Destroyed. Going to next instance...');
opts.isDone = true;
opts.currPage = 1; // if you need to go back to this instance
opts.isPaused = false;
binder.unbind('smartscroll.infscr.' + opts.infid);
break;
case 'unknown':
// unknown error.
debug('Unknown Error. WHAT DID YOU DO?!...');
showDoneMsg();
opts.isDone = true;
opts.currPage = 1; // if you need to go back to this instance
binder.unbind('smartscroll.infscr.' + opts.infid);
break;
}
}
// Destroy current instance of the plugin
$.fn.infinitescroll.destroy = function infscr_destroy() {
// replace with shorthand function
var opts = $.infinitescroll.opts,
error = $.fn.infinitescroll._error;
opts.isDestroyed = true;
return error([302]);
}
// Scroll binding + unbinding
$.fn.infinitescroll.binding = function infscr_binding(binding) {
// replace with shorthand function
var opts = $.infinitescroll.opts,
setup = $.fn.infinitescroll._setup,
error = $.fn.infinitescroll._error,
debug = $.fn.infinitescroll._debug;
switch(binding) {
case 'bind':
opts.binder.bind('smartscroll.infscr.'+opts.infid, setup);
break;
case 'unbind':
opts.binder.unbind('smartscroll.infscr.'+opts.infid);
break;
}
debug('Binding',binding);
return false;
}
/*
* smartscroll: debounced scroll event for jQuery *
* https://github.com/lukeshumard/smartscroll
* Based on smartresize by @louis_remi: https://github.com/lrbabe/jquery.smartresize.js *
* Copyright 2011 Louis-Remi & Luke Shumard * Licensed under the MIT license. *
*/
var event = $.event,
scrollTimeout;
event.special.smartscroll = {
setup: function() {
$(this).bind( "scroll", event.special.smartscroll.handler );
},
teardown: function() {
$(this).unbind( "scroll", event.special.smartscroll.handler );
},
handler: function( event, execAsap ) {
// Save the context
var context = this,
args = arguments;
// set correct event type
event.type = "smartscroll";
if (scrollTimeout) { clearTimeout(scrollTimeout); }
scrollTimeout = setTimeout(function() {
jQuery.event.handle.apply( context, args );
}, execAsap === "execAsap"? 0 : 100);
}
};
$.fn.smartscroll = function( fn ) {
return fn ? this.bind( "smartscroll", fn ) : this.trigger( "smartscroll", ["execAsap"] );
};
})(jQuery);

View File

@@ -1,116 +0,0 @@
var KEYCODES = {
BACKSPACE : 8,
TAB : 9,
ENTER : 13,
RETURN : 13,
SHIFT : 16,
CTRL : 17,
ALT : 18,
PAUSE : 19,
BREAK : 19,
CAPSLOCK : 20,
ESCAPE : 27,
ESC : 27,
SPACEBAR : 32,
PAGEUP : 33,
PAGEDOWN : 34,
END : 35,
HOME : 36,
LEFT : 37,
UP : 38,
RIGHT : 39,
DOWN : 40,
INSERT : 45,
DEL : 46,
DELETE : 46,
0 : 48,
1 : 49,
2 : 50,
3 : 51,
4 : 52,
5 : 53,
6 : 54,
7 : 55,
8 : 56,
9 : 57,
A : 65,
B : 66,
C : 67,
D : 68,
E : 69,
F : 70,
G : 71,
H : 72,
I : 73,
J : 74,
K : 75,
L : 76,
M : 77,
N : 78,
O : 79,
P : 80,
Q : 81,
R : 82,
S : 83,
T : 84,
U : 85,
V : 86,
W : 87,
X : 88,
Y : 89,
Z : 90,
LEFTWINDOW : 91,
RIGHTWINDOW : 92,
SELECT : 93,
NUMPAD0 : 96,
NUMPAD1 : 97,
NUMPAD2 : 98,
NUMPAD3 : 99,
NUMPAD4 : 100,
NUMPAD5 : 101,
NUMPAD6 : 102,
NUMPAD7 : 103,
NUMPAD8 : 104,
NUMPAD9 : 105,
MULTIPLY : 106,
ADD : 107,
SUBTRACT : 109,
DECIMALPOINT : 110,
DIVIDE : 111,
F1 : 112,
F2 : 113,
F3 : 114,
F4 : 115,
F5 : 116,
F6 : 117,
F7 : 118,
F8 : 119,
F9 : 120,
F10 : 121,
F11 : 122,
F12 : 123,
NUMLOCK : 144,
SCROLLLOCK : 145,
SEMICOLON : 186,
EQUALSIGN : 187,
COMMA : 188,
DASH : 189,
PERIOD : 190,
FORWARDSLASH : 191,
ACCENTGRAVE : 192,
OPENBRACKET : 219,
BACKSLASH : 220,
CLOSEBRACKET : 221,
SINGLEQUOTE : 222,
isInsertion : function(keyCode){
if(keyCode <= 46 && keyCode != this.RETURN && keyCode != this.SPACEBAR){
return false;
}else if(keyCode > 90 && keyCode < 96){
return false;
}else if(keyCode >= 112 && keyCode <= 145){
return false;
}else {
return true;
}
}
};

View File

@@ -1,26 +0,0 @@
jQuery.fn.center = function () {
this.css("position","absolute");
this.css("top", ( $(window).height() - this.height() ) / 2+$(window).scrollTop() + "px");
this.css("left", ( $(window).width() - this.width() ) / 2+$(window).scrollLeft() + "px");
return this;
}
$(document).ready( function(){
var username = $("#user_username"),
password = $("#user_password"),
forgotPass = $("#forgot_password_link"),
controls = $("#controls");
$("#login").center();
$(window).resize(function(){
$("#login").center();
});
username.focus();
$("form").submit(function(){
$('#asterisk').addClass('rideSpinners');
forgotPass.addClass('hidden');
controls.addClass('hidden');
});
});

View File

@@ -1,26 +0,0 @@
var Mentions = {
initialize: function(mentionsInput) {
return mentionsInput.mentionsInput(Mentions.options);
},
fetchContacts : function(){
Mentions.contacts || $.getJSON("/contacts", function(data) {
Mentions.contacts = data;
});
},
options: {
elastic: false,
minChars: 1,
onDataRequest: function(mode, query, callback) {
var filteredResults = _.filter(Mentions.contacts, function(item) { return item.name.toLowerCase().indexOf(query.toLowerCase()) > -1 });
callback.call(this, filteredResults.slice(0,5));
},
templates: {
mentionItemSyntax: _.template("@{<%= mention.name %> ; <%= mention.handle %>}")
}
}
};

View File

@@ -1,265 +0,0 @@
$(document).ready(function(){
$('.shield a').click(function(){
$(this).parents('.shield_wrapper').remove();
});
var showLoader = function(link){
link.addClass('loading');
};
var removeLoader = function(link){
link.removeClass('loading')
.toggleClass('active')
.toggleClass('inactive');
};
/* Heart toggle */
$(".like_action", ".stream").bind("tap click", function(evt){
evt.preventDefault();
var link = $(this),
likeCounter = $(this).closest(".stream_element").find("like_count"),
href = link.attr("href");
if(!link.hasClass("loading")){
if(link.hasClass('inactive')) {
$.ajax({
url: href,
dataType: 'json',
type: 'POST',
beforeSend: showLoader(link),
success: function(data){
removeLoader(link);
link.attr("href", href + "/" + data["id"]);
if(likeCounter){
likeCounter.text(parseInt(likeCounter.text) + 1);
}
}
});
}
else if(link.hasClass("active")){
$.ajax({
url: link.attr("href"),
dataType: 'json',
type: 'DELETE',
beforeSend: showLoader(link),
complete: function(data){
removeLoader(link);
link.attr("href", href.replace(/\/\d+$/, ''));
if(likeCounter){
likeCounter.text(parseInt(likeCounter.text) - 1);
}
}
});
}
}
});
/* Reshare */
$(".reshare_action", ".stream").bind("tap click", function(evt){
evt.preventDefault();
var link = $(this),
href = link.attr("href"),
confirmText = link.attr('title');
if(!link.hasClass("loading")) {
if(link.hasClass('inactive')) {
if(confirm(confirmText)) {
$.ajax({
url: href + "&provider_display_name=mobile",
dataType: 'json',
type: 'POST',
beforeSend: showLoader(link),
success: function(data){
removeLoader(link);
},
error: function(data){
removeLoader(link);
alert("Failed to reshare!");
}
});
}
}
}
});
/* Show comments */
$(".show_comments", ".stream").bind("tap click", function(evt){
evt.preventDefault();
var link = $(this),
parent = link.closest(".bottom_bar").first(),
commentsContainer = function(){ return parent.find(".comment_container").first(); }
existingCommentsContainer = commentsContainer();
if( link.hasClass('active') ) {
existingCommentsContainer.hide();
if(!link.hasClass('bottom_collapse')){
link.removeClass('active');
} else {
parent.find(".show_comments").first().removeClass('active');
}
$('html,body').scrollTop(parent.offset().top - parent.closest(".stream_element").height() - 8);
} else if( existingCommentsContainer.length > 0) {
if(!existingCommentsContainer.hasClass('noComments')) {
$.ajax({
url: link.attr('href'),
success: function(data){
parent.append($(data).find('.comments_container').html());
link.addClass('active');
existingCommentsContainer.show();
scrollToOffset(parent, commentsContainer());
}
});
} else {
existingCommentsContainer.show();
}
link.addClass('active');
} else {
$.ajax({
url: link.attr('href'),
success: function(data){
parent.append(data);
link.addClass('active');
scrollToOffset(parent, commentsContainer());
}
});
}
});
var scrollToOffset = function(parent, commentsContainer){
var commentCount = commentsContainer.find("li.comment").length;
if( commentCount > 3 ) {
var lastComment = commentsContainer.find("li:nth-child("+(commentCount-4)+")");
$('html,body').animate({
scrollTop: lastComment.offset().top
}, 1000);
}
};
$(".stream").delegate("a.comment_action", "tap click", function(evt){
evt.preventDefault();
var link = $(this);
if(link.hasClass('inactive')) {
var parent = link.closest(".bottom_bar").first(),
container = link.closest('.bottom_bar').find('.add_comment_bottom_link_container').first();
$.ajax({
url: link.attr('href'),
beforeSend: function(){
link.addClass('loading');
},
context: link,
success: function(data){
var textarea = function(target) { return target.closest(".stream_element").find('textarea.comment_box').first()[0] };
link.removeClass('loading')
if(!link.hasClass("add_comment_bottom_link")){
link.removeClass('inactive');
}
container.hide();
parent.append(data);
console.log($(this).closest(".stream_element").find('textarea'));
MBP.autogrow(textarea($(this)));
}
});
}
});
$(".stream").delegate("a.cancel_new_comment", "tap click", function(evt){
evt.preventDefault();
var link = $(this);
form = link.closest("form"),
commentActionLink = link.closest(".bottom_bar").find("a.comment_action").first();
container = link.closest('.bottom_bar').find('.add_comment_bottom_link_container');
if(container.length > 0 ){
container.first().show();
}
commentActionLink.addClass("inactive");
form.remove();
});
$(".new_comment").live("submit", function(evt){
evt.preventDefault();
var form = $(this);
$.post(form.attr('action')+"?format=mobile", form.serialize(), function(data){
var bottomBar = form.closest('.bottom_bar').first(),
container = bottomBar.find('.add_comment_bottom_link_container'),
commentActionLink = bottomBar.find("a.comment_action").first();
reactionLink = bottomBar.find(".show_comments").first(),
commentCount = bottomBar.find(".comment_count");
if(container.length > 0) {
container.before(data);
form.remove();
container.show();
} else {
var container = $("<div class='comments_container not_all_present'></div>"),
comments = $("<ul class='comments'></ul>");
comments.html(data);
container.append(comments);
form.remove();
container.appendTo(bottomBar)
}
reactionLink.text(reactionLink.text().replace(/(\d+)/, function(match){ return parseInt(match) + 1; }));
commentCount.text(commentCount.text().replace(/(\d+)/, function(match){ return parseInt(match) + 1; }));
commentActionLink.addClass("inactive");
}, 'html');
});
$(".service_icon").bind("tap click", function(evt) {
var service = $(this).toggleClass("dim"),
selectedServices = $("#new_status_message .service_icon:not(.dim)"),
provider = service.attr("id"),
hiddenField = $("#new_status_message input[name='services[]'][value='" + provider + "']"),
publisherMaxChars = 40000,
serviceMaxChars;
$("#new_status_message .counter").remove();
$.each(selectedServices, function() {
serviceMaxChars = parseInt($(this).attr("maxchar"));
if(publisherMaxChars > serviceMaxChars) {
publisherMaxChars = serviceMaxChars;
}
});
$('#status_message_text').charCount({allowed: publisherMaxChars, warning: publisherMaxChars/10 });
if(hiddenField.length > 0) { hiddenField.remove(); }
else {
$("#new_status_message").append(
$("<input/>", {
name: "services[]",
type: "hidden",
value: provider
})
);
}
});
$("#submit_new_message").bind("tap click", function(evt){
evt.preventDefault();
$("#new_status_message").submit();
});
});

View File

@@ -1,7 +0,0 @@
Diaspora.Pages.AspectsIndex = function() {
var self = this;
this.subscribe("page/ready", function(evt, document) {
self.aspectNavigation = self.instantiate("AspectNavigation", document.find("ul#aspect_nav"));
});
};

View File

@@ -1,9 +0,0 @@
Diaspora.Pages.ContactsIndex = function() {
var self = this;
this.subscribe("page/ready", function(evt, document) {
self.infiniteScroll = self.instantiate("InfiniteScroll");
$('.conversation_button').twipsy({position: 'below'});
});
};

View File

@@ -1,7 +0,0 @@
Diaspora.Pages.FeaturedUsersIndex = function() {
var self = this;
this.subscribe("page/ready", function(evt, document) {
self.infiniteScroll = self.instantiate("InfiniteScroll");
});
};

View File

@@ -1,8 +0,0 @@
Diaspora.Pages.InvitationsEdit = function() {
var self = this;
this.subscribe("page/ready", function(evt, body) {
jQuery.ajaxSetup({'cache': true});
$('#user_username').twipsy({trigger: 'select', placement: 'right'});
});
};

View File

@@ -1,12 +0,0 @@
Diaspora.Pages.InvitationsNew = function() {
var self = this;
this.subscribe("page/ready", function(evt, document) {
var rtl = $('html').attr('dir') == 'rtl',
position = rtl ? 'left' : 'right';
$('#new_user [title]').twipsy({trigger: 'focus', placement: position});
$('#user_email').focus();
});
};

View File

@@ -1,8 +0,0 @@
Diaspora.Pages.NotificationsIndex = function() {
var self = this;
this.subscribe("page/ready", function(evt, document) {
self.infiniteScroll = self.instantiate("InfiniteScroll");
self.instantiate("TimeAgo", document.find("abbr.timeago"));
});
};

View File

@@ -1,7 +0,0 @@
Diaspora.Pages.ServicesFinder = function() {
var self = this;
this.subscribe("page/ready", function(evt, document) {
self.infiniteScroll = self.instantiate("InfiniteScroll");
});
};

View File

@@ -1,81 +0,0 @@
Diaspora.Pages.UsersGettingStarted = function() {
var self = this;
this.subscribe("page/ready", function(evt, body) {
self.peopleSearch = self.instantiate("Search", body.find("form.people.search_form"));
self.tagSearch = self.instantiate("Search", body.find("form.tag_input.search_form"));
$('#edit_profile').bind('ajax:success', function(evt, data, status, xhr){
$('#gs-name-form-spinner').addClass("hidden");
$('.profile .saved').show();
$('.profile .saved').fadeOut(2000);
});
// It seems that the default behavior of rails ujs is to clear the remote form
$('#edit_profile').bind('ajax:complete', function(evt, xhr, status){
var firstNameField = $("#profile_first_name");
firstNameField.val(firstNameField.data("cachedValue"));
/* flash message prompt */
var message = Diaspora.I18n.t("getting_started.hey", {'name': $("#profile_first_name").val()});
Diaspora.page.flashMessages.render({success: true, notice: message});
});
$("#profile_first_name").bind("change", function(){
$(this).data("cachedValue", $(this).val());
$('#edit_profile').submit();
$('#gs-name-form-spinner').removeClass("hidden");
});
$("#profile_first_name").bind("blur", function(){
$(this).removeClass("active_input");
});
$("#profile_first_name").bind("focus", function(){
$(this).addClass("active_input");
});
$("#awesome_button").bind("click", function(evt){
evt.preventDefault();
var confirmMessage = Diaspora.I18n.t("getting_started.no_tags");
if(($("#as-selections-tags").find(".as-selection-item").length > 0) || confirm(confirmMessage)) {
$('.tag_input').submit();
/* flash message prompt */
var message = Diaspora.I18n.t("getting_started.preparing_your_stream");
Diaspora.page.flashMessages.render({success: true, notice: message});
} else {
/* flash message prompt */
var message = Diaspora.I18n.t("getting_started.alright_ill_wait");
Diaspora.page.flashMessages.render({success: true, notice: message});
}
});
/* ------ */
var autocompleteInput = $("#follow_tags");
autocompleteInput.autoSuggest("/tags", {
selectedItemProp: "name",
searchObjProps: "name",
asHtmlID: "tags",
neverSubmit: true,
retriveLimit: 10,
selectionLimit: false,
minChars: 2,
keyDelay: 200,
startText: "",
emptyText: "no_results"
});
autocompleteInput.bind('keydown', function(evt){
if(evt.keyCode == 13 || evt.keyCode == 9 || evt.keyCode == 32){
evt.preventDefault();
if( $('li.as-result-item.active').length == 0 ){
$('li.as-result-item').first().click();
}
}
});
});
};

View File

@@ -1,72 +0,0 @@
/* Copyright (c) 2010-2011, Diaspora Inc. This file is
* licensed under the Affero General Public License version 3 or later. See
* the COPYRIGHT file.
*/
$(document).ready(function() {
//edit photo
$("#edit_photo_toggle").bind('click', function(evt) {
evt.preventDefault();
$("#photo_edit_options").toggle();
$(".edit_photo input:text").first().focus();
});
$('.edit_photo').bind('ajax:loading', function(data, json, xhr) {
$("#photo_edit_options").toggle();
$("#photo_spinner").show();
$("#show_photo").find("img").fadeTo(200,0.3);
});
$('.edit_photo').bind('ajax:failure', function(data, json, xhr) {
Diaspora.Alert.show("Failed to delete photo.", "Are you sure you own this?");
$("#show_photo").find("img").fadeTo(200,1);
$("#photo_spinner").hide();
});
$('.edit_photo').bind('ajax:success', function(data, json, xhr) {
json = $.parseJSON(json);
$(".edit_photo input:text").val(json.photo.text);
$("#caption").html(json.photo.text);
$("#show_photo").find("img").fadeTo(200,1);
$("#photo_spinner").hide();
});
// make profile photo
$('.make_profile_photo').bind('ajax:loading', function(data, json, xhr) {
var person_id = $(this).closest(".photo_options").attr('data-actor_person');
$("img[data-person_id='" + person_id + "']").fadeTo(200, 0.3);
});
$('.make_profile_photo').bind('ajax:success', function(data, json, xhr) {
json = $.parseJSON(json);
$("img[data-person_id='" + json.person_id + "']").fadeTo(200, 1).attr('src', json.image_url_small);
});
$('.make_profile_photo').bind('ajax:failure', function(data, json, xhr) {
var person_id = $(this).closest(".photo_options").attr('data-actor_person');
Diaspora.Alert.show("Failed to update profile photo!");
$("img[data-person_id='" + person_id + "']").fadeTo(200, 1);
});
// right/left hotkeys
$(document).keyup(function(e){
if(!$(e.target).hasClass('comment_box')){
//left
if(e.keyCode == 37) {
if( $("#photo_show_left").length > 0 ){
document.location = $("#photo_show_left").attr('href');
}
//right
} else if(e.keyCode == 39) {
if( $("#photo_show_right").length > 0 ){
document.location = $("#photo_show_right").attr('href');
}
}
}
});
});

View File

@@ -1,221 +0,0 @@
/* Copyright (c) 2010-2011, Diaspora Inc. This file is
* licensed under the Affero General Public License version 3 or later. See
* the COPYRIGHT file.
*/
//TODO: make this a widget
var Publisher = {
bookmarklet : false,
form: function(){
return Publisher.cachedForm = Publisher.cachedForm || $('#publisher');
},
input: function(){
return Publisher.cachedInput = Publisher.cachedInput || Publisher.form().find('#status_message_fake_text');
},
wrapper: function(){
return Publisher.cachedWrapper = Publisher.cachedWrapper || Publisher.form().find('#publisher_textarea_wrapper');
},
hiddenInput: function(){
return Publisher.cachedHiddenInput= Publisher.cachedHiddenInput || Publisher.form().find('#status_message_text');
},
submit: function(){
return Publisher.cachedSubmit = Publisher.cachedSubmit || Publisher.form().find("input[type='submit']");
},
determineSubmitAvailability: function(){
var onlyWhitespaces = ($.trim(Publisher.input().val()) === ''),
isSubmitDisabled = Publisher.submit().attr('disabled'),
isPhotoAttached = ($("#photodropzone").children().length > 0);
if ((onlyWhitespaces && !isPhotoAttached) && !isSubmitDisabled) {
Publisher.submit().attr('disabled', 'disabled');
} else if ((!onlyWhitespaces || isPhotoAttached) && isSubmitDisabled) {
Publisher.submit().removeAttr('disabled');
}
},
clear: function(){
$("#photodropzone").find('li').remove();
Publisher.input().mentionsInput("reset");
Publisher.wrapper().removeClass("with_attachments");
Publisher.hiddenInput().val('');
Publisher.determineSubmitAvailability()
},
bindServiceIcons: function(){
$(".service_icon").bind("click", function(evt){
$(this).toggleClass("dim");
Publisher.toggleServiceField($(this));
});
},
toggleServiceField: function(service){
Publisher.createCounter(service);
var provider = service.attr('id');
var hidden_field = $('#publisher [name="services[]"][value="'+provider+'"]');
if(hidden_field.length > 0){
hidden_field.remove();
} else {
$("#publisher .content_creation form").append(
'<input id="services_" name="services[]" type="hidden" value="'+provider+'">');
}
},
isPublicPost: function(){
return $('#publisher [name="aspect_ids[]"]').first().val() == "public";
},
isToAllAspects: function(){
return $('#publisher [name="aspect_ids[]"]').first().val() == "all_aspects";
},
selectedAspectIds: function() {
var aspects = $('#publisher [name="aspect_ids[]"]');
var aspectIds = [];
aspects.each(function() { aspectIds.push( parseInt($(this).attr('value'))); });
return aspectIds;
},
removeRadioSelection: function(hiddenFields){
$.each(hiddenFields, function(index, value){
var el = $(value);
if(el.val() == "all_aspects" || el.val() == "public") {
el.remove();
}
});
},
toggleAspectIds: function(li) {
var aspectId = li.attr('data-aspect_id'),
hiddenFields = $('#publisher [name="aspect_ids[]"]'),
appendId = function(){
$("#publisher .content_creation form").append(
'<input id="aspect_ids_" name="aspect_ids[]" type="hidden" value="'+aspectId+'">');
};
if(li.hasClass('radio')){
$.each(hiddenFields, function(index, value){
$(value).remove();
});
appendId();
// close dropdown after selecting a binary option
li.closest('.dropdown').removeClass('active');
} else {
var hiddenField = $('#publisher [name="aspect_ids[]"][value="'+aspectId+'"]');
// remove all radio selections
Publisher.removeRadioSelection(hiddenFields);
if(hiddenField.length > 0){
hiddenField.remove();
} else {
appendId();
}
}
},
createCounter: function(service){
var counter = $("#publisher .counter");
counter.remove();
var min = 40000;
var a = $('.service_icon:not(.dim)');
if(a.length > 0){
$.each(a, function(index, value){
var num = parseInt($(value).attr('maxchar'));
if (min > num) { min = num; }
});
$('#status_message_fake_text').charCount({allowed: min, warning: min/10 });
}
},
bindAspectToggles: function() {
$('#publisher .dropdown .dropdown_list li').bind("click", function(evt){
var li = $(this),
button = li.parent('.dropdown').find('.button');
if(li.hasClass('radio')){
AspectsDropdown.toggleRadio(li);
} else {
AspectsDropdown.toggleCheckbox(li);
}
AspectsDropdown.updateNumber(li.closest(".dropdown_list"), null, li.parent().find('li.selected').length, '');
Publisher.toggleAspectIds(li);
});
},
textChange : function(){
Publisher.determineSubmitAvailability();
Publisher.input().mentionsInput("val", function(value) {
Publisher.hiddenInput().val(value);
});
},
triggerGettingStarted: function(){
Publisher.setUpPopovers("#publisher .dropdown", {trigger: 'manual', offset: 10, id: "message_visibility_explain", placement:'below', html:true}, 1000);
Publisher.setUpPopovers("#publisher #status_message_fake_text", {trigger: 'manual', placement: 'right', offset: 30, id: "first_message_explain", html:true}, 600);
Publisher.setUpPopovers("#gs-shim", {trigger: 'manual', placement: 'left', id:"stream_explain", offset: -5, html:true}, 1400);
$("#publisher .button.creation").bind("click", function(){
$("#publisher .dropdown").popover("hide");
$("#publisher #status_message_fake_text").popover("hide");
});
},
setUpPopovers: function(selector, options, timeout){
var selection = $(selector);
selection.popover(options);
selection.bind("click", function(){$(this).popover("hide")});
setTimeout(function(){
selection.popover("show");
var popup = selection.data('popover').$tip[0],
closeIcon = $(popup).find(".close");
closeIcon.bind("click",function(){
if($(".popover").length == 1){
$.get("/getting_started_completed");
};
selection.popover("hide");
});
}, timeout);
},
initialize: function() {
Publisher.cachedForm = Publisher.cachedSubmit =
Publisher.cachedInput = Publisher.cachedHiddenInput = false;
Publisher.bindServiceIcons();
Publisher.bindAspectToggles();
Mentions.initialize(Publisher.input());
Publisher.input().bind("focus", function(){
Mentions.fetchContacts();
})
if(Publisher.hiddenInput().val() === "") {
Publisher.hiddenInput().val(Publisher.input().val());
}
Publisher.input().autoResize({'extraSpace' : 10});
Publisher.input().bind('textchange', Publisher.textChange);
}
};
$(document).ready(function() {
Publisher.initialize();
Diaspora.page.subscribe("stream/reloaded", Publisher.initialize);
});

View File

@@ -1,404 +0,0 @@
/*!
* Rails 3 Client Side Validations - v3.1.0
* https://github.com/bcardarlela/client_side_validations
*
* Copyright (c) 2011 Brian Cardarella
* Licensed under the MIT license
* http://www.opensource.org/licenses/mit-license.php
*/
(function($) {
$.fn.validate = function() {
return this.filter('form[data-validate]').each(function() {
var form = $(this);
var settings = window[form.attr('id')];
// Set up the events for the form
form
.submit(function() { return form.isValid(settings.validators); })
.bind('ajax:beforeSend', function() { return form.isValid(settings.validators); })
// Callbacks
.bind('form:validate:after', function(eventData) { clientSideValidations.callbacks.form.after( form, eventData); })
.bind('form:validate:before', function(eventData) { clientSideValidations.callbacks.form.before(form, eventData); })
.bind('form:validate:fail', function(eventData) { clientSideValidations.callbacks.form.fail( form, eventData); })
.bind('form:validate:pass', function(eventData) { clientSideValidations.callbacks.form.pass( form, eventData); })
// Set up the events for each validatable form element
.find('[data-validate]:input:not(:radio)')
.live('focusout', function() { $(this).isValid(settings.validators); })
.live('change', function() { $(this).data('changed', true); })
// Callbacks
.live('element:validate:after', function(eventData) { clientSideValidations.callbacks.element.after( $(this), eventData); })
.live('element:validate:before', function(eventData) { clientSideValidations.callbacks.element.before($(this), eventData); })
.live('element:validate:fail', function(eventData, message) {
var element = $(this);
clientSideValidations.callbacks.element.fail(element, message, function() {
addError(element, message);
}, eventData) })
.live('element:validate:pass', function(eventData) {
var element = $(this);
clientSideValidations.callbacks.element.pass(element, function() {
removeError(element);
}, eventData) })
// Checkboxes - Live events don't support filter
.end().find('[data-validate]:checkbox')
.live('click', function() { $(this).isValid(settings.validators); })
// Inputs for confirmations
.end().find('[id*=_confirmation]').each(function() {
var confirmationElement = $(this),
element = form.find('#' + this.id.match(/(.+)_confirmation/)[1] + '[data-validate]:input');
if (element[0]) {
$('#' + confirmationElement.attr('id'))
.live('focusout', function() {
element.data('changed', true).isValid(settings.validators);
})
.live('keyup', function() {
element.data('changed', true).isValid(settings.validators);
})
}
});
var addError = function(element, message) {
clientSideValidations.formBuilders[settings.type].add(element, settings, message);
}
var removeError = function(element) {
clientSideValidations.formBuilders[settings.type].remove(element, settings);
}
});
}
$.fn.isValid = function(validators) {
if ($(this[0]).is('form')) {
return validateForm($(this[0]), validators);
} else {
return validateElement($(this[0]), validators[this[0].name]);
}
}
var validateForm = function(form, validators) {
var valid = true;
form.trigger('form:validate:before').find('[data-validate]:input').each(function() {
if (!$(this).isValid(validators)) { valid = false; }
});
if (valid) {
form.trigger('form:validate:pass');
} else {
form.trigger('form:validate:fail');
}
form.trigger('form:validate:after');
return valid;
}
var validateElement = function(element, validators) {
element.trigger('element:validate:before');
if (element.data('changed') !== false) {
var valid = true;
element.data('changed', false);
// Because 'length' is defined on the list of validators we cannot call jQuery.each on
// the clientSideValidations.validators.all() object
for (kind in clientSideValidations.validators.all()) {
if (validators[kind] && (message = clientSideValidations.validators.all()[kind](element, validators[kind]))) {
element.trigger('element:validate:fail', message).data('valid', false);
valid = false;
break;
}
}
if (valid) { element.data('valid', null); element.trigger('element:validate:pass'); }
}
element.trigger('element:validate:after');
return element.data('valid') === false ? false : true;
}
// Main hook
// If new forms are dynamically introduced into the DOM the .validate() method
// must be invoked on that form
$(function() { $('form[data-validate]').validate(); })
})(jQuery);
var clientSideValidations = {
validators: {
all: function() { return jQuery.extend({}, clientSideValidations.validators.local, clientSideValidations.validators.remote) },
local: {
presence: function(element, options) {
if (/^\s*$/.test(element.val() || "")) {
return options.message;
}
},
acceptance: function(element, options) {
switch (element.attr('type')) {
case 'checkbox':
if (!element.attr('checked')) {
return options.message;
}
break;
case 'text':
if (element.val() != (options.accept || '1')) {
return options.message;
}
break;
}
},
format: function(element, options) {
if ((message = this.presence(element, options)) && options.allow_blank == true) {
return;
} else if (message) {
return message;
} else {
if (options['with'] && !options['with'].test(element.val())) {
return options.message;
} else if (options['without'] && options['without'].test(element.val())) {
return options.message;
}
}
},
numericality: function(element, options) {
if (!/^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d*)?$/.test(element.val())) {
return options.messages.numericality;
}
if (options.only_integer && !/^\d+$/.test(element.val())) {
return options.messages.only_integer;
}
var CHECKS = { greater_than: '>', greater_than_or_equal_to: '>=',
equal_to: '==', less_than: '<', less_than_or_equal_to: '<=' }
for (var check in CHECKS) {
if (options[check] != undefined && !(new Function("return " + element.val() + CHECKS[check] + options[check])())) {
return options.messages[check];
}
}
if (options.odd && !(parseInt(element.val()) % 2)) {
return options.messages.odd;
}
if (options.even && (parseInt(element.val()) % 2)) {
return options.messages.even;
}
},
length: function(element, options) {
var blankOptions = {};
if (options.is) {
blankOptions.message = options.messages.is;
} else if (options.minimum) {
blankOptions.message = options.messages.minimum;
}
if ((message = this.presence(element, blankOptions)) && options.allow_blank == true) {
return;
} else if (message) {
return message;
} else {
var CHECKS = { is: '==', minimum: '>=', maximum: '<=' }
var tokenizer = options.js_tokenizer || "split('')";
var tokenized_length = new Function("element", "return (element.val()." + tokenizer + " || '').length;")(element);
for (var check in CHECKS) {
if (options[check] && !(new Function("return " + tokenized_length + CHECKS[check] + options[check])())) {
return options.messages[check];
}
}
}
},
exclusion: function(element, options) {
if ((message = this.presence(element, options)) && options.allow_blank == true) {
return;
} else if (message) {
return message;
} else {
if (options['in']) {
for (var i = 0; i < options['in'].length; i++) {
if (options['in'][i] == element.val()) {
return options.message;
}
}
} else if (options['range']) {
var lower = options['range'][0],
upper = options['range'][1];
if (element.val() >= lower && element.val() <= upper) {
return options.message;
}
}
}
},
inclusion: function(element, options) {
if ((message = this.presence(element, options)) && options.allow_blank == true) {
return;
} else if (message) {
return message;
} else {
if (options['in']) {
for (var i = 0; i < options['in'].length; i++) {
if (options['in'][i] == element.val()) {
return;
}
}
return options.message;
} else if (options['range']) {
var lower = options['range'][0],
upper = options['range'][1];
if (element.val() >= lower && element.val() <= upper) {
return;
} else {
return options.message;
}
}
}
},
confirmation: function(element, options) {
if (element.val() != jQuery('#' + element.attr('id') + '_confirmation').val()) {
return options.message;
}
}
},
remote: {
uniqueness: function(element, options) {
var data = {};
data['case_sensitive'] = !!options.case_sensitive;
if (options.id) {
data['id'] = options.id;
}
if (options.scope) {
data.scope = {}
for (key in options.scope) {
var scoped_element = jQuery('[name="' + element.attr('name').replace(/\[\w+]$/, '[' + key + ']' + '"]'));
if (scoped_element[0] && scoped_element.val() != options.scope[key]) {
data.scope[key] = scoped_element.val();
scoped_element.unbind('change.' + element.id).bind('change.' + element.id, function() { element.trigger('change'); element.trigger('focusout'); });
} else {
data.scope[key] = options.scope[key];
}
}
}
// Kind of a hack but this will isolate the resource name and attribute.
// e.g. user[records_attributes][0][title] => records[title]
// e.g. user[record_attributes][title] => record[title]
// Server side handles classifying the resource properly
if (/_attributes]/.test(element.attr('name'))) {
var name = element.attr('name').match(/\[\w+_attributes]/g).pop().match(/\[(\w+)_attributes]/).pop();
name += /(\[\w+])$/.exec(element.attr('name'))[1];
} else {
var name = element.attr('name');
}
// Override the name if a nested module class is passed
if (options['class']) {
name = options['class'] + '[' + name.split('[')[1]
}
data[name] = element.val();
if (jQuery.ajax({
url: '/validators/uniqueness',
data: data,
async: false
}).status == 200) {
return options.message;
}
}
}
},
formBuilders: {
'ActionView::Helpers::FormBuilder': {
add: function(element, settings, message) {
if (element.data('valid') !== false && jQuery('label.message[for="' + element.attr('id') + '"]')[0] == undefined) {
var inputErrorField = jQuery(settings.input_tag),
labelErrorField = jQuery(settings.label_tag),
label = jQuery('label[for="' + element.attr('id') + '"]:not(.message)');
if (element.attr('autofocus')) { element.attr('autofocus', false) };
element.before(inputErrorField);
inputErrorField.find('span#input_tag').replaceWith(element);
inputErrorField.find('label.message').attr('for', element.attr('id'));
labelErrorField.find('label.message').attr('for', element.attr('id'));
label.replaceWith(labelErrorField);
labelErrorField.find('label#label_tag').replaceWith(label);
}
jQuery('label.message[for="' + element.attr('id') + '"]').text(message);
},
remove: function(element, settings) {
var errorFieldClass = jQuery(settings.input_tag).attr('class'),
inputErrorField = element.closest('.' + errorFieldClass),
label = jQuery('label[for="' + element.attr('id') + '"]:not(.message)'),
labelErrorField = label.closest('.' + errorFieldClass);
if (inputErrorField[0]) {
inputErrorField.find('#' + element.attr('id')).detach();
inputErrorField.replaceWith(element);
label.detach();
labelErrorField.replaceWith(label);
}
}
},
'SimpleForm::FormBuilder': {
add: function(element, settings, message) {
if (element.data('valid') !== false) {
var wrapper = element.closest(settings.wrapper_tag);
wrapper.addClass(settings.wrapper_error_class);
var errorElement = $('<' + settings.error_tag + ' class="' + settings.error_class + '">' + message + '</' + settings.error_tag + '>');
wrapper.append(errorElement);
} else {
element.parent().find(settings.error_tag + '.' + settings.error_class).text(message);
}
},
remove: function(element, settings) {
var wrapper = element.closest(settings.wrapper_tag + '.' + settings.wrapper_error_class);
wrapper.removeClass(settings.wrapper_error_class);
var errorElement = wrapper.find(settings.error_tag + '.' + settings.error_class);
errorElement.remove();
}
},
'Formtastic::FormBuilder': {
add: function(element, settings, message) {
if (element.data('valid') !== false) {
var wrapper = element.closest('li');
wrapper.addClass('error');
var errorElement = $('<p class="' + settings.inline_error_class + '">' + message + '</p>');
wrapper.append(errorElement);
} else {
element.parent().find('p.' + settings.inline_error_class).text(message);
}
},
remove: function(element, settings) {
var wrapper = element.closest('li.error');
wrapper.removeClass('error');
var errorElement = wrapper.find('p.' + settings.inline_error_class);
errorElement.remove();
}
},
'NestedForm::Builder': {
add: function(element, settings, message) {
clientSideValidations.formBuilders['ActionView::Helpers::FormBuilder'].add(element, settings, message);
},
remove: function(element, settings, message) {
clientSideValidations.formBuilders['ActionView::Helpers::FormBuilder'].remove(element, settings, message);
}
}
},
callbacks: {
element: {
after: function(element, eventData) { },
before: function(element, eventData) { },
fail: function(element, message, addError, eventData) { addError() },
pass: function(element, removeError, eventData) { removeError() }
},
form: {
after: function(form, eventData) { },
before: function(form, eventData) { },
fail: function(form, eventData) { },
pass: function(form, eventData) { }
}
}
};

File diff suppressed because it is too large Load Diff

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