alchemy_cms 2.2.4 → 2.3.rc5
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -1
- data/.travis.yml +3 -4
- data/Gemfile +1 -0
- data/README.md +10 -6
- data/alchemy_cms.gemspec +5 -2
- data/app/assets/images/alchemy/icons.png +0 -0
- data/app/assets/images/sassy-ie-overlay.png +0 -0
- data/app/assets/javascripts/alchemy/alchemy.base.js +50 -59
- data/app/assets/javascripts/alchemy/alchemy.buttons.js +14 -4
- data/app/assets/javascripts/alchemy/alchemy.datepicker.js +8 -2
- data/app/assets/javascripts/alchemy/alchemy.elements_window.js +11 -3
- data/app/assets/javascripts/alchemy/alchemy.gui.js.coffee +1 -1
- data/app/assets/javascripts/alchemy/alchemy.link_overlay.js.coffee +14 -1
- data/app/assets/javascripts/alchemy/alchemy.preview.js +1 -1
- data/app/assets/javascripts/alchemy/alchemy.preview_window.js +12 -4
- data/app/assets/javascripts/alchemy/alchemy.uploader.js +4 -1
- data/app/assets/javascripts/alchemy/alchemy.windows.js +18 -8
- data/app/assets/stylesheets/alchemy/_defaults.scss +84 -120
- data/app/assets/stylesheets/alchemy/alchemy.css +2 -2
- data/app/assets/stylesheets/alchemy/archive.css.scss +288 -0
- data/app/assets/stylesheets/alchemy/base.css.scss +95 -390
- data/app/assets/stylesheets/alchemy/dashboard.css.scss +4 -4
- data/app/assets/stylesheets/alchemy/elements.css.scss +83 -118
- data/app/assets/stylesheets/alchemy/flash.css.scss +1 -1
- data/app/assets/stylesheets/alchemy/form_elements.css.scss +528 -0
- data/app/assets/stylesheets/alchemy/frame.css.scss +13 -39
- data/app/assets/stylesheets/alchemy/icons.css.scss +217 -228
- data/app/assets/stylesheets/alchemy/jquery-ui.alchemy.css.scss +48 -50
- data/app/assets/stylesheets/alchemy/jquery.Jcrop.css.scss +1 -1
- data/app/assets/stylesheets/alchemy/login.css.scss +1 -5
- data/app/assets/stylesheets/alchemy/menubar.css.scss +19 -29
- data/app/assets/stylesheets/alchemy/pagination.css.scss +3 -4
- data/app/assets/stylesheets/alchemy/sitemap.css.scss +81 -81
- data/app/assets/stylesheets/alchemy/tables.css.scss +63 -57
- data/app/assets/stylesheets/alchemy/tinymce_dialog.css.scss +57 -57
- data/app/assets/stylesheets/alchemy/upload.css.scss +6 -6
- data/app/assets/stylesheets/tiny_mce/plugins/inlinepopups/skins/alchemy/window.css.scss +6 -10
- data/app/controllers/alchemy/admin/attachments_controller.rb +5 -4
- data/app/controllers/alchemy/admin/base_controller.rb +1 -9
- data/app/controllers/alchemy/admin/contents_controller.rb +4 -6
- data/app/controllers/alchemy/admin/elements_controller.rb +2 -2
- data/app/controllers/alchemy/admin/pages_controller.rb +2 -2
- data/app/controllers/alchemy/admin/pictures_controller.rb +74 -15
- data/app/controllers/alchemy/attachments_controller.rb +8 -2
- data/app/controllers/alchemy/base_controller.rb +47 -5
- data/app/controllers/alchemy/elements_controller.rb +1 -1
- data/app/controllers/alchemy/messages_controller.rb +12 -12
- data/app/controllers/alchemy/pages_controller.rb +5 -1
- data/app/controllers/alchemy/pictures_controller.rb +9 -4
- data/app/controllers/alchemy/user_sessions_controller.rb +2 -4
- data/app/helpers/alchemy/admin/base_helper.rb +98 -19
- data/app/helpers/alchemy/admin/contents_helper.rb +2 -2
- data/app/helpers/alchemy/admin/elements_helper.rb +2 -3
- data/app/helpers/alchemy/base_helper.rb +6 -5
- data/app/helpers/alchemy/elements_helper.rb +2 -2
- data/app/helpers/alchemy/essences_helper.rb +4 -5
- data/app/helpers/alchemy/pages_helper.rb +15 -79
- data/app/helpers/alchemy/url_helper.rb +67 -0
- data/app/mailers/alchemy/messages.rb +1 -1
- data/app/mailers/alchemy/notifications.rb +1 -1
- data/app/models/alchemy/attachment.rb +11 -2
- data/app/models/alchemy/cell.rb +20 -10
- data/app/models/alchemy/content.rb +4 -3
- data/app/models/alchemy/element.rb +170 -178
- data/app/models/alchemy/language/code.rb +4 -1
- data/app/models/alchemy/message.rb +19 -3
- data/app/models/alchemy/page.rb +45 -40
- data/app/models/alchemy/picture.rb +24 -2
- data/app/models/alchemy/user.rb +2 -3
- data/app/views/alchemy/admin/attachments/_archive_overlay.html.erb +12 -12
- data/app/views/alchemy/admin/attachments/_attachment.html.erb +1 -1
- data/app/views/alchemy/admin/attachments/create.js.erb +1 -0
- data/app/views/alchemy/admin/attachments/edit.html.erb +9 -3
- data/app/views/alchemy/admin/attachments/index.html.erb +3 -2
- data/app/views/alchemy/admin/contents/_missing.html.erb +1 -1
- data/app/views/alchemy/admin/contents/create.js.erb +54 -0
- data/app/views/alchemy/admin/contents/new.html.erb +9 -4
- data/app/views/alchemy/admin/elements/{_add_content.html.erb → _add_picture.html.erb} +4 -4
- data/app/views/alchemy/admin/elements/_elements_select.html.erb +2 -1
- data/app/views/alchemy/admin/elements/_new_element_form.html.erb +1 -1
- data/app/views/alchemy/admin/elements/{_picture_editor.html.erb → _picture_gallery_editor.html.erb} +7 -11
- data/app/views/alchemy/admin/elements/fold.js.erb +46 -0
- data/app/views/alchemy/admin/elements/index.html.erb +24 -24
- data/app/views/alchemy/admin/elements/list.js.erb +11 -9
- data/app/views/alchemy/admin/essence_files/assign.js.erb +3 -1
- data/app/views/alchemy/admin/essence_pictures/destroy.js.erb +28 -0
- data/app/views/alchemy/admin/pages/_contactform_links.html.erb +8 -6
- data/app/views/alchemy/admin/pages/_external_link.html.erb +11 -9
- data/app/views/alchemy/admin/pages/_file_link.html.erb +10 -8
- data/app/views/alchemy/admin/pages/_internal_link.html.erb +14 -10
- data/app/views/alchemy/admin/pages/_new_page_form.html.erb +1 -1
- data/app/views/alchemy/admin/pages/_page.html.erb +1 -1
- data/app/views/alchemy/admin/pages/_page_for_links.html.erb +32 -21
- data/app/views/alchemy/admin/pages/configure.html.erb +2 -2
- data/app/views/alchemy/admin/pages/configure_external.html.erb +13 -13
- data/app/views/alchemy/admin/pages/edit.html.erb +2 -2
- data/app/views/alchemy/admin/pages/index.html.erb +26 -24
- data/app/views/alchemy/admin/pages/link.html.erb +2 -5
- data/app/views/alchemy/admin/partials/_upload_form.html.erb +28 -12
- data/app/views/alchemy/admin/pictures/_archive.html.erb +54 -0
- data/app/views/alchemy/admin/pictures/_archive_overlay.html.erb +10 -7
- data/app/views/alchemy/admin/pictures/_filter_and_size_bar.html.erb +21 -22
- data/app/views/alchemy/admin/pictures/_filter_bar.html.erb +31 -0
- data/app/views/alchemy/admin/pictures/_overlay_picture_list.html.erb +9 -0
- data/app/views/alchemy/admin/pictures/_picture.html.erb +36 -6
- data/app/views/alchemy/admin/pictures/_tag_list.html.erb +27 -0
- data/app/views/alchemy/admin/pictures/archive_overlay.js.erb +3 -1
- data/app/views/alchemy/admin/pictures/create.js.erb +4 -5
- data/app/views/alchemy/admin/pictures/edit.html.erb +26 -0
- data/app/views/alchemy/admin/pictures/edit_multiple.html.erb +39 -0
- data/app/views/alchemy/admin/pictures/index.html.erb +81 -70
- data/app/views/alchemy/admin/pictures/index.js.erb +3 -0
- data/app/views/alchemy/admin/pictures/new.html.erb +1 -0
- data/app/views/alchemy/admin/resources/index.html.erb +3 -1
- data/app/views/alchemy/admin/users/_table.html.erb +1 -1
- data/app/views/alchemy/admin/users/index.html.erb +27 -23
- data/app/views/alchemy/elements/_article_editor.html.erb +7 -2
- data/app/views/alchemy/elements/_bild_editor.html.erb +1 -1
- data/app/views/alchemy/elements/_bild_text_editor.html.erb +6 -1
- data/app/views/alchemy/elements/_bild_text_view.html.erb +3 -3
- data/app/views/alchemy/elements/_image_mosaic_editor.html.erb +1 -1
- data/app/views/alchemy/elements/_image_mosaic_view.html.erb +2 -2
- data/app/views/alchemy/elements/_intro_image_text_view.html.erb +4 -4
- data/app/views/alchemy/elements/_searchresult_editor.html.erb +4 -1
- data/app/views/alchemy/essences/_essence_boolean_editor.html.erb +1 -1
- data/app/views/alchemy/essences/_essence_picture_editor.html.erb +2 -3
- data/app/views/alchemy/essences/_essence_picture_tools.html.erb +1 -1
- data/app/views/alchemy/search/_form.html.erb +8 -0
- data/app/views/alchemy/search/_result.html.erb +3 -2
- data/app/views/alchemy/search/_results.html.erb +28 -0
- data/app/views/alchemy/user_sessions/leave.html.erb +4 -4
- data/app/views/alchemy/user_sessions/login.html.erb +1 -2
- data/app/views/layouts/alchemy/admin.html.erb +30 -10
- data/app/views/layouts/alchemy/login.html.erb +2 -39
- data/config/alchemy/elements.yml +1 -2
- data/config/alchemy/page_layouts.yml +8 -5
- data/config/authorization_rules.rb +27 -18
- data/config/initializers/localeapp.rb +9 -0
- data/config/locales/alchemy.de.yml +93 -56
- data/config/locales/alchemy.en.yml +73 -50
- data/config/routes.rb +3 -1
- data/db/migrate/20120704181529_add_upload_hash_to_alchemy_picture.rb +5 -0
- data/db/migrate/20120705214247_acts_as_taggable_on_migration.rb +28 -0
- data/db/migrate/20120728185830_add_cached_tag_list_to_alchemy_pictures.rb +5 -0
- data/db/migrate/20120831135441_set_alchemy_languages_country_code_default_to_empty_string.rb +9 -0
- data/lib/alchemy/capistrano.rb +2 -2
- data/lib/alchemy/essence.rb +14 -0
- data/lib/alchemy/page_layout.rb +0 -6
- data/lib/alchemy/resource.rb +9 -15
- data/lib/alchemy/upgrader.rb +18 -3
- data/lib/alchemy/version.rb +5 -1
- data/lib/alchemy_cms.rb +4 -1
- data/lib/rails/generators/alchemy/deploy_script/deploy_script_generator.rb +16 -6
- data/lib/rails/generators/alchemy/deploy_script/templates/deploy.rb.tt +17 -3
- data/lib/rails/generators/alchemy/elements/elements_generator.rb +6 -1
- data/lib/rails/generators/alchemy/elements/templates/editor.html.erb +10 -1
- data/lib/rails/generators/alchemy/elements/templates/view.html.erb +17 -18
- data/lib/rails/generators/alchemy/scaffold/files/pages.html.erb +4 -2
- data/lib/tasks/fleximage.rake +2 -2
- data/spec/controllers/admin/contents_controller_spec.rb +2 -2
- data/spec/controllers/admin/elements_controller_spec.rb +30 -1
- data/spec/controllers/admin/pages_controller_spec.rb +35 -18
- data/spec/controllers/admin/trash_controller_spec.rb +40 -16
- data/spec/controllers/attachments_controller_spec.rb +62 -0
- data/spec/controllers/base_controller_spec.rb +43 -42
- data/spec/controllers/elements_controller_spec.rb +30 -0
- data/spec/controllers/pages_controller_spec.rb +22 -5
- data/spec/controllers/pictures_controller_spec.rb +82 -0
- data/spec/dummy/app/models/event.rb +2 -1
- data/spec/dummy/config/database.yml +3 -2
- data/spec/dummy/db/schema.rb +51 -27
- data/spec/factories.rb +29 -8
- data/spec/helpers/admin/base_helper_spec.rb +134 -21
- data/spec/helpers/admin/contents_helper_spec.rb +2 -2
- data/spec/helpers/admin/elements_helper_spec.rb +17 -9
- data/spec/helpers/admin/essences_helper_spec.rb +7 -6
- data/spec/helpers/essences_helper_spec.rb +8 -7
- data/spec/helpers/pages_helper_spec.rb +208 -325
- data/spec/helpers/url_helper_spec.rb +171 -0
- data/spec/integration/admin/link_overlay_spec.rb +53 -0
- data/spec/integration/admin/modules_integration_spec.rb +22 -26
- data/spec/integration/admin/pages_controller_spec.rb +10 -19
- data/spec/integration/admin/picture_library_integration_spec.rb +52 -0
- data/spec/integration/admin/resources_integration_spec.rb +68 -75
- data/spec/integration/pages_controller_spec.rb +70 -61
- data/spec/integration/security_spec.rb +3 -5
- data/spec/integration/translation_integration_spec.rb +56 -0
- data/spec/libraries/essence_spec.rb +18 -0
- data/spec/libraries/resource_spec.rb +101 -79
- data/spec/libraries/resources_helper_spec.rb +3 -0
- data/spec/models/content_spec.rb +63 -60
- data/spec/models/element_spec.rb +203 -93
- data/spec/models/language_spec.rb +90 -65
- data/spec/models/page_layout_spec.rb +37 -0
- data/spec/models/page_spec.rb +181 -113
- data/spec/models/picture_spec.rb +73 -26
- data/spec/models/resource_spec.rb +52 -23
- data/spec/support/alchemy/specs_helpers.rb +2 -0
- data/spec/support/image.png +0 -0
- data/spec/{helpers/url_helpers_spec.rb → url_helpers_spec.rb} +0 -0
- data/vendor/assets/javascripts/jquery_plugins/jquery.selectBoxIt.js +1909 -0
- data/vendor/assets/javascripts/jquery_plugins/preloadCssImages.jQuery_v5.js +152 -0
- metadata +106 -33
- data/app/assets/stylesheets/alchemy/buttons.css.scss +0 -361
- data/app/assets/stylesheets/alchemy/jquery.sb.css.scss +0 -260
- data/app/views/alchemy/admin/contents/create.js.coffee +0 -49
- data/app/views/alchemy/admin/elements/fold.js.coffee +0 -37
- data/app/views/alchemy/admin/essence_pictures/destroy.js.coffee +0 -19
- data/app/views/alchemy/admin/pictures/_pictures_list.html.erb +0 -16
- data/app/views/alchemy/admin/pictures/update.js.erb +0 -3
- data/spec/dummy/config/locales/en.yml +0 -5
- data/spec/dummy/config/locales/fo.yml +0 -5
- data/spec/page_layout_spec.rb +0 -35
- data/vendor/assets/javascripts/jquery_plugins/jquery.in-place-edit.js +0 -172
- data/vendor/assets/javascripts/jquery_plugins/jquery.sb.min.js +0 -14
data/spec/models/picture_spec.rb
CHANGED
@@ -1,54 +1,101 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
3
|
+
module Alchemy
|
4
|
+
describe Picture do
|
4
5
|
|
5
|
-
|
6
|
+
let :image_file do
|
7
|
+
File.new(File.expand_path('../../support/image.png', __FILE__))
|
8
|
+
end
|
6
9
|
|
7
|
-
it "
|
8
|
-
|
9
|
-
|
10
|
+
it "is valid with valid attributes" do
|
11
|
+
picture = Picture.new(:image_file => image_file)
|
12
|
+
picture.should be_valid
|
10
13
|
end
|
11
14
|
|
12
|
-
|
15
|
+
describe '#suffix' do
|
13
16
|
|
14
|
-
|
15
|
-
|
17
|
+
it "should return the suffix of original filename" do
|
18
|
+
pic = stub_model(Picture, :image_filename => 'kitten.JPG')
|
19
|
+
pic.suffix.should == "jpg"
|
16
20
|
end
|
17
21
|
|
18
|
-
|
19
|
-
|
22
|
+
context "image has no suffix" do
|
23
|
+
|
24
|
+
before(:each) do
|
25
|
+
@pic = stub_model(Picture, :image_filename => 'kitten')
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should return empty string" do
|
29
|
+
@pic.suffix.should == ""
|
30
|
+
end
|
31
|
+
|
20
32
|
end
|
21
33
|
|
22
34
|
end
|
23
35
|
|
24
|
-
|
36
|
+
describe '#humanized_name' do
|
37
|
+
|
38
|
+
it "should return a humanized version of original filename" do
|
39
|
+
pic = stub_model(Picture, :image_filename => 'cute_kitten.JPG')
|
40
|
+
pic.humanized_name.should == "Cute kitten"
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should not remove incidents of suffix from filename" do
|
44
|
+
pic = stub_model(Picture, :image_filename => 'cute_kitten_mo.jpgi.JPG')
|
45
|
+
pic.humanized_name.should == "Cute kitten mo.jpgi"
|
46
|
+
pic.humanized_name.should_not == "Cute kitten moi"
|
47
|
+
end
|
48
|
+
|
49
|
+
context "image has no suffix" do
|
50
|
+
|
51
|
+
before(:each) do
|
52
|
+
@pic = stub_model(Picture, :image_filename => 'cute_kitten')
|
53
|
+
@pic.stub!(:suffix).and_return("")
|
54
|
+
end
|
25
55
|
|
26
|
-
|
56
|
+
it "should return humanized name" do
|
57
|
+
@pic.humanized_name.should == "Cute kitten"
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
27
61
|
|
28
|
-
it "should return a humanized version of original filename" do
|
29
|
-
pic = stub_model(Alchemy::Picture, :image_filename => 'cute_kitten.JPG')
|
30
|
-
pic.humanized_name.should == "Cute kitten"
|
31
62
|
end
|
32
63
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
64
|
+
describe '#self.last_upload' do
|
65
|
+
|
66
|
+
it "should return all pictures that have the same upload-hash as the most recent picture" do
|
67
|
+
other_upload = Picture.create!(:image_file => image_file, :upload_hash => '456')
|
68
|
+
same_upload = Picture.create!(:image_file => image_file, :upload_hash => '123')
|
69
|
+
most_recent = Picture.create!(:image_file => image_file, :upload_hash => '123')
|
70
|
+
|
71
|
+
Picture.last_upload.should include(most_recent)
|
72
|
+
Picture.last_upload.should include(same_upload)
|
73
|
+
Picture.last_upload.should_not include(other_upload)
|
74
|
+
|
75
|
+
[other_upload, same_upload, most_recent].each { |p| p.destroy }
|
76
|
+
end
|
77
|
+
|
37
78
|
end
|
38
79
|
|
39
|
-
|
80
|
+
describe '#self.recent' do
|
40
81
|
|
41
|
-
before(:
|
42
|
-
|
43
|
-
@
|
82
|
+
before(:all) do
|
83
|
+
now = Time.now
|
84
|
+
@recent = Picture.create!(:image_file => image_file)
|
85
|
+
@old_picture = Picture.create!(:image_file => image_file)
|
86
|
+
@recent.update_column(:created_at, now-23.hours)
|
87
|
+
@old_picture.update_column(:created_at, now-10.days)
|
44
88
|
end
|
45
89
|
|
46
|
-
it "should return
|
47
|
-
|
90
|
+
it "should return all pictures that have been created in the last 24 hours" do
|
91
|
+
Picture.recent.should include(@recent)
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should not return old pictures" do
|
95
|
+
Picture.recent.should_not include(@old_picture)
|
48
96
|
end
|
49
97
|
|
50
98
|
end
|
51
99
|
|
52
100
|
end
|
53
|
-
|
54
101
|
end
|
@@ -16,29 +16,18 @@ module Alchemy
|
|
16
16
|
}
|
17
17
|
end
|
18
18
|
|
19
|
-
def resource_relations
|
20
|
-
{
|
21
|
-
"event" => {
|
22
|
-
"location_id" => {
|
23
|
-
"attr_method" => "location#name",
|
24
|
-
"attr_type" => "string"
|
25
|
-
}
|
26
|
-
}
|
27
|
-
}
|
28
|
-
end
|
29
|
-
|
30
19
|
let(:resource) { Resource.new("admin/events", module_definition) }
|
31
20
|
|
32
21
|
describe "#initialize" do
|
33
|
-
it "should set an instance variable
|
22
|
+
it "should set an instance variable that holds the controller path" do
|
34
23
|
resource.instance_variable_get(:@controller_path).should == "admin/events"
|
35
24
|
end
|
36
25
|
|
37
|
-
it "should set an instance variable
|
26
|
+
it "should set an instance variable that holds the module definition" do
|
38
27
|
resource.instance_variable_get(:@module_definition).should == module_definition
|
39
28
|
end
|
40
29
|
|
41
|
-
it "should set the standard
|
30
|
+
it "should set the standard database attributes (rails defaults) to be skipped" do
|
42
31
|
resource.skip_attributes.should == %W[id updated_at created_at creator_id updater_id]
|
43
32
|
end
|
44
33
|
end
|
@@ -62,7 +51,7 @@ module Alchemy
|
|
62
51
|
end
|
63
52
|
|
64
53
|
describe "#permission_scope" do
|
65
|
-
it "should set an instance variable
|
54
|
+
it "should set an instance variable that holds the permission scope for declarative authorization" do
|
66
55
|
resource.permission_scope
|
67
56
|
resource.instance_variable_get(:@_permission).should == :admin_events
|
68
57
|
end
|
@@ -75,23 +64,63 @@ module Alchemy
|
|
75
64
|
end
|
76
65
|
|
77
66
|
describe "#attributes" do
|
78
|
-
it "should not return the to
|
67
|
+
it "should not return the to-be-skipped attributes" do
|
79
68
|
resource.class.const_get(:DEFAULT_SKIPPED_ATTRIBUTES).each do |skipped_attr|
|
80
|
-
resource.attributes.detect{|a| a[:name] == skipped_attr }.should == nil
|
69
|
+
resource.attributes.detect { |a| a[:name] == skipped_attr }.should == nil
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "when skip_attributes is defined as class-method in the model" do
|
74
|
+
before do
|
75
|
+
Event.class_eval do
|
76
|
+
def self.skip_attributes
|
77
|
+
%W[hidden_name]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
after do
|
82
|
+
Event.class_eval do
|
83
|
+
class << self
|
84
|
+
undef :skip_attributes
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should not return the attributes returned by that method" do
|
90
|
+
resource.attributes.detect { |a| a[:name] == 'hidden_name' }.should be_nil
|
91
|
+
resource.attributes.detect { |a| a[:name] == 'name' }.should_not be_nil
|
81
92
|
end
|
82
93
|
end
|
83
94
|
|
84
|
-
context "when
|
95
|
+
context "when resource_relations defined as class-method in the model" do
|
96
|
+
before do
|
97
|
+
Event.class_eval do
|
98
|
+
def self.resource_relations
|
99
|
+
{
|
100
|
+
:location_id => {:attr_method => "location#name", :attr_type => :string},
|
101
|
+
:organizer_id => {:attr_method => "organizer#name", :attr_type => :string}
|
102
|
+
}
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
after do
|
107
|
+
Event.class_eval do
|
108
|
+
class << self
|
109
|
+
undef :resource_relations
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
85
113
|
it "should use the attribute location#name instead of location_id" do
|
86
|
-
|
87
|
-
|
114
|
+
resource.attributes.detect { |a| a[:name] == "location#name" }.should == {:name => "location#name", :type => :string}
|
115
|
+
end
|
116
|
+
it "should use the attribute organizer#name instead of organizer_id" do
|
117
|
+
resource.attributes.detect { |a| a[:name] == "organizer#name" }.should == {:name => "organizer#name", :type => :string}
|
88
118
|
end
|
89
119
|
end
|
90
120
|
|
91
|
-
context "when
|
121
|
+
context "when resource_relation is not defined" do
|
92
122
|
it "should use the attribute location_id" do
|
93
|
-
|
94
|
-
resource.attributes.detect{|a| a[:name] == "location_id" }.should == {:name=>"location_id", :type=> :integer}
|
123
|
+
resource.attributes.detect { |a| a[:name] == "location_id" }.should == {:name => "location_id", :type => :integer}
|
95
124
|
end
|
96
125
|
end
|
97
126
|
|
@@ -28,6 +28,8 @@ module Alchemy
|
|
28
28
|
#
|
29
29
|
def login_into_alchemy
|
30
30
|
visit '/alchemy/admin/login'
|
31
|
+
# Giving phantomjs some time to start under ruby 1.8.7 on travis-ci
|
32
|
+
sleep(5) if ENV['CI'] && RUBY_VERSION == "1.8.7"
|
31
33
|
fill_in('alchemy_user_session_login', :with => 'jdoe')
|
32
34
|
fill_in('alchemy_user_session_password', :with => 's3cr3t')
|
33
35
|
click_on('Login')
|
Binary file
|
File without changes
|
@@ -0,0 +1,1909 @@
|
|
1
|
+
/* jquery Selectboxit - v0.9.0 - 2012-05-21
|
2
|
+
* http://www.gregfranko.com/jQuery.selectBoxIt.js/
|
3
|
+
* Copyright (c) 2012 Greg Franko; Licensed MIT */
|
4
|
+
|
5
|
+
// Immediately-Invoked Function Expression (IIFE) [Ben Alman Blog Post](http://benalman.com/news/2010/11/immediately-invoked-function-expression/) that calls another IIFE that contains all of the plugin logic. I used this pattern so that anyone viewing this code would not have to scroll to the bottom of the page to view the local parameters that were passed to the main IIFE.
|
6
|
+
|
7
|
+
(function (selectBoxIt) {
|
8
|
+
|
9
|
+
//ECMAScript 5 Strict Mode: [John Resig Blog Post](http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/)
|
10
|
+
"use strict";
|
11
|
+
|
12
|
+
// Calls the second IIFE and locally passes in the global jQuery, window, and document objects
|
13
|
+
selectBoxIt(jQuery, window, document);
|
14
|
+
|
15
|
+
}
|
16
|
+
|
17
|
+
// Locally passes in `jQuery`, the `window` object, the `document` object, and an `undefined` variable. The `jQuery`, `window` and `document` objects are passed in locally, to improve performance, since javascript first searches for a variable match within the local variables set before searching the global variables set. All of the global variables are also passed in locally to be minifier friendly. `undefined` can be passed in locally, because it is not a reserved word in JavaScript.
|
18
|
+
|
19
|
+
(function ($, window, document, undefined) {
|
20
|
+
|
21
|
+
// ECMAScript 5 Strict Mode: [John Resig Blog Post](http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/)
|
22
|
+
"use strict";
|
23
|
+
|
24
|
+
// Calling the jQueryUI Widget Factory Method
|
25
|
+
$.widget("selectBox.selectBoxIt", {
|
26
|
+
|
27
|
+
// Plugin version
|
28
|
+
|
29
|
+
version: "0.9.0",
|
30
|
+
|
31
|
+
// These options will be used as defaults
|
32
|
+
options: {
|
33
|
+
|
34
|
+
// **showEffect**: Accepts String: "none", "fadeIn", "show", "slideDown", or any of the jQueryUI show effects (i.e. "bounce")
|
35
|
+
showEffect: "none",
|
36
|
+
|
37
|
+
// **showEffectOptions**: Accepts an object literal. All of the available properties are based on the jqueryUI effect options
|
38
|
+
showEffectOptions: {},
|
39
|
+
|
40
|
+
// **showEffectSpeed**: Accepts Number (milliseconds) or String: "slow", "medium", or "fast"
|
41
|
+
showEffectSpeed: "medium",
|
42
|
+
|
43
|
+
// **hideEffect**: Accepts String: "none", "fadeOut", "hide", "slideUp", or any of the jQueryUI hide effects (i.e. "explode")
|
44
|
+
hideEffect: "none",
|
45
|
+
|
46
|
+
// **hideEffectOptions**: Accepts an object literal. All of the available properties are based on the jqueryUI effect options
|
47
|
+
hideEffectOptions: {},
|
48
|
+
|
49
|
+
// **hideEffectSpeed**: Accepts Number (milliseconds) or String: "slow", "medium", or "fast"
|
50
|
+
hideEffectSpeed: "medium",
|
51
|
+
|
52
|
+
// **showFirstOption**: Shows the first dropdown list option within the dropdown list options list
|
53
|
+
showFirstOption: true,
|
54
|
+
|
55
|
+
// **defaultText**: Overrides the text used by the dropdown list selected option to allow a user to specify custom text. Accepts a String.
|
56
|
+
defaultText: "",
|
57
|
+
|
58
|
+
// **defaultIcon**: Overrides the icon used by the dropdown list selected option to allow a user to specify a custom icon. Accepts a String (CSS class name(s)).
|
59
|
+
defaultIcon: "",
|
60
|
+
|
61
|
+
// **downArrowIcon**: Overrides the default down arrow used by the dropdown list to allow a user to specify a custom image. Accepts a String (CSS class name(s)).
|
62
|
+
downArrowIcon: ""
|
63
|
+
|
64
|
+
},
|
65
|
+
|
66
|
+
// _Create
|
67
|
+
// -------
|
68
|
+
// Sets the Plugin Instance variables and
|
69
|
+
// constructs the plugin. Only called once.
|
70
|
+
_create: function() {
|
71
|
+
|
72
|
+
// The original select box DOM element
|
73
|
+
this.originalElem = this.element[0];
|
74
|
+
|
75
|
+
// The original select box DOM element wrapped in a jQuery object
|
76
|
+
this.selectBox = this.element;
|
77
|
+
|
78
|
+
// All of the original select box options
|
79
|
+
this.selectItems = this.element.find("option");
|
80
|
+
|
81
|
+
// The first option in the original select box
|
82
|
+
this.firstSelectItem = this.element.find("option").slice(0, 1);
|
83
|
+
|
84
|
+
// The index of the currently selected dropdown list option
|
85
|
+
this.currentFocus = 0;
|
86
|
+
|
87
|
+
// Keeps track of which blur events will hide the dropdown list options
|
88
|
+
this.blur = true;
|
89
|
+
|
90
|
+
// The html document height
|
91
|
+
this.documentHeight = $(document).height();
|
92
|
+
|
93
|
+
// Array holding all of the original select box options text
|
94
|
+
this.textArray = [];
|
95
|
+
|
96
|
+
// Maintains search order in the `search` method
|
97
|
+
this.currentIndex = 0;
|
98
|
+
|
99
|
+
// Whether or not the dropdown list opens up or down (depending on how much room is on the page)
|
100
|
+
this.flipped = false;
|
101
|
+
|
102
|
+
// Creates the div elements that will become the dropdown
|
103
|
+
this._createDiv();
|
104
|
+
|
105
|
+
// Creates the ul element that will become the dropdown options list
|
106
|
+
this._createUnorderedList();
|
107
|
+
|
108
|
+
// Hides the original select box and adds the new plugin DOM elements to the page
|
109
|
+
this._replaceSelectBox();
|
110
|
+
|
111
|
+
// Adds event handlers to the new dropdown list
|
112
|
+
this._eventHandlers();
|
113
|
+
|
114
|
+
if(this.originalElem.disabled && this.disable) {
|
115
|
+
|
116
|
+
// Disables the dropdown list if the original dropdown list had the `disabled` attribute
|
117
|
+
this.disable();
|
118
|
+
|
119
|
+
}
|
120
|
+
|
121
|
+
if(this._ariaAccessibility) {
|
122
|
+
|
123
|
+
// Adds ARIA accessibillity tags to the dropdown list
|
124
|
+
this._ariaAccessibility();
|
125
|
+
|
126
|
+
}
|
127
|
+
|
128
|
+
// Adds jQueryUI classes to the dropdown list if the jqueryUI option is set to true
|
129
|
+
this._addClasses();
|
130
|
+
|
131
|
+
// Focus the selectbox div, if the original select tag has autofocus attribute
|
132
|
+
if (this.selectBox.attr("autofocus")) {
|
133
|
+
|
134
|
+
this.div.focus();
|
135
|
+
|
136
|
+
}
|
137
|
+
|
138
|
+
// Triggers a custom `create` event on the original dropdown list
|
139
|
+
this.selectBox.trigger("create");
|
140
|
+
|
141
|
+
},
|
142
|
+
// _Create Div
|
143
|
+
// -----------
|
144
|
+
// Creates new div and span elements to replace
|
145
|
+
// the original select box with a dropdown list
|
146
|
+
_createDiv: function() {
|
147
|
+
|
148
|
+
// Creates a span element that contains the dropdown list text value
|
149
|
+
this.divText = $("<span/>", {
|
150
|
+
|
151
|
+
// Dynamically sets the span `id` attribute
|
152
|
+
"id": this.originalElem.id + "SelectBoxItText",
|
153
|
+
|
154
|
+
"class": "selectboxit-text",
|
155
|
+
|
156
|
+
// IE specific attribute to not allow the element to be selected
|
157
|
+
"unselectable": "on",
|
158
|
+
|
159
|
+
// Sets the span `text` to equal the original select box default value
|
160
|
+
"text": this.firstSelectItem.text()
|
161
|
+
|
162
|
+
}).
|
163
|
+
|
164
|
+
// Sets the HTML5 data attribute on the divText `span` element
|
165
|
+
attr("data-val", this.originalElem.value);
|
166
|
+
|
167
|
+
// Creates a span element that contains the dropdown list text value
|
168
|
+
this.divImage = $("<span/>", {
|
169
|
+
|
170
|
+
// Dynamically sets the span `id` attribute
|
171
|
+
"id": this.originalElem.id + "SelectBoxItDefaultIcon",
|
172
|
+
|
173
|
+
"class": "selectboxit-default-icon",
|
174
|
+
|
175
|
+
// IE specific attribute to not allow the element to be selected
|
176
|
+
"unselectable": "on"
|
177
|
+
|
178
|
+
});
|
179
|
+
|
180
|
+
// Creates a div to act as the new dropdown list
|
181
|
+
this.div = $("<div/>", {
|
182
|
+
|
183
|
+
// Dynamically sets the div `id` attribute
|
184
|
+
"id": this.originalElem.id + "SelectBoxIt",
|
185
|
+
|
186
|
+
"class": "selectboxit" + (this.originalElem.className !== "" ? " " + this.originalElem.className : ""),
|
187
|
+
|
188
|
+
// Sets the div `name` attribute to be the same name as the original select box
|
189
|
+
"name": this.originalElem.name,
|
190
|
+
|
191
|
+
// Sets the div `tabindex` attribute to 0 to allow the div to be focusable
|
192
|
+
"tabindex": 0,
|
193
|
+
|
194
|
+
// IE specific attribute to not allow the element to be selected
|
195
|
+
"unselectable": "on"
|
196
|
+
|
197
|
+
}).
|
198
|
+
|
199
|
+
// Appends the default text to the inner dropdown list div element
|
200
|
+
append(this.divImage).append(this.divText);
|
201
|
+
|
202
|
+
// Create the div container that will hold all of the dropdown list dom elements
|
203
|
+
this.divContainer = $("<div/>", {
|
204
|
+
|
205
|
+
"id": this.originalElem.id + "SelectBoxItContainer",
|
206
|
+
|
207
|
+
"class": "selectboxit-container"
|
208
|
+
}).
|
209
|
+
|
210
|
+
// Appends the inner dropdown list div element to the dropdown list container div element
|
211
|
+
append(this.div);
|
212
|
+
|
213
|
+
// Maintains chainability
|
214
|
+
return this;
|
215
|
+
},
|
216
|
+
|
217
|
+
// _Create Unordered List
|
218
|
+
// ----------------------
|
219
|
+
// Creates an unordered list element to hold the
|
220
|
+
// new dropdown list options that directly match
|
221
|
+
// the values of the original select box options
|
222
|
+
_createUnorderedList: function() {
|
223
|
+
|
224
|
+
// Storing the context of the widget
|
225
|
+
var self = this,
|
226
|
+
|
227
|
+
dataDisabled,
|
228
|
+
|
229
|
+
optgroupClass = "",
|
230
|
+
|
231
|
+
optgroupElement = "",
|
232
|
+
|
233
|
+
iconClass,
|
234
|
+
|
235
|
+
// Declaring the variable that will hold all of the dropdown list option elements
|
236
|
+
currentItem = "",
|
237
|
+
// Creates an unordered list element
|
238
|
+
createdList = $("<ul/>", {
|
239
|
+
|
240
|
+
// Sets the unordered list `id` attribute
|
241
|
+
"id": this.originalElem.id + "SelectBoxItOptions",
|
242
|
+
|
243
|
+
"class": "selectboxit-options",
|
244
|
+
|
245
|
+
//Sets the unordered list `tabindex` attribute to -1 to prevent the unordered list from being focusable
|
246
|
+
"tabindex": -1
|
247
|
+
|
248
|
+
});
|
249
|
+
|
250
|
+
// Checks the `showFirstOption` plugin option to determine if the first dropdown list option should be shown in the options list.
|
251
|
+
if (!this.options.showFirstOption) {
|
252
|
+
|
253
|
+
// Excludes the first dropdown list option from the options list
|
254
|
+
this.selectItems = this.selectBox.find("option").slice(1);
|
255
|
+
}
|
256
|
+
|
257
|
+
// Loops through the original select box options list and copies the text of each
|
258
|
+
// into new list item elements of the new dropdown list
|
259
|
+
this.selectItems.each(function(index) {
|
260
|
+
|
261
|
+
dataDisabled = $(this).prop("disabled");
|
262
|
+
|
263
|
+
iconClass = $(this).data("icon") || "";
|
264
|
+
|
265
|
+
// If the current option being traversed is within an optgroup
|
266
|
+
|
267
|
+
if($(this).parent().is("optgroup")) {
|
268
|
+
|
269
|
+
optgroupClass = "selectboxit-optgroup-option";
|
270
|
+
|
271
|
+
if($(this).index() === 0) {
|
272
|
+
|
273
|
+
optgroupElement = '<div class="selectboxit-optgroup-header" data-disabled="true">' + $(this).parent().first().attr("label") + '</div>';
|
274
|
+
|
275
|
+
}
|
276
|
+
|
277
|
+
else {
|
278
|
+
|
279
|
+
optgroupElement = "";
|
280
|
+
|
281
|
+
}
|
282
|
+
|
283
|
+
}
|
284
|
+
|
285
|
+
// If the current option being traversed is not within an optgroup
|
286
|
+
|
287
|
+
else {
|
288
|
+
|
289
|
+
optgroupClass = "";
|
290
|
+
|
291
|
+
}
|
292
|
+
|
293
|
+
// Uses string concatenation instead of append for speed since the number of dropdown list options is unknown.
|
294
|
+
currentItem += optgroupElement + '<li id="' + index + '" data-val="' + this.value + '" data-disabled="' + dataDisabled + '" class="' + optgroupClass + '"><span class="' + iconClass + '"></span>' + $(this).text() + '</li>';
|
295
|
+
|
296
|
+
// Stores all of the original select box options text inside of an array
|
297
|
+
// (Used later in the `searchAlgorithm` method)
|
298
|
+
self.textArray[index] = $(this).text();
|
299
|
+
|
300
|
+
// Checks the original select box option for the `selected` attribute
|
301
|
+
if (this.selected) {
|
302
|
+
|
303
|
+
//Replace the default text with the selected option
|
304
|
+
self.divText.text($(this).text());
|
305
|
+
|
306
|
+
//Set the currently selected option
|
307
|
+
self.currentFocus = index;
|
308
|
+
}
|
309
|
+
|
310
|
+
});
|
311
|
+
|
312
|
+
// If the `defaultText` option is being used
|
313
|
+
if (self.options.defaultText) {
|
314
|
+
|
315
|
+
//Overrides the current dropdown default text with the value the user specifies in the `defaultText` option
|
316
|
+
self.divText.text(self.options.defaultText);
|
317
|
+
}
|
318
|
+
|
319
|
+
// If the `defaultText` HTML5 data attribute is being used
|
320
|
+
if (self.selectBox.data("text")) {
|
321
|
+
|
322
|
+
// Overrides the current dropdown default text with the value from the HTML5 `defaultText` value
|
323
|
+
self.divText.text(self.selectBox.data("text"));
|
324
|
+
self.options.defaultText = self.selectBox.data("text");
|
325
|
+
|
326
|
+
}
|
327
|
+
|
328
|
+
// Append the list item to the unordered list
|
329
|
+
createdList.append(currentItem);
|
330
|
+
|
331
|
+
// Stores the dropdown list options list inside of the `list` instance variable
|
332
|
+
this.list = createdList;
|
333
|
+
|
334
|
+
// Append the dropdown list options list to the div container element
|
335
|
+
this.divContainer.append(this.list);
|
336
|
+
|
337
|
+
// Stores the individual dropdown list options inside of the `listItems` instance variable
|
338
|
+
this.listItems = this.list.find("li");
|
339
|
+
|
340
|
+
// Set the disabled CSS class for select box options
|
341
|
+
this.list.find("li[data-disabled='true']").not(".optgroupHeader").addClass("ui-state-disabled");
|
342
|
+
|
343
|
+
// If the first select box option is disabled, and the user has chosen to not show the first select box option
|
344
|
+
if (this.currentFocus === 0 && !this.options.showFirstOption && this.listItems.eq(0).hasClass("ui-state-disabled")) {
|
345
|
+
|
346
|
+
//Sets the default value of the dropdown list to the first option that is not disabled
|
347
|
+
this.currentFocus = +this.listItems.not(".ui-state-disabled").first().attr("id");
|
348
|
+
|
349
|
+
}
|
350
|
+
|
351
|
+
this.divImage.addClass(this.selectBox.data("icon") || this.options.defaultIcon || this.listItems.eq(this.currentFocus).find("span").attr("class"));
|
352
|
+
|
353
|
+
//Maintains chainability
|
354
|
+
return this;
|
355
|
+
},
|
356
|
+
|
357
|
+
// _Replace Select Box
|
358
|
+
// -------------------
|
359
|
+
// Hides the original dropdown list and inserts
|
360
|
+
// the new DOM elements
|
361
|
+
_replaceSelectBox: function() {
|
362
|
+
|
363
|
+
// Hides the original select box
|
364
|
+
this.selectBox.css("display", "none").
|
365
|
+
|
366
|
+
// Adds the new dropdown list to the page directly after the hidden original select box element
|
367
|
+
after(this.divContainer);
|
368
|
+
|
369
|
+
// The height of the dropdown list
|
370
|
+
var height = this.div.height();
|
371
|
+
|
372
|
+
// The down arrow element of the dropdown list
|
373
|
+
this.downArrow = $("<span/>", {
|
374
|
+
|
375
|
+
// Dynamically sets the span `id` attribute of the dropdown list down arrow
|
376
|
+
"id": this.originalElem.id + "SelectBoxItArrow",
|
377
|
+
|
378
|
+
"class": "selectboxit-arrow",
|
379
|
+
|
380
|
+
// IE specific attribute to not allow the dropdown list text to be selected
|
381
|
+
"unselectable": "on"
|
382
|
+
|
383
|
+
});
|
384
|
+
|
385
|
+
// The down arrow container element of the dropdown list
|
386
|
+
this.downArrowContainer = $("<span/>", {
|
387
|
+
|
388
|
+
// Dynamically sets the span `id` attribute for the down arrow container element
|
389
|
+
"id": this.originalElem.id + "SelectBoxItArrowContainer",
|
390
|
+
|
391
|
+
"class": "selectboxit-arrow-container",
|
392
|
+
|
393
|
+
// IE specific attribute to not allow the dropdown list text to be selected
|
394
|
+
"unselectable": "on",
|
395
|
+
|
396
|
+
// The dynamic CSS of the down arrow container element
|
397
|
+
"style": "height:" + height + "px;"
|
398
|
+
|
399
|
+
}).
|
400
|
+
|
401
|
+
// Inserts the down arrow element inside of the down arrow container element
|
402
|
+
append(this.downArrow);
|
403
|
+
|
404
|
+
// Appends the down arrow element to the dropdown list
|
405
|
+
this.div.append(this.downArrowContainer);
|
406
|
+
|
407
|
+
this.divImage.css({
|
408
|
+
|
409
|
+
"margin-top": height / 4
|
410
|
+
|
411
|
+
});
|
412
|
+
|
413
|
+
this.listItems.find("span").css({
|
414
|
+
|
415
|
+
"margin-top": height / 4
|
416
|
+
|
417
|
+
});
|
418
|
+
|
419
|
+
// Maintains chainability
|
420
|
+
return this;
|
421
|
+
},
|
422
|
+
|
423
|
+
// _Scroll-To-View
|
424
|
+
// ---------------
|
425
|
+
// Updates the dropdown list scrollTop value
|
426
|
+
_scrollToView: function(type) {
|
427
|
+
|
428
|
+
// The current scroll positioning of the dropdown list options list
|
429
|
+
var listScrollTop = this.list.scrollTop(),
|
430
|
+
|
431
|
+
// The height of the currently selected dropdown list option
|
432
|
+
currentItemHeight = this.listItems.eq(this.currentFocus).height(),
|
433
|
+
|
434
|
+
// The relative distance from the currently selected dropdown list option to the the top of the dropdown list options list
|
435
|
+
currentTopPosition = this.listItems.eq(this.currentFocus).position().top,
|
436
|
+
|
437
|
+
// The height of the dropdown list option list
|
438
|
+
listHeight = this.list.height();
|
439
|
+
|
440
|
+
// Scrolling logic for a text search
|
441
|
+
if (type === "search") {
|
442
|
+
|
443
|
+
// Increases the dropdown list options `scrollTop` if a user is searching for an option
|
444
|
+
// below the currently selected option that is not visible
|
445
|
+
if (listHeight - currentTopPosition < currentItemHeight) {
|
446
|
+
|
447
|
+
// The selected option will be shown at the very bottom of the visible options list
|
448
|
+
this.list.scrollTop(listScrollTop + (currentTopPosition - (listHeight - currentItemHeight)));
|
449
|
+
|
450
|
+
}
|
451
|
+
|
452
|
+
// Decreases the dropdown list options `scrollTop` if a user is searching for an option above the currently selected option that is not visible
|
453
|
+
else if (currentTopPosition < -1) {
|
454
|
+
|
455
|
+
this.list.scrollTop(currentTopPosition - currentItemHeight);
|
456
|
+
|
457
|
+
}
|
458
|
+
}
|
459
|
+
|
460
|
+
// Scrolling logic for the `up` keyboard navigation
|
461
|
+
else if (type === "up") {
|
462
|
+
|
463
|
+
// Decreases the dropdown list option list `scrollTop` if a user is navigating to an element that is not visible
|
464
|
+
if (currentTopPosition < -1) {
|
465
|
+
|
466
|
+
this.list.scrollTop(listScrollTop - Math.abs(this.listItems.eq(this.currentFocus).position().top));
|
467
|
+
|
468
|
+
}
|
469
|
+
}
|
470
|
+
|
471
|
+
// Scrolling logic for the `down` keyboard navigation
|
472
|
+
else if (type === "down") {
|
473
|
+
|
474
|
+
// Increases the dropdown list options `scrollTop` if a user is navigating to an element that is not fully visible
|
475
|
+
if (listHeight - currentTopPosition < currentItemHeight) {
|
476
|
+
|
477
|
+
// Increases the dropdown list options `scrollTop` by the height of the current option item.
|
478
|
+
this.list.scrollTop((listScrollTop + (Math.abs(this.listItems.eq(this.currentFocus).position().top) - listHeight + currentItemHeight)));
|
479
|
+
|
480
|
+
}
|
481
|
+
}
|
482
|
+
|
483
|
+
// Maintains chainability
|
484
|
+
return this;
|
485
|
+
},
|
486
|
+
|
487
|
+
// _Callback
|
488
|
+
// ---------
|
489
|
+
// Call the function passed into the method
|
490
|
+
_callbackSupport: function(callback) {
|
491
|
+
|
492
|
+
// Checks to make sure the parameter passed in is a function
|
493
|
+
if ($.isFunction(callback)) {
|
494
|
+
|
495
|
+
// Calls the method passed in as a parameter and sets the current `SelectBoxIt` object that is stored in the jQuery data method as the context(allows for `this` to reference the SelectBoxIt API Methods in the callback function. The `div` DOM element that acts as the new dropdown list is also passed as the only parameter to the callback
|
496
|
+
callback.call(this.element.data(this.widgetName), this.div);
|
497
|
+
|
498
|
+
}
|
499
|
+
},
|
500
|
+
|
501
|
+
// Open
|
502
|
+
// ----
|
503
|
+
// Opens the dropdown list options list
|
504
|
+
open: function(callback) {
|
505
|
+
|
506
|
+
if(!this.list.is(":visible")) {
|
507
|
+
|
508
|
+
var self = this;
|
509
|
+
|
510
|
+
// Triggers a custom "open" event on the original select box
|
511
|
+
this.selectBox.trigger("open");
|
512
|
+
|
513
|
+
if (this._dynamicPositioning) {
|
514
|
+
// Dynamically positions the dropdown list options list
|
515
|
+
this._dynamicPositioning();
|
516
|
+
}
|
517
|
+
|
518
|
+
// Determines what jQuery effect to use when opening the dropdown list options list
|
519
|
+
switch (this.options.showEffect) {
|
520
|
+
|
521
|
+
// Uses `no effect`
|
522
|
+
case "none":
|
523
|
+
|
524
|
+
// Does not require a callback function because this animation will complete before the call to `scrollToView`
|
525
|
+
this.list.show();
|
526
|
+
|
527
|
+
// Updates the list `scrollTop` attribute
|
528
|
+
this._scrollToView("search");
|
529
|
+
|
530
|
+
break;
|
531
|
+
|
532
|
+
// Uses the jQuery `show` special effect
|
533
|
+
case "show":
|
534
|
+
|
535
|
+
// Requires a callback function to determine when the `show` animation is complete
|
536
|
+
this.list.show(this.options.showEffectSpeed, function() {
|
537
|
+
|
538
|
+
// Updates the list `scrollTop` attribute
|
539
|
+
self._scrollToView("search");
|
540
|
+
|
541
|
+
});
|
542
|
+
|
543
|
+
break;
|
544
|
+
|
545
|
+
// Uses the jQuery `slideDown` special effect
|
546
|
+
case "slideDown":
|
547
|
+
|
548
|
+
// Requires a callback function to determine when the `slideDown` animation is complete
|
549
|
+
this.list.slideDown(this.options.showEffectSpeed, function() {
|
550
|
+
|
551
|
+
// Updates the list `scrollTop` attribute
|
552
|
+
self._scrollToView("search");
|
553
|
+
|
554
|
+
});
|
555
|
+
|
556
|
+
break;
|
557
|
+
|
558
|
+
// Uses the jQuery `fadeIn` special effect
|
559
|
+
case "fadeIn":
|
560
|
+
|
561
|
+
// Does not require a callback function because this animation will complete before the call to `scrollToView`
|
562
|
+
this.list.fadeIn(this.options.showEffectSpeed);
|
563
|
+
|
564
|
+
// Updates the list `scrollTop` attribute
|
565
|
+
this._scrollToView("search");
|
566
|
+
|
567
|
+
break;
|
568
|
+
|
569
|
+
// If none of the above options were passed, then a `jqueryUI show effect` is expected
|
570
|
+
default:
|
571
|
+
|
572
|
+
// Allows for custom show effects via the [jQueryUI core effects](http://http://jqueryui.com/demos/show/)
|
573
|
+
this.list.show(this.options.showEffect, this.options.showEffectOptions, this.options.showEffectSpeed, function() {
|
574
|
+
|
575
|
+
// Updates the list `scrollTop` attribute
|
576
|
+
self._scrollToView("search");
|
577
|
+
|
578
|
+
});
|
579
|
+
|
580
|
+
break;
|
581
|
+
|
582
|
+
}
|
583
|
+
|
584
|
+
}
|
585
|
+
|
586
|
+
// Provide callback function support
|
587
|
+
this._callbackSupport(callback);
|
588
|
+
|
589
|
+
// Maintains chainability
|
590
|
+
return this;
|
591
|
+
},
|
592
|
+
|
593
|
+
// Close
|
594
|
+
// -----
|
595
|
+
// Closes the dropdown list options list
|
596
|
+
close: function(callback) {
|
597
|
+
|
598
|
+
if(this.list.is(":visible")) {
|
599
|
+
|
600
|
+
var self = this;
|
601
|
+
|
602
|
+
// Triggers a custom "close" event on the original select box
|
603
|
+
this.selectBox.trigger("close");
|
604
|
+
|
605
|
+
// Determines what jQuery effect to use when closing the dropdown list options list
|
606
|
+
switch (this.options.hideEffect) {
|
607
|
+
|
608
|
+
// Uses `no effect`
|
609
|
+
case "none":
|
610
|
+
|
611
|
+
// Does not require a callback function because this animation will complete before the call to `scrollToView`
|
612
|
+
this.list.hide();
|
613
|
+
|
614
|
+
// Updates the list `scrollTop` attribute
|
615
|
+
this._scrollToView("search");
|
616
|
+
|
617
|
+
break;
|
618
|
+
|
619
|
+
// Uses the jQuery `hide` special effect
|
620
|
+
case "hide":
|
621
|
+
|
622
|
+
this.list.hide(this.options.hideEffectSpeed);
|
623
|
+
|
624
|
+
break;
|
625
|
+
|
626
|
+
// Uses the jQuery `slideUp` special effect
|
627
|
+
case "slideUp":
|
628
|
+
|
629
|
+
this.list.slideUp(this.options.hideEffectSpeed);
|
630
|
+
|
631
|
+
break;
|
632
|
+
|
633
|
+
// Uses the jQuery `fadeOut` special effect
|
634
|
+
case "fadeOut":
|
635
|
+
|
636
|
+
this.list.fadeOut(this.options.hideEffectSpeed);
|
637
|
+
|
638
|
+
break;
|
639
|
+
|
640
|
+
// If none of the above options were passed, then a `jqueryUI hide effect` is expected
|
641
|
+
default:
|
642
|
+
|
643
|
+
// Allows for custom hide effects via the [jQueryUI core effects](http://http://jqueryui.com/demos/hide/)
|
644
|
+
this.list.hide(this.options.hideEffect, this.options.hideEffectOptions, this.options.hideEffectSpeed, function() {
|
645
|
+
|
646
|
+
//Updates the list `scrollTop` attribute
|
647
|
+
self._scrollToView("search");
|
648
|
+
|
649
|
+
});
|
650
|
+
|
651
|
+
break;
|
652
|
+
}
|
653
|
+
|
654
|
+
}
|
655
|
+
|
656
|
+
// Provide callback function support
|
657
|
+
this._callbackSupport(callback);
|
658
|
+
|
659
|
+
// Maintains chainability
|
660
|
+
return this;
|
661
|
+
},
|
662
|
+
|
663
|
+
|
664
|
+
// _Event Handlers
|
665
|
+
// ---------------
|
666
|
+
// Adds event handlers to the new dropdown list
|
667
|
+
_eventHandlers: function() {
|
668
|
+
|
669
|
+
// LOCAL VARIABLES
|
670
|
+
var self = this,
|
671
|
+
|
672
|
+
upKey = 38,
|
673
|
+
|
674
|
+
downKey = 40,
|
675
|
+
|
676
|
+
enterKey = 13,
|
677
|
+
|
678
|
+
backspaceKey = 8,
|
679
|
+
|
680
|
+
tabKey = 9,
|
681
|
+
|
682
|
+
spaceKey = 32,
|
683
|
+
|
684
|
+
escKey = 27;
|
685
|
+
|
686
|
+
// Select Box events
|
687
|
+
this.div.bind({
|
688
|
+
|
689
|
+
// `click` event with the `selectBoxIt` namespace
|
690
|
+
"click.selectBoxIt": function() {
|
691
|
+
|
692
|
+
if(!self.div.is(":focus")) {
|
693
|
+
|
694
|
+
$(this).focus();
|
695
|
+
|
696
|
+
}
|
697
|
+
|
698
|
+
// The `click` handler logic will only be applied if the dropdown list is enabled
|
699
|
+
if (!self.originalElem.disabled) {
|
700
|
+
|
701
|
+
// Triggers the `click` event on the original select box
|
702
|
+
self.selectBox.trigger("click");
|
703
|
+
|
704
|
+
// If the dropdown list options list is visible when a user clicks on the dropdown list
|
705
|
+
if (self.list.is(":visible")) {
|
706
|
+
|
707
|
+
// Closes the dropdown list options list
|
708
|
+
self.close();
|
709
|
+
}
|
710
|
+
|
711
|
+
// If the dropdown list options list is not visible when a user clicks on the dropdown list
|
712
|
+
else {
|
713
|
+
|
714
|
+
// Shows the dropdown list options list
|
715
|
+
self.open();
|
716
|
+
}
|
717
|
+
}
|
718
|
+
},
|
719
|
+
|
720
|
+
// `mousedown` event with the `selectBoxIt` namespace
|
721
|
+
"mousedown.selectBoxIt": function() {
|
722
|
+
|
723
|
+
// Stores data in the jQuery `data` method to help determine if the dropdown list gains focus from a click or tabstop. The mousedown event fires before the focus event.
|
724
|
+
$(this).data("mdown", true);
|
725
|
+
},
|
726
|
+
|
727
|
+
// `blur` event with the `selectBoxIt` namespace. Uses special blur logic to make sure the dropdown list closes correctly
|
728
|
+
"blur.selectBoxIt": function() {
|
729
|
+
|
730
|
+
// If `self.blur` property is true
|
731
|
+
if (self.blur) {
|
732
|
+
|
733
|
+
// Triggers both the `blur` and `focusout` events on the original select box.
|
734
|
+
// The `focusout` event was also triggered because the event bubbles
|
735
|
+
// This event has to be used when using event delegation (such as the jQuery `delegate` or `on` methods)
|
736
|
+
// Popular open source projects such as Backbone.js utilize event delegation to bind events, so if you are using Backbone.js, use the `focusout` event instead of the `blur` event
|
737
|
+
self.selectBox.trigger("blur").trigger("focusout");
|
738
|
+
|
739
|
+
//If the dropdown options list is visible
|
740
|
+
if (self.list.is(":visible")) {
|
741
|
+
//Closes the dropdown list options list
|
742
|
+
self.close();
|
743
|
+
}
|
744
|
+
}
|
745
|
+
},
|
746
|
+
|
747
|
+
"focus.selectBoxIt": function() {
|
748
|
+
|
749
|
+
// Stores the data associated with the mousedown event inside of a local variable
|
750
|
+
var mdown = $(this).data("mdown");
|
751
|
+
|
752
|
+
// Removes the jQuery data associated with the mousedown event
|
753
|
+
$(this).removeData('mdown');
|
754
|
+
|
755
|
+
// If a mousedown event did not occur and no data was passed to the focus event (this correctly triggers the focus event), then the dropdown list gained focus from a tabstop
|
756
|
+
if (!mdown) {
|
757
|
+
|
758
|
+
// Triggers the `tabFocus` custom event on the original select box
|
759
|
+
self.selectBox.trigger("tabFocus");
|
760
|
+
}
|
761
|
+
|
762
|
+
// Only trigger the `focus` event on the original select box if the dropdown list is hidden (this verifies that only the correct `focus` events are used to trigger the event on the original select box
|
763
|
+
if(!self.list.is(":visible")) {
|
764
|
+
|
765
|
+
//Triggers the `focus` default event on the original select box
|
766
|
+
self.selectBox.trigger("focus").trigger("focusin");
|
767
|
+
|
768
|
+
}
|
769
|
+
},
|
770
|
+
|
771
|
+
// `keydown` event with the `selectBoxIt` namespace. Catches all user keyboard navigations
|
772
|
+
"keydown.selectBoxIt": function(e) {
|
773
|
+
|
774
|
+
// Stores the `keycode` value in a local variable
|
775
|
+
var currentKey = e.keyCode;
|
776
|
+
|
777
|
+
// Performs keyboard events if the dropdown list is focused
|
778
|
+
if (self.div.is(":focus")) {
|
779
|
+
|
780
|
+
// Supports keyboard navigation
|
781
|
+
switch (currentKey) {
|
782
|
+
|
783
|
+
// If the user presses the `down key`
|
784
|
+
case downKey:
|
785
|
+
|
786
|
+
// Prevents the page from moving down
|
787
|
+
e.preventDefault();
|
788
|
+
|
789
|
+
// If the plugin options allow keyboard navigation
|
790
|
+
if (self.moveDown) {
|
791
|
+
|
792
|
+
//Opens the dropdown list if it's hidden
|
793
|
+
if (self.list.is(":hidden")) {
|
794
|
+
self.open();
|
795
|
+
}
|
796
|
+
|
797
|
+
// Moves the focus down to the dropdown list option directly beneath the currently selected selectbox option
|
798
|
+
self.moveDown();
|
799
|
+
|
800
|
+
}
|
801
|
+
|
802
|
+
break;
|
803
|
+
|
804
|
+
//If the user presses the `up key`
|
805
|
+
case upKey:
|
806
|
+
|
807
|
+
// Prevents the page from moving up
|
808
|
+
e.preventDefault();
|
809
|
+
|
810
|
+
// If the plugin options allow keyboard navgiation
|
811
|
+
if (self.moveUp) {
|
812
|
+
|
813
|
+
//Opens the dropdown list if it's hidden
|
814
|
+
if (self.list.is(":hidden")) {
|
815
|
+
self.open();
|
816
|
+
}
|
817
|
+
|
818
|
+
// Moves the focus up to the dropdown list option directly above the currently selected selectbox option
|
819
|
+
self.moveUp();
|
820
|
+
|
821
|
+
}
|
822
|
+
|
823
|
+
break;
|
824
|
+
|
825
|
+
// If the user presses the `enter key`
|
826
|
+
case enterKey:
|
827
|
+
|
828
|
+
// Prevents the default event from being triggered
|
829
|
+
e.preventDefault();
|
830
|
+
|
831
|
+
// Checks to see if the dropdown list options list is open
|
832
|
+
if (self.list.is(":visible")) {
|
833
|
+
|
834
|
+
// Closes the dropdown list options list
|
835
|
+
self.close();
|
836
|
+
}
|
837
|
+
|
838
|
+
// If the first dropdown list option is not shown in the options list, and the dropdown list has not been interacted with, then update the dropdown list value when the enter key is pressed
|
839
|
+
if (!self.options.showFirstOption && self.div.text() === self.firstSelectItem.text() && self.currentFocus === 0 || (self.options.showFirstOption && self.options.defaultText) || (!self.options.showFirstOption && !self.listItems.eq(0).not("[data-disabled='true']"))) {
|
840
|
+
|
841
|
+
// Updates the dropdown list value
|
842
|
+
self.selectBox.val(self.listItems.eq(self.currentFocus).attr("data-val")).
|
843
|
+
|
844
|
+
// Triggers a `change` event on the original select box
|
845
|
+
trigger("change");
|
846
|
+
}
|
847
|
+
|
848
|
+
// Triggers the `enter` events on the original select box
|
849
|
+
self.selectBox.trigger("enter");
|
850
|
+
|
851
|
+
break;
|
852
|
+
|
853
|
+
// If the user presses the `tab key`
|
854
|
+
case tabKey:
|
855
|
+
|
856
|
+
// Triggers the custom `tabBlur` events on the original select box
|
857
|
+
self.selectBox.trigger("tabBlur");
|
858
|
+
|
859
|
+
break;
|
860
|
+
|
861
|
+
// If the user presses the `backspace key`
|
862
|
+
case backspaceKey:
|
863
|
+
|
864
|
+
// Prevents the browser from navigating to the previous page in its history
|
865
|
+
e.preventDefault();
|
866
|
+
|
867
|
+
// Triggers the custom `backspace` event on the original select box
|
868
|
+
self.selectBox.trigger("backspace");
|
869
|
+
|
870
|
+
break;
|
871
|
+
|
872
|
+
// If the user presses the `escape key`
|
873
|
+
case escKey:
|
874
|
+
|
875
|
+
// Closes the dropdown options list
|
876
|
+
self.close();
|
877
|
+
|
878
|
+
break;
|
879
|
+
|
880
|
+
// Default is to break out of the switch statement
|
881
|
+
default:
|
882
|
+
|
883
|
+
break;
|
884
|
+
|
885
|
+
}
|
886
|
+
}
|
887
|
+
},
|
888
|
+
|
889
|
+
// `keypress` event with the `selectBoxIt` namespace. Catches all user keyboard text searches since you can only reliably get character codes using the `keypress` event
|
890
|
+
"keypress.selectBoxIt": function(e) {
|
891
|
+
|
892
|
+
// Performs a text search if the dropdown list is focused
|
893
|
+
if (self.div.is(":focus")) {
|
894
|
+
|
895
|
+
// Sets the current key to the `keyCode` value if `charCode` does not exist. Used for cross
|
896
|
+
// browser support since IE uses `keyCode` instead of `charCode`.
|
897
|
+
var currentKey = e.charCode || e.keyCode,
|
898
|
+
|
899
|
+
// Converts unicode values to characters
|
900
|
+
alphaNumericKey = String.fromCharCode(currentKey);
|
901
|
+
|
902
|
+
// If the user presses the `space bar`
|
903
|
+
if (currentKey === spaceKey) {
|
904
|
+
|
905
|
+
// Prevents the browser from scrolling to the bottom of the page
|
906
|
+
e.preventDefault();
|
907
|
+
}
|
908
|
+
|
909
|
+
// If the plugin options allow text searches
|
910
|
+
if (self.search) {
|
911
|
+
|
912
|
+
// Calls `search` and passes the character value of the user's text search
|
913
|
+
self.search(alphaNumericKey, true, "");
|
914
|
+
}
|
915
|
+
}
|
916
|
+
},
|
917
|
+
|
918
|
+
// `mousenter` event with the `selectBoxIt` namespace .The mouseenter JavaScript event is proprietary to Internet Explorer. Because of the event's general utility, jQuery simulates this event so that it can be used regardless of browser.
|
919
|
+
"mouseenter.selectBoxIt": function() {
|
920
|
+
|
921
|
+
// Trigger the `mouseenter` event on the original select box
|
922
|
+
self.selectBox.trigger("mouseenter");
|
923
|
+
},
|
924
|
+
|
925
|
+
// `mouseleave` event with the `selectBoxIt` namespace. The mouseleave JavaScript event is proprietary to Internet Explorer. Because of the event's general utility, jQuery simulates this event so that it can be used regardless of browser.
|
926
|
+
"mouseleave.selectBoxIt": function() {
|
927
|
+
|
928
|
+
// Trigger the `mouseleave` event on the original select box
|
929
|
+
self.selectBox.trigger("mouseleave");
|
930
|
+
}
|
931
|
+
|
932
|
+
});
|
933
|
+
|
934
|
+
// Select box options events that set the dropdown list blur logic (decides when the dropdown list gets
|
935
|
+
// closed)
|
936
|
+
this.list.bind({
|
937
|
+
|
938
|
+
// `mouseover` event with the `selectBoxIt` namespace
|
939
|
+
"mouseover.selectBoxIt": function() {
|
940
|
+
|
941
|
+
// Prevents the dropdown list options list from closing
|
942
|
+
self.blur = false;
|
943
|
+
},
|
944
|
+
|
945
|
+
// `mouseout` event with the `selectBoxIt` namespace
|
946
|
+
"mouseout.selectBoxIt": function() {
|
947
|
+
|
948
|
+
// Allows the dropdown list options list to close
|
949
|
+
self.blur = true;
|
950
|
+
},
|
951
|
+
|
952
|
+
// `focusin` event with the `selectBoxIt` namespace
|
953
|
+
"focusin.selectBoxIt": function() {
|
954
|
+
|
955
|
+
// Prevents the default browser outline border to flicker, which results because of the `blur` event
|
956
|
+
self.div.focus();
|
957
|
+
}
|
958
|
+
|
959
|
+
})
|
960
|
+
|
961
|
+
// Select box individual options events bound with the jQuery `delegate` method. `Delegate` was used because binding individual events to each list item (since we don't know how many there will be) would decrease performance. Instead, we bind each event to the unordered list, provide the list item context, and allow the list item events to bubble up (`event bubbling`). This greatly increases page performance because we only have to bind an event to one element instead of x number of elements. Delegates the `click` event with the `selectBoxIt` namespace to the list items
|
962
|
+
.delegate("li", "click.selectBoxIt", function() {
|
963
|
+
|
964
|
+
if (!$(this).data("disabled")) {
|
965
|
+
|
966
|
+
// Sets the original dropdown list value and triggers the `change` event on the original select box
|
967
|
+
self.originalElem.value = $(this).attr("data-val");
|
968
|
+
|
969
|
+
// Sets `currentFocus` to the currently focused dropdown list option.
|
970
|
+
// The unary `+` operator casts the string to a number
|
971
|
+
// [James Padolsey Blog Post](http://james.padolsey.com/javascript/terse-javascript-101-part-2/)
|
972
|
+
self.currentFocus = +this.id;
|
973
|
+
|
974
|
+
// Closes the list after selecting an option
|
975
|
+
self.close();
|
976
|
+
|
977
|
+
// Triggers the dropdown list `change` event if a value change occurs
|
978
|
+
if (self.originalElem.value !== self.divText.attr("data-val")) {
|
979
|
+
|
980
|
+
self.selectBox.trigger("change");
|
981
|
+
|
982
|
+
}
|
983
|
+
}
|
984
|
+
})
|
985
|
+
|
986
|
+
// Delegates the `focus` event with the `selectBoxIt` namespace to the list items
|
987
|
+
.delegate("li", "focus.selectBoxIt", function() {
|
988
|
+
|
989
|
+
if (!$(this).data("disabled")) {
|
990
|
+
|
991
|
+
// Sets the original select box current value and triggers the change event
|
992
|
+
self.originalElem.value = $(this).attr("data-val");
|
993
|
+
|
994
|
+
// Triggers the dropdown list `change` event if a value change occurs
|
995
|
+
if (self.originalElem.value !== self.divText.attr("data-val")) {
|
996
|
+
|
997
|
+
self.selectBox.trigger("change");
|
998
|
+
|
999
|
+
}
|
1000
|
+
}
|
1001
|
+
});
|
1002
|
+
|
1003
|
+
// Original dropdown list events
|
1004
|
+
this.selectBox.bind({
|
1005
|
+
|
1006
|
+
// `change` event handler with the `selectBoxIt` namespace
|
1007
|
+
"change.selectBoxIt": function() {
|
1008
|
+
|
1009
|
+
// Find list item entry with value of original select
|
1010
|
+
self.currentFocus = self.listItems.index($('li[data-val="' + self.originalElem.value + '"]'));
|
1011
|
+
|
1012
|
+
// Sets the new dropdown list text to the value of the original dropdown list
|
1013
|
+
self.divText.text(self.listItems.eq(self.currentFocus).text()).attr("data-val", self.originalElem.value);
|
1014
|
+
|
1015
|
+
if(self.listItems.eq(self.currentFocus).find("span").attr("class")) {
|
1016
|
+
|
1017
|
+
self.divImage.attr("class", self.listItems.eq(self.currentFocus).find("span").attr("class"));
|
1018
|
+
}
|
1019
|
+
},
|
1020
|
+
|
1021
|
+
// `disable` event with the `selectBoxIt` namespace
|
1022
|
+
"disable.selectBoxIt": function() {
|
1023
|
+
|
1024
|
+
// Adds the `disabled` CSS class to the new dropdown list to visually show that it is disabled
|
1025
|
+
self.div.addClass("ui-state-disabled");
|
1026
|
+
},
|
1027
|
+
|
1028
|
+
// `enable` event with the `selectBoxIt` namespace
|
1029
|
+
"enable.selectBoxIt": function() {
|
1030
|
+
|
1031
|
+
// Removes the `disabled` CSS class from the new dropdown list to visually show that it is enabled
|
1032
|
+
self.div.removeClass("ui-state-disabled");
|
1033
|
+
}
|
1034
|
+
});
|
1035
|
+
|
1036
|
+
// // Binding the original change event to the new custom one
|
1037
|
+
// .on("change", function(e) {
|
1038
|
+
// console.log(e)
|
1039
|
+
// self.selectBoxIt.trigger("change.selectBoxIt");
|
1040
|
+
// });
|
1041
|
+
|
1042
|
+
// Maintains chainability
|
1043
|
+
return this;
|
1044
|
+
},
|
1045
|
+
|
1046
|
+
// _addClasses
|
1047
|
+
// ---------
|
1048
|
+
// Adds SelectBoxIt CSS classes
|
1049
|
+
_addClasses: function() {
|
1050
|
+
|
1051
|
+
var self = this,
|
1052
|
+
|
1053
|
+
focusClass = "selectboxit-focus",
|
1054
|
+
|
1055
|
+
hoverClass = "selectboxit-hover";
|
1056
|
+
|
1057
|
+
this.downArrow.addClass(this.selectBox.data("downarrow") || this.options.downArrowIcon || "");
|
1058
|
+
|
1059
|
+
// Adds the default class to the dropdown list
|
1060
|
+
this.div.addClass("selectboxit-widget");
|
1061
|
+
|
1062
|
+
// Adds the default styling for the dropdown list options
|
1063
|
+
this.list.addClass("selectboxit-widget selectboxit-widget-content");
|
1064
|
+
|
1065
|
+
// Select box individual option events
|
1066
|
+
this.listItems.bind({
|
1067
|
+
|
1068
|
+
// `focus` event with the `selectBoxIt` namespace
|
1069
|
+
"focus.selectBoxIt": function() {
|
1070
|
+
|
1071
|
+
// Adds the focus CSS class to the currently focused dropdown list option
|
1072
|
+
$(this).addClass(focusClass);
|
1073
|
+
|
1074
|
+
},
|
1075
|
+
|
1076
|
+
// `blur` event with the `selectBoxIt` namespace
|
1077
|
+
"blur.selectBoxIt": function() {
|
1078
|
+
|
1079
|
+
// Removes the focus CSS class from the previously focused dropdown list option
|
1080
|
+
$(this).removeClass(focusClass);
|
1081
|
+
|
1082
|
+
}
|
1083
|
+
|
1084
|
+
});
|
1085
|
+
|
1086
|
+
// Select box events
|
1087
|
+
this.selectBox.bind({
|
1088
|
+
|
1089
|
+
// `click` event with the `selectBoxIt` namespace
|
1090
|
+
"open.selectBoxIt": function() {
|
1091
|
+
|
1092
|
+
// Removes the jQueryUI hover class from the dropdown list and adds the jQueryUI focus class for both the dropdown list and the currently selected dropdown list option
|
1093
|
+
self.div.removeClass(hoverClass).add(self.listItems.eq(self.currentFocus)).
|
1094
|
+
|
1095
|
+
addClass(focusClass);
|
1096
|
+
},
|
1097
|
+
|
1098
|
+
"blur.selectBoxIt": function() {
|
1099
|
+
|
1100
|
+
self.div.removeClass(focusClass);
|
1101
|
+
|
1102
|
+
},
|
1103
|
+
|
1104
|
+
// `mousenter` event with the `selectBoxIt` namespace
|
1105
|
+
"mouseenter.selectBoxIt": function() {
|
1106
|
+
|
1107
|
+
self.div.addClass(hoverClass);
|
1108
|
+
|
1109
|
+
},
|
1110
|
+
|
1111
|
+
// `mouseleave` event with the `selectBoxIt` namespace
|
1112
|
+
"mouseleave.selectBoxIt": function() {
|
1113
|
+
|
1114
|
+
// Removes the hover CSS class on the previously hovered dropdown list option
|
1115
|
+
self.div.removeClass(hoverClass);
|
1116
|
+
|
1117
|
+
}
|
1118
|
+
|
1119
|
+
});
|
1120
|
+
|
1121
|
+
this.listItems.bind({
|
1122
|
+
|
1123
|
+
"mouseenter.selectBoxIt": function() {
|
1124
|
+
|
1125
|
+
// Sets the dropdown list individual options back to the default state and sets the hover CSS class on the currently hovered option
|
1126
|
+
self.listItems.removeClass(focusClass);
|
1127
|
+
|
1128
|
+
$(this).addClass(hoverClass);
|
1129
|
+
|
1130
|
+
},
|
1131
|
+
|
1132
|
+
"mouseleave.selectBoxIt": function() {
|
1133
|
+
|
1134
|
+
$(this).removeClass(hoverClass);
|
1135
|
+
|
1136
|
+
}
|
1137
|
+
|
1138
|
+
});
|
1139
|
+
|
1140
|
+
// Maintains chainability
|
1141
|
+
return this;
|
1142
|
+
|
1143
|
+
}
|
1144
|
+
|
1145
|
+
});
|
1146
|
+
|
1147
|
+
})); // End of core module
|
1148
|
+
$(function() {
|
1149
|
+
|
1150
|
+
//_ARIA Accessibility
|
1151
|
+
// ------------------
|
1152
|
+
// Adds ARIA (Accessible Rich Internet Applications)
|
1153
|
+
// Accessibility Tags to the Select Box
|
1154
|
+
|
1155
|
+
$.selectBox.selectBoxIt.prototype._ariaAccessibility = function() {
|
1156
|
+
|
1157
|
+
var self = this;
|
1158
|
+
|
1159
|
+
//Adds `ARIA attributes` to the dropdown list
|
1160
|
+
this.div.attr({
|
1161
|
+
|
1162
|
+
//W3C `combobox` description: A presentation of a select; usually similar to a textbox where users can type ahead to select an option.
|
1163
|
+
"role": "combobox",
|
1164
|
+
|
1165
|
+
//W3C `aria-autocomplete` description: Indicates whether user input completion suggestions are provided.
|
1166
|
+
"aria-autocomplete": "list",
|
1167
|
+
|
1168
|
+
//W3C `aria-expanded` description: Indicates whether the element, or another grouping element it controls, is currently expanded or collapsed.
|
1169
|
+
"aria-expanded": "false",
|
1170
|
+
|
1171
|
+
//W3C `aria-owns` description: The value of the aria-owns attribute is a space-separated list of IDREFS that reference one or more elements in the document by ID. The reason for adding aria-owns is to expose a parent/child contextual relationship to assistive technologies that is otherwise impossible to infer from the DOM.
|
1172
|
+
"aria-owns": this.list.attr("id"),
|
1173
|
+
|
1174
|
+
//W3C `aria-activedescendant` description: This is used when a composite widget is responsible for managing its current active child to reduce the overhead of having all children be focusable. Examples include: multi-level lists, trees, and grids.
|
1175
|
+
"aria-activedescendant": this.listItems.eq(this.currentFocus).attr("id"),
|
1176
|
+
|
1177
|
+
//W3C `aria-label` description: It provides the user with a recognizable name of the object.
|
1178
|
+
"aria-label": $("label[for='" + this.originalElem.id + "']").text() || "",
|
1179
|
+
|
1180
|
+
//W3C `aria-live` description: Indicates that an element will be updated.
|
1181
|
+
//Use the assertive value when the update needs to be communicated to the user more urgently.
|
1182
|
+
"aria-live": "assertive"
|
1183
|
+
}).
|
1184
|
+
|
1185
|
+
//Dynamically adds `ARIA attributes` if the new dropdown list is enabled or disabled
|
1186
|
+
bind({
|
1187
|
+
|
1188
|
+
//Select box custom `disable` event with the `selectBoxIt` namespace
|
1189
|
+
"disable.selectBoxIt" : function() {
|
1190
|
+
|
1191
|
+
//W3C `aria-disabled` description: Indicates that the element is perceivable but disabled, so it is not editable or otherwise operable.
|
1192
|
+
self.div.attr("aria-disabled", "true");
|
1193
|
+
|
1194
|
+
},
|
1195
|
+
|
1196
|
+
//Select box custom `enable` event with the `selectBoxIt` namespace
|
1197
|
+
"enable.selectBoxIt" : function() {
|
1198
|
+
|
1199
|
+
//W3C `aria-disabled` description: Indicates that the element is perceivable but disabled, so it is not editable or otherwise operable.
|
1200
|
+
self.div.attr("aria-disabled", "false");
|
1201
|
+
|
1202
|
+
}
|
1203
|
+
|
1204
|
+
});
|
1205
|
+
|
1206
|
+
//Adds ARIA attributes to the dropdown list options list
|
1207
|
+
self.list.attr({
|
1208
|
+
|
1209
|
+
//W3C `listbox` description: A widget that allows the user to select one or more items from a list of choices.
|
1210
|
+
"role": "listbox",
|
1211
|
+
|
1212
|
+
//Indicates that the dropdown list options list is currently hidden
|
1213
|
+
"aria-hidden": "true"
|
1214
|
+
});
|
1215
|
+
|
1216
|
+
//Adds `ARIA attributes` to the dropdown list options
|
1217
|
+
self.listItems.attr({
|
1218
|
+
|
1219
|
+
//This must be set for each element when the container element role is set to `listbox`
|
1220
|
+
"role": "option"
|
1221
|
+
});
|
1222
|
+
|
1223
|
+
//Dynamically updates the new dropdown list `aria-label` attribute after the original dropdown list value changes
|
1224
|
+
self.selectBox.bind({
|
1225
|
+
|
1226
|
+
//Custom `change` event with the `selectBoxIt` namespace
|
1227
|
+
"change.selectBoxIt": function() {
|
1228
|
+
|
1229
|
+
//Provides the user with a recognizable name of the object.
|
1230
|
+
self.divText.attr("aria-label", self.originalElem.value);
|
1231
|
+
|
1232
|
+
},
|
1233
|
+
|
1234
|
+
//Custom `open` event with the `selectBoxIt` namespace
|
1235
|
+
"open.selectBoxIt": function() {
|
1236
|
+
|
1237
|
+
//Indicates that the dropdown list options list is currently visible
|
1238
|
+
self.list.attr("aria-hidden", "false");
|
1239
|
+
|
1240
|
+
//Indicates that the dropdown list is currently expanded
|
1241
|
+
self.div.attr("aria-expanded", "true");
|
1242
|
+
|
1243
|
+
},
|
1244
|
+
|
1245
|
+
//Custom `close` event with the `selectBoxIt` namespace
|
1246
|
+
"close.selectBoxIt": function() {
|
1247
|
+
|
1248
|
+
//Indicates that the dropdown list options list is currently hidden
|
1249
|
+
self.list.attr("aria-hidden", "true");
|
1250
|
+
|
1251
|
+
//Indicates that the dropdown list is currently collapsed
|
1252
|
+
self.div.attr("aria-expanded", "false");
|
1253
|
+
|
1254
|
+
}
|
1255
|
+
|
1256
|
+
});
|
1257
|
+
|
1258
|
+
//Maintains chainability
|
1259
|
+
return this;
|
1260
|
+
|
1261
|
+
};
|
1262
|
+
|
1263
|
+
});
|
1264
|
+
$(function() {
|
1265
|
+
|
1266
|
+
//Destroy
|
1267
|
+
// ------
|
1268
|
+
// Delays execution by the amount of time
|
1269
|
+
// specified by the parameter
|
1270
|
+
|
1271
|
+
$.selectBox.selectBoxIt.prototype.destroy = function(callback) {
|
1272
|
+
|
1273
|
+
//Unbinds all of the dropdown list event handlers with the `selectBoxIt` namespace
|
1274
|
+
this.div.unbind(".selectBoxIt").
|
1275
|
+
|
1276
|
+
//Undelegates all of the dropdown list event handlers with the `selectBoxIt` namespace
|
1277
|
+
undelegate(".selectBoxIt");
|
1278
|
+
|
1279
|
+
//Remove all of the `selectBoxIt` DOM elements from the page
|
1280
|
+
this.divContainer.remove();
|
1281
|
+
|
1282
|
+
//Triggers the custom `destroy` event on the original select box and then shows the original dropdown list
|
1283
|
+
this.selectBox.trigger("destroy").show();
|
1284
|
+
|
1285
|
+
// Calls the jQueryUI Widget Factory destroy method
|
1286
|
+
$.Widget.prototype.destroy.call(this);
|
1287
|
+
|
1288
|
+
//Provides callback function support
|
1289
|
+
this._callbackSupport(callback);
|
1290
|
+
|
1291
|
+
//Maintains chainability
|
1292
|
+
return this;
|
1293
|
+
|
1294
|
+
};
|
1295
|
+
|
1296
|
+
});
|
1297
|
+
$(function() {
|
1298
|
+
|
1299
|
+
//Disable
|
1300
|
+
// ------
|
1301
|
+
// Disables the new dropdown list
|
1302
|
+
|
1303
|
+
$.selectBox.selectBoxIt.prototype.disable = function(callback) {
|
1304
|
+
|
1305
|
+
if(!this.options.disabled) {
|
1306
|
+
|
1307
|
+
//Makes sure the dropdown list is closed
|
1308
|
+
this.close();
|
1309
|
+
|
1310
|
+
//Triggers a `disable` custom event on the original select box
|
1311
|
+
this.selectBox.trigger("disable")
|
1312
|
+
|
1313
|
+
//Sets the `disabled` attribute on the original select box
|
1314
|
+
.attr("disabled", "disabled");
|
1315
|
+
|
1316
|
+
//Makes the dropdown list not focusable by removing the `tabindex` attribute
|
1317
|
+
this.div.removeAttr("tabindex").css("cursor", "default");
|
1318
|
+
|
1319
|
+
// Calls the jQueryUI Widget Factory disable method to make sure all options are correctly synced
|
1320
|
+
$.Widget.prototype.disable.call(this);
|
1321
|
+
|
1322
|
+
//Provides callback function support
|
1323
|
+
this._callbackSupport(callback);
|
1324
|
+
|
1325
|
+
//Maintains chainability
|
1326
|
+
return this;
|
1327
|
+
|
1328
|
+
}
|
1329
|
+
|
1330
|
+
};
|
1331
|
+
|
1332
|
+
//_Is Disabled
|
1333
|
+
// -----------
|
1334
|
+
// Checks the original select box for the
|
1335
|
+
// disabled attribute
|
1336
|
+
|
1337
|
+
$.selectBox.selectBoxIt.prototype._isDisabled = function(callback) {
|
1338
|
+
|
1339
|
+
//If the original select box is disabled
|
1340
|
+
if (this.originalElem.disabled) {
|
1341
|
+
|
1342
|
+
//Disables the dropdown list
|
1343
|
+
this.disable();
|
1344
|
+
}
|
1345
|
+
|
1346
|
+
//Maintains chainability
|
1347
|
+
return this;
|
1348
|
+
|
1349
|
+
};
|
1350
|
+
|
1351
|
+
});
|
1352
|
+
$(function() {
|
1353
|
+
|
1354
|
+
//_Dynamic positioning
|
1355
|
+
// ------------------
|
1356
|
+
// Dynamically positions the dropdown list options list
|
1357
|
+
|
1358
|
+
$.selectBox.selectBoxIt.prototype._dynamicPositioning = function() {
|
1359
|
+
|
1360
|
+
//Returns the x and y coordinates of the dropdown list options list relative to the document
|
1361
|
+
var listOffsetTop = this.div.offset().top,
|
1362
|
+
|
1363
|
+
//The height of the dropdown list options list
|
1364
|
+
listHeight = this.list.height(),
|
1365
|
+
|
1366
|
+
//The height of the dropdown list DOM element
|
1367
|
+
selectBoxHeight = this.div.height();
|
1368
|
+
|
1369
|
+
//Places the dropdown list options list on top of the dropdown list if the dropdown list options list does not fit on the page when opened
|
1370
|
+
if ((listOffsetTop + selectBoxHeight + listHeight >= $(window).height() + $(window).scrollTop()) && (listOffsetTop - listHeight >= 0)) {
|
1371
|
+
|
1372
|
+
//If the dropdown list currently opens downward
|
1373
|
+
if (!this.flipped) {
|
1374
|
+
|
1375
|
+
//Sets custom CSS properties to place the dropdown list options directly above the dropdown list
|
1376
|
+
this.list.css("top", (this.divContainer.position().top - this.list.height()) - 2);
|
1377
|
+
|
1378
|
+
//Sets the `flipped` instance variable to false to reflect that the dropdown list opens upward
|
1379
|
+
this.flipped = true;
|
1380
|
+
|
1381
|
+
}
|
1382
|
+
|
1383
|
+
}
|
1384
|
+
|
1385
|
+
//If the dropdown list options have enough room on the page to open downward
|
1386
|
+
else {
|
1387
|
+
|
1388
|
+
//If the dropdown list is currently opening upward
|
1389
|
+
if (this.flipped) {
|
1390
|
+
|
1391
|
+
//Sets custom CSS properties to place the dropdown list options directly below the dropdown list
|
1392
|
+
this.list.css("top", (this.divContainer.position().top + this.div.height()) + 2);
|
1393
|
+
|
1394
|
+
//Sets the `flipped` instance variable to false to reflect that the dropdown list opens downward
|
1395
|
+
this.flipped = false;
|
1396
|
+
|
1397
|
+
}
|
1398
|
+
|
1399
|
+
}
|
1400
|
+
|
1401
|
+
};
|
1402
|
+
|
1403
|
+
});
|
1404
|
+
$(function() {
|
1405
|
+
|
1406
|
+
//Enable
|
1407
|
+
// -----
|
1408
|
+
// Enables the new dropdown list
|
1409
|
+
|
1410
|
+
$.selectBox.selectBoxIt.prototype.enable = function(callback) {
|
1411
|
+
|
1412
|
+
if(this.options.disabled) {
|
1413
|
+
|
1414
|
+
//Triggers a `enable` custom event on the original select box
|
1415
|
+
this.selectBox.trigger("enable")
|
1416
|
+
|
1417
|
+
//Removes the `disabled` attribute from the original dropdown list
|
1418
|
+
.removeAttr("disabled");
|
1419
|
+
|
1420
|
+
//Make the dropdown list focusable
|
1421
|
+
this.div.attr("tabindex", 0).css("cursor", "pointer");
|
1422
|
+
|
1423
|
+
$.Widget.prototype.enable.call(this);
|
1424
|
+
|
1425
|
+
//Provide callback function support
|
1426
|
+
this._callbackSupport(callback);
|
1427
|
+
|
1428
|
+
}
|
1429
|
+
|
1430
|
+
//Maintains chainability
|
1431
|
+
return this;
|
1432
|
+
|
1433
|
+
};
|
1434
|
+
|
1435
|
+
});
|
1436
|
+
$(function() {
|
1437
|
+
|
1438
|
+
//Move Down
|
1439
|
+
// --------
|
1440
|
+
// Handles the down keyboard navigation logic
|
1441
|
+
|
1442
|
+
$.selectBox.selectBoxIt.prototype.moveDown = function(callback) {
|
1443
|
+
|
1444
|
+
//Increments `currentFocus`, which represents the currently focused list item `id` attribute.
|
1445
|
+
this.currentFocus += 1;
|
1446
|
+
|
1447
|
+
//Determines whether the dropdown option the user is trying to go to is currently disabled
|
1448
|
+
var disabled = this.listItems.eq(this.currentFocus).data("disabled"),
|
1449
|
+
|
1450
|
+
hasNextEnabled = this.listItems.eq(this.currentFocus).nextAll("li").not("[data-disabled='true']").first().length;
|
1451
|
+
|
1452
|
+
//If the user has reached the top of the list
|
1453
|
+
if (this.currentFocus === this.listItems.length) {
|
1454
|
+
|
1455
|
+
//Does not allow the user to continue to go up the list
|
1456
|
+
this.currentFocus -= 1;
|
1457
|
+
|
1458
|
+
}
|
1459
|
+
|
1460
|
+
//If the option the user is trying to go to is disabled, but there is another enabled option
|
1461
|
+
else if (disabled && hasNextEnabled) {
|
1462
|
+
|
1463
|
+
//Blur the previously selected option
|
1464
|
+
this.listItems.eq(this.currentFocus - 1).blur();
|
1465
|
+
|
1466
|
+
//Call the `moveDown` method again
|
1467
|
+
this.moveDown();
|
1468
|
+
|
1469
|
+
//Exit the method
|
1470
|
+
return;
|
1471
|
+
|
1472
|
+
}
|
1473
|
+
|
1474
|
+
//If the option the user is trying to go to is disabled, but there is not another enabled option
|
1475
|
+
else if (disabled && !hasNextEnabled) {
|
1476
|
+
|
1477
|
+
this.currentFocus -= 1;
|
1478
|
+
|
1479
|
+
}
|
1480
|
+
|
1481
|
+
//If the user has not reached the bottom of the unordered list
|
1482
|
+
else {
|
1483
|
+
|
1484
|
+
//Blurs the previously focused list item
|
1485
|
+
//The jQuery `end()` method allows you to continue chaining while also using a different selector
|
1486
|
+
this.listItems.eq(this.currentFocus - 1).blur().end().
|
1487
|
+
|
1488
|
+
//Focuses the currently focused list item
|
1489
|
+
eq(this.currentFocus).focus();
|
1490
|
+
|
1491
|
+
//Calls `scrollToView` to make sure the `scrollTop` is correctly updated. The `down` user action
|
1492
|
+
this._scrollToView("down");
|
1493
|
+
|
1494
|
+
//Triggers the custom `moveDown` event on the original select box
|
1495
|
+
this.selectBox.trigger("moveDown");
|
1496
|
+
|
1497
|
+
}
|
1498
|
+
|
1499
|
+
//Provide callback function support
|
1500
|
+
this._callbackSupport(callback);
|
1501
|
+
|
1502
|
+
//Maintains chainability
|
1503
|
+
return this;
|
1504
|
+
};
|
1505
|
+
|
1506
|
+
//Move Up
|
1507
|
+
// ------
|
1508
|
+
// Handles the up keyboard navigation logic
|
1509
|
+
$.selectBox.selectBoxIt.prototype.moveUp = function(callback) {
|
1510
|
+
|
1511
|
+
//Increments `currentFocus`, which represents the currently focused list item `id` attribute.
|
1512
|
+
this.currentFocus -= 1;
|
1513
|
+
|
1514
|
+
//Determines whether the dropdown option the user is trying to go to is currently disabled
|
1515
|
+
var disabled = this.listItems.eq(this.currentFocus).data("disabled"),
|
1516
|
+
|
1517
|
+
hasPreviousEnabled = this.listItems.eq(this.currentFocus).prevAll("li").not("[data-disabled='true']").first().length;
|
1518
|
+
|
1519
|
+
//If the user has reached the top of the list
|
1520
|
+
if (this.currentFocus === -1) {
|
1521
|
+
|
1522
|
+
//Does not allow the user to continue to go up the list
|
1523
|
+
this.currentFocus += 1;
|
1524
|
+
}
|
1525
|
+
|
1526
|
+
//If the option the user is trying to go to is disabled and the user is not trying to go up after the user has reached the top of the list
|
1527
|
+
else if (disabled && hasPreviousEnabled) {
|
1528
|
+
|
1529
|
+
//Blur the previously selected option
|
1530
|
+
this.listItems.eq(this.currentFocus + 1).blur();
|
1531
|
+
|
1532
|
+
//Call the `moveUp` method again
|
1533
|
+
this.moveUp();
|
1534
|
+
|
1535
|
+
//Exit the method
|
1536
|
+
return;
|
1537
|
+
}
|
1538
|
+
|
1539
|
+
else if (disabled && !hasPreviousEnabled) {
|
1540
|
+
|
1541
|
+
this.currentFocus += 1;
|
1542
|
+
|
1543
|
+
}
|
1544
|
+
|
1545
|
+
//If the user has not reached the top of the unordered list
|
1546
|
+
else {
|
1547
|
+
|
1548
|
+
//Blurs the previously focused list item
|
1549
|
+
//The jQuery `end()` method allows you to continue chaining while also using a different selector
|
1550
|
+
this.listItems.eq(this.currentFocus + 1).blur().end().
|
1551
|
+
|
1552
|
+
//Focuses the currently focused list item
|
1553
|
+
eq(this.currentFocus).focus();
|
1554
|
+
|
1555
|
+
//Calls `scrollToView` to make sure the `scrollTop` is correctly updated. The `down` user action
|
1556
|
+
this._scrollToView("up");
|
1557
|
+
|
1558
|
+
//Triggers the custom `moveDown` event on the original select box
|
1559
|
+
this.selectBox.trigger("moveUp");
|
1560
|
+
|
1561
|
+
}
|
1562
|
+
|
1563
|
+
//Provide callback function support
|
1564
|
+
this._callbackSupport(callback);
|
1565
|
+
|
1566
|
+
//Maintains chainability
|
1567
|
+
return this;
|
1568
|
+
};
|
1569
|
+
|
1570
|
+
});
|
1571
|
+
$(function() {
|
1572
|
+
|
1573
|
+
// _Set Current Search Option
|
1574
|
+
// -------------------------
|
1575
|
+
// Sets the currently selected dropdown list search option
|
1576
|
+
|
1577
|
+
$.selectBox.selectBoxIt.prototype._setCurrentSearchOption = function(currentOption) {
|
1578
|
+
|
1579
|
+
// Does not change the current option if `showFirstOption` is false and the matched search item is the hidden first option.
|
1580
|
+
// Otherwise, the current option value is updated
|
1581
|
+
if (!(currentOption === 0 && !this.options.showFirstOption) && this.listItems.eq(currentOption).data("disabled") !== true) {
|
1582
|
+
|
1583
|
+
//Updates the default dropdown list text
|
1584
|
+
this.divText.text(this.textArray[currentOption]);
|
1585
|
+
|
1586
|
+
//Calls the `blur` event of the currently selected dropdown list option
|
1587
|
+
this.listItems.eq(this.currentFocus).blur();
|
1588
|
+
|
1589
|
+
//Sets `currentIndex` to the currently selected dropdown list option
|
1590
|
+
this.currentIndex = currentOption;
|
1591
|
+
|
1592
|
+
//Sets `currentFocus` to the currently selected dropdown list option
|
1593
|
+
this.currentFocus = currentOption;
|
1594
|
+
|
1595
|
+
//Focuses the currently selected dropdown list option
|
1596
|
+
this.listItems.eq(this.currentFocus).focus();
|
1597
|
+
|
1598
|
+
//Updates the scrollTop so that the currently selected dropdown list option is visible to the user
|
1599
|
+
this._scrollToView("search");
|
1600
|
+
|
1601
|
+
//Triggers the custom `search` event on the original select box
|
1602
|
+
this.selectBox.trigger("search");
|
1603
|
+
|
1604
|
+
}
|
1605
|
+
|
1606
|
+
//Maintains chainability
|
1607
|
+
return this;
|
1608
|
+
|
1609
|
+
};
|
1610
|
+
|
1611
|
+
// _Search Algorithm
|
1612
|
+
// -----------------
|
1613
|
+
// Uses regular expressions to find text matches
|
1614
|
+
$.selectBox.selectBoxIt.prototype._searchAlgorithm = function(currentIndex, alphaNumeric) {
|
1615
|
+
|
1616
|
+
// Boolean to determine if a pattern match exists
|
1617
|
+
var matchExists = false,
|
1618
|
+
|
1619
|
+
//Iteration variable used in the outermost for loop
|
1620
|
+
x,
|
1621
|
+
|
1622
|
+
//Iteration variable used in the nested for loop
|
1623
|
+
y,
|
1624
|
+
|
1625
|
+
//Variable used to cache the length of the text array (Small enhancement to speed up traversing)
|
1626
|
+
arrayLength;
|
1627
|
+
|
1628
|
+
//Loops through the text array to find a pattern match
|
1629
|
+
for (x = currentIndex, arrayLength = this.textArray.length; x < arrayLength; x += 1) {
|
1630
|
+
|
1631
|
+
//Nested for loop to help search for a pattern match with the currently traversed array item
|
1632
|
+
for (y = 0; y < arrayLength; y += 1) {
|
1633
|
+
|
1634
|
+
//Searches for a match
|
1635
|
+
if (this.textArray[y].search(alphaNumeric) !== -1) {
|
1636
|
+
|
1637
|
+
//`matchExists` is set to true if there is a match
|
1638
|
+
matchExists = true;
|
1639
|
+
|
1640
|
+
//Exits the nested for loop
|
1641
|
+
y = arrayLength;
|
1642
|
+
|
1643
|
+
}
|
1644
|
+
|
1645
|
+
} //End nested for loop
|
1646
|
+
|
1647
|
+
//If a match does not exist
|
1648
|
+
if (!matchExists) {
|
1649
|
+
|
1650
|
+
//Sets the current text to the last entered character
|
1651
|
+
this.currentText = this.currentText.charAt(this.currentText.length - 1).
|
1652
|
+
|
1653
|
+
//Escapes the regular expression to make sure special characters are seen as literal characters instead of special commands
|
1654
|
+
replace(/[|()\[{.+*?$\\]/g, "\\$0");
|
1655
|
+
|
1656
|
+
//Resets the regular expression with the new value of `self.currentText`
|
1657
|
+
alphaNumeric = new RegExp(this.currentText, "gi");
|
1658
|
+
|
1659
|
+
}
|
1660
|
+
|
1661
|
+
//Searches based on the first letter of the dropdown list options text if the currentText < 2 characters
|
1662
|
+
if (this.currentText.length < 2) {
|
1663
|
+
|
1664
|
+
//If there is a match based on the first character
|
1665
|
+
if ((this.textArray[x].charAt(0).search(alphaNumeric) !== -1)) {
|
1666
|
+
|
1667
|
+
//Sets properties of that dropdown list option to make it the currently selected option
|
1668
|
+
this._setCurrentSearchOption(x);
|
1669
|
+
|
1670
|
+
//Increments the current index by one
|
1671
|
+
this.currentIndex += 1;
|
1672
|
+
|
1673
|
+
//Exits the search
|
1674
|
+
return false;
|
1675
|
+
|
1676
|
+
}
|
1677
|
+
}
|
1678
|
+
|
1679
|
+
// If `self.currentText` > 1 character
|
1680
|
+
else {
|
1681
|
+
|
1682
|
+
// If there is a match based on the entire string
|
1683
|
+
if ((this.textArray[x].search(alphaNumeric) !== -1)) {
|
1684
|
+
|
1685
|
+
// Sets properties of that dropdown list option to make it the currently selected option
|
1686
|
+
this._setCurrentSearchOption(x);
|
1687
|
+
|
1688
|
+
// Exits the search
|
1689
|
+
return false;
|
1690
|
+
}
|
1691
|
+
}
|
1692
|
+
|
1693
|
+
// If the current text search is an exact match
|
1694
|
+
if (this.textArray[x].toLowerCase() === this.currentText.toLowerCase()) {
|
1695
|
+
|
1696
|
+
// Sets properties of that dropdown list option to make it the currently selected option
|
1697
|
+
this._setCurrentSearchOption(x);
|
1698
|
+
|
1699
|
+
// Resets the current text search to a blank string to start fresh again
|
1700
|
+
this.currentText = "";
|
1701
|
+
|
1702
|
+
// Exits the search
|
1703
|
+
return false;
|
1704
|
+
|
1705
|
+
}
|
1706
|
+
}
|
1707
|
+
|
1708
|
+
//Returns true if there is not a match at all
|
1709
|
+
return true;
|
1710
|
+
};
|
1711
|
+
|
1712
|
+
// Search
|
1713
|
+
// ------
|
1714
|
+
// Calls searchAlgorithm()
|
1715
|
+
$.selectBox.selectBoxIt.prototype.search = function(alphaNumericKey, rememberPreviousSearch, callback) {
|
1716
|
+
|
1717
|
+
// If the search method is being called internally by the plugin, and not externally as a method by a user
|
1718
|
+
if (rememberPreviousSearch) {
|
1719
|
+
|
1720
|
+
// Continued search with history from past searches. Properly escapes the regular expression
|
1721
|
+
this.currentText += alphaNumericKey.replace(/[|()\[{.+*?$\\]/g, "\\$0");
|
1722
|
+
|
1723
|
+
}
|
1724
|
+
|
1725
|
+
else {
|
1726
|
+
|
1727
|
+
// Brand new search. Properly escapes the regular expression
|
1728
|
+
this.currentText = alphaNumericKey.replace(/[|()\[{.+*?$\\]/g, "\\$0");
|
1729
|
+
|
1730
|
+
}
|
1731
|
+
|
1732
|
+
// Wraps the current user text search in a regular expression that is case insensitive and searches globally
|
1733
|
+
var alphaNumeric = new RegExp(this.currentText, "gi"),
|
1734
|
+
|
1735
|
+
// Calls `searchAlgorithm` which searches an array that contains all of the dropdown list option values.
|
1736
|
+
notFound = this._searchAlgorithm(this.currentIndex, alphaNumeric);
|
1737
|
+
|
1738
|
+
// Searches the list again if a match is not found. This is needed, because the first search started at the array indece of the currently selected dropdown list option, and does not search the options before the current array indece.
|
1739
|
+
// If there are many similar dropdown list options, starting the search at the indece of the currently selected dropdown list option is needed to properly traverse the text array.
|
1740
|
+
if (notFound) {
|
1741
|
+
|
1742
|
+
// Searches the dropdown list values starting from the beginning of the text array
|
1743
|
+
this._searchAlgorithm(0, alphaNumeric);
|
1744
|
+
|
1745
|
+
}
|
1746
|
+
|
1747
|
+
// Provide callback function support
|
1748
|
+
this._callbackSupport(callback);
|
1749
|
+
|
1750
|
+
// Maintains chainability
|
1751
|
+
return this;
|
1752
|
+
|
1753
|
+
};
|
1754
|
+
|
1755
|
+
});
|
1756
|
+
$(function() {
|
1757
|
+
|
1758
|
+
//Set Option
|
1759
|
+
// ----------
|
1760
|
+
// Accepts an string key, a value, and a callback function to replace a single
|
1761
|
+
// property of the plugin options object
|
1762
|
+
|
1763
|
+
$.selectBox.selectBoxIt.prototype.setOption = function(key, value, callback) {
|
1764
|
+
|
1765
|
+
//If a user sets the `showFirstOption` to false
|
1766
|
+
if (key === "showFirstOption" && !value) {
|
1767
|
+
|
1768
|
+
//Hides the first option in the dropdown list
|
1769
|
+
this.listItems.eq(0).hide();
|
1770
|
+
|
1771
|
+
}
|
1772
|
+
|
1773
|
+
//If a user sets the `showFirstOption` to true
|
1774
|
+
else if (key === "showFirstOption" && value) {
|
1775
|
+
|
1776
|
+
//Shows the first option in the dropdown list
|
1777
|
+
this.listItems.eq(0).show();
|
1778
|
+
|
1779
|
+
}
|
1780
|
+
|
1781
|
+
else if(key === "defaultIcon" && value) {
|
1782
|
+
|
1783
|
+
this.divImage.attr("class", value);
|
1784
|
+
|
1785
|
+
}
|
1786
|
+
|
1787
|
+
else if(key === "downArrowIcon" && value) {
|
1788
|
+
|
1789
|
+
this.downArrow.attr("class", value);
|
1790
|
+
|
1791
|
+
}
|
1792
|
+
|
1793
|
+
//If a user sets the defaultText option
|
1794
|
+
else if (key === "defaultText") {
|
1795
|
+
|
1796
|
+
//Sets the new dropdown list default text
|
1797
|
+
this.divText.text(value);
|
1798
|
+
|
1799
|
+
}
|
1800
|
+
|
1801
|
+
$.Widget.prototype._setOption.apply(this, arguments);
|
1802
|
+
|
1803
|
+
//Provides callback function support
|
1804
|
+
this._callbackSupport(callback);
|
1805
|
+
|
1806
|
+
//Maintains chainability
|
1807
|
+
return this;
|
1808
|
+
};
|
1809
|
+
|
1810
|
+
});
|
1811
|
+
$(function() {
|
1812
|
+
|
1813
|
+
//Set Options
|
1814
|
+
// ----------
|
1815
|
+
// Accepts an object to replace plugin options
|
1816
|
+
// properties of the plugin options object
|
1817
|
+
|
1818
|
+
$.selectBox.selectBoxIt.prototype.setOptions = function(newOptions, callback) {
|
1819
|
+
|
1820
|
+
$.Widget.prototype._setOptions.apply(this, arguments);
|
1821
|
+
|
1822
|
+
//If the `showFirstOption` option is true
|
1823
|
+
if (this.options.showFirstOption) {
|
1824
|
+
|
1825
|
+
//Shows the first option in the dropdown list
|
1826
|
+
this.listItems.eq(0).show();
|
1827
|
+
|
1828
|
+
}
|
1829
|
+
|
1830
|
+
//If the `showFirstOption` option is false
|
1831
|
+
else {
|
1832
|
+
|
1833
|
+
//Hides the first option in the dropdown list
|
1834
|
+
this.listItems.eq(0).hide();
|
1835
|
+
|
1836
|
+
}
|
1837
|
+
|
1838
|
+
if(this.options.defaultIcon) {
|
1839
|
+
|
1840
|
+
this.divImage.attr("class", this.options.defaultIcon);
|
1841
|
+
|
1842
|
+
}
|
1843
|
+
|
1844
|
+
if(this.options.downArrowIcon) {
|
1845
|
+
|
1846
|
+
this.downArrow.attr("class", this.options.downArrowIcon);
|
1847
|
+
|
1848
|
+
}
|
1849
|
+
|
1850
|
+
//If the defaultText option is set, make sure the dropdown list default text reflects this value
|
1851
|
+
if (this.options.defaultText) {
|
1852
|
+
|
1853
|
+
this.divText.text(this.options.defaultText);
|
1854
|
+
|
1855
|
+
}
|
1856
|
+
|
1857
|
+
//Provide callback function support
|
1858
|
+
this._callbackSupport(callback);
|
1859
|
+
|
1860
|
+
return this;
|
1861
|
+
|
1862
|
+
};
|
1863
|
+
|
1864
|
+
});
|
1865
|
+
$(function() {
|
1866
|
+
|
1867
|
+
//Wait
|
1868
|
+
// ---
|
1869
|
+
// Delays execution by the amount of time
|
1870
|
+
// specified by the parameter
|
1871
|
+
|
1872
|
+
$.selectBox.selectBoxIt.prototype.wait = function(time, callback) {
|
1873
|
+
|
1874
|
+
var self = this,
|
1875
|
+
|
1876
|
+
//The timeout variable stores a Deferred Object, which will be resolved after the time specified in the parameter
|
1877
|
+
timeout = this.returnTimeout(time);
|
1878
|
+
|
1879
|
+
//Once the Deferred object is resolved, call the callback function
|
1880
|
+
timeout.then(function() {
|
1881
|
+
|
1882
|
+
//Provide callback function support
|
1883
|
+
self._callbackSupport(callback);
|
1884
|
+
|
1885
|
+
});
|
1886
|
+
|
1887
|
+
//Maintains chainability
|
1888
|
+
return this;
|
1889
|
+
|
1890
|
+
};
|
1891
|
+
|
1892
|
+
//Return timeout
|
1893
|
+
// -------------
|
1894
|
+
// Returns a Deferred Object after the time
|
1895
|
+
// specified by the parameter
|
1896
|
+
|
1897
|
+
$.selectBox.selectBoxIt.prototype.returnTimeout = function(time) {
|
1898
|
+
|
1899
|
+
//Returns a Deferred Object
|
1900
|
+
return $.Deferred(function(dfd) {
|
1901
|
+
|
1902
|
+
//Call the JavaScript `setTimeout function and resolve the Deferred Object
|
1903
|
+
setTimeout(dfd.resolve, time);
|
1904
|
+
|
1905
|
+
});
|
1906
|
+
|
1907
|
+
};
|
1908
|
+
|
1909
|
+
});
|