social_stream 2.1.1 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +5 -2
- data/LICENSE +1 -1
- data/base/Rakefile +3 -17
- data/base/app/assets/images/flags/de.png +0 -0
- data/base/app/assets/images/flags/fr.png +0 -0
- data/base/app/assets/images/flags/hu.png +0 -0
- data/base/app/assets/images/flags/nl.png +0 -0
- data/base/app/assets/javascripts/social_stream/actor.js +34 -0
- data/base/app/assets/javascripts/social_stream/contact.js +40 -5
- data/base/app/assets/javascripts/social_stream/flash.js +6 -1
- data/base/app/assets/javascripts/social_stream/group.js +6 -5
- data/base/app/assets/javascripts/social_stream/relation_customs.js +0 -1
- data/base/app/assets/stylesheets/social_stream/base/adjust/layout/_adjust.css.sass +3 -0
- data/base/app/assets/stylesheets/social_stream/base/buttons/_buttons.scss.sass +0 -2
- data/base/app/assets/stylesheets/social_stream/base/contacts/_contacts.css.sass +1 -11
- data/base/app/assets/stylesheets/social_stream/base/contacts/layouts/_contacts.css.sass +1 -29
- data/base/app/assets/stylesheets/social_stream/base/layouts/_header.css.sass +0 -1
- data/base/app/assets/stylesheets/social_stream/base/mixins/_buttons.css.sass +5 -0
- data/base/app/assets/stylesheets/social_stream/base/mixins/_layout.css.sass +36 -7
- data/base/app/controllers/actors_controller.rb +25 -0
- data/base/app/controllers/contacts_controller.rb +38 -8
- data/base/app/controllers/groups_controller.rb +2 -21
- data/base/app/controllers/profiles_controller.rb +4 -0
- data/base/app/controllers/settings_controller.rb +12 -2
- data/base/app/controllers/users_controller.rb +0 -8
- data/base/app/helpers/contacts_helper.rb +8 -0
- data/base/app/models/activity.rb +19 -6
- data/base/app/models/actor.rb +49 -11
- data/base/app/models/contact.rb +12 -5
- data/base/app/models/group.rb +13 -11
- data/base/app/models/permission.rb +30 -0
- data/base/app/models/relation.rb +17 -7
- data/base/app/models/relation/custom.rb +2 -40
- data/base/app/models/relation/follow.rb +8 -2
- data/base/app/models/relation/owner.rb +10 -0
- data/base/app/models/relation/single.rb +7 -4
- data/base/app/models/site.rb +1 -1
- data/base/app/models/tie.rb +8 -4
- data/base/app/views/contacts/_add_button.html.erb +9 -0
- data/base/app/views/contacts/_button.html.erb +3 -3
- data/base/app/views/contacts/_button_multiple.html.erb +4 -0
- data/base/app/views/contacts/_button_simple.html.erb +17 -0
- data/base/app/views/contacts/_new_modal.html.erb +22 -0
- data/base/app/views/contacts/destroy.js.erb +5 -4
- data/base/app/views/contacts/update.js.erb +5 -4
- data/base/app/views/devise/confirmations/new.html.erb +14 -9
- data/base/app/views/frontpage/_presentation.html.erb +2 -2
- data/base/app/views/groups/_form.html.erb +20 -20
- data/base/app/views/groups/show.html.erb +5 -5
- data/base/app/views/layouts/_header_dropdown_menu.html.erb +3 -5
- data/base/app/views/layouts/_header_signed_in.erb +3 -3
- data/base/app/views/layouts/_logo_in.html.erb +1 -1
- data/base/app/views/layouts/application.html.erb +2 -0
- data/base/app/views/permissions/_list.html.erb +1 -1
- data/base/app/views/profiles/_avatar.html.erb +1 -5
- data/base/app/views/profiles/_avatar_edit.html.erb +5 -0
- data/base/app/views/profiles/_comunication-info.html.erb +2 -36
- data/base/app/views/profiles/_comunication-info_edit.html.erb +27 -0
- data/base/app/views/profiles/_edit_icon.html.erb +0 -1
- data/base/app/views/profiles/_experience.html.erb +1 -6
- data/base/app/views/profiles/_experience_edit.html.erb +5 -0
- data/base/app/views/profiles/_personal.html.erb +1 -39
- data/base/app/views/profiles/_personal_edit.html.erb +31 -0
- data/base/app/views/profiles/_profile_edit.html.erb +7 -0
- data/base/app/views/profiles/_tags.html.erb +1 -4
- data/base/app/views/profiles/_tags_edit.html.erb +4 -0
- data/base/app/views/profiles/edit.html.erb +24 -0
- data/base/app/views/settings/_notifications.html.erb +12 -4
- data/base/app/views/users/show.html.erb +5 -5
- data/base/config/locales/de.yml +654 -0
- data/base/config/locales/en.yml +20 -9
- data/base/config/locales/es.yml +151 -140
- data/base/config/locales/fr.yml +656 -0
- data/base/config/locales/hu.yml +653 -0
- data/base/config/locales/nl.yml +656 -0
- data/base/config/locales/pt.yml +250 -239
- data/base/config/locales/rails.de.yml +203 -0
- data/base/config/locales/rails.fr.yml +222 -0
- data/base/config/locales/rails.hu.yml +199 -0
- data/base/config/locales/rails.nl.yml +199 -0
- data/base/config/locales/zh.yml +214 -202
- data/base/config/routes.rb +22 -30
- data/base/db/migrate/20130708152633_set_group_owners.rb +19 -0
- data/base/db/migrate/20130723133530_actor_notification_settings.rb +10 -0
- data/base/lib/generators/social_stream/base/templates/initializer.rb +13 -7
- data/base/lib/rails/social_stream.rb +2 -0
- data/base/lib/social_stream/base.rb +27 -7
- data/base/lib/social_stream/base/ability.rb +3 -5
- data/base/lib/social_stream/base/autoload.rb +1 -0
- data/base/lib/social_stream/base/version.rb +1 -1
- data/base/lib/social_stream/controllers/authorship.rb +18 -0
- data/base/lib/social_stream/controllers/helpers.rb +18 -4
- data/base/lib/social_stream/controllers/objects.rb +2 -8
- data/base/lib/social_stream/controllers/subjects.rb +9 -2
- data/base/lib/social_stream/models/supertype.rb +2 -0
- data/base/lib/social_stream/population/activity_object.rb +6 -8
- data/base/lib/social_stream/routing/mapper.rb +52 -0
- data/base/social_stream-base.gemspec +4 -7
- data/base/spec/controllers/groups_controller_spec.rb +19 -8
- data/base/spec/controllers/posts_controller_spec.rb +114 -147
- data/base/spec/models/post_authorization_spec.rb +190 -204
- data/base/spec/models/post_spec.rb +17 -63
- data/base/vendor/assets/javascripts/bootstrap-multiselect.js +434 -127
- data/documents/Rakefile +1 -6
- data/documents/config/locales/de.yml +93 -0
- data/documents/config/locales/es.yml +2 -2
- data/documents/config/locales/fr.yml +93 -0
- data/documents/config/locales/hu.yml +93 -0
- data/documents/config/locales/nl.yml +93 -0
- data/documents/config/locales/zh.yml +4 -4
- data/documents/config/routes.rb +7 -9
- data/documents/lib/social_stream/documents/version.rb +1 -1
- data/documents/social_stream-documents.gemspec +4 -4
- data/events/Rakefile +1 -6
- data/events/config/locales/es.yml +3 -3
- data/events/config/locales/zh.yml +3 -3
- data/events/lib/social_stream/events/models/actor.rb +2 -1
- data/events/lib/social_stream/events/version.rb +1 -1
- data/events/social_stream-events.gemspec +1 -1
- data/lib/generators/social_stream/install_generator.rb +1 -0
- data/lib/social_stream.rb +1 -0
- data/lib/social_stream/version.rb +1 -1
- data/lib/tasks/i18n.rake +22 -2
- data/linkser/Rakefile +1 -6
- data/linkser/config/locales/de.yml +17 -0
- data/linkser/config/locales/es.yml +2 -2
- data/linkser/config/locales/fr.yml +17 -0
- data/linkser/config/locales/hu.yml +17 -0
- data/linkser/config/locales/nl.yml +17 -0
- data/linkser/config/locales/zh.yml +2 -2
- data/linkser/lib/social_stream/linkser/version.rb +1 -1
- data/linkser/social_stream-linkser.gemspec +1 -1
- data/oauth2_server/Rakefile +1 -6
- data/oauth2_server/app/assets/images/step_1.png +0 -0
- data/oauth2_server/app/assets/images/step_2.png +0 -0
- data/oauth2_server/app/assets/images/step_3.png +0 -0
- data/oauth2_server/app/assets/javascripts/social_stream/site_client.js +27 -0
- data/oauth2_server/app/assets/stylesheets/social_stream/oauth2_server/applications/layout/_applications-oauth2server.css.sass +0 -5
- data/oauth2_server/app/assets/stylesheets/social_stream/oauth2_server/create/layout/_create-oauth2server.css.sass +3 -5
- data/oauth2_server/app/assets/stylesheets/social_stream/oauth2_server/show/layout/_show-oauth2server.css.sass +8 -6
- data/oauth2_server/app/controllers/site/clients_controller.rb +17 -41
- data/oauth2_server/app/decorators/social_stream/base/relation_decorator.rb +2 -0
- data/oauth2_server/app/decorators/social_stream/base/user_decorator.rb +1 -20
- data/oauth2_server/app/models/relation/manager.rb +1 -10
- data/oauth2_server/app/models/site/client.rb +4 -2
- data/oauth2_server/app/views/site/clients/_destroy.html.erb +11 -0
- data/oauth2_server/app/views/site/clients/_edit.html.erb +9 -2
- data/oauth2_server/app/views/site/clients/_edit_step_2.html.erb +6 -6
- data/oauth2_server/app/views/site/clients/_edit_step_3.html.erb +8 -6
- data/oauth2_server/app/views/site/clients/_form.html.erb +11 -11
- data/oauth2_server/app/views/site/clients/_list.html.erb +23 -11
- data/oauth2_server/app/views/site/clients/edit.html.erb +1 -1
- data/oauth2_server/app/views/site/clients/index.html.erb +9 -40
- data/oauth2_server/app/views/site/clients/show.html.erb +66 -68
- data/oauth2_server/config/locales/en.yml +19 -0
- data/oauth2_server/config/locales/es.yml +23 -4
- data/oauth2_server/config/locales/zh.yml +32 -13
- data/oauth2_server/config/routes.rb +3 -1
- data/oauth2_server/lib/social_stream/oauth2_server.rb +4 -1
- data/oauth2_server/lib/social_stream/oauth2_server/ability.rb +1 -1
- data/oauth2_server/lib/social_stream/oauth2_server/models/user.rb +18 -0
- data/oauth2_server/lib/social_stream/oauth2_server/version.rb +1 -1
- data/oauth2_server/social_stream-oauth2_server.gemspec +1 -1
- data/oauth2_server/spec/controllers/site_clients_controller_authorization_spec.rb +7 -0
- data/ostatus/Rakefile +1 -6
- data/ostatus/config/locales/zh.yml +8 -8
- data/ostatus/lib/social_stream-ostatus.rb +3 -0
- data/ostatus/lib/social_stream/ostatus/version.rb +1 -1
- data/ostatus/social_stream-ostatus.gemspec +1 -1
- data/places/config/locales/es.yml +51 -51
- data/presence/config/locales/es.yml +48 -48
- data/presence/config/locales/zh.yml +48 -48
- data/presence/lib/social_stream/presence/version.rb +1 -1
- data/presence/social_stream-presence.gemspec +1 -1
- data/social_stream.gemspec +8 -7
- metadata +60 -29
- data/base/app/assets/javascripts/social_stream/follow.js +0 -28
- data/base/app/controllers/followers_controller.rb +0 -34
- data/base/app/helpers/followers_helper.rb +0 -5
- data/base/app/views/contacts/_link_follow.html.erb +0 -16
- data/base/app/views/followers/destroy.js.erb +0 -1
- data/base/app/views/followers/index.html.erb +0 -30
- data/base/app/views/followers/update.js.erb +0 -3
- data/base/app/views/frontpage/_characteristics.html.erb +0 -23
- data/base/lib/social_stream/routing/constraints/custom.rb +0 -11
- data/base/lib/social_stream/routing/constraints/follow.rb +0 -11
- data/base/spec/controllers/followers_controller_spec.rb +0 -37
@@ -31,78 +31,32 @@ describe Post do
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
|
35
|
-
before :all do
|
36
|
-
@ss_relation_model = SocialStream.relation_model
|
37
|
-
end
|
38
|
-
|
39
|
-
after :all do
|
40
|
-
SocialStream.relation_model = @ss_relation_model
|
41
|
-
end
|
42
|
-
|
43
|
-
context "in follow relation model" do
|
44
|
-
before do
|
45
|
-
SocialStream.relation_model = :follow
|
46
|
-
end
|
47
|
-
|
48
|
-
it "should allow create to follower" do
|
49
|
-
tie = Factory(:follow)
|
50
|
-
|
51
|
-
post = Post.new :text => "testing",
|
52
|
-
:author_id => tie.receiver.id,
|
53
|
-
:owner_id => tie.sender.id,
|
54
|
-
:user_author_id => tie.receiver.id
|
55
|
-
|
56
|
-
ability = Ability.new(tie.receiver_subject)
|
57
|
-
|
58
|
-
ability.should be_able_to(:create, post)
|
59
|
-
end
|
34
|
+
describe 'with default SocialStream.custom_relations' do
|
60
35
|
|
61
|
-
|
62
|
-
|
36
|
+
it "should allow create to friend" do
|
37
|
+
tie = Factory(:friend)
|
63
38
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
39
|
+
post = Post.new :text => "testing",
|
40
|
+
:author_id => tie.receiver.id,
|
41
|
+
:owner_id => tie.sender.id,
|
42
|
+
:user_author_id => tie.receiver.id
|
68
43
|
|
69
|
-
|
70
|
-
|
71
|
-
post.post_activity.relations.should include(Relation::Public.instance)
|
72
|
-
end
|
44
|
+
ability = Ability.new(tie.receiver_subject)
|
73
45
|
|
46
|
+
ability.should be_able_to(:create, post)
|
74
47
|
end
|
75
48
|
|
76
|
-
|
77
|
-
|
78
|
-
SocialStream.relation_model = :custom
|
79
|
-
end
|
80
|
-
|
81
|
-
it "should allow create to friend" do
|
82
|
-
tie = Factory(:friend)
|
49
|
+
it "should fill relation" do
|
50
|
+
tie = Factory(:friend)
|
83
51
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
52
|
+
post = Post.new :text => "testing",
|
53
|
+
:author_id => tie.receiver.id,
|
54
|
+
:owner_id => tie.sender.id,
|
55
|
+
:user_author_id => tie.receiver.id
|
88
56
|
|
89
|
-
|
57
|
+
post.save!
|
90
58
|
|
91
|
-
|
92
|
-
end
|
93
|
-
|
94
|
-
it "should fill relation" do
|
95
|
-
tie = Factory(:friend)
|
96
|
-
|
97
|
-
post = Post.new :text => "testing",
|
98
|
-
:author_id => tie.receiver.id,
|
99
|
-
:owner_id => tie.sender.id,
|
100
|
-
:user_author_id => tie.receiver.id
|
101
|
-
|
102
|
-
post.save!
|
103
|
-
|
104
|
-
post.post_activity.relations.should include(tie.relation)
|
105
|
-
end
|
59
|
+
post.post_activity.relations.should include(tie.relation)
|
106
60
|
end
|
107
61
|
|
108
62
|
describe "a new post" do
|
@@ -16,216 +16,523 @@
|
|
16
16
|
* See the License for the specific language governing permissions and
|
17
17
|
* limitations under the License.
|
18
18
|
*/
|
19
|
-
!function
|
19
|
+
!function($) {"use strict";// jshint ;_;
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
if(typeof ko != 'undefined' && ko.bindingHandlers && !ko.bindingHandlers.multiselect){
|
21
|
+
if ( typeof ko != 'undefined' && ko.bindingHandlers && !ko.bindingHandlers.multiselect) {
|
24
22
|
ko.bindingHandlers.multiselect = {
|
25
|
-
init: function
|
23
|
+
init : function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
|
24
|
+
},
|
25
|
+
update : function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
|
26
26
|
var ms = $(element).data('multiselect');
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
// We dont want to refresh the multiselect since it would delete / recreate all items
|
35
|
-
$(element).data('blockRefresh', true);
|
36
|
-
|
37
|
-
// Force the binding to be updated by triggering the change event on the select element
|
38
|
-
$(element).trigger('change');
|
39
|
-
|
40
|
-
// Call any defined change handler
|
41
|
-
return prev(option, checked);
|
27
|
+
if (!ms) {
|
28
|
+
$(element).multiselect(ko.utils.unwrapObservable(valueAccessor()));
|
29
|
+
}
|
30
|
+
else
|
31
|
+
if (allBindingsAccessor().options && allBindingsAccessor().options().length !== ms.originalOptions.length) {
|
32
|
+
ms.updateOriginalOptions();
|
33
|
+
$(element).multiselect('rebuild');
|
42
34
|
}
|
43
|
-
},
|
44
|
-
update: function (element) {
|
45
|
-
var blockRefresh = $(element).data('blockRefresh') || false;
|
46
|
-
if (!blockRefresh) { $(element).multiselect("rebuild"); }
|
47
|
-
$.data(element, 'blockRefresh', false);
|
48
35
|
}
|
49
36
|
};
|
50
37
|
}
|
51
38
|
|
52
39
|
function Multiselect(select, options) {
|
53
|
-
|
54
|
-
this.options = this.getOptions(options);
|
55
|
-
this.select = $(select);
|
56
|
-
|
57
|
-
// Manually add the multiple attribute, if its not already set.
|
58
|
-
if (!this.select.attr('multiple')) {
|
59
|
-
this.select.attr('multiple', true);
|
60
|
-
}
|
61
|
-
|
62
|
-
this.container = $(this.options.buttonContainer)
|
63
|
-
.append('<button type="button" class="multiselect dropdown-toggle ' + this.options.buttonClass + '" data-toggle="dropdown">' + this.options.buttonText($('option:selected', select)) + '</button>')
|
64
|
-
.append('<ul class="dropdown-menu"></ul>');
|
65
40
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
41
|
+
this.options = this.getOptions(options);
|
42
|
+
this.$select = $(select);
|
43
|
+
this.originalOptions = this.$select.clone()[0].options;
|
44
|
+
//we have to clone to create a new reference
|
45
|
+
this.query = '';
|
46
|
+
this.searchTimeout = null;
|
47
|
+
|
48
|
+
this.options.multiple = this.$select.attr('multiple') == "multiple";
|
49
|
+
|
50
|
+
this.$container = $(this.options.buttonContainer).append('<button type="button" class="multiselect dropdown-toggle ' + this.options.buttonClass + '" data-toggle="dropdown">' + this.options.buttonText(this.getSelected(), this.$select) + '</button>')
|
51
|
+
.append('<ul class="multiselect-container dropdown-menu' + (this.options.dropRight ? ' pull-right' : '') + '"></ul>');
|
52
|
+
|
53
|
+
if (this.options.buttonWidth) {
|
54
|
+
$('button', this.$container).css({
|
55
|
+
'width' : this.options.buttonWidth
|
56
|
+
});
|
57
|
+
}
|
71
58
|
|
72
59
|
// Set max height of dropdown menu to activate auto scrollbar.
|
73
60
|
if (this.options.maxHeight) {
|
74
|
-
|
75
|
-
|
76
|
-
'
|
77
|
-
'overflow-
|
61
|
+
// TODO: Add a class for this option to move the css declarations.
|
62
|
+
$('.multiselect-container', this.$container).css({
|
63
|
+
'max-height' : this.options.maxHeight + 'px',
|
64
|
+
'overflow-y' : 'auto',
|
65
|
+
'overflow-x' : 'hidden'
|
78
66
|
});
|
79
67
|
}
|
80
68
|
|
81
|
-
|
69
|
+
// Enable filtering.
|
70
|
+
if (this.options.enableFiltering || this.options.enableCaseInsensitiveFiltering) {
|
71
|
+
this.buildFilter();
|
72
|
+
}
|
73
|
+
|
74
|
+
this.buildDropdown();
|
75
|
+
this.updateButtonText();
|
82
76
|
|
83
|
-
this.
|
84
|
-
.hide()
|
85
|
-
.after(this.container);
|
77
|
+
this.$select.hide().after(this.$container);
|
86
78
|
};
|
87
79
|
|
88
80
|
Multiselect.prototype = {
|
89
|
-
|
90
|
-
defaults: {
|
91
|
-
// Default text function will either print 'None selected' in case no
|
92
|
-
// or a list of the selected options up to a length of 3 selected options.
|
81
|
+
|
82
|
+
defaults : {
|
83
|
+
// Default text function will either print 'None selected' in case no
|
84
|
+
// option is selected, or a list of the selected options up to a length of 3 selected options.
|
93
85
|
// If more than 3 options are selected, the number of selected options is printed.
|
94
|
-
buttonText: function(options) {
|
86
|
+
buttonText : function(options, select) {
|
95
87
|
if (options.length == 0) {
|
96
|
-
return
|
88
|
+
return this.nonSelectedText + '<b class="caret"></b>';
|
97
89
|
}
|
98
|
-
else
|
99
|
-
|
90
|
+
else
|
91
|
+
if (options.length > 3) {
|
92
|
+
return options.length + ' ' + this.nSelectedText + ' <b class="caret"></b>';
|
100
93
|
}
|
101
94
|
else {
|
102
95
|
var selected = '';
|
103
96
|
options.each(function() {
|
104
|
-
|
97
|
+
var label = ($(this).attr('label') !== undefined) ? $(this).attr('label') : $(this).html();
|
98
|
+
|
99
|
+
selected += label + ', ';
|
105
100
|
});
|
106
|
-
return selected.substr(0, selected.length -2) + ' <b class="caret"></b>';
|
101
|
+
return selected.substr(0, selected.length - 2) + ' <b class="caret"></b>';
|
107
102
|
}
|
108
103
|
},
|
109
104
|
// Is triggered on change of the selected options.
|
110
|
-
onChange: function() {
|
105
|
+
onChange : function(option, checked) {
|
111
106
|
|
112
107
|
},
|
113
|
-
buttonClass: 'btn',
|
114
|
-
|
115
|
-
|
116
|
-
|
108
|
+
buttonClass : 'btn',
|
109
|
+
dropRight : false,
|
110
|
+
selectedClass : 'active',
|
111
|
+
buttonWidth : 'auto',
|
112
|
+
buttonContainer : '<div class="btn-group" />',
|
113
|
+
// Maximum height of the dropdown menu.
|
117
114
|
// If maximum height is exceeded a scrollbar will be displayed.
|
118
|
-
maxHeight:
|
115
|
+
maxHeight : false,
|
116
|
+
includeSelectAllOption : false,
|
117
|
+
selectAllText : ' Select all',
|
118
|
+
selectAllValue : 'multiselect-all',
|
119
|
+
enableFiltering : false,
|
120
|
+
enableCaseInsensitiveFiltering : false,
|
121
|
+
filterPlaceholder : 'Search',
|
122
|
+
// possible options: 'text', 'value', 'both'
|
123
|
+
filterBehavior : 'text',
|
124
|
+
preventInputChangeEvent: false,
|
125
|
+
nonSelectedText: 'None selected',
|
126
|
+
nSelectedText: 'selected'
|
119
127
|
},
|
120
|
-
|
121
|
-
isMobile: function() {
|
122
|
-
return navigator.userAgent.match(/Android|webOS|iPhone|iPad|iPod|BlackBerry/i);
|
123
|
-
},
|
124
128
|
|
125
|
-
constructor: Multiselect,
|
129
|
+
constructor : Multiselect,
|
126
130
|
|
127
|
-
|
131
|
+
// Will build an dropdown element for the given option.
|
132
|
+
createOptionValue : function(element) {
|
133
|
+
if ($(element).is(':selected')) {
|
134
|
+
$(element).attr('selected', 'selected').prop('selected', true);
|
135
|
+
}
|
128
136
|
|
129
|
-
//
|
130
|
-
$('
|
131
|
-
|
132
|
-
|
133
|
-
$(element).prop('selected', 'selected');
|
134
|
-
}
|
137
|
+
// Support the label attribute on options.
|
138
|
+
var label = $(element).attr('label') || $(element).html();
|
139
|
+
var value = $(element).val();
|
140
|
+
var inputType = this.options.multiple ? "checkbox" : "radio";
|
135
141
|
|
136
|
-
|
142
|
+
var $li = $('<li><a href="javascript:void(0);"><label class="' + inputType + '"><input type="' + inputType + '" /></label></a></li>');
|
137
143
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
144
|
+
var selected = $(element).prop('selected') || false;
|
145
|
+
var $checkbox = $('input', $li);
|
146
|
+
$checkbox.val(value);
|
147
|
+
|
148
|
+
if (value == this.options.selectAllValue) {
|
149
|
+
$checkbox.parent().parent().addClass('multiselect-all');
|
150
|
+
}
|
151
|
+
|
152
|
+
$('label', $li).append(" " + label);
|
153
|
+
|
154
|
+
$('.multiselect-container', this.$container).append($li);
|
155
|
+
|
156
|
+
if ($(element).is(':disabled')) {
|
157
|
+
$checkbox.attr('disabled', 'disabled').prop('disabled', true).parents('li').addClass('disabled');
|
158
|
+
}
|
159
|
+
|
160
|
+
$checkbox.prop('checked', selected);
|
161
|
+
|
162
|
+
if (selected && this.options.selectedClass) {
|
163
|
+
$checkbox.parents('li').addClass(this.options.selectedClass);
|
164
|
+
}
|
165
|
+
},
|
166
|
+
|
167
|
+
toggleActiveState : function(shouldBeActive) {
|
168
|
+
if (this.$select.attr('disabled') == undefined) {
|
169
|
+
$('button.multiselect.dropdown-toggle', this.$container).removeClass('disabled');
|
170
|
+
}
|
171
|
+
else {
|
172
|
+
$('button.multiselect.dropdown-toggle', this.$container).addClass('disabled');
|
173
|
+
}
|
174
|
+
},
|
175
|
+
|
176
|
+
// Build the dropdown and bind event handling.
|
177
|
+
buildDropdown : function() {
|
178
|
+
var alreadyHasSelectAll = this.$select[0][0] ? this.$select[0][0].value == this.options.selectAllValue : false;
|
179
|
+
|
180
|
+
// If options.includeSelectAllOption === true, add the include all
|
181
|
+
// checkbox.
|
182
|
+
if (this.options.includeSelectAllOption && this.options.multiple && !alreadyHasSelectAll) {
|
183
|
+
this.$select.prepend('<option value="' + this.options.selectAllValue + '">' + this.options.selectAllText + '</option>');
|
184
|
+
}
|
185
|
+
|
186
|
+
this.toggleActiveState();
|
187
|
+
|
188
|
+
this.$select.children().each($.proxy(function(index, element) {
|
189
|
+
// Support optgroups and options without a group simultaneously.
|
190
|
+
var tag = $(element).prop('tagName').toLowerCase();
|
191
|
+
if (tag == 'optgroup') {
|
192
|
+
var group = element;
|
193
|
+
var groupName = $(group).prop('label');
|
146
194
|
|
147
|
-
|
148
|
-
|
195
|
+
// Add a header for the group.
|
196
|
+
var $li = $('<li><label class="multiselect-group"></label></li>');
|
197
|
+
$('label', $li).text(groupName);
|
198
|
+
$('.multiselect-container', this.$container).append($li);
|
199
|
+
|
200
|
+
// Add the options of the group.
|
201
|
+
$('option', group).each($.proxy(function(index, element) {
|
202
|
+
this.createOptionValue(element);
|
203
|
+
}, this));
|
204
|
+
}
|
205
|
+
else
|
206
|
+
if (tag == 'option') {
|
207
|
+
this.createOptionValue(element);
|
208
|
+
}
|
209
|
+
else {
|
210
|
+
// Ignore illegal tags.
|
149
211
|
}
|
150
212
|
}, this));
|
151
213
|
|
152
214
|
// Bind the change event on the dropdown elements.
|
153
|
-
$('
|
215
|
+
$('.multiselect-container li input', this.$container).on('change', $.proxy(function(event) {
|
154
216
|
var checked = $(event.target).prop('checked') || false;
|
217
|
+
var isSelectAllOption = $(event.target).val() == this.options.selectAllValue;
|
218
|
+
|
219
|
+
// Apply or unapply the configured selected class.
|
220
|
+
if (this.options.selectedClass) {
|
221
|
+
if (checked) {
|
222
|
+
$(event.target).parents('li').addClass(this.options.selectedClass);
|
223
|
+
}
|
224
|
+
else {
|
225
|
+
$(event.target).parents('li').removeClass(this.options.selectedClass);
|
226
|
+
}
|
227
|
+
}
|
228
|
+
|
229
|
+
var $option = $('option', this.$select).filter(function() {
|
230
|
+
return $(this).val() == $(event.target).val();
|
231
|
+
});
|
232
|
+
|
233
|
+
var $optionsNotThis = $('option', this.$select).not($option);
|
234
|
+
var $checkboxesNotThis = $('input', this.$container).not($(event.target));
|
235
|
+
|
236
|
+
// Toggle all options if the select all option was changed.
|
237
|
+
if (isSelectAllOption) {
|
238
|
+
$checkboxesNotThis.filter(function() {
|
239
|
+
return $(this).is(':checked') != checked;
|
240
|
+
}).trigger('click');
|
241
|
+
}
|
155
242
|
|
156
243
|
if (checked) {
|
157
|
-
$
|
244
|
+
$option.prop('selected', true);
|
245
|
+
|
246
|
+
if (this.options.multiple) {
|
247
|
+
$option.attr('selected', 'selected');
|
248
|
+
}
|
249
|
+
else {
|
250
|
+
if (this.options.selectedClass) {
|
251
|
+
$($checkboxesNotThis).parents('li').removeClass(this.options.selectedClass);
|
252
|
+
}
|
253
|
+
|
254
|
+
$($checkboxesNotThis).prop('checked', false);
|
255
|
+
|
256
|
+
$optionsNotThis.removeAttr('selected').prop('selected', false);
|
257
|
+
|
258
|
+
// It's a single selection, so close.
|
259
|
+
$(this.$container).find(".multiselect.dropdown-toggle").click();
|
260
|
+
}
|
261
|
+
|
262
|
+
if (this.options.selectedClass == "active") {
|
263
|
+
$optionsNotThis.parents("a").css("outline", "");
|
264
|
+
}
|
265
|
+
|
158
266
|
}
|
159
267
|
else {
|
160
|
-
$
|
268
|
+
$option.removeAttr('selected').prop('selected', false);
|
161
269
|
}
|
162
270
|
|
163
|
-
|
271
|
+
this.updateButtonText();
|
164
272
|
|
165
|
-
|
166
|
-
|
167
|
-
|
273
|
+
this.options.onChange($option, checked);
|
274
|
+
|
275
|
+
this.$select.change();
|
276
|
+
|
277
|
+
if(this.options.preventInputChangeEvent) {
|
278
|
+
return false;
|
279
|
+
}
|
280
|
+
}, this));
|
281
|
+
|
282
|
+
$('.multiselect-container li a', this.$container).on('touchstart click', function(event) {
|
283
|
+
event.stopPropagation();
|
284
|
+
$(event.target).blur();
|
285
|
+
});
|
286
|
+
|
287
|
+
// Keyboard support.
|
288
|
+
this.$container.on('keydown', $.proxy(function(event) {
|
289
|
+
if ($('input[type="text"]', this.$container).is(':focus'))
|
290
|
+
return;
|
291
|
+
if ((event.keyCode == 9 || event.keyCode == 27) && this.$container.hasClass('open')) {
|
292
|
+
// Close on tab or escape.
|
293
|
+
$(this.$container).find(".multiselect.dropdown-toggle").click();
|
168
294
|
}
|
169
295
|
else {
|
170
|
-
|
296
|
+
var $items = $(this.$container).find("li:not(.divider):visible a");
|
297
|
+
|
298
|
+
if (!$items.length) {
|
299
|
+
return;
|
300
|
+
}
|
301
|
+
|
302
|
+
var index = $items.index($items.filter(':focus'));
|
303
|
+
|
304
|
+
// Navigation up.
|
305
|
+
if (event.keyCode == 38 && index > 0) {
|
306
|
+
index--;
|
307
|
+
}
|
308
|
+
// Navigate down.
|
309
|
+
else
|
310
|
+
if (event.keyCode == 40 && index < $items.length - 1) {
|
311
|
+
index++;
|
312
|
+
}
|
313
|
+
else
|
314
|
+
if (!~index) {
|
315
|
+
index = 0;
|
316
|
+
}
|
317
|
+
|
318
|
+
var $current = $items.eq(index);
|
319
|
+
$current.focus();
|
320
|
+
|
321
|
+
// Override style for items in li:active.
|
322
|
+
if (this.options.selectedClass == "active") {
|
323
|
+
$current.css("outline", "thin dotted #333").css("outline", "5px auto -webkit-focus-ring-color");
|
324
|
+
|
325
|
+
$items.not($current).css("outline", "");
|
326
|
+
}
|
327
|
+
|
328
|
+
if (event.keyCode == 32 || event.keyCode == 13) {
|
329
|
+
var $checkbox = $current.find('input');
|
330
|
+
|
331
|
+
$checkbox.prop("checked", !$checkbox.prop("checked"));
|
332
|
+
$checkbox.change();
|
333
|
+
}
|
334
|
+
|
335
|
+
event.stopPropagation();
|
336
|
+
event.preventDefault();
|
171
337
|
}
|
172
|
-
|
173
|
-
var options = $('option:selected', this.select);
|
174
|
-
$('button', this.container).html(this.options.buttonText(options));
|
175
|
-
|
176
|
-
this.options.onChange(option, checked);
|
177
338
|
}, this));
|
339
|
+
},
|
340
|
+
|
341
|
+
// Build and bind filter.
|
342
|
+
buildFilter: function() {
|
343
|
+
$('.multiselect-container', this.$container).prepend('<div class="input-prepend"><span class="add-on"><i class="icon-search"></i></span><input class="multiselect-search" type="text" placeholder="' + this.options.filterPlaceholder + '"></div>');
|
178
344
|
|
179
|
-
$('
|
345
|
+
$('.multiselect-search', this.$container).val(this.query).on('click', function(event) {
|
180
346
|
event.stopPropagation();
|
181
|
-
})
|
347
|
+
}).on('keydown', $.proxy(function(event) {
|
348
|
+
// This is useful to catch "keydown" events after the browser has
|
349
|
+
// updated the control.
|
350
|
+
clearTimeout(this.searchTimeout);
|
351
|
+
|
352
|
+
this.searchTimeout = this.asyncFunction($.proxy(function() {
|
353
|
+
|
354
|
+
if (this.query != event.target.value) {
|
355
|
+
this.query = event.target.value;
|
356
|
+
|
357
|
+
$.each($('.multiselect-container li', this.$container), $.proxy(function(index, element) {
|
358
|
+
var value = $('input', element).val();
|
359
|
+
if (value != this.options.selectAllValue) {
|
360
|
+
var text = $('label', element).text();
|
361
|
+
var value = $('input', element).val();
|
362
|
+
if (value && text && value != this.options.selectAllValue) {
|
363
|
+
// by default lets assume that element is not
|
364
|
+
// interesting for this search
|
365
|
+
var showElement = false;
|
366
|
+
|
367
|
+
var filterCandidate = '';
|
368
|
+
if ((this.options.filterBehavior == 'text' || this.options.filterBehavior == 'both')) {
|
369
|
+
filterCandidate = text;
|
370
|
+
}
|
371
|
+
if ((this.options.filterBehavior == 'value' || this.options.filterBehavior == 'both')) {
|
372
|
+
filterCandidate = value;
|
373
|
+
}
|
374
|
+
|
375
|
+
if (this.options.enableCaseInsensitiveFiltering && filterCandidate.toLowerCase().indexOf(this.query.toLowerCase()) > -1) {
|
376
|
+
showElement = true;
|
377
|
+
}
|
378
|
+
else if (filterCandidate.indexOf(this.query) > -1) {
|
379
|
+
showElement = true;
|
380
|
+
}
|
381
|
+
|
382
|
+
if (showElement) {
|
383
|
+
$(element).show();
|
384
|
+
}
|
385
|
+
else {
|
386
|
+
$(element).hide();
|
387
|
+
}
|
388
|
+
}
|
389
|
+
}
|
390
|
+
}, this));
|
391
|
+
}
|
392
|
+
}, this), 300, this);
|
393
|
+
}, this));
|
182
394
|
},
|
183
395
|
|
184
396
|
// Destroy - unbind - the plugin.
|
185
|
-
destroy: function() {
|
186
|
-
this
|
187
|
-
this
|
397
|
+
destroy : function() {
|
398
|
+
this.$container.remove();
|
399
|
+
this.$select.show();
|
188
400
|
},
|
189
401
|
|
190
402
|
// Refreshs the checked options based on the current state of the select.
|
191
|
-
refresh: function() {
|
192
|
-
$('option', this
|
403
|
+
refresh : function() {
|
404
|
+
$('option', this.$select).each($.proxy(function(index, element) {
|
405
|
+
var $input = $('.multiselect-container li input', this.$container).filter(function() {
|
406
|
+
return $(this).val() == $(element).val();
|
407
|
+
});
|
408
|
+
|
193
409
|
if ($(element).is(':selected')) {
|
194
|
-
$
|
195
|
-
|
410
|
+
$input.prop('checked', true);
|
411
|
+
|
412
|
+
if (this.options.selectedClass) {
|
413
|
+
$input.parents('li').addClass(this.options.selectedClass);
|
414
|
+
}
|
415
|
+
}
|
416
|
+
else {
|
417
|
+
$input.prop('checked', false);
|
418
|
+
|
419
|
+
if (this.options.selectedClass) {
|
420
|
+
$input.parents('li').removeClass(this.options.selectedClass);
|
421
|
+
}
|
422
|
+
}
|
423
|
+
|
424
|
+
if ($(element).is(":disabled")) {
|
425
|
+
$input.attr('disabled', 'disabled').prop('disabled', true).parents('li').addClass('disabled');
|
196
426
|
}
|
197
427
|
else {
|
198
|
-
$('
|
199
|
-
$('ul li input[value="' + $(element).val() + '"]', this.container).parents('li').removeClass('active');
|
428
|
+
$input.removeAttr('disabled').prop('disabled', false).parents('li').removeClass('disabled');
|
200
429
|
}
|
201
430
|
}, this));
|
202
431
|
|
203
|
-
|
432
|
+
this.updateButtonText();
|
433
|
+
},
|
434
|
+
|
435
|
+
// Select an option by its value.
|
436
|
+
select : function(value) {
|
437
|
+
var $option = $('option', this.$select).filter(function() {
|
438
|
+
return $(this).val() == value;
|
439
|
+
});
|
440
|
+
var $checkbox = $('.multiselect-container li input', this.$container).filter(function() {
|
441
|
+
return $(this).val() == value;
|
442
|
+
});
|
443
|
+
|
444
|
+
if (this.options.selectedClass) {
|
445
|
+
$checkbox.parents('li').addClass(this.options.selectedClass);
|
446
|
+
}
|
447
|
+
|
448
|
+
$checkbox.prop('checked', true);
|
449
|
+
|
450
|
+
$option.attr('selected', 'selected').prop('selected', true);
|
451
|
+
|
452
|
+
this.updateButtonText();
|
453
|
+
this.options.onChange($option, true);
|
454
|
+
},
|
455
|
+
|
456
|
+
// Deselect an option by its value.
|
457
|
+
deselect : function(value) {
|
458
|
+
var $option = $('option', this.$select).filter(function() {
|
459
|
+
return $(this).val() == value;
|
460
|
+
});
|
461
|
+
var $checkbox = $('.multiselect-container li input', this.$container).filter(function() {
|
462
|
+
return $(this).val() == value;
|
463
|
+
});
|
464
|
+
|
465
|
+
if (this.options.selectedClass) {
|
466
|
+
$checkbox.parents('li').removeClass(this.options.selectedClass);
|
467
|
+
}
|
468
|
+
|
469
|
+
$checkbox.prop('checked', false);
|
470
|
+
|
471
|
+
$option.removeAttr('selected').prop('selected', false);
|
472
|
+
|
473
|
+
this.updateButtonText();
|
474
|
+
this.options.onChange($option, false);
|
204
475
|
},
|
205
476
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
477
|
+
// Rebuild the whole dropdown menu.
|
478
|
+
rebuild : function() {
|
479
|
+
$('.multiselect-container', this.$container).html('');
|
480
|
+
this.buildDropdown(this.$select, this.options);
|
481
|
+
this.updateButtonText();
|
482
|
+
|
483
|
+
// Enable filtering.
|
484
|
+
if (this.options.enableFiltering || this.options.enableCaseInsensitiveFiltering) {
|
485
|
+
this.buildFilter();
|
486
|
+
}
|
487
|
+
},
|
210
488
|
|
211
489
|
// Get options by merging defaults and given options.
|
212
|
-
getOptions: function(options) {
|
490
|
+
getOptions : function(options) {
|
213
491
|
return $.extend({}, this.defaults, options);
|
492
|
+
},
|
493
|
+
|
494
|
+
updateButtonText : function() {
|
495
|
+
var options = this.getSelected();
|
496
|
+
$('button', this.$container).html(this.options.buttonText(options, this.$select));
|
497
|
+
},
|
498
|
+
|
499
|
+
// Get all selected options.
|
500
|
+
getSelected : function() {
|
501
|
+
return $('option:selected[value!="' + this.options.selectAllValue + '"]', this.$select);
|
502
|
+
},
|
503
|
+
|
504
|
+
updateOriginalOptions : function() {
|
505
|
+
this.originalOptions = this.$select.clone()[0].options;
|
506
|
+
},
|
507
|
+
|
508
|
+
asyncFunction : function(callback, timeout, self) {
|
509
|
+
var args = Array.prototype.slice.call(arguments, 3);
|
510
|
+
return setTimeout(function() {
|
511
|
+
callback.apply(self || window, args);
|
512
|
+
}, timeout);
|
214
513
|
}
|
215
514
|
};
|
216
515
|
|
217
|
-
$.fn.multiselect = function
|
218
|
-
return this.each(function
|
219
|
-
var data = $(this).data('multiselect'),
|
220
|
-
options = typeof option == 'object' && option;
|
516
|
+
$.fn.multiselect = function(option, parameter) {
|
517
|
+
return this.each(function() {
|
518
|
+
var data = $(this).data('multiselect'), options = typeof option == 'object' && option;
|
221
519
|
|
520
|
+
// Initialize the multiselect.
|
222
521
|
if (!data) {
|
223
|
-
$(this).data('multiselect', (data = new Multiselect(this, options)));
|
522
|
+
$(this).data('multiselect', ( data = new Multiselect(this, options)));
|
224
523
|
}
|
225
524
|
|
226
|
-
|
227
|
-
|
525
|
+
// Call multiselect method.
|
526
|
+
if ( typeof option == 'string') {
|
527
|
+
data[option](parameter);
|
228
528
|
}
|
229
529
|
});
|
230
|
-
}
|
530
|
+
};
|
531
|
+
|
532
|
+
$.fn.multiselect.Constructor = Multiselect;
|
533
|
+
|
534
|
+
$(function() {
|
535
|
+
$("select[data-role=multiselect]").multiselect();
|
536
|
+
});
|
537
|
+
|
231
538
|
}(window.jQuery);
|