event_calendar_engine 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +23 -0
- data/Gemfile.lock +161 -0
- data/README +1 -0
- data/Rakefile +39 -0
- data/VERSION +1 -0
- data/app/controllers/application_controller.rb +4 -0
- data/app/controllers/attendees_controller.rb +53 -0
- data/app/controllers/event_calendar/application_controller.rb +5 -0
- data/app/controllers/event_revisions_controller.rb +26 -0
- data/app/controllers/events_controller.rb +129 -0
- data/app/helpers/event_calendar/application_helper.rb +118 -0
- data/app/helpers/events_helper.rb +2 -0
- data/app/models/attendee.rb +6 -0
- data/app/models/deletable_instance_methods.rb +7 -0
- data/app/models/event.rb +124 -0
- data/app/models/event_revision.rb +9 -0
- data/app/models/participant.rb +23 -0
- data/app/models/participator.rb +20 -0
- data/app/views/attendees/index.html.erb +14 -0
- data/app/views/attendees/new.html.erb +13 -0
- data/app/views/event-calendar-shared/_flash.html.erb +7 -0
- data/app/views/event-calendar-shared/_main_menu.html.erb +3 -0
- data/app/views/event-calendar-shared/_navigation.html.erb +15 -0
- data/app/views/event_revisions/index.html.erb +31 -0
- data/app/views/event_revisions/show.html.erb +5 -0
- data/app/views/events/_browse_event_revisions.html.erb +19 -0
- data/app/views/events/_event.html.erb +10 -0
- data/app/views/events/_event_details.html.erb +26 -0
- data/app/views/events/_form.html.erb +36 -0
- data/app/views/events/attendees.html.erb +29 -0
- data/app/views/events/edit.html.erb +3 -0
- data/app/views/events/index.html.erb +41 -0
- data/app/views/events/new.html.erb +3 -0
- data/app/views/events/search.html.erb +11 -0
- data/app/views/events/show.html.erb +30 -0
- data/app/views/layouts/application.html.erb +49 -0
- data/config/application.rb +42 -0
- data/config/blueprint_settings.yml +10 -0
- data/config/boot.rb +13 -0
- data/config/cucumber.yml +8 -0
- data/config/database.example.yml +22 -0
- data/config/environment.rb +5 -0
- data/config/environments/development.rb +26 -0
- data/config/environments/production.rb +49 -0
- data/config/environments/test.rb +38 -0
- data/config/initializers/backtrace_silencers.rb +7 -0
- data/config/initializers/formtastic.rb +55 -0
- data/config/initializers/inflections.rb +10 -0
- data/config/initializers/mime_types.rb +5 -0
- data/config/initializers/secret_token.rb +9 -0
- data/config/initializers/session_store.rb +10 -0
- data/config/locales/en.yml +5 -0
- data/config/routes.rb +67 -0
- data/db/migrate/20101011142543_create_events.rb +19 -0
- data/db/migrate/20101011172027_create_attendees.rb +16 -0
- data/db/migrate/20101011200048_make_events_revisable.rb +25 -0
- data/db/schema.rb +45 -0
- data/db/seeds.rb +7 -0
- data/lib/event_calendar.rb +3 -0
- data/lib/event_calendar/engine.rb +25 -0
- data/lib/generators/event_calendar/install/USAGE +5 -0
- data/lib/generators/event_calendar/install/install_generator.rb +19 -0
- data/lib/generators/event_calendar/install/templates/event_calendar.rake +169 -0
- data/lib/tasks/blueprint.rake +25 -0
- data/lib/tasks/cucumber.rake +53 -0
- data/public/404.html +26 -0
- data/public/422.html +26 -0
- data/public/500.html +26 -0
- data/public/favicon.ico +0 -0
- data/public/images/rails.png +0 -0
- data/public/javascripts/event_calendar.js +62 -0
- data/public/javascripts/event_calendar_behaviors.js +131 -0
- data/public/javascripts/fullcalendar.js +3965 -0
- data/public/javascripts/jquery-ui-1.7.2.custom.min.js +298 -0
- data/public/javascripts/jquery.clonePosition.js +27 -0
- data/public/javascripts/jquery.js +154 -0
- data/public/javascripts/jquery.qtip-1.0.0-rc3.js +2149 -0
- data/public/javascripts/jquery.string.1.0-min.js +6 -0
- data/public/javascripts/jquery.tablesorter.min.js +2 -0
- data/public/javascripts/lowpro.jquery.js +224 -0
- data/public/javascripts/rails.js +132 -0
- data/public/robots.txt +5 -0
- data/public/stylesheets/blueprint/grid.css +280 -0
- data/public/stylesheets/blueprint/icons/cross.png +0 -0
- data/public/stylesheets/blueprint/icons/doc.png +0 -0
- data/public/stylesheets/blueprint/icons/email.png +0 -0
- data/public/stylesheets/blueprint/icons/external.png +0 -0
- data/public/stylesheets/blueprint/icons/feed.png +0 -0
- data/public/stylesheets/blueprint/icons/im.png +0 -0
- data/public/stylesheets/blueprint/icons/key.png +0 -0
- data/public/stylesheets/blueprint/icons/pdf.png +0 -0
- data/public/stylesheets/blueprint/icons/tick.png +0 -0
- data/public/stylesheets/blueprint/icons/visited.png +0 -0
- data/public/stylesheets/blueprint/icons/xls.png +0 -0
- data/public/stylesheets/blueprint/ie.css +36 -0
- data/public/stylesheets/blueprint/plugins/buttons/icons/cross.png +0 -0
- data/public/stylesheets/blueprint/plugins/buttons/icons/key.png +0 -0
- data/public/stylesheets/blueprint/plugins/buttons/icons/tick.png +0 -0
- data/public/stylesheets/blueprint/plugins/buttons/readme.txt +32 -0
- data/public/stylesheets/blueprint/plugins/buttons/screen.css +97 -0
- data/public/stylesheets/blueprint/plugins/fancy-type/readme.txt +14 -0
- data/public/stylesheets/blueprint/plugins/fancy-type/screen.css +71 -0
- data/public/stylesheets/blueprint/plugins/link-icons/icons/doc.png +0 -0
- data/public/stylesheets/blueprint/plugins/link-icons/icons/email.png +0 -0
- data/public/stylesheets/blueprint/plugins/link-icons/icons/external.png +0 -0
- data/public/stylesheets/blueprint/plugins/link-icons/icons/feed.png +0 -0
- data/public/stylesheets/blueprint/plugins/link-icons/icons/im.png +0 -0
- data/public/stylesheets/blueprint/plugins/link-icons/icons/pdf.png +0 -0
- data/public/stylesheets/blueprint/plugins/link-icons/icons/visited.png +0 -0
- data/public/stylesheets/blueprint/plugins/link-icons/icons/xls.png +0 -0
- data/public/stylesheets/blueprint/plugins/link-icons/readme.txt +18 -0
- data/public/stylesheets/blueprint/plugins/link-icons/screen.css +40 -0
- data/public/stylesheets/blueprint/plugins/rtl/readme.txt +10 -0
- data/public/stylesheets/blueprint/plugins/rtl/screen.css +110 -0
- data/public/stylesheets/blueprint/plugins/silksprite/sprite.css +1 -0
- data/public/stylesheets/blueprint/plugins/silksprite/sprites.png +0 -0
- data/public/stylesheets/blueprint/print.css +29 -0
- data/public/stylesheets/blueprint/readme.txt +12 -0
- data/public/stylesheets/blueprint/screen.css +429 -0
- data/public/stylesheets/error_messages.css +65 -0
- data/public/stylesheets/formtastic.css +131 -0
- data/public/stylesheets/formtastic_changes.css +14 -0
- data/public/stylesheets/fullcalendar.css +574 -0
- data/public/stylesheets/fullcalendar_changes.css +0 -0
- data/public/stylesheets/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/public/stylesheets/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/public/stylesheets/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/public/stylesheets/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/public/stylesheets/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/public/stylesheets/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/public/stylesheets/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/public/stylesheets/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/public/stylesheets/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/public/stylesheets/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/public/stylesheets/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/public/stylesheets/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/public/stylesheets/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/public/stylesheets/smoothness/jquery-ui-1.7.2.custom.css +406 -0
- data/public/stylesheets/tablesorter/blue/asc.gif +0 -0
- data/public/stylesheets/tablesorter/blue/bg.gif +0 -0
- data/public/stylesheets/tablesorter/blue/desc.gif +0 -0
- data/public/stylesheets/tablesorter/blue/style.css +39 -0
- data/public/stylesheets/text_and_colors.css +49 -0
- data/spec/controllers/attendees_controller_spec.rb +27 -0
- data/spec/controllers/event_revisions_controller_spec.rb +86 -0
- data/spec/controllers/events_controller_spec.rb +168 -0
- data/spec/fixtures/event_calendar_events.yml +8 -0
- data/spec/models/deletable_instance_methods_spec.rb +61 -0
- data/spec/models/event_revision_spec.rb +36 -0
- data/spec/models/event_spec.rb +139 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/spec_helpers/mocks.rb +6 -0
- data/vendor/plugins/searchable_by/MIT-LICENSE +20 -0
- data/vendor/plugins/searchable_by/README +55 -0
- data/vendor/plugins/searchable_by/Rakefile +23 -0
- data/vendor/plugins/searchable_by/init.rb +2 -0
- data/vendor/plugins/searchable_by/install.rb +1 -0
- data/vendor/plugins/searchable_by/lib/searchable_by.rb +137 -0
- data/vendor/plugins/searchable_by/tasks/searchable_by_tasks.rake +4 -0
- data/vendor/plugins/searchable_by/test/boot.rb +21 -0
- data/vendor/plugins/searchable_by/test/database.yml +22 -0
- data/vendor/plugins/searchable_by/test/fixtures/companies.yml +10 -0
- data/vendor/plugins/searchable_by/test/fixtures/company.rb +5 -0
- data/vendor/plugins/searchable_by/test/fixtures/employee.rb +5 -0
- data/vendor/plugins/searchable_by/test/fixtures/employees.yml +28 -0
- data/vendor/plugins/searchable_by/test/fixtures/schema.rb +18 -0
- data/vendor/plugins/searchable_by/test/helper.rb +12 -0
- data/vendor/plugins/searchable_by/test/lib/activerecord_test_case.rb +43 -0
- data/vendor/plugins/searchable_by/test/lib/activerecord_test_connector.rb +75 -0
- data/vendor/plugins/searchable_by/test/lib/load_fixtures.rb +9 -0
- data/vendor/plugins/searchable_by/test/searchable_by_test.rb +73 -0
- data/vendor/plugins/searchable_by/uninstall.rb +1 -0
- metadata +606 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
namespace :blueprint do
|
2
|
+
desc "Install/Update blueprint stylesheets (+plugins)"
|
3
|
+
task :install do
|
4
|
+
blueprint_css = File.expand_path("~/.blueprint_css")
|
5
|
+
|
6
|
+
if File.exists?(blueprint_css)
|
7
|
+
blueprint_path = File.new(blueprint_css, 'r').read.strip
|
8
|
+
else
|
9
|
+
puts("What is the path to your blueprint checkout?")
|
10
|
+
blueprint_path = $stdin.gets.strip
|
11
|
+
end
|
12
|
+
|
13
|
+
compress_script = File.join(blueprint_path, 'lib', 'compress.rb')
|
14
|
+
|
15
|
+
while !File.exists?(compress_script)
|
16
|
+
puts("Could not find #{compress_script}. Please enter new path.")
|
17
|
+
blueprint_path = $stdin.gets.strip
|
18
|
+
compress_script = File.join(blueprint_path, 'lib', 'compress.rb')
|
19
|
+
end
|
20
|
+
|
21
|
+
File.open(blueprint_css, 'w'){|f| f.write(blueprint_path)}
|
22
|
+
|
23
|
+
system "ruby #{compress_script} -f #{Rails.root.to_s}/config/blueprint_settings.yml -p default"
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril.
|
2
|
+
# It is recommended to regenerate this file in the future when you upgrade to a
|
3
|
+
# newer version of cucumber-rails. Consider adding your own code to a new file
|
4
|
+
# instead of editing this one. Cucumber will automatically load all features/**/*.rb
|
5
|
+
# files.
|
6
|
+
|
7
|
+
|
8
|
+
unless ARGV.any? {|a| a =~ /^gems/} # Don't load anything when running the gems:* tasks
|
9
|
+
|
10
|
+
vendored_cucumber_bin = Dir["#{Rails.root}/vendor/{gems,plugins}/cucumber*/bin/cucumber"].first
|
11
|
+
$LOAD_PATH.unshift(File.dirname(vendored_cucumber_bin) + '/../lib') unless vendored_cucumber_bin.nil?
|
12
|
+
|
13
|
+
begin
|
14
|
+
require 'cucumber/rake/task'
|
15
|
+
|
16
|
+
namespace :cucumber do
|
17
|
+
Cucumber::Rake::Task.new({:ok => 'db:test:prepare'}, 'Run features that should pass') do |t|
|
18
|
+
t.binary = vendored_cucumber_bin # If nil, the gem's binary is used.
|
19
|
+
t.fork = true # You may get faster startup if you set this to false
|
20
|
+
t.profile = 'default'
|
21
|
+
end
|
22
|
+
|
23
|
+
Cucumber::Rake::Task.new({:wip => 'db:test:prepare'}, 'Run features that are being worked on') do |t|
|
24
|
+
t.binary = vendored_cucumber_bin
|
25
|
+
t.fork = true # You may get faster startup if you set this to false
|
26
|
+
t.profile = 'wip'
|
27
|
+
end
|
28
|
+
|
29
|
+
Cucumber::Rake::Task.new({:rerun => 'db:test:prepare'}, 'Record failing features and run only them if any exist') do |t|
|
30
|
+
t.binary = vendored_cucumber_bin
|
31
|
+
t.fork = true # You may get faster startup if you set this to false
|
32
|
+
t.profile = 'rerun'
|
33
|
+
end
|
34
|
+
|
35
|
+
desc 'Run all features'
|
36
|
+
task :all => [:ok, :wip]
|
37
|
+
end
|
38
|
+
desc 'Alias for cucumber:ok'
|
39
|
+
task :cucumber => 'cucumber:ok'
|
40
|
+
|
41
|
+
task :default => :cucumber
|
42
|
+
|
43
|
+
task :features => :cucumber do
|
44
|
+
STDERR.puts "*** The 'features' task is deprecated. See rake -T cucumber ***"
|
45
|
+
end
|
46
|
+
rescue LoadError
|
47
|
+
desc 'cucumber rake task not available (cucumber not installed)'
|
48
|
+
task :cucumber do
|
49
|
+
abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
data/public/404.html
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>The page you were looking for doesn't exist (404)</title>
|
5
|
+
<style type="text/css">
|
6
|
+
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
|
7
|
+
div.dialog {
|
8
|
+
width: 25em;
|
9
|
+
padding: 0 4em;
|
10
|
+
margin: 4em auto 0 auto;
|
11
|
+
border: 1px solid #ccc;
|
12
|
+
border-right-color: #999;
|
13
|
+
border-bottom-color: #999;
|
14
|
+
}
|
15
|
+
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
|
16
|
+
</style>
|
17
|
+
</head>
|
18
|
+
|
19
|
+
<body>
|
20
|
+
<!-- This file lives in public/404.html -->
|
21
|
+
<div class="dialog">
|
22
|
+
<h1>The page you were looking for doesn't exist.</h1>
|
23
|
+
<p>You may have mistyped the address or the page may have moved.</p>
|
24
|
+
</div>
|
25
|
+
</body>
|
26
|
+
</html>
|
data/public/422.html
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>The change you wanted was rejected (422)</title>
|
5
|
+
<style type="text/css">
|
6
|
+
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
|
7
|
+
div.dialog {
|
8
|
+
width: 25em;
|
9
|
+
padding: 0 4em;
|
10
|
+
margin: 4em auto 0 auto;
|
11
|
+
border: 1px solid #ccc;
|
12
|
+
border-right-color: #999;
|
13
|
+
border-bottom-color: #999;
|
14
|
+
}
|
15
|
+
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
|
16
|
+
</style>
|
17
|
+
</head>
|
18
|
+
|
19
|
+
<body>
|
20
|
+
<!-- This file lives in public/422.html -->
|
21
|
+
<div class="dialog">
|
22
|
+
<h1>The change you wanted was rejected.</h1>
|
23
|
+
<p>Maybe you tried to change something you didn't have access to.</p>
|
24
|
+
</div>
|
25
|
+
</body>
|
26
|
+
</html>
|
data/public/500.html
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>We're sorry, but something went wrong (500)</title>
|
5
|
+
<style type="text/css">
|
6
|
+
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
|
7
|
+
div.dialog {
|
8
|
+
width: 25em;
|
9
|
+
padding: 0 4em;
|
10
|
+
margin: 4em auto 0 auto;
|
11
|
+
border: 1px solid #ccc;
|
12
|
+
border-right-color: #999;
|
13
|
+
border-bottom-color: #999;
|
14
|
+
}
|
15
|
+
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
|
16
|
+
</style>
|
17
|
+
</head>
|
18
|
+
|
19
|
+
<body>
|
20
|
+
<!-- This file lives in public/500.html -->
|
21
|
+
<div class="dialog">
|
22
|
+
<h1>We're sorry, but something went wrong.</h1>
|
23
|
+
<p>We've been notified about this issue and we'll take a look at it shortly.</p>
|
24
|
+
</div>
|
25
|
+
</body>
|
26
|
+
</html>
|
data/public/favicon.ico
ADDED
File without changes
|
Binary file
|
@@ -0,0 +1,62 @@
|
|
1
|
+
// use to give the preview of details for an event below a calendar
|
2
|
+
var updateEventDescription = function(event, jsEvent) {
|
3
|
+
$("#event_quick_description")[0].innerHTML = "";
|
4
|
+
$("#event_quick_description").append(
|
5
|
+
$("<h3/>").append(
|
6
|
+
$('<a/>', { text : event.title, href : event.url })
|
7
|
+
)
|
8
|
+
);
|
9
|
+
$("#event_quick_description")[0].innerHTML += "Location: " + event.location + "<br/>";
|
10
|
+
$("#event_quick_description")[0].innerHTML += event.description;
|
11
|
+
|
12
|
+
$("#event_quick_description").show();
|
13
|
+
}
|
14
|
+
|
15
|
+
|
16
|
+
jQuery(function($) {
|
17
|
+
$('a.show_hide_link').attach(ShowHideLink);
|
18
|
+
});
|
19
|
+
|
20
|
+
/*
|
21
|
+
http://www.learningjquery.com/2007/08/clearing-form-data
|
22
|
+
*/
|
23
|
+
$.fn.clearForm = function() {
|
24
|
+
return this.each(function() {
|
25
|
+
var type = this.type, tag = this.tagName.toLowerCase();
|
26
|
+
if (tag == 'form')
|
27
|
+
return $(':input',this).clearForm();
|
28
|
+
if (type == 'text' || type == 'password' || tag == 'textarea')
|
29
|
+
this.value = '';
|
30
|
+
else if (type == 'checkbox' || type == 'radio')
|
31
|
+
this.checked = false;
|
32
|
+
else if (tag == 'select')
|
33
|
+
this.selectedIndex = -1;
|
34
|
+
});
|
35
|
+
};
|
36
|
+
|
37
|
+
DynamicForm = $.klass({
|
38
|
+
initialize: function(options) {
|
39
|
+
this.formId = options.formId; // new_description
|
40
|
+
this.formContainer = options.formContainer; // blank_description_form
|
41
|
+
this.targetIdName = options.targetIdName; // file_attachment_id
|
42
|
+
this.targetContentName = options.targetContentName; // file_attachment[description]
|
43
|
+
this.targetContentType = options.targetContentType;
|
44
|
+
this.actionPrefix = options.actionPrefix; // /file_attachments
|
45
|
+
},
|
46
|
+
onclick: function(e) {
|
47
|
+
e.preventDefault();
|
48
|
+
|
49
|
+
var targetIdValue = this.element.attr(this.targetIdName);
|
50
|
+
var targetContentValue = this.element.attr(this.targetContentName);
|
51
|
+
|
52
|
+
$('#' + this.formId).attr("action", this.actionPrefix + "/" + targetIdValue);
|
53
|
+
|
54
|
+
$('#' + this.formId).clearForm();
|
55
|
+
|
56
|
+
$('#' + this.formContainer).insertBefore(this.element);
|
57
|
+
|
58
|
+
$(this.targetContentType + '[name='+ this.targetContentName +']').val(targetContentValue);
|
59
|
+
|
60
|
+
$('#' + this.formContainer).show();
|
61
|
+
}
|
62
|
+
});
|
@@ -0,0 +1,131 @@
|
|
1
|
+
/*
|
2
|
+
* all lowpro behaviors
|
3
|
+
*/
|
4
|
+
|
5
|
+
|
6
|
+
CheckBoxToggle = $.klass({
|
7
|
+
initialize: function(toggle_selector) {
|
8
|
+
this.toggle_selector = toggle_selector;
|
9
|
+
},
|
10
|
+
|
11
|
+
onclick: function() {
|
12
|
+
var to_toggle = this.element[0].checked;
|
13
|
+
$(this.toggle_selector + ' input[type=checkbox]').each(function(index) {
|
14
|
+
$(this)[0].checked = to_toggle;
|
15
|
+
});
|
16
|
+
}
|
17
|
+
});
|
18
|
+
|
19
|
+
/*
|
20
|
+
* use like:
|
21
|
+
*
|
22
|
+
* $('.some_category_field').attach(SelectPopper, '#div_with_ul');
|
23
|
+
* $('#div_with_ul').attach(SetSelector, '.some_category_field');
|
24
|
+
*
|
25
|
+
*/
|
26
|
+
|
27
|
+
SelectPopper = $.klass({
|
28
|
+
// provide array of select_options
|
29
|
+
// TODO: detect a hash and use it to do option text/value
|
30
|
+
initialize: function(select_options) {
|
31
|
+
if(select_options.length > 0) {
|
32
|
+
var _a_ul = $('<ul/>', { style : 'list-style-type: none; margin: 0' } );
|
33
|
+
$.each(select_options, function(index, value) {
|
34
|
+
$('<li/>', { text : value, style : "padding: 3px;" }).appendTo(_a_ul);
|
35
|
+
});
|
36
|
+
|
37
|
+
this.select_box = $('<div/>', { style : 'display:none; background: white; border: 1px solid #999;',
|
38
|
+
"class" : "select_popper_box" } );
|
39
|
+
_a_ul.appendTo(this.select_box);
|
40
|
+
this.select_box.appendTo('body');
|
41
|
+
this.select_box.attach(SetSelector, this.element);
|
42
|
+
}
|
43
|
+
},
|
44
|
+
|
45
|
+
onfocus: function(e) {
|
46
|
+
if(this.select_box) {
|
47
|
+
this.select_box.clonePosition(this.element, { cloneHeight: false, offsetLeft: 1, offsetTop: this.element.height() });
|
48
|
+
this.select_box.css('width', (this.element.offsetWidth - 2) + 'px');
|
49
|
+
this.select_box.show();
|
50
|
+
}
|
51
|
+
},
|
52
|
+
|
53
|
+
onblur: function(e) {
|
54
|
+
if(this.select_box) {
|
55
|
+
var _select_box = this.select_box;
|
56
|
+
setTimeout(function() {
|
57
|
+
_select_box.hide();
|
58
|
+
}, 250);
|
59
|
+
}
|
60
|
+
}
|
61
|
+
});
|
62
|
+
|
63
|
+
SetSelector = $.klass({
|
64
|
+
initialize: function(input_element) {
|
65
|
+
this.select_field = $(input_element);
|
66
|
+
},
|
67
|
+
|
68
|
+
onclick: $.delegate({
|
69
|
+
'li': function(click_target) {
|
70
|
+
this.select_field.val(click_target.text());
|
71
|
+
this.element.hide();
|
72
|
+
}
|
73
|
+
})
|
74
|
+
});
|
75
|
+
|
76
|
+
|
77
|
+
TextareaExpander = $.klass({
|
78
|
+
initialize: function(pixelsToGrowBy, maxHeight) {
|
79
|
+
this.pixelsToGrowBy = (typeof pixelsToGrowBy == 'undefined') ? 20 : pixelsToGrowBy;
|
80
|
+
this.maxHeight = (typeof maxHeight == 'undefined') ? 999 : maxHeight;
|
81
|
+
// run it once to blow up any textareas on first page load
|
82
|
+
this.onkeypress();
|
83
|
+
},
|
84
|
+
|
85
|
+
onkeypress: function(e) {
|
86
|
+
var curr_h = this.element.height();
|
87
|
+
var scroll_h = this.element[0].scrollHeight;
|
88
|
+
while(curr_h < scroll_h && curr_h < this.maxHeight) {
|
89
|
+
this.element.css( { 'height': (curr_h + this.pixelsToGrowBy) + 'px' });
|
90
|
+
curr_h = this.element.height();
|
91
|
+
scroll_h = this.element[0].scrollHeight;
|
92
|
+
}
|
93
|
+
}
|
94
|
+
});
|
95
|
+
|
96
|
+
|
97
|
+
ShowHideLink = $.klass({
|
98
|
+
initialize: function(options) {
|
99
|
+
options = options || {};
|
100
|
+
this.showEffect = options.show_effect;
|
101
|
+
this.hideEffect = options.hide_effect;
|
102
|
+
this.showEffectParams = options.show_effect_params;
|
103
|
+
this.hideEffectParams = options.hide_effect_params;
|
104
|
+
this.showClassName = $.string(this.element[0].href).toQueryParams()['show'];
|
105
|
+
this.hideClassName = $.string(this.element[0].href).toQueryParams()['hide'];
|
106
|
+
this.toggleClassName = $.string(this.element[0].href).toQueryParams()['toggle'];
|
107
|
+
this.hideMe = options.hide_me;
|
108
|
+
this.doNotStop = options.do_not_stop || false;
|
109
|
+
this.myToggleClass = options.my_toggle_class;
|
110
|
+
},
|
111
|
+
|
112
|
+
onclick: function(e) {
|
113
|
+
if(this.hideClassName) {
|
114
|
+
//$('.'+this.hideClassName).invoke(this.hideEffect || 'hide', this.hideEffectParams);
|
115
|
+
$('.'+this.hideClassName).hide();
|
116
|
+
}
|
117
|
+
if(this.showClassName) {
|
118
|
+
//$('.'+this.showClassName).invoke(this.showEffect || 'show', this.showEffectParams);
|
119
|
+
$('.'+this.showClassName).show();
|
120
|
+
}
|
121
|
+
if(this.toggleClassName) {
|
122
|
+
$('.'+this.toggleClassName).toggle();
|
123
|
+
}
|
124
|
+
if(this.hideMe) {
|
125
|
+
this.element.hide();
|
126
|
+
}
|
127
|
+
this.element.blur();
|
128
|
+
|
129
|
+
return this.doNotStop;
|
130
|
+
}
|
131
|
+
});
|
@@ -0,0 +1,3965 @@
|
|
1
|
+
/**
|
2
|
+
* @preserve
|
3
|
+
* FullCalendar v1.4.6
|
4
|
+
* http://arshaw.com/fullcalendar/
|
5
|
+
*
|
6
|
+
* Use fullcalendar.css for basic styling.
|
7
|
+
* For event drag & drop, required jQuery UI draggable.
|
8
|
+
* For event resizing, requires jQuery UI resizable.
|
9
|
+
*
|
10
|
+
* Copyright (c) 2009 Adam Shaw
|
11
|
+
* Dual licensed under the MIT and GPL licenses:
|
12
|
+
* http://www.opensource.org/licenses/mit-license.php
|
13
|
+
* http://www.gnu.org/licenses/gpl.html
|
14
|
+
*
|
15
|
+
* Date: Mon May 31 10:18:29 2010 -0700
|
16
|
+
*
|
17
|
+
*/
|
18
|
+
|
19
|
+
(function($) {
|
20
|
+
|
21
|
+
|
22
|
+
var fc = $.fullCalendar = {};
|
23
|
+
var views = fc.views = {};
|
24
|
+
|
25
|
+
|
26
|
+
/* Defaults
|
27
|
+
-----------------------------------------------------------------------------*/
|
28
|
+
|
29
|
+
var defaults = {
|
30
|
+
|
31
|
+
// display
|
32
|
+
defaultView: 'month',
|
33
|
+
aspectRatio: 1.35,
|
34
|
+
header: {
|
35
|
+
left: 'title',
|
36
|
+
center: '',
|
37
|
+
right: 'today prev,next'
|
38
|
+
},
|
39
|
+
weekends: true,
|
40
|
+
|
41
|
+
// editing
|
42
|
+
//editable: false,
|
43
|
+
//disableDragging: false,
|
44
|
+
//disableResizing: false,
|
45
|
+
|
46
|
+
allDayDefault: true,
|
47
|
+
|
48
|
+
// event ajax
|
49
|
+
lazyFetching: true,
|
50
|
+
startParam: 'start',
|
51
|
+
endParam: 'end',
|
52
|
+
|
53
|
+
// time formats
|
54
|
+
titleFormat: {
|
55
|
+
month: 'MMMM yyyy',
|
56
|
+
week: "MMM d[ yyyy]{ '—'[ MMM] d yyyy}",
|
57
|
+
day: 'dddd, MMM d, yyyy'
|
58
|
+
},
|
59
|
+
columnFormat: {
|
60
|
+
month: 'ddd',
|
61
|
+
week: 'ddd M/d',
|
62
|
+
day: 'dddd M/d'
|
63
|
+
},
|
64
|
+
timeFormat: { // for event elements
|
65
|
+
'': 'h(:mm)t' // default
|
66
|
+
},
|
67
|
+
|
68
|
+
// locale
|
69
|
+
isRTL: false,
|
70
|
+
firstDay: 0,
|
71
|
+
monthNames: ['January','February','March','April','May','June','July','August','September','October','November','December'],
|
72
|
+
monthNamesShort: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],
|
73
|
+
dayNames: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
|
74
|
+
dayNamesShort: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'],
|
75
|
+
buttonText: {
|
76
|
+
prev: ' ◄ ',
|
77
|
+
next: ' ► ',
|
78
|
+
prevYear: ' << ',
|
79
|
+
nextYear: ' >> ',
|
80
|
+
today: 'today',
|
81
|
+
month: 'month',
|
82
|
+
week: 'week',
|
83
|
+
day: 'day'
|
84
|
+
},
|
85
|
+
|
86
|
+
// jquery-ui theming
|
87
|
+
theme: false,
|
88
|
+
buttonIcons: {
|
89
|
+
prev: 'circle-triangle-w',
|
90
|
+
next: 'circle-triangle-e'
|
91
|
+
},
|
92
|
+
|
93
|
+
//selectable: false,
|
94
|
+
unselectAuto: true
|
95
|
+
|
96
|
+
};
|
97
|
+
|
98
|
+
// right-to-left defaults
|
99
|
+
var rtlDefaults = {
|
100
|
+
header: {
|
101
|
+
left: 'next,prev today',
|
102
|
+
center: '',
|
103
|
+
right: 'title'
|
104
|
+
},
|
105
|
+
buttonText: {
|
106
|
+
prev: ' ► ',
|
107
|
+
next: ' ◄ ',
|
108
|
+
prevYear: ' >> ',
|
109
|
+
nextYear: ' << '
|
110
|
+
},
|
111
|
+
buttonIcons: {
|
112
|
+
prev: 'circle-triangle-e',
|
113
|
+
next: 'circle-triangle-w'
|
114
|
+
}
|
115
|
+
};
|
116
|
+
|
117
|
+
// function for adding/overriding defaults
|
118
|
+
var setDefaults = fc.setDefaults = function(d) {
|
119
|
+
$.extend(true, defaults, d);
|
120
|
+
};
|
121
|
+
|
122
|
+
|
123
|
+
|
124
|
+
/* .fullCalendar jQuery function
|
125
|
+
-----------------------------------------------------------------------------*/
|
126
|
+
|
127
|
+
$.fn.fullCalendar = function(options) {
|
128
|
+
|
129
|
+
// method calling
|
130
|
+
if (typeof options == 'string') {
|
131
|
+
var args = Array.prototype.slice.call(arguments, 1),
|
132
|
+
res;
|
133
|
+
this.each(function() {
|
134
|
+
var data = $.data(this, 'fullCalendar');
|
135
|
+
if (data) {
|
136
|
+
var meth = data[options];
|
137
|
+
if (meth) {
|
138
|
+
var r = meth.apply(this, args);
|
139
|
+
if (res === undefined) {
|
140
|
+
res = r;
|
141
|
+
}
|
142
|
+
}
|
143
|
+
}
|
144
|
+
});
|
145
|
+
if (res !== undefined) {
|
146
|
+
return res;
|
147
|
+
}
|
148
|
+
return this;
|
149
|
+
}
|
150
|
+
|
151
|
+
// pluck the 'events' and 'eventSources' options
|
152
|
+
var eventSources = options.eventSources || [];
|
153
|
+
delete options.eventSources;
|
154
|
+
if (options.events) {
|
155
|
+
eventSources.push(options.events);
|
156
|
+
delete options.events;
|
157
|
+
}
|
158
|
+
|
159
|
+
// first event source reserved for 'sticky' events
|
160
|
+
eventSources.unshift([]);
|
161
|
+
|
162
|
+
// initialize options
|
163
|
+
options = $.extend(true, {},
|
164
|
+
defaults,
|
165
|
+
(options.isRTL || options.isRTL===undefined && defaults.isRTL) ? rtlDefaults : {},
|
166
|
+
options
|
167
|
+
);
|
168
|
+
var tm = options.theme ? 'ui' : 'fc'; // for making theme classes
|
169
|
+
|
170
|
+
|
171
|
+
this.each(function() {
|
172
|
+
|
173
|
+
|
174
|
+
/* Instance Initialization
|
175
|
+
-----------------------------------------------------------------------------*/
|
176
|
+
|
177
|
+
// element
|
178
|
+
var _element = this,
|
179
|
+
element = $(_element).addClass('fc'),
|
180
|
+
elementOuterWidth,
|
181
|
+
content = $("<div class='fc-content " + tm + "-widget-content' style='position:relative'/>").prependTo(_element),
|
182
|
+
suggestedViewHeight,
|
183
|
+
resizeUID = 0,
|
184
|
+
ignoreWindowResize = 0,
|
185
|
+
date = new Date(),
|
186
|
+
viewName, // the current view name (TODO: look into getting rid of)
|
187
|
+
view, // the current view
|
188
|
+
viewInstances = {},
|
189
|
+
absoluteViewElement;
|
190
|
+
|
191
|
+
|
192
|
+
|
193
|
+
if (options.isRTL) {
|
194
|
+
element.addClass('fc-rtl');
|
195
|
+
}
|
196
|
+
if (options.theme) {
|
197
|
+
element.addClass('ui-widget');
|
198
|
+
}
|
199
|
+
|
200
|
+
if (options.year !== undefined && options.year != date.getFullYear()) {
|
201
|
+
date.setDate(1);
|
202
|
+
date.setMonth(0);
|
203
|
+
date.setFullYear(options.year);
|
204
|
+
}
|
205
|
+
if (options.month !== undefined && options.month != date.getMonth()) {
|
206
|
+
date.setDate(1);
|
207
|
+
date.setMonth(options.month);
|
208
|
+
}
|
209
|
+
if (options.date !== undefined) {
|
210
|
+
date.setDate(options.date);
|
211
|
+
}
|
212
|
+
|
213
|
+
|
214
|
+
|
215
|
+
/* View Rendering
|
216
|
+
-----------------------------------------------------------------------------*/
|
217
|
+
|
218
|
+
function changeView(v) {
|
219
|
+
if (v != viewName) {
|
220
|
+
ignoreWindowResize++; // because setMinHeight might change the height before render (and subsequently setSize) is reached
|
221
|
+
|
222
|
+
viewUnselect();
|
223
|
+
|
224
|
+
var oldView = view,
|
225
|
+
newViewElement;
|
226
|
+
|
227
|
+
if (oldView) {
|
228
|
+
if (oldView.eventsChanged) {
|
229
|
+
eventsDirty();
|
230
|
+
oldView.eventDirty = oldView.eventsChanged = false;
|
231
|
+
}
|
232
|
+
if (oldView.beforeHide) {
|
233
|
+
oldView.beforeHide(); // called before changing min-height. if called after, scroll state is reset (in Opera)
|
234
|
+
}
|
235
|
+
setMinHeight(content, content.height());
|
236
|
+
oldView.element.hide();
|
237
|
+
}else{
|
238
|
+
setMinHeight(content, 1); // needs to be 1 (not 0) for IE7, or else view dimensions miscalculated
|
239
|
+
}
|
240
|
+
content.css('overflow', 'hidden');
|
241
|
+
|
242
|
+
if (viewInstances[v]) {
|
243
|
+
(view = viewInstances[v]).element.show();
|
244
|
+
}else{
|
245
|
+
view = viewInstances[v] = fc.views[v](
|
246
|
+
newViewElement = absoluteViewElement =
|
247
|
+
$("<div class='fc-view fc-view-" + v + "' style='position:absolute'/>")
|
248
|
+
.appendTo(content),
|
249
|
+
options
|
250
|
+
);
|
251
|
+
}
|
252
|
+
|
253
|
+
if (header) {
|
254
|
+
// update 'active' view button
|
255
|
+
header.find('div.fc-button-' + viewName).removeClass(tm + '-state-active');
|
256
|
+
header.find('div.fc-button-' + v).addClass(tm + '-state-active');
|
257
|
+
}
|
258
|
+
|
259
|
+
view.name = viewName = v;
|
260
|
+
render(); // after height has been set, will make absoluteViewElement's position=relative, then set to null
|
261
|
+
content.css('overflow', '');
|
262
|
+
if (oldView) {
|
263
|
+
setMinHeight(content, 1);
|
264
|
+
}
|
265
|
+
if (!newViewElement && view.afterShow) {
|
266
|
+
view.afterShow(); // called after setting min-height/overflow, so in final scroll state (for Opera)
|
267
|
+
}
|
268
|
+
|
269
|
+
ignoreWindowResize--;
|
270
|
+
}
|
271
|
+
}
|
272
|
+
|
273
|
+
|
274
|
+
function render(inc) {
|
275
|
+
if (elementVisible()) {
|
276
|
+
ignoreWindowResize++; // because view.renderEvents might temporarily change the height before setSize is reached
|
277
|
+
|
278
|
+
viewUnselect();
|
279
|
+
|
280
|
+
if (suggestedViewHeight === undefined) {
|
281
|
+
calcSize();
|
282
|
+
}
|
283
|
+
|
284
|
+
if (!view.start || inc || date < view.start || date >= view.end) {
|
285
|
+
view.render(date, inc || 0); // responsible for clearing events
|
286
|
+
setSize(true);
|
287
|
+
if (!eventStart || !options.lazyFetching || view.visStart < eventStart || view.visEnd > eventEnd) {
|
288
|
+
fetchAndRenderEvents();
|
289
|
+
}else{
|
290
|
+
view.renderEvents(events); // don't refetch
|
291
|
+
}
|
292
|
+
}
|
293
|
+
else if (view.sizeDirty || view.eventsDirty || !options.lazyFetching) {
|
294
|
+
view.clearEvents();
|
295
|
+
if (view.sizeDirty) {
|
296
|
+
setSize();
|
297
|
+
}
|
298
|
+
if (options.lazyFetching) {
|
299
|
+
view.renderEvents(events); // don't refetch
|
300
|
+
}else{
|
301
|
+
fetchAndRenderEvents();
|
302
|
+
}
|
303
|
+
}
|
304
|
+
elementOuterWidth = element.outerWidth();
|
305
|
+
view.sizeDirty = false;
|
306
|
+
view.eventsDirty = false;
|
307
|
+
|
308
|
+
if (header) {
|
309
|
+
// update title text
|
310
|
+
header.find('h2.fc-header-title').html(view.title);
|
311
|
+
// enable/disable 'today' button
|
312
|
+
var today = new Date();
|
313
|
+
if (today >= view.start && today < view.end) {
|
314
|
+
header.find('div.fc-button-today').addClass(tm + '-state-disabled');
|
315
|
+
}else{
|
316
|
+
header.find('div.fc-button-today').removeClass(tm + '-state-disabled');
|
317
|
+
}
|
318
|
+
}
|
319
|
+
|
320
|
+
ignoreWindowResize--;
|
321
|
+
view.trigger('viewDisplay', _element);
|
322
|
+
}
|
323
|
+
}
|
324
|
+
|
325
|
+
|
326
|
+
function elementVisible() {
|
327
|
+
return _element.offsetWidth !== 0;
|
328
|
+
}
|
329
|
+
|
330
|
+
function bodyVisible() {
|
331
|
+
return $('body')[0].offsetWidth !== 0;
|
332
|
+
}
|
333
|
+
|
334
|
+
function viewUnselect() {
|
335
|
+
if (view) {
|
336
|
+
view.unselect();
|
337
|
+
}
|
338
|
+
}
|
339
|
+
|
340
|
+
|
341
|
+
// called when any event objects have been added/removed/changed, rerenders
|
342
|
+
function eventsChanged() {
|
343
|
+
eventsDirty();
|
344
|
+
if (elementVisible()) {
|
345
|
+
view.clearEvents();
|
346
|
+
view.renderEvents(events);
|
347
|
+
view.eventsDirty = false;
|
348
|
+
}
|
349
|
+
}
|
350
|
+
|
351
|
+
// marks other views' events as dirty
|
352
|
+
function eventsDirty() {
|
353
|
+
$.each(viewInstances, function() {
|
354
|
+
this.eventsDirty = true;
|
355
|
+
});
|
356
|
+
}
|
357
|
+
|
358
|
+
// called when we know the element size has changed
|
359
|
+
function sizeChanged() {
|
360
|
+
sizesDirty();
|
361
|
+
if (elementVisible()) {
|
362
|
+
calcSize();
|
363
|
+
setSize();
|
364
|
+
viewUnselect();
|
365
|
+
view.rerenderEvents();
|
366
|
+
view.sizeDirty = false;
|
367
|
+
}
|
368
|
+
}
|
369
|
+
|
370
|
+
// marks other views' sizes as dirty
|
371
|
+
function sizesDirty() {
|
372
|
+
$.each(viewInstances, function() {
|
373
|
+
this.sizeDirty = true;
|
374
|
+
});
|
375
|
+
}
|
376
|
+
|
377
|
+
|
378
|
+
|
379
|
+
|
380
|
+
/* Event Sources and Fetching
|
381
|
+
-----------------------------------------------------------------------------*/
|
382
|
+
|
383
|
+
var events = [],
|
384
|
+
eventStart, eventEnd;
|
385
|
+
|
386
|
+
// Fetch from ALL sources. Clear 'events' array and populate
|
387
|
+
function fetchEvents(callback) {
|
388
|
+
events = [];
|
389
|
+
eventStart = cloneDate(view.visStart);
|
390
|
+
eventEnd = cloneDate(view.visEnd);
|
391
|
+
var queued = eventSources.length,
|
392
|
+
sourceDone = function() {
|
393
|
+
if (!--queued) {
|
394
|
+
if (callback) {
|
395
|
+
callback(events);
|
396
|
+
}
|
397
|
+
}
|
398
|
+
}, i=0;
|
399
|
+
for (; i<eventSources.length; i++) {
|
400
|
+
fetchEventSource(eventSources[i], sourceDone);
|
401
|
+
}
|
402
|
+
}
|
403
|
+
|
404
|
+
// Fetch from a particular source. Append to the 'events' array
|
405
|
+
function fetchEventSource(src, callback) {
|
406
|
+
var prevViewName = view.name,
|
407
|
+
prevDate = cloneDate(date),
|
408
|
+
reportEvents = function(a) {
|
409
|
+
if (prevViewName == view.name && +prevDate == +date && // protects from fast switching
|
410
|
+
$.inArray(src, eventSources) != -1) { // makes sure source hasn't been removed
|
411
|
+
for (var i=0; i<a.length; i++) {
|
412
|
+
normalizeEvent(a[i], options);
|
413
|
+
a[i].source = src;
|
414
|
+
}
|
415
|
+
events = events.concat(a);
|
416
|
+
if (callback) {
|
417
|
+
callback(a);
|
418
|
+
}
|
419
|
+
}
|
420
|
+
},
|
421
|
+
reportEventsAndPop = function(a) {
|
422
|
+
reportEvents(a);
|
423
|
+
popLoading();
|
424
|
+
};
|
425
|
+
if (typeof src == 'string') {
|
426
|
+
var params = {};
|
427
|
+
params[options.startParam] = Math.round(eventStart.getTime() / 1000);
|
428
|
+
params[options.endParam] = Math.round(eventEnd.getTime() / 1000);
|
429
|
+
if (options.cacheParam) {
|
430
|
+
params[options.cacheParam] = (new Date()).getTime(); // TODO: deprecate cacheParam
|
431
|
+
}
|
432
|
+
pushLoading();
|
433
|
+
$.ajax({
|
434
|
+
url: src,
|
435
|
+
dataType: 'json',
|
436
|
+
data: params,
|
437
|
+
cache: options.cacheParam || false, // don't let jquery prevent caching if cacheParam is being used
|
438
|
+
success: reportEventsAndPop
|
439
|
+
});
|
440
|
+
}
|
441
|
+
else if ($.isFunction(src)) {
|
442
|
+
pushLoading();
|
443
|
+
src(cloneDate(eventStart), cloneDate(eventEnd), reportEventsAndPop);
|
444
|
+
}
|
445
|
+
else {
|
446
|
+
reportEvents(src); // src is an array
|
447
|
+
}
|
448
|
+
}
|
449
|
+
|
450
|
+
|
451
|
+
// for convenience
|
452
|
+
function fetchAndRenderEvents() {
|
453
|
+
fetchEvents(function(events) {
|
454
|
+
view.renderEvents(events); // maintain `this` in view
|
455
|
+
});
|
456
|
+
}
|
457
|
+
|
458
|
+
|
459
|
+
|
460
|
+
/* Loading State
|
461
|
+
-----------------------------------------------------------------------------*/
|
462
|
+
|
463
|
+
var loadingLevel = 0;
|
464
|
+
|
465
|
+
function pushLoading() {
|
466
|
+
if (!loadingLevel++) {
|
467
|
+
view.trigger('loading', _element, true);
|
468
|
+
}
|
469
|
+
}
|
470
|
+
|
471
|
+
function popLoading() {
|
472
|
+
if (!--loadingLevel) {
|
473
|
+
view.trigger('loading', _element, false);
|
474
|
+
}
|
475
|
+
}
|
476
|
+
|
477
|
+
|
478
|
+
|
479
|
+
/* Public Methods
|
480
|
+
-----------------------------------------------------------------------------*/
|
481
|
+
|
482
|
+
var publicMethods = {
|
483
|
+
|
484
|
+
render: function() {
|
485
|
+
calcSize();
|
486
|
+
sizesDirty();
|
487
|
+
eventsDirty();
|
488
|
+
render();
|
489
|
+
},
|
490
|
+
|
491
|
+
changeView: changeView,
|
492
|
+
|
493
|
+
getView: function() {
|
494
|
+
return view;
|
495
|
+
},
|
496
|
+
|
497
|
+
getDate: function() {
|
498
|
+
return date;
|
499
|
+
},
|
500
|
+
|
501
|
+
option: function(name, value) {
|
502
|
+
if (value === undefined) {
|
503
|
+
return options[name];
|
504
|
+
}
|
505
|
+
if (name == 'height' || name == 'contentHeight' || name == 'aspectRatio') {
|
506
|
+
options[name] = value;
|
507
|
+
sizeChanged();
|
508
|
+
}
|
509
|
+
},
|
510
|
+
|
511
|
+
destroy: function() {
|
512
|
+
$(window).unbind('resize', windowResize);
|
513
|
+
if (header) {
|
514
|
+
header.remove();
|
515
|
+
}
|
516
|
+
content.remove();
|
517
|
+
$.removeData(_element, 'fullCalendar');
|
518
|
+
},
|
519
|
+
|
520
|
+
//
|
521
|
+
// Navigation
|
522
|
+
//
|
523
|
+
|
524
|
+
prev: function() {
|
525
|
+
render(-1);
|
526
|
+
},
|
527
|
+
|
528
|
+
next: function() {
|
529
|
+
render(1);
|
530
|
+
},
|
531
|
+
|
532
|
+
prevYear: function() {
|
533
|
+
addYears(date, -1);
|
534
|
+
render();
|
535
|
+
},
|
536
|
+
|
537
|
+
nextYear: function() {
|
538
|
+
addYears(date, 1);
|
539
|
+
render();
|
540
|
+
},
|
541
|
+
|
542
|
+
today: function() {
|
543
|
+
date = new Date();
|
544
|
+
render();
|
545
|
+
},
|
546
|
+
|
547
|
+
gotoDate: function(year, month, dateNum) {
|
548
|
+
if (typeof year == 'object') {
|
549
|
+
date = cloneDate(year); // provided 1 argument, a Date
|
550
|
+
}else{
|
551
|
+
if (year !== undefined) {
|
552
|
+
date.setFullYear(year);
|
553
|
+
}
|
554
|
+
if (month !== undefined) {
|
555
|
+
date.setMonth(month);
|
556
|
+
}
|
557
|
+
if (dateNum !== undefined) {
|
558
|
+
date.setDate(dateNum);
|
559
|
+
}
|
560
|
+
}
|
561
|
+
render();
|
562
|
+
},
|
563
|
+
|
564
|
+
incrementDate: function(years, months, days) {
|
565
|
+
if (years !== undefined) {
|
566
|
+
addYears(date, years);
|
567
|
+
}
|
568
|
+
if (months !== undefined) {
|
569
|
+
addMonths(date, months);
|
570
|
+
}
|
571
|
+
if (days !== undefined) {
|
572
|
+
addDays(date, days);
|
573
|
+
}
|
574
|
+
render();
|
575
|
+
},
|
576
|
+
|
577
|
+
//
|
578
|
+
// Event Manipulation
|
579
|
+
//
|
580
|
+
|
581
|
+
updateEvent: function(event) { // update an existing event
|
582
|
+
var i, len = events.length, e,
|
583
|
+
startDelta = event.start - event._start,
|
584
|
+
endDelta = event.end ?
|
585
|
+
(event.end - (event._end || view.defaultEventEnd(event))) // event._end would be null if event.end
|
586
|
+
: 0; // was null and event was just resized
|
587
|
+
for (i=0; i<len; i++) {
|
588
|
+
e = events[i];
|
589
|
+
if (e._id == event._id && e != event) {
|
590
|
+
e.start = new Date(+e.start + startDelta);
|
591
|
+
if (event.end) {
|
592
|
+
if (e.end) {
|
593
|
+
e.end = new Date(+e.end + endDelta);
|
594
|
+
}else{
|
595
|
+
e.end = new Date(+view.defaultEventEnd(e) + endDelta);
|
596
|
+
}
|
597
|
+
}else{
|
598
|
+
e.end = null;
|
599
|
+
}
|
600
|
+
e.title = event.title;
|
601
|
+
e.url = event.url;
|
602
|
+
e.allDay = event.allDay;
|
603
|
+
e.className = event.className;
|
604
|
+
e.editable = event.editable;
|
605
|
+
normalizeEvent(e, options);
|
606
|
+
}
|
607
|
+
}
|
608
|
+
normalizeEvent(event, options);
|
609
|
+
eventsChanged();
|
610
|
+
},
|
611
|
+
|
612
|
+
renderEvent: function(event, stick) { // render a new event
|
613
|
+
normalizeEvent(event, options);
|
614
|
+
if (!event.source) {
|
615
|
+
if (stick) {
|
616
|
+
(event.source = eventSources[0]).push(event);
|
617
|
+
}
|
618
|
+
events.push(event);
|
619
|
+
}
|
620
|
+
eventsChanged();
|
621
|
+
},
|
622
|
+
|
623
|
+
removeEvents: function(filter) {
|
624
|
+
if (!filter) { // remove all
|
625
|
+
events = [];
|
626
|
+
// clear all array sources
|
627
|
+
for (var i=0; i<eventSources.length; i++) {
|
628
|
+
if (typeof eventSources[i] == 'object') {
|
629
|
+
eventSources[i] = [];
|
630
|
+
}
|
631
|
+
}
|
632
|
+
}else{
|
633
|
+
if (!$.isFunction(filter)) { // an event ID
|
634
|
+
var id = filter + '';
|
635
|
+
filter = function(e) {
|
636
|
+
return e._id == id;
|
637
|
+
};
|
638
|
+
}
|
639
|
+
events = $.grep(events, filter, true);
|
640
|
+
// remove events from array sources
|
641
|
+
for (var i=0; i<eventSources.length; i++) {
|
642
|
+
if (typeof eventSources[i] == 'object') {
|
643
|
+
eventSources[i] = $.grep(eventSources[i], filter, true);
|
644
|
+
}
|
645
|
+
}
|
646
|
+
}
|
647
|
+
eventsChanged();
|
648
|
+
},
|
649
|
+
|
650
|
+
clientEvents: function(filter) {
|
651
|
+
if ($.isFunction(filter)) {
|
652
|
+
return $.grep(events, filter);
|
653
|
+
}
|
654
|
+
else if (filter) { // an event ID
|
655
|
+
filter += '';
|
656
|
+
return $.grep(events, function(e) {
|
657
|
+
return e._id == filter;
|
658
|
+
});
|
659
|
+
}
|
660
|
+
return events; // else, return all
|
661
|
+
},
|
662
|
+
|
663
|
+
rerenderEvents: eventsChanged, // TODO: think of renaming eventsChanged
|
664
|
+
|
665
|
+
//
|
666
|
+
// Event Source
|
667
|
+
//
|
668
|
+
|
669
|
+
addEventSource: function(source) {
|
670
|
+
eventSources.push(source);
|
671
|
+
fetchEventSource(source, eventsChanged);
|
672
|
+
},
|
673
|
+
|
674
|
+
removeEventSource: function(source) {
|
675
|
+
eventSources = $.grep(eventSources, function(src) {
|
676
|
+
return src != source;
|
677
|
+
});
|
678
|
+
// remove all client events from that source
|
679
|
+
events = $.grep(events, function(e) {
|
680
|
+
return e.source != source;
|
681
|
+
});
|
682
|
+
eventsChanged();
|
683
|
+
},
|
684
|
+
|
685
|
+
refetchEvents: function() {
|
686
|
+
fetchEvents(eventsChanged);
|
687
|
+
},
|
688
|
+
|
689
|
+
//
|
690
|
+
// selection
|
691
|
+
//
|
692
|
+
|
693
|
+
select: function(start, end, allDay) {
|
694
|
+
view.select(start, end, allDay===undefined ? true : allDay);
|
695
|
+
},
|
696
|
+
|
697
|
+
unselect: function() {
|
698
|
+
view.unselect();
|
699
|
+
}
|
700
|
+
|
701
|
+
};
|
702
|
+
|
703
|
+
$.data(this, 'fullCalendar', publicMethods); // TODO: look into memory leak implications
|
704
|
+
|
705
|
+
|
706
|
+
|
707
|
+
/* Header
|
708
|
+
-----------------------------------------------------------------------------*/
|
709
|
+
|
710
|
+
var header,
|
711
|
+
sections = options.header;
|
712
|
+
if (sections) {
|
713
|
+
header = $("<table class='fc-header'/>")
|
714
|
+
.append($("<tr/>")
|
715
|
+
.append($("<td class='fc-header-left'/>").append(buildSection(sections.left)))
|
716
|
+
.append($("<td class='fc-header-center'/>").append(buildSection(sections.center)))
|
717
|
+
.append($("<td class='fc-header-right'/>").append(buildSection(sections.right))))
|
718
|
+
.prependTo(element);
|
719
|
+
}
|
720
|
+
function buildSection(buttonStr) {
|
721
|
+
if (buttonStr) {
|
722
|
+
var tr = $("<tr/>");
|
723
|
+
$.each(buttonStr.split(' '), function(i) {
|
724
|
+
if (i > 0) {
|
725
|
+
tr.append("<td><span class='fc-header-space'/></td>");
|
726
|
+
}
|
727
|
+
var prevButton;
|
728
|
+
$.each(this.split(','), function(j, buttonName) {
|
729
|
+
if (buttonName == 'title') {
|
730
|
+
tr.append("<td><h2 class='fc-header-title'> </h2></td>");
|
731
|
+
if (prevButton) {
|
732
|
+
prevButton.addClass(tm + '-corner-right');
|
733
|
+
}
|
734
|
+
prevButton = null;
|
735
|
+
}else{
|
736
|
+
var buttonClick;
|
737
|
+
if (publicMethods[buttonName]) {
|
738
|
+
buttonClick = publicMethods[buttonName];
|
739
|
+
}
|
740
|
+
else if (views[buttonName]) {
|
741
|
+
buttonClick = function() {
|
742
|
+
button.removeClass(tm + '-state-hover');
|
743
|
+
changeView(buttonName);
|
744
|
+
};
|
745
|
+
}
|
746
|
+
if (buttonClick) {
|
747
|
+
if (prevButton) {
|
748
|
+
prevButton.addClass(tm + '-no-right');
|
749
|
+
}
|
750
|
+
var button,
|
751
|
+
icon = options.theme ? smartProperty(options.buttonIcons, buttonName) : null,
|
752
|
+
text = smartProperty(options.buttonText, buttonName);
|
753
|
+
if (icon) {
|
754
|
+
button = $("<div class='fc-button-" + buttonName + " ui-state-default'>" +
|
755
|
+
"<a><span class='ui-icon ui-icon-" + icon + "'/></a></div>");
|
756
|
+
}
|
757
|
+
else if (text) {
|
758
|
+
button = $("<div class='fc-button-" + buttonName + " " + tm + "-state-default'>" +
|
759
|
+
"<a><span>" + text + "</span></a></div>");
|
760
|
+
}
|
761
|
+
if (button) {
|
762
|
+
button
|
763
|
+
.click(function() {
|
764
|
+
if (!button.hasClass(tm + '-state-disabled')) {
|
765
|
+
buttonClick();
|
766
|
+
}
|
767
|
+
})
|
768
|
+
.mousedown(function() {
|
769
|
+
button
|
770
|
+
.not('.' + tm + '-state-active')
|
771
|
+
.not('.' + tm + '-state-disabled')
|
772
|
+
.addClass(tm + '-state-down');
|
773
|
+
})
|
774
|
+
.mouseup(function() {
|
775
|
+
button.removeClass(tm + '-state-down');
|
776
|
+
})
|
777
|
+
.hover(
|
778
|
+
function() {
|
779
|
+
button
|
780
|
+
.not('.' + tm + '-state-active')
|
781
|
+
.not('.' + tm + '-state-disabled')
|
782
|
+
.addClass(tm + '-state-hover');
|
783
|
+
},
|
784
|
+
function() {
|
785
|
+
button
|
786
|
+
.removeClass(tm + '-state-hover')
|
787
|
+
.removeClass(tm + '-state-down');
|
788
|
+
}
|
789
|
+
)
|
790
|
+
.appendTo($("<td/>").appendTo(tr));
|
791
|
+
if (prevButton) {
|
792
|
+
prevButton.addClass(tm + '-no-right');
|
793
|
+
}else{
|
794
|
+
button.addClass(tm + '-corner-left');
|
795
|
+
}
|
796
|
+
prevButton = button;
|
797
|
+
}
|
798
|
+
}
|
799
|
+
}
|
800
|
+
});
|
801
|
+
if (prevButton) {
|
802
|
+
prevButton.addClass(tm + '-corner-right');
|
803
|
+
}
|
804
|
+
});
|
805
|
+
return $("<table/>").append(tr);
|
806
|
+
}
|
807
|
+
}
|
808
|
+
|
809
|
+
|
810
|
+
|
811
|
+
/* Resizing
|
812
|
+
-----------------------------------------------------------------------------*/
|
813
|
+
|
814
|
+
|
815
|
+
function calcSize() {
|
816
|
+
if (options.contentHeight) {
|
817
|
+
suggestedViewHeight = options.contentHeight;
|
818
|
+
}
|
819
|
+
else if (options.height) {
|
820
|
+
suggestedViewHeight = options.height - (header ? header.height() : 0) - vsides(content[0]);
|
821
|
+
}
|
822
|
+
else {
|
823
|
+
suggestedViewHeight = Math.round(content.width() / Math.max(options.aspectRatio, .5));
|
824
|
+
}
|
825
|
+
}
|
826
|
+
|
827
|
+
|
828
|
+
function setSize(dateChanged) {
|
829
|
+
ignoreWindowResize++;
|
830
|
+
view.setHeight(suggestedViewHeight, dateChanged);
|
831
|
+
if (absoluteViewElement) {
|
832
|
+
absoluteViewElement.css('position', 'relative');
|
833
|
+
absoluteViewElement = null;
|
834
|
+
}
|
835
|
+
view.setWidth(content.width(), dateChanged);
|
836
|
+
ignoreWindowResize--;
|
837
|
+
}
|
838
|
+
|
839
|
+
|
840
|
+
function windowResize() {
|
841
|
+
if (!ignoreWindowResize) {
|
842
|
+
if (view.start) { // view has already been rendered
|
843
|
+
var uid = ++resizeUID;
|
844
|
+
setTimeout(function() { // add a delay
|
845
|
+
if (uid == resizeUID && !ignoreWindowResize && elementVisible()) {
|
846
|
+
if (elementOuterWidth != (elementOuterWidth = element.outerWidth())) {
|
847
|
+
ignoreWindowResize++; // in case the windowResize callback changes the height
|
848
|
+
sizeChanged();
|
849
|
+
view.trigger('windowResize', _element);
|
850
|
+
ignoreWindowResize--;
|
851
|
+
}
|
852
|
+
}
|
853
|
+
}, 200);
|
854
|
+
}else{
|
855
|
+
// calendar must have been initialized in a 0x0 iframe that has just been resized
|
856
|
+
lateRender();
|
857
|
+
}
|
858
|
+
}
|
859
|
+
}
|
860
|
+
$(window).resize(windowResize);
|
861
|
+
|
862
|
+
|
863
|
+
// let's begin...
|
864
|
+
changeView(options.defaultView);
|
865
|
+
|
866
|
+
|
867
|
+
// needed for IE in a 0x0 iframe, b/c when it is resized, never triggers a windowResize
|
868
|
+
if (!bodyVisible()) {
|
869
|
+
lateRender();
|
870
|
+
}
|
871
|
+
|
872
|
+
|
873
|
+
// called when we know the calendar couldn't be rendered when it was initialized,
|
874
|
+
// but we think it's ready now
|
875
|
+
function lateRender() {
|
876
|
+
setTimeout(function() { // IE7 needs this so dimensions are calculated correctly
|
877
|
+
if (!view.start && bodyVisible()) { // !view.start makes sure this never happens more than once
|
878
|
+
render();
|
879
|
+
}
|
880
|
+
},0);
|
881
|
+
}
|
882
|
+
|
883
|
+
|
884
|
+
});
|
885
|
+
|
886
|
+
return this;
|
887
|
+
|
888
|
+
};
|
889
|
+
|
890
|
+
|
891
|
+
|
892
|
+
/* Important Event Utilities
|
893
|
+
-----------------------------------------------------------------------------*/
|
894
|
+
|
895
|
+
var fakeID = 0;
|
896
|
+
|
897
|
+
function normalizeEvent(event, options) {
|
898
|
+
event._id = event._id || (event.id === undefined ? '_fc' + fakeID++ : event.id + '');
|
899
|
+
if (event.date) {
|
900
|
+
if (!event.start) {
|
901
|
+
event.start = event.date;
|
902
|
+
}
|
903
|
+
delete event.date;
|
904
|
+
}
|
905
|
+
event._start = cloneDate(event.start = parseDate(event.start));
|
906
|
+
event.end = parseDate(event.end);
|
907
|
+
if (event.end && event.end <= event.start) {
|
908
|
+
event.end = null;
|
909
|
+
}
|
910
|
+
event._end = event.end ? cloneDate(event.end) : null;
|
911
|
+
if (event.allDay === undefined) {
|
912
|
+
event.allDay = options.allDayDefault;
|
913
|
+
}
|
914
|
+
if (event.className) {
|
915
|
+
if (typeof event.className == 'string') {
|
916
|
+
event.className = event.className.split(/\s+/);
|
917
|
+
}
|
918
|
+
}else{
|
919
|
+
event.className = [];
|
920
|
+
}
|
921
|
+
}
|
922
|
+
// TODO: if there is no start date, return false to indicate an invalid event
|
923
|
+
|
924
|
+
|
925
|
+
/* Grid-based Views: month, basicWeek, basicDay
|
926
|
+
-----------------------------------------------------------------------------*/
|
927
|
+
|
928
|
+
setDefaults({
|
929
|
+
weekMode: 'fixed'
|
930
|
+
});
|
931
|
+
|
932
|
+
views.month = function(element, options) {
|
933
|
+
return new Grid(element, options, {
|
934
|
+
render: function(date, delta) {
|
935
|
+
if (delta) {
|
936
|
+
addMonths(date, delta);
|
937
|
+
date.setDate(1);
|
938
|
+
}
|
939
|
+
// start/end
|
940
|
+
var start = this.start = cloneDate(date, true);
|
941
|
+
start.setDate(1);
|
942
|
+
this.end = addMonths(cloneDate(start), 1);
|
943
|
+
// visStart/visEnd
|
944
|
+
var visStart = this.visStart = cloneDate(start),
|
945
|
+
visEnd = this.visEnd = cloneDate(this.end),
|
946
|
+
nwe = options.weekends ? 0 : 1;
|
947
|
+
if (nwe) {
|
948
|
+
skipWeekend(visStart);
|
949
|
+
skipWeekend(visEnd, -1, true);
|
950
|
+
}
|
951
|
+
addDays(visStart, -((visStart.getDay() - Math.max(options.firstDay, nwe) + 7) % 7));
|
952
|
+
addDays(visEnd, (7 - visEnd.getDay() + Math.max(options.firstDay, nwe)) % 7);
|
953
|
+
// row count
|
954
|
+
var rowCnt = Math.round((visEnd - visStart) / (DAY_MS * 7));
|
955
|
+
if (options.weekMode == 'fixed') {
|
956
|
+
addDays(visEnd, (6 - rowCnt) * 7);
|
957
|
+
rowCnt = 6;
|
958
|
+
}
|
959
|
+
// title
|
960
|
+
this.title = formatDate(
|
961
|
+
start,
|
962
|
+
this.option('titleFormat'),
|
963
|
+
options
|
964
|
+
);
|
965
|
+
// render
|
966
|
+
this.renderGrid(
|
967
|
+
rowCnt, options.weekends ? 7 : 5,
|
968
|
+
this.option('columnFormat'),
|
969
|
+
true
|
970
|
+
);
|
971
|
+
}
|
972
|
+
});
|
973
|
+
};
|
974
|
+
|
975
|
+
views.basicWeek = function(element, options) {
|
976
|
+
return new Grid(element, options, {
|
977
|
+
render: function(date, delta) {
|
978
|
+
if (delta) {
|
979
|
+
addDays(date, delta * 7);
|
980
|
+
}
|
981
|
+
var visStart = this.visStart = cloneDate(
|
982
|
+
this.start = addDays(cloneDate(date), -((date.getDay() - options.firstDay + 7) % 7))
|
983
|
+
),
|
984
|
+
visEnd = this.visEnd = cloneDate(
|
985
|
+
this.end = addDays(cloneDate(visStart), 7)
|
986
|
+
);
|
987
|
+
if (!options.weekends) {
|
988
|
+
skipWeekend(visStart);
|
989
|
+
skipWeekend(visEnd, -1, true);
|
990
|
+
}
|
991
|
+
this.title = formatDates(
|
992
|
+
visStart,
|
993
|
+
addDays(cloneDate(visEnd), -1),
|
994
|
+
this.option('titleFormat'),
|
995
|
+
options
|
996
|
+
);
|
997
|
+
this.renderGrid(
|
998
|
+
1, options.weekends ? 7 : 5,
|
999
|
+
this.option('columnFormat'),
|
1000
|
+
false
|
1001
|
+
);
|
1002
|
+
}
|
1003
|
+
});
|
1004
|
+
};
|
1005
|
+
|
1006
|
+
views.basicDay = function(element, options) {
|
1007
|
+
return new Grid(element, options, {
|
1008
|
+
render: function(date, delta) {
|
1009
|
+
if (delta) {
|
1010
|
+
addDays(date, delta);
|
1011
|
+
if (!options.weekends) {
|
1012
|
+
skipWeekend(date, delta < 0 ? -1 : 1);
|
1013
|
+
}
|
1014
|
+
}
|
1015
|
+
this.title = formatDate(date, this.option('titleFormat'), options);
|
1016
|
+
this.start = this.visStart = cloneDate(date, true);
|
1017
|
+
this.end = this.visEnd = addDays(cloneDate(this.start), 1);
|
1018
|
+
this.renderGrid(
|
1019
|
+
1, 1,
|
1020
|
+
this.option('columnFormat'),
|
1021
|
+
false
|
1022
|
+
);
|
1023
|
+
}
|
1024
|
+
});
|
1025
|
+
};
|
1026
|
+
|
1027
|
+
|
1028
|
+
// rendering bugs
|
1029
|
+
|
1030
|
+
var tdHeightBug;
|
1031
|
+
|
1032
|
+
|
1033
|
+
function Grid(element, options, methods) {
|
1034
|
+
|
1035
|
+
var tm, firstDay,
|
1036
|
+
nwe, // no weekends (int)
|
1037
|
+
rtl, dis, dit, // day index sign / translate
|
1038
|
+
viewWidth, viewHeight,
|
1039
|
+
rowCnt, colCnt,
|
1040
|
+
colWidth,
|
1041
|
+
thead, tbody,
|
1042
|
+
cachedEvents=[],
|
1043
|
+
segmentContainer,
|
1044
|
+
dayContentPositions = new HorizontalPositionCache(function(dayOfWeek) {
|
1045
|
+
return tbody.find('td:eq(' + ((dayOfWeek - Math.max(firstDay,nwe)+colCnt) % colCnt) + ') div div');
|
1046
|
+
}),
|
1047
|
+
selectionManager,
|
1048
|
+
selectionMatrix,
|
1049
|
+
// ...
|
1050
|
+
|
1051
|
+
// initialize superclass
|
1052
|
+
view = $.extend(this, viewMethods, methods, {
|
1053
|
+
renderGrid: renderGrid,
|
1054
|
+
renderEvents: renderEvents,
|
1055
|
+
rerenderEvents: rerenderEvents,
|
1056
|
+
clearEvents: clearEvents,
|
1057
|
+
setHeight: setHeight,
|
1058
|
+
setWidth: setWidth,
|
1059
|
+
defaultEventEnd: function(event) { // calculates an end if event doesnt have one, mostly for resizing
|
1060
|
+
return cloneDate(event.start);
|
1061
|
+
}
|
1062
|
+
});
|
1063
|
+
view.init(element, options);
|
1064
|
+
|
1065
|
+
|
1066
|
+
|
1067
|
+
/* Grid Rendering
|
1068
|
+
-----------------------------------------------------------------------------*/
|
1069
|
+
|
1070
|
+
|
1071
|
+
disableTextSelection(element.addClass('fc-grid'));
|
1072
|
+
|
1073
|
+
|
1074
|
+
function renderGrid(r, c, colFormat, showNumbers) {
|
1075
|
+
|
1076
|
+
rowCnt = r;
|
1077
|
+
colCnt = c;
|
1078
|
+
|
1079
|
+
// update option-derived variables
|
1080
|
+
tm = options.theme ? 'ui' : 'fc';
|
1081
|
+
nwe = options.weekends ? 0 : 1;
|
1082
|
+
firstDay = options.firstDay;
|
1083
|
+
if (rtl = options.isRTL) {
|
1084
|
+
dis = -1;
|
1085
|
+
dit = colCnt - 1;
|
1086
|
+
}else{
|
1087
|
+
dis = 1;
|
1088
|
+
dit = 0;
|
1089
|
+
}
|
1090
|
+
|
1091
|
+
var month = view.start.getMonth(),
|
1092
|
+
today = clearTime(new Date()),
|
1093
|
+
s, i, j, d = cloneDate(view.visStart);
|
1094
|
+
|
1095
|
+
if (!tbody) { // first time, build all cells from scratch
|
1096
|
+
|
1097
|
+
var table = $("<table/>").appendTo(element);
|
1098
|
+
|
1099
|
+
s = "<thead><tr>";
|
1100
|
+
for (i=0; i<colCnt; i++) {
|
1101
|
+
s += "<th class='fc-" +
|
1102
|
+
dayIDs[d.getDay()] + ' ' + // needs to be first
|
1103
|
+
tm + '-state-default' +
|
1104
|
+
(i==dit ? ' fc-leftmost' : '') +
|
1105
|
+
"'>" + formatDate(d, colFormat, options) + "</th>";
|
1106
|
+
addDays(d, 1);
|
1107
|
+
if (nwe) {
|
1108
|
+
skipWeekend(d);
|
1109
|
+
}
|
1110
|
+
}
|
1111
|
+
thead = $(s + "</tr></thead>").appendTo(table);
|
1112
|
+
|
1113
|
+
s = "<tbody>";
|
1114
|
+
d = cloneDate(view.visStart);
|
1115
|
+
for (i=0; i<rowCnt; i++) {
|
1116
|
+
s += "<tr class='fc-week" + i + "'>";
|
1117
|
+
for (j=0; j<colCnt; j++) {
|
1118
|
+
s += "<td class='fc-" +
|
1119
|
+
dayIDs[d.getDay()] + ' ' + // needs to be first
|
1120
|
+
tm + '-state-default fc-day' + (i*colCnt+j) +
|
1121
|
+
(j==dit ? ' fc-leftmost' : '') +
|
1122
|
+
(rowCnt>1 && d.getMonth() != month ? ' fc-other-month' : '') +
|
1123
|
+
(+d == +today ?
|
1124
|
+
' fc-today '+tm+'-state-highlight' :
|
1125
|
+
' fc-not-today') + "'>" +
|
1126
|
+
(showNumbers ? "<div class='fc-day-number'>" + d.getDate() + "</div>" : '') +
|
1127
|
+
"<div class='fc-day-content'><div style='position:relative'> </div></div></td>";
|
1128
|
+
addDays(d, 1);
|
1129
|
+
if (nwe) {
|
1130
|
+
skipWeekend(d);
|
1131
|
+
}
|
1132
|
+
}
|
1133
|
+
s += "</tr>";
|
1134
|
+
}
|
1135
|
+
tbody = $(s + "</tbody>").appendTo(table);
|
1136
|
+
dayBind(tbody.find('td'));
|
1137
|
+
|
1138
|
+
segmentContainer = $("<div style='position:absolute;z-index:8;top:0;left:0'/>").appendTo(element);
|
1139
|
+
|
1140
|
+
}else{ // NOT first time, reuse as many cells as possible
|
1141
|
+
|
1142
|
+
clearEvents();
|
1143
|
+
|
1144
|
+
var prevRowCnt = tbody.find('tr').length;
|
1145
|
+
if (rowCnt < prevRowCnt) {
|
1146
|
+
tbody.find('tr:gt(' + (rowCnt-1) + ')').remove(); // remove extra rows
|
1147
|
+
}
|
1148
|
+
else if (rowCnt > prevRowCnt) { // needs to create new rows...
|
1149
|
+
s = '';
|
1150
|
+
for (i=prevRowCnt; i<rowCnt; i++) {
|
1151
|
+
s += "<tr class='fc-week" + i + "'>";
|
1152
|
+
for (j=0; j<colCnt; j++) {
|
1153
|
+
s += "<td class='fc-" +
|
1154
|
+
dayIDs[d.getDay()] + ' ' + // needs to be first
|
1155
|
+
tm + '-state-default fc-new fc-day' + (i*colCnt+j) +
|
1156
|
+
(j==dit ? ' fc-leftmost' : '') + "'>" +
|
1157
|
+
(showNumbers ? "<div class='fc-day-number'></div>" : '') +
|
1158
|
+
"<div class='fc-day-content'><div style='position:relative'> </div></div>" +
|
1159
|
+
"</td>";
|
1160
|
+
addDays(d, 1);
|
1161
|
+
if (nwe) {
|
1162
|
+
skipWeekend(d);
|
1163
|
+
}
|
1164
|
+
}
|
1165
|
+
s += "</tr>";
|
1166
|
+
}
|
1167
|
+
tbody.append(s);
|
1168
|
+
}
|
1169
|
+
dayBind(tbody.find('td.fc-new').removeClass('fc-new'));
|
1170
|
+
|
1171
|
+
// re-label and re-class existing cells
|
1172
|
+
d = cloneDate(view.visStart);
|
1173
|
+
tbody.find('td').each(function() {
|
1174
|
+
var td = $(this);
|
1175
|
+
if (rowCnt > 1) {
|
1176
|
+
if (d.getMonth() == month) {
|
1177
|
+
td.removeClass('fc-other-month');
|
1178
|
+
}else{
|
1179
|
+
td.addClass('fc-other-month');
|
1180
|
+
}
|
1181
|
+
}
|
1182
|
+
if (+d == +today) {
|
1183
|
+
td.removeClass('fc-not-today')
|
1184
|
+
.addClass('fc-today')
|
1185
|
+
.addClass(tm + '-state-highlight');
|
1186
|
+
}else{
|
1187
|
+
td.addClass('fc-not-today')
|
1188
|
+
.removeClass('fc-today')
|
1189
|
+
.removeClass(tm + '-state-highlight');
|
1190
|
+
}
|
1191
|
+
td.find('div.fc-day-number').text(d.getDate());
|
1192
|
+
addDays(d, 1);
|
1193
|
+
if (nwe) {
|
1194
|
+
skipWeekend(d);
|
1195
|
+
}
|
1196
|
+
});
|
1197
|
+
|
1198
|
+
if (rowCnt == 1) { // more changes likely (week or day view)
|
1199
|
+
|
1200
|
+
// redo column header text and class
|
1201
|
+
d = cloneDate(view.visStart);
|
1202
|
+
thead.find('th').each(function() {
|
1203
|
+
$(this).text(formatDate(d, colFormat, options));
|
1204
|
+
this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]);
|
1205
|
+
addDays(d, 1);
|
1206
|
+
if (nwe) {
|
1207
|
+
skipWeekend(d);
|
1208
|
+
}
|
1209
|
+
});
|
1210
|
+
|
1211
|
+
// redo cell day-of-weeks
|
1212
|
+
d = cloneDate(view.visStart);
|
1213
|
+
tbody.find('td').each(function() {
|
1214
|
+
this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]);
|
1215
|
+
addDays(d, 1);
|
1216
|
+
if (nwe) {
|
1217
|
+
skipWeekend(d);
|
1218
|
+
}
|
1219
|
+
});
|
1220
|
+
|
1221
|
+
}
|
1222
|
+
|
1223
|
+
}
|
1224
|
+
|
1225
|
+
}
|
1226
|
+
|
1227
|
+
|
1228
|
+
|
1229
|
+
function setHeight(height) {
|
1230
|
+
viewHeight = height;
|
1231
|
+
var leftTDs = tbody.find('tr td:first-child'),
|
1232
|
+
tbodyHeight = viewHeight - thead.height(),
|
1233
|
+
rowHeight1, rowHeight2;
|
1234
|
+
if (options.weekMode == 'variable') {
|
1235
|
+
rowHeight1 = rowHeight2 = Math.floor(tbodyHeight / (rowCnt==1 ? 2 : 6));
|
1236
|
+
}else{
|
1237
|
+
rowHeight1 = Math.floor(tbodyHeight / rowCnt);
|
1238
|
+
rowHeight2 = tbodyHeight - rowHeight1*(rowCnt-1);
|
1239
|
+
}
|
1240
|
+
if (tdHeightBug === undefined) {
|
1241
|
+
// bug in firefox where cell height includes padding
|
1242
|
+
var tr = tbody.find('tr:first'),
|
1243
|
+
td = tr.find('td:first');
|
1244
|
+
td.height(rowHeight1);
|
1245
|
+
tdHeightBug = rowHeight1 != td.height();
|
1246
|
+
}
|
1247
|
+
if (tdHeightBug) {
|
1248
|
+
leftTDs.slice(0, -1).height(rowHeight1);
|
1249
|
+
leftTDs.slice(-1).height(rowHeight2);
|
1250
|
+
}else{
|
1251
|
+
setOuterHeight(leftTDs.slice(0, -1), rowHeight1);
|
1252
|
+
setOuterHeight(leftTDs.slice(-1), rowHeight2);
|
1253
|
+
}
|
1254
|
+
}
|
1255
|
+
|
1256
|
+
|
1257
|
+
function setWidth(width) {
|
1258
|
+
viewWidth = width;
|
1259
|
+
dayContentPositions.clear();
|
1260
|
+
setOuterWidth(
|
1261
|
+
thead.find('th').slice(0, -1),
|
1262
|
+
colWidth = Math.floor(viewWidth / colCnt)
|
1263
|
+
);
|
1264
|
+
}
|
1265
|
+
|
1266
|
+
|
1267
|
+
|
1268
|
+
/* Event Rendering
|
1269
|
+
-----------------------------------------------------------------------------*/
|
1270
|
+
|
1271
|
+
|
1272
|
+
function renderEvents(events) {
|
1273
|
+
view.reportEvents(cachedEvents = events);
|
1274
|
+
renderSegs(compileSegs(events));
|
1275
|
+
}
|
1276
|
+
|
1277
|
+
|
1278
|
+
function rerenderEvents(modifiedEventId) {
|
1279
|
+
clearEvents();
|
1280
|
+
renderSegs(compileSegs(cachedEvents), modifiedEventId);
|
1281
|
+
}
|
1282
|
+
|
1283
|
+
|
1284
|
+
function clearEvents() {
|
1285
|
+
view._clearEvents(); // only clears the hashes
|
1286
|
+
segmentContainer.empty();
|
1287
|
+
}
|
1288
|
+
|
1289
|
+
|
1290
|
+
function compileSegs(events) {
|
1291
|
+
var d1 = cloneDate(view.visStart),
|
1292
|
+
d2 = addDays(cloneDate(d1), colCnt),
|
1293
|
+
visEventsEnds = $.map(events, exclEndDay),
|
1294
|
+
i, row,
|
1295
|
+
j, level,
|
1296
|
+
k, seg,
|
1297
|
+
segs=[];
|
1298
|
+
for (i=0; i<rowCnt; i++) {
|
1299
|
+
row = stackSegs(view.sliceSegs(events, visEventsEnds, d1, d2));
|
1300
|
+
for (j=0; j<row.length; j++) {
|
1301
|
+
level = row[j];
|
1302
|
+
for (k=0; k<level.length; k++) {
|
1303
|
+
seg = level[k];
|
1304
|
+
seg.row = i;
|
1305
|
+
seg.level = j;
|
1306
|
+
segs.push(seg);
|
1307
|
+
}
|
1308
|
+
}
|
1309
|
+
addDays(d1, 7);
|
1310
|
+
addDays(d2, 7);
|
1311
|
+
}
|
1312
|
+
return segs;
|
1313
|
+
}
|
1314
|
+
|
1315
|
+
|
1316
|
+
function renderSegs(segs, modifiedEventId) {
|
1317
|
+
_renderDaySegs(
|
1318
|
+
segs,
|
1319
|
+
rowCnt,
|
1320
|
+
view,
|
1321
|
+
0,
|
1322
|
+
viewWidth,
|
1323
|
+
function(i) { return tbody.find('tr:eq('+i+')') },
|
1324
|
+
dayContentPositions.left,
|
1325
|
+
dayContentPositions.right,
|
1326
|
+
segmentContainer,
|
1327
|
+
bindSegHandlers,
|
1328
|
+
modifiedEventId
|
1329
|
+
);
|
1330
|
+
}
|
1331
|
+
|
1332
|
+
|
1333
|
+
function bindSegHandlers(event, eventElement, seg) {
|
1334
|
+
view.eventElementHandlers(event, eventElement);
|
1335
|
+
if (event.editable || event.editable === undefined && options.editable) {
|
1336
|
+
draggableEvent(event, eventElement);
|
1337
|
+
if (seg.isEnd) {
|
1338
|
+
view.resizableDayEvent(event, eventElement, colWidth);
|
1339
|
+
}
|
1340
|
+
}
|
1341
|
+
}
|
1342
|
+
|
1343
|
+
|
1344
|
+
|
1345
|
+
/* Event Dragging
|
1346
|
+
-----------------------------------------------------------------------------*/
|
1347
|
+
|
1348
|
+
|
1349
|
+
function draggableEvent(event, eventElement) {
|
1350
|
+
if (!options.disableDragging && eventElement.draggable) {
|
1351
|
+
var matrix,
|
1352
|
+
dayDelta = 0;
|
1353
|
+
eventElement.draggable({
|
1354
|
+
zIndex: 9,
|
1355
|
+
delay: 50,
|
1356
|
+
opacity: view.option('dragOpacity'),
|
1357
|
+
revertDuration: options.dragRevertDuration,
|
1358
|
+
start: function(ev, ui) {
|
1359
|
+
view.hideEvents(event, eventElement);
|
1360
|
+
view.trigger('eventDragStart', eventElement, event, ev, ui);
|
1361
|
+
matrix = buildDayMatrix(function(cell) {
|
1362
|
+
eventElement.draggable('option', 'revert', !cell || !cell.rowDelta && !cell.colDelta);
|
1363
|
+
clearOverlays();
|
1364
|
+
if (cell) {
|
1365
|
+
dayDelta = cell.rowDelta*7 + cell.colDelta*dis;
|
1366
|
+
renderDayOverlays(
|
1367
|
+
matrix,
|
1368
|
+
addDays(cloneDate(event.start), dayDelta),
|
1369
|
+
addDays(exclEndDay(event), dayDelta)
|
1370
|
+
);
|
1371
|
+
}else{
|
1372
|
+
dayDelta = 0;
|
1373
|
+
}
|
1374
|
+
});
|
1375
|
+
matrix.mouse(ev);
|
1376
|
+
},
|
1377
|
+
drag: function(ev) {
|
1378
|
+
matrix.mouse(ev);
|
1379
|
+
},
|
1380
|
+
stop: function(ev, ui) {
|
1381
|
+
clearOverlays();
|
1382
|
+
view.trigger('eventDragStop', eventElement, event, ev, ui);
|
1383
|
+
if (dayDelta) {
|
1384
|
+
eventElement.find('a').removeAttr('href'); // prevents safari from visiting the link
|
1385
|
+
view.eventDrop(this, event, dayDelta, 0, event.allDay, ev, ui);
|
1386
|
+
}else{
|
1387
|
+
if ($.browser.msie) {
|
1388
|
+
eventElement.css('filter', ''); // clear IE opacity side-effects
|
1389
|
+
}
|
1390
|
+
view.showEvents(event, eventElement);
|
1391
|
+
}
|
1392
|
+
}
|
1393
|
+
});
|
1394
|
+
}
|
1395
|
+
}
|
1396
|
+
|
1397
|
+
|
1398
|
+
|
1399
|
+
/* Day clicking and binding
|
1400
|
+
---------------------------------------------------------*/
|
1401
|
+
|
1402
|
+
function dayBind(days) {
|
1403
|
+
days.click(dayClick)
|
1404
|
+
.mousedown(selectionMousedown);
|
1405
|
+
}
|
1406
|
+
|
1407
|
+
function dayClick(ev) {
|
1408
|
+
if (!view.option('selectable')) { // SelectionManager will worry about dayClick
|
1409
|
+
var n = parseInt(this.className.match(/fc\-day(\d+)/)[1]),
|
1410
|
+
date = addDays(
|
1411
|
+
cloneDate(view.visStart),
|
1412
|
+
Math.floor(n/colCnt) * 7 + n % colCnt
|
1413
|
+
);
|
1414
|
+
// TODO: what about weekends in middle of week?
|
1415
|
+
view.trigger('dayClick', this, date, true, ev);
|
1416
|
+
}
|
1417
|
+
}
|
1418
|
+
|
1419
|
+
|
1420
|
+
|
1421
|
+
/* Selecting
|
1422
|
+
--------------------------------------------------------*/
|
1423
|
+
|
1424
|
+
selectionManager = new SelectionManager(
|
1425
|
+
view,
|
1426
|
+
unselect,
|
1427
|
+
function(startDate, endDate, allDay) {
|
1428
|
+
renderDayOverlays(
|
1429
|
+
selectionMatrix,
|
1430
|
+
startDate,
|
1431
|
+
addDays(cloneDate(endDate), 1)
|
1432
|
+
);
|
1433
|
+
},
|
1434
|
+
clearOverlays
|
1435
|
+
);
|
1436
|
+
|
1437
|
+
function selectionMousedown(ev) {
|
1438
|
+
if (view.option('selectable')) {
|
1439
|
+
selectionMatrix = buildDayMatrix(function(cell) {
|
1440
|
+
if (cell) {
|
1441
|
+
var d = cellDate(cell.row, cell.col);
|
1442
|
+
selectionManager.drag(d, d, true);
|
1443
|
+
}else{
|
1444
|
+
selectionManager.drag();
|
1445
|
+
}
|
1446
|
+
});
|
1447
|
+
documentDragHelp(
|
1448
|
+
function(ev) {
|
1449
|
+
selectionMatrix.mouse(ev);
|
1450
|
+
},
|
1451
|
+
function(ev) {
|
1452
|
+
selectionManager.dragStop(ev);
|
1453
|
+
}
|
1454
|
+
);
|
1455
|
+
selectionManager.dragStart(ev);
|
1456
|
+
selectionMatrix.mouse(ev);
|
1457
|
+
return false; // prevent auto-unselect and text selection
|
1458
|
+
}
|
1459
|
+
}
|
1460
|
+
|
1461
|
+
documentUnselectAuto(view, unselect);
|
1462
|
+
|
1463
|
+
view.select = function(start, end, allDay) {
|
1464
|
+
if (!end) {
|
1465
|
+
end = cloneDate(start);
|
1466
|
+
}
|
1467
|
+
selectionMatrix = buildDayMatrix();
|
1468
|
+
selectionManager.select(start, end, allDay);
|
1469
|
+
};
|
1470
|
+
|
1471
|
+
function unselect() {
|
1472
|
+
selectionManager.unselect();
|
1473
|
+
}
|
1474
|
+
view.unselect = unselect;
|
1475
|
+
|
1476
|
+
|
1477
|
+
|
1478
|
+
|
1479
|
+
/* Semi-transparent Overlay Helpers
|
1480
|
+
------------------------------------------------------*/
|
1481
|
+
|
1482
|
+
function renderDayOverlays(matrix, overlayStart, overlayEnd) { // overlayEnd is exclusive
|
1483
|
+
var rowStart = cloneDate(view.visStart);
|
1484
|
+
var rowEnd = addDays(cloneDate(rowStart), colCnt);
|
1485
|
+
for (var i=0; i<rowCnt; i++) {
|
1486
|
+
var stretchStart = new Date(Math.max(rowStart, overlayStart));
|
1487
|
+
var stretchEnd = new Date(Math.min(rowEnd, overlayEnd));
|
1488
|
+
if (stretchStart < stretchEnd) {
|
1489
|
+
var colStart, colEnd;
|
1490
|
+
if (rtl) {
|
1491
|
+
colStart = dayDiff(stretchEnd, rowStart)*dis+dit+1;
|
1492
|
+
colEnd = dayDiff(stretchStart, rowStart)*dis+dit+1;
|
1493
|
+
}else{
|
1494
|
+
colStart = dayDiff(stretchStart, rowStart);
|
1495
|
+
colEnd = dayDiff(stretchEnd, rowStart);
|
1496
|
+
}
|
1497
|
+
var rect = matrix.rect(i, colStart, i+1, colEnd, element);
|
1498
|
+
dayBind(
|
1499
|
+
view.renderOverlay(rect, element)
|
1500
|
+
);
|
1501
|
+
}
|
1502
|
+
addDays(rowStart, 7);
|
1503
|
+
addDays(rowEnd, 7);
|
1504
|
+
}
|
1505
|
+
}
|
1506
|
+
|
1507
|
+
function clearOverlays() {
|
1508
|
+
view.clearOverlays();
|
1509
|
+
}
|
1510
|
+
|
1511
|
+
|
1512
|
+
|
1513
|
+
|
1514
|
+
/* Utils
|
1515
|
+
---------------------------------------------------*/
|
1516
|
+
|
1517
|
+
|
1518
|
+
function buildDayMatrix(changeCallback) {
|
1519
|
+
var tds = tbody.find('tr:first td');
|
1520
|
+
if (rtl) {
|
1521
|
+
tds = $(tds.get().reverse());
|
1522
|
+
}
|
1523
|
+
return new HoverMatrix(tbody.find('tr'), tds, changeCallback);
|
1524
|
+
}
|
1525
|
+
|
1526
|
+
|
1527
|
+
function cellDate(r, c) { // convert r,c to date
|
1528
|
+
return addDays(cloneDate(view.visStart), r*7 + c*dis+dit);
|
1529
|
+
// TODO: what about weekends in middle of week?
|
1530
|
+
}
|
1531
|
+
|
1532
|
+
|
1533
|
+
}
|
1534
|
+
|
1535
|
+
|
1536
|
+
function _renderDaySegs(segs, rowCnt, view, minLeft, maxLeft, getRow, dayContentLeft, dayContentRight, segmentContainer, bindSegHandlers, modifiedEventId) {
|
1537
|
+
|
1538
|
+
var options=view.options,
|
1539
|
+
rtl=options.isRTL,
|
1540
|
+
i, segCnt=segs.length, seg,
|
1541
|
+
event,
|
1542
|
+
className,
|
1543
|
+
left, right,
|
1544
|
+
html='',
|
1545
|
+
eventElements,
|
1546
|
+
eventElement,
|
1547
|
+
triggerRes,
|
1548
|
+
hsideCache={},
|
1549
|
+
vmarginCache={},
|
1550
|
+
key, val,
|
1551
|
+
rowI, top, levelI, levelHeight,
|
1552
|
+
rowDivs=[],
|
1553
|
+
rowDivTops=[];
|
1554
|
+
|
1555
|
+
// calculate desired position/dimensions, create html
|
1556
|
+
for (i=0; i<segCnt; i++) {
|
1557
|
+
seg = segs[i];
|
1558
|
+
event = seg.event;
|
1559
|
+
className = 'fc-event fc-event-hori ';
|
1560
|
+
if (rtl) {
|
1561
|
+
if (seg.isStart) {
|
1562
|
+
className += 'fc-corner-right ';
|
1563
|
+
}
|
1564
|
+
if (seg.isEnd) {
|
1565
|
+
className += 'fc-corner-left ';
|
1566
|
+
}
|
1567
|
+
left = seg.isEnd ? dayContentLeft(seg.end.getDay()-1) : minLeft;
|
1568
|
+
right = seg.isStart ? dayContentRight(seg.start.getDay()) : maxLeft;
|
1569
|
+
}else{
|
1570
|
+
if (seg.isStart) {
|
1571
|
+
className += 'fc-corner-left ';
|
1572
|
+
}
|
1573
|
+
if (seg.isEnd) {
|
1574
|
+
className += 'fc-corner-right ';
|
1575
|
+
}
|
1576
|
+
left = seg.isStart ? dayContentLeft(seg.start.getDay()) : minLeft;
|
1577
|
+
right = seg.isEnd ? dayContentRight(seg.end.getDay()-1) : maxLeft;
|
1578
|
+
}
|
1579
|
+
html +=
|
1580
|
+
"<div class='" + className + event.className.join(' ') + "' style='position:absolute;z-index:8;left:"+left+"px'>" +
|
1581
|
+
"<a" + (event.url ? " href='" + htmlEscape(event.url) + "'" : '') + ">" +
|
1582
|
+
(!event.allDay && seg.isStart ?
|
1583
|
+
"<span class='fc-event-time'>" +
|
1584
|
+
htmlEscape(formatDates(event.start, event.end, view.option('timeFormat'), options)) +
|
1585
|
+
"</span>"
|
1586
|
+
:'') +
|
1587
|
+
"<span class='fc-event-title'>" + htmlEscape(event.title) + "</span>" +
|
1588
|
+
"</a>" +
|
1589
|
+
((event.editable || event.editable === undefined && options.editable) && !options.disableResizing && $.fn.resizable ?
|
1590
|
+
"<div class='ui-resizable-handle ui-resizable-" + (rtl ? 'w' : 'e') + "'></div>"
|
1591
|
+
: '') +
|
1592
|
+
"</div>";
|
1593
|
+
seg.left = left;
|
1594
|
+
seg.outerWidth = right - left;
|
1595
|
+
}
|
1596
|
+
segmentContainer[0].innerHTML = html; // faster than html()
|
1597
|
+
eventElements = segmentContainer.children();
|
1598
|
+
|
1599
|
+
// retrieve elements, run through eventRender callback, bind handlers
|
1600
|
+
for (i=0; i<segCnt; i++) {
|
1601
|
+
seg = segs[i];
|
1602
|
+
eventElement = $(eventElements[i]); // faster than eq()
|
1603
|
+
event = seg.event;
|
1604
|
+
triggerRes = view.trigger('eventRender', event, event, eventElement);
|
1605
|
+
if (triggerRes === false) {
|
1606
|
+
eventElement.remove();
|
1607
|
+
}else{
|
1608
|
+
if (triggerRes && triggerRes !== true) {
|
1609
|
+
eventElement.remove();
|
1610
|
+
eventElement = $(triggerRes)
|
1611
|
+
.css({
|
1612
|
+
position: 'absolute',
|
1613
|
+
left: seg.left
|
1614
|
+
})
|
1615
|
+
.appendTo(segmentContainer);
|
1616
|
+
}
|
1617
|
+
seg.element = eventElement;
|
1618
|
+
if (event._id === modifiedEventId) {
|
1619
|
+
bindSegHandlers(event, eventElement, seg);
|
1620
|
+
}else{
|
1621
|
+
eventElement[0]._fci = i; // for lazySegBind
|
1622
|
+
}
|
1623
|
+
view.reportEventElement(event, eventElement);
|
1624
|
+
}
|
1625
|
+
}
|
1626
|
+
|
1627
|
+
lazySegBind(segmentContainer, segs, bindSegHandlers);
|
1628
|
+
|
1629
|
+
// record event horizontal sides
|
1630
|
+
for (i=0; i<segCnt; i++) {
|
1631
|
+
seg = segs[i];
|
1632
|
+
if (eventElement = seg.element) {
|
1633
|
+
val = hsideCache[key = seg.key = cssKey(eventElement[0])];
|
1634
|
+
seg.hsides = val === undefined ? (hsideCache[key] = hsides(eventElement[0], true)) : val;
|
1635
|
+
}
|
1636
|
+
}
|
1637
|
+
|
1638
|
+
// set event widths
|
1639
|
+
for (i=0; i<segCnt; i++) {
|
1640
|
+
seg = segs[i];
|
1641
|
+
if (eventElement = seg.element) {
|
1642
|
+
eventElement[0].style.width = seg.outerWidth - seg.hsides + 'px';
|
1643
|
+
}
|
1644
|
+
}
|
1645
|
+
|
1646
|
+
// record event heights
|
1647
|
+
for (i=0; i<segCnt; i++) {
|
1648
|
+
seg = segs[i];
|
1649
|
+
if (eventElement = seg.element) {
|
1650
|
+
val = vmarginCache[key = seg.key];
|
1651
|
+
seg.outerHeight = eventElement[0].offsetHeight + (
|
1652
|
+
val === undefined ? (vmarginCache[key] = vmargins(eventElement[0])) : val
|
1653
|
+
);
|
1654
|
+
}
|
1655
|
+
}
|
1656
|
+
|
1657
|
+
// set row heights, calculate event tops (in relation to row top)
|
1658
|
+
for (i=0, rowI=0; rowI<rowCnt; rowI++) {
|
1659
|
+
top = levelI = levelHeight = 0;
|
1660
|
+
while (i<segCnt && (seg = segs[i]).row == rowI) {
|
1661
|
+
if (seg.level != levelI) {
|
1662
|
+
top += levelHeight;
|
1663
|
+
levelHeight = 0;
|
1664
|
+
levelI++;
|
1665
|
+
}
|
1666
|
+
levelHeight = Math.max(levelHeight, seg.outerHeight||0);
|
1667
|
+
seg.top = top;
|
1668
|
+
i++;
|
1669
|
+
}
|
1670
|
+
rowDivs[rowI] = getRow(rowI).find('td:first div.fc-day-content > div') // optimal selector?
|
1671
|
+
.height(top + levelHeight);
|
1672
|
+
}
|
1673
|
+
|
1674
|
+
// calculate row tops
|
1675
|
+
for (rowI=0; rowI<rowCnt; rowI++) {
|
1676
|
+
rowDivTops[rowI] = rowDivs[rowI][0].offsetTop;
|
1677
|
+
}
|
1678
|
+
|
1679
|
+
// set event tops
|
1680
|
+
for (i=0; i<segCnt; i++) {
|
1681
|
+
seg = segs[i];
|
1682
|
+
if (eventElement = seg.element) {
|
1683
|
+
eventElement[0].style.top = rowDivTops[seg.row] + seg.top + 'px';
|
1684
|
+
event = seg.event;
|
1685
|
+
view.trigger('eventAfterRender', event, event, eventElement);
|
1686
|
+
}
|
1687
|
+
}
|
1688
|
+
|
1689
|
+
}
|
1690
|
+
|
1691
|
+
|
1692
|
+
|
1693
|
+
/* Agenda Views: agendaWeek/agendaDay
|
1694
|
+
-----------------------------------------------------------------------------*/
|
1695
|
+
|
1696
|
+
setDefaults({
|
1697
|
+
allDaySlot: true,
|
1698
|
+
allDayText: 'all-day',
|
1699
|
+
firstHour: 6,
|
1700
|
+
slotMinutes: 30,
|
1701
|
+
defaultEventMinutes: 120,
|
1702
|
+
axisFormat: 'h(:mm)tt',
|
1703
|
+
timeFormat: {
|
1704
|
+
agenda: 'h:mm{ - h:mm}'
|
1705
|
+
},
|
1706
|
+
dragOpacity: {
|
1707
|
+
agenda: .5
|
1708
|
+
},
|
1709
|
+
minTime: 0,
|
1710
|
+
maxTime: 24
|
1711
|
+
});
|
1712
|
+
|
1713
|
+
views.agendaWeek = function(element, options) {
|
1714
|
+
return new Agenda(element, options, {
|
1715
|
+
render: function(date, delta) {
|
1716
|
+
if (delta) {
|
1717
|
+
addDays(date, delta * 7);
|
1718
|
+
}
|
1719
|
+
var visStart = this.visStart = cloneDate(
|
1720
|
+
this.start = addDays(cloneDate(date), -((date.getDay() - options.firstDay + 7) % 7))
|
1721
|
+
),
|
1722
|
+
visEnd = this.visEnd = cloneDate(
|
1723
|
+
this.end = addDays(cloneDate(visStart), 7)
|
1724
|
+
);
|
1725
|
+
if (!options.weekends) {
|
1726
|
+
skipWeekend(visStart);
|
1727
|
+
skipWeekend(visEnd, -1, true);
|
1728
|
+
}
|
1729
|
+
this.title = formatDates(
|
1730
|
+
visStart,
|
1731
|
+
addDays(cloneDate(visEnd), -1),
|
1732
|
+
this.option('titleFormat'),
|
1733
|
+
options
|
1734
|
+
);
|
1735
|
+
this.renderAgenda(
|
1736
|
+
options.weekends ? 7 : 5,
|
1737
|
+
this.option('columnFormat')
|
1738
|
+
);
|
1739
|
+
}
|
1740
|
+
});
|
1741
|
+
};
|
1742
|
+
|
1743
|
+
views.agendaDay = function(element, options) {
|
1744
|
+
return new Agenda(element, options, {
|
1745
|
+
render: function(date, delta) {
|
1746
|
+
if (delta) {
|
1747
|
+
addDays(date, delta);
|
1748
|
+
if (!options.weekends) {
|
1749
|
+
skipWeekend(date, delta < 0 ? -1 : 1);
|
1750
|
+
}
|
1751
|
+
}
|
1752
|
+
this.title = formatDate(date, this.option('titleFormat'), options);
|
1753
|
+
this.start = this.visStart = cloneDate(date, true);
|
1754
|
+
this.end = this.visEnd = addDays(cloneDate(this.start), 1);
|
1755
|
+
this.renderAgenda(
|
1756
|
+
1,
|
1757
|
+
this.option('columnFormat')
|
1758
|
+
);
|
1759
|
+
}
|
1760
|
+
});
|
1761
|
+
};
|
1762
|
+
|
1763
|
+
function Agenda(element, options, methods) {
|
1764
|
+
|
1765
|
+
var head, body, bodyContent, bodyTable, bg,
|
1766
|
+
colCnt,
|
1767
|
+
slotCnt=0, // spanning all the way across
|
1768
|
+
axisWidth, colWidth, slotHeight,
|
1769
|
+
viewWidth, viewHeight,
|
1770
|
+
savedScrollTop,
|
1771
|
+
cachedEvents=[],
|
1772
|
+
daySegmentContainer,
|
1773
|
+
slotSegmentContainer,
|
1774
|
+
tm, firstDay,
|
1775
|
+
nwe, // no weekends (int)
|
1776
|
+
rtl, dis, dit, // day index sign / translate
|
1777
|
+
minMinute, maxMinute,
|
1778
|
+
colContentPositions = new HorizontalPositionCache(function(col) {
|
1779
|
+
return bg.find('td:eq(' + col + ') div div');
|
1780
|
+
}),
|
1781
|
+
slotTopCache = {},
|
1782
|
+
daySelectionManager,
|
1783
|
+
slotSelectionManager,
|
1784
|
+
selectionHelper,
|
1785
|
+
selectionMatrix,
|
1786
|
+
// ...
|
1787
|
+
|
1788
|
+
view = $.extend(this, viewMethods, methods, {
|
1789
|
+
renderAgenda: renderAgenda,
|
1790
|
+
renderEvents: renderEvents,
|
1791
|
+
rerenderEvents: rerenderEvents,
|
1792
|
+
clearEvents: clearEvents,
|
1793
|
+
setHeight: setHeight,
|
1794
|
+
setWidth: setWidth,
|
1795
|
+
beforeHide: function() {
|
1796
|
+
savedScrollTop = body.scrollTop();
|
1797
|
+
},
|
1798
|
+
afterShow: function() {
|
1799
|
+
body.scrollTop(savedScrollTop);
|
1800
|
+
},
|
1801
|
+
defaultEventEnd: function(event) {
|
1802
|
+
var start = cloneDate(event.start);
|
1803
|
+
if (event.allDay) {
|
1804
|
+
return start;
|
1805
|
+
}
|
1806
|
+
return addMinutes(start, options.defaultEventMinutes);
|
1807
|
+
}
|
1808
|
+
});
|
1809
|
+
view.init(element, options);
|
1810
|
+
|
1811
|
+
|
1812
|
+
|
1813
|
+
/* Time-slot rendering
|
1814
|
+
-----------------------------------------------------------------------------*/
|
1815
|
+
|
1816
|
+
|
1817
|
+
disableTextSelection(element.addClass('fc-agenda'));
|
1818
|
+
|
1819
|
+
|
1820
|
+
function renderAgenda(c, colFormat) {
|
1821
|
+
|
1822
|
+
colCnt = c;
|
1823
|
+
|
1824
|
+
// update option-derived variables
|
1825
|
+
tm = options.theme ? 'ui' : 'fc';
|
1826
|
+
nwe = options.weekends ? 0 : 1;
|
1827
|
+
firstDay = options.firstDay;
|
1828
|
+
if (rtl = options.isRTL) {
|
1829
|
+
dis = -1;
|
1830
|
+
dit = colCnt - 1;
|
1831
|
+
}else{
|
1832
|
+
dis = 1;
|
1833
|
+
dit = 0;
|
1834
|
+
}
|
1835
|
+
minMinute = parseTime(options.minTime);
|
1836
|
+
maxMinute = parseTime(options.maxTime);
|
1837
|
+
|
1838
|
+
var d0 = rtl ? addDays(cloneDate(view.visEnd), -1) : cloneDate(view.visStart),
|
1839
|
+
d = cloneDate(d0),
|
1840
|
+
today = clearTime(new Date());
|
1841
|
+
|
1842
|
+
if (!head) { // first time rendering, build from scratch
|
1843
|
+
|
1844
|
+
var i,
|
1845
|
+
minutes,
|
1846
|
+
slotNormal = options.slotMinutes % 15 == 0, //...
|
1847
|
+
|
1848
|
+
// head
|
1849
|
+
s = "<div class='fc-agenda-head' style='position:relative;z-index:4'>" +
|
1850
|
+
"<table style='width:100%'>" +
|
1851
|
+
"<tr class='fc-first" + (options.allDaySlot ? '' : ' fc-last') + "'>" +
|
1852
|
+
"<th class='fc-leftmost " +
|
1853
|
+
tm + "-state-default'> </th>";
|
1854
|
+
for (i=0; i<colCnt; i++) {
|
1855
|
+
s += "<th class='fc-" +
|
1856
|
+
dayIDs[d.getDay()] + ' ' + // needs to be first
|
1857
|
+
tm + '-state-default' +
|
1858
|
+
"'>" + formatDate(d, colFormat, options) + "</th>";
|
1859
|
+
addDays(d, dis);
|
1860
|
+
if (nwe) {
|
1861
|
+
skipWeekend(d, dis);
|
1862
|
+
}
|
1863
|
+
}
|
1864
|
+
s += "<th class='" + tm + "-state-default'> </th></tr>";
|
1865
|
+
if (options.allDaySlot) {
|
1866
|
+
s += "<tr class='fc-all-day'>" +
|
1867
|
+
"<th class='fc-axis fc-leftmost " + tm + "-state-default'>" + options.allDayText + "</th>" +
|
1868
|
+
"<td colspan='" + colCnt + "' class='" + tm + "-state-default'>" +
|
1869
|
+
"<div class='fc-day-content'><div style='position:relative'> </div></div></td>" +
|
1870
|
+
"<th class='" + tm + "-state-default'> </th>" +
|
1871
|
+
"</tr><tr class='fc-divider fc-last'><th colspan='" + (colCnt+2) + "' class='" +
|
1872
|
+
tm + "-state-default fc-leftmost'><div/></th></tr>";
|
1873
|
+
}
|
1874
|
+
s+= "</table></div>";
|
1875
|
+
head = $(s).appendTo(element);
|
1876
|
+
dayBind(head.find('td'));
|
1877
|
+
|
1878
|
+
// all-day event container
|
1879
|
+
daySegmentContainer = $("<div style='position:absolute;z-index:8;top:0;left:0'/>").appendTo(head);
|
1880
|
+
|
1881
|
+
// body
|
1882
|
+
d = zeroDate();
|
1883
|
+
var maxd = addMinutes(cloneDate(d), maxMinute);
|
1884
|
+
addMinutes(d, minMinute);
|
1885
|
+
s = "<table>";
|
1886
|
+
for (i=0; d < maxd; i++) {
|
1887
|
+
minutes = d.getMinutes();
|
1888
|
+
s += "<tr class='" +
|
1889
|
+
(!i ? 'fc-first' : (!minutes ? '' : 'fc-minor')) +
|
1890
|
+
"'><th class='fc-axis fc-leftmost " + tm + "-state-default'>" +
|
1891
|
+
((!slotNormal || !minutes) ? formatDate(d, options.axisFormat) : ' ') +
|
1892
|
+
"</th><td class='fc-slot" + i + ' ' +
|
1893
|
+
tm + "-state-default'><div style='position:relative'> </div></td></tr>";
|
1894
|
+
addMinutes(d, options.slotMinutes);
|
1895
|
+
slotCnt++;
|
1896
|
+
}
|
1897
|
+
s += "</table>";
|
1898
|
+
body = $("<div class='fc-agenda-body' style='position:relative;z-index:2;overflow:auto'/>")
|
1899
|
+
.append(bodyContent = $("<div style='position:relative;overflow:hidden'>")
|
1900
|
+
.append(bodyTable = $(s)))
|
1901
|
+
.appendTo(element);
|
1902
|
+
slotBind(body.find('td'));
|
1903
|
+
|
1904
|
+
// slot event container
|
1905
|
+
slotSegmentContainer = $("<div style='position:absolute;z-index:8;top:0;left:0'/>").appendTo(bodyContent);
|
1906
|
+
|
1907
|
+
// background stripes
|
1908
|
+
d = cloneDate(d0);
|
1909
|
+
s = "<div class='fc-agenda-bg' style='position:absolute;z-index:1'>" +
|
1910
|
+
"<table style='width:100%;height:100%'><tr class='fc-first'>";
|
1911
|
+
for (i=0; i<colCnt; i++) {
|
1912
|
+
s += "<td class='fc-" +
|
1913
|
+
dayIDs[d.getDay()] + ' ' + // needs to be first
|
1914
|
+
tm + '-state-default ' +
|
1915
|
+
(!i ? 'fc-leftmost ' : '') +
|
1916
|
+
(+d == +today ? tm + '-state-highlight fc-today' : 'fc-not-today') +
|
1917
|
+
"'><div class='fc-day-content'><div> </div></div></td>";
|
1918
|
+
addDays(d, dis);
|
1919
|
+
if (nwe) {
|
1920
|
+
skipWeekend(d, dis);
|
1921
|
+
}
|
1922
|
+
}
|
1923
|
+
s += "</tr></table></div>";
|
1924
|
+
bg = $(s).appendTo(element);
|
1925
|
+
|
1926
|
+
}else{ // skeleton already built, just modify it
|
1927
|
+
|
1928
|
+
clearEvents();
|
1929
|
+
|
1930
|
+
// redo column header text and class
|
1931
|
+
head.find('tr:first th').slice(1, -1).each(function() {
|
1932
|
+
$(this).text(formatDate(d, colFormat, options));
|
1933
|
+
this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]);
|
1934
|
+
addDays(d, dis);
|
1935
|
+
if (nwe) {
|
1936
|
+
skipWeekend(d, dis);
|
1937
|
+
}
|
1938
|
+
});
|
1939
|
+
|
1940
|
+
// change classes of background stripes
|
1941
|
+
d = cloneDate(d0);
|
1942
|
+
bg.find('td').each(function() {
|
1943
|
+
this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]);
|
1944
|
+
if (+d == +today) {
|
1945
|
+
$(this)
|
1946
|
+
.removeClass('fc-not-today')
|
1947
|
+
.addClass('fc-today')
|
1948
|
+
.addClass(tm + '-state-highlight');
|
1949
|
+
}else{
|
1950
|
+
$(this)
|
1951
|
+
.addClass('fc-not-today')
|
1952
|
+
.removeClass('fc-today')
|
1953
|
+
.removeClass(tm + '-state-highlight');
|
1954
|
+
}
|
1955
|
+
addDays(d, dis);
|
1956
|
+
if (nwe) {
|
1957
|
+
skipWeekend(d, dis);
|
1958
|
+
}
|
1959
|
+
});
|
1960
|
+
|
1961
|
+
}
|
1962
|
+
|
1963
|
+
}
|
1964
|
+
|
1965
|
+
|
1966
|
+
function resetScroll() {
|
1967
|
+
var d0 = zeroDate(),
|
1968
|
+
scrollDate = cloneDate(d0);
|
1969
|
+
scrollDate.setHours(options.firstHour);
|
1970
|
+
var top = timePosition(d0, scrollDate) + 1, // +1 for the border
|
1971
|
+
scroll = function() {
|
1972
|
+
body.scrollTop(top);
|
1973
|
+
};
|
1974
|
+
scroll();
|
1975
|
+
setTimeout(scroll, 0); // overrides any previous scroll state made by the browser
|
1976
|
+
}
|
1977
|
+
|
1978
|
+
|
1979
|
+
function setHeight(height, dateChanged) {
|
1980
|
+
viewHeight = height;
|
1981
|
+
slotTopCache = {};
|
1982
|
+
|
1983
|
+
body.height(height - head.height());
|
1984
|
+
|
1985
|
+
slotHeight = body.find('tr:first div').height() + 1;
|
1986
|
+
|
1987
|
+
bg.css({
|
1988
|
+
top: head.find('tr').height(),
|
1989
|
+
height: height
|
1990
|
+
});
|
1991
|
+
|
1992
|
+
if (dateChanged) {
|
1993
|
+
resetScroll();
|
1994
|
+
}
|
1995
|
+
}
|
1996
|
+
|
1997
|
+
|
1998
|
+
function setWidth(width) {
|
1999
|
+
viewWidth = width;
|
2000
|
+
colContentPositions.clear();
|
2001
|
+
|
2002
|
+
body.width(width);
|
2003
|
+
bodyTable.width('');
|
2004
|
+
|
2005
|
+
var topTDs = head.find('tr:first th'),
|
2006
|
+
stripeTDs = bg.find('td'),
|
2007
|
+
clientWidth = body[0].clientWidth;
|
2008
|
+
|
2009
|
+
bodyTable.width(clientWidth);
|
2010
|
+
|
2011
|
+
// time-axis width
|
2012
|
+
axisWidth = 0;
|
2013
|
+
setOuterWidth(
|
2014
|
+
head.find('tr:lt(2) th:first').add(body.find('tr:first th'))
|
2015
|
+
.width('')
|
2016
|
+
.each(function() {
|
2017
|
+
axisWidth = Math.max(axisWidth, $(this).outerWidth());
|
2018
|
+
}),
|
2019
|
+
axisWidth
|
2020
|
+
);
|
2021
|
+
|
2022
|
+
// column width
|
2023
|
+
colWidth = Math.floor((clientWidth - axisWidth) / colCnt);
|
2024
|
+
setOuterWidth(stripeTDs.slice(0, -1), colWidth);
|
2025
|
+
setOuterWidth(topTDs.slice(1, -2), colWidth);
|
2026
|
+
setOuterWidth(topTDs.slice(-2, -1), clientWidth - axisWidth - colWidth*(colCnt-1));
|
2027
|
+
|
2028
|
+
bg.css({
|
2029
|
+
left: axisWidth,
|
2030
|
+
width: clientWidth - axisWidth
|
2031
|
+
});
|
2032
|
+
}
|
2033
|
+
|
2034
|
+
|
2035
|
+
|
2036
|
+
/* Slot/Day clicking and binding
|
2037
|
+
-----------------------------------------------------------------------*/
|
2038
|
+
|
2039
|
+
|
2040
|
+
function dayBind(tds) {
|
2041
|
+
tds.click(slotClick)
|
2042
|
+
.mousedown(daySelectionMousedown);
|
2043
|
+
}
|
2044
|
+
|
2045
|
+
|
2046
|
+
function slotBind(tds) {
|
2047
|
+
tds.click(slotClick)
|
2048
|
+
.mousedown(slotSelectionMousedown);
|
2049
|
+
}
|
2050
|
+
|
2051
|
+
|
2052
|
+
function slotClick(ev) {
|
2053
|
+
if (!view.option('selectable')) { // SelectionManager will worry about dayClick
|
2054
|
+
var col = Math.floor((ev.pageX - bg.offset().left) / colWidth),
|
2055
|
+
date = addDays(cloneDate(view.visStart), dit + dis*col),
|
2056
|
+
rowMatch = this.className.match(/fc-slot(\d+)/);
|
2057
|
+
if (rowMatch) {
|
2058
|
+
var mins = parseInt(rowMatch[1]) * options.slotMinutes,
|
2059
|
+
hours = Math.floor(mins/60);
|
2060
|
+
date.setHours(hours);
|
2061
|
+
date.setMinutes(mins%60 + minMinute);
|
2062
|
+
view.trigger('dayClick', this, date, false, ev);
|
2063
|
+
}else{
|
2064
|
+
view.trigger('dayClick', this, date, true, ev);
|
2065
|
+
}
|
2066
|
+
}
|
2067
|
+
}
|
2068
|
+
|
2069
|
+
|
2070
|
+
|
2071
|
+
/* Event Rendering
|
2072
|
+
-----------------------------------------------------------------------------*/
|
2073
|
+
|
2074
|
+
function renderEvents(events, modifiedEventId) {
|
2075
|
+
view.reportEvents(cachedEvents = events);
|
2076
|
+
var i, len=events.length,
|
2077
|
+
dayEvents=[],
|
2078
|
+
slotEvents=[];
|
2079
|
+
for (i=0; i<len; i++) {
|
2080
|
+
if (events[i].allDay) {
|
2081
|
+
dayEvents.push(events[i]);
|
2082
|
+
}else{
|
2083
|
+
slotEvents.push(events[i]);
|
2084
|
+
}
|
2085
|
+
}
|
2086
|
+
renderDaySegs(compileDaySegs(dayEvents), modifiedEventId);
|
2087
|
+
renderSlotSegs(compileSlotSegs(slotEvents), modifiedEventId);
|
2088
|
+
}
|
2089
|
+
|
2090
|
+
|
2091
|
+
function rerenderEvents(modifiedEventId) {
|
2092
|
+
clearEvents();
|
2093
|
+
renderEvents(cachedEvents, modifiedEventId);
|
2094
|
+
}
|
2095
|
+
|
2096
|
+
|
2097
|
+
function clearEvents() {
|
2098
|
+
view._clearEvents(); // only clears the hashes
|
2099
|
+
daySegmentContainer.empty();
|
2100
|
+
slotSegmentContainer.empty();
|
2101
|
+
}
|
2102
|
+
|
2103
|
+
|
2104
|
+
|
2105
|
+
|
2106
|
+
|
2107
|
+
function compileDaySegs(events) {
|
2108
|
+
var levels = stackSegs(view.sliceSegs(events, $.map(events, exclEndDay), view.visStart, view.visEnd)),
|
2109
|
+
i, levelCnt=levels.length, level,
|
2110
|
+
j, seg,
|
2111
|
+
segs=[];
|
2112
|
+
for (i=0; i<levelCnt; i++) {
|
2113
|
+
level = levels[i];
|
2114
|
+
for (j=0; j<level.length; j++) {
|
2115
|
+
seg = level[j];
|
2116
|
+
seg.row = 0;
|
2117
|
+
seg.level = i;
|
2118
|
+
segs.push(seg);
|
2119
|
+
}
|
2120
|
+
}
|
2121
|
+
return segs;
|
2122
|
+
}
|
2123
|
+
|
2124
|
+
|
2125
|
+
function compileSlotSegs(events) {
|
2126
|
+
var d = addMinutes(cloneDate(view.visStart), minMinute),
|
2127
|
+
visEventEnds = $.map(events, slotEventEnd),
|
2128
|
+
i, col,
|
2129
|
+
j, level,
|
2130
|
+
k, seg,
|
2131
|
+
segs=[];
|
2132
|
+
for (i=0; i<colCnt; i++) {
|
2133
|
+
col = stackSegs(view.sliceSegs(events, visEventEnds, d, addMinutes(cloneDate(d), maxMinute-minMinute)));
|
2134
|
+
countForwardSegs(col);
|
2135
|
+
for (j=0; j<col.length; j++) {
|
2136
|
+
level = col[j];
|
2137
|
+
for (k=0; k<level.length; k++) {
|
2138
|
+
seg = level[k];
|
2139
|
+
seg.col = i;
|
2140
|
+
seg.level = j;
|
2141
|
+
segs.push(seg);
|
2142
|
+
}
|
2143
|
+
}
|
2144
|
+
addDays(d, 1, true);
|
2145
|
+
}
|
2146
|
+
return segs;
|
2147
|
+
}
|
2148
|
+
|
2149
|
+
|
2150
|
+
|
2151
|
+
|
2152
|
+
// renders 'all-day' events at the top
|
2153
|
+
|
2154
|
+
function renderDaySegs(segs, modifiedEventId) {
|
2155
|
+
if (options.allDaySlot) {
|
2156
|
+
_renderDaySegs(
|
2157
|
+
segs,
|
2158
|
+
1,
|
2159
|
+
view,
|
2160
|
+
axisWidth,
|
2161
|
+
viewWidth,
|
2162
|
+
function() {
|
2163
|
+
return head.find('tr.fc-all-day');
|
2164
|
+
},
|
2165
|
+
function(dayOfWeek) {
|
2166
|
+
return axisWidth + colContentPositions.left(dayOfWeekCol(dayOfWeek));
|
2167
|
+
},
|
2168
|
+
function(dayOfWeek) {
|
2169
|
+
return axisWidth + colContentPositions.right(dayOfWeekCol(dayOfWeek));
|
2170
|
+
},
|
2171
|
+
daySegmentContainer,
|
2172
|
+
daySegBind,
|
2173
|
+
modifiedEventId
|
2174
|
+
);
|
2175
|
+
setHeight(viewHeight); // might have pushed the body down, so resize
|
2176
|
+
}
|
2177
|
+
}
|
2178
|
+
|
2179
|
+
|
2180
|
+
|
2181
|
+
// renders events in the 'time slots' at the bottom
|
2182
|
+
|
2183
|
+
function renderSlotSegs(segs, modifiedEventId) {
|
2184
|
+
|
2185
|
+
var i, segCnt=segs.length, seg,
|
2186
|
+
event,
|
2187
|
+
className,
|
2188
|
+
top, bottom,
|
2189
|
+
colI, levelI, forward,
|
2190
|
+
leftmost,
|
2191
|
+
availWidth,
|
2192
|
+
outerWidth,
|
2193
|
+
left,
|
2194
|
+
html='',
|
2195
|
+
eventElements,
|
2196
|
+
eventElement,
|
2197
|
+
triggerRes,
|
2198
|
+
vsideCache={},
|
2199
|
+
hsideCache={},
|
2200
|
+
key, val,
|
2201
|
+
titleSpan,
|
2202
|
+
height;
|
2203
|
+
|
2204
|
+
// calculate position/dimensions, create html
|
2205
|
+
for (i=0; i<segCnt; i++) {
|
2206
|
+
seg = segs[i];
|
2207
|
+
event = seg.event;
|
2208
|
+
className = 'fc-event fc-event-vert ';
|
2209
|
+
if (seg.isStart) {
|
2210
|
+
className += 'fc-corner-top ';
|
2211
|
+
}
|
2212
|
+
if (seg.isEnd) {
|
2213
|
+
className += 'fc-corner-bottom ';
|
2214
|
+
}
|
2215
|
+
top = timePosition(seg.start, seg.start);
|
2216
|
+
bottom = timePosition(seg.start, seg.end);
|
2217
|
+
colI = seg.col;
|
2218
|
+
levelI = seg.level;
|
2219
|
+
forward = seg.forward || 0;
|
2220
|
+
leftmost = axisWidth + colContentPositions.left(colI*dis + dit);
|
2221
|
+
availWidth = axisWidth + colContentPositions.right(colI*dis + dit) - leftmost;
|
2222
|
+
availWidth = Math.min(availWidth-6, availWidth*.95); // TODO: move this to CSS
|
2223
|
+
if (levelI) {
|
2224
|
+
// indented and thin
|
2225
|
+
outerWidth = availWidth / (levelI + forward + 1);
|
2226
|
+
}else{
|
2227
|
+
if (forward) {
|
2228
|
+
// moderately wide, aligned left still
|
2229
|
+
outerWidth = ((availWidth / (forward + 1)) - (12/2)) * 2; // 12 is the predicted width of resizer =
|
2230
|
+
}else{
|
2231
|
+
// can be entire width, aligned left
|
2232
|
+
outerWidth = availWidth;
|
2233
|
+
}
|
2234
|
+
}
|
2235
|
+
left = leftmost + // leftmost possible
|
2236
|
+
(availWidth / (levelI + forward + 1) * levelI) // indentation
|
2237
|
+
* dis + (rtl ? availWidth - outerWidth : 0); // rtl
|
2238
|
+
seg.top = top;
|
2239
|
+
seg.left = left;
|
2240
|
+
seg.outerWidth = outerWidth;
|
2241
|
+
seg.outerHeight = bottom - top;
|
2242
|
+
html += slotSegHtml(event, seg, className);
|
2243
|
+
}
|
2244
|
+
slotSegmentContainer[0].innerHTML = html; // faster than html()
|
2245
|
+
eventElements = slotSegmentContainer.children();
|
2246
|
+
|
2247
|
+
// retrieve elements, run through eventRender callback, bind event handlers
|
2248
|
+
for (i=0; i<segCnt; i++) {
|
2249
|
+
seg = segs[i];
|
2250
|
+
event = seg.event;
|
2251
|
+
eventElement = $(eventElements[i]); // faster than eq()
|
2252
|
+
triggerRes = view.trigger('eventRender', event, event, eventElement);
|
2253
|
+
if (triggerRes === false) {
|
2254
|
+
eventElement.remove();
|
2255
|
+
}else{
|
2256
|
+
if (triggerRes && triggerRes !== true) {
|
2257
|
+
eventElement.remove();
|
2258
|
+
eventElement = $(triggerRes)
|
2259
|
+
.css({
|
2260
|
+
position: 'absolute',
|
2261
|
+
top: seg.top,
|
2262
|
+
left: seg.left
|
2263
|
+
})
|
2264
|
+
.appendTo(slotSegmentContainer);
|
2265
|
+
}
|
2266
|
+
seg.element = eventElement;
|
2267
|
+
if (event._id === modifiedEventId) {
|
2268
|
+
slotSegBind(event, eventElement, seg);
|
2269
|
+
}else{
|
2270
|
+
eventElement[0]._fci = i; // for lazySegBind
|
2271
|
+
}
|
2272
|
+
view.reportEventElement(event, eventElement);
|
2273
|
+
}
|
2274
|
+
}
|
2275
|
+
|
2276
|
+
lazySegBind(slotSegmentContainer, segs, slotSegBind);
|
2277
|
+
|
2278
|
+
// record event sides and title positions
|
2279
|
+
for (i=0; i<segCnt; i++) {
|
2280
|
+
seg = segs[i];
|
2281
|
+
if (eventElement = seg.element) {
|
2282
|
+
val = vsideCache[key = seg.key = cssKey(eventElement[0])];
|
2283
|
+
seg.vsides = val === undefined ? (vsideCache[key] = vsides(eventElement[0], true)) : val;
|
2284
|
+
val = hsideCache[key];
|
2285
|
+
seg.hsides = val === undefined ? (hsideCache[key] = hsides(eventElement[0], true)) : val;
|
2286
|
+
titleSpan = eventElement.find('span.fc-event-title');
|
2287
|
+
if (titleSpan.length) {
|
2288
|
+
seg.titleTop = titleSpan[0].offsetTop;
|
2289
|
+
}
|
2290
|
+
}
|
2291
|
+
}
|
2292
|
+
|
2293
|
+
// set all positions/dimensions at once
|
2294
|
+
for (i=0; i<segCnt; i++) {
|
2295
|
+
seg = segs[i];
|
2296
|
+
if (eventElement = seg.element) {
|
2297
|
+
eventElement[0].style.width = seg.outerWidth - seg.hsides + 'px';
|
2298
|
+
eventElement[0].style.height = (height = seg.outerHeight - seg.vsides) + 'px';
|
2299
|
+
event = seg.event;
|
2300
|
+
if (seg.titleTop !== undefined && height - seg.titleTop < 10) {
|
2301
|
+
// not enough room for title, put it in the time header
|
2302
|
+
eventElement.find('span.fc-event-time')
|
2303
|
+
.text(formatDate(event.start, view.option('timeFormat')) + ' - ' + event.title);
|
2304
|
+
eventElement.find('span.fc-event-title')
|
2305
|
+
.remove();
|
2306
|
+
}
|
2307
|
+
view.trigger('eventAfterRender', event, event, eventElement);
|
2308
|
+
}
|
2309
|
+
}
|
2310
|
+
|
2311
|
+
}
|
2312
|
+
|
2313
|
+
function slotSegHtml(event, seg, className) {
|
2314
|
+
return "<div class='" + className + event.className.join(' ') + "' style='position:absolute;z-index:8;top:" + seg.top + "px;left:" + seg.left + "px'>" +
|
2315
|
+
"<a" + (event.url ? " href='" + htmlEscape(event.url) + "'" : '') + ">" +
|
2316
|
+
"<span class='fc-event-bg'></span>" +
|
2317
|
+
"<span class='fc-event-time'>" + htmlEscape(formatDates(event.start, event.end, view.option('timeFormat'))) + "</span>" +
|
2318
|
+
"<span class='fc-event-title'>" + htmlEscape(event.title) + "</span>" +
|
2319
|
+
"</a>" +
|
2320
|
+
((event.editable || event.editable === undefined && options.editable) && !options.disableResizing && $.fn.resizable ?
|
2321
|
+
"<div class='ui-resizable-handle ui-resizable-s'>=</div>"
|
2322
|
+
: '') +
|
2323
|
+
"</div>";
|
2324
|
+
}
|
2325
|
+
|
2326
|
+
|
2327
|
+
|
2328
|
+
function daySegBind(event, eventElement, seg) {
|
2329
|
+
view.eventElementHandlers(event, eventElement);
|
2330
|
+
if (event.editable || event.editable === undefined && options.editable) {
|
2331
|
+
draggableDayEvent(event, eventElement, seg.isStart);
|
2332
|
+
if (seg.isEnd) {
|
2333
|
+
view.resizableDayEvent(event, eventElement, colWidth);
|
2334
|
+
}
|
2335
|
+
}
|
2336
|
+
}
|
2337
|
+
|
2338
|
+
|
2339
|
+
|
2340
|
+
function slotSegBind(event, eventElement, seg) {
|
2341
|
+
view.eventElementHandlers(event, eventElement);
|
2342
|
+
if (event.editable || event.editable === undefined && options.editable) {
|
2343
|
+
var timeElement = eventElement.find('span.fc-event-time');
|
2344
|
+
draggableSlotEvent(event, eventElement, timeElement);
|
2345
|
+
if (seg.isEnd) {
|
2346
|
+
resizableSlotEvent(event, eventElement, timeElement);
|
2347
|
+
}
|
2348
|
+
}
|
2349
|
+
}
|
2350
|
+
|
2351
|
+
|
2352
|
+
|
2353
|
+
|
2354
|
+
/* Event Dragging
|
2355
|
+
-----------------------------------------------------------------------------*/
|
2356
|
+
|
2357
|
+
|
2358
|
+
|
2359
|
+
// when event starts out FULL-DAY
|
2360
|
+
|
2361
|
+
function draggableDayEvent(event, eventElement, isStart) {
|
2362
|
+
if (!options.disableDragging && eventElement.draggable) {
|
2363
|
+
var origPosition, origWidth,
|
2364
|
+
resetElement,
|
2365
|
+
allDay=true,
|
2366
|
+
matrix;
|
2367
|
+
eventElement.draggable({
|
2368
|
+
zIndex: 9,
|
2369
|
+
opacity: view.option('dragOpacity', 'month'), // use whatever the month view was using
|
2370
|
+
revertDuration: options.dragRevertDuration,
|
2371
|
+
start: function(ev, ui) {
|
2372
|
+
view.hideEvents(event, eventElement);
|
2373
|
+
view.trigger('eventDragStart', eventElement, event, ev, ui);
|
2374
|
+
origPosition = eventElement.position();
|
2375
|
+
origWidth = eventElement.width();
|
2376
|
+
resetElement = function() {
|
2377
|
+
if (!allDay) {
|
2378
|
+
eventElement
|
2379
|
+
.width(origWidth)
|
2380
|
+
.height('')
|
2381
|
+
.draggable('option', 'grid', null);
|
2382
|
+
allDay = true;
|
2383
|
+
}
|
2384
|
+
};
|
2385
|
+
matrix = buildDayMatrix(function(cell) {
|
2386
|
+
eventElement.draggable('option', 'revert', !cell || !cell.rowDelta && !cell.colDelta);
|
2387
|
+
view.clearOverlays();
|
2388
|
+
if (cell) {
|
2389
|
+
if (!cell.row) {
|
2390
|
+
// on full-days
|
2391
|
+
renderDayOverlay(
|
2392
|
+
matrix,
|
2393
|
+
addDays(cloneDate(event.start), cell.colDelta),
|
2394
|
+
addDays(exclEndDay(event), cell.colDelta)
|
2395
|
+
);
|
2396
|
+
resetElement();
|
2397
|
+
}else{
|
2398
|
+
// mouse is over bottom slots
|
2399
|
+
if (isStart && allDay) {
|
2400
|
+
// convert event to temporary slot-event
|
2401
|
+
setOuterHeight(
|
2402
|
+
eventElement.width(colWidth - 10), // don't use entire width
|
2403
|
+
slotHeight * Math.round(
|
2404
|
+
(event.end ? ((event.end - event.start)/MINUTE_MS) : options.defaultEventMinutes)
|
2405
|
+
/options.slotMinutes)
|
2406
|
+
);
|
2407
|
+
eventElement.draggable('option', 'grid', [colWidth, 1]);
|
2408
|
+
allDay = false;
|
2409
|
+
}
|
2410
|
+
}
|
2411
|
+
}
|
2412
|
+
},true);
|
2413
|
+
matrix.mouse(ev);
|
2414
|
+
},
|
2415
|
+
drag: function(ev, ui) {
|
2416
|
+
matrix.mouse(ev);
|
2417
|
+
},
|
2418
|
+
stop: function(ev, ui) {
|
2419
|
+
view.trigger('eventDragStop', eventElement, event, ev, ui);
|
2420
|
+
view.clearOverlays();
|
2421
|
+
var cell = matrix.cell;
|
2422
|
+
var dayDelta = dis * (
|
2423
|
+
allDay ? // can't trust cell.colDelta when using slot grid
|
2424
|
+
(cell ? cell.colDelta : 0) :
|
2425
|
+
Math.floor((ui.position.left - origPosition.left) / colWidth)
|
2426
|
+
);
|
2427
|
+
if (!cell || !dayDelta && !cell.rowDelta) {
|
2428
|
+
// over nothing (has reverted)
|
2429
|
+
resetElement();
|
2430
|
+
if ($.browser.msie) {
|
2431
|
+
eventElement.css('filter', ''); // clear IE opacity side-effects
|
2432
|
+
}
|
2433
|
+
view.showEvents(event, eventElement);
|
2434
|
+
}else{
|
2435
|
+
eventElement.find('a').removeAttr('href'); // prevents safari from visiting the link
|
2436
|
+
view.eventDrop(
|
2437
|
+
this, event, dayDelta,
|
2438
|
+
allDay ? 0 : // minute delta
|
2439
|
+
Math.round((eventElement.offset().top - bodyContent.offset().top) / slotHeight)
|
2440
|
+
* options.slotMinutes
|
2441
|
+
+ minMinute
|
2442
|
+
- (event.start.getHours() * 60 + event.start.getMinutes()),
|
2443
|
+
allDay, ev, ui
|
2444
|
+
);
|
2445
|
+
}
|
2446
|
+
}
|
2447
|
+
});
|
2448
|
+
}
|
2449
|
+
}
|
2450
|
+
|
2451
|
+
|
2452
|
+
|
2453
|
+
// when event starts out IN TIMESLOTS
|
2454
|
+
|
2455
|
+
function draggableSlotEvent(event, eventElement, timeElement) {
|
2456
|
+
if (!options.disableDragging && eventElement.draggable) {
|
2457
|
+
var origPosition,
|
2458
|
+
resetElement,
|
2459
|
+
prevSlotDelta, slotDelta,
|
2460
|
+
allDay=false,
|
2461
|
+
matrix;
|
2462
|
+
eventElement.draggable({
|
2463
|
+
zIndex: 9,
|
2464
|
+
scroll: false,
|
2465
|
+
grid: [colWidth, slotHeight],
|
2466
|
+
axis: colCnt==1 ? 'y' : false,
|
2467
|
+
opacity: view.option('dragOpacity'),
|
2468
|
+
revertDuration: options.dragRevertDuration,
|
2469
|
+
start: function(ev, ui) {
|
2470
|
+
view.hideEvents(event, eventElement);
|
2471
|
+
view.trigger('eventDragStart', eventElement, event, ev, ui);
|
2472
|
+
if ($.browser.msie) {
|
2473
|
+
eventElement.find('span.fc-event-bg').hide(); // nested opacities mess up in IE, just hide
|
2474
|
+
}
|
2475
|
+
origPosition = eventElement.position();
|
2476
|
+
resetElement = function() {
|
2477
|
+
// convert back to original slot-event
|
2478
|
+
if (allDay) {
|
2479
|
+
timeElement.css('display', ''); // show() was causing display=inline
|
2480
|
+
eventElement.draggable('option', 'grid', [colWidth, slotHeight]);
|
2481
|
+
allDay = false;
|
2482
|
+
}
|
2483
|
+
};
|
2484
|
+
prevSlotDelta = 0;
|
2485
|
+
matrix = buildDayMatrix(function(cell) {
|
2486
|
+
eventElement.draggable('option', 'revert', !cell);
|
2487
|
+
view.clearOverlays();
|
2488
|
+
if (cell) {
|
2489
|
+
if (!cell.row && options.allDaySlot) { // over full days
|
2490
|
+
if (!allDay) {
|
2491
|
+
// convert to temporary all-day event
|
2492
|
+
allDay = true;
|
2493
|
+
timeElement.hide();
|
2494
|
+
eventElement.draggable('option', 'grid', null);
|
2495
|
+
}
|
2496
|
+
renderDayOverlay(
|
2497
|
+
matrix,
|
2498
|
+
addDays(cloneDate(event.start), cell.colDelta),
|
2499
|
+
addDays(exclEndDay(event), cell.colDelta)
|
2500
|
+
);
|
2501
|
+
}else{ // on slots
|
2502
|
+
resetElement();
|
2503
|
+
}
|
2504
|
+
}
|
2505
|
+
},true);
|
2506
|
+
matrix.mouse(ev);
|
2507
|
+
},
|
2508
|
+
drag: function(ev, ui) {
|
2509
|
+
slotDelta = Math.round((ui.position.top - origPosition.top) / slotHeight);
|
2510
|
+
if (slotDelta != prevSlotDelta) {
|
2511
|
+
if (!allDay) {
|
2512
|
+
// update time header
|
2513
|
+
var minuteDelta = slotDelta*options.slotMinutes,
|
2514
|
+
newStart = addMinutes(cloneDate(event.start), minuteDelta),
|
2515
|
+
newEnd;
|
2516
|
+
if (event.end) {
|
2517
|
+
newEnd = addMinutes(cloneDate(event.end), minuteDelta);
|
2518
|
+
}
|
2519
|
+
timeElement.text(formatDates(newStart, newEnd, view.option('timeFormat')));
|
2520
|
+
}
|
2521
|
+
prevSlotDelta = slotDelta;
|
2522
|
+
}
|
2523
|
+
matrix.mouse(ev);
|
2524
|
+
},
|
2525
|
+
stop: function(ev, ui) {
|
2526
|
+
view.clearOverlays();
|
2527
|
+
view.trigger('eventDragStop', eventElement, event, ev, ui);
|
2528
|
+
var cell = matrix.cell,
|
2529
|
+
dayDelta = dis * (
|
2530
|
+
allDay ? // can't trust cell.colDelta when using slot grid
|
2531
|
+
(cell ? cell.colDelta : 0) :
|
2532
|
+
Math.floor((ui.position.left - origPosition.left) / colWidth)
|
2533
|
+
);
|
2534
|
+
if (!cell || !slotDelta && !dayDelta) {
|
2535
|
+
resetElement();
|
2536
|
+
if ($.browser.msie) {
|
2537
|
+
eventElement
|
2538
|
+
.css('filter', '') // clear IE opacity side-effects
|
2539
|
+
.find('span.fc-event-bg').css('display', ''); // .show() made display=inline
|
2540
|
+
}
|
2541
|
+
eventElement.css(origPosition); // sometimes fast drags make event revert to wrong position
|
2542
|
+
view.showEvents(event, eventElement);
|
2543
|
+
}else{
|
2544
|
+
view.eventDrop(
|
2545
|
+
this, event, dayDelta,
|
2546
|
+
allDay ? 0 : slotDelta * options.slotMinutes, // minute delta
|
2547
|
+
allDay, ev, ui
|
2548
|
+
);
|
2549
|
+
}
|
2550
|
+
}
|
2551
|
+
});
|
2552
|
+
}
|
2553
|
+
}
|
2554
|
+
|
2555
|
+
|
2556
|
+
|
2557
|
+
|
2558
|
+
/* Event Resizing
|
2559
|
+
-----------------------------------------------------------------------------*/
|
2560
|
+
|
2561
|
+
// for TIMESLOT events
|
2562
|
+
|
2563
|
+
function resizableSlotEvent(event, eventElement, timeElement) {
|
2564
|
+
if (!options.disableResizing && eventElement.resizable) {
|
2565
|
+
var slotDelta, prevSlotDelta;
|
2566
|
+
eventElement.resizable({
|
2567
|
+
handles: {
|
2568
|
+
s: 'div.ui-resizable-s'
|
2569
|
+
},
|
2570
|
+
grid: slotHeight,
|
2571
|
+
start: function(ev, ui) {
|
2572
|
+
slotDelta = prevSlotDelta = 0;
|
2573
|
+
view.hideEvents(event, eventElement);
|
2574
|
+
if ($.browser.msie && $.browser.version == '6.0') {
|
2575
|
+
eventElement.css('overflow', 'hidden');
|
2576
|
+
}
|
2577
|
+
eventElement.css('z-index', 9);
|
2578
|
+
view.trigger('eventResizeStart', this, event, ev, ui);
|
2579
|
+
},
|
2580
|
+
resize: function(ev, ui) {
|
2581
|
+
// don't rely on ui.size.height, doesn't take grid into account
|
2582
|
+
slotDelta = Math.round((Math.max(slotHeight, eventElement.height()) - ui.originalSize.height) / slotHeight);
|
2583
|
+
if (slotDelta != prevSlotDelta) {
|
2584
|
+
timeElement.text(
|
2585
|
+
formatDates(
|
2586
|
+
event.start,
|
2587
|
+
(!slotDelta && !event.end) ? null : // no change, so don't display time range
|
2588
|
+
addMinutes(view.eventEnd(event), options.slotMinutes*slotDelta),
|
2589
|
+
view.option('timeFormat')
|
2590
|
+
)
|
2591
|
+
);
|
2592
|
+
prevSlotDelta = slotDelta;
|
2593
|
+
}
|
2594
|
+
},
|
2595
|
+
stop: function(ev, ui) {
|
2596
|
+
view.trigger('eventResizeStop', this, event, ev, ui);
|
2597
|
+
if (slotDelta) {
|
2598
|
+
view.eventResize(this, event, 0, options.slotMinutes*slotDelta, ev, ui);
|
2599
|
+
}else{
|
2600
|
+
eventElement.css('z-index', 8);
|
2601
|
+
view.showEvents(event, eventElement);
|
2602
|
+
// BUG: if event was really short, need to put title back in span
|
2603
|
+
}
|
2604
|
+
}
|
2605
|
+
});
|
2606
|
+
}
|
2607
|
+
}
|
2608
|
+
|
2609
|
+
|
2610
|
+
|
2611
|
+
|
2612
|
+
/* Selecting
|
2613
|
+
-----------------------------------------------------------------------------*/
|
2614
|
+
|
2615
|
+
daySelectionManager = new SelectionManager(
|
2616
|
+
view,
|
2617
|
+
unselect,
|
2618
|
+
function(startDate, endDate, allDay) {
|
2619
|
+
renderDayOverlay(
|
2620
|
+
selectionMatrix,
|
2621
|
+
startDate,
|
2622
|
+
addDays(cloneDate(endDate), 1)
|
2623
|
+
);
|
2624
|
+
},
|
2625
|
+
clearSelection
|
2626
|
+
);
|
2627
|
+
|
2628
|
+
function daySelectionMousedown(ev) {
|
2629
|
+
if (view.option('selectable')) {
|
2630
|
+
selectionMatrix = buildDayMatrix(function(cell) {
|
2631
|
+
if (cell) {
|
2632
|
+
var d = dayColDate(cell.col);
|
2633
|
+
daySelectionManager.drag(d, d, true);
|
2634
|
+
}else{
|
2635
|
+
daySelectionManager.drag();
|
2636
|
+
}
|
2637
|
+
});
|
2638
|
+
documentDragHelp(
|
2639
|
+
function(ev) {
|
2640
|
+
selectionMatrix.mouse(ev);
|
2641
|
+
},
|
2642
|
+
function(ev) {
|
2643
|
+
daySelectionManager.dragStop(ev);
|
2644
|
+
}
|
2645
|
+
);
|
2646
|
+
daySelectionManager.dragStart(ev);
|
2647
|
+
selectionMatrix.mouse(ev);
|
2648
|
+
return false; // prevent auto-unselect and text selection
|
2649
|
+
}
|
2650
|
+
}
|
2651
|
+
|
2652
|
+
slotSelectionManager = new SelectionManager(
|
2653
|
+
view,
|
2654
|
+
unselect,
|
2655
|
+
renderSlotSelection,
|
2656
|
+
clearSelection
|
2657
|
+
);
|
2658
|
+
|
2659
|
+
function slotSelectionMousedown(ev) {
|
2660
|
+
if (view.option('selectable')) {
|
2661
|
+
selectionMatrix = buildSlotMatrix(function(cell) {
|
2662
|
+
if (cell) {
|
2663
|
+
var d = slotCellDate(cell.row, cell.origCol);
|
2664
|
+
slotSelectionManager.drag(d, addMinutes(cloneDate(d), options.slotMinutes), false);
|
2665
|
+
}else{
|
2666
|
+
slotSelectionManager.drag();
|
2667
|
+
}
|
2668
|
+
});
|
2669
|
+
documentDragHelp(
|
2670
|
+
function(ev) {
|
2671
|
+
selectionMatrix.mouse(ev);
|
2672
|
+
},
|
2673
|
+
function(ev) {
|
2674
|
+
slotSelectionManager.dragStop(ev);
|
2675
|
+
}
|
2676
|
+
);
|
2677
|
+
slotSelectionManager.dragStart(ev);
|
2678
|
+
selectionMatrix.mouse(ev);
|
2679
|
+
return false; // prevent auto-unselect and text selection
|
2680
|
+
}
|
2681
|
+
}
|
2682
|
+
|
2683
|
+
documentUnselectAuto(view, unselect);
|
2684
|
+
|
2685
|
+
this.select = function(start, end, allDay) {
|
2686
|
+
if (allDay) {
|
2687
|
+
if (options.allDaySlot) {
|
2688
|
+
if (!end) {
|
2689
|
+
end = cloneDate(start);
|
2690
|
+
}
|
2691
|
+
selectionMatrix = buildDayMatrix();
|
2692
|
+
daySelectionManager.select(start, end, allDay);
|
2693
|
+
}
|
2694
|
+
}else{
|
2695
|
+
if (!end) {
|
2696
|
+
end = addMinutes(cloneDate(start), options.slotMinutes);
|
2697
|
+
}
|
2698
|
+
selectionMatrix = buildSlotMatrix();
|
2699
|
+
slotSelectionManager.select(start, end, allDay);
|
2700
|
+
}
|
2701
|
+
};
|
2702
|
+
|
2703
|
+
function unselect() {
|
2704
|
+
slotSelectionManager.unselect();
|
2705
|
+
daySelectionManager.unselect();
|
2706
|
+
}
|
2707
|
+
this.unselect = unselect;
|
2708
|
+
|
2709
|
+
|
2710
|
+
|
2711
|
+
/* Selecting drawing utils
|
2712
|
+
-----------------------------------------------------------------------------*/
|
2713
|
+
|
2714
|
+
function renderSlotSelection(startDate, endDate) {
|
2715
|
+
var helperOption = view.option('selectHelper');
|
2716
|
+
if (helperOption) {
|
2717
|
+
var col = dayDiff(startDate, view.visStart);
|
2718
|
+
if (col >= 0 && col < colCnt) { // only works when times are on same day
|
2719
|
+
var rect = selectionMatrix.rect(0, col*dis+dit, 1, col*dis+dit+1, bodyContent); // only for horizontal coords
|
2720
|
+
var top = timePosition(startDate, startDate);
|
2721
|
+
var bottom = timePosition(startDate, endDate);
|
2722
|
+
if (bottom > top) { // protect against selections that are entirely before or after visible range
|
2723
|
+
rect.top = top;
|
2724
|
+
rect.height = bottom - top;
|
2725
|
+
rect.left += 2;
|
2726
|
+
rect.width -= 5;
|
2727
|
+
if ($.isFunction(helperOption)) {
|
2728
|
+
var helperRes = helperOption(startDate, endDate);
|
2729
|
+
if (helperRes) {
|
2730
|
+
rect.position = 'absolute';
|
2731
|
+
rect.zIndex = 8;
|
2732
|
+
selectionHelper = $(helperRes)
|
2733
|
+
.css(rect)
|
2734
|
+
.appendTo(bodyContent);
|
2735
|
+
}
|
2736
|
+
}else{
|
2737
|
+
selectionHelper = $(slotSegHtml(
|
2738
|
+
{
|
2739
|
+
title: '',
|
2740
|
+
start: startDate,
|
2741
|
+
end: endDate,
|
2742
|
+
className: [],
|
2743
|
+
editable: false
|
2744
|
+
},
|
2745
|
+
rect,
|
2746
|
+
'fc-event fc-event-vert fc-corner-top fc-corner-bottom '
|
2747
|
+
));
|
2748
|
+
if ($.browser.msie) {
|
2749
|
+
selectionHelper.find('span.fc-event-bg').hide(); // nested opacities mess up in IE, just hide
|
2750
|
+
}
|
2751
|
+
selectionHelper.css('opacity', view.option('dragOpacity'));
|
2752
|
+
}
|
2753
|
+
if (selectionHelper) {
|
2754
|
+
slotBind(selectionHelper);
|
2755
|
+
bodyContent.append(selectionHelper);
|
2756
|
+
setOuterWidth(selectionHelper, rect.width, true); // needs to be after appended
|
2757
|
+
setOuterHeight(selectionHelper, rect.height, true);
|
2758
|
+
}
|
2759
|
+
}
|
2760
|
+
}
|
2761
|
+
}else{
|
2762
|
+
renderSlotOverlay(selectionMatrix, startDate, endDate);
|
2763
|
+
}
|
2764
|
+
}
|
2765
|
+
|
2766
|
+
function clearSelection() {
|
2767
|
+
clearOverlays();
|
2768
|
+
if (selectionHelper) {
|
2769
|
+
selectionHelper.remove();
|
2770
|
+
selectionHelper = null;
|
2771
|
+
}
|
2772
|
+
}
|
2773
|
+
|
2774
|
+
|
2775
|
+
|
2776
|
+
|
2777
|
+
|
2778
|
+
/* Semi-transparent Overlay Helpers
|
2779
|
+
-----------------------------------------------------*/
|
2780
|
+
|
2781
|
+
function renderDayOverlay(matrix, startDate, endDate) {
|
2782
|
+
var startCol, endCol;
|
2783
|
+
if (rtl) {
|
2784
|
+
startCol = dayDiff(endDate, view.visStart)*dis+dit+1;
|
2785
|
+
endCol = dayDiff(startDate, view.visStart)*dis+dit+1;
|
2786
|
+
}else{
|
2787
|
+
startCol = dayDiff(startDate, view.visStart);
|
2788
|
+
endCol = dayDiff(endDate, view.visStart);
|
2789
|
+
}
|
2790
|
+
startCol = Math.max(0, startCol);
|
2791
|
+
endCol = Math.min(colCnt, endCol);
|
2792
|
+
if (startCol < endCol) {
|
2793
|
+
var rect = matrix.rect(0, startCol, 1, endCol, head);
|
2794
|
+
dayBind(
|
2795
|
+
view.renderOverlay(rect, head)
|
2796
|
+
);
|
2797
|
+
}
|
2798
|
+
}
|
2799
|
+
|
2800
|
+
function renderSlotOverlay(matrix, overlayStart, overlayEnd) {
|
2801
|
+
var dayStart = cloneDate(view.visStart);
|
2802
|
+
var dayEnd = addDays(cloneDate(dayStart), 1);
|
2803
|
+
for (var i=0; i<colCnt; i++) {
|
2804
|
+
var stretchStart = new Date(Math.max(dayStart, overlayStart));
|
2805
|
+
var stretchEnd = new Date(Math.min(dayEnd, overlayEnd));
|
2806
|
+
if (stretchStart < stretchEnd) {
|
2807
|
+
var rect = matrix.rect(0, i*dis+dit, 1, i*dis+dit+1, bodyContent); // only use it for horizontal coords
|
2808
|
+
var top = timePosition(dayStart, stretchStart);
|
2809
|
+
var bottom = timePosition(dayStart, stretchEnd);
|
2810
|
+
rect.top = top;
|
2811
|
+
rect.height = bottom - top;
|
2812
|
+
slotBind(
|
2813
|
+
view.renderOverlay(rect, bodyContent)
|
2814
|
+
);
|
2815
|
+
}
|
2816
|
+
addDays(dayStart, 1);
|
2817
|
+
addDays(dayEnd, 1);
|
2818
|
+
}
|
2819
|
+
}
|
2820
|
+
|
2821
|
+
function clearOverlays() {
|
2822
|
+
view.clearOverlays();
|
2823
|
+
}
|
2824
|
+
|
2825
|
+
|
2826
|
+
|
2827
|
+
|
2828
|
+
/* Coordinate Utilities
|
2829
|
+
-----------------------------------------------------------------------------*/
|
2830
|
+
|
2831
|
+
// get the Y coordinate of the given time on the given day (both Date objects)
|
2832
|
+
function timePosition(day, time) { // both date objects. day holds 00:00 of current day
|
2833
|
+
day = cloneDate(day, true);
|
2834
|
+
if (time < addMinutes(cloneDate(day), minMinute)) {
|
2835
|
+
return 0;
|
2836
|
+
}
|
2837
|
+
if (time >= addMinutes(cloneDate(day), maxMinute)) {
|
2838
|
+
return bodyContent.height();
|
2839
|
+
}
|
2840
|
+
var slotMinutes = options.slotMinutes,
|
2841
|
+
minutes = time.getHours()*60 + time.getMinutes() - minMinute,
|
2842
|
+
slotI = Math.floor(minutes / slotMinutes),
|
2843
|
+
slotTop = slotTopCache[slotI];
|
2844
|
+
if (slotTop === undefined) {
|
2845
|
+
slotTop = slotTopCache[slotI] = body.find('tr:eq(' + slotI + ') td div')[0].offsetTop;
|
2846
|
+
}
|
2847
|
+
return Math.max(0, Math.round(
|
2848
|
+
slotTop - 1 + slotHeight * ((minutes % slotMinutes) / slotMinutes)
|
2849
|
+
));
|
2850
|
+
}
|
2851
|
+
|
2852
|
+
function buildDayMatrix(changeCallback, includeSlotArea) {
|
2853
|
+
var rowElements = options.allDaySlot ? head.find('td') : $([]);
|
2854
|
+
if (includeSlotArea) {
|
2855
|
+
rowElements = rowElements.add(body);
|
2856
|
+
}
|
2857
|
+
return new HoverMatrix(rowElements, bg.find('td'), changeCallback);
|
2858
|
+
}
|
2859
|
+
|
2860
|
+
function buildSlotMatrix(changeCallback) {
|
2861
|
+
return new HoverMatrix(bodyTable.find('td'), bg.find('td'), changeCallback);
|
2862
|
+
}
|
2863
|
+
|
2864
|
+
|
2865
|
+
|
2866
|
+
|
2867
|
+
/* Date Utilities
|
2868
|
+
----------------------------------------------------*/
|
2869
|
+
|
2870
|
+
function slotEventEnd(event) {
|
2871
|
+
if (event.end) {
|
2872
|
+
return cloneDate(event.end);
|
2873
|
+
}else{
|
2874
|
+
return addMinutes(cloneDate(event.start), options.defaultEventMinutes);
|
2875
|
+
}
|
2876
|
+
}
|
2877
|
+
|
2878
|
+
function dayOfWeekCol(dayOfWeek) {
|
2879
|
+
return ((dayOfWeek - Math.max(firstDay,nwe)+colCnt) % colCnt)*dis+dit;
|
2880
|
+
}
|
2881
|
+
|
2882
|
+
|
2883
|
+
// generating dates from cell row & columns
|
2884
|
+
|
2885
|
+
function dayColDate(col) {
|
2886
|
+
return addDays(cloneDate(view.visStart), col*dis+dit);
|
2887
|
+
}
|
2888
|
+
|
2889
|
+
function slotCellDate(row, col) {
|
2890
|
+
var d = dayColDate(col);
|
2891
|
+
addMinutes(d, minMinute + row*options.slotMinutes);
|
2892
|
+
return d;
|
2893
|
+
}
|
2894
|
+
|
2895
|
+
|
2896
|
+
|
2897
|
+
}
|
2898
|
+
|
2899
|
+
|
2900
|
+
// count the number of colliding, higher-level segments (for event squishing)
|
2901
|
+
|
2902
|
+
function countForwardSegs(levels) {
|
2903
|
+
var i, j, k, level, segForward, segBack;
|
2904
|
+
for (i=levels.length-1; i>0; i--) {
|
2905
|
+
level = levels[i];
|
2906
|
+
for (j=0; j<level.length; j++) {
|
2907
|
+
segForward = level[j];
|
2908
|
+
for (k=0; k<levels[i-1].length; k++) {
|
2909
|
+
segBack = levels[i-1][k];
|
2910
|
+
if (segsCollide(segForward, segBack)) {
|
2911
|
+
segBack.forward = Math.max(segBack.forward||0, (segForward.forward||0)+1);
|
2912
|
+
}
|
2913
|
+
}
|
2914
|
+
}
|
2915
|
+
}
|
2916
|
+
}
|
2917
|
+
|
2918
|
+
|
2919
|
+
/* Methods & Utilities for All Views
|
2920
|
+
-----------------------------------------------------------------------------*/
|
2921
|
+
|
2922
|
+
var viewMethods = {
|
2923
|
+
|
2924
|
+
/*
|
2925
|
+
* Objects inheriting these methods must implement the following properties/methods:
|
2926
|
+
* - title
|
2927
|
+
* - start
|
2928
|
+
* - end
|
2929
|
+
* - visStart
|
2930
|
+
* - visEnd
|
2931
|
+
* - defaultEventEnd(event)
|
2932
|
+
* - render(events)
|
2933
|
+
* - rerenderEvents()
|
2934
|
+
*
|
2935
|
+
*
|
2936
|
+
* z-index reservations:
|
2937
|
+
* 3 - day-overlay
|
2938
|
+
* 8 - events
|
2939
|
+
* 9 - dragging/resizing events
|
2940
|
+
*
|
2941
|
+
*/
|
2942
|
+
|
2943
|
+
|
2944
|
+
|
2945
|
+
init: function(element, options) {
|
2946
|
+
this.element = element;
|
2947
|
+
this.options = options;
|
2948
|
+
this.eventsByID = {};
|
2949
|
+
this.eventElements = [];
|
2950
|
+
this.eventElementsByID = {};
|
2951
|
+
this.usedOverlays = [];
|
2952
|
+
this.unusedOverlays = [];
|
2953
|
+
},
|
2954
|
+
|
2955
|
+
|
2956
|
+
|
2957
|
+
// triggers an event handler, always append view as last arg
|
2958
|
+
|
2959
|
+
trigger: function(name, thisObj) {
|
2960
|
+
if (this.options[name]) {
|
2961
|
+
return this.options[name].apply(thisObj || this, Array.prototype.slice.call(arguments, 2).concat([this]));
|
2962
|
+
}
|
2963
|
+
},
|
2964
|
+
|
2965
|
+
|
2966
|
+
|
2967
|
+
// returns a Date object for an event's end
|
2968
|
+
|
2969
|
+
eventEnd: function(event) {
|
2970
|
+
return event.end ? cloneDate(event.end) : this.defaultEventEnd(event); // TODO: make sure always using copies
|
2971
|
+
},
|
2972
|
+
|
2973
|
+
|
2974
|
+
|
2975
|
+
// report when view receives new events
|
2976
|
+
|
2977
|
+
reportEvents: function(events) { // events are already normalized at this point
|
2978
|
+
var i, len=events.length, event,
|
2979
|
+
eventsByID = this.eventsByID = {};
|
2980
|
+
for (i=0; i<len; i++) {
|
2981
|
+
event = events[i];
|
2982
|
+
if (eventsByID[event._id]) {
|
2983
|
+
eventsByID[event._id].push(event);
|
2984
|
+
}else{
|
2985
|
+
eventsByID[event._id] = [event];
|
2986
|
+
}
|
2987
|
+
}
|
2988
|
+
},
|
2989
|
+
|
2990
|
+
|
2991
|
+
|
2992
|
+
// report when view creates an element for an event
|
2993
|
+
|
2994
|
+
reportEventElement: function(event, element) {
|
2995
|
+
this.eventElements.push(element);
|
2996
|
+
var eventElementsByID = this.eventElementsByID;
|
2997
|
+
if (eventElementsByID[event._id]) {
|
2998
|
+
eventElementsByID[event._id].push(element);
|
2999
|
+
}else{
|
3000
|
+
eventElementsByID[event._id] = [element];
|
3001
|
+
}
|
3002
|
+
},
|
3003
|
+
|
3004
|
+
|
3005
|
+
|
3006
|
+
// event element manipulation
|
3007
|
+
|
3008
|
+
_clearEvents: function() { // only resets hashes
|
3009
|
+
this.eventElements = [];
|
3010
|
+
this.eventElementsByID = {};
|
3011
|
+
},
|
3012
|
+
|
3013
|
+
showEvents: function(event, exceptElement) {
|
3014
|
+
this._eee(event, exceptElement, 'show');
|
3015
|
+
},
|
3016
|
+
|
3017
|
+
hideEvents: function(event, exceptElement) {
|
3018
|
+
this._eee(event, exceptElement, 'hide');
|
3019
|
+
},
|
3020
|
+
|
3021
|
+
_eee: function(event, exceptElement, funcName) { // event-element-each
|
3022
|
+
var elements = this.eventElementsByID[event._id],
|
3023
|
+
i, len = elements.length;
|
3024
|
+
for (i=0; i<len; i++) {
|
3025
|
+
if (elements[i][0] != exceptElement[0]) { // AHAHAHAHAHAHAHAH
|
3026
|
+
elements[i][funcName]();
|
3027
|
+
}
|
3028
|
+
}
|
3029
|
+
},
|
3030
|
+
|
3031
|
+
|
3032
|
+
|
3033
|
+
// event modification reporting
|
3034
|
+
|
3035
|
+
eventDrop: function(e, event, dayDelta, minuteDelta, allDay, ev, ui) {
|
3036
|
+
var view = this,
|
3037
|
+
oldAllDay = event.allDay,
|
3038
|
+
eventId = event._id;
|
3039
|
+
view.moveEvents(view.eventsByID[eventId], dayDelta, minuteDelta, allDay);
|
3040
|
+
view.trigger('eventDrop', e, event, dayDelta, minuteDelta, allDay, function() { // TODO: change docs
|
3041
|
+
// TODO: investigate cases where this inverse technique might not work
|
3042
|
+
view.moveEvents(view.eventsByID[eventId], -dayDelta, -minuteDelta, oldAllDay);
|
3043
|
+
view.rerenderEvents();
|
3044
|
+
}, ev, ui);
|
3045
|
+
view.eventsChanged = true;
|
3046
|
+
view.rerenderEvents(eventId);
|
3047
|
+
},
|
3048
|
+
|
3049
|
+
eventResize: function(e, event, dayDelta, minuteDelta, ev, ui) {
|
3050
|
+
var view = this,
|
3051
|
+
eventId = event._id;
|
3052
|
+
view.elongateEvents(view.eventsByID[eventId], dayDelta, minuteDelta);
|
3053
|
+
view.trigger('eventResize', e, event, dayDelta, minuteDelta, function() {
|
3054
|
+
// TODO: investigate cases where this inverse technique might not work
|
3055
|
+
view.elongateEvents(view.eventsByID[eventId], -dayDelta, -minuteDelta);
|
3056
|
+
view.rerenderEvents();
|
3057
|
+
}, ev, ui);
|
3058
|
+
view.eventsChanged = true;
|
3059
|
+
view.rerenderEvents(eventId);
|
3060
|
+
},
|
3061
|
+
|
3062
|
+
|
3063
|
+
|
3064
|
+
// event modification
|
3065
|
+
|
3066
|
+
moveEvents: function(events, dayDelta, minuteDelta, allDay) {
|
3067
|
+
minuteDelta = minuteDelta || 0;
|
3068
|
+
for (var e, len=events.length, i=0; i<len; i++) {
|
3069
|
+
e = events[i];
|
3070
|
+
if (allDay !== undefined) {
|
3071
|
+
e.allDay = allDay;
|
3072
|
+
}
|
3073
|
+
addMinutes(addDays(e.start, dayDelta, true), minuteDelta);
|
3074
|
+
if (e.end) {
|
3075
|
+
e.end = addMinutes(addDays(e.end, dayDelta, true), minuteDelta);
|
3076
|
+
}
|
3077
|
+
normalizeEvent(e, this.options);
|
3078
|
+
}
|
3079
|
+
},
|
3080
|
+
|
3081
|
+
elongateEvents: function(events, dayDelta, minuteDelta) {
|
3082
|
+
minuteDelta = minuteDelta || 0;
|
3083
|
+
for (var e, len=events.length, i=0; i<len; i++) {
|
3084
|
+
e = events[i];
|
3085
|
+
e.end = addMinutes(addDays(this.eventEnd(e), dayDelta, true), minuteDelta);
|
3086
|
+
normalizeEvent(e, this.options);
|
3087
|
+
}
|
3088
|
+
},
|
3089
|
+
|
3090
|
+
|
3091
|
+
|
3092
|
+
// semi-transparent overlay (while dragging or selecting)
|
3093
|
+
|
3094
|
+
renderOverlay: function(rect, parent) {
|
3095
|
+
var e = this.unusedOverlays.shift();
|
3096
|
+
if (!e) {
|
3097
|
+
e = $("<div class='fc-cell-overlay' style='position:absolute;z-index:3'/>");
|
3098
|
+
}
|
3099
|
+
if (e[0].parentNode != parent[0]) {
|
3100
|
+
e.appendTo(parent);
|
3101
|
+
}
|
3102
|
+
this.usedOverlays.push(e.css(rect).show());
|
3103
|
+
return e;
|
3104
|
+
},
|
3105
|
+
|
3106
|
+
clearOverlays: function() {
|
3107
|
+
var e;
|
3108
|
+
while (e = this.usedOverlays.shift()) {
|
3109
|
+
this.unusedOverlays.push(e.hide().unbind());
|
3110
|
+
}
|
3111
|
+
},
|
3112
|
+
|
3113
|
+
|
3114
|
+
|
3115
|
+
|
3116
|
+
// common horizontal event resizing
|
3117
|
+
|
3118
|
+
resizableDayEvent: function(event, eventElement, colWidth) {
|
3119
|
+
var view = this;
|
3120
|
+
if (!view.options.disableResizing && eventElement.resizable) {
|
3121
|
+
eventElement.resizable({
|
3122
|
+
handles: view.options.isRTL ? {w:'div.ui-resizable-w'} : {e:'div.ui-resizable-e'},
|
3123
|
+
grid: colWidth,
|
3124
|
+
minWidth: colWidth/2, // need this or else IE throws errors when too small
|
3125
|
+
containment: view.element.parent().parent(), // the main element...
|
3126
|
+
// ... a fix. wouldn't allow extending to last column in agenda views (jq ui bug?)
|
3127
|
+
start: function(ev, ui) {
|
3128
|
+
eventElement.css('z-index', 9);
|
3129
|
+
view.hideEvents(event, eventElement);
|
3130
|
+
view.trigger('eventResizeStart', this, event, ev, ui);
|
3131
|
+
},
|
3132
|
+
stop: function(ev, ui) {
|
3133
|
+
view.trigger('eventResizeStop', this, event, ev, ui);
|
3134
|
+
// ui.size.width wasn't working with grid correctly, use .width()
|
3135
|
+
var dayDelta = Math.round((eventElement.width() - ui.originalSize.width) / colWidth);
|
3136
|
+
if (dayDelta) {
|
3137
|
+
view.eventResize(this, event, dayDelta, 0, ev, ui);
|
3138
|
+
}else{
|
3139
|
+
eventElement.css('z-index', 8);
|
3140
|
+
view.showEvents(event, eventElement);
|
3141
|
+
}
|
3142
|
+
}
|
3143
|
+
});
|
3144
|
+
}
|
3145
|
+
},
|
3146
|
+
|
3147
|
+
|
3148
|
+
|
3149
|
+
// attaches eventClick, eventMouseover, eventMouseout
|
3150
|
+
|
3151
|
+
eventElementHandlers: function(event, eventElement) {
|
3152
|
+
var view = this;
|
3153
|
+
eventElement
|
3154
|
+
.click(function(ev) {
|
3155
|
+
if (!eventElement.hasClass('ui-draggable-dragging') &&
|
3156
|
+
!eventElement.hasClass('ui-resizable-resizing')) {
|
3157
|
+
return view.trigger('eventClick', this, event, ev);
|
3158
|
+
}
|
3159
|
+
})
|
3160
|
+
.hover(
|
3161
|
+
function(ev) {
|
3162
|
+
view.trigger('eventMouseover', this, event, ev);
|
3163
|
+
},
|
3164
|
+
function(ev) {
|
3165
|
+
view.trigger('eventMouseout', this, event, ev);
|
3166
|
+
}
|
3167
|
+
);
|
3168
|
+
},
|
3169
|
+
|
3170
|
+
|
3171
|
+
|
3172
|
+
// get a property from the 'options' object, using smart view naming
|
3173
|
+
|
3174
|
+
option: function(name, viewName) {
|
3175
|
+
var v = this.options[name];
|
3176
|
+
if (typeof v == 'object') {
|
3177
|
+
return smartProperty(v, viewName || this.name);
|
3178
|
+
}
|
3179
|
+
return v;
|
3180
|
+
},
|
3181
|
+
|
3182
|
+
|
3183
|
+
|
3184
|
+
// event rendering utilities
|
3185
|
+
|
3186
|
+
sliceSegs: function(events, visEventEnds, start, end) {
|
3187
|
+
var segs = [],
|
3188
|
+
i, len=events.length, event,
|
3189
|
+
eventStart, eventEnd,
|
3190
|
+
segStart, segEnd,
|
3191
|
+
isStart, isEnd;
|
3192
|
+
for (i=0; i<len; i++) {
|
3193
|
+
event = events[i];
|
3194
|
+
eventStart = event.start;
|
3195
|
+
eventEnd = visEventEnds[i];
|
3196
|
+
if (eventEnd > start && eventStart < end) {
|
3197
|
+
if (eventStart < start) {
|
3198
|
+
segStart = cloneDate(start);
|
3199
|
+
isStart = false;
|
3200
|
+
}else{
|
3201
|
+
segStart = eventStart;
|
3202
|
+
isStart = true;
|
3203
|
+
}
|
3204
|
+
if (eventEnd > end) {
|
3205
|
+
segEnd = cloneDate(end);
|
3206
|
+
isEnd = false;
|
3207
|
+
}else{
|
3208
|
+
segEnd = eventEnd;
|
3209
|
+
isEnd = true;
|
3210
|
+
}
|
3211
|
+
segs.push({
|
3212
|
+
event: event,
|
3213
|
+
start: segStart,
|
3214
|
+
end: segEnd,
|
3215
|
+
isStart: isStart,
|
3216
|
+
isEnd: isEnd,
|
3217
|
+
msLength: segEnd - segStart
|
3218
|
+
});
|
3219
|
+
}
|
3220
|
+
}
|
3221
|
+
return segs.sort(segCmp);
|
3222
|
+
}
|
3223
|
+
|
3224
|
+
|
3225
|
+
};
|
3226
|
+
|
3227
|
+
|
3228
|
+
|
3229
|
+
function lazySegBind(container, segs, bindHandlers) {
|
3230
|
+
container.unbind('mouseover').mouseover(function(ev) {
|
3231
|
+
var parent=ev.target, e,
|
3232
|
+
i, seg;
|
3233
|
+
while (parent != this) {
|
3234
|
+
e = parent;
|
3235
|
+
parent = parent.parentNode;
|
3236
|
+
}
|
3237
|
+
if ((i = e._fci) !== undefined) {
|
3238
|
+
e._fci = undefined;
|
3239
|
+
seg = segs[i];
|
3240
|
+
bindHandlers(seg.event, seg.element, seg);
|
3241
|
+
$(ev.target).trigger(ev);
|
3242
|
+
}
|
3243
|
+
ev.stopPropagation();
|
3244
|
+
});
|
3245
|
+
}
|
3246
|
+
|
3247
|
+
|
3248
|
+
|
3249
|
+
// event rendering calculation utilities
|
3250
|
+
|
3251
|
+
function stackSegs(segs) {
|
3252
|
+
var levels = [],
|
3253
|
+
i, len = segs.length, seg,
|
3254
|
+
j, collide, k;
|
3255
|
+
for (i=0; i<len; i++) {
|
3256
|
+
seg = segs[i];
|
3257
|
+
j = 0; // the level index where seg should belong
|
3258
|
+
while (true) {
|
3259
|
+
collide = false;
|
3260
|
+
if (levels[j]) {
|
3261
|
+
for (k=0; k<levels[j].length; k++) {
|
3262
|
+
if (segsCollide(levels[j][k], seg)) {
|
3263
|
+
collide = true;
|
3264
|
+
break;
|
3265
|
+
}
|
3266
|
+
}
|
3267
|
+
}
|
3268
|
+
if (collide) {
|
3269
|
+
j++;
|
3270
|
+
}else{
|
3271
|
+
break;
|
3272
|
+
}
|
3273
|
+
}
|
3274
|
+
if (levels[j]) {
|
3275
|
+
levels[j].push(seg);
|
3276
|
+
}else{
|
3277
|
+
levels[j] = [seg];
|
3278
|
+
}
|
3279
|
+
}
|
3280
|
+
return levels;
|
3281
|
+
}
|
3282
|
+
|
3283
|
+
function segCmp(a, b) {
|
3284
|
+
return (b.msLength - a.msLength) * 100 + (a.event.start - b.event.start);
|
3285
|
+
}
|
3286
|
+
|
3287
|
+
function segsCollide(seg1, seg2) {
|
3288
|
+
return seg1.end > seg2.start && seg1.start < seg2.end;
|
3289
|
+
}
|
3290
|
+
|
3291
|
+
|
3292
|
+
|
3293
|
+
|
3294
|
+
function SelectionManager(view, initFunc, displayFunc, clearFunc) {
|
3295
|
+
|
3296
|
+
var t = this;
|
3297
|
+
var selected = false;
|
3298
|
+
var initialElement;
|
3299
|
+
var initialRange;
|
3300
|
+
var start;
|
3301
|
+
var end;
|
3302
|
+
var allDay;
|
3303
|
+
|
3304
|
+
|
3305
|
+
t.dragStart = function(ev) {
|
3306
|
+
initFunc();
|
3307
|
+
start = end = undefined;
|
3308
|
+
initialRange = undefined;
|
3309
|
+
initialElement = ev.currentTarget;
|
3310
|
+
};
|
3311
|
+
|
3312
|
+
|
3313
|
+
t.drag = function(currentStart, currentEnd, currentAllDay) {
|
3314
|
+
if (currentStart) {
|
3315
|
+
var range = [currentStart, currentEnd];
|
3316
|
+
if (!initialRange) {
|
3317
|
+
initialRange = range;
|
3318
|
+
}
|
3319
|
+
var dates = initialRange.concat(range).sort(cmp);
|
3320
|
+
start = dates[0];
|
3321
|
+
end = dates[3];
|
3322
|
+
allDay = currentAllDay;
|
3323
|
+
clearFunc();
|
3324
|
+
displayFunc(cloneDate(start), cloneDate(end), allDay);
|
3325
|
+
}else{
|
3326
|
+
// called with no arguments
|
3327
|
+
start = end = undefined;
|
3328
|
+
clearFunc();
|
3329
|
+
}
|
3330
|
+
};
|
3331
|
+
|
3332
|
+
|
3333
|
+
t.dragStop = function(ev) {
|
3334
|
+
if (start) {
|
3335
|
+
if (+initialRange[0] == +start && +initialRange[1] == +end) {
|
3336
|
+
view.trigger('dayClick', initialElement, start, allDay, ev);
|
3337
|
+
}
|
3338
|
+
_select();
|
3339
|
+
}
|
3340
|
+
};
|
3341
|
+
|
3342
|
+
|
3343
|
+
t.select = function(newStart, newEnd, newAllDay) {
|
3344
|
+
initFunc();
|
3345
|
+
start = newStart;
|
3346
|
+
end = newEnd;
|
3347
|
+
allDay = newAllDay;
|
3348
|
+
displayFunc(cloneDate(start), cloneDate(end), allDay);
|
3349
|
+
_select();
|
3350
|
+
};
|
3351
|
+
|
3352
|
+
|
3353
|
+
function _select() { // just set the selected flag, and trigger
|
3354
|
+
selected = true;
|
3355
|
+
view.trigger('select', view, start, end, allDay);
|
3356
|
+
}
|
3357
|
+
|
3358
|
+
|
3359
|
+
function unselect() {
|
3360
|
+
if (selected) {
|
3361
|
+
selected = false;
|
3362
|
+
start = end = undefined;
|
3363
|
+
clearFunc();
|
3364
|
+
view.trigger('unselect', view);
|
3365
|
+
}
|
3366
|
+
}
|
3367
|
+
t.unselect = unselect;
|
3368
|
+
|
3369
|
+
}
|
3370
|
+
|
3371
|
+
|
3372
|
+
function documentDragHelp(mousemove, mouseup) {
|
3373
|
+
function _mouseup(ev) {
|
3374
|
+
mouseup(ev);
|
3375
|
+
$(document)
|
3376
|
+
.unbind('mousemove', mousemove)
|
3377
|
+
.unbind('mouseup', _mouseup);
|
3378
|
+
}
|
3379
|
+
$(document)
|
3380
|
+
.mousemove(mousemove)
|
3381
|
+
.mouseup(_mouseup);
|
3382
|
+
}
|
3383
|
+
|
3384
|
+
|
3385
|
+
function documentUnselectAuto(view, unselectFunc) {
|
3386
|
+
if (view.option('selectable') && view.option('unselectAuto')) {
|
3387
|
+
$(document).mousedown(function(ev) {
|
3388
|
+
var ignore = view.option('unselectCancel');
|
3389
|
+
if (ignore) {
|
3390
|
+
if ($(ev.target).parents(ignore).length) { // could be optimized to stop after first match
|
3391
|
+
return;
|
3392
|
+
}
|
3393
|
+
}
|
3394
|
+
unselectFunc();
|
3395
|
+
});
|
3396
|
+
}
|
3397
|
+
}
|
3398
|
+
|
3399
|
+
|
3400
|
+
|
3401
|
+
|
3402
|
+
/* Date Math
|
3403
|
+
-----------------------------------------------------------------------------*/
|
3404
|
+
|
3405
|
+
var DAY_MS = 86400000,
|
3406
|
+
HOUR_MS = 3600000,
|
3407
|
+
MINUTE_MS = 60000;
|
3408
|
+
|
3409
|
+
function addYears(d, n, keepTime) {
|
3410
|
+
d.setFullYear(d.getFullYear() + n);
|
3411
|
+
if (!keepTime) {
|
3412
|
+
clearTime(d);
|
3413
|
+
}
|
3414
|
+
return d;
|
3415
|
+
}
|
3416
|
+
|
3417
|
+
function addMonths(d, n, keepTime) { // prevents day overflow/underflow
|
3418
|
+
if (+d) { // prevent infinite looping on invalid dates
|
3419
|
+
var m = d.getMonth() + n,
|
3420
|
+
check = cloneDate(d);
|
3421
|
+
check.setDate(1);
|
3422
|
+
check.setMonth(m);
|
3423
|
+
d.setMonth(m);
|
3424
|
+
if (!keepTime) {
|
3425
|
+
clearTime(d);
|
3426
|
+
}
|
3427
|
+
while (d.getMonth() != check.getMonth()) {
|
3428
|
+
d.setDate(d.getDate() + (d < check ? 1 : -1));
|
3429
|
+
}
|
3430
|
+
}
|
3431
|
+
return d;
|
3432
|
+
}
|
3433
|
+
|
3434
|
+
function addDays(d, n, keepTime) { // deals with daylight savings
|
3435
|
+
if (+d) {
|
3436
|
+
var dd = d.getDate() + n,
|
3437
|
+
check = cloneDate(d);
|
3438
|
+
check.setHours(9); // set to middle of day
|
3439
|
+
check.setDate(dd);
|
3440
|
+
d.setDate(dd);
|
3441
|
+
if (!keepTime) {
|
3442
|
+
clearTime(d);
|
3443
|
+
}
|
3444
|
+
fixDate(d, check);
|
3445
|
+
}
|
3446
|
+
return d;
|
3447
|
+
}
|
3448
|
+
fc.addDays = addDays;
|
3449
|
+
|
3450
|
+
function fixDate(d, check) { // force d to be on check's YMD, for daylight savings purposes
|
3451
|
+
if (+d) { // prevent infinite looping on invalid dates
|
3452
|
+
while (d.getDate() != check.getDate()) {
|
3453
|
+
d.setTime(+d + (d < check ? 1 : -1) * HOUR_MS);
|
3454
|
+
}
|
3455
|
+
}
|
3456
|
+
}
|
3457
|
+
|
3458
|
+
function addMinutes(d, n) {
|
3459
|
+
d.setMinutes(d.getMinutes() + n);
|
3460
|
+
return d;
|
3461
|
+
}
|
3462
|
+
|
3463
|
+
function clearTime(d) {
|
3464
|
+
d.setHours(0);
|
3465
|
+
d.setMinutes(0);
|
3466
|
+
d.setSeconds(0);
|
3467
|
+
d.setMilliseconds(0);
|
3468
|
+
return d;
|
3469
|
+
}
|
3470
|
+
|
3471
|
+
function cloneDate(d, dontKeepTime) {
|
3472
|
+
if (dontKeepTime) {
|
3473
|
+
return clearTime(new Date(+d));
|
3474
|
+
}
|
3475
|
+
return new Date(+d);
|
3476
|
+
}
|
3477
|
+
fc.cloneDate = cloneDate;
|
3478
|
+
|
3479
|
+
function zeroDate() { // returns a Date with time 00:00:00 and dateOfMonth=1
|
3480
|
+
var i=0, d;
|
3481
|
+
do {
|
3482
|
+
d = new Date(1970, i++, 1);
|
3483
|
+
} while (d.getHours()); // != 0
|
3484
|
+
return d;
|
3485
|
+
}
|
3486
|
+
|
3487
|
+
function skipWeekend(date, inc, excl) {
|
3488
|
+
inc = inc || 1;
|
3489
|
+
while (!date.getDay() || (excl && date.getDay()==1 || !excl && date.getDay()==6)) {
|
3490
|
+
addDays(date, inc);
|
3491
|
+
}
|
3492
|
+
return date;
|
3493
|
+
}
|
3494
|
+
|
3495
|
+
function dayDiff(d1, d2) { // d1 - d2
|
3496
|
+
return Math.round((cloneDate(d1, true) - cloneDate(d2, true)) / DAY_MS);
|
3497
|
+
}
|
3498
|
+
|
3499
|
+
|
3500
|
+
|
3501
|
+
/* Date Parsing
|
3502
|
+
-----------------------------------------------------------------------------*/
|
3503
|
+
|
3504
|
+
var parseDate = fc.parseDate = function(s) {
|
3505
|
+
if (typeof s == 'object') { // already a Date object
|
3506
|
+
return s;
|
3507
|
+
}
|
3508
|
+
if (typeof s == 'number') { // a UNIX timestamp
|
3509
|
+
return new Date(s * 1000);
|
3510
|
+
}
|
3511
|
+
if (typeof s == 'string') {
|
3512
|
+
if (s.match(/^\d+$/)) { // a UNIX timestamp
|
3513
|
+
return new Date(parseInt(s) * 1000);
|
3514
|
+
}
|
3515
|
+
return parseISO8601(s, true) || (s ? new Date(s) : null);
|
3516
|
+
}
|
3517
|
+
// TODO: never return invalid dates (like from new Date(<string>)), return null instead
|
3518
|
+
return null;
|
3519
|
+
};
|
3520
|
+
|
3521
|
+
var parseISO8601 = fc.parseISO8601 = function(s, ignoreTimezone) {
|
3522
|
+
// derived from http://delete.me.uk/2005/03/iso8601.html
|
3523
|
+
// TODO: for a know glitch/feature, read tests/issue_206_parseDate_dst.html
|
3524
|
+
var m = s.match(/^([0-9]{4})(-([0-9]{2})(-([0-9]{2})([T ]([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?$/);
|
3525
|
+
if (!m) {
|
3526
|
+
return null;
|
3527
|
+
}
|
3528
|
+
var date = new Date(m[1], 0, 1),
|
3529
|
+
check = new Date(m[1], 0, 1, 9, 0),
|
3530
|
+
offset = 0;
|
3531
|
+
if (m[3]) {
|
3532
|
+
date.setMonth(m[3] - 1);
|
3533
|
+
check.setMonth(m[3] - 1);
|
3534
|
+
}
|
3535
|
+
if (m[5]) {
|
3536
|
+
date.setDate(m[5]);
|
3537
|
+
check.setDate(m[5]);
|
3538
|
+
}
|
3539
|
+
fixDate(date, check);
|
3540
|
+
if (m[7]) {
|
3541
|
+
date.setHours(m[7]);
|
3542
|
+
}
|
3543
|
+
if (m[8]) {
|
3544
|
+
date.setMinutes(m[8]);
|
3545
|
+
}
|
3546
|
+
if (m[10]) {
|
3547
|
+
date.setSeconds(m[10]);
|
3548
|
+
}
|
3549
|
+
if (m[12]) {
|
3550
|
+
date.setMilliseconds(Number("0." + m[12]) * 1000);
|
3551
|
+
}
|
3552
|
+
fixDate(date, check);
|
3553
|
+
if (!ignoreTimezone) {
|
3554
|
+
if (m[14]) {
|
3555
|
+
offset = Number(m[16]) * 60 + Number(m[17]);
|
3556
|
+
offset *= m[15] == '-' ? 1 : -1;
|
3557
|
+
}
|
3558
|
+
offset -= date.getTimezoneOffset();
|
3559
|
+
}
|
3560
|
+
return new Date(+date + (offset * 60 * 1000));
|
3561
|
+
};
|
3562
|
+
|
3563
|
+
var parseTime = fc.parseTime = function(s) { // returns minutes since start of day
|
3564
|
+
if (typeof s == 'number') { // an hour
|
3565
|
+
return s * 60;
|
3566
|
+
}
|
3567
|
+
if (typeof s == 'object') { // a Date object
|
3568
|
+
return s.getHours() * 60 + s.getMinutes();
|
3569
|
+
}
|
3570
|
+
var m = s.match(/(\d+)(?::(\d+))?\s*(\w+)?/);
|
3571
|
+
if (m) {
|
3572
|
+
var h = parseInt(m[1]);
|
3573
|
+
if (m[3]) {
|
3574
|
+
h %= 12;
|
3575
|
+
if (m[3].toLowerCase().charAt(0) == 'p') {
|
3576
|
+
h += 12;
|
3577
|
+
}
|
3578
|
+
}
|
3579
|
+
return h * 60 + (m[2] ? parseInt(m[2]) : 0);
|
3580
|
+
}
|
3581
|
+
};
|
3582
|
+
|
3583
|
+
|
3584
|
+
|
3585
|
+
/* Date Formatting
|
3586
|
+
-----------------------------------------------------------------------------*/
|
3587
|
+
|
3588
|
+
var formatDate = fc.formatDate = function(date, format, options) {
|
3589
|
+
return formatDates(date, null, format, options);
|
3590
|
+
};
|
3591
|
+
|
3592
|
+
var formatDates = fc.formatDates = function(date1, date2, format, options) {
|
3593
|
+
options = options || defaults;
|
3594
|
+
var date = date1,
|
3595
|
+
otherDate = date2,
|
3596
|
+
i, len = format.length, c,
|
3597
|
+
i2, formatter,
|
3598
|
+
res = '';
|
3599
|
+
for (i=0; i<len; i++) {
|
3600
|
+
c = format.charAt(i);
|
3601
|
+
if (c == "'") {
|
3602
|
+
for (i2=i+1; i2<len; i2++) {
|
3603
|
+
if (format.charAt(i2) == "'") {
|
3604
|
+
if (date) {
|
3605
|
+
if (i2 == i+1) {
|
3606
|
+
res += "'";
|
3607
|
+
}else{
|
3608
|
+
res += format.substring(i+1, i2);
|
3609
|
+
}
|
3610
|
+
i = i2;
|
3611
|
+
}
|
3612
|
+
break;
|
3613
|
+
}
|
3614
|
+
}
|
3615
|
+
}
|
3616
|
+
else if (c == '(') {
|
3617
|
+
for (i2=i+1; i2<len; i2++) {
|
3618
|
+
if (format.charAt(i2) == ')') {
|
3619
|
+
var subres = formatDate(date, format.substring(i+1, i2), options);
|
3620
|
+
if (parseInt(subres.replace(/\D/, ''))) {
|
3621
|
+
res += subres;
|
3622
|
+
}
|
3623
|
+
i = i2;
|
3624
|
+
break;
|
3625
|
+
}
|
3626
|
+
}
|
3627
|
+
}
|
3628
|
+
else if (c == '[') {
|
3629
|
+
for (i2=i+1; i2<len; i2++) {
|
3630
|
+
if (format.charAt(i2) == ']') {
|
3631
|
+
var subformat = format.substring(i+1, i2);
|
3632
|
+
var subres = formatDate(date, subformat, options);
|
3633
|
+
if (subres != formatDate(otherDate, subformat, options)) {
|
3634
|
+
res += subres;
|
3635
|
+
}
|
3636
|
+
i = i2;
|
3637
|
+
break;
|
3638
|
+
}
|
3639
|
+
}
|
3640
|
+
}
|
3641
|
+
else if (c == '{') {
|
3642
|
+
date = date2;
|
3643
|
+
otherDate = date1;
|
3644
|
+
}
|
3645
|
+
else if (c == '}') {
|
3646
|
+
date = date1;
|
3647
|
+
otherDate = date2;
|
3648
|
+
}
|
3649
|
+
else {
|
3650
|
+
for (i2=len; i2>i; i2--) {
|
3651
|
+
if (formatter = dateFormatters[format.substring(i, i2)]) {
|
3652
|
+
if (date) {
|
3653
|
+
res += formatter(date, options);
|
3654
|
+
}
|
3655
|
+
i = i2 - 1;
|
3656
|
+
break;
|
3657
|
+
}
|
3658
|
+
}
|
3659
|
+
if (i2 == i) {
|
3660
|
+
if (date) {
|
3661
|
+
res += c;
|
3662
|
+
}
|
3663
|
+
}
|
3664
|
+
}
|
3665
|
+
}
|
3666
|
+
return res;
|
3667
|
+
};
|
3668
|
+
|
3669
|
+
var dateFormatters = {
|
3670
|
+
s : function(d) { return d.getSeconds() },
|
3671
|
+
ss : function(d) { return zeroPad(d.getSeconds()) },
|
3672
|
+
m : function(d) { return d.getMinutes() },
|
3673
|
+
mm : function(d) { return zeroPad(d.getMinutes()) },
|
3674
|
+
h : function(d) { return d.getHours() % 12 || 12 },
|
3675
|
+
hh : function(d) { return zeroPad(d.getHours() % 12 || 12) },
|
3676
|
+
H : function(d) { return d.getHours() },
|
3677
|
+
HH : function(d) { return zeroPad(d.getHours()) },
|
3678
|
+
d : function(d) { return d.getDate() },
|
3679
|
+
dd : function(d) { return zeroPad(d.getDate()) },
|
3680
|
+
ddd : function(d,o) { return o.dayNamesShort[d.getDay()] },
|
3681
|
+
dddd: function(d,o) { return o.dayNames[d.getDay()] },
|
3682
|
+
M : function(d) { return d.getMonth() + 1 },
|
3683
|
+
MM : function(d) { return zeroPad(d.getMonth() + 1) },
|
3684
|
+
MMM : function(d,o) { return o.monthNamesShort[d.getMonth()] },
|
3685
|
+
MMMM: function(d,o) { return o.monthNames[d.getMonth()] },
|
3686
|
+
yy : function(d) { return (d.getFullYear()+'').substring(2) },
|
3687
|
+
yyyy: function(d) { return d.getFullYear() },
|
3688
|
+
t : function(d) { return d.getHours() < 12 ? 'a' : 'p' },
|
3689
|
+
tt : function(d) { return d.getHours() < 12 ? 'am' : 'pm' },
|
3690
|
+
T : function(d) { return d.getHours() < 12 ? 'A' : 'P' },
|
3691
|
+
TT : function(d) { return d.getHours() < 12 ? 'AM' : 'PM' },
|
3692
|
+
u : function(d) { return formatDate(d, "yyyy-MM-dd'T'HH:mm:ss'Z'") },
|
3693
|
+
S : function(d) {
|
3694
|
+
var date = d.getDate();
|
3695
|
+
if (date > 10 && date < 20) {
|
3696
|
+
return 'th';
|
3697
|
+
}
|
3698
|
+
return ['st', 'nd', 'rd'][date%10-1] || 'th';
|
3699
|
+
}
|
3700
|
+
};
|
3701
|
+
|
3702
|
+
|
3703
|
+
|
3704
|
+
/* Element Dimensions
|
3705
|
+
-----------------------------------------------------------------------------*/
|
3706
|
+
|
3707
|
+
function setOuterWidth(element, width, includeMargins) {
|
3708
|
+
element.each(function(i, _element) {
|
3709
|
+
_element.style.width = width - hsides(_element, includeMargins) + 'px';
|
3710
|
+
});
|
3711
|
+
}
|
3712
|
+
|
3713
|
+
function setOuterHeight(element, height, includeMargins) {
|
3714
|
+
element.each(function(i, _element) {
|
3715
|
+
_element.style.height = height - vsides(_element, includeMargins) + 'px';
|
3716
|
+
});
|
3717
|
+
}
|
3718
|
+
|
3719
|
+
|
3720
|
+
function hsides(_element, includeMargins) {
|
3721
|
+
return (parseFloat(jQuery.curCSS(_element, 'paddingLeft', true)) || 0) +
|
3722
|
+
(parseFloat(jQuery.curCSS(_element, 'paddingRight', true)) || 0) +
|
3723
|
+
(parseFloat(jQuery.curCSS(_element, 'borderLeftWidth', true)) || 0) +
|
3724
|
+
(parseFloat(jQuery.curCSS(_element, 'borderRightWidth', true)) || 0) +
|
3725
|
+
(includeMargins ? hmargins(_element) : 0);
|
3726
|
+
}
|
3727
|
+
|
3728
|
+
function hmargins(_element) {
|
3729
|
+
return (parseFloat(jQuery.curCSS(_element, 'marginLeft', true)) || 0) +
|
3730
|
+
(parseFloat(jQuery.curCSS(_element, 'marginRight', true)) || 0);
|
3731
|
+
}
|
3732
|
+
|
3733
|
+
function vsides(_element, includeMargins) {
|
3734
|
+
return (parseFloat(jQuery.curCSS(_element, 'paddingTop', true)) || 0) +
|
3735
|
+
(parseFloat(jQuery.curCSS(_element, 'paddingBottom', true)) || 0) +
|
3736
|
+
(parseFloat(jQuery.curCSS(_element, 'borderTopWidth', true)) || 0) +
|
3737
|
+
(parseFloat(jQuery.curCSS(_element, 'borderBottomWidth', true)) || 0) +
|
3738
|
+
(includeMargins ? vmargins(_element) : 0);
|
3739
|
+
}
|
3740
|
+
|
3741
|
+
function vmargins(_element) {
|
3742
|
+
return (parseFloat(jQuery.curCSS(_element, 'marginTop', true)) || 0) +
|
3743
|
+
(parseFloat(jQuery.curCSS(_element, 'marginBottom', true)) || 0);
|
3744
|
+
}
|
3745
|
+
|
3746
|
+
|
3747
|
+
|
3748
|
+
|
3749
|
+
function setMinHeight(element, h) {
|
3750
|
+
h = typeof h == 'number' ? h + 'px' : h;
|
3751
|
+
element[0].style.cssText += ';min-height:' + h + ';_height:' + h;
|
3752
|
+
}
|
3753
|
+
|
3754
|
+
|
3755
|
+
|
3756
|
+
/* Position Calculation
|
3757
|
+
-----------------------------------------------------------------------------*/
|
3758
|
+
// nasty bugs in opera 9.25
|
3759
|
+
// position()'s top returning incorrectly with TR/TD or elements within TD
|
3760
|
+
|
3761
|
+
var topBug;
|
3762
|
+
|
3763
|
+
function topCorrect(tr) { // tr/th/td or anything else
|
3764
|
+
if (topBug !== false) {
|
3765
|
+
var cell;
|
3766
|
+
if (tr.is('th,td')) {
|
3767
|
+
tr = (cell = tr).parent();
|
3768
|
+
}
|
3769
|
+
if (topBug === undefined && tr.is('tr')) {
|
3770
|
+
topBug = tr.position().top != tr.children().position().top;
|
3771
|
+
}
|
3772
|
+
if (topBug) {
|
3773
|
+
return tr.parent().position().top + (cell ? tr.position().top - cell.position().top : 0);
|
3774
|
+
}
|
3775
|
+
}
|
3776
|
+
return 0;
|
3777
|
+
}
|
3778
|
+
|
3779
|
+
|
3780
|
+
|
3781
|
+
/* Hover Matrix
|
3782
|
+
-----------------------------------------------------------------------------*/
|
3783
|
+
|
3784
|
+
function HoverMatrix(rowElements, colElements, changeCallback) {
|
3785
|
+
|
3786
|
+
var t=this,
|
3787
|
+
tops=[], lefts=[],
|
3788
|
+
origRow, origCol,
|
3789
|
+
currRow, currCol,
|
3790
|
+
e;
|
3791
|
+
|
3792
|
+
$.each(rowElements, function(i, _e) {
|
3793
|
+
e = $(_e);
|
3794
|
+
tops.push(e.offset().top + topCorrect(e));
|
3795
|
+
});
|
3796
|
+
tops.push(tops[tops.length-1] + e.outerHeight());
|
3797
|
+
$.each(colElements, function(i, _e) {
|
3798
|
+
e = $(_e);
|
3799
|
+
lefts.push(e.offset().left);
|
3800
|
+
});
|
3801
|
+
lefts.push(lefts[lefts.length-1] + e.outerWidth());
|
3802
|
+
|
3803
|
+
|
3804
|
+
t.mouse = function(ev) {
|
3805
|
+
var x = ev.pageX;
|
3806
|
+
var y = ev.pageY;
|
3807
|
+
var r, c;
|
3808
|
+
for (r=0; r<tops.length && y>=tops[r]; r++) {}
|
3809
|
+
for (c=0; c<lefts.length && x>=lefts[c]; c++) {}
|
3810
|
+
r = r >= tops.length ? -1 : r - 1;
|
3811
|
+
c = c >= lefts.length ? -1 : c - 1;
|
3812
|
+
if (r != currRow || c != currCol) {
|
3813
|
+
currRow = r;
|
3814
|
+
currCol = c;
|
3815
|
+
if (r == -1 || c == -1) {
|
3816
|
+
t.cell = null;
|
3817
|
+
}else{
|
3818
|
+
if (origRow === undefined) {
|
3819
|
+
origRow = r;
|
3820
|
+
origCol = c;
|
3821
|
+
}
|
3822
|
+
t.cell = {
|
3823
|
+
row: r,
|
3824
|
+
col: c,
|
3825
|
+
top: tops[r],
|
3826
|
+
left: lefts[c],
|
3827
|
+
width: lefts[c+1] - lefts[c],
|
3828
|
+
height: tops[r+1] - tops[r],
|
3829
|
+
origRow: origRow,
|
3830
|
+
origCol: origCol,
|
3831
|
+
isOrig: r==origRow && c==origCol,
|
3832
|
+
rowDelta: r-origRow,
|
3833
|
+
colDelta: c-origCol
|
3834
|
+
};
|
3835
|
+
}
|
3836
|
+
changeCallback(t.cell);
|
3837
|
+
}
|
3838
|
+
};
|
3839
|
+
|
3840
|
+
t.rect = function(row0, col0, row1, col1, originElement) { // row1,col1 are exclusive
|
3841
|
+
var origin = originElement.offset();
|
3842
|
+
return {
|
3843
|
+
top: tops[row0] - origin.top,
|
3844
|
+
left: lefts[col0] - origin.left,
|
3845
|
+
width: lefts[col1] - lefts[col0],
|
3846
|
+
height: tops[row1] - tops[row0]
|
3847
|
+
};
|
3848
|
+
};
|
3849
|
+
|
3850
|
+
}
|
3851
|
+
|
3852
|
+
|
3853
|
+
|
3854
|
+
/* Misc Utils
|
3855
|
+
-----------------------------------------------------------------------------*/
|
3856
|
+
|
3857
|
+
var undefined,
|
3858
|
+
dayIDs = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
|
3859
|
+
|
3860
|
+
function zeroPad(n) {
|
3861
|
+
return (n < 10 ? '0' : '') + n;
|
3862
|
+
}
|
3863
|
+
|
3864
|
+
function smartProperty(obj, name) { // get a camel-cased/namespaced property of an object
|
3865
|
+
if (obj[name] !== undefined) {
|
3866
|
+
return obj[name];
|
3867
|
+
}
|
3868
|
+
var parts = name.split(/(?=[A-Z])/),
|
3869
|
+
i=parts.length-1, res;
|
3870
|
+
for (; i>=0; i--) {
|
3871
|
+
res = obj[parts[i].toLowerCase()];
|
3872
|
+
if (res !== undefined) {
|
3873
|
+
return res;
|
3874
|
+
}
|
3875
|
+
}
|
3876
|
+
return obj[''];
|
3877
|
+
}
|
3878
|
+
|
3879
|
+
function htmlEscape(s) {
|
3880
|
+
return s
|
3881
|
+
.replace(/&/g, '&')
|
3882
|
+
.replace(/</g, '<')
|
3883
|
+
.replace(/>/g, '>')
|
3884
|
+
.replace(/'/g, ''')
|
3885
|
+
.replace(/"/g, '"');
|
3886
|
+
}
|
3887
|
+
|
3888
|
+
|
3889
|
+
|
3890
|
+
function HorizontalPositionCache(getElement) {
|
3891
|
+
|
3892
|
+
var t = this,
|
3893
|
+
elements = {},
|
3894
|
+
lefts = {},
|
3895
|
+
rights = {};
|
3896
|
+
|
3897
|
+
function e(i) {
|
3898
|
+
return elements[i] = elements[i] || getElement(i);
|
3899
|
+
}
|
3900
|
+
|
3901
|
+
t.left = function(i) {
|
3902
|
+
return lefts[i] = lefts[i] === undefined ? e(i).position().left : lefts[i];
|
3903
|
+
};
|
3904
|
+
|
3905
|
+
t.right = function(i) {
|
3906
|
+
return rights[i] = rights[i] === undefined ? t.left(i) + e(i).width() : rights[i];
|
3907
|
+
};
|
3908
|
+
|
3909
|
+
t.clear = function() {
|
3910
|
+
elements = {};
|
3911
|
+
lefts = {};
|
3912
|
+
rights = {};
|
3913
|
+
};
|
3914
|
+
|
3915
|
+
}
|
3916
|
+
|
3917
|
+
|
3918
|
+
|
3919
|
+
function cssKey(_element) {
|
3920
|
+
return _element.id + '/' + _element.className + '/' + _element.style.cssText.replace(/(^|;)\s*(top|left|width|height)\s*:[^;]*/ig, '');
|
3921
|
+
}
|
3922
|
+
|
3923
|
+
|
3924
|
+
|
3925
|
+
function cmp(a, b) {
|
3926
|
+
return a - b;
|
3927
|
+
}
|
3928
|
+
|
3929
|
+
|
3930
|
+
|
3931
|
+
function exclEndDay(event) {
|
3932
|
+
if (event.end) {
|
3933
|
+
return _exclEndDay(event.end, event.allDay);
|
3934
|
+
}else{
|
3935
|
+
return addDays(cloneDate(event.start), 1);
|
3936
|
+
}
|
3937
|
+
}
|
3938
|
+
|
3939
|
+
function _exclEndDay(end, allDay) {
|
3940
|
+
end = cloneDate(end);
|
3941
|
+
return allDay || end.getHours() || end.getMinutes() ? addDays(end, 1) : end;
|
3942
|
+
}
|
3943
|
+
|
3944
|
+
|
3945
|
+
|
3946
|
+
function disableTextSelection(element) {
|
3947
|
+
element
|
3948
|
+
.attr('unselectable', 'on')
|
3949
|
+
.css('MozUserSelect', 'none')
|
3950
|
+
.bind('selectstart.ui', function() { return false; });
|
3951
|
+
}
|
3952
|
+
|
3953
|
+
/*
|
3954
|
+
function enableTextSelection(element) {
|
3955
|
+
element
|
3956
|
+
.attr('unselectable', 'off')
|
3957
|
+
.css('MozUserSelect', '')
|
3958
|
+
.unbind('selectstart.ui');
|
3959
|
+
}
|
3960
|
+
*/
|
3961
|
+
|
3962
|
+
|
3963
|
+
|
3964
|
+
|
3965
|
+
})(jQuery);
|