diff --git a/.cane b/.cane deleted file mode 100644 index be58694b1..000000000 --- a/.cane +++ /dev/null @@ -1 +0,0 @@ ---no-doc diff --git a/.gitignore b/.gitignore index 86eb14668..8157fccec 100755 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ .elasticbeanstalk/ .DS_Store /public/data +/public/assets *.png coverage .tags diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 000000000..77b4529d0 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,9 @@ +inherit_gem: + rubocop-github: + - config/default.yml + - config/rails.yml + + +Rails/OutputSafety: + Exclude: + - 'app/controllers/password_resets_controller.rb' diff --git a/.ruby-version b/.ruby-version index 0bee604df..37d02a6e3 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.3.3 +3.3.8 diff --git a/.travis.yml b/.travis.yml index 77410e043..600481b49 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: ruby rvm: - - "2.3.3" + - "2.6.5" before_install: - "phantomjs --version" @@ -13,5 +13,4 @@ before_install: before_script: bundle exec rails db:test:prepare cache: bundler -sudo: false env: RAILSGOAT_MAINTAINER=true diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..00c1c7682 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,77 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. +Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. +Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at railsgoat@gmail.com. +All complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. +The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..a0ee2be71 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,66 @@ +# Ways to Contribute to RailsGoat + +Thanks for your interest on contributing to RailsGoat! +Here are a few general guidelines on contributing and reporting +bugs to RailsGoat that we ask you to take a look first. +Notice that all of your interactions in the project are +expected to follow our [Code of Conduct](./CODE_OF_CONDUCT.md). + +## Reporting Issues + +Before reporting a new issue, please be sure that the issue wasn't +already reported or fixed by searching on GitHub through our +[issues](https://github.com/OWASP/railsgoat/issues). + +When creating a new issue, be sure to include a **title and clear description**, +as much relevant information as possible, and either a test case example or +even better a **sample Rails app that replicates the issue** - +RailsGoat has a lot of moving parts and it's functionality can be affected +by third party gems, so we need as much context and details as possible +to identify what might be broken for you. + +Avoid opening new issues to ask questions in our issues tracker. +Please go through the project wiki, documentation and source code first, +or try to ask your question in our +[Slack Channel](https://owasp.slack.com/messages/C04THC44W). + +## Sending Pull Requests + +Before sending a new Pull Request, take a look on existing Pull Requests +and Issues to see if the proposed change or fix has been discussed in +the past, or if the change was already implemented but not yet released. + +We expect new Pull Requests to include enough tests for new or changed +behavior, and we aim to maintain everything as most backwards compatible +as possible, reserving breaking changes to be ship in major releases +when necessary + +If your Pull Request includes new or changed behavior, be sure that the +changes are beneficial to a wide range of use cases or it's an application +specific change that might not be so valuable to other applications. + +We also welcome Pull Requests that improve our existing documentation +(both our `README.md` and the doc sections in the source code). + +## Other Ways to Contribute + +We welcome anyone that wants to contribute to RailsGoat to triage +and reply to open issues to help troubleshoot and fix existing bugs +on RailsGoat. Here is what you can do: + +* Help ensure that existing issues follows the recommendations from the +[Reporting Issues template](./ISSUE_TEMPLATE.md), +providing feeback to the issue's author on what might be missing. +* Review and update the existing content of our +[Wiki](https://github.com/OWASP/railsgoat/wiki) +with up to date instructions and code samples - the wiki was grown +with several different tutorials and references that we can't keep +track of everything, so if there is a page that showcases an integration +or customization that you are familiar with feel free to update it +as necessary. +* Review existing Pull Requests, and testing patches against real +existing applications that use RailsGoat. + +Thanks again for your interest on contributing to the project! + +:heart: diff --git a/Dockerfile b/Dockerfile index 145b23120..1af097ce0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,9 @@ -FROM ruby:2.3.3 +FROM --platform=linux/amd64 ruby:2.6.5 RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs RUN mkdir /myapp WORKDIR /myapp ADD Gemfile /myapp/Gemfile ADD Gemfile.lock /myapp/Gemfile.lock +RUN gem install bundler -v 1.17.3 RUN bundle install ADD . /myapp - diff --git a/Gemfile b/Gemfile index fafe45541..cd7a9736c 100644 --- a/Gemfile +++ b/Gemfile @@ -1,100 +1,65 @@ -source 'https://rubygems.org' +# frozen_string_literal: true +source "https://rubygems.org" #don't upgrade -gem 'rails', '5.0.1' +gem "rails", "7.1.5" + +ruby "3.3.8" + +gem "aruba" +gem "bcrypt" +gem "coffee-rails" +gem "execjs" +gem "foreman" +gem "jquery-fileupload-rails" +gem "jquery-rails" +gem "minitest" +gem "powder" # Pow related gem +gem "pry-rails" # not in dev group in case running via prod/staging @ a training +gem "puma" +gem "rails-perftest" +gem "rake" +gem "responders" #For Rails 4.2 # LOCKED DOWN +gem "ruby-prof" +gem "sassc-rails" +gem "simplecov", require: false, group: :test +gem "sqlite3" +gem "mini_racer" # Modern V8 JavaScript runtime +gem "turbolinks" +gem "uglifier" +gem "unicorn" -ruby '2.3.3' - -gem 'rake' - -# Bundle edge Rails instead: -# gem 'rails', git: 'https://github.com/rails/rails' - -gem 'sqlite3' -gem 'foreman' -gem 'crack', '0.3.1' - -# Pry for Rails, not in dev group in case running via prod/staging @ a training -gem 'pry-rails' +# Add SMTP server support using MailCatcher +# NOTE: https://github.com/sj26/mailcatcher#bundler +# gem 'mailcatcher' group :development, :mysql do - gem 'brakeman' - gem 'bundler-audit' - gem 'guard-brakeman' - gem 'guard-livereload' - gem 'guard-rspec' - gem 'guard-shell' - gem 'pry' - gem 'rack-livereload' - gem 'rb-fsevent' - gem 'travis-lint' - gem 'better_errors' - gem 'binding_of_caller' + gem "better_errors" + gem "binding_of_caller" + gem "bundler-audit" + gem "guard-livereload" + gem "guard-rspec" + gem "guard-shell" + gem "pry" + gem "rack-livereload" + gem "rb-fsevent" + gem "rubocop-github" + gem "travis-lint" end -gem 'simplecov', :require => false, :group => :test - group :development, :test, :mysql do - gem 'launchy' - gem 'capybara' - gem 'database_cleaner' - gem 'poltergeist' - gem 'rspec-rails' - gem 'test-unit' + gem "capybara" + gem "database_cleaner" + gem "launchy" + gem "poltergeist" + gem "rspec-rails", '4.0.0.beta3' # 4/26/2019: LOCKED DOWN + gem "test-unit" end -group :mysql do - gem 'mysql2' +group :openshift do + gem "pg" end -# Gems used only for assets and not required -# in production environments by default. -gem 'sass-rails' -gem 'coffee-rails' -gem 'jquery-fileupload-rails' -gem 'uglifier' -gem 'turbolinks' - -# See https://github.com/sstephenson/execjs#readme for more supported runtimes -# gem 'therubyracer', :platforms => :ruby - -gem 'jquery-rails' - -# To use ActiveModel has_secure_password - gem 'bcrypt' - -# To use Jbuilder templates for JSON -# gem 'jbuilder' - -# Use unicorn as the app server - gem 'unicorn' - -# Pow related gem -gem 'powder' - -gem 'aruba' -#gem 'minitest', '~> 4.0', :require=> "minitest/autorun" - -#gem 'minitest' - -# Deploy with Capistrano -# gem 'capistrano' - -# To use debugger -# gem 'debugger' - -gem 'execjs' -gem 'therubyracer' - -# Add SMTP server support using MailCatcher -# NOTE: https://github.com/sj26/mailcatcher#bundler -# gem 'mailcatcher' - -#For Rails 4.0 -#group :doc do -# # bundle exec rails doc:rails generates the API under doc/api. -# gem 'sdoc', require: false -#end - -#For Rails 4.2 -gem 'responders' +group :mysql do + # gem "mysql2" # Commented out for initial setup - install MySQL first if needed +end diff --git a/Gemfile.lock b/Gemfile.lock index 8e01d9cc9..9f13ad913 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,126 +1,196 @@ GEM remote: https://rubygems.org/ specs: - actioncable (5.0.1) - actionpack (= 5.0.1) - nio4r (~> 1.2) - websocket-driver (~> 0.6.1) - actionmailer (5.0.1) - actionpack (= 5.0.1) - actionview (= 5.0.1) - activejob (= 5.0.1) + actioncable (7.1.5) + actionpack (= 7.1.5) + activesupport (= 7.1.5) + nio4r (~> 2.0) + websocket-driver (>= 0.6.1) + zeitwerk (~> 2.6) + actionmailbox (7.1.5) + actionpack (= 7.1.5) + activejob (= 7.1.5) + activerecord (= 7.1.5) + activestorage (= 7.1.5) + activesupport (= 7.1.5) + mail (>= 2.7.1) + net-imap + net-pop + net-smtp + actionmailer (7.1.5) + actionpack (= 7.1.5) + actionview (= 7.1.5) + activejob (= 7.1.5) + activesupport (= 7.1.5) mail (~> 2.5, >= 2.5.4) - rails-dom-testing (~> 2.0) - actionpack (5.0.1) - actionview (= 5.0.1) - activesupport (= 5.0.1) - rack (~> 2.0) - rack-test (~> 0.6.3) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.0.1) - activesupport (= 5.0.1) + net-imap + net-pop + net-smtp + rails-dom-testing (~> 2.2) + actionpack (7.1.5) + actionview (= 7.1.5) + activesupport (= 7.1.5) + nokogiri (>= 1.8.5) + racc + rack (>= 2.2.4) + rack-session (>= 1.0.1) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + actiontext (7.1.5) + actionpack (= 7.1.5) + activerecord (= 7.1.5) + activestorage (= 7.1.5) + activesupport (= 7.1.5) + globalid (>= 0.6.0) + nokogiri (>= 1.8.5) + actionview (7.1.5) + activesupport (= 7.1.5) builder (~> 3.1) - erubis (~> 2.7.0) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.0.2) - activejob (5.0.1) - activesupport (= 5.0.1) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activejob (7.1.5) + activesupport (= 7.1.5) globalid (>= 0.3.6) - activemodel (5.0.1) - activesupport (= 5.0.1) - activerecord (5.0.1) - activemodel (= 5.0.1) - activesupport (= 5.0.1) - arel (~> 7.0) - activesupport (5.0.1) + activemodel (7.1.5) + activesupport (= 7.1.5) + activerecord (7.1.5) + activemodel (= 7.1.5) + activesupport (= 7.1.5) + timeout (>= 0.4.0) + activestorage (7.1.5) + actionpack (= 7.1.5) + activejob (= 7.1.5) + activerecord (= 7.1.5) + activesupport (= 7.1.5) + marcel (~> 1.0) + activesupport (7.1.5) + base64 + benchmark (>= 0.3) + bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (~> 0.7) - minitest (~> 5.1) - tzinfo (~> 1.1) - addressable (2.5.0) - public_suffix (~> 2.0, >= 2.0.2) - arel (7.1.4) - aruba (0.14.2) - childprocess (~> 0.5.6) - contracts (~> 0.9) - cucumber (>= 1.3.19) - ffi (~> 1.9.10) - rspec-expectations (>= 2.99) - thor (~> 0.19) - bcrypt (3.1.11) - better_errors (2.1.1) - coderay (>= 1.0.0) - erubis (>= 2.6.6) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + logger (>= 1.4.2) + minitest (>= 5.1) + mutex_m + securerandom (>= 0.3) + tzinfo (~> 2.0) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + aruba (2.3.1) + bundler (>= 1.17, < 3.0) + contracts (>= 0.16.0, < 0.18.0) + cucumber (>= 8.0, < 11.0) + rspec-expectations (~> 3.4) + thor (~> 1.0) + ast (2.4.3) + base64 (0.3.0) + bcrypt (3.1.20) + benchmark (0.4.1) + better_errors (2.10.1) + erubi (>= 1.0.0) rack (>= 0.9.0) - binding_of_caller (0.7.2) - debug_inspector (>= 0.0.1) - brakeman (3.4.1) - builder (3.2.3) - bundler-audit (0.5.0) - bundler (~> 1.2) - thor (~> 0.18) - capybara (2.11.0) + rouge (>= 1.0.0) + bigdecimal (3.2.2) + binding_of_caller (1.0.1) + debug_inspector (>= 1.2.0) + builder (3.3.0) + bundler-audit (0.9.2) + bundler (>= 1.2.0, < 3) + thor (~> 1.0) + capybara (3.40.0) addressable - mime-types (>= 1.16) - nokogiri (>= 1.3.3) - rack (>= 1.0.0) - rack-test (>= 0.5.4) - xpath (~> 2.0) - childprocess (0.5.9) - ffi (~> 1.0, >= 1.0.11) + matrix + mini_mime (>= 0.1.3) + nokogiri (~> 1.11) + rack (>= 1.6.0) + rack-test (>= 0.6.3) + regexp_parser (>= 1.5, < 3.0) + xpath (~> 3.2) + childprocess (5.1.0) + logger (~> 1.5) cliver (0.3.2) - coderay (1.1.1) - coffee-rails (4.2.1) + coderay (1.1.3) + coffee-rails (5.0.0) coffee-script (>= 2.2.0) - railties (>= 4.0.0, < 5.2.x) + railties (>= 5.2.0) coffee-script (2.4.1) coffee-script-source execjs coffee-script-source (1.12.2) - concurrent-ruby (1.0.4) - contracts (0.14.0) - crack (0.3.1) - cucumber (2.4.0) - builder (>= 2.1.2) - cucumber-core (~> 1.5.0) - cucumber-wire (~> 0.0.1) - diff-lcs (>= 1.1.3) - gherkin (~> 4.0) - multi_json (>= 1.7.5, < 2.0) - multi_test (>= 0.1.2) - cucumber-core (1.5.0) - gherkin (~> 4.0) - cucumber-wire (0.0.1) - database_cleaner (1.5.3) - debug_inspector (0.0.2) - diff-lcs (1.2.5) - docile (1.1.5) - em-websocket (0.5.1) + concurrent-ruby (1.3.5) + connection_pool (2.5.3) + contracts (0.17.2) + crass (1.0.6) + cucumber (10.0.0) + base64 (~> 0.2) + builder (~> 3.2) + cucumber-ci-environment (> 9, < 11) + cucumber-core (> 15, < 17) + cucumber-cucumber-expressions (> 17, < 19) + cucumber-html-formatter (> 20.3, < 22) + diff-lcs (~> 1.5) + logger (~> 1.6) + mini_mime (~> 1.1) + multi_test (~> 1.1) + sys-uname (~> 1.3) + cucumber-ci-environment (10.0.1) + cucumber-core (15.1.0) + cucumber-gherkin (> 27, < 31) + cucumber-messages (> 26, < 29) + cucumber-tag-expressions (> 5, < 7) + cucumber-cucumber-expressions (18.0.1) + bigdecimal + cucumber-gherkin (30.0.4) + cucumber-messages (> 25, < 28) + cucumber-html-formatter (21.13.0) + cucumber-messages (> 19, < 28) + cucumber-messages (27.2.0) + cucumber-tag-expressions (6.1.2) + database_cleaner (2.1.0) + database_cleaner-active_record (>= 2, < 3) + database_cleaner-active_record (2.2.1) + activerecord (>= 5.a) + database_cleaner-core (~> 2.0.0) + database_cleaner-core (2.0.1) + date (3.4.1) + debug_inspector (1.2.0) + diff-lcs (1.6.2) + docile (1.4.1) + drb (2.2.3) + em-websocket (0.5.3) eventmachine (>= 0.12.9) - http_parser.rb (~> 0.6.0) - erubis (2.7.0) - eventmachine (1.2.1) - execjs (2.7.0) - ffi (1.9.14) - foreman (0.82.0) - thor (~> 0.19.1) - formatador (0.2.5) - gherkin (4.0.0) - globalid (0.3.7) - activesupport (>= 4.1.0) - guard (2.14.0) + http_parser.rb (~> 0) + erb (5.0.2) + erubi (1.13.1) + eventmachine (1.2.7) + execjs (2.10.0) + ffi (1.17.2-aarch64-linux-gnu) + ffi (1.17.2-aarch64-linux-musl) + ffi (1.17.2-arm-linux-gnu) + ffi (1.17.2-arm-linux-musl) + ffi (1.17.2-arm64-darwin) + ffi (1.17.2-x86_64-darwin) + ffi (1.17.2-x86_64-linux-gnu) + ffi (1.17.2-x86_64-linux-musl) + foreman (0.88.1) + formatador (1.1.1) + globalid (1.2.1) + activesupport (>= 6.1) + guard (2.19.1) formatador (>= 0.2.4) listen (>= 2.7, < 4.0) - lumberjack (~> 1.0) + logger (~> 1.6) + lumberjack (>= 1.0.12, < 2.0) nenv (~> 0.1) notiffany (~> 0.0) - pry (>= 0.9.12) + ostruct (~> 0.6) + pry (>= 0.13.0) shellany (~> 0.0) thor (>= 0.18.1) - guard-brakeman (0.8.3) - brakeman (>= 2.1.1) - guard (>= 2.0.0) guard-compat (1.2.1) guard-livereload (2.5.2) em-websocket (~> 0.5) @@ -131,215 +201,351 @@ GEM guard (~> 2.1) guard-compat (~> 1.1) rspec (>= 2.99.0, < 4.0) - guard-shell (0.7.1) + guard-shell (0.7.2) guard (>= 2.0.0) guard-compat (~> 1.0) - http_parser.rb (0.6.0) - i18n (0.7.0) - jquery-fileupload-rails (0.4.7) + http_parser.rb (0.8.0) + i18n (1.14.7) + concurrent-ruby (~> 1.0) + io-console (0.8.1) + irb (1.15.2) + pp (>= 0.6.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + jquery-fileupload-rails (1.0.0) actionpack (>= 3.1) railties (>= 3.1) - sass (>= 3.2) - jquery-rails (4.2.1) + sassc + jquery-rails (4.6.0) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) - json (2.0.3) - kgio (2.11.0) - launchy (2.4.3) - addressable (~> 2.3) - libv8 (3.16.14.17) - listen (3.1.5) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - ruby_dep (~> 1.2) - loofah (2.0.3) - nokogiri (>= 1.5.9) - lumberjack (1.0.10) - mail (2.6.4) - mime-types (>= 1.16, < 4) - method_source (0.8.2) - mime-types (3.1) - mime-types-data (~> 3.2015) - mime-types-data (3.2016.0521) - mini_portile2 (2.1.0) - minitest (5.10.1) - multi_json (1.12.1) - multi_test (0.1.2) - mysql2 (0.4.5) + json (2.13.0) + kgio (2.11.4) + language_server-protocol (3.17.0.5) + launchy (3.1.1) + addressable (~> 2.8) + childprocess (~> 5.0) + logger (~> 1.6) + libv8-node (24.1.0.0) + libv8-node (24.1.0.0-aarch64-linux) + libv8-node (24.1.0.0-arm64-darwin) + libv8-node (24.1.0.0-x86_64-darwin) + libv8-node (24.1.0.0-x86_64-linux) + libv8-node (24.1.0.0-x86_64-linux-musl) + lint_roller (1.1.0) + listen (3.9.0) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + logger (1.7.0) + loofah (2.24.1) + crass (~> 1.0.2) + nokogiri (>= 1.12.0) + lumberjack (1.3.1) + mail (2.8.1) + mini_mime (>= 0.1.1) + net-imap + net-pop + net-smtp + marcel (1.0.4) + matrix (0.4.3) + method_source (1.1.0) + mini_mime (1.1.5) + mini_racer (0.19.0) + libv8-node (~> 24.1.0.0) + minitest (5.25.5) + multi_json (1.17.0) + multi_test (1.1.0) + mutex_m (0.3.0) nenv (0.3.0) - nio4r (1.2.1) - nokogiri (1.7.0.1) - mini_portile2 (~> 2.1.0) - notiffany (0.1.1) + net-imap (0.5.9) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.2) + timeout + net-smtp (0.5.1) + net-protocol + nio4r (2.7.4) + nokogiri (1.18.9-aarch64-linux-gnu) + racc (~> 1.4) + nokogiri (1.18.9-aarch64-linux-musl) + racc (~> 1.4) + nokogiri (1.18.9-arm-linux-gnu) + racc (~> 1.4) + nokogiri (1.18.9-arm-linux-musl) + racc (~> 1.4) + nokogiri (1.18.9-arm64-darwin) + racc (~> 1.4) + nokogiri (1.18.9-x86_64-darwin) + racc (~> 1.4) + nokogiri (1.18.9-x86_64-linux-gnu) + racc (~> 1.4) + nokogiri (1.18.9-x86_64-linux-musl) + racc (~> 1.4) + notiffany (0.1.3) nenv (~> 0.1) shellany (~> 0.0) - poltergeist (1.12.0) - capybara (~> 2.1) + ostruct (0.6.3) + parallel (1.27.0) + parser (3.3.8.0) + ast (~> 2.4.1) + racc + pg (1.5.9) + poltergeist (1.18.1) + capybara (>= 2.1, < 4) cliver (~> 0.3.1) websocket-driver (>= 0.2.0) - powder (0.3.0) + powder (0.4.0) thor (>= 0.11.5) - power_assert (0.4.1) - pry (0.10.4) - coderay (~> 1.1.0) - method_source (~> 0.8.1) - slop (~> 3.4) - pry-rails (0.3.4) - pry (>= 0.9.10) - public_suffix (2.0.4) - rack (2.0.1) - rack-livereload (0.3.16) - rack - rack-test (0.6.3) - rack (>= 1.0) - rails (5.0.1) - actioncable (= 5.0.1) - actionmailer (= 5.0.1) - actionpack (= 5.0.1) - actionview (= 5.0.1) - activejob (= 5.0.1) - activemodel (= 5.0.1) - activerecord (= 5.0.1) - activesupport (= 5.0.1) - bundler (>= 1.3.0, < 2.0) - railties (= 5.0.1) - sprockets-rails (>= 2.0.0) - rails-dom-testing (2.0.2) - activesupport (>= 4.2.0, < 6.0) - nokogiri (~> 1.6) - rails-html-sanitizer (1.0.3) - loofah (~> 2.0) - railties (5.0.1) - actionpack (= 5.0.1) - activesupport (= 5.0.1) - method_source - rake (>= 0.8.7) - thor (>= 0.18.1, < 2.0) - raindrops (0.17.0) - rake (12.0.0) - rb-fsevent (0.9.8) - rb-inotify (0.9.7) - ffi (>= 0.5.0) - ref (2.0.0) - responders (2.3.0) - railties (>= 4.2.0, < 5.1) - rspec (3.5.0) - rspec-core (~> 3.5.0) - rspec-expectations (~> 3.5.0) - rspec-mocks (~> 3.5.0) - rspec-core (3.5.4) - rspec-support (~> 3.5.0) - rspec-expectations (3.5.0) + power_assert (2.0.5) + pp (0.6.2) + prettyprint + prettyprint (0.2.0) + prism (1.4.0) + pry (0.15.2) + coderay (~> 1.1) + method_source (~> 1.0) + pry-rails (0.3.11) + pry (>= 0.13.0) + psych (5.2.6) + date + stringio + public_suffix (6.0.2) + puma (6.6.0) + nio4r (~> 2.0) + racc (1.8.1) + rack (3.1.16) + rack-livereload (0.6.1) + rack (>= 3.0, < 3.2) + rack-session (2.1.1) + base64 (>= 0.1.0) + rack (>= 3.0.0) + rack-test (2.2.0) + rack (>= 1.3) + rackup (2.2.1) + rack (>= 3) + rails (7.1.5) + actioncable (= 7.1.5) + actionmailbox (= 7.1.5) + actionmailer (= 7.1.5) + actionpack (= 7.1.5) + actiontext (= 7.1.5) + actionview (= 7.1.5) + activejob (= 7.1.5) + activemodel (= 7.1.5) + activerecord (= 7.1.5) + activestorage (= 7.1.5) + activesupport (= 7.1.5) + bundler (>= 1.15.0) + railties (= 7.1.5) + rails-dom-testing (2.3.0) + activesupport (>= 5.0.0) + minitest + nokogiri (>= 1.6) + rails-html-sanitizer (1.6.2) + loofah (~> 2.21) + nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) + rails-perftest (0.0.7) + railties (7.1.5) + actionpack (= 7.1.5) + activesupport (= 7.1.5) + irb + rackup (>= 1.0.0) + rake (>= 12.2) + thor (~> 1.0, >= 1.2.2) + zeitwerk (~> 2.6) + rainbow (3.1.1) + raindrops (0.20.1) + rake (13.3.0) + rb-fsevent (0.11.2) + rb-inotify (0.11.1) + ffi (~> 1.0) + rdoc (6.14.2) + erb + psych (>= 4.0.0) + regexp_parser (2.10.0) + reline (0.6.2) + io-console (~> 0.5) + responders (3.1.1) + actionpack (>= 5.2) + railties (>= 5.2) + rouge (4.6.0) + rspec (3.13.1) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.5) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.5.0) - rspec-mocks (3.5.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.5) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.5.0) - rspec-rails (3.5.2) - actionpack (>= 3.0) - activesupport (>= 3.0) - railties (>= 3.0) - rspec-core (~> 3.5.0) - rspec-expectations (~> 3.5.0) - rspec-mocks (~> 3.5.0) - rspec-support (~> 3.5.0) - rspec-support (3.5.0) - ruby_dep (1.5.0) - sass (3.4.22) - sass-rails (5.0.6) - railties (>= 4.0.0, < 6) - sass (~> 3.1) - sprockets (>= 2.8, < 4.0) - sprockets-rails (>= 2.0, < 4.0) - tilt (>= 1.1, < 3) + rspec-support (~> 3.13.0) + rspec-rails (4.0.0.beta3) + actionpack (>= 4.2) + activesupport (>= 4.2) + railties (>= 4.2) + rspec-core (~> 3.8) + rspec-expectations (~> 3.8) + rspec-mocks (~> 3.8) + rspec-support (~> 3.8) + rspec-support (3.13.4) + rubocop (1.78.0) + json (~> 2.3) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.45.1, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.46.0) + parser (>= 3.3.7.2) + prism (~> 1.4) + rubocop-github (0.26.0) + rubocop (>= 1.76) + rubocop-performance (>= 1.24) + rubocop-rails (>= 2.23) + rubocop-performance (1.25.0) + lint_roller (~> 1.1) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.38.0, < 2.0) + rubocop-rails (2.32.0) + activesupport (>= 4.2.0) + lint_roller (~> 1.1) + rack (>= 1.1) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.44.0, < 2.0) + ruby-prof (1.7.2) + base64 + ruby-progressbar (1.13.0) + sassc (2.4.0) + ffi (~> 1.9) + sassc-rails (2.1.2) + railties (>= 4.0.0) + sassc (>= 2.0) + sprockets (> 3.0) + sprockets-rails + tilt + securerandom (0.4.1) shellany (0.0.1) - simplecov (0.12.0) - docile (~> 1.1.0) - json (>= 1.8, < 3) - simplecov-html (~> 0.10.0) - simplecov-html (0.10.0) - slop (3.6.0) - sprockets (3.7.1) + simplecov (0.22.0) + docile (~> 1.1) + simplecov-html (~> 0.11) + simplecov_json_formatter (~> 0.1) + simplecov-html (0.13.2) + simplecov_json_formatter (0.1.4) + sprockets (4.2.2) concurrent-ruby (~> 1.0) - rack (> 1, < 3) - sprockets-rails (3.2.0) - actionpack (>= 4.0) - activesupport (>= 4.0) + logger + rack (>= 2.2.4, < 4) + sprockets-rails (3.5.2) + actionpack (>= 6.1) + activesupport (>= 6.1) sprockets (>= 3.0.0) - sqlite3 (1.3.13) - test-unit (3.2.3) + sqlite3 (2.7.3-aarch64-linux-gnu) + sqlite3 (2.7.3-aarch64-linux-musl) + sqlite3 (2.7.3-arm-linux-gnu) + sqlite3 (2.7.3-arm-linux-musl) + sqlite3 (2.7.3-arm64-darwin) + sqlite3 (2.7.3-x86_64-darwin) + sqlite3 (2.7.3-x86_64-linux-gnu) + sqlite3 (2.7.3-x86_64-linux-musl) + stringio (3.1.7) + sys-uname (1.3.1) + ffi (~> 1.1) + test-unit (3.7.0) power_assert - therubyracer (0.12.2) - libv8 (~> 3.16.14.0) - ref - thor (0.19.4) - thread_safe (0.3.5) - tilt (2.0.5) + thor (1.4.0) + tilt (2.6.1) + timeout (0.4.3) travis-lint (2.0.0) json - turbolinks (5.0.1) - turbolinks-source (~> 5) - turbolinks-source (5.0.0) - tzinfo (1.2.2) - thread_safe (~> 0.1) - uglifier (3.0.4) + turbolinks (5.2.1) + turbolinks-source (~> 5.2) + turbolinks-source (5.2.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + uglifier (4.2.1) execjs (>= 0.3.0, < 3) - unicorn (5.2.0) + unicode-display_width (3.1.4) + unicode-emoji (~> 4.0, >= 4.0.4) + unicode-emoji (4.0.4) + unicorn (6.1.0) kgio (~> 2.6) raindrops (~> 0.7) - websocket-driver (0.6.4) + websocket-driver (0.8.0) + base64 websocket-extensions (>= 0.1.0) - websocket-extensions (0.1.2) - xpath (2.0.0) - nokogiri (~> 1.3) + websocket-extensions (0.1.5) + xpath (3.2.0) + nokogiri (~> 1.8) + zeitwerk (2.7.3) PLATFORMS - ruby + aarch64-linux-gnu + aarch64-linux-musl + arm-linux + arm-linux-gnu + arm-linux-musl + arm64-darwin + armv7l-linux + universal-darwin + x86_64-darwin + x86_64-linux + x86_64-linux-gnu + x86_64-linux-musl DEPENDENCIES aruba bcrypt better_errors binding_of_caller - brakeman bundler-audit capybara coffee-rails - crack (= 0.3.1) database_cleaner execjs foreman - guard-brakeman guard-livereload guard-rspec guard-shell jquery-fileupload-rails jquery-rails launchy - mysql2 + mini_racer + minitest + pg poltergeist powder pry pry-rails + puma rack-livereload - rails (= 5.0.1) + rails (= 7.1.5) + rails-perftest rake rb-fsevent responders - rspec-rails - sass-rails + rspec-rails (= 4.0.0.beta3) + rubocop-github + ruby-prof + sassc-rails simplecov sqlite3 test-unit - therubyracer travis-lint turbolinks uglifier unicorn RUBY VERSION - ruby 2.3.3p222 + ruby 3.3.8p144 BUNDLED WITH - 1.13.7 + 2.7.1 diff --git a/Guardfile b/Guardfile index af80ae309..18b70eb99 100755 --- a/Guardfile +++ b/Guardfile @@ -1,18 +1,19 @@ +# frozen_string_literal: true # A sample Guardfile # More info at https://github.com/guard/guard#readme -guard 'brakeman', :run_on_start => true do +guard "brakeman", run_on_start: true do watch(%r{^app/.+\.(erb|haml|rhtml|rb)$}) watch(%r{^config/.+\.rb$}) watch(%r{^lib/.+\.rb$}) - watch('Gemfile') + watch("Gemfile") end guard :shell do - watch(%r{^Gemfile|Gemfile.lock$}) { system("bundle-audit")} + watch(%r{^Gemfile|Gemfile.lock$}) { system("bundle-audit") } end -guard 'livereload', host: "railsgoat.dev", port: '35727' do +guard "livereload", host: "railsgoat.dev", port: "35727" do watch(%r{app/views/.+\.(erb|haml|slim)$}) watch(%r{app/helpers/.+\.rb}) watch(%r{public/.+\.(css|js|html)}) @@ -22,24 +23,23 @@ guard 'livereload', host: "railsgoat.dev", port: '35727' do end -guard 'rspec' do +guard :rspec, cmd: "bundle exec rspec" do watch(%r{^spec/.+_spec\.rb$}) watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } - watch('spec/spec_helper.rb') { "spec" } + watch("spec/spec_helper.rb") { "spec" } # Rails example watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" } watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] } watch(%r{^spec/support/(.+)\.rb$}) { "spec" } - watch('config/routes.rb') { "spec/routing" } - watch('app/controllers/application_controller.rb') { "spec/controllers" } + watch("config/routes.rb") { "spec/routing" } + watch("app/controllers/application_controller.rb") { "spec/controllers" } # Capybara features specs watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/features/#{m[1]}_spec.rb" } # Turnip features and steps watch(%r{^spec/acceptance/(.+)\.feature$}) - watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' } + watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance" } end - diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..d0c95536c --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,20 @@ + + +🐞 **Problem** + + + +🎯 **Goal** + + + +💡 **Possible solutions** + + +📋 **Steps to solve the problem** + + * Comment below about what you've started working on. + * Add, commit, push your changes + * Submit a pull request and add this in comments - `Addresses #` + * Ask for a review in comments section of pull request + * Celebrate your contribution to this project 🎉 diff --git a/LICENSE.md b/LICENSE.md index b47c76535..c27e4b5cc 100755 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013-2014 The Open Web Application Security Project +Copyright (c) 2013-2018 The Open Web Application Security Project Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file +THE SOFTWARE. diff --git a/README.md b/README.md index f8b7754ed..9f93c8af7 100755 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # RailsGoat [![Build Status](https://api.travis-ci.org/OWASP/railsgoat.png?branch=master)](https://travis-ci.org/OWASP/railsgoat) [![Code Climate](https://codeclimate.com/github/OWASP/railsgoat.png)](https://codeclimate.com/github/OWASP/railsgoat) -RailsGoat is a vulnerable version of the Ruby on Rails Framework from versions 3 to 5. It includes vulnerabilities from the OWASP Top 10, as well as some "extras" that the initial project contributors felt worthwhile to share. This project is designed to educate both developers, as well as security professionals. +RailsGoat is a vulnerable version of the Ruby on Rails Framework from versions 3 to 6. It includes vulnerabilities from the OWASP Top 10, as well as some "extras" that the initial project contributors felt worthwhile to share. This project is designed to educate both developers, as well as security professionals. ## Support @@ -8,19 +8,17 @@ If you are looking for support or troubleshooting assistance, please visit our [ ## Getting Started -To begin, install the Ruby Version Manager (RVM): +To begin, if you do not have Ruby, Git, MySQL, and Postgres, we suggest +using this [site](https://gorails.com/setup) to install the software. +Pick the appropriate operating system and follow the instructions. -```bash -$ curl -L https://get.rvm.io | bash -s stable --autolibs=3 --ruby=2.3.3 -``` - -After installing the package, clone this repo: +After installing the above software, clone this repo: ```bash $ git clone git@github.com:OWASP/railsgoat.git ``` -**NOTE: NOT NECESSARY IF YOU WANT TO WORK WITH RAILS 5.** Otherwise, if you wish to use the Rails 3 or 4 versions, you'll need to switch branches: +**NOTE: NOT NECESSARY IF YOU WANT TO WORK WITH RAILS 6.** Otherwise, if you wish to use the Rails 3 or 4 versions, you'll need to switch branches: ```bash $ cd railsgoat @@ -54,28 +52,11 @@ $ rails server Open your favorite browser, navigate to `http://localhost:3000` and start hacking! -## Vagrant Install - -To run Railsgoat with Vagrant you must first have [Vagrant](https://www.vagrantup.com/) and [Virtualbox](https://www.virtualbox.org/) installed. Once those dependencies are installed cd into the Railsgoat directory where you've cloned the code and run. - -``` -#~/code/railsgoat -$ vagrant up -... - railsgoat: Port: 3000:3000 - railsgoat: - railsgoat: Container created: 3084633a81675346 -==> railsgoat: Starting container... -==> railsgoat: Provisioners will not be run since container doesn't support SSH. -$ -``` -Once you see the preceeding message Railsgoat is running on your localhost on port 3000. - -Open your favorite browser, navigate to `http://localhost:3000` and start hacking! - ## Docker Install To run Railsgoat with Docker you must first have [Docker](https://docs.docker.com/engine/installation/) and [Docker Compose](https://docs.docker.com/compose/install/) installed. Once those dependencies are installed, cd into the Railsgoat directory where you've cloned the code and run. Rails requires Compose **1.6.0** or above and require a Docker Engine of version **1.10.0** or above. +For Mac Apple Silicon (ARM64) you must also have Rosetta install + ``` #~/code/railsgoat $ docker-compose build @@ -94,7 +75,7 @@ Note: if your container exits with an error, it may be because a server is alrea ``` A server is already running. Check /myapp/tmp/pids/server.pid. => Booting Thin -=> Rails 5.0.1 application starting in development on +=> Rails 6.0.0 application starting in development on http://0.0.0.0:3000 => Run `rails server -h` for more startup options => Ctrl-C to shutdown server @@ -116,9 +97,6 @@ To run just one spec: $ rails training SPEC=spec/vulnerabilities/sql_injection_spec.rb ``` -NOTE: As vulnerabilities are fixed in the application, these specs will not change to `passing`, but to `pending`. - - ## MySQL Environment By default in development mode Railsgoat runs with a SQLite database. There is an environment defined to use MySQL. For some of the SQL injection vulnerabilities to work you have to run the app with MySQL as the database. The following steps will setup and run Railsgoat to use MySQL. *MySQL must be installed and running before running these steps* @@ -158,18 +136,10 @@ Alternatively, you can run MailCatcher in the foreground by running `mailcatcher ## Contributing -As changes are made to the application, the Capybara RSpecs can be used to verify that the vulnerabilities in the application are still intact. To use them in this way, and have them `pass` instead of `fail`, set the `RAILSGOAT_MAINTAINER` environment variable. +Please see our [contribution document](./CONTRIBUTING.md) to learn more. Additionally, note that as changes are made to the application, the Capybara RSpecs can be used to verify that the vulnerabilities in the application are still intact. To use them in this way, and have them change to `pending` instead of `fail`, set the `RAILSGOAT_MAINTAINER` environment variable. Conversion to the OWASP Top Ten 2013 completed in November, 2013. # License -The MIT License (MIT) - -Copyright (c) 2013-2014 The Open Web Application Security Project - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +[The MIT License (MIT)](./LICENSE.md) diff --git a/Rakefile b/Rakefile index e85f91391..4237d413c 100755 --- a/Rakefile +++ b/Rakefile @@ -1,6 +1,7 @@ +# frozen_string_literal: true # Add your own tasks in files placed in lib/tasks ending in .rake, # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. -require_relative 'config/application' +require_relative "config/application" Rails.application.load_tasks diff --git a/Vagrantfile b/Vagrantfile deleted file mode 100644 index 37788ef9f..000000000 --- a/Vagrantfile +++ /dev/null @@ -1,13 +0,0 @@ -VAGRANTFILE_API_VERSION = "2" -ENV['VAGRANT_DEFAULT_PROVIDER'] ||= 'docker' -Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| - config.vm.define "railsgoat" do |rg| - rg.vm.provider "docker" do |d| - d.image = "owasp/railsgoat" - d.name = "railsgoat" - d.ports = ["3000:3000"] - d.vagrant_vagrantfile = "./Vagrantfile.proxy" - end - end - -end diff --git a/Vagrantfile.proxy b/Vagrantfile.proxy deleted file mode 100644 index ac72dfc01..000000000 --- a/Vagrantfile.proxy +++ /dev/null @@ -1,10 +0,0 @@ -VAGRANTFILE_API_VERSION = "2" - -Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| - config.vm.box = "hashicorp/precise64" - config.vm.provision "docker" - config.vm.provision "shell", inline: - "ps aux | grep 'sshd:' | awk '{print $2}' | xargs kill" - - config.vm.network :forwarded_port, guest: 3000, host: 3000 -end diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js new file mode 100644 index 000000000..b16e53d6d --- /dev/null +++ b/app/assets/config/manifest.js @@ -0,0 +1,3 @@ +//= link_tree ../images +//= link_directory ../javascripts .js +//= link_directory ../stylesheets .css diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index aa51d0732..fd0a47777 100755 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -13,19 +13,16 @@ //= require jquery //= require jquery_ujs //= require turbolinks -//= require wysiwyg/wysihtml5-0.3.0.js //= require jquery.min.js //= require jquery.scrollUp.js //= require bootstrap.js -//= require wysiwyg/bootstrap-wysihtml5.js //= require bootstrap-colorpicker.js //= require date-picker/date.js //= require date-picker/daterangepicker.js //= require bootstrap-timepicker.js //= require jquery.bootstrap.wizard.js -//= require jquery.sparkline.js //= require tiny-scrollbar.js -//= require jquery.validate.js +//= require jquery.validate.min.js //= require jquery.snippet.js //= require jquery.easy-pie-chart.js //= require jquery-fileupload/basic diff --git a/app/assets/javascripts/bootstrap-timepicker.js b/app/assets/javascripts/bootstrap-timepicker.js index 9d984e044..789473d34 100755 --- a/app/assets/javascripts/bootstrap-timepicker.js +++ b/app/assets/javascripts/bootstrap-timepicker.js @@ -745,14 +745,14 @@ case 'modal': template = ''; diff --git a/app/assets/javascripts/fullcalendar.js b/app/assets/javascripts/fullcalendar.js deleted file mode 100755 index 0d43e5866..000000000 --- a/app/assets/javascripts/fullcalendar.js +++ /dev/null @@ -1,5378 +0,0 @@ -/*! - * FullCalendar v1.6.1 - * Docs & License: http://arshaw.com/fullcalendar/ - * (c) 2013 Adam Shaw - */ - -/* - * Use fullcalendar.css for basic styling. - * For event drag & drop, requires jQuery UI draggable. - * For event resizing, requires jQuery UI resizable. - */ - -(function($, undefined) { - - -;; - -var defaults = { - - // display - defaultView: 'month', - aspectRatio: 1.35, - header: { - left: 'title', - center: '', - right: 'today prev,next' - }, - weekends: true, - weekNumbers: false, - weekNumberCalculation: 'iso', - weekNumberTitle: 'W', - - // editing - //editable: false, - //disableDragging: false, - //disableResizing: false, - - allDayDefault: true, - ignoreTimezone: true, - - // event ajax - lazyFetching: true, - startParam: 'start', - endParam: 'end', - - // time formats - titleFormat: { - month: 'MMMM yyyy', - week: "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", - day: 'dddd, MMM d, yyyy' - }, - columnFormat: { - month: 'ddd', - week: 'ddd M/d', - day: 'dddd M/d' - }, - timeFormat: { // for event elements - '': 'h(:mm)t' // default - }, - - // locale - isRTL: false, - firstDay: 0, - monthNames: ['January','February','March','April','May','June','July','August','September','October','November','December'], - monthNamesShort: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'], - dayNames: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'], - dayNamesShort: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'], - buttonText: { - prev: "", - next: "", - prevYear: "«", - nextYear: "»", - today: 'today', - month: 'month', - week: 'week', - day: 'day' - }, - - // jquery-ui theming - theme: false, - buttonIcons: { - prev: 'circle-triangle-w', - next: 'circle-triangle-e' - }, - - //selectable: false, - unselectAuto: true, - - dropAccept: '*' - -}; - -// right-to-left defaults -var rtlDefaults = { - header: { - left: 'next,prev today', - center: '', - right: 'title' - }, - buttonText: { - prev: "", - next: "", - prevYear: "»", - nextYear: "«" - }, - buttonIcons: { - prev: 'circle-triangle-e', - next: 'circle-triangle-w' - } -}; - - - -;; - -var fc = $.fullCalendar = { version: "1.6.1" }; -var fcViews = fc.views = {}; - - -$.fn.fullCalendar = function(options) { - - - // method calling - if (typeof options == 'string') { - var args = Array.prototype.slice.call(arguments, 1); - var res; - this.each(function() { - var calendar = $.data(this, 'fullCalendar'); - if (calendar && $.isFunction(calendar[options])) { - var r = calendar[options].apply(calendar, args); - if (res === undefined) { - res = r; - } - if (options == 'destroy') { - $.removeData(this, 'fullCalendar'); - } - } - }); - if (res !== undefined) { - return res; - } - return this; - } - - - // would like to have this logic in EventManager, but needs to happen before options are recursively extended - var eventSources = options.eventSources || []; - delete options.eventSources; - if (options.events) { - eventSources.push(options.events); - delete options.events; - } - - - options = $.extend(true, {}, - defaults, - (options.isRTL || options.isRTL===undefined && defaults.isRTL) ? rtlDefaults : {}, - options - ); - - - this.each(function(i, _element) { - var element = $(_element); - var calendar = new Calendar(element, options, eventSources); - element.data('fullCalendar', calendar); // TODO: look into memory leak implications - calendar.render(); - }); - - - return this; - -}; - - -// function for adding/overriding defaults -function setDefaults(d) { - $.extend(true, defaults, d); -} - - - -;; - - -function Calendar(element, options, eventSources) { - var t = this; - - - // exports - t.options = options; - t.render = render; - t.destroy = destroy; - t.refetchEvents = refetchEvents; - t.reportEvents = reportEvents; - t.reportEventChange = reportEventChange; - t.rerenderEvents = rerenderEvents; - t.changeView = changeView; - t.select = select; - t.unselect = unselect; - t.prev = prev; - t.next = next; - t.prevYear = prevYear; - t.nextYear = nextYear; - t.today = today; - t.gotoDate = gotoDate; - t.incrementDate = incrementDate; - t.formatDate = function(format, date) { return formatDate(format, date, options) }; - t.formatDates = function(format, date1, date2) { return formatDates(format, date1, date2, options) }; - t.getDate = getDate; - t.getView = getView; - t.option = option; - t.trigger = trigger; - - - // imports - EventManager.call(t, options, eventSources); - var isFetchNeeded = t.isFetchNeeded; - var fetchEvents = t.fetchEvents; - - - // locals - var _element = element[0]; - var header; - var headerElement; - var content; - var tm; // for making theme classes - var currentView; - var viewInstances = {}; - var elementOuterWidth; - var suggestedViewHeight; - var absoluteViewElement; - var resizeUID = 0; - var ignoreWindowResize = 0; - var date = new Date(); - var events = []; - var _dragElement; - - - - /* Main Rendering - -----------------------------------------------------------------------------*/ - - - setYMD(date, options.year, options.month, options.date); - - - function render(inc) { - if (!content) { - initialRender(); - }else{ - calcSize(); - markSizesDirty(); - markEventsDirty(); - renderView(inc); - } - } - - - function initialRender() { - tm = options.theme ? 'ui' : 'fc'; - element.addClass('fc'); - if (options.isRTL) { - element.addClass('fc-rtl'); - } - else { - element.addClass('fc-ltr'); - } - if (options.theme) { - element.addClass('ui-widget'); - } - content = $("
") - .prependTo(element); - header = new Header(t, options); - headerElement = header.render(); - if (headerElement) { - element.prepend(headerElement); - } - changeView(options.defaultView); - $(window).resize(windowResize); - // needed for IE in a 0x0 iframe, b/c when it is resized, never triggers a windowResize - if (!bodyVisible()) { - lateRender(); - } - } - - - // called when we know the calendar couldn't be rendered when it was initialized, - // but we think it's ready now - function lateRender() { - setTimeout(function() { // IE7 needs this so dimensions are calculated correctly - if (!currentView.start && bodyVisible()) { // !currentView.start makes sure this never happens more than once - renderView(); - } - },0); - } - - - function destroy() { - $(window).unbind('resize', windowResize); - header.destroy(); - content.remove(); - element.removeClass('fc fc-rtl ui-widget'); - } - - - - function elementVisible() { - return _element.offsetWidth !== 0; - } - - - function bodyVisible() { - return $('body')[0].offsetWidth !== 0; - } - - - - /* View Rendering - -----------------------------------------------------------------------------*/ - - // TODO: improve view switching (still weird transition in IE, and FF has whiteout problem) - - function changeView(newViewName) { - if (!currentView || newViewName != currentView.name) { - ignoreWindowResize++; // because setMinHeight might change the height before render (and subsequently setSize) is reached - - unselect(); - - var oldView = currentView; - var newViewElement; - - if (oldView) { - (oldView.beforeHide || noop)(); // called before changing min-height. if called after, scroll state is reset (in Opera) - setMinHeight(content, content.height()); - oldView.element.hide(); - }else{ - setMinHeight(content, 1); // needs to be 1 (not 0) for IE7, or else view dimensions miscalculated - } - content.css('overflow', 'hidden'); - - currentView = viewInstances[newViewName]; - if (currentView) { - currentView.element.show(); - }else{ - currentView = viewInstances[newViewName] = new fcViews[newViewName]( - newViewElement = absoluteViewElement = - $("
") - .appendTo(content), - t // the calendar object - ); - } - - if (oldView) { - header.deactivateButton(oldView.name); - } - header.activateButton(newViewName); - - renderView(); // after height has been set, will make absoluteViewElement's position=relative, then set to null - - content.css('overflow', ''); - if (oldView) { - setMinHeight(content, 1); - } - - if (!newViewElement) { - (currentView.afterShow || noop)(); // called after setting min-height/overflow, so in final scroll state (for Opera) - } - - ignoreWindowResize--; - } - } - - - - function renderView(inc) { - if (elementVisible()) { - ignoreWindowResize++; // because renderEvents might temporarily change the height before setSize is reached - - unselect(); - - if (suggestedViewHeight === undefined) { - calcSize(); - } - - var forceEventRender = false; - if (!currentView.start || inc || date < currentView.start || date >= currentView.end) { - // view must render an entire new date range (and refetch/render events) - currentView.render(date, inc || 0); // responsible for clearing events - setSize(true); - forceEventRender = true; - } - else if (currentView.sizeDirty) { - // view must resize (and rerender events) - currentView.clearEvents(); - setSize(); - forceEventRender = true; - } - else if (currentView.eventsDirty) { - currentView.clearEvents(); - forceEventRender = true; - } - currentView.sizeDirty = false; - currentView.eventsDirty = false; - updateEvents(forceEventRender); - - elementOuterWidth = element.outerWidth(); - - header.updateTitle(currentView.title); - var today = new Date(); - if (today >= currentView.start && today < currentView.end) { - header.disableButton('today'); - }else{ - header.enableButton('today'); - } - - ignoreWindowResize--; - currentView.trigger('viewDisplay', _element); - } - } - - - - /* Resizing - -----------------------------------------------------------------------------*/ - - - function updateSize() { - markSizesDirty(); - if (elementVisible()) { - calcSize(); - setSize(); - unselect(); - currentView.clearEvents(); - currentView.renderEvents(events); - currentView.sizeDirty = false; - } - } - - - function markSizesDirty() { - $.each(viewInstances, function(i, inst) { - inst.sizeDirty = true; - }); - } - - - function calcSize() { - if (options.contentHeight) { - suggestedViewHeight = options.contentHeight; - } - else if (options.height) { - suggestedViewHeight = options.height - (headerElement ? headerElement.height() : 0) - vsides(content); - } - else { - suggestedViewHeight = Math.round(content.width() / Math.max(options.aspectRatio, .5)); - } - } - - - function setSize(dateChanged) { // todo: dateChanged? - ignoreWindowResize++; - currentView.setHeight(suggestedViewHeight, dateChanged); - if (absoluteViewElement) { - absoluteViewElement.css('position', 'relative'); - absoluteViewElement = null; - } - currentView.setWidth(content.width(), dateChanged); - ignoreWindowResize--; - } - - - function windowResize() { - if (!ignoreWindowResize) { - if (currentView.start) { // view has already been rendered - var uid = ++resizeUID; - setTimeout(function() { // add a delay - if (uid == resizeUID && !ignoreWindowResize && elementVisible()) { - if (elementOuterWidth != (elementOuterWidth = element.outerWidth())) { - ignoreWindowResize++; // in case the windowResize callback changes the height - updateSize(); - currentView.trigger('windowResize', _element); - ignoreWindowResize--; - } - } - }, 200); - }else{ - // calendar must have been initialized in a 0x0 iframe that has just been resized - lateRender(); - } - } - } - - - - /* Event Fetching/Rendering - -----------------------------------------------------------------------------*/ - - - // fetches events if necessary, rerenders events if necessary (or if forced) - function updateEvents(forceRender) { - if (!options.lazyFetching || isFetchNeeded(currentView.visStart, currentView.visEnd)) { - refetchEvents(); - } - else if (forceRender) { - rerenderEvents(); - } - } - - - function refetchEvents() { - fetchEvents(currentView.visStart, currentView.visEnd); // will call reportEvents - } - - - // called when event data arrives - function reportEvents(_events) { - events = _events; - rerenderEvents(); - } - - - // called when a single event's data has been changed - function reportEventChange(eventID) { - rerenderEvents(eventID); - } - - - // attempts to rerenderEvents - function rerenderEvents(modifiedEventID) { - markEventsDirty(); - if (elementVisible()) { - currentView.clearEvents(); - currentView.renderEvents(events, modifiedEventID); - currentView.eventsDirty = false; - } - } - - - function markEventsDirty() { - $.each(viewInstances, function(i, inst) { - inst.eventsDirty = true; - }); - } - - - - /* Selection - -----------------------------------------------------------------------------*/ - - - function select(start, end, allDay) { - currentView.select(start, end, allDay===undefined ? true : allDay); - } - - - function unselect() { // safe to be called before renderView - if (currentView) { - currentView.unselect(); - } - } - - - - /* Date - -----------------------------------------------------------------------------*/ - - - function prev() { - renderView(-1); - } - - - function next() { - renderView(1); - } - - - function prevYear() { - addYears(date, -1); - renderView(); - } - - - function nextYear() { - addYears(date, 1); - renderView(); - } - - - function today() { - date = new Date(); - renderView(); - } - - - function gotoDate(year, month, dateOfMonth) { - if (year instanceof Date) { - date = cloneDate(year); // provided 1 argument, a Date - }else{ - setYMD(date, year, month, dateOfMonth); - } - renderView(); - } - - - function incrementDate(years, months, days) { - if (years !== undefined) { - addYears(date, years); - } - if (months !== undefined) { - addMonths(date, months); - } - if (days !== undefined) { - addDays(date, days); - } - renderView(); - } - - - function getDate() { - return cloneDate(date); - } - - - - /* Misc - -----------------------------------------------------------------------------*/ - - - function getView() { - return currentView; - } - - - function option(name, value) { - if (value === undefined) { - return options[name]; - } - if (name == 'height' || name == 'contentHeight' || name == 'aspectRatio') { - options[name] = value; - updateSize(); - } - } - - - function trigger(name, thisObj) { - if (options[name]) { - return options[name].apply( - thisObj || _element, - Array.prototype.slice.call(arguments, 2) - ); - } - } - - - - /* External Dragging - ------------------------------------------------------------------------*/ - - if (options.droppable) { - $(document) - .bind('dragstart', function(ev, ui) { - var _e = ev.target; - var e = $(_e); - if (!e.parents('.fc').length) { // not already inside a calendar - var accept = options.dropAccept; - if ($.isFunction(accept) ? accept.call(_e, e) : e.is(accept)) { - _dragElement = _e; - currentView.dragStart(_dragElement, ev, ui); - } - } - }) - .bind('dragstop', function(ev, ui) { - if (_dragElement) { - currentView.dragStop(_dragElement, ev, ui); - _dragElement = null; - } - }); - } - - -} - -;; - -function Header(calendar, options) { - var t = this; - - - // exports - t.render = render; - t.destroy = destroy; - t.updateTitle = updateTitle; - t.activateButton = activateButton; - t.deactivateButton = deactivateButton; - t.disableButton = disableButton; - t.enableButton = enableButton; - - - // locals - var element = $([]); - var tm; - - - - function render() { - tm = options.theme ? 'ui' : 'fc'; - var sections = options.header; - if (sections) { - element = $("") - .append( - $("") - .append(renderSection('left')) - .append(renderSection('center')) - .append(renderSection('right')) - ); - return element; - } - } - - - function destroy() { - element.remove(); - } - - - function renderSection(position) { - var e = $("" + - ""; - - if (showWeekNumbers) { - s += ""; - } - - for (i=0; i"; // fc- needed for setDayID - } - s += - "" + - "" + - "" + - "" + - "" + - ""; - for (i=0; i" + // fc- needed for setDayID - "
" + - "
" + - "
 
" + - "
" + - "
" + - ""; - } - s += - "
" + - "" + - "" + - "
"); - var buttonStr = options.header[position]; - if (buttonStr) { - $.each(buttonStr.split(' '), function(i) { - if (i > 0) { - e.append(""); - } - var prevButton; - $.each(this.split(','), function(j, buttonName) { - if (buttonName == 'title') { - e.append("

 

"); - if (prevButton) { - prevButton.addClass(tm + '-corner-right'); - } - prevButton = null; - }else{ - var buttonClick; - if (calendar[buttonName]) { - buttonClick = calendar[buttonName]; // calendar method - } - else if (fcViews[buttonName]) { - buttonClick = function() { - button.removeClass(tm + '-state-hover'); // forget why - calendar.changeView(buttonName); - }; - } - if (buttonClick) { - var icon = options.theme ? smartProperty(options.buttonIcons, buttonName) : null; // why are we using smartProperty here? - var text = smartProperty(options.buttonText, buttonName); // why are we using smartProperty here? - var button = $( - "" + - (icon ? - "" + - "" + - "" : - text - ) + - "" - ) - .click(function() { - if (!button.hasClass(tm + '-state-disabled')) { - buttonClick(); - } - }) - .mousedown(function() { - button - .not('.' + tm + '-state-active') - .not('.' + tm + '-state-disabled') - .addClass(tm + '-state-down'); - }) - .mouseup(function() { - button.removeClass(tm + '-state-down'); - }) - .hover( - function() { - button - .not('.' + tm + '-state-active') - .not('.' + tm + '-state-disabled') - .addClass(tm + '-state-hover'); - }, - function() { - button - .removeClass(tm + '-state-hover') - .removeClass(tm + '-state-down'); - } - ) - .appendTo(e); - disableTextSelection(button); - if (!prevButton) { - button.addClass(tm + '-corner-left'); - } - prevButton = button; - } - } - }); - if (prevButton) { - prevButton.addClass(tm + '-corner-right'); - } - }); - } - return e; - } - - - function updateTitle(html) { - element.find('h2') - .html(html); - } - - - function activateButton(buttonName) { - element.find('span.fc-button-' + buttonName) - .addClass(tm + '-state-active'); - } - - - function deactivateButton(buttonName) { - element.find('span.fc-button-' + buttonName) - .removeClass(tm + '-state-active'); - } - - - function disableButton(buttonName) { - element.find('span.fc-button-' + buttonName) - .addClass(tm + '-state-disabled'); - } - - - function enableButton(buttonName) { - element.find('span.fc-button-' + buttonName) - .removeClass(tm + '-state-disabled'); - } - - -} - -;; - -fc.sourceNormalizers = []; -fc.sourceFetchers = []; - -var ajaxDefaults = { - dataType: 'json', - cache: false -}; - -var eventGUID = 1; - - -function EventManager(options, _sources) { - var t = this; - - - // exports - t.isFetchNeeded = isFetchNeeded; - t.fetchEvents = fetchEvents; - t.addEventSource = addEventSource; - t.removeEventSource = removeEventSource; - t.updateEvent = updateEvent; - t.renderEvent = renderEvent; - t.removeEvents = removeEvents; - t.clientEvents = clientEvents; - t.normalizeEvent = normalizeEvent; - - - // imports - var trigger = t.trigger; - var getView = t.getView; - var reportEvents = t.reportEvents; - - - // locals - var stickySource = { events: [] }; - var sources = [ stickySource ]; - var rangeStart, rangeEnd; - var currentFetchID = 0; - var pendingSourceCnt = 0; - var loadingLevel = 0; - var cache = []; - - - for (var i=0; i<_sources.length; i++) { - _addEventSource(_sources[i]); - } - - - - /* Fetching - -----------------------------------------------------------------------------*/ - - - function isFetchNeeded(start, end) { - return !rangeStart || start < rangeStart || end > rangeEnd; - } - - - function fetchEvents(start, end) { - rangeStart = start; - rangeEnd = end; - cache = []; - var fetchID = ++currentFetchID; - var len = sources.length; - pendingSourceCnt = len; - for (var i=0; i)), return null instead - return null; -} - - -function parseISO8601(s, ignoreTimezone) { // ignoreTimezone defaults to false - // derived from http://delete.me.uk/2005/03/iso8601.html - // TODO: for a know glitch/feature, read tests/issue_206_parseDate_dst.html - 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}))?))?)?)?)?$/); - if (!m) { - return null; - } - var date = new Date(m[1], 0, 1); - if (ignoreTimezone || !m[13]) { - var check = new Date(m[1], 0, 1, 9, 0); - if (m[3]) { - date.setMonth(m[3] - 1); - check.setMonth(m[3] - 1); - } - if (m[5]) { - date.setDate(m[5]); - check.setDate(m[5]); - } - fixDate(date, check); - if (m[7]) { - date.setHours(m[7]); - } - if (m[8]) { - date.setMinutes(m[8]); - } - if (m[10]) { - date.setSeconds(m[10]); - } - if (m[12]) { - date.setMilliseconds(Number("0." + m[12]) * 1000); - } - fixDate(date, check); - }else{ - date.setUTCFullYear( - m[1], - m[3] ? m[3] - 1 : 0, - m[5] || 1 - ); - date.setUTCHours( - m[7] || 0, - m[8] || 0, - m[10] || 0, - m[12] ? Number("0." + m[12]) * 1000 : 0 - ); - if (m[14]) { - var offset = Number(m[16]) * 60 + (m[18] ? Number(m[18]) : 0); - offset *= m[15] == '-' ? 1 : -1; - date = new Date(+date + (offset * 60 * 1000)); - } - } - return date; -} - - -function parseTime(s) { // returns minutes since start of day - if (typeof s == 'number') { // an hour - return s * 60; - } - if (typeof s == 'object') { // a Date object - return s.getHours() * 60 + s.getMinutes(); - } - var m = s.match(/(\d+)(?::(\d+))?\s*(\w+)?/); - if (m) { - var h = parseInt(m[1], 10); - if (m[3]) { - h %= 12; - if (m[3].toLowerCase().charAt(0) == 'p') { - h += 12; - } - } - return h * 60 + (m[2] ? parseInt(m[2], 10) : 0); - } -} - - - -/* Date Formatting ------------------------------------------------------------------------------*/ -// TODO: use same function formatDate(date, [date2], format, [options]) - - -function formatDate(date, format, options) { - return formatDates(date, null, format, options); -} - - -function formatDates(date1, date2, format, options) { - options = options || defaults; - var date = date1, - otherDate = date2, - i, len = format.length, c, - i2, formatter, - res = ''; - for (i=0; ii; i2--) { - if (formatter = dateFormatters[format.substring(i, i2)]) { - if (date) { - res += formatter(date, options); - } - i = i2 - 1; - break; - } - } - if (i2 == i) { - if (date) { - res += c; - } - } - } - } - return res; -}; - - -var dateFormatters = { - s : function(d) { return d.getSeconds() }, - ss : function(d) { return zeroPad(d.getSeconds()) }, - m : function(d) { return d.getMinutes() }, - mm : function(d) { return zeroPad(d.getMinutes()) }, - h : function(d) { return d.getHours() % 12 || 12 }, - hh : function(d) { return zeroPad(d.getHours() % 12 || 12) }, - H : function(d) { return d.getHours() }, - HH : function(d) { return zeroPad(d.getHours()) }, - d : function(d) { return d.getDate() }, - dd : function(d) { return zeroPad(d.getDate()) }, - ddd : function(d,o) { return o.dayNamesShort[d.getDay()] }, - dddd: function(d,o) { return o.dayNames[d.getDay()] }, - M : function(d) { return d.getMonth() + 1 }, - MM : function(d) { return zeroPad(d.getMonth() + 1) }, - MMM : function(d,o) { return o.monthNamesShort[d.getMonth()] }, - MMMM: function(d,o) { return o.monthNames[d.getMonth()] }, - yy : function(d) { return (d.getFullYear()+'').substring(2) }, - yyyy: function(d) { return d.getFullYear() }, - t : function(d) { return d.getHours() < 12 ? 'a' : 'p' }, - tt : function(d) { return d.getHours() < 12 ? 'am' : 'pm' }, - T : function(d) { return d.getHours() < 12 ? 'A' : 'P' }, - TT : function(d) { return d.getHours() < 12 ? 'AM' : 'PM' }, - u : function(d) { return formatDate(d, "yyyy-MM-dd'T'HH:mm:ss'Z'") }, - S : function(d) { - var date = d.getDate(); - if (date > 10 && date < 20) { - return 'th'; - } - return ['st', 'nd', 'rd'][date%10-1] || 'th'; - }, - w : function(d, o) { // local - return o.weekNumberCalculation(d); - }, - W : function(d) { // ISO - return iso8601Week(d); - } -}; -fc.dateFormatters = dateFormatters; - - -/* thanks jQuery UI (https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.datepicker.js) - * - * Set as calculateWeek to determine the week of the year based on the ISO 8601 definition. - * @param date Date - the date to get the week for - * @return number - the number of the week within the year that contains this date - */ -function iso8601Week(date) { - var time; - var checkDate = new Date(date.getTime()); - - // Find Thursday of this week starting on Monday - checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); - - time = checkDate.getTime(); - checkDate.setMonth(0); // Compare with Jan 1 - checkDate.setDate(1); - return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1; -} - - -;; - -fc.applyAll = applyAll; - - -/* Event Date Math ------------------------------------------------------------------------------*/ - - -function exclEndDay(event) { - if (event.end) { - return _exclEndDay(event.end, event.allDay); - }else{ - return addDays(cloneDate(event.start), 1); - } -} - - -function _exclEndDay(end, allDay) { - end = cloneDate(end); - return allDay || end.getHours() || end.getMinutes() ? addDays(end, 1) : clearTime(end); -} - - -function segCmp(a, b) { - return (b.msLength - a.msLength) * 100 + (a.event.start - b.event.start); -} - - -function segsCollide(seg1, seg2) { - return seg1.end > seg2.start && seg1.start < seg2.end; -} - - - -/* Event Sorting ------------------------------------------------------------------------------*/ - - -// event rendering utilities -function sliceSegs(events, visEventEnds, start, end) { - var segs = [], - i, len=events.length, event, - eventStart, eventEnd, - segStart, segEnd, - isStart, isEnd; - for (i=0; i start && eventStart < end) { - if (eventStart < start) { - segStart = cloneDate(start); - isStart = false; - }else{ - segStart = eventStart; - isStart = true; - } - if (eventEnd > end) { - segEnd = cloneDate(end); - isEnd = false; - }else{ - segEnd = eventEnd; - isEnd = true; - } - segs.push({ - event: event, - start: segStart, - end: segEnd, - isStart: isStart, - isEnd: isEnd, - msLength: segEnd - segStart - }); - } - } - return segs.sort(segCmp); -} - - -// event rendering calculation utilities -function stackSegs(segs) { - var levels = [], - i, len = segs.length, seg, - j, collide, k; - for (i=0; i=0; i--) { - res = obj[parts[i].toLowerCase()]; - if (res !== undefined) { - return res; - } - } - return obj['']; -} - - -function htmlEscape(s) { - return s.replace(/&/g, '&') - .replace(//g, '>') - .replace(/'/g, ''') - .replace(/"/g, '"') - .replace(/\n/g, '
'); -} - - -function cssKey(_element) { - return _element.id + '/' + _element.className + '/' + _element.style.cssText.replace(/(^|;)\s*(top|left|width|height)\s*:[^;]*/ig, ''); -} - - -function disableTextSelection(element) { - element - .attr('unselectable', 'on') - .css('MozUserSelect', 'none') - .bind('selectstart.ui', function() { return false; }); -} - - -/* -function enableTextSelection(element) { - element - .attr('unselectable', 'off') - .css('MozUserSelect', '') - .unbind('selectstart.ui'); -} -*/ - - -function markFirstLast(e) { - e.children() - .removeClass('fc-first fc-last') - .filter(':first-child') - .addClass('fc-first') - .end() - .filter(':last-child') - .addClass('fc-last'); -} - - -function setDayID(cell, date) { - cell.each(function(i, _cell) { - _cell.className = _cell.className.replace(/^fc-\w*/, 'fc-' + dayIDs[date.getDay()]); - // TODO: make a way that doesn't rely on order of classes - }); -} - - -function getSkinCss(event, opt) { - var source = event.source || {}; - var eventColor = event.color; - var sourceColor = source.color; - var optionColor = opt('eventColor'); - var backgroundColor = - event.backgroundColor || - eventColor || - source.backgroundColor || - sourceColor || - opt('eventBackgroundColor') || - optionColor; - var borderColor = - event.borderColor || - eventColor || - source.borderColor || - sourceColor || - opt('eventBorderColor') || - optionColor; - var textColor = - event.textColor || - source.textColor || - opt('eventTextColor'); - var statements = []; - if (backgroundColor) { - statements.push('background-color:' + backgroundColor); - } - if (borderColor) { - statements.push('border-color:' + borderColor); - } - if (textColor) { - statements.push('color:' + textColor); - } - return statements.join(';'); -} - - -function applyAll(functions, thisObj, args) { - if ($.isFunction(functions)) { - functions = [ functions ]; - } - if (functions) { - var i; - var ret; - for (i=0; i") - .appendTo(element); - } - - - - function buildTable(showNumbers) { - var html = ''; - var i, j; - var headerClass = tm + "-widget-header"; - var contentClass = tm + "-widget-content"; - var month = t.start.getMonth(); - var today = clearTime(new Date()); - var cellDate; // not to be confused with local function. TODO: better names - var cellClasses; - var cell; - - html += "" + - "" + - ""; - - if (showWeekNumbers) { - html += "" + - "" + - ""; - - for (i=0; i" + - "
" + - ""; - } - - for (j=0; j" + - "
"; - if (showNumbers) { - html += "
" + cellDate.getDate() + "
"; - } - html += "
" + - "
 
" + - "
" + - "
" + - ""; - } - - html += ""; - } - html += "
" + - "
"; - } - - for (i=0; i"; - } - - html += "
"; - - lockHeight(); // the unlock happens later, in setHeight()... - if (table) { - table.remove(); - } - table = $(html).appendTo(element); - - head = table.find('thead'); - headCells = head.find('.fc-day-header'); - body = table.find('tbody'); - bodyRows = body.find('tr'); - bodyCells = body.find('.fc-day'); - bodyFirstCells = bodyRows.find('td:first-child'); - bodyCellTopInners = bodyRows.eq(0).find('.fc-day-content > div'); - - markFirstLast(head.add(head.find('tr'))); // marks first+last tr/th's - markFirstLast(bodyRows); // marks first+last td's - bodyRows.eq(0).addClass('fc-first'); - bodyRows.filter(':last').addClass('fc-last'); - - if (showWeekNumbers) { - head.find('.fc-week-number').text(weekNumberTitle); - } - - headCells.each(function(i, _cell) { - var date = indexDate(i); - $(_cell).text(formatDate(date, colFormat)); - }); - - if (showWeekNumbers) { - body.find('.fc-week-number > div').each(function(i, _cell) { - var weekStart = _cellDate(i, 0); - $(_cell).text(formatDate(weekStart, weekNumberFormat)); - }); - } - - bodyCells.each(function(i, _cell) { - var date = indexDate(i); - trigger('dayRender', t, date, $(_cell)); - }); - - dayBind(bodyCells); - } - - - - function setHeight(height) { - viewHeight = height; - - var bodyHeight = viewHeight - head.height(); - var rowHeight; - var rowHeightLast; - var cell; - - if (opt('weekMode') == 'variable') { - rowHeight = rowHeightLast = Math.floor(bodyHeight / (rowCnt==1 ? 2 : 6)); - }else{ - rowHeight = Math.floor(bodyHeight / rowCnt); - rowHeightLast = bodyHeight - rowHeight * (rowCnt-1); - } - - bodyFirstCells.each(function(i, _cell) { - if (i < rowCnt) { - cell = $(_cell); - setMinHeight( - cell.find('> div'), - (i==rowCnt-1 ? rowHeightLast : rowHeight) - vsides(cell) - ); - } - }); - - unlockHeight(); - } - - - function setWidth(width) { - viewWidth = width; - colContentPositions.clear(); - - weekNumberWidth = 0; - if (showWeekNumbers) { - weekNumberWidth = head.find('th.fc-week-number').outerWidth(); - } - - colWidth = Math.floor((viewWidth - weekNumberWidth) / colCnt); - setOuterWidth(headCells.slice(0, -1), colWidth); - } - - - - /* Day clicking and binding - -----------------------------------------------------------*/ - - - function dayBind(days) { - days.click(dayClick) - .mousedown(daySelectionMousedown); - } - - - function dayClick(ev) { - if (!opt('selectable')) { // if selectable, SelectionManager will worry about dayClick - var date = parseISO8601($(this).data('date')); - trigger('dayClick', this, date, true, ev); - } - } - - - - /* Semi-transparent Overlay Helpers - ------------------------------------------------------*/ - - - function renderDayOverlay(overlayStart, overlayEnd, refreshCoordinateGrid) { // overlayEnd is exclusive - if (refreshCoordinateGrid) { - coordinateGrid.build(); - } - var rowStart = cloneDate(t.visStart); - var rowEnd = addDays(cloneDate(rowStart), colCnt); - for (var i=0; i" + - "
"; - } - else { - s += "  
  
"; - dayTable = $(s).appendTo(element); - dayHead = dayTable.find('thead'); - dayHeadCells = dayHead.find('th').slice(1, -1); - dayBody = dayTable.find('tbody'); - dayBodyCells = dayBody.find('td').slice(0, -1); - dayBodyCellInners = dayBodyCells.find('div.fc-day-content div'); - dayBodyFirstCell = dayBodyCells.eq(0); - dayBodyFirstCellStretcher = dayBodyFirstCell.find('> div'); - - markFirstLast(dayHead.add(dayHead.find('tr'))); - markFirstLast(dayBody.add(dayBody.find('tr'))); - - axisFirstCells = dayHead.find('th:first'); - gutterCells = dayTable.find('.fc-agenda-gutter'); - - slotLayer = - $("
") - .appendTo(element); - - if (opt('allDaySlot')) { - - daySegmentContainer = - $("
") - .appendTo(slotLayer); - - s = - "" + - "" + - "" + - "" + - "" + - "" + - "
" + opt('allDayText') + "" + - "
" + - "
 
"; - allDayTable = $(s).appendTo(slotLayer); - allDayRow = allDayTable.find('tr'); - - dayBind(allDayRow.find('td')); - - axisFirstCells = axisFirstCells.add(allDayTable.find('th:first')); - gutterCells = gutterCells.add(allDayTable.find('th.fc-agenda-gutter')); - - slotLayer.append( - "
" + - "
" + - "
" - ); - - }else{ - - daySegmentContainer = $([]); // in jQuery 1.4, we can just do $() - - } - - slotScroller = - $("
") - .appendTo(slotLayer); - - slotContent = - $("
") - .appendTo(slotScroller); - - slotSegmentContainer = - $("
") - .appendTo(slotContent); - - s = - "" + - ""; - d = zeroDate(); - maxd = addMinutes(cloneDate(d), maxMinute); - addMinutes(d, minMinute); - slotCnt = 0; - for (i=0; d < maxd; i++) { - minutes = d.getMinutes(); - s += - "" + - "" + - "" + - ""; - addMinutes(d, opt('slotMinutes')); - slotCnt++; - } - s += - "" + - "
" + - ((!slotNormal || !minutes) ? formatDate(d, opt('axisFormat')) : ' ') + - "" + - "
 
" + - "
"; - slotTable = $(s).appendTo(slotContent); - slotTableFirstInner = slotTable.find('div:first'); - - slotBind(slotTable.find('td')); - - axisFirstCells = axisFirstCells.add(slotTable.find('th:first')); - } - - - - function updateCells() { - var i; - var headCell; - var bodyCell; - var date; - var today = clearTime(new Date()); - - if (showWeekNumbers) { - var weekText = formatDate(colDate(0), weekNumberFormat); - if (rtl) { - weekText = weekText + weekNumberTitle; - } - else { - weekText = weekNumberTitle + weekText; - } - dayHead.find('.fc-week-number').text(weekText); - } - - for (i=0; i= 0) { - addMinutes(d, minMinute + slotIndex * snapMinutes); - } - return d; - } - - - function colDate(col) { // returns dates with 00:00:00 - return addDays(cloneDate(t.visStart), col*dis+dit); - } - - - function cellIsAllDay(cell) { - return opt('allDaySlot') && !cell.row; - } - - - function dayOfWeekCol(dayOfWeek) { - return ((dayOfWeek - Math.max(firstDay, nwe) + colCnt) % colCnt)*dis+dit; - } - - - - - // get the Y coordinate of the given time on the given day (both Date objects) - function timePosition(day, time) { // both date objects. day holds 00:00 of current day - day = cloneDate(day, true); - if (time < addMinutes(cloneDate(day), minMinute)) { - return 0; - } - if (time >= addMinutes(cloneDate(day), maxMinute)) { - return slotTable.height(); - } - var slotMinutes = opt('slotMinutes'), - minutes = time.getHours()*60 + time.getMinutes() - minMinute, - slotI = Math.floor(minutes / slotMinutes), - slotTop = slotTopCache[slotI]; - if (slotTop === undefined) { - slotTop = slotTopCache[slotI] = slotTable.find('tr:eq(' + slotI + ') td div')[0].offsetTop; //.position().top; // need this optimization??? - } - return Math.max(0, Math.round( - slotTop - 1 + slotHeight * ((minutes % slotMinutes) / slotMinutes) - )); - } - - - function allDayBounds() { - return { - left: axisWidth, - right: viewWidth - gutterWidth - } - } - - - function getAllDayRow(index) { - return allDayRow; - } - - - function defaultEventEnd(event) { - var start = cloneDate(event.start); - if (event.allDay) { - return start; - } - return addMinutes(start, opt('defaultEventMinutes')); - } - - - - /* Selection - ---------------------------------------------------------------------------------*/ - - - function defaultSelectionEnd(startDate, allDay) { - if (allDay) { - return cloneDate(startDate); - } - return addMinutes(cloneDate(startDate), opt('slotMinutes')); - } - - - function renderSelection(startDate, endDate, allDay) { // only for all-day - if (allDay) { - if (opt('allDaySlot')) { - renderDayOverlay(startDate, addDays(cloneDate(endDate), 1), true); - } - }else{ - renderSlotSelection(startDate, endDate); - } - } - - - function renderSlotSelection(startDate, endDate) { - var helperOption = opt('selectHelper'); - coordinateGrid.build(); - if (helperOption) { - var col = dayDiff(startDate, t.visStart) * dis + dit; - if (col >= 0 && col < colCnt) { // only works when times are on same day - var rect = coordinateGrid.rect(0, col, 0, col, slotContent); // only for horizontal coords - var top = timePosition(startDate, startDate); - var bottom = timePosition(startDate, endDate); - if (bottom > top) { // protect against selections that are entirely before or after visible range - rect.top = top; - rect.height = bottom - top; - rect.left += 2; - rect.width -= 5; - if ($.isFunction(helperOption)) { - var helperRes = helperOption(startDate, endDate); - if (helperRes) { - rect.position = 'absolute'; - rect.zIndex = 8; - selectionHelper = $(helperRes) - .css(rect) - .appendTo(slotContent); - } - }else{ - rect.isStart = true; // conside rect a "seg" now - rect.isEnd = true; // - selectionHelper = $(slotSegHtml( - { - title: '', - start: startDate, - end: endDate, - className: ['fc-select-helper'], - editable: false - }, - rect - )); - selectionHelper.css('opacity', opt('dragOpacity')); - } - if (selectionHelper) { - slotBind(selectionHelper); - slotContent.append(selectionHelper); - setOuterWidth(selectionHelper, rect.width, true); // needs to be after appended - setOuterHeight(selectionHelper, rect.height, true); - } - } - } - }else{ - renderSlotOverlay(startDate, endDate); - } - } - - - function clearSelection() { - clearOverlays(); - if (selectionHelper) { - selectionHelper.remove(); - selectionHelper = null; - } - } - - - function slotSelectionMousedown(ev) { - if (ev.which == 1 && opt('selectable')) { // ev.which==1 means left mouse button - unselect(ev); - var dates; - hoverListener.start(function(cell, origCell) { - clearSelection(); - if (cell && cell.col == origCell.col && !cellIsAllDay(cell)) { - var d1 = cellDate(origCell); - var d2 = cellDate(cell); - dates = [ - d1, - addMinutes(cloneDate(d1), snapMinutes), // calculate minutes depending on selection slot minutes - d2, - addMinutes(cloneDate(d2), snapMinutes) - ].sort(cmp); - renderSlotSelection(dates[0], dates[3]); - }else{ - dates = null; - } - }, ev); - $(document).one('mouseup', function(ev) { - hoverListener.stop(); - if (dates) { - if (+dates[0] == +dates[1]) { - reportDayClick(dates[0], false, ev); - } - reportSelection(dates[0], dates[3], false, ev); - } - }); - } - } - - - function reportDayClick(date, allDay, ev) { - trigger('dayClick', dayBodyCells[dayOfWeekCol(date.getDay())], date, allDay, ev); - } - - - - /* External Dragging - --------------------------------------------------------------------------------*/ - - - function dragStart(_dragElement, ev, ui) { - hoverListener.start(function(cell) { - clearOverlays(); - if (cell) { - if (cellIsAllDay(cell)) { - renderCellOverlay(cell.row, cell.col, cell.row, cell.col); - }else{ - var d1 = cellDate(cell); - var d2 = addMinutes(cloneDate(d1), opt('defaultEventMinutes')); - renderSlotOverlay(d1, d2); - } - } - }, ev); - } - - - function dragStop(_dragElement, ev, ui) { - var cell = hoverListener.stop(); - clearOverlays(); - if (cell) { - trigger('drop', _dragElement, cellDate(cell), cellIsAllDay(cell), ev, ui); - } - } - - -} - -;; - -function AgendaEventRenderer() { - var t = this; - - - // exports - t.renderEvents = renderEvents; - t.compileDaySegs = compileDaySegs; // for DayEventRenderer - t.clearEvents = clearEvents; - t.slotSegHtml = slotSegHtml; - t.bindDaySeg = bindDaySeg; - - - // imports - DayEventRenderer.call(t); - var opt = t.opt; - var trigger = t.trigger; - //var setOverflowHidden = t.setOverflowHidden; - var isEventDraggable = t.isEventDraggable; - var isEventResizable = t.isEventResizable; - var eventEnd = t.eventEnd; - var reportEvents = t.reportEvents; - var reportEventClear = t.reportEventClear; - var eventElementHandlers = t.eventElementHandlers; - var setHeight = t.setHeight; - var getDaySegmentContainer = t.getDaySegmentContainer; - var getSlotSegmentContainer = t.getSlotSegmentContainer; - var getHoverListener = t.getHoverListener; - var getMaxMinute = t.getMaxMinute; - var getMinMinute = t.getMinMinute; - var timePosition = t.timePosition; - var colContentLeft = t.colContentLeft; - var colContentRight = t.colContentRight; - var renderDaySegs = t.renderDaySegs; - var resizableDayEvent = t.resizableDayEvent; // TODO: streamline binding architecture - var getColCnt = t.getColCnt; - var getColWidth = t.getColWidth; - var getSnapHeight = t.getSnapHeight; - var getSnapMinutes = t.getSnapMinutes; - var getBodyContent = t.getBodyContent; - var reportEventElement = t.reportEventElement; - var showEvents = t.showEvents; - var hideEvents = t.hideEvents; - var eventDrop = t.eventDrop; - var eventResize = t.eventResize; - var renderDayOverlay = t.renderDayOverlay; - var clearOverlays = t.clearOverlays; - var calendar = t.calendar; - var formatDate = calendar.formatDate; - var formatDates = calendar.formatDates; - - - - /* Rendering - ----------------------------------------------------------------------------*/ - - - function renderEvents(events, modifiedEventId) { - reportEvents(events); - var i, len=events.length, - dayEvents=[], - slotEvents=[]; - for (i=0; i" + - "
" + - "
" + - htmlEscape(formatDates(event.start, event.end, opt('timeFormat'))) + - "
" + - "
" + - htmlEscape(event.title) + - "
" + - "
" + - "
"; - if (seg.isEnd && isEventResizable(event)) { - html += - "
=
"; - } - html += - ""; - return html; - } - - - function bindDaySeg(event, eventElement, seg) { - if (isEventDraggable(event)) { - draggableDayEvent(event, eventElement, seg.isStart); - } - if (seg.isEnd && isEventResizable(event)) { - resizableDayEvent(event, eventElement, seg); - } - eventElementHandlers(event, eventElement); - // needs to be after, because resizableDayEvent might stopImmediatePropagation on click - } - - - function bindSlotSeg(event, eventElement, seg) { - var timeElement = eventElement.find('div.fc-event-time'); - if (isEventDraggable(event)) { - draggableSlotEvent(event, eventElement, timeElement); - } - if (seg.isEnd && isEventResizable(event)) { - resizableSlotEvent(event, eventElement, timeElement); - } - eventElementHandlers(event, eventElement); - } - - - - /* Dragging - -----------------------------------------------------------------------------------*/ - - - // when event starts out FULL-DAY - - function draggableDayEvent(event, eventElement, isStart) { - var origWidth; - var revert; - var allDay=true; - var dayDelta; - var dis = opt('isRTL') ? -1 : 1; - var hoverListener = getHoverListener(); - var colWidth = getColWidth(); - var snapHeight = getSnapHeight(); - var snapMinutes = getSnapMinutes(); - var minMinute = getMinMinute(); - eventElement.draggable({ - zIndex: 9, - opacity: opt('dragOpacity', 'month'), // use whatever the month view was using - revertDuration: opt('dragRevertDuration'), - start: function(ev, ui) { - trigger('eventDragStart', eventElement, event, ev, ui); - hideEvents(event, eventElement); - origWidth = eventElement.width(); - hoverListener.start(function(cell, origCell, rowDelta, colDelta) { - clearOverlays(); - if (cell) { - //setOverflowHidden(true); - revert = false; - dayDelta = colDelta * dis; - if (!cell.row) { - // on full-days - renderDayOverlay( - addDays(cloneDate(event.start), dayDelta), - addDays(exclEndDay(event), dayDelta) - ); - resetElement(); - }else{ - // mouse is over bottom slots - if (isStart) { - if (allDay) { - // convert event to temporary slot-event - eventElement.width(colWidth - 10); // don't use entire width - setOuterHeight( - eventElement, - snapHeight * Math.round( - (event.end ? ((event.end - event.start) / MINUTE_MS) : opt('defaultEventMinutes')) / - snapMinutes - ) - ); - eventElement.draggable('option', 'grid', [colWidth, 1]); - allDay = false; - } - }else{ - revert = true; - } - } - revert = revert || (allDay && !dayDelta); - }else{ - resetElement(); - //setOverflowHidden(false); - revert = true; - } - eventElement.draggable('option', 'revert', revert); - }, ev, 'drag'); - }, - stop: function(ev, ui) { - hoverListener.stop(); - clearOverlays(); - trigger('eventDragStop', eventElement, event, ev, ui); - if (revert) { - // hasn't moved or is out of bounds (draggable has already reverted) - resetElement(); - eventElement.css('filter', ''); // clear IE opacity side-effects - showEvents(event, eventElement); - }else{ - // changed! - var minuteDelta = 0; - if (!allDay) { - minuteDelta = Math.round((eventElement.offset().top - getBodyContent().offset().top) / snapHeight) - * snapMinutes - + minMinute - - (event.start.getHours() * 60 + event.start.getMinutes()); - } - eventDrop(this, event, dayDelta, minuteDelta, allDay, ev, ui); - } - //setOverflowHidden(false); - } - }); - function resetElement() { - if (!allDay) { - eventElement - .width(origWidth) - .height('') - .draggable('option', 'grid', null); - allDay = true; - } - } - } - - - // when event starts out IN TIMESLOTS - - function draggableSlotEvent(event, eventElement, timeElement) { - var origPosition; - var allDay=false; - var dayDelta; - var minuteDelta; - var prevMinuteDelta; - var dis = opt('isRTL') ? -1 : 1; - var hoverListener = getHoverListener(); - var colCnt = getColCnt(); - var colWidth = getColWidth(); - var snapHeight = getSnapHeight(); - var snapMinutes = getSnapMinutes(); - eventElement.draggable({ - zIndex: 9, - scroll: false, - grid: [colWidth, snapHeight], - axis: colCnt==1 ? 'y' : false, - opacity: opt('dragOpacity'), - revertDuration: opt('dragRevertDuration'), - start: function(ev, ui) { - trigger('eventDragStart', eventElement, event, ev, ui); - hideEvents(event, eventElement); - origPosition = eventElement.position(); - minuteDelta = prevMinuteDelta = 0; - hoverListener.start(function(cell, origCell, rowDelta, colDelta) { - eventElement.draggable('option', 'revert', !cell); - clearOverlays(); - if (cell) { - dayDelta = colDelta * dis; - if (opt('allDaySlot') && !cell.row) { - // over full days - if (!allDay) { - // convert to temporary all-day event - allDay = true; - timeElement.hide(); - eventElement.draggable('option', 'grid', null); - } - renderDayOverlay( - addDays(cloneDate(event.start), dayDelta), - addDays(exclEndDay(event), dayDelta) - ); - }else{ - // on slots - resetElement(); - } - } - }, ev, 'drag'); - }, - drag: function(ev, ui) { - minuteDelta = Math.round((ui.position.top - origPosition.top) / snapHeight) * snapMinutes; - if (minuteDelta != prevMinuteDelta) { - if (!allDay) { - updateTimeText(minuteDelta); - } - prevMinuteDelta = minuteDelta; - } - }, - stop: function(ev, ui) { - var cell = hoverListener.stop(); - clearOverlays(); - trigger('eventDragStop', eventElement, event, ev, ui); - if (cell && (dayDelta || minuteDelta || allDay)) { - // changed! - eventDrop(this, event, dayDelta, allDay ? 0 : minuteDelta, allDay, ev, ui); - }else{ - // either no change or out-of-bounds (draggable has already reverted) - resetElement(); - eventElement.css('filter', ''); // clear IE opacity side-effects - eventElement.css(origPosition); // sometimes fast drags make event revert to wrong position - updateTimeText(0); - showEvents(event, eventElement); - } - } - }); - function updateTimeText(minuteDelta) { - var newStart = addMinutes(cloneDate(event.start), minuteDelta); - var newEnd; - if (event.end) { - newEnd = addMinutes(cloneDate(event.end), minuteDelta); - } - timeElement.text(formatDates(newStart, newEnd, opt('timeFormat'))); - } - function resetElement() { - // convert back to original slot-event - if (allDay) { - timeElement.css('display', ''); // show() was causing display=inline - eventElement.draggable('option', 'grid', [colWidth, snapHeight]); - allDay = false; - } - } - } - - - - /* Resizing - --------------------------------------------------------------------------------------*/ - - - function resizableSlotEvent(event, eventElement, timeElement) { - var snapDelta, prevSnapDelta; - var snapHeight = getSnapHeight(); - var snapMinutes = getSnapMinutes(); - eventElement.resizable({ - handles: { - s: '.ui-resizable-handle' - }, - grid: snapHeight, - start: function(ev, ui) { - snapDelta = prevSnapDelta = 0; - hideEvents(event, eventElement); - eventElement.css('z-index', 9); - trigger('eventResizeStart', this, event, ev, ui); - }, - resize: function(ev, ui) { - // don't rely on ui.size.height, doesn't take grid into account - snapDelta = Math.round((Math.max(snapHeight, eventElement.height()) - ui.originalSize.height) / snapHeight); - if (snapDelta != prevSnapDelta) { - timeElement.text( - formatDates( - event.start, - (!snapDelta && !event.end) ? null : // no change, so don't display time range - addMinutes(eventEnd(event), snapMinutes*snapDelta), - opt('timeFormat') - ) - ); - prevSnapDelta = snapDelta; - } - }, - stop: function(ev, ui) { - trigger('eventResizeStop', this, event, ev, ui); - if (snapDelta) { - eventResize(this, event, 0, snapMinutes*snapDelta, ev, ui); - }else{ - eventElement.css('z-index', 8); - showEvents(event, eventElement); - // BUG: if event was really short, need to put title back in span - } - } - }); - } - - -} - - -function countForwardSegs(levels) { - var i, j, k, level, segForward, segBack; - for (i=levels.length-1; i>0; i--) { - level = levels[i]; - for (j=0; j"); - var elements; - var segmentContainer = getDaySegmentContainer(); - var i; - var segCnt = segs.length; - var element; - tempContainer[0].innerHTML = daySegHTML(segs); // faster than .html() - elements = tempContainer.children(); - segmentContainer.append(elements); - daySegElementResolve(segs, elements); - daySegCalcHSides(segs); - daySegSetWidths(segs); - daySegCalcHeights(segs); - daySegSetTops(segs, getRowTops(getRowDivs())); - elements = []; - for (i=0; i" + - "
"; - if (!event.allDay && seg.isStart) { - html += - "" + - htmlEscape(formatDates(event.start, event.end, opt('timeFormat'))) + - ""; - } - html += - "" + htmlEscape(event.title) + "" + - "
"; - if (seg.isEnd && isEventResizable(event)) { - html += - "
" + - "   " + // makes hit area a lot better for IE6/7 - "
"; - } - html += - ""; - seg.left = left; - seg.outerWidth = right - left; - seg.startCol = leftCol; - seg.endCol = rightCol + 1; // needs to be exclusive - } - return html; - } - - - function daySegElementResolve(segs, elements) { // sets seg.element - var i; - var segCnt = segs.length; - var seg; - var event; - var element; - var triggerRes; - for (i=0; i div'); // optimal selector? - } - return rowDivs; - } - - - function getRowTops(rowDivs) { - var i; - var rowCnt = rowDivs.length; - var tops = []; - for (i=0; i selection for IE - element - .mousedown(function(ev) { // prevent native selection for others - ev.preventDefault(); - }) - .click(function(ev) { - if (isResizing) { - ev.preventDefault(); // prevent link from being visited (only method that worked in IE6) - ev.stopImmediatePropagation(); // prevent fullcalendar eventClick handler from being called - // (eventElementHandlers needs to be bound after resizableDayEvent) - } - }); - - handle.mousedown(function(ev) { - if (ev.which != 1) { - return; // needs to be left mouse button - } - isResizing = true; - var hoverListener = t.getHoverListener(); - var rowCnt = getRowCnt(); - var colCnt = getColCnt(); - var dis = rtl ? -1 : 1; - var dit = rtl ? colCnt-1 : 0; - var elementTop = element.css('top'); - var dayDelta; - var helpers; - var eventCopy = $.extend({}, event); - var minCell = dateCell(event.start); - clearSelection(); - $('body') - .css('cursor', direction + '-resize') - .one('mouseup', mouseup); - trigger('eventResizeStart', this, event, ev); - hoverListener.start(function(cell, origCell) { - if (cell) { - var r = Math.max(minCell.row, cell.row); - var c = cell.col; - if (rowCnt == 1) { - r = 0; // hack for all-day area in agenda views - } - if (r == minCell.row) { - if (rtl) { - c = Math.min(minCell.col, c); - }else{ - c = Math.max(minCell.col, c); - } - } - dayDelta = (r*7 + c*dis+dit) - (origCell.row*7 + origCell.col*dis+dit); - var newEnd = addDays(eventEnd(event), dayDelta, true); - if (dayDelta) { - eventCopy.end = newEnd; - var oldHelpers = helpers; - helpers = renderTempDaySegs(compileDaySegs([eventCopy]), seg.row, elementTop); - helpers.find('*').css('cursor', direction + '-resize'); - if (oldHelpers) { - oldHelpers.remove(); - } - hideEvents(event); - }else{ - if (helpers) { - showEvents(event); - helpers.remove(); - helpers = null; - } - } - clearOverlays(); - renderDayOverlay(event.start, addDays(cloneDate(newEnd), 1)); // coordinate grid already rebuild at hoverListener.start - } - }, ev); - - function mouseup(ev) { - trigger('eventResizeStop', this, event, ev); - $('body').css('cursor', ''); - hoverListener.stop(); - clearOverlays(); - if (dayDelta) { - eventResize(this, event, dayDelta, 0, ev); - // event redraw will clear helpers - } - // otherwise, the drag handler already restored the old events - - setTimeout(function() { // make this happen after the element's click event - isResizing = false; - },0); - } - - }); - } - - -} - -;; - -//BUG: unselect needs to be triggered when events are dragged+dropped - -function SelectionManager() { - var t = this; - - - // exports - t.select = select; - t.unselect = unselect; - t.reportSelection = reportSelection; - t.daySelectionMousedown = daySelectionMousedown; - - - // imports - var opt = t.opt; - var trigger = t.trigger; - var defaultSelectionEnd = t.defaultSelectionEnd; - var renderSelection = t.renderSelection; - var clearSelection = t.clearSelection; - - - // locals - var selected = false; - - - - // unselectAuto - if (opt('selectable') && opt('unselectAuto')) { - $(document).mousedown(function(ev) { - var ignore = opt('unselectCancel'); - if (ignore) { - if ($(ev.target).parents(ignore).length) { // could be optimized to stop after first match - return; - } - } - unselect(ev); - }); - } - - - function select(startDate, endDate, allDay) { - unselect(); - if (!endDate) { - endDate = defaultSelectionEnd(startDate, allDay); - } - renderSelection(startDate, endDate, allDay); - reportSelection(startDate, endDate, allDay); - } - - - function unselect(ev) { - if (selected) { - selected = false; - clearSelection(); - trigger('unselect', null, ev); - } - } - - - function reportSelection(startDate, endDate, allDay, ev) { - selected = true; - trigger('select', null, startDate, endDate, allDay, ev); - } - - - function daySelectionMousedown(ev) { // not really a generic manager method, oh well - var cellDate = t.cellDate; - var cellIsAllDay = t.cellIsAllDay; - var hoverListener = t.getHoverListener(); - var reportDayClick = t.reportDayClick; // this is hacky and sort of weird - if (ev.which == 1 && opt('selectable')) { // which==1 means left mouse button - unselect(ev); - var _mousedownElement = this; - var dates; - hoverListener.start(function(cell, origCell) { // TODO: maybe put cellDate/cellIsAllDay info in cell - clearSelection(); - if (cell && cellIsAllDay(cell)) { - dates = [ cellDate(origCell), cellDate(cell) ].sort(cmp); - renderSelection(dates[0], dates[1], true); - }else{ - dates = null; - } - }, ev); - $(document).one('mouseup', function(ev) { - hoverListener.stop(); - if (dates) { - if (+dates[0] == +dates[1]) { - reportDayClick(dates[0], true, ev); - } - reportSelection(dates[0], dates[1], true, ev); - } - }); - } - } - - -} - -;; - -function OverlayManager() { - var t = this; - - - // exports - t.renderOverlay = renderOverlay; - t.clearOverlays = clearOverlays; - - - // locals - var usedOverlays = []; - var unusedOverlays = []; - - - function renderOverlay(rect, parent) { - var e = unusedOverlays.shift(); - if (!e) { - e = $("
"); - } - if (e[0].parentNode != parent[0]) { - e.appendTo(parent); - } - usedOverlays.push(e.css(rect).show()); - return e; - } - - - function clearOverlays() { - var e; - while (e = usedOverlays.shift()) { - unusedOverlays.push(e.hide().unbind()); - } - } - - -} - -;; - -function CoordinateGrid(buildFunc) { - - var t = this; - var rows; - var cols; - - - t.build = function() { - rows = []; - cols = []; - buildFunc(rows, cols); - }; - - - t.cell = function(x, y) { - var rowCnt = rows.length; - var colCnt = cols.length; - var i, r=-1, c=-1; - for (i=0; i= rows[i][0] && y < rows[i][1]) { - r = i; - break; - } - } - for (i=0; i= cols[i][0] && x < cols[i][1]) { - c = i; - break; - } - } - return (r>=0 && c>=0) ? { row:r, col:c } : null; - }; - - - t.rect = function(row0, col0, row1, col1, originElement) { // row1,col1 is inclusive - var origin = originElement.offset(); - return { - top: rows[row0][0] - origin.top, - left: cols[col0][0] - origin.left, - width: cols[col1][1] - cols[col0][0], - height: rows[row1][1] - rows[row0][0] - }; - }; - -} - -;; - -function HoverListener(coordinateGrid) { - - - var t = this; - var bindType; - var change; - var firstCell; - var cell; - - - t.start = function(_change, ev, _bindType) { - change = _change; - firstCell = cell = null; - coordinateGrid.build(); - mouse(ev); - bindType = _bindType || 'mousemove'; - $(document).bind(bindType, mouse); - }; - - - function mouse(ev) { - _fixUIEvent(ev); // see below - var newCell = coordinateGrid.cell(ev.pageX, ev.pageY); - if (!newCell != !cell || newCell && (newCell.row != cell.row || newCell.col != cell.col)) { - if (newCell) { - if (!firstCell) { - firstCell = newCell; - } - change(newCell, firstCell, newCell.row-firstCell.row, newCell.col-firstCell.col); - }else{ - change(newCell, firstCell); - } - cell = newCell; - } - } - - - t.stop = function() { - $(document).unbind(bindType, mouse); - return cell; - }; - - -} - - - -// this fix was only necessary for jQuery UI 1.8.16 (and jQuery 1.7 or 1.7.1) -// upgrading to jQuery UI 1.8.17 (and using either jQuery 1.7 or 1.7.1) fixed the problem -// but keep this in here for 1.8.16 users -// and maybe remove it down the line - -function _fixUIEvent(event) { // for issue 1168 - if (event.pageX === undefined) { - event.pageX = event.originalEvent.pageX; - event.pageY = event.originalEvent.pageY; - } -} -;; - -function HorizontalPositionCache(getElement) { - - var t = this, - elements = {}, - lefts = {}, - rights = {}; - - function e(i) { - return elements[i] = elements[i] || getElement(i); - } - - t.left = function(i) { - return lefts[i] = lefts[i] === undefined ? e(i).position().left : lefts[i]; - }; - - t.right = function(i) { - return rights[i] = rights[i] === undefined ? t.left(i) + e(i).width() : rights[i]; - }; - - t.clear = function() { - elements = {}; - lefts = {}; - rights = {}; - }; - -} - -;; - -})(jQuery); \ No newline at end of file diff --git a/app/assets/javascripts/fullcalendar.min.js b/app/assets/javascripts/fullcalendar.min.js new file mode 100644 index 000000000..f43a7f3f8 --- /dev/null +++ b/app/assets/javascripts/fullcalendar.min.js @@ -0,0 +1,11 @@ +/*! + * FullCalendar v3.6.1 + * Docs & License: https://fullcalendar.io/ + * (c) 2017 Adam Shaw + */ +!function(t){"function"==typeof define&&define.amd?define(["jquery","moment"],t):"object"==typeof exports?module.exports=t(require("jquery"),require("moment")):t(jQuery,moment)}(function(t,e){function n(t){return j(t,qt)}function i(t,e){e.left&&t.css({"border-left-width":1,"margin-left":e.left-1}),e.right&&t.css({"border-right-width":1,"margin-right":e.right-1})}function r(t){t.css({"margin-left":"","margin-right":"","border-left-width":"","border-right-width":""})}function s(){t("body").addClass("fc-not-allowed")}function o(){t("body").removeClass("fc-not-allowed")}function a(e,n,i){var r=Math.floor(n/e.length),s=Math.floor(n-r*(e.length-1)),o=[],a=[],u=[],c=0;l(e),e.each(function(n,i){var l=n===e.length-1?s:r,h=t(i).outerHeight(!0);h *").each(function(e,i){var r=t(i).outerWidth();r>n&&(n=r)}),n++,e.width(n),n}function c(t,e){var n,i=t.add(e);return i.css({position:"relative",left:-1}),n=t.outerHeight()-e.outerHeight(),i.css({position:"",left:""}),n}function h(e){var n=e.css("position"),i=e.parents().filter(function(){var e=t(this);return/(auto|scroll)/.test(e.css("overflow")+e.css("overflow-y")+e.css("overflow-x"))}).eq(0);return"fixed"!==n&&i.length?i:t(e[0].ownerDocument||document)}function d(t,e){var n=t.offset(),i=n.left-(e?e.left:0),r=n.top-(e?e.top:0);return{left:i,right:i+t.outerWidth(),top:r,bottom:r+t.outerHeight()}}function f(t,e){var n=t.offset(),i=p(t),r=n.left+w(t,"border-left-width")+i.left-(e?e.left:0),s=n.top+w(t,"border-top-width")+i.top-(e?e.top:0);return{left:r,right:r+t[0].clientWidth,top:s,bottom:s+t[0].clientHeight}}function g(t,e){var n=t.offset(),i=n.left+w(t,"border-left-width")+w(t,"padding-left")-(e?e.left:0),r=n.top+w(t,"border-top-width")+w(t,"padding-top")-(e?e.top:0);return{left:i,right:i+t.width(),top:r,bottom:r+t.height()}}function p(t){var e,n=t[0].offsetWidth-t[0].clientWidth,i=t[0].offsetHeight-t[0].clientHeight;return n=v(n),i=v(i),e={left:0,right:0,top:0,bottom:i},m()&&"rtl"==t.css("direction")?e.left=n:e.right=n,e}function v(t){return t=Math.max(0,t),t=Math.round(t)}function m(){return null===Yt&&(Yt=y()),Yt}function y(){var e=t("
").css({position:"absolute",top:-1e3,left:0,border:0,padding:0,overflow:"scroll",direction:"rtl"}).appendTo("body"),n=e.children(),i=n.offset().left>e.offset().left;return e.remove(),i}function w(t,e){return parseFloat(t.css(e))||0}function D(t){return 1==t.which&&!t.ctrlKey}function b(t){var e=t.originalEvent.touches;return e&&e.length?e[0].pageX:t.pageX}function E(t){var e=t.originalEvent.touches;return e&&e.length?e[0].pageY:t.pageY}function S(t){return/^touch/.test(t.type)}function C(t){t.addClass("fc-unselectable").on("selectstart",T)}function R(t){t.removeClass("fc-unselectable").off("selectstart",T)}function T(t){t.preventDefault()}function I(t,e){var n={left:Math.max(t.left,e.left),right:Math.min(t.right,e.right),top:Math.max(t.top,e.top),bottom:Math.min(t.bottom,e.bottom)};return n.left=1&&at(r)));n++);return i}function N(t,e){var n=O(t);return"week"===n&&"object"==typeof e&&e.days&&(n="day"),n}function V(t,n,i){return null!=i?i.diff(n,t,!0):e.isDuration(n)?n.as(t):n.end.diff(n.start,t,!0)}function G(t,e,n){var i;return _(n)?(e-t)/n:(i=n.asMonths(),Math.abs(i)>=1&&at(i)?e.diff(t,"months",!0)/i:e.diff(t,"days",!0)/n.asDays())}function U(t,e){var n,i;return _(t)||_(e)?t/e:(n=t.asMonths(),i=e.asMonths(),Math.abs(n)>=1&&at(n)&&Math.abs(i)>=1&&at(i)?n/i:t.asDays()/e.asDays())}function W(t,n){var i;return _(t)?e.duration(t*n):(i=t.asMonths(),Math.abs(i)>=1&&at(i)?e.duration({months:i*n}):e.duration({days:t.asDays()*n}))}function _(t){return Boolean(t.hours()||t.minutes()||t.seconds()||t.milliseconds())}function q(t){return"[object Date]"===Object.prototype.toString.call(t)||t instanceof Date}function Y(t){return"string"==typeof t&&/^\d+\:\d+(?:\:\d+\.?(?:\d{3})?)?$/.test(t)}function j(t,e){var n,i,r,s,o,a,l={};if(e)for(n=0;n=0;s--)if("object"==typeof(o=t[s][i]))r.unshift(o);else if(void 0!==o){l[i]=o;break}r.length&&(l[i]=j(r))}for(n=t.length-1;n>=0;n--){a=t[n];for(i in a)i in l||(l[i]=a[i])}return l}function Z(t,e){for(var n in t)Q(t,n)&&(e[n]=t[n])}function Q(t,e){return Qt.call(t,e)}function $(e,n,i){if(t.isFunction(e)&&(e=[e]),e){var r,s;for(r=0;r/g,">").replace(/'/g,"'").replace(/"/g,""").replace(/\n/g,"
")}function nt(t){return t.replace(/&.*?;/g,"")}function it(e){var n=[];return t.each(e,function(t,e){null!=e&&n.push(t+":"+e)}),n.join(";")}function rt(e){var n=[];return t.each(e,function(t,e){null!=e&&n.push(t+'="'+et(e)+'"')}),n.join(" ")}function st(t){return t.charAt(0).toUpperCase()+t.slice(1)}function ot(t,e){return t-e}function at(t){return t%1==0}function lt(t,e){var n=t[e];return function(){return n.apply(t,arguments)}}function ut(t,e,n){var i,r,s,o,a,l=function(){var u=+new Date-o;u"),g.append(o("left")).append(o("right")).append(o("center")).append('
')):s()}function s(){g&&(g.remove(),g=f.el=null)}function o(i){var r=e.theme,s=t('
'),o=n.layout[i],a=e.opt("customButtons")||{},l=e.overrides.buttonText||{},u=e.opt("buttonText")||{};return o&&t.each(o.split(" "),function(n){var i,o=t(),c=!0;t.each(this.split(","),function(n,i){var s,h,d,f,g,v,m,y;"title"==i?(o=o.add(t("

 

")),c=!1):((s=a[i])?(d=function(t){s.click&&s.click.call(y[0],t)},(f=r.getCustomButtonIconClass(s))||(f=r.getIconClass(i))||(g=s.text)):(h=e.getViewSpec(i))?(p.push(i),d=function(){e.changeView(i)},(g=h.buttonTextOverride)||(f=r.getIconClass(i))||(g=h.buttonTextDefault)):e[i]&&(d=function(){e[i]()},(g=l[i])||(f=r.getIconClass(i))||(g=u[i])),d&&(m=["fc-"+i+"-button",r.getClass("button"),r.getClass("stateDefault")],g?v=et(g):f&&(v=""),y=t('").click(function(t){y.hasClass(r.getClass("stateDisabled"))||(d(t),(y.hasClass(r.getClass("stateActive"))||y.hasClass(r.getClass("stateDisabled")))&&y.removeClass(r.getClass("stateHover")))}).mousedown(function(){y.not("."+r.getClass("stateActive")).not("."+r.getClass("stateDisabled")).addClass(r.getClass("stateDown"))}).mouseup(function(){y.removeClass(r.getClass("stateDown"))}).hover(function(){y.not("."+r.getClass("stateActive")).not("."+r.getClass("stateDisabled")).addClass(r.getClass("stateHover"))},function(){y.removeClass(r.getClass("stateHover")).removeClass(r.getClass("stateDown"))}),o=o.add(y)))}),c&&o.first().addClass(r.getClass("cornerLeft")).end().last().addClass(r.getClass("cornerRight")).end(),o.length>1?(i=t("
"),c&&i.addClass(r.getClass("buttonGroup")),i.append(o),s.append(i)):s.append(o)}),s}function a(t){g&&g.find("h2").text(t)}function l(t){g&&g.find(".fc-"+t+"-button").addClass(e.theme.getClass("stateActive"))}function u(t){g&&g.find(".fc-"+t+"-button").removeClass(e.theme.getClass("stateActive"))}function c(t){g&&g.find(".fc-"+t+"-button").prop("disabled",!0).addClass(e.theme.getClass("stateDisabled"))}function h(t){g&&g.find(".fc-"+t+"-button").prop("disabled",!1).removeClass(e.theme.getClass("stateDisabled"))}function d(){return p}var f=this;f.setToolbarOptions=i,f.render=r,f.removeElement=s,f.updateTitle=a,f.activateButton=l,f.deactivateButton=u,f.disableButton=c,f.enableButton=h,f.getViewsWithButtons=d,f.el=null;var g,p=[]}function Et(t,e,n){var i;for(i=0;is&&r.push(new Ue(s,i.startMs)),i.endMs>s&&(s=i.endMs);return s=t.leftCol)return!0;return!1}function At(t,e){return t.leftCol-e.leftCol}function Lt(t){var e,n,i,r=[];for(e=0;ee.top&&t.tops&&(!l[o]||u.isSame(c,l[o]))&&(o-1!==s||"."!==f[o]);o--)v=f[o]+v;for(a=s;a<=o;a++)m+=f[a],y+=g[a];return(m||y)&&(w=r?y+i+m:m+i+y),d(p+w+v)}function r(t){return D[t]||(D[t]=s(t))}function s(t){var e=o(t);return{fakeFormatString:l(e),sameUnits:u(e)}}function o(t){for(var e,n=[],i=/\[([^\]]*)\]|\(([^\)]*)\)|(LTS|LT|(\w)\4*o?)|([^\w\[\(]+)/g;e=i.exec(t);)e[1]?n.push.apply(n,a(e[1])):e[2]?n.push({maybe:o(e[2])}):e[3]?n.push({token:e[3]}):e[5]&&n.push.apply(n,a(e[5]));return n}function a(t){return". "===t?["."," "]:[t]}function l(t){var e,n,i=[];for(e=0;er.value)&&(r=i);return r?r.unit:null}Wt.formatDate=t,Wt.formatRange=n,Wt.oldMomentFormat=e,Wt.queryMostGranularFormatUnit=f;var g="\v",p="",v="",m=new RegExp(v+"([^"+v+"]*)"+v,"g"),y={t:function(t){return e(t,"a").charAt(0)},T:function(t){return e(t,"A").charAt(0)}},w={Y:{value:1,unit:"year"},M:{value:2,unit:"month"},W:{value:3,unit:"week"},w:{value:3,unit:"week"},D:{value:4,unit:"day"},d:{value:4,unit:"day"}},D={}}();var ee=Wt.formatDate,ne=Wt.formatRange,ie=Wt.oldMomentFormat;Wt.Class=dt,dt.extend=function(){var t,e={};for(t=0;t0}},se=Wt.ListenerMixin=function(){var e=0;return{listenerId:null,listenTo:function(e,n,i){if("object"==typeof n)for(var r in n)n.hasOwnProperty(r)&&this.listenTo(e,r,n[r]);else"string"==typeof n&&e.on(n+"."+this.getListenerNamespace(),t.proxy(i,this))},stopListeningTo:function(t,e){t.off((e||"")+"."+this.getListenerNamespace())},getListenerNamespace:function(){return null==this.listenerId&&(this.listenerId=e++),"_listener"+this.listenerId}}}(),oe={standardPropMap:{},applyProps:function(t){var e,n=this.standardPropMap,i={},r={};for(e in t)!0===n[e]?this[e]=t[e]:!1===n[e]?i[e]=t[e]:r[e]=t[e];return this.applyMiscProps(r),this.applyManualStandardProps(i)},applyManualStandardProps:function(t){return!0},applyMiscProps:function(t){},isStandardProp:function(t){return t in this.standardPropMap}},ae=function(t){var e=this.prototype;e.hasOwnProperty("standardPropMap")||(e.standardPropMap=Object.create(e.standardPropMap)),Z(t,e.standardPropMap)},le=function(t,e){var n,i=this.prototype.standardPropMap;for(n in i)null!=t[n]&&!0===i[n]&&(e[n]=t[n])},ue=dt.extend(re,se,{_props:null,_watchers:null,_globalWatchArgs:{},constructor:function(){this._watchers={},this._props={},this.applyGlobalWatchers(),this.constructed()},constructed:function(){},applyGlobalWatchers:function(){var t,e=this._globalWatchArgs;for(t in e)this.watch.apply(this,e[t])},has:function(t){return t in this._props},get:function(t){return void 0===t?this._props:this._props[t]},set:function(t,e){var n;"string"==typeof t?(n={},n[t]=void 0===e?null:e):n=t,this.setProps(n)},reset:function(t){var e,n=this._props,i={};for(e in n)i[e]=void 0;for(e in t)i[e]=t[e];this.setProps(i)},unset:function(t){var e,n,i={};for(e="string"==typeof t?[t]:t,n=0;n=0;e--)switch(n=i[e],n.type){case"init":r=!1;case"add":case"remove":i.splice(e,1)}return r&&i.push(t),r}});Wt.RenderQueue=de;var fe=dt.extend(se,{isHidden:!0,options:null,el:null,margin:10,constructor:function(t){this.options=t||{}},show:function(){this.isHidden&&(this.el||this.render(),this.el.show(),this.position(),this.isHidden=!1,this.trigger("show"))},hide:function(){this.isHidden||(this.el.hide(),this.isHidden=!0,this.trigger("hide"))},render:function(){var e=this,n=this.options;this.el=t('
').addClass(n.className||"").css({top:0,left:0}).append(n.content).appendTo(n.parentEl),this.el.on("click",".fc-close",function(){e.hide()}),n.autoHide&&this.listenTo(t(document),"mousedown",this.documentMousedown)},documentMousedown:function(e){this.el&&!t(e.target).closest(this.el).length&&this.hide()},removeElement:function(){this.hide(),this.el&&(this.el.remove(),this.el=null),this.stopListeningTo(t(document),"mousedown")},position:function(){var e,n,i,r,s,o=this.options,a=this.el.offsetParent().offset(),l=this.el.outerWidth(),u=this.el.outerHeight(),c=t(window),d=h(this.el);r=o.top||0,s=void 0!==o.left?o.left:void 0!==o.right?o.right-l:0,d.is(window)||d.is(document)?(d=c,e=0,n=0):(i=d.offset(),e=i.top,n=i.left),e+=c.scrollTop(),n+=c.scrollLeft(),!1!==o.viewportConstrain&&(r=Math.min(r,e+d.outerHeight()-u-this.margin),r=Math.max(r,e+this.margin),s=Math.min(s,n+d.outerWidth()-l-this.margin),s=Math.max(s,n+this.margin)),this.el.css({top:r-a.top,left:s-a.left})},trigger:function(t){this.options[t]&&this.options[t].apply(this,Array.prototype.slice.call(arguments,1))}}),ge=Wt.CoordCache=dt.extend({els:null,forcedOffsetParentEl:null,origin:null,boundingRect:null,isHorizontal:!1,isVertical:!1,lefts:null,rights:null,tops:null,bottoms:null,constructor:function(e){this.els=t(e.els),this.isHorizontal=e.isHorizontal,this.isVertical=e.isVertical,this.forcedOffsetParentEl=e.offsetParent?t(e.offsetParent):null},build:function(){var t=this.forcedOffsetParentEl;!t&&this.els.length>0&&(t=this.els.eq(0).offsetParent()),this.origin=t?t.offset():null,this.boundingRect=this.queryBoundingRect(),this.isHorizontal&&this.buildElHorizontals(),this.isVertical&&this.buildElVerticals()},clear:function(){this.origin=null,this.boundingRect=null,this.lefts=null,this.rights=null,this.tops=null,this.bottoms=null},ensureBuilt:function(){this.origin||this.build()},buildElHorizontals:function(){var e=[],n=[];this.els.each(function(i,r){var s=t(r),o=s.offset().left,a=s.outerWidth();e.push(o),n.push(o+a)}),this.lefts=e,this.rights=n},buildElVerticals:function(){var e=[],n=[];this.els.each(function(i,r){var s=t(r),o=s.offset().top,a=s.outerHeight();e.push(o),n.push(o+a)}),this.tops=e,this.bottoms=n},getHorizontalIndex:function(t){this.ensureBuilt();var e,n=this.lefts,i=this.rights,r=n.length;for(e=0;e=n[e]&&t=n[e]&&t0&&(t=h(this.els.eq(0)),!t.is(document))?f(t):null},isPointInBounds:function(t,e){return this.isLeftInBounds(t)&&this.isTopInBounds(e)},isLeftInBounds:function(t){return!this.boundingRect||t>=this.boundingRect.left&&t=this.boundingRect.top&&t=i*i&&this.handleDistanceSurpassed(t),this.isDragging&&this.handleDrag(e,n,t)},handleDrag:function(t,e,n){this.trigger("drag",t,e,n),this.updateAutoScroll(n)},endDrag:function(t){this.isDragging&&(this.isDragging=!1,this.handleDragEnd(t))},handleDragEnd:function(t){this.trigger("dragEnd",t)},startDelay:function(t){var e=this;this.delay?this.delayTimeoutId=setTimeout(function(){e.handleDelayEnd(t)},this.delay):this.handleDelayEnd(t)},handleDelayEnd:function(t){this.isDelayEnded=!0,this.isDistanceSurpassed&&this.startDrag(t)},handleDistanceSurpassed:function(t){this.isDistanceSurpassed=!0,this.isDelayEnded&&this.startDrag(t)},handleTouchMove:function(t){this.isDragging&&this.shouldCancelTouchScroll&&t.preventDefault(),this.handleMove(t)},handleMouseMove:function(t){this.handleMove(t)},handleTouchScroll:function(t){this.isDragging&&!this.scrollAlwaysKills||this.endInteraction(t,!0)},trigger:function(t){this.options[t]&&this.options[t].apply(this,Array.prototype.slice.call(arguments,1)),this["_"+t]&&this["_"+t].apply(this,Array.prototype.slice.call(arguments,1))}});pe.mixin({isAutoScroll:!1,scrollBounds:null,scrollTopVel:null,scrollLeftVel:null,scrollIntervalId:null,scrollSensitivity:30,scrollSpeed:200,scrollIntervalMs:50,initAutoScroll:function(){var t=this.scrollEl;this.isAutoScroll=this.options.scroll&&t&&!t.is(window)&&!t.is(document),this.isAutoScroll&&this.listenTo(t,"scroll",ut(this.handleDebouncedScroll,100))},destroyAutoScroll:function(){this.endAutoScroll(),this.isAutoScroll&&this.stopListeningTo(this.scrollEl,"scroll")},computeScrollBounds:function(){this.isAutoScroll&&(this.scrollBounds=d(this.scrollEl))},updateAutoScroll:function(t){var e,n,i,r,s=this.scrollSensitivity,o=this.scrollBounds,a=0,l=0;o&&(e=(s-(E(t)-o.top))/s,n=(s-(o.bottom-E(t)))/s,i=(s-(b(t)-o.left))/s,r=(s-(o.right-b(t)))/s,e>=0&&e<=1?a=e*this.scrollSpeed*-1:n>=0&&n<=1&&(a=n*this.scrollSpeed),i>=0&&i<=1?l=i*this.scrollSpeed*-1:r>=0&&r<=1&&(l=r*this.scrollSpeed)),this.setScrollVel(a,l)},setScrollVel:function(t,e){this.scrollTopVel=t,this.scrollLeftVel=e,this.constrainScrollVel(),!this.scrollTopVel&&!this.scrollLeftVel||this.scrollIntervalId||(this.scrollIntervalId=setInterval(lt(this,"scrollIntervalFunc"),this.scrollIntervalMs))},constrainScrollVel:function(){var t=this.scrollEl;this.scrollTopVel<0?t.scrollTop()<=0&&(this.scrollTopVel=0):this.scrollTopVel>0&&t.scrollTop()+t[0].clientHeight>=t[0].scrollHeight&&(this.scrollTopVel=0),this.scrollLeftVel<0?t.scrollLeft()<=0&&(this.scrollLeftVel=0):this.scrollLeftVel>0&&t.scrollLeft()+t[0].clientWidth>=t[0].scrollWidth&&(this.scrollLeftVel=0)},scrollIntervalFunc:function(){var t=this.scrollEl,e=this.scrollIntervalMs/1e3;this.scrollTopVel&&t.scrollTop(t.scrollTop()+this.scrollTopVel*e),this.scrollLeftVel&&t.scrollLeft(t.scrollLeft()+this.scrollLeftVel*e),this.constrainScrollVel(),this.scrollTopVel||this.scrollLeftVel||this.endAutoScroll()},endAutoScroll:function(){this.scrollIntervalId&&(clearInterval(this.scrollIntervalId),this.scrollIntervalId=null,this.handleScrollEnd())},handleDebouncedScroll:function(){this.scrollIntervalId||this.handleScrollEnd()},handleScrollEnd:function(){}});var ve=pe.extend({component:null,origHit:null,hit:null,coordAdjust:null,constructor:function(t,e){pe.call(this,e),this.component=t},handleInteractionStart:function(t){var e,n,i,r=this.subjectEl;this.component.hitsNeeded(),this.computeScrollBounds(),t?(n={left:b(t),top:E(t)},i=n,r&&(e=d(r),i=H(i,e)),this.origHit=this.queryHit(i.left,i.top),r&&this.options.subjectCenter&&(this.origHit&&(e=I(this.origHit,e)||e),i=M(e)),this.coordAdjust=x(i,n)):(this.origHit=null,this.coordAdjust=null),pe.prototype.handleInteractionStart.apply(this,arguments)},handleDragStart:function(t){var e;pe.prototype.handleDragStart.apply(this,arguments),(e=this.queryHit(b(t),E(t)))&&this.handleHitOver(e)},handleDrag:function(t,e,n){var i;pe.prototype.handleDrag.apply(this,arguments),i=this.queryHit(b(n),E(n)),vt(i,this.hit)||(this.hit&&this.handleHitOut(),i&&this.handleHitOver(i))},handleDragEnd:function(){this.handleHitDone(),pe.prototype.handleDragEnd.apply(this,arguments)},handleHitOver:function(t){var e=vt(t,this.origHit);this.hit=t,this.trigger("hitOver",this.hit,e,this.origHit)},handleHitOut:function(){this.hit&&(this.trigger("hitOut",this.hit),this.handleHitDone(),this.hit=null)},handleHitDone:function(){this.hit&&this.trigger("hitDone",this.hit)},handleInteractionEnd:function(){pe.prototype.handleInteractionEnd.apply(this,arguments),this.origHit=null,this.hit=null,this.component.hitsNotNeeded()},handleScrollEnd:function(){pe.prototype.handleScrollEnd.apply(this,arguments),this.isDragging&&(this.component.releaseHits(),this.component.prepareHits())},queryHit:function(t,e){return this.coordAdjust&&(t+=this.coordAdjust.left,e+=this.coordAdjust.top),this.component.queryHit(t,e)}});Wt.touchMouseIgnoreWait=500;var me=dt.extend(se,re,{isTouching:!1,mouseIgnoreDepth:0,handleScrollProxy:null,bind:function(){var e=this;this.listenTo(t(document),{touchstart:this.handleTouchStart,touchcancel:this.handleTouchCancel,touchend:this.handleTouchEnd,mousedown:this.handleMouseDown,mousemove:this.handleMouseMove,mouseup:this.handleMouseUp,click:this.handleClick,selectstart:this.handleSelectStart,contextmenu:this.handleContextMenu}),window.addEventListener("touchmove",this.handleTouchMoveProxy=function(n){e.handleTouchMove(t.Event(n))},{passive:!1}),window.addEventListener("scroll",this.handleScrollProxy=function(n){e.handleScroll(t.Event(n))},!0)},unbind:function(){this.stopListeningTo(t(document)),window.removeEventListener("touchmove",this.handleTouchMoveProxy),window.removeEventListener("scroll",this.handleScrollProxy,!0)},handleTouchStart:function(t){this.stopTouch(t,!0),this.isTouching=!0,this.trigger("touchstart",t)},handleTouchMove:function(t){this.isTouching&&this.trigger("touchmove",t)},handleTouchCancel:function(t){this.isTouching&&(this.trigger("touchcancel",t),this.stopTouch(t))},handleTouchEnd:function(t){this.stopTouch(t)},handleMouseDown:function(t){this.shouldIgnoreMouse()||this.trigger("mousedown",t)},handleMouseMove:function(t){this.shouldIgnoreMouse()||this.trigger("mousemove",t)},handleMouseUp:function(t){this.shouldIgnoreMouse()||this.trigger("mouseup",t)},handleClick:function(t){this.shouldIgnoreMouse()||this.trigger("click",t)},handleSelectStart:function(t){this.trigger("selectstart",t)},handleContextMenu:function(t){this.trigger("contextmenu",t)},handleScroll:function(t){this.trigger("scroll",t)},stopTouch:function(t,e){this.isTouching&&(this.isTouching=!1,this.trigger("touchend",t),e||this.startTouchMouseIgnore())},startTouchMouseIgnore:function(){var t=this,e=Wt.touchMouseIgnoreWait;e&&(this.mouseIgnoreDepth++,setTimeout(function(){t.mouseIgnoreDepth--},e))},shouldIgnoreMouse:function(){return this.isTouching||Boolean(this.mouseIgnoreDepth)}});!function(){var t=null,e=0;me.get=function(){return t||(t=new me,t.bind()),t},me.needed=function(){me.get(),e++},me.unneeded=function(){--e||(t.unbind(),t=null)}}();var ye=dt.extend(se,{options:null,sourceEl:null,el:null,parentEl:null,top0:null,left0:null,y0:null,x0:null,topDelta:null,leftDelta:null,isFollowing:!1,isHidden:!1,isAnimating:!1,constructor:function(e,n){this.options=n=n||{},this.sourceEl=e,this.parentEl=n.parentEl?t(n.parentEl):e.parent()},start:function(e){this.isFollowing||(this.isFollowing=!0,this.y0=E(e),this.x0=b(e),this.topDelta=0,this.leftDelta=0,this.isHidden||this.updatePosition(),S(e)?this.listenTo(t(document),"touchmove",this.handleMove):this.listenTo(t(document),"mousemove",this.handleMove))},stop:function(e,n){function i(){r.isAnimating=!1,r.removeElement(),r.top0=r.left0=null,n&&n()}var r=this,s=this.options.revertDuration;this.isFollowing&&!this.isAnimating&&(this.isFollowing=!1,this.stopListeningTo(t(document)),e&&s&&!this.isHidden?(this.isAnimating=!0,this.el.animate({top:this.top0,left:this.left0},{duration:s,complete:i})):i())},getEl:function(){var t=this.el;return t||(t=this.el=this.sourceEl.clone().addClass(this.options.additionalClass||"").css({position:"absolute",visibility:"",display:this.isHidden?"none":"",margin:0,right:"auto",bottom:"auto",width:this.sourceEl.width(),height:this.sourceEl.height(),opacity:this.options.opacity||"",zIndex:this.options.zIndex}),t.addClass("fc-unselectable"),t.appendTo(this.parentEl)),t},removeElement:function(){this.el&&(this.el.remove(),this.el=null)},updatePosition:function(){var t,e;this.getEl(),null===this.top0&&(t=this.sourceEl.offset(),e=this.el.offsetParent().offset(),this.top0=t.top-e.top,this.left0=t.left-e.left),this.el.css({top:this.top0+this.topDelta,left:this.left0+this.leftDelta})},handleMove:function(t){this.topDelta=E(t)-this.y0,this.leftDelta=b(t)-this.x0,this.isHidden||this.updatePosition()},hide:function(){this.isHidden||(this.isHidden=!0,this.el&&this.el.hide())},show:function(){this.isHidden&&(this.isHidden=!1,this.updatePosition(),this.getEl().show())}}),we=Wt.Scroller=dt.extend({el:null,scrollEl:null,overflowX:null,overflowY:null,constructor:function(t){t=t||{},this.overflowX=t.overflowX||t.overflow||"auto",this.overflowY=t.overflowY||t.overflow||"auto"},render:function(){this.el=this.renderEl(),this.applyOverflow()},renderEl:function(){return this.scrollEl=t('
')},clear:function(){this.setHeight("auto"),this.applyOverflow()},destroy:function(){this.el.remove()},applyOverflow:function(){this.scrollEl.css({"overflow-x":this.overflowX,"overflow-y":this.overflowY})},lockOverflow:function(t){var e=this.overflowX,n=this.overflowY;t=t||this.getScrollbarWidths(),"auto"===e&&(e=t.top||t.bottom||this.scrollEl[0].scrollWidth-1>this.scrollEl[0].clientWidth?"scroll":"hidden"),"auto"===n&&(n=t.left||t.right||this.scrollEl[0].scrollHeight-1>this.scrollEl[0].clientHeight?"scroll":"hidden"),this.scrollEl.css({"overflow-x":e,"overflow-y":n})},setHeight:function(t){this.scrollEl.height(t)},getScrollTop:function(){return this.scrollEl.scrollTop()},setScrollTop:function(t){this.scrollEl.scrollTop(t)},getClientWidth:function(){return this.scrollEl[0].clientWidth},getClientHeight:function(){return this.scrollEl[0].clientHeight},getScrollbarWidths:function(){return p(this.scrollEl)}});yt.prototype.proxyCall=function(t){var e=Array.prototype.slice.call(arguments,1),n=[];return this.items.forEach(function(i){n.push(i[t].apply(i,e))}),n};var De=dt.extend({view:null,component:null,constructor:function(t){this.view=t._getView(),this.component=t},opt:function(t){return this.view.opt(t)},end:function(){}}),be=De.extend({dragListener:null,constructor:function(t){De.call(this,t),this.dragListener=this.buildDragListener()},end:function(){this.dragListener.endInteraction()},bindToEl:function(t){var e=this.component,n=this.dragListener;e.bindDateHandlerToEl(t,"mousedown",function(t){e.shouldIgnoreMouse()||n.startInteraction(t)}),e.bindDateHandlerToEl(t,"touchstart",function(t){e.shouldIgnoreTouch()||n.startInteraction(t)})},buildDragListener:function(){var t,e=this,n=this.component,i=new ve(n,{scroll:this.opt("dragScroll"),interactionStart:function(){t=i.origHit},hitOver:function(e,n,i){n||(t=null)},hitOut:function(){t=null},interactionEnd:function(i,r){var s;!r&&t&&(s=n.getSafeHitFootprint(t))&&e.view.triggerDayClick(s,n.getHitEl(t),i)}});return i.shouldCancelTouchScroll=!1,i.scrollAlwaysKills=!0,i}}),Ee=Wt.DateSelecting=De.extend({dragListener:null,constructor:function(t){De.call(this,t),this.dragListener=this.buildDragListener()},end:function(){this.dragListener.endInteraction()},getDelay:function(){var t=this.opt("selectLongPressDelay");return null==t&&(t=this.opt("longPressDelay")),t},bindToEl:function(t){var e=this,n=this.component,i=this.dragListener;n.bindDateHandlerToEl(t,"mousedown",function(t){e.opt("selectable")&&!n.shouldIgnoreMouse()&&i.startInteraction(t,{distance:e.opt("selectMinDistance")})}),n.bindDateHandlerToEl(t,"touchstart",function(t){e.opt("selectable")&&!n.shouldIgnoreTouch()&&i.startInteraction(t,{delay:e.getDelay()})}),C(t)},buildDragListener:function(){var t,e=this,n=this.component;return new ve(n,{scroll:this.opt("dragScroll"),interactionStart:function(){t=null},dragStart:function(t){e.view.unselect(t)},hitOver:function(i,r,o){var a,l;o&&(a=n.getSafeHitFootprint(o),l=n.getSafeHitFootprint(i),t=a&&l?e.computeSelection(a,l):null,t?n.renderSelectionFootprint(t):!1===t&&s())},hitOut:function(){t=null,n.unrenderSelection()},hitDone:function(){o()},interactionEnd:function(n,i){!i&&t&&e.view.reportSelection(t,n)}})},computeSelection:function(t,e){var n=this.computeSelectionFootprint(t,e);return!(n&&!this.isSelectionFootprintAllowed(n))&&n},computeSelectionFootprint:function(t,e){var n=[t.unzonedRange.startMs,t.unzonedRange.endMs,e.unzonedRange.startMs,e.unzonedRange.endMs];return n.sort(ot),new We(new Ue(n[0],n[3]),t.isAllDay)},isSelectionFootprintAllowed:function(t){return this.component.dateProfile.validUnzonedRange.containsRange(t.unzonedRange)&&this.view.calendar.isSelectionFootprintAllowed(t)}}),Se=Wt.EventDragging=De.extend({eventPointing:null,dragListener:null,isDragging:!1,constructor:function(t,e){De.call(this,t),this.eventPointing=e},end:function(){this.dragListener&&this.dragListener.endInteraction()},getSelectionDelay:function(){var t=this.opt("eventLongPressDelay");return null==t&&(t=this.opt("longPressDelay")),t},bindToEl:function(t){var e=this.component;e.bindSegHandlerToEl(t,"mousedown",this.handleMousedown.bind(this)),e.bindSegHandlerToEl(t,"touchstart",this.handleTouchStart.bind(this))},handleMousedown:function(t,e){this.component.canStartDrag(t,e)&&this.buildDragListener(t).startInteraction(e,{distance:5})},handleTouchStart:function(t,e){var n=this.component,i={delay:this.view.isEventDefSelected(t.footprint.eventDef)?0:this.getSelectionDelay()};n.canStartDrag(t,e)?this.buildDragListener(t).startInteraction(e,i):n.canStartSelection(t,e)&&this.buildSelectListener(t).startInteraction(e,i)},buildSelectListener:function(t){var e=this,n=this.view,i=t.footprint.eventDef,r=t.footprint.eventInstance;if(this.dragListener)return this.dragListener;var s=this.dragListener=new pe({dragStart:function(t){s.isTouch&&!n.isEventDefSelected(i)&&r&&n.selectEventInstance(r)},interactionEnd:function(t){e.dragListener=null}});return s},buildDragListener:function(t){var e,n,i,r=this,a=this.component,l=this.view,u=l.calendar,c=u.eventManager,h=t.el,d=t.footprint.eventDef,f=t.footprint.eventInstance;if(this.dragListener)return this.dragListener;var g=this.dragListener=new ve(l,{scroll:this.opt("dragScroll"),subjectEl:h,subjectCenter:!0,interactionStart:function(i){t.component=a,e=!1,n=new ye(t.el,{additionalClass:"fc-dragging",parentEl:l.el,opacity:g.isTouch?null:r.opt("dragOpacity"),revertDuration:r.opt("dragRevertDuration"),zIndex:2}),n.hide(),n.start(i)},dragStart:function(n){g.isTouch&&!l.isEventDefSelected(d)&&f&&l.selectEventInstance(f),e=!0,r.eventPointing.handleMouseout(t,n),r.segDragStart(t,n),l.hideEventsWithId(t.footprint.eventDef.id)},hitOver:function(e,o,h){var f,p,v,m=!0;t.hit&&(h=t.hit),f=h.component.getSafeHitFootprint(h),p=e.component.getSafeHitFootprint(e),f&&p?(i=r.computeEventDropMutation(f,p,d),i?(v=c.buildMutatedEventInstanceGroup(d.id,i),m=a.isEventInstanceGroupAllowed(v)):m=!1):m=!1,m||(i=null,s()),i&&l.renderDrag(a.eventRangesToEventFootprints(v.sliceRenderRanges(a.dateProfile.renderUnzonedRange,u)),t,g.isTouch)?n.hide():n.show(),o&&(i=null)},hitOut:function(){l.unrenderDrag(t),n.show(),i=null},hitDone:function(){o()},interactionEnd:function(s){delete t.component,n.stop(!i,function(){e&&(l.unrenderDrag(t),r.segDragStop(t,s)),l.showEventsWithId(t.footprint.eventDef.id),i&&l.reportEventDrop(f,i,h,s)}),r.dragListener=null}});return g},segDragStart:function(t,e){this.isDragging=!0,this.component.publiclyTrigger("eventDragStart",{context:t.el[0],args:[t.footprint.getEventLegacy(),e,{},this.view]})},segDragStop:function(t,e){this.isDragging=!1,this.component.publiclyTrigger("eventDragStop",{context:t.el[0],args:[t.footprint.getEventLegacy(),e,{},this.view]})},computeEventDropMutation:function(t,e,n){var i=new rn;return i.setDateMutation(this.computeEventDateMutation(t,e)),i},computeEventDateMutation:function(t,e){var n,i,r=t.unzonedRange.getStart(),s=e.unzonedRange.getStart(),o=!1,a=!1,l=!1;return t.isAllDay!==e.isAllDay&&(o=!0,e.isAllDay?(l=!0,r.stripTime()):a=!0),n=this.component.diffDates(s,r),i=new sn,i.clearEnd=o,i.forceTimed=a,i.forceAllDay=l,i.setDateDelta(n),i}}),Ce=Wt.EventResizing=De.extend({eventPointing:null,dragListener:null,isResizing:!1,constructor:function(t,e){De.call(this,t),this.eventPointing=e},end:function(){this.dragListener&&this.dragListener.endInteraction()},bindToEl:function(t){var e=this.component;e.bindSegHandlerToEl(t,"mousedown",this.handleMouseDown.bind(this)),e.bindSegHandlerToEl(t,"touchstart",this.handleTouchStart.bind(this))},handleMouseDown:function(e,n){this.component.canStartResize(e,n)&&this.buildDragListener(e,t(n.target).is(".fc-start-resizer")).startInteraction(n,{distance:5})},handleTouchStart:function(e,n){this.component.canStartResize(e,n)&&this.buildDragListener(e,t(n.target).is(".fc-start-resizer")).startInteraction(n)},buildDragListener:function(t,e){var n,i,r=this,a=this.component,l=this.view,u=l.calendar,c=u.eventManager,h=t.el,d=t.footprint.eventDef,f=t.footprint.eventInstance;return this.dragListener=new ve(a,{scroll:this.opt("dragScroll"),subjectEl:h,interactionStart:function(){n=!1},dragStart:function(e){n=!0,r.eventPointing.handleMouseout(t,e),r.segResizeStart(t,e)},hitOver:function(n,o,h){var f,g=!0,p=a.getSafeHitFootprint(h),v=a.getSafeHitFootprint(n);p&&v?(i=e?r.computeEventStartResizeMutation(p,v,t.footprint):r.computeEventEndResizeMutation(p,v,t.footprint),i?(f=c.buildMutatedEventInstanceGroup(d.id,i),g=a.isEventInstanceGroupAllowed(f)):g=!1):g=!1,g?i.isEmpty()&&(i=null):(i=null,s()),i&&(l.hideEventsWithId(t.footprint.eventDef.id),l.renderEventResize(a.eventRangesToEventFootprints(f.sliceRenderRanges(a.dateProfile.renderUnzonedRange,u)),t))},hitOut:function(){i=null},hitDone:function(){l.unrenderEventResize(t),l.showEventsWithId(t.footprint.eventDef.id),o()},interactionEnd:function(e){n&&r.segResizeStop(t,e),i&&l.reportEventResize(f,i,h,e),r.dragListener=null}})},segResizeStart:function(t,e){this.isResizing=!0,this.component.publiclyTrigger("eventResizeStart",{context:t.el[0],args:[t.footprint.getEventLegacy(),e,{},this.view]})},segResizeStop:function(t,e){this.isResizing=!1,this.component.publiclyTrigger("eventResizeStop",{context:t.el[0],args:[t.footprint.getEventLegacy(),e,{},this.view]})},computeEventStartResizeMutation:function(t,e,n){var i,r,s=n.componentFootprint.unzonedRange,o=this.component.diffDates(e.unzonedRange.getStart(),t.unzonedRange.getStart());return s.getStart().add(o)s.getStart()&&(i=new sn,i.setEndDelta(o),r=new rn,r.setDateMutation(i),r)}}),Re=Wt.ExternalDropping=De.extend(se,{dragListener:null,isDragging:!1,end:function(){this.dragListener&&this.dragListener.endInteraction()},bindToDocument:function(){this.listenTo(t(document),{dragstart:this.handleDragStart,sortstart:this.handleDragStart})},unbindFromDocument:function(){this.stopListeningTo(t(document))},handleDragStart:function(e,n){var i,r;this.opt("droppable")&&(i=t((n?n.item:null)||e.target),r=this.opt("dropAccept"),(t.isFunction(r)?r.call(i[0],i):i.is(r))&&(this.isDragging||this.listenToExternalDrag(i,e,n)))},listenToExternalDrag:function(t,e,n){var i,r=this,a=this.component,l=this.view,u=wt(t);(r.dragListener=new ve(a,{interactionStart:function(){r.isDragging=!0},hitOver:function(t){var e,n=!0,o=t.component.getSafeHitFootprint(t);o?(i=r.computeExternalDrop(o,u),i?(e=new Je(i.buildInstances()),n=u.eventProps?a.isEventInstanceGroupAllowed(e):a.isExternalInstanceGroupAllowed(e)):n=!1):n=!1,n||(i=null,s()),i&&a.renderDrag(a.eventRangesToEventFootprints(e.sliceRenderRanges(a.dateProfile.renderUnzonedRange,l.calendar)))},hitOut:function(){i=null},hitDone:function(){o(),a.unrenderDrag()},interactionEnd:function(e){i&&l.reportExternalDrop(i,Boolean(u.eventProps),Boolean(u.stick),t,e,n),r.isDragging=!1,r.dragListener=null}})).startDrag(e)},computeExternalDrop:function(e,n){var i,r=this.view.calendar,s=Wt.moment.utc(e.unzonedRange.startMs).stripZone();return e.isAllDay&&(n.startTime?s.time(n.startTime):s.stripTime()),n.duration&&(i=s.clone().add(n.duration)),s=r.applyTimezone(s),i&&(i=r.applyTimezone(i)),$e.parse(t.extend({},n.eventProps,{start:s,end:i}),new on(r))}});Wt.dataAttrPrefix="";var Te=Wt.EventPointing=De.extend({mousedOverSeg:null,bindToEl:function(t){var e=this.component;e.bindSegHandlerToEl(t,"click",this.handleClick.bind(this)),e.bindSegHandlerToEl(t,"mouseenter",this.handleMouseover.bind(this)),e.bindSegHandlerToEl(t,"mouseleave",this.handleMouseout.bind(this))},handleClick:function(t,e){!1===this.component.publiclyTrigger("eventClick",{context:t.el[0],args:[t.footprint.getEventLegacy(),e,this.view]})&&e.preventDefault()},handleMouseover:function(t,e){me.get().shouldIgnoreMouse()||this.mousedOverSeg||(this.mousedOverSeg=t,this.view.isEventDefResizable(t.footprint.eventDef)&&t.el.addClass("fc-allow-mouse-resize"),this.component.publiclyTrigger("eventMouseover",{context:t.el[0],args:[t.footprint.getEventLegacy(),e,this.view]}))},handleMouseout:function(t,e){this.mousedOverSeg&&(this.mousedOverSeg=null,this.view.isEventDefResizable(t.footprint.eventDef)&&t.el.removeClass("fc-allow-mouse-resize"),this.component.publiclyTrigger("eventMouseout",{context:t.el[0],args:[t.footprint.getEventLegacy(),e||{},this.view]}))},end:function(){this.mousedOverSeg&&this.handleMouseout(this.mousedOverSeg)}}),Ie=Wt.StandardInteractionsMixin={dateClickingClass:be,dateSelectingClass:Ee,eventPointingClass:Te,eventDraggingClass:Se,eventResizingClass:Ce,externalDroppingClass:Re},He=Wt.EventRenderer=dt.extend({view:null,component:null,fillRenderer:null,fgSegs:null,bgSegs:null,eventTimeFormat:null,displayEventTime:null,displayEventEnd:null,constructor:function(t,e){this.view=t._getView(),this.component=t,this.fillRenderer=e},opt:function(t){return this.view.opt(t)},rangeUpdated:function(){var t,e;this.eventTimeFormat=this.opt("eventTimeFormat")||this.opt("timeFormat")||this.computeEventTimeFormat(),t=this.opt("displayEventTime"),null==t&&(t=this.computeDisplayEventTime()),e=this.opt("displayEventEnd"),null==e&&(e=this.computeDisplayEventEnd()),this.displayEventTime=t,this.displayEventEnd=e},render:function(t){var e,n,i,r=this.component._getDateProfile(),s=[],o=[];for(e in t)n=t[e],i=n.sliceRenderRanges(r.activeUnzonedRange),n.getEventDef().hasBgRendering()?s.push.apply(s,i):o.push.apply(o,i);this.renderBgRanges(s),this.renderFgRanges(o)},unrender:function(){this.unrenderBgRanges(),this.unrenderFgRanges()},renderFgRanges:function(t){var e=this.component.eventRangesToEventFootprints(t),n=this.component.eventFootprintsToSegs(e);n=this.renderFgSegEls(n),!1!==this.renderFgSegs(n)&&(this.fgSegs=n)},unrenderFgRanges:function(){this.unrenderFgSegs(this.fgSegs||[]),this.fgSegs=null},renderBgRanges:function(t){var e=this.component.eventRangesToEventFootprints(t),n=this.component.eventFootprintsToSegs(e);!1!==this.renderBgSegs(n)&&(this.bgSegs=n)},unrenderBgRanges:function(){this.unrenderBgSegs(),this.bgSegs=null},getSegs:function(){return(this.bgSegs||[]).concat(this.fgSegs||[])},renderFgSegs:function(t){return!1},unrenderFgSegs:function(t){},renderBgSegs:function(t){var e=this;if(!this.fillRenderer)return!1;this.fillRenderer.renderSegs("bgEvent",t,{getClasses:function(t){return e.getBgClasses(t.footprint.eventDef)},getCss:function(t){return{"background-color":e.getBgColor(t.footprint.eventDef)}},filterEl:function(t,n){return e.filterEventRenderEl(t.footprint,n)}})},unrenderBgSegs:function(){this.fillRenderer&&this.fillRenderer.unrender("bgEvent")},renderFgSegEls:function(e,n){var i,r=this,s=this.view.hasPublicHandlers("eventRender"),o="",a=[];if(e.length){for(i=0;i"},attachSegEls:function(t,e){},reportEls:function(e,n){this.elsByFill[e]?this.elsByFill[e]=this.elsByFill[e].add(n):this.elsByFill[e]=t(n)}}),Pe=Wt.HelperRenderer=dt.extend({view:null,component:null,eventRenderer:null,helperEls:null,constructor:function(t,e){this.view=t._getView(),this.component=t,this.eventRenderer=e},renderComponentFootprint:function(t){this.renderEventFootprints([this.fabricateEventFootprint(t)])},renderEventDraggingFootprints:function(t,e,n){this.renderEventFootprints(t,e,"fc-dragging",n?null:this.view.opt("dragOpacity"))},renderEventResizingFootprints:function(t,e,n){this.renderEventFootprints(t,e,"fc-resizing")},renderEventFootprints:function(t,e,n,i){var r,s=this.component.eventFootprintsToSegs(t),o="fc-helper "+(n||"");for(s=this.eventRenderer.renderFgSegEls(s),r=0;r'+i+"
":""+i+""},getAllDayHtml:function(){return this.opt("allDayHtml")||et(this.opt("allDayText"))},getDayClasses:function(t,e){var n,i=this._getView(),r=[];return this.dateProfile.activeUnzonedRange.containsDate(t)?(r.push("fc-"+jt[t.day()]),i.isDateInOtherMonth(t,this.dateProfile)&&r.push("fc-other-month"),n=i.calendar.getNow(),t.isSame(n,"day")?(r.push("fc-today"),!0!==e&&r.push(i.calendar.theme.getClass("today"))):t=this.nextDayThreshold&&s.add(1,"days"),s<=n&&(s=n.clone().add(1,"days")),{start:n,end:s}},isMultiDayRange:function(t){var e=this.computeDayRange(t);return e.end.diff(e.start,"days")>1}});var ke=Wt.InteractiveDateComponent=Fe.extend({dateClickingClass:null,dateSelectingClass:null,eventPointingClass:null,eventDraggingClass:null,eventResizingClass:null,externalDroppingClass:null,dateClicking:null,dateSelecting:null,eventPointing:null,eventDragging:null,eventResizing:null,externalDropping:null,segSelector:".fc-event-container > *",largeUnit:null,constructor:function(){Fe.call(this),this.dateSelectingClass&&(this.dateClicking=new this.dateClickingClass(this)),this.dateSelectingClass&&(this.dateSelecting=new this.dateSelectingClass(this)),this.eventPointingClass&&(this.eventPointing=new this.eventPointingClass(this)),this.eventDraggingClass&&this.eventPointing&&(this.eventDragging=new this.eventDraggingClass(this,this.eventPointing)),this.eventResizingClass&&this.eventPointing&&(this.eventResizing=new this.eventResizingClass(this,this.eventPointing)),this.externalDroppingClass&&(this.externalDropping=new this.externalDroppingClass(this))},setElement:function(t){Fe.prototype.setElement.apply(this,arguments),this.dateClicking&&this.dateClicking.bindToEl(t),this.dateSelecting&&this.dateSelecting.bindToEl(t),this.bindAllSegHandlersToEl(t)},unrender:function(){this.endInteractions(),Fe.prototype.unrender.apply(this,arguments)},executeEventUnrender:function(){this.endInteractions(),Fe.prototype.executeEventUnrender.apply(this,arguments)},bindGlobalHandlers:function(){Fe.prototype.bindGlobalHandlers.apply(this,arguments),this.externalDropping&&this.externalDropping.bindToDocument()},unbindGlobalHandlers:function(){Fe.prototype.unbindGlobalHandlers.apply(this,arguments),this.externalDropping&&this.externalDropping.unbindFromDocument()},bindDateHandlerToEl:function(e,n,i){var r=this;this.el.on(n,function(e){if(!t(e.target).is(r.segSelector+","+r.segSelector+" *,.fc-more,a[data-goto]"))return i.call(r,e)})},bindAllSegHandlersToEl:function(t){[this.eventPointing,this.eventDragging,this.eventResizing].forEach(function(e){e&&e.bindToEl(t)})},bindSegHandlerToEl:function(e,n,i){var r=this;e.on(n,this.segSelector,function(e){var n=t(this).data("fc-seg");if(n&&!r.shouldIgnoreEventPointing())return i.call(r,n,e)})},shouldIgnoreMouse:function(){return me.get().shouldIgnoreMouse()},shouldIgnoreTouch:function(){var t=this._getView();return t.isSelected||t.selectedEvent},shouldIgnoreEventPointing:function(){return this.eventDragging&&this.eventDragging.isDragging||this.eventResizing&&this.eventResizing.isResizing},canStartSelection:function(t,e){return S(e)&&!this.canStartResize(t,e)&&(this.isEventDefDraggable(t.footprint.eventDef)||this.isEventDefResizable(t.footprint.eventDef))},canStartDrag:function(t,e){return!this.canStartResize(t,e)&&this.isEventDefDraggable(t.footprint.eventDef)},canStartResize:function(e,n){var i=this._getView(),r=e.footprint.eventDef;return(!S(n)||i.isEventDefSelected(r))&&this.isEventDefResizable(r)&&t(n.target).is(".fc-resizer")},endInteractions:function(){[this.dateClicking,this.dateSelecting,this.eventPointing,this.eventDragging,this.eventResizing].forEach(function(t){t&&t.end()})},isEventDefDraggable:function(t){return this.isEventDefStartEditable(t)},isEventDefStartEditable:function(t){var e=t.isStartExplicitlyEditable();return null==e&&null==(e=this.opt("eventStartEditable"))&&(e=this.isEventDefGenerallyEditable(t)),e},isEventDefGenerallyEditable:function(t){var e=t.isExplicitlyEditable();return null==e&&(e=this.opt("editable")),e},isEventDefResizableFromStart:function(t){return this.opt("eventResizableFromStart")&&this.isEventDefResizable(t)},isEventDefResizableFromEnd:function(t){return this.isEventDefResizable(t)},isEventDefResizable:function(t){var e=t.isDurationExplicitlyEditable();return null==e&&null==(e=this.opt("eventDurationEditable"))&&(e=this.isEventDefGenerallyEditable(t)),e},diffDates:function(t,e){return this.largeUnit?L(t,e,this.largeUnit):B(t,e)},isEventInstanceGroupAllowed:function(t){var e,n=this._getView(),i=this.dateProfile,r=this.eventRangesToEventFootprints(t.getAllEventRanges());for(e=0;e=e.length?e[e.length-1]+1:e[n]},computeColHeadFormat:function(){return this.rowCnt>1||this.colCnt>10?"ddd":this.colCnt>1?this.opt("dayOfMonthFormat"):"dddd"},sliceRangeByRow:function(t){var e,n,i,r,s,o=this.daysPerRow,a=this.view.computeDayRange(t),l=this.getDateDayIndex(a.start),u=this.getDateDayIndex(a.end.clone().subtract(1,"days")),c=[];for(e=0;e'+this.renderHeadTrHtml()+"
"},renderHeadIntroHtml:function(){return this.renderIntroHtml()},renderHeadTrHtml:function(){return""+(this.isRTL?"":this.renderHeadIntroHtml())+this.renderHeadDateCellsHtml()+(this.isRTL?this.renderHeadIntroHtml():"")+""},renderHeadDateCellsHtml:function(){var t,e,n=[];for(t=0;t1?' colspan="'+e+'"':"")+(n?" "+n:"")+">"+(r?i.buildGotoAnchorHtml({date:t,forceOff:this.rowCnt>1||1===this.colCnt},o):o)+""},renderBgTrHtml:function(t){return""+(this.isRTL?"":this.renderBgIntroHtml(t))+this.renderBgCellsHtml(t)+(this.isRTL?this.renderBgIntroHtml(t):"")+""},renderBgIntroHtml:function(t){return this.renderIntroHtml()},renderBgCellsHtml:function(t){var e,n,i=[];for(e=0;e"},renderIntroHtml:function(){},bookendCells:function(t){var e=this.renderIntroHtml();e&&(this.isRTL?t.append(e):t.prepend(e))}},Ae=Wt.View=ke.extend({type:null,name:null,title:null,calendar:null,viewSpec:null,options:null,renderQueue:null,batchRenderDepth:0,queuedScroll:null,isSelected:!1,selectedEventInstance:null,eventOrderSpecs:null,isHiddenDayHash:null,isNowIndicatorRendered:null,initialNowDate:null,initialNowQueriedMs:null,nowIndicatorTimeoutID:null,nowIndicatorIntervalID:null,constructor:function(t,e){this.calendar=t,this.viewSpec=e,this.type=e.type,this.options=e.options,this.name=this.type,ke.call(this),this.initRenderQueue(),this.initHiddenDays(),this.bindBaseRenderHandlers(),this.eventOrderSpecs=P(this.opt("eventOrder")),this.initialize&&this.initialize()},_getView:function(){return this},opt:function(t){return this.options[t]},initRenderQueue:function(){this.renderQueue=new de({event:this.opt("eventRenderWait")}),this.renderQueue.on("start",this.onRenderQueueStart.bind(this)),this.renderQueue.on("stop",this.onRenderQueueStop.bind(this)),this.on("before:change",this.startBatchRender),this.on("change",this.stopBatchRender)},onRenderQueueStart:function(){this.calendar.freezeContentHeight(),this.addScroll(this.queryScroll())},onRenderQueueStop:function(){this.calendar.updateViewSize()&&this.popScroll(),this.calendar.thawContentHeight()},startBatchRender:function(){this.batchRenderDepth++||this.renderQueue.pause()},stopBatchRender:function(){--this.batchRenderDepth||this.renderQueue.resume()},requestRender:function(t,e,n){this.renderQueue.queue(t,e,n)},whenSizeUpdated:function(t){this.renderQueue.isRunning?this.renderQueue.one("stop",t.bind(this)):t.call(this)},computeTitle:function(t){var e;return e=/^(year|month)$/.test(t.currentRangeUnit)?t.currentUnzonedRange:t.activeUnzonedRange,this.formatRange({start:this.calendar.msToMoment(e.startMs,t.isRangeAllDay),end:this.calendar.msToMoment(e.endMs,t.isRangeAllDay)},t.isRangeAllDay,this.opt("titleFormat")||this.computeTitleFormat(t),this.opt("titleRangeSeparator"))},computeTitleFormat:function(t){var e=t.currentRangeUnit;return"year"==e?"YYYY":"month"==e?this.opt("monthYearFormat"):t.currentUnzonedRange.as("days")>1?"ll":"LL"},setDate:function(t){var e=this.get("dateProfile"),n=this.buildDateProfile(t,null,!0);e&&e.activeUnzonedRange.equals(n.activeUnzonedRange)||this.set("dateProfile",n)},unsetDate:function(){this.unset("dateProfile")},fetchInitialEvents:function(t){var e=this.calendar,n=t.isRangeAllDay&&!this.usesMinMaxTime;return e.requestEvents(e.msToMoment(t.activeUnzonedRange.startMs,n),e.msToMoment(t.activeUnzonedRange.endMs,n))},bindEventChanges:function(){this.listenTo(this.calendar,"eventsReset",this.resetEvents)},unbindEventChanges:function(){this.stopListeningTo(this.calendar,"eventsReset")},setEvents:function(t){this.set("currentEvents",t),this.set("hasEvents",!0)},unsetEvents:function(){this.unset("currentEvents"),this.unset("hasEvents")},resetEvents:function(t){this.startBatchRender(),this.unsetEvents(),this.setEvents(t),this.stopBatchRender()},requestDateRender:function(t){var e=this;this.requestRender(function(){e.executeDateRender(t)},"date","init")},requestDateUnrender:function(){var t=this;this.requestRender(function(){t.executeDateUnrender()},"date","destroy")},executeDateRender:function(t){Fe.prototype.executeDateRender.apply(this,arguments),this.render&&this.render(),this.trigger("datesRendered"),this.addScroll({isDateInit:!0}),this.startNowIndicator()},executeDateUnrender:function(){this.unselect(),this.stopNowIndicator(),this.trigger("before:datesUnrendered"),this.destroy&&this.destroy(),Fe.prototype.executeDateUnrender.apply(this,arguments)},bindBaseRenderHandlers:function(){var t=this;this.on("datesRendered",function(){t.whenSizeUpdated(t.triggerViewRender)}),this.on("before:datesUnrendered",function(){t.triggerViewDestroy()})},triggerViewRender:function(){this.publiclyTrigger("viewRender",{context:this,args:[this,this.el]})},triggerViewDestroy:function(){this.publiclyTrigger("viewDestroy",{context:this,args:[this,this.el]})},requestEventsRender:function(t){var e=this;this.requestRender(function(){e.executeEventRender(t),e.whenSizeUpdated(e.triggerAfterEventsRendered)},"event","init")},requestEventsUnrender:function(){var t=this;this.requestRender(function(){t.triggerBeforeEventsDestroyed(),t.executeEventUnrender()},"event","destroy")},requestBusinessHoursRender:function(t){var e=this;this.requestRender(function(){e.renderBusinessHours(t)},"businessHours","init")},requestBusinessHoursUnrender:function(){var t=this;this.requestRender(function(){t.unrenderBusinessHours()},"businessHours","destroy")},bindGlobalHandlers:function(){ke.prototype.bindGlobalHandlers.apply(this,arguments),this.listenTo(me.get(),{touchstart:this.processUnselect,mousedown:this.handleDocumentMousedown})},unbindGlobalHandlers:function(){ke.prototype.unbindGlobalHandlers.apply(this,arguments),this.stopListeningTo(me.get())},startNowIndicator:function(){var t,n,i,r=this;this.opt("nowIndicator")&&(t=this.getNowIndicatorUnit())&&(n=lt(this,"updateNowIndicator"),this.initialNowDate=this.calendar.getNow(),this.initialNowQueriedMs=+new Date,i=this.initialNowDate.clone().startOf(t).add(1,t)-this.initialNowDate,this.nowIndicatorTimeoutID=setTimeout(function(){r.nowIndicatorTimeoutID=null,n(),i=+e.duration(1,t),i=Math.max(100,i),r.nowIndicatorIntervalID=setInterval(n,i)},i))},updateNowIndicator:function(){this.isDatesRendered&&this.initialNowDate&&(this.unrenderNowIndicator(),this.renderNowIndicator(this.initialNowDate.clone().add(new Date-this.initialNowQueriedMs)),this.isNowIndicatorRendered=!0)},stopNowIndicator:function(){this.isNowIndicatorRendered&&(this.nowIndicatorTimeoutID&&(clearTimeout(this.nowIndicatorTimeoutID),this.nowIndicatorTimeoutID=null),this.nowIndicatorIntervalID&&(clearTimeout(this.nowIndicatorIntervalID),this.nowIndicatorIntervalID=null),this.unrenderNowIndicator(),this.isNowIndicatorRendered=!1)},updateSize:function(t,e,n){this.setHeight?this.setHeight(t,e):ke.prototype.updateSize.apply(this,arguments),this.updateNowIndicator()},addScroll:function(e){var n=this.queuedScroll||(this.queuedScroll={});t.extend(n,e)},popScroll:function(){this.applyQueuedScroll(),this.queuedScroll=null},applyQueuedScroll:function(){this.queuedScroll&&this.applyScroll(this.queuedScroll)},queryScroll:function(){var e={};return this.isDatesRendered&&t.extend(e,this.queryDateScroll()),e},applyScroll:function(e){e.isDateInit&&this.isDatesRendered&&t.extend(e,this.computeInitialDateScroll()),this.isDatesRendered&&this.applyDateScroll(e)},computeInitialDateScroll:function(){return{}},queryDateScroll:function(){return{}},applyDateScroll:function(t){},reportEventDrop:function(t,n,i,r){var s=this.calendar.eventManager,o=s.mutateEventsWithId(t.def.id,n,this.calendar),a=n.dateMutation;a&&(t.dateProfile=a.buildNewDateProfile(t.dateProfile,this.calendar)),this.triggerEventDrop(t,a&&a.dateDelta||e.duration(),o,i,r)},triggerEventDrop:function(t,e,n,i,r){this.publiclyTrigger("eventDrop",{context:i[0],args:[t.toLegacy(),e,n,r,{},this]})},reportExternalDrop:function(t,e,n,i,r,s){e&&this.calendar.eventManager.addEventDef(t,n),this.triggerExternalDrop(t,e,i,r,s)},triggerExternalDrop:function(t,e,n,i,r){this.publiclyTrigger("drop",{context:n[0],args:[t.dateProfile.start.clone(),i,r,this]}),e&&this.publiclyTrigger("eventReceive",{context:this,args:[t.buildInstance().toLegacy(),this]})},reportEventResize:function(t,e,n,i){var r=this.calendar.eventManager,s=r.mutateEventsWithId(t.def.id,e,this.calendar);t.dateProfile=e.dateMutation.buildNewDateProfile(t.dateProfile,this.calendar),this.triggerEventResize(t,e.dateMutation.endDelta,s,n,i)},triggerEventResize:function(t,e,n,i,r){this.publiclyTrigger("eventResize",{context:i[0],args:[t.toLegacy(),e,n,r,{},this]})},select:function(t,e){this.unselect(e),this.renderSelectionFootprint(t),this.reportSelection(t,e)},renderSelectionFootprint:function(t,e){this.renderSelection?this.renderSelection(t.toLegacy(this.calendar)):ke.prototype.renderSelectionFootprint.apply(this,arguments)},reportSelection:function(t,e){this.isSelected=!0,this.triggerSelect(t,e)},triggerSelect:function(t,e){var n=this.calendar.footprintToDateProfile(t);this.publiclyTrigger("select",{context:this,args:[n.start,n.end,e,this]})},unselect:function(t){this.isSelected&&(this.isSelected=!1,this.destroySelection&&this.destroySelection(),this.unrenderSelection(),this.publiclyTrigger("unselect",{context:this,args:[t,this]}))},selectEventInstance:function(t){this.selectedEventInstance&&this.selectedEventInstance===t||(this.unselectEventInstance(),this.getEventSegs().forEach(function(e){e.footprint.eventInstance===t&&e.el&&e.el.addClass("fc-selected")}),this.selectedEventInstance=t)},unselectEventInstance:function(){this.selectedEventInstance&&(this.getEventSegs().forEach(function(t){t.el&&t.el.removeClass("fc-selected")}),this.selectedEventInstance=null)},isEventDefSelected:function(t){return this.selectedEventInstance&&this.selectedEventInstance.def.id===t.id},handleDocumentMousedown:function(t){D(t)&&this.processUnselect(t)},processUnselect:function(t){this.processRangeUnselect(t),this.processEventUnselect(t)},processRangeUnselect:function(e){var n;this.isSelected&&this.opt("unselectAuto")&&((n=this.opt("unselectCancel"))&&t(e.target).closest(n).length||this.unselect(e))},processEventUnselect:function(e){this.selectedEventInstance&&(t(e.target).closest(".fc-selected").length||this.unselectEventInstance())},triggerBaseRendered:function(){this.publiclyTrigger("viewRender",{context:this,args:[this,this.el]})},triggerBaseUnrendered:function(){this.publiclyTrigger("viewDestroy",{context:this,args:[this,this.el]})},triggerDayClick:function(t,e,n){var i=this.calendar.footprintToDateProfile(t);this.publiclyTrigger("dayClick",{context:e,args:[i.start,n,this]})}});Ae.watch("displayingDates",["isInDom","dateProfile"],function(t){this.requestDateRender(t.dateProfile)},function(){this.requestDateUnrender()}),Ae.watch("displayingBusinessHours",["displayingDates","businessHourGenerator"],function(t){this.requestBusinessHoursRender(t.businessHourGenerator)},function(){this.requestBusinessHoursUnrender()}),Ae.watch("initialEvents",["dateProfile"],function(t){return this.fetchInitialEvents(t.dateProfile)}),Ae.watch("bindingEvents",["initialEvents"],function(t){this.setEvents(t.initialEvents),this.bindEventChanges()},function(){this.unbindEventChanges(),this.unsetEvents()}),Ae.watch("displayingEvents",["displayingDates","hasEvents"],function(){this.requestEventsRender(this.get("currentEvents"))},function(){this.requestEventsUnrender()}),Ae.watch("title",["dateProfile"],function(t){return this.title=this.computeTitle(t.dateProfile)}),Ae.watch("legacyDateProps",["dateProfile"],function(t){var e=this.calendar,n=t.dateProfile;this.start=e.msToMoment(n.activeUnzonedRange.startMs,n.isRangeAllDay),this.end=e.msToMoment(n.activeUnzonedRange.endMs,n.isRangeAllDay),this.intervalStart=e.msToMoment(n.currentUnzonedRange.startMs,n.isRangeAllDay),this.intervalEnd=e.msToMoment(n.currentUnzonedRange.endMs,n.isRangeAllDay)}),Ae.mixin({usesMinMaxTime:!1,start:null,end:null,intervalStart:null,intervalEnd:null,buildPrevDateProfile:function(t){var e=this.get("dateProfile"),n=t.clone().startOf(e.currentRangeUnit).subtract(e.dateIncrement);return this.buildDateProfile(n,-1)},buildNextDateProfile:function(t){var e=this.get("dateProfile"),n=t.clone().startOf(e.currentRangeUnit).add(e.dateIncrement);return this.buildDateProfile(n,1)},buildDateProfile:function(t,n,i){var r,s,o,a,l,u,c=!t.hasTime(),h=null,d=null;return r=this.buildValidRange(),r=this.trimHiddenDays(r),i&&(t=this.calendar.msToUtcMoment(r.constrainDate(t),c)),s=this.buildCurrentRangeInfo(t,n),o=/^(year|month|week|day)$/.test(s.unit),a=this.buildRenderRange(this.trimHiddenDays(s.unzonedRange),s.unit,o),a=this.trimHiddenDays(a),l=a.clone(),this.opt("showNonCurrentDates")||(l=l.intersect(s.unzonedRange)),h=e.duration(this.opt("minTime")),d=e.duration(this.opt("maxTime")),l=this.adjustActiveRange(l,h,d),l=l.intersect(r),l&&(t=this.calendar.msToUtcMoment(l.constrainDate(t),c)),u=s.unzonedRange.intersectsWith(r),{validUnzonedRange:r,currentUnzonedRange:s.unzonedRange,currentRangeUnit:s.unit,isRangeAllDay:o,activeUnzonedRange:l,renderUnzonedRange:a,minTime:h,maxTime:d,isValid:u,date:t,dateIncrement:this.buildDateIncrement(s.duration)}},buildValidRange:function(){return this.getUnzonedRangeOption("validRange",this.calendar.getNow())||new Ue},buildCurrentRangeInfo:function(t,e){var n,i=null,r=null,s=null;return this.viewSpec.duration?(i=this.viewSpec.duration,r=this.viewSpec.durationUnit,s=this.buildRangeFromDuration(t,e,i,r)):(n=this.opt("dayCount"))?(r="day",s=this.buildRangeFromDayCount(t,e,n)):(s=this.buildCustomVisibleRange(t))?r=O(s.getStart(),s.getEnd()):(i=this.getFallbackDuration(),r=O(i),s=this.buildRangeFromDuration(t,e,i,r)),{duration:i,unit:r,unzonedRange:s}},getFallbackDuration:function(){return e.duration({days:1})},adjustActiveRange:function(t,e,n){var i=t.getStart(),r=t.getEnd();return this.usesMinMaxTime&&(e<0&&i.time(0).add(e),n>864e5&&r.time(n-864e5)),new Ue(i,r)},buildRangeFromDuration:function(t,n,i,r){var s,o,a,l=this.opt("dateAlignment"),u=t.clone();return i.as("days")<=1&&this.isHiddenDay(u)&&(u=this.skipHiddenDays(u,n),u.startOf("day")),l||(o=this.opt("dateIncrement"),o?(a=e.duration(o),l=a").prependTo(n),this.initToolbars(),this.renderHeader(),this.renderFooter(),this.renderView(this.opt("defaultView")),this.opt("handleWindowResize")&&t(window).resize(this.windowResizeProxy=ut(this.windowResize.bind(this),this.opt("windowResizeDelay")))},destroy:function(){this.view&&this.clearView(),this.toolbarsManager.proxyCall("removeElement"),this.contentEl.remove(),this.el.removeClass("fc fc-ltr fc-rtl"),this.optionsModel.unwatch("settingTheme"),this.optionsModel.unwatch("settingBusinessHourGenerator"),this.el.off(".fc"),this.windowResizeProxy&&(t(window).unbind("resize",this.windowResizeProxy),this.windowResizeProxy=null),me.unneeded()},elementVisible:function(){return this.el.is(":visible")},bindViewHandlers:function(t){var e=this;t.watch("titleForCalendar",["title"],function(n){t===e.view&&e.setToolbarsTitle(n.title)}),t.watch("dateProfileForCalendar",["dateProfile"],function(n){t===e.view&&(e.currentDate=n.dateProfile.date,e.updateToolbarButtons(n.dateProfile))})},unbindViewHandlers:function(t){this.stopListeningTo(t),t.unwatch("titleForCalendar"),t.unwatch("dateProfileForCalendar")},renderView:function(e){var n,i=this.view;this.freezeContentHeight(),i&&e&&i.type!==e&&this.clearView(),!this.view&&e&&(n=this.view=this.viewsByType[e]||(this.viewsByType[e]=this.instantiateView(e)),this.bindViewHandlers(n),n.setElement(t("
").appendTo(this.contentEl)),this.toolbarsManager.proxyCall("activateButton",e)),this.view&&(this.view.get("businessHourGenerator")!==this.businessHourGenerator&&this.view.set("businessHourGenerator",this.businessHourGenerator),this.view.setDate(this.currentDate)),this.thawContentHeight()},clearView:function(){var t=this.view;this.toolbarsManager.proxyCall("deactivateButton",t.type),this.unbindViewHandlers(t),t.removeElement(),this.view=null},reinitView:function(){var t=this.view,e=t.queryScroll();this.freezeContentHeight(),this.clearView(),this.calcSize(),this.renderView(t.type),this.view.applyScroll(e),this.thawContentHeight()},getSuggestedViewHeight:function(){return null===this.suggestedViewHeight&&this.calcSize(),this.suggestedViewHeight},isHeightAuto:function(){return"auto"===this.opt("contentHeight")||"auto"===this.opt("height")},updateViewSize:function(t){var e,n=this.view;if(!this.ignoreUpdateViewSize&&n)return t&&(this.calcSize(),e=n.queryScroll()),this.ignoreUpdateViewSize++,n.updateSize(this.getSuggestedViewHeight(),this.isHeightAuto(),t),this.ignoreUpdateViewSize--,t&&n.applyScroll(e),!0},calcSize:function(){this.elementVisible()&&this._calcSize()},_calcSize:function(){var t=this.opt("contentHeight"),e=this.opt("height");this.suggestedViewHeight="number"==typeof t?t:"function"==typeof t?t():"number"==typeof e?e-this.queryToolbarsHeight():"function"==typeof e?e()-this.queryToolbarsHeight():"parent"===e?this.el.parent().height()-this.queryToolbarsHeight():Math.round(this.contentEl.width()/Math.max(this.opt("aspectRatio"),.5))},windowResize:function(t){t.target===window&&this.view&&this.view.isDatesRendered&&this.updateViewSize(!0)&&this.publiclyTrigger("windowResize",[this.view])},freezeContentHeight:function(){this.freezeContentHeightDepth++||this.forceFreezeContentHeight()},forceFreezeContentHeight:function(){this.contentEl.css({width:"100%",height:this.contentEl.height(),overflow:"hidden"})},thawContentHeight:function(){this.freezeContentHeightDepth--,this.contentEl.css({width:"",height:"",overflow:""}),this.freezeContentHeightDepth&&this.forceFreezeContentHeight()}}),Le.mixin({header:null,footer:null,toolbarsManager:null,initToolbars:function(){this.header=new bt(this,this.computeHeaderOptions()),this.footer=new bt(this,this.computeFooterOptions()),this.toolbarsManager=new yt([this.header,this.footer])},computeHeaderOptions:function(){return{extraClasses:"fc-header-toolbar",layout:this.opt("header")}},computeFooterOptions:function(){return{extraClasses:"fc-footer-toolbar",layout:this.opt("footer")}},renderHeader:function(){var t=this.header;t.setToolbarOptions(this.computeHeaderOptions()),t.render(),t.el&&this.el.prepend(t.el)},renderFooter:function(){var t=this.footer;t.setToolbarOptions(this.computeFooterOptions()),t.render(),t.el&&this.el.append(t.el)},setToolbarsTitle:function(t){this.toolbarsManager.proxyCall("updateTitle",t)},updateToolbarButtons:function(t){var e=this.getNow(),n=this.view,i=n.buildDateProfile(e),r=n.buildPrevDateProfile(this.currentDate),s=n.buildNextDateProfile(this.currentDate);this.toolbarsManager.proxyCall(i.isValid&&!t.currentUnzonedRange.containsDate(e)?"enableButton":"disableButton","today"),this.toolbarsManager.proxyCall(r.isValid?"enableButton":"disableButton","prev"),this.toolbarsManager.proxyCall(s.isValid?"enableButton":"disableButton","next")},queryToolbarsHeight:function(){return this.toolbarsManager.items.reduce(function(t,e){return t+(e.el?e.el.outerHeight(!0):0)},0)}}),Le.prototype.isEventInstanceGroupAllowed=function(t){var e,n=t.getEventDef(),i=this.eventRangesToEventFootprints(t.getAllEventRanges()),r=this.getPeerEventInstances(n),s=r.map(Pt),o=this.eventRangesToEventFootprints(s),a=n.getConstraint(),l=n.getOverlap(),u=this.opt("eventAllow");for(e=0;et.startMs)&&(null===this.startMs||null===t.endMs||this.startMs=this.startMs)&&(null===this.endMs||null!==t.endMs&&t.endMs<=this.endMs)},containsDate:function(t){var e=t.valueOf();return(null===this.startMs||e>=this.startMs)&&(null===this.endMs||e=this.endMs&&(e=this.endMs-1),e},equals:function(t){return this.startMs===t.startMs&&this.endMs===t.endMs},clone:function(){var t=new Ue(this.startMs,this.endMs);return t.isStart=this.isStart,t.isEnd=this.isEnd,t},getStart:function(){if(null!==this.startMs)return Wt.moment.utc(this.startMs).stripZone()},getEnd:function(){if(null!==this.endMs)return Wt.moment.utc(this.endMs).stripZone()},as:function(t){return e.utc(this.endMs).diff(e.utc(this.startMs),t,!0)}}),We=Wt.ComponentFootprint=dt.extend({unzonedRange:null,isAllDay:!1,constructor:function(t,e){this.unzonedRange=t,this.isAllDay=e},toLegacy:function(t){return{start:t.msToMoment(this.unzonedRange.startMs,this.isAllDay),end:t.msToMoment(this.unzonedRange.endMs,this.isAllDay)}}}),_e=dt.extend(re,{start:null,end:null,timezone:null,unzonedRange:null,requestsByUid:null,pendingCnt:0,freezeDepth:0,stuntedReleaseCnt:0,releaseCnt:0,eventDefsByUid:null,eventDefsById:null,eventInstanceGroupsById:null,constructor:function(t,e,n){this.start=t,this.end=e,this.timezone=n,this.unzonedRange=new Ue(t.clone().stripZone(),e.clone().stripZone()),this.requestsByUid={},this.eventDefsByUid={},this.eventDefsById={},this.eventInstanceGroupsById={}},isWithinRange:function(t,e){return!t.isBefore(this.start)&&!e.isAfter(this.end)},requestSources:function(t){this.freeze();for(var e=0;e
'),s=r.find("tr"),a>0&&s.append(''),s.append(n.el.attr("colspan",l-a)),l'),this.component.bookendCells(s),r}}),mn=He.extend({dayGrid:null,rowStructs:null,constructor:function(t){He.apply(this,arguments),this.dayGrid=t},renderBgRanges:function(e){e=t.grep(e,function(t){return t.eventDef.isAllDay()}),He.prototype.renderBgRanges.call(this,e)},renderFgSegs:function(e){var n=this.rowStructs=this.renderSegRows(e);this.dayGrid.rowEls.each(function(e,i){t(i).find(".fc-content-skeleton > table").append(n[e].tbodyEl)})},unrenderFgSegs:function(){for(var t,e=this.rowStructs||[];t=e.pop();)t.tbodyEl.remove();this.rowStructs=null},renderSegRows:function(t){var e,n,i=[];for(e=this.groupSegRows(t),n=0;n"),a.append(c)),v[r][o]=c,m[r][o]=c,o++}var r,s,o,a,l,u,c,h=this.dayGrid.colCnt,d=this.buildSegLevels(n),f=Math.max(1,d.length),g=t(""),p=[],v=[],m=[];for(r=0;r"),p.push([]),v.push([]),m.push([]),s)for(l=0;l').append(u.el),u.leftCol!=u.rightCol?c.attr("colspan",u.rightCol-u.leftCol+1):m[r][o]=c;o<=u.rightCol;)v[r][o]=c,p[r][o]=u,o++;a.append(c)}i(h),this.dayGrid.bookendCells(a),g.append(a)}return{row:e,tbodyEl:g,cellMatrix:v,segMatrix:p,segLevels:d,segs:n}},buildSegLevels:function(t){var e,n,i,r=[];for(this.sortEventSegs(t),e=0;e'+et(n)+""),i=''+(et(s.title||"")||" ")+"",'
'+(this.isRTL?i+" "+d:d+" "+i)+"
"+(l?'
':"")+(u?'
':"")+""}}),yn=Pe.extend({renderSegs:function(e,n){var i,r=[];return i=this.eventRenderer.renderSegRows(e),this.component.rowEls.each(function(e,s){var o,a,l=t(s),u=t('
');n&&n.row===e?a=n.el.position().top:(o=l.find(".fc-content-skeleton tbody"),o.length||(o=l.find(".fc-content-skeleton table")),a=o.position().top),u.css("top",a).find("table").append(i[e].tbodyEl),l.append(u),r.push(u[0])}),t(r)}}),wn=Wt.DayGrid=ke.extend(Ie,Be,{eventRendererClass:mn,businessHourRendererClass:Me,helperRendererClass:yn,fillRendererClass:vn,view:null,helperRenderer:null,cellWeekNumbersVisible:!1,bottomCoordPadding:0,headContainerEl:null,rowEls:null,cellEls:null,rowCoordCache:null,colCoordCache:null,isRigid:!1,hasAllDayBusinessHours:!0,constructor:function(t){this.view=t,ke.call(this)},componentFootprintToSegs:function(t){var e,n,i=this.sliceRangeByRow(t.unzonedRange);for(e=0;e
'+this.renderBgTrHtml(t)+'
'+(this.getIsNumbersVisible()?""+this.renderNumberTrHtml(t)+"":"")+"
"},getIsNumbersVisible:function(){return this.getIsDayNumbersVisible()||this.cellWeekNumbersVisible},getIsDayNumbersVisible:function(){return this.rowCnt>1},renderNumberTrHtml:function(t){return""+(this.isRTL?"":this.renderNumberIntroHtml(t))+this.renderNumberCellsHtml(t)+(this.isRTL?this.renderNumberIntroHtml(t):"")+""},renderNumberIntroHtml:function(t){return this.renderIntroHtml()},renderNumberCellsHtml:function(t){var e,n,i=[];for(e=0;e",this.cellWeekNumbersVisible&&t.day()==n&&(r+=i.buildGotoAnchorHtml({date:t,type:"week"},{class:"fc-week-number"},t.format("w"))),o&&(r+=i.buildGotoAnchorHtml(t,{class:"fc-day-number"},t.date())),r+=""):""},prepareHits:function(){this.colCoordCache.build(),this.rowCoordCache.build(),this.rowCoordCache.bottoms[this.rowCnt-1]+=this.bottomCoordPadding},releaseHits:function(){this.colCoordCache.clear(),this.rowCoordCache.clear()},queryHit:function(t,e){if(this.colCoordCache.isLeftInBounds(t)&&this.rowCoordCache.isTopInBounds(e)){var n=this.colCoordCache.getHorizontalIndex(t),i=this.rowCoordCache.getVerticalIndex(e);if(null!=i&&null!=n)return this.getCellHit(i,n)}},getHitFootprint:function(t){var e=this.getCellRange(t.row,t.col);return new We(new Ue(e.start,e.end),!0)},getHitEl:function(t){return this.getCellEl(t.row,t.col)},getCellHit:function(t,e){return{row:t,col:e,component:this,left:this.colCoordCache.getLeftOffset(e),right:this.colCoordCache.getRightOffset(e),top:this.rowCoordCache.getTopOffset(t),bottom:this.rowCoordCache.getBottomOffset(t)}},getCellEl:function(t,e){return this.cellEls.eq(t*this.colCnt+e)},unrenderEvents:function(){this.removeSegPopover(),ke.prototype.unrenderEvents.apply(this,arguments)},getOwnEventSegs:function(){return ke.prototype.getOwnEventSegs.apply(this,arguments).concat(this.popoverSegs||[])},renderDrag:function(t,e,n){var i;for(i=0;i td > :first-child").each(n),r.position().top+s>a)return i;return!1},limitRow:function(e,n){function i(i){for(;E").append(y),d.append(m),b.push(m[0])),E++}var r,s,o,a,l,u,c,h,d,f,g,p,v,m,y,w=this,D=this.eventRenderer.rowStructs[e],b=[],E=0;if(n&&n').attr("rowspan",f),u=h[p],y=this.renderMoreLink(e,l.leftCol+p,[l].concat(u)),m=t("
").append(y),v.append(m),g.push(v[0]),b.push(v[0]);d.addClass("fc-limited").after(t(g)),o.push(d[0])}}i(this.colCnt),D.moreEls=t(b),D.limitedEls=t(o)}},unlimitRow:function(t){var e=this.eventRenderer.rowStructs[t];e.moreEls&&(e.moreEls.remove(),e.moreEls=null),e.limitedEls&&(e.limitedEls.removeClass("fc-limited"),e.limitedEls=null)},renderMoreLink:function(e,n,i){var r=this,s=this.view;return t('').text(this.getMoreLinkText(i.length)).on("click",function(o){var a=r.opt("eventLimitClick"),l=r.getCellDate(e,n),u=t(this),c=r.getCellEl(e,n),h=r.getCellSegs(e,n),d=r.resliceDaySegs(h,l),f=r.resliceDaySegs(i,l);"function"==typeof a&&(a=r.publiclyTrigger("eventLimitClick",{context:s,args:[{date:l.clone(),dayEl:c,moreEl:u,segs:d,hiddenSegs:f},o,s]})),"popover"===a?r.showSegPopover(e,n,u,d):"string"==typeof a&&s.calendar.zoomTo(l,a)})},showSegPopover:function(t,e,n,i){var r,s,o=this,a=this.view,l=n.parent();r=1==this.rowCnt?a.el:this.rowEls.eq(t),s={className:"fc-more-popover "+a.calendar.theme.getClass("popover"),content:this.renderSegPopoverContent(t,e,i),parentEl:a.el,top:r.offset().top,autoHide:!0,viewportConstrain:this.opt("popoverViewportConstrain"),hide:function(){o.popoverSegs&&o.triggerBeforeEventSegsDestroyed(o.popoverSegs),o.segPopover.removeElement(),o.segPopover=null,o.popoverSegs=null}},this.isRTL?s.right=l.offset().left+l.outerWidth()+1:s.left=l.offset().left-1,this.segPopover=new fe(s),this.segPopover.show(),this.bindAllSegHandlersToEl(this.segPopover.el),this.triggerAfterEventSegsRendered(i)},renderSegPopoverContent:function(e,n,i){var r,s=this.view,o=s.calendar.theme,a=this.getCellDate(e,n).format(this.opt("dayPopoverFormat")),l=t('
'+et(a)+'
'),u=l.find(".fc-event-container");for(i=this.eventRenderer.renderFgSegEls(i,!0),this.popoverSegs=i,r=0;r').appendTo(e),this.el.find(".fc-body > tr > td").append(e),this.dayGrid.headContainerEl=this.el.find(".fc-head-container"),this.dayGrid.setElement(n)},unrenderSkeleton:function(){this.dayGrid.removeElement(),this.scroller.destroy()},renderSkeletonHtml:function(){var t=this.calendar.theme;return''+(this.opt("columnHeader")?'':"")+'
 
'},weekNumberStyleAttr:function(){return null!==this.weekNumberWidth?'style="width:'+this.weekNumberWidth+'px"':""},hasRigidRows:function(){var t=this.opt("eventLimit");return t&&"number"!=typeof t},updateSize:function(t,e,n){var s,o,a=this.opt("eventLimit"),l=this.dayGrid.headContainerEl.find(".fc-row");if(!this.dayGrid.rowEls)return void(e||(s=this.computeScrollerHeight(t),this.scroller.setHeight(s)));Ae.prototype.updateSize.apply(this,arguments),this.dayGrid.colWeekNumbersVisible&&(this.weekNumberWidth=u(this.el.find(".fc-week-number"))),this.scroller.clear(),r(l),this.dayGrid.removeSegPopover(),a&&"number"==typeof a&&this.dayGrid.limitRows(a),s=this.computeScrollerHeight(t),this.setGridHeight(s,e),a&&"number"!=typeof a&&this.dayGrid.limitRows(a),e||(this.scroller.setHeight(s),o=this.scroller.getScrollbarWidths(),(o.left||o.right)&&(i(l,o),s=this.computeScrollerHeight(t),this.scroller.setHeight(s)),this.scroller.lockOverflow(o))},computeScrollerHeight:function(t){return t-c(this.el,this.scroller.el)},setGridHeight:function(t,e){e?l(this.dayGrid.rowEls):a(this.dayGrid.rowEls,t,!0)},computeInitialDateScroll:function(){return{top:0}},queryDateScroll:function(){return{top:this.scroller.getScrollTop()}},applyDateScroll:function(t){void 0!==t.top&&this.scroller.setScrollTop(t.top)}}),bn={colWeekNumbersVisible:!1,renderHeadIntroHtml:function(){var t=this.view;return this.colWeekNumbersVisible?'"+et(this.opt("weekNumberTitle"))+"":""},renderNumberIntroHtml:function(t){var e=this.view,n=this.getCellDate(t,0);return this.colWeekNumbersVisible?'"+e.buildGotoAnchorHtml({date:n,type:"week",forceOff:1===this.colCnt},n.format("w"))+"":""},renderBgIntroHtml:function(){var t=this.view;return this.colWeekNumbersVisible?'":""},renderIntroHtml:function(){var t=this.view +;return this.colWeekNumbersVisible?'":""},getIsNumbersVisible:function(){return wn.prototype.getIsNumbersVisible.apply(this,arguments)||this.colWeekNumbersVisible}},En=Wt.MonthView=Dn.extend({buildRenderRange:function(t,e,n){var i,r=Dn.prototype.buildRenderRange.apply(this,arguments),s=this.calendar.msToUtcMoment(r.startMs,n),o=this.calendar.msToUtcMoment(r.endMs,n);return this.isFixedWeeks()&&(i=Math.ceil(o.diff(s,"weeks",!0)),o.add(6-i,"weeks")),new Ue(s,o)},setGridHeight:function(t,e){e&&(t*=this.rowCnt/6),a(this.dayGrid.rowEls,t,!e)},isFixedWeeks:function(){return this.opt("fixedWeekCount")},isDateInOtherMonth:function(t,n){return t.month()!==e.utc(n.currentUnzonedRange.startMs).month()}});_t.basic={class:Dn},_t.basicDay={type:"basic",duration:{days:1}},_t.basicWeek={type:"basic",duration:{weeks:1}},_t.month={class:En,duration:{months:1},defaults:{fixedWeekCount:!0}};var Sn=xe.extend({attachSegEls:function(t,e){var n,i=this.component;return"bgEvent"===t?n=i.bgContainerEls:"businessHours"===t?n=i.businessContainerEls:"highlight"===t&&(n=i.highlightContainerEls),i.updateSegVerticals(e),i.attachSegsByCol(i.groupSegsByCol(e),n),e.map(function(t){return t.el[0]})}}),Cn=He.extend({timeGrid:null,constructor:function(t){He.apply(this,arguments),this.timeGrid=t},renderFgSegs:function(t){this.renderFgSegsIntoContainers(t,this.timeGrid.fgContainerEls)},renderFgSegsIntoContainers:function(t,e){var n,i;for(n=this.timeGrid.groupSegsByCol(t),i=0;i
'+(n?'
'+et(n)+"
":"")+(u.title?'
'+et(u.title)+"
":"")+'
'+(d?'
':"")+""},updateFgSegCoords:function(t){this.timeGrid.computeSegVerticals(t),this.computeFgSegHorizontals(t),this.timeGrid.assignSegVerticals(t),this.assignFgSegHorizontals(t)},computeFgSegHorizontals:function(t){var e,n,i;if(this.sortEventSegs(t),e=Lt(t),Ot(e),n=e[0]){for(i=0;i=0;n--)if(i=e.duration(Pn[n]),r=U(i,t),at(r)&&r>1)return i;return e.duration(t)},renderDates:function(t){this.dateProfile=t,this.updateDayTable(),this.renderSlats(),this.renderColumns()},renderSkeleton:function(){var t=this.view.calendar.theme;this.el.html('
'),this.bottomRuleEl=this.el.find("hr")},renderSlats:function(){var t=this.view.calendar.theme;this.slatContainerEl=this.el.find("> .fc-slats").html(''+this.renderSlatRowHtml()+"
"),this.slatEls=this.slatContainerEl.find("tr"),this.slatCoordCache=new ge({els:this.slatEls,isVertical:!0})},renderSlatRowHtml:function(){for(var t,n,i,r=this.view,s=r.calendar,o=s.theme,a=this.isRTL,l=this.dateProfile,u="",c=e.duration(+l.minTime),h=e.duration(0);c"+(n?""+et(t.format(this.labelFormat))+"":"")+"",u+='"+(a?"":i)+''+(a?i:"")+"",c.add(this.slotDuration),h.add(this.slotDuration);return u},renderColumns:function(){var t=this.dateProfile,e=this.view.calendar.theme;this.dayRanges=this.dayDates.map(function(e){return new Ue(e.clone().add(t.minTime),e.clone().add(t.maxTime))}),this.headContainerEl&&this.headContainerEl.html(this.renderHeadHtml()),this.el.find("> .fc-bg").html(''+this.renderBgTrHtml(0)+"
"),this.colEls=this.el.find(".fc-day, .fc-disabled-day"),this.colCoordCache=new ge({els:this.colEls,isHorizontal:!0}),this.renderContentSkeleton()},renderContentSkeleton:function(){var e,n,i="";for(e=0;e
';n=t('
'+i+"
"),this.colContainerEls=n.find(".fc-content-col"),this.helperContainerEls=n.find(".fc-helper-container"),this.fgContainerEls=n.find(".fc-event-container:not(.fc-helper-container)"),this.bgContainerEls=n.find(".fc-bgevent-container"),this.highlightContainerEls=n.find(".fc-highlight-container"),this.businessContainerEls=n.find(".fc-business-container"),this.bookendCells(n.find("tr")),this.el.append(n)},groupSegsByCol:function(t){var e,n=[];for(e=0;e
').css("top",r).appendTo(this.colContainerEls.eq(i[n].col))[0]);i.length>0&&s.push(t('
').css("top",r).appendTo(this.el.find(".fc-content-skeleton"))[0]),this.nowIndicatorEls=t(s)},unrenderNowIndicator:function(){this.nowIndicatorEls&&(this.nowIndicatorEls.remove(),this.nowIndicatorEls=null)},updateSize:function(t,e,n){ke.prototype.updateSize.apply(this,arguments),this.slatCoordCache.build(),n&&this.updateSegVerticals([].concat(this.eventRenderer.getSegs(),this.businessSegs||[]))},getTotalSlatHeight:function(){return this.slatContainerEl.outerHeight()},computeDateTop:function(t,n){return this.computeTimeTop(e.duration(t-n.clone().stripTime()))},computeTimeTop:function(t){var e,n,i=this.slatEls.length,r=this.dateProfile,s=(t-r.minTime)/this.slotDuration;return s=Math.max(0,s),s=Math.min(i,s),e=Math.floor(s),e=Math.min(e,i-1),n=s-e,this.slatCoordCache.getTopPosition(e)+this.slatCoordCache.getHeight(e)*n},updateSegVerticals:function(t){this.computeSegVerticals(t),this.assignSegVerticals(t)},computeSegVerticals:function(t){var e,n,i,r=this.opt("agendaEventMinHeight");for(e=0;e').appendTo(e),this.el.find(".fc-body > tr > td").append(e),this.timeGrid.headContainerEl=this.el.find(".fc-head-container"),this.timeGrid.setElement(n),this.dayGrid&&(this.dayGrid.setElement(this.el.find(".fc-day-grid")),this.dayGrid.bottomCoordPadding=this.dayGrid.el.next("hr").outerHeight())},unrenderSkeleton:function(){this.timeGrid.removeElement(),this.dayGrid&&this.dayGrid.removeElement(),this.scroller.destroy()},renderSkeletonHtml:function(){var t=this.calendar.theme;return''+(this.opt("columnHeader")?'':"")+'
 
'+(this.dayGrid?'

':"")+"
"},axisStyleAttr:function(){return null!==this.axisWidth?'style="width:'+this.axisWidth+'px"':""},getNowIndicatorUnit:function(){return this.timeGrid.getNowIndicatorUnit()},updateSize:function(t,e,n){var s,o,a;if(Ae.prototype.updateSize.apply(this,arguments),this.axisWidth=u(this.el.find(".fc-axis")),!this.timeGrid.colEls)return void(e||(o=this.computeScrollerHeight(t),this.scroller.setHeight(o)));var l=this.el.find(".fc-row:not(.fc-scroller *)");this.timeGrid.bottomRuleEl.hide(),this.scroller.clear(),r(l),this.dayGrid&&(this.dayGrid.removeSegPopover(),s=this.opt("eventLimit"),s&&"number"!=typeof s&&(s=xn),s&&this.dayGrid.limitRows(s)),e||(o=this.computeScrollerHeight(t),this.scroller.setHeight(o),a=this.scroller.getScrollbarWidths(),(a.left||a.right)&&(i(l,a),o=this.computeScrollerHeight(t),this.scroller.setHeight(o)),this.scroller.lockOverflow(a),this.timeGrid.getTotalSlatHeight()"+e.buildGotoAnchorHtml({date:i,type:"week",forceOff:this.colCnt>1},et(t))+""):'"},renderBgIntroHtml:function(){var t=this.view;return'"},renderIntroHtml:function(){return'"}},Mn={renderBgIntroHtml:function(){var t=this.view;return'"+t.getAllDayHtml()+""},renderIntroHtml:function(){return'"}},xn=5,Pn=[{hours:1},{minutes:30},{minutes:15},{seconds:30},{seconds:15}];_t.agenda={class:In,defaults:{allDaySlot:!0,slotDuration:"00:30:00",slotEventOverlap:!0}},_t.agendaDay={type:"agenda",duration:{days:1}},_t.agendaWeek={type:"agenda",duration:{weeks:1}};var zn=Wt.ListView=Ae.extend({segSelector:".fc-list-item",scroller:null,contentEl:null,dayDates:null,dayRanges:null,constructor:function(){Ae.apply(this,arguments),this.scroller=new we({overflowX:"hidden",overflowY:"auto"})},renderSkeleton:function(){this.el.addClass("fc-list-view "+this.calendar.theme.getClass("listView")),this.scroller.render(),this.scroller.el.appendTo(this.el),this.contentEl=this.scroller.scrollEl},unrenderSkeleton:function(){this.scroller.destroy()},updateSize:function(t,e,n){this.scroller.setHeight(this.computeScrollerHeight(t))},computeScrollerHeight:function(t){return t-c(this.el,this.scroller.el)},renderDates:function(t){for(var e=this.calendar,n=e.msToUtcMoment(t.renderUnzonedRange.startMs,!0),i=e.msToUtcMoment(t.renderUnzonedRange.endMs,!0),r=[],s=[];n'+(this.displayEventTime?''+(e||"")+"":"")+'"+et(o.title||"")+""},computeEventTimeFormat:function(){return this.opt("mediumTimeFormat")}}),eventPointingClass:Te.extend({handleClick:function(e,n){var i;Te.prototype.handleClick.apply(this,arguments),t(n.target).closest("a[href]").length||(i=e.footprint.eventDef.url)&&!n.isDefaultPrevented()&&(window.location.href=i)}}),renderEmptyMessage:function(){this.contentEl.html('
'+et(this.opt("noEventsMessage"))+"
")},renderSegList:function(e){var n,i,r,s=this.groupSegsByDay(e),o=t('
'),a=o.find("tbody");for(n=0;n'+(e?this.buildGotoAnchorHtml(t,{class:"fc-list-heading-main"},et(t.format(e))):"")+(n?this.buildGotoAnchorHtml(t,{class:"fc-list-heading-alt"},et(t.format(n))):"")+""}});return _t.list={class:zn,buttonTextKey:"list",defaults:{buttonText:"list",listDayFormat:"LL",noEventsMessage:"No events to display"}},_t.listDay={type:"list",duration:{days:1},defaults:{listDayFormat:"dddd"}},_t.listWeek={type:"list",duration:{weeks:1},defaults:{listDayFormat:"dddd",listDayAltFormat:"LL"}},_t.listMonth={type:"list",duration:{month:1},defaults:{listDayAltFormat:"dddd"}},_t.listYear={type:"list",duration:{year:1},defaults:{listDayAltFormat:"dddd"}},Wt}); \ No newline at end of file diff --git a/app/assets/javascripts/jquery.dataTables.js b/app/assets/javascripts/jquery.dataTables.js deleted file mode 100755 index 1d8a220ba..000000000 --- a/app/assets/javascripts/jquery.dataTables.js +++ /dev/null @@ -1,12099 +0,0 @@ -/** - * @summary DataTables - * @description Paginate, search and sort HTML tables - * @version 1.9.4 - * @file jquery.dataTables.js - * @author Allan Jardine (www.sprymedia.co.uk) - * @contact www.sprymedia.co.uk/contact - * - * @copyright Copyright 2008-2012 Allan Jardine, all rights reserved. - * - * This source file is free software, under either the GPL v2 license or a - * BSD style license, available at: - * http://datatables.net/license_gpl2 - * http://datatables.net/license_bsd - * - * This source file is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. - * - * For details please refer to: http://www.datatables.net - */ - -/*jslint evil: true, undef: true, browser: true */ -/*globals $, jQuery,define,_fnExternApiFunc,_fnInitialise,_fnInitComplete,_fnLanguageCompat,_fnAddColumn,_fnColumnOptions,_fnAddData,_fnCreateTr,_fnGatherData,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnServerParams,_fnAddOptionsHtml,_fnFeatureHtmlTable,_fnScrollDraw,_fnAdjustColumnSizing,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnBuildSearchArray,_fnBuildSearchRow,_fnFilterCreateSearch,_fnDataToSearch,_fnSort,_fnSortAttachListener,_fnSortingClasses,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnFeatureHtmlLength,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnNodeToDataIndex,_fnVisbleColumns,_fnCalculateEnd,_fnConvertToWidth,_fnCalculateColumnWidths,_fnScrollingWidthAdjust,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnDetectType,_fnSettingsFromNode,_fnGetDataMaster,_fnGetTrNodes,_fnGetTdNodes,_fnEscapeRegex,_fnDeleteIndex,_fnReOrderIndex,_fnColumnOrdering,_fnLog,_fnClearTable,_fnSaveState,_fnLoadState,_fnCreateCookie,_fnReadCookie,_fnDetectHeader,_fnGetUniqueThs,_fnScrollBarWidth,_fnApplyToChildren,_fnMap,_fnGetRowData,_fnGetCellData,_fnSetCellData,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnApplyColumnDefs,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnJsonString,_fnRender,_fnNodeToColumnIndex,_fnInfoMacros,_fnBrowserDetect,_fnGetColumns*/ - -(/** @lends */function( window, document, undefined ) { - -(function( factory ) { - "use strict"; - - // Define as an AMD module if possible - if ( typeof define === 'function' && define.amd ) - { - define( ['jquery'], factory ); - } - /* Define using browser globals otherwise - * Prevent multiple instantiations if the script is loaded twice - */ - else if ( jQuery && !jQuery.fn.dataTable ) - { - factory( jQuery ); - } -} -(/** @lends */function( $ ) { - "use strict"; - /** - * DataTables is a plug-in for the jQuery Javascript library. It is a - * highly flexible tool, based upon the foundations of progressive - * enhancement, which will add advanced interaction controls to any - * HTML table. For a full list of features please refer to - * DataTables.net. - * - * Note that the DataTable object is not a global variable but is - * aliased to jQuery.fn.DataTable and jQuery.fn.dataTable through which - * it may be accessed. - * - * @class - * @param {object} [oInit={}] Configuration object for DataTables. Options - * are defined by {@link DataTable.defaults} - * @requires jQuery 1.3+ - * - * @example - * // Basic initialisation - * $(document).ready( function { - * $('#example').dataTable(); - * } ); - * - * @example - * // Initialisation with configuration options - in this case, disable - * // pagination and sorting. - * $(document).ready( function { - * $('#example').dataTable( { - * "bPaginate": false, - * "bSort": false - * } ); - * } ); - */ - var DataTable = function( oInit ) - { - - - /** - * Add a column to the list used for the table with default values - * @param {object} oSettings dataTables settings object - * @param {node} nTh The th element for this column - * @memberof DataTable#oApi - */ - function _fnAddColumn( oSettings, nTh ) - { - var oDefaults = DataTable.defaults.columns; - var iCol = oSettings.aoColumns.length; - var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, { - "sSortingClass": oSettings.oClasses.sSortable, - "sSortingClassJUI": oSettings.oClasses.sSortJUI, - "nTh": nTh ? nTh : document.createElement('th'), - "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '', - "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol], - "mData": oDefaults.mData ? oDefaults.oDefaults : iCol - } ); - oSettings.aoColumns.push( oCol ); - - /* Add a column specific filter */ - if ( oSettings.aoPreSearchCols[ iCol ] === undefined || oSettings.aoPreSearchCols[ iCol ] === null ) - { - oSettings.aoPreSearchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch ); - } - else - { - var oPre = oSettings.aoPreSearchCols[ iCol ]; - - /* Don't require that the user must specify bRegex, bSmart or bCaseInsensitive */ - if ( oPre.bRegex === undefined ) - { - oPre.bRegex = true; - } - - if ( oPre.bSmart === undefined ) - { - oPre.bSmart = true; - } - - if ( oPre.bCaseInsensitive === undefined ) - { - oPre.bCaseInsensitive = true; - } - } - - /* Use the column options function to initialise classes etc */ - _fnColumnOptions( oSettings, iCol, null ); - } - - - /** - * Apply options for a column - * @param {object} oSettings dataTables settings object - * @param {int} iCol column index to consider - * @param {object} oOptions object with sType, bVisible and bSearchable etc - * @memberof DataTable#oApi - */ - function _fnColumnOptions( oSettings, iCol, oOptions ) - { - var oCol = oSettings.aoColumns[ iCol ]; - - /* User specified column options */ - if ( oOptions !== undefined && oOptions !== null ) - { - /* Backwards compatibility for mDataProp */ - if ( oOptions.mDataProp && !oOptions.mData ) - { - oOptions.mData = oOptions.mDataProp; - } - - if ( oOptions.sType !== undefined ) - { - oCol.sType = oOptions.sType; - oCol._bAutoType = false; - } - - $.extend( oCol, oOptions ); - _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" ); - - /* iDataSort to be applied (backwards compatibility), but aDataSort will take - * priority if defined - */ - if ( oOptions.iDataSort !== undefined ) - { - oCol.aDataSort = [ oOptions.iDataSort ]; - } - _fnMap( oCol, oOptions, "aDataSort" ); - } - - /* Cache the data get and set functions for speed */ - var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null; - var mData = _fnGetObjectDataFn( oCol.mData ); - - oCol.fnGetData = function (oData, sSpecific) { - var innerData = mData( oData, sSpecific ); - - if ( oCol.mRender && (sSpecific && sSpecific !== '') ) - { - return mRender( innerData, sSpecific, oData ); - } - return innerData; - }; - oCol.fnSetData = _fnSetObjectDataFn( oCol.mData ); - - /* Feature sorting overrides column specific when off */ - if ( !oSettings.oFeatures.bSort ) - { - oCol.bSortable = false; - } - - /* Check that the class assignment is correct for sorting */ - if ( !oCol.bSortable || - ($.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1) ) - { - oCol.sSortingClass = oSettings.oClasses.sSortableNone; - oCol.sSortingClassJUI = ""; - } - else if ( $.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1 ) - { - oCol.sSortingClass = oSettings.oClasses.sSortable; - oCol.sSortingClassJUI = oSettings.oClasses.sSortJUI; - } - else if ( $.inArray('asc', oCol.asSorting) != -1 && $.inArray('desc', oCol.asSorting) == -1 ) - { - oCol.sSortingClass = oSettings.oClasses.sSortableAsc; - oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIAscAllowed; - } - else if ( $.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) != -1 ) - { - oCol.sSortingClass = oSettings.oClasses.sSortableDesc; - oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIDescAllowed; - } - } - - - /** - * Adjust the table column widths for new data. Note: you would probably want to - * do a redraw after calling this function! - * @param {object} oSettings dataTables settings object - * @memberof DataTable#oApi - */ - function _fnAdjustColumnSizing ( oSettings ) - { - /* Not interested in doing column width calculation if auto-width is disabled */ - if ( oSettings.oFeatures.bAutoWidth === false ) - { - return false; - } - - _fnCalculateColumnWidths( oSettings ); - for ( var i=0 , iLen=oSettings.aoColumns.length ; i
')[0]; - oSettings.nTable.parentNode.insertBefore( nHolding, oSettings.nTable ); - - /* - * All DataTables are wrapped in a div - */ - oSettings.nTableWrapper = $('
')[0]; - oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling; - - /* Track where we want to insert the option */ - var nInsertNode = oSettings.nTableWrapper; - - /* Loop over the user set positioning and place the elements as needed */ - var aDom = oSettings.sDom.split(''); - var nTmp, iPushFeature, cOption, nNewNode, cNext, sAttr, j; - for ( var i=0 ; i
')[0]; - - /* Check to see if we should append an id and/or a class name to the container */ - cNext = aDom[i+1]; - if ( cNext == "'" || cNext == '"' ) - { - sAttr = ""; - j = 2; - while ( aDom[i+j] != cNext ) - { - sAttr += aDom[i+j]; - j++; - } - - /* Replace jQuery UI constants */ - if ( sAttr == "H" ) - { - sAttr = oSettings.oClasses.sJUIHeader; - } - else if ( sAttr == "F" ) - { - sAttr = oSettings.oClasses.sJUIFooter; - } - - /* The attribute can be in the format of "#id.class", "#id" or "class" This logic - * breaks the string into parts and applies them as needed - */ - if ( sAttr.indexOf('.') != -1 ) - { - var aSplit = sAttr.split('.'); - nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1); - nNewNode.className = aSplit[1]; - } - else if ( sAttr.charAt(0) == "#" ) - { - nNewNode.id = sAttr.substr(1, sAttr.length-1); - } - else - { - nNewNode.className = sAttr; - } - - i += j; /* Move along the position array */ - } - - nInsertNode.appendChild( nNewNode ); - nInsertNode = nNewNode; - } - else if ( cOption == '>' ) - { - /* End container div */ - nInsertNode = nInsertNode.parentNode; - } - else if ( cOption == 'l' && oSettings.oFeatures.bPaginate && oSettings.oFeatures.bLengthChange ) - { - /* Length */ - nTmp = _fnFeatureHtmlLength( oSettings ); - iPushFeature = 1; - } - else if ( cOption == 'f' && oSettings.oFeatures.bFilter ) - { - /* Filter */ - nTmp = _fnFeatureHtmlFilter( oSettings ); - iPushFeature = 1; - } - else if ( cOption == 'r' && oSettings.oFeatures.bProcessing ) - { - /* pRocessing */ - nTmp = _fnFeatureHtmlProcessing( oSettings ); - iPushFeature = 1; - } - else if ( cOption == 't' ) - { - /* Table */ - nTmp = _fnFeatureHtmlTable( oSettings ); - iPushFeature = 1; - } - else if ( cOption == 'i' && oSettings.oFeatures.bInfo ) - { - /* Info */ - nTmp = _fnFeatureHtmlInfo( oSettings ); - iPushFeature = 1; - } - else if ( cOption == 'p' && oSettings.oFeatures.bPaginate ) - { - /* Pagination */ - nTmp = _fnFeatureHtmlPaginate( oSettings ); - iPushFeature = 1; - } - else if ( DataTable.ext.aoFeatures.length !== 0 ) - { - /* Plug-in features */ - var aoFeatures = DataTable.ext.aoFeatures; - for ( var k=0, kLen=aoFeatures.length ; k') : - sSearchStr==="" ? '' : sSearchStr+' '; - - var nFilter = document.createElement( 'div' ); - nFilter.className = oSettings.oClasses.sFilter; - nFilter.innerHTML = ''; - if ( !oSettings.aanFeatures.f ) - { - nFilter.id = oSettings.sTableId+'_filter'; - } - - var jqFilter = $('input[type="text"]', nFilter); - - // Store a reference to the input element, so other input elements could be - // added to the filter wrapper if needed (submit button for example) - nFilter._DT_Input = jqFilter[0]; - - jqFilter.val( oPreviousSearch.sSearch.replace('"','"') ); - jqFilter.bind( 'keyup.DT', function(e) { - /* Update all other filter input elements for the new display */ - var n = oSettings.aanFeatures.f; - var val = this.value==="" ? "" : this.value; // mental IE8 fix :-( - - for ( var i=0, iLen=n.length ; i=0 ; i-- ) - { - var sData = _fnDataToSearch( _fnGetCellData( oSettings, oSettings.aiDisplay[i], iColumn, 'filter' ), - oSettings.aoColumns[iColumn].sType ); - if ( ! rpSearch.test( sData ) ) - { - oSettings.aiDisplay.splice( i, 1 ); - iIndexCorrector++; - } - } - } - - - /** - * Filter the data table based on user input and draw the table - * @param {object} oSettings dataTables settings object - * @param {string} sInput string to filter on - * @param {int} iForce optional - force a research of the master array (1) or not (undefined or 0) - * @param {bool} bRegex treat as a regular expression or not - * @param {bool} bSmart perform smart filtering or not - * @param {bool} bCaseInsensitive Do case insenstive matching or not - * @memberof DataTable#oApi - */ - function _fnFilter( oSettings, sInput, iForce, bRegex, bSmart, bCaseInsensitive ) - { - var i; - var rpSearch = _fnFilterCreateSearch( sInput, bRegex, bSmart, bCaseInsensitive ); - var oPrevSearch = oSettings.oPreviousSearch; - - /* Check if we are forcing or not - optional parameter */ - if ( !iForce ) - { - iForce = 0; - } - - /* Need to take account of custom filtering functions - always filter */ - if ( DataTable.ext.afnFiltering.length !== 0 ) - { - iForce = 1; - } - - /* - * If the input is blank - we want the full data set - */ - if ( sInput.length <= 0 ) - { - oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length); - oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); - } - else - { - /* - * We are starting a new search or the new search string is smaller - * then the old one (i.e. delete). Search from the master array - */ - if ( oSettings.aiDisplay.length == oSettings.aiDisplayMaster.length || - oPrevSearch.sSearch.length > sInput.length || iForce == 1 || - sInput.indexOf(oPrevSearch.sSearch) !== 0 ) - { - /* Nuke the old display array - we are going to rebuild it */ - oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length); - - /* Force a rebuild of the search array */ - _fnBuildSearchArray( oSettings, 1 ); - - /* Search through all records to populate the search array - * The the oSettings.aiDisplayMaster and asDataSearch arrays have 1 to 1 - * mapping - */ - for ( i=0 ; i').html(sSearch).text(); - } - - // Strip newline characters - return sSearch.replace( /[\n\r]/g, " " ); - } - - /** - * Build a regular expression object suitable for searching a table - * @param {string} sSearch string to search for - * @param {bool} bRegex treat as a regular expression or not - * @param {bool} bSmart perform smart filtering or not - * @param {bool} bCaseInsensitive Do case insensitive matching or not - * @returns {RegExp} constructed object - * @memberof DataTable#oApi - */ - function _fnFilterCreateSearch( sSearch, bRegex, bSmart, bCaseInsensitive ) - { - var asSearch, sRegExpString; - - if ( bSmart ) - { - /* Generate the regular expression to use. Something along the lines of: - * ^(?=.*?\bone\b)(?=.*?\btwo\b)(?=.*?\bthree\b).*$ - */ - asSearch = bRegex ? sSearch.split( ' ' ) : _fnEscapeRegex( sSearch ).split( ' ' ); - sRegExpString = '^(?=.*?'+asSearch.join( ')(?=.*?' )+').*$'; - return new RegExp( sRegExpString, bCaseInsensitive ? "i" : "" ); - } - else - { - sSearch = bRegex ? sSearch : _fnEscapeRegex( sSearch ); - return new RegExp( sSearch, bCaseInsensitive ? "i" : "" ); - } - } - - - /** - * Convert raw data into something that the user can search on - * @param {string} sData data to be modified - * @param {string} sType data type - * @returns {string} search string - * @memberof DataTable#oApi - */ - function _fnDataToSearch ( sData, sType ) - { - if ( typeof DataTable.ext.ofnSearch[sType] === "function" ) - { - return DataTable.ext.ofnSearch[sType]( sData ); - } - else if ( sData === null ) - { - return ''; - } - else if ( sType == "html" ) - { - return sData.replace(/[\r\n]/g," ").replace( /<.*?>/g, "" ); - } - else if ( typeof sData === "string" ) - { - return sData.replace(/[\r\n]/g," "); - } - return sData; - } - - - /** - * scape a string such that it can be used in a regular expression - * @param {string} sVal string to escape - * @returns {string} escaped string - * @memberof DataTable#oApi - */ - function _fnEscapeRegex ( sVal ) - { - var acEscape = [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ]; - var reReplace = new RegExp( '(\\' + acEscape.join('|\\') + ')', 'g' ); - return sVal.replace(reReplace, '\\$1'); - } - - - /** - * Generate the node required for the info display - * @param {object} oSettings dataTables settings object - * @returns {node} Information element - * @memberof DataTable#oApi - */ - function _fnFeatureHtmlInfo ( oSettings ) - { - var nInfo = document.createElement( 'div' ); - nInfo.className = oSettings.oClasses.sInfo; - - /* Actions that are to be taken once only for this feature */ - if ( !oSettings.aanFeatures.i ) - { - /* Add draw callback */ - oSettings.aoDrawCallback.push( { - "fn": _fnUpdateInfo, - "sName": "information" - } ); - - /* Add id */ - nInfo.id = oSettings.sTableId+'_info'; - } - oSettings.nTable.setAttribute( 'aria-describedby', oSettings.sTableId+'_info' ); - - return nInfo; - } - - - /** - * Update the information elements in the display - * @param {object} oSettings dataTables settings object - * @memberof DataTable#oApi - */ - function _fnUpdateInfo ( oSettings ) - { - /* Show information about the table */ - if ( !oSettings.oFeatures.bInfo || oSettings.aanFeatures.i.length === 0 ) - { - return; - } - - var - oLang = oSettings.oLanguage, - iStart = oSettings._iDisplayStart+1, - iEnd = oSettings.fnDisplayEnd(), - iMax = oSettings.fnRecordsTotal(), - iTotal = oSettings.fnRecordsDisplay(), - sOut; - - if ( iTotal === 0 ) - { - /* Empty record set */ - sOut = oLang.sInfoEmpty; - } - else { - /* Normal record set */ - sOut = oLang.sInfo; - } - - if ( iTotal != iMax ) - { - /* Record set after filtering */ - sOut += ' ' + oLang.sInfoFiltered; - } - - // Convert the macros - sOut += oLang.sInfoPostFix; - sOut = _fnInfoMacros( oSettings, sOut ); - - if ( oLang.fnInfoCallback !== null ) - { - sOut = oLang.fnInfoCallback.call( oSettings.oInstance, - oSettings, iStart, iEnd, iMax, iTotal, sOut ); - } - - var n = oSettings.aanFeatures.i; - for ( var i=0, iLen=n.length ; i'; - var i, iLen; - var aLengthMenu = oSettings.aLengthMenu; - - if ( aLengthMenu.length == 2 && typeof aLengthMenu[0] === 'object' && - typeof aLengthMenu[1] === 'object' ) - { - for ( i=0, iLen=aLengthMenu[0].length ; i'+aLengthMenu[1][i]+''; - } - } - else - { - for ( i=0, iLen=aLengthMenu.length ; i'+aLengthMenu[i]+''; - } - } - sStdMenu += ''; - - var nLength = document.createElement( 'div' ); - if ( !oSettings.aanFeatures.l ) - { - nLength.id = oSettings.sTableId+'_length'; - } - nLength.className = oSettings.oClasses.sLength; - nLength.innerHTML = ''; - - /* - * Set the length to the current display length - thanks to Andrea Pavlovic for this fix, - * and Stefan Skopnik for fixing the fix! - */ - $('select option[value="'+oSettings._iDisplayLength+'"]', nLength).attr("selected", true); - - $('select', nLength).bind( 'change.DT', function(e) { - var iVal = $(this).val(); - - /* Update all other length options for the new display */ - var n = oSettings.aanFeatures.l; - for ( i=0, iLen=n.length ; i oSettings.aiDisplay.length || - oSettings._iDisplayLength == -1 ) - { - oSettings._iDisplayEnd = oSettings.aiDisplay.length; - } - else - { - oSettings._iDisplayEnd = oSettings._iDisplayStart + oSettings._iDisplayLength; - } - } - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Note that most of the paging logic is done in - * DataTable.ext.oPagination - */ - - /** - * Generate the node required for default pagination - * @param {object} oSettings dataTables settings object - * @returns {node} Pagination feature node - * @memberof DataTable#oApi - */ - function _fnFeatureHtmlPaginate ( oSettings ) - { - if ( oSettings.oScroll.bInfinite ) - { - return null; - } - - var nPaginate = document.createElement( 'div' ); - nPaginate.className = oSettings.oClasses.sPaging+oSettings.sPaginationType; - - DataTable.ext.oPagination[ oSettings.sPaginationType ].fnInit( oSettings, nPaginate, - function( oSettings ) { - _fnCalculateEnd( oSettings ); - _fnDraw( oSettings ); - } - ); - - /* Add a draw callback for the pagination on first instance, to update the paging display */ - if ( !oSettings.aanFeatures.p ) - { - oSettings.aoDrawCallback.push( { - "fn": function( oSettings ) { - DataTable.ext.oPagination[ oSettings.sPaginationType ].fnUpdate( oSettings, function( oSettings ) { - _fnCalculateEnd( oSettings ); - _fnDraw( oSettings ); - } ); - }, - "sName": "pagination" - } ); - } - return nPaginate; - } - - - /** - * Alter the display settings to change the page - * @param {object} oSettings dataTables settings object - * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last" - * or page number to jump to (integer) - * @returns {bool} true page has changed, false - no change (no effect) eg 'first' on page 1 - * @memberof DataTable#oApi - */ - function _fnPageChange ( oSettings, mAction ) - { - var iOldStart = oSettings._iDisplayStart; - - if ( typeof mAction === "number" ) - { - oSettings._iDisplayStart = mAction * oSettings._iDisplayLength; - if ( oSettings._iDisplayStart > oSettings.fnRecordsDisplay() ) - { - oSettings._iDisplayStart = 0; - } - } - else if ( mAction == "first" ) - { - oSettings._iDisplayStart = 0; - } - else if ( mAction == "previous" ) - { - oSettings._iDisplayStart = oSettings._iDisplayLength>=0 ? - oSettings._iDisplayStart - oSettings._iDisplayLength : - 0; - - /* Correct for under-run */ - if ( oSettings._iDisplayStart < 0 ) - { - oSettings._iDisplayStart = 0; - } - } - else if ( mAction == "next" ) - { - if ( oSettings._iDisplayLength >= 0 ) - { - /* Make sure we are not over running the display array */ - if ( oSettings._iDisplayStart + oSettings._iDisplayLength < oSettings.fnRecordsDisplay() ) - { - oSettings._iDisplayStart += oSettings._iDisplayLength; - } - } - else - { - oSettings._iDisplayStart = 0; - } - } - else if ( mAction == "last" ) - { - if ( oSettings._iDisplayLength >= 0 ) - { - var iPages = parseInt( (oSettings.fnRecordsDisplay()-1) / oSettings._iDisplayLength, 10 ) + 1; - oSettings._iDisplayStart = (iPages-1) * oSettings._iDisplayLength; - } - else - { - oSettings._iDisplayStart = 0; - } - } - else - { - _fnLog( oSettings, 0, "Unknown paging action: "+mAction ); - } - $(oSettings.oInstance).trigger('page', oSettings); - - return iOldStart != oSettings._iDisplayStart; - } - - - - /** - * Generate the node required for the processing node - * @param {object} oSettings dataTables settings object - * @returns {node} Processing element - * @memberof DataTable#oApi - */ - function _fnFeatureHtmlProcessing ( oSettings ) - { - var nProcessing = document.createElement( 'div' ); - - if ( !oSettings.aanFeatures.r ) - { - nProcessing.id = oSettings.sTableId+'_processing'; - } - nProcessing.innerHTML = oSettings.oLanguage.sProcessing; - nProcessing.className = oSettings.oClasses.sProcessing; - oSettings.nTable.parentNode.insertBefore( nProcessing, oSettings.nTable ); - - return nProcessing; - } - - - /** - * Display or hide the processing indicator - * @param {object} oSettings dataTables settings object - * @param {bool} bShow Show the processing indicator (true) or not (false) - * @memberof DataTable#oApi - */ - function _fnProcessingDisplay ( oSettings, bShow ) - { - if ( oSettings.oFeatures.bProcessing ) - { - var an = oSettings.aanFeatures.r; - for ( var i=0, iLen=an.length ; i 0 ) - { - nCaption = nCaption[0]; - if ( nCaption._captionSide === "top" ) - { - nScrollHeadTable.appendChild( nCaption ); - } - else if ( nCaption._captionSide === "bottom" && nTfoot ) - { - nScrollFootTable.appendChild( nCaption ); - } - } - - /* - * Sizing - */ - /* When x-scrolling add the width and a scroller to move the header with the body */ - if ( oSettings.oScroll.sX !== "" ) - { - nScrollHead.style.width = _fnStringToCss( oSettings.oScroll.sX ); - nScrollBody.style.width = _fnStringToCss( oSettings.oScroll.sX ); - - if ( nTfoot !== null ) - { - nScrollFoot.style.width = _fnStringToCss( oSettings.oScroll.sX ); - } - - /* When the body is scrolled, then we also want to scroll the headers */ - $(nScrollBody).scroll( function (e) { - nScrollHead.scrollLeft = this.scrollLeft; - - if ( nTfoot !== null ) - { - nScrollFoot.scrollLeft = this.scrollLeft; - } - } ); - } - - /* When yscrolling, add the height */ - if ( oSettings.oScroll.sY !== "" ) - { - nScrollBody.style.height = _fnStringToCss( oSettings.oScroll.sY ); - } - - /* Redraw - align columns across the tables */ - oSettings.aoDrawCallback.push( { - "fn": _fnScrollDraw, - "sName": "scrolling" - } ); - - /* Infinite scrolling event handlers */ - if ( oSettings.oScroll.bInfinite ) - { - $(nScrollBody).scroll( function() { - /* Use a blocker to stop scrolling from loading more data while other data is still loading */ - if ( !oSettings.bDrawing && $(this).scrollTop() !== 0 ) - { - /* Check if we should load the next data set */ - if ( $(this).scrollTop() + $(this).height() > - $(oSettings.nTable).height() - oSettings.oScroll.iLoadGap ) - { - /* Only do the redraw if we have to - we might be at the end of the data */ - if ( oSettings.fnDisplayEnd() < oSettings.fnRecordsDisplay() ) - { - _fnPageChange( oSettings, 'next' ); - _fnCalculateEnd( oSettings ); - _fnDraw( oSettings ); - } - } - } - } ); - } - - oSettings.nScrollHead = nScrollHead; - oSettings.nScrollFoot = nScrollFoot; - - return nScroller; - } - - - /** - * Update the various tables for resizing. It's a bit of a pig this function, but - * basically the idea to: - * 1. Re-create the table inside the scrolling div - * 2. Take live measurements from the DOM - * 3. Apply the measurements - * 4. Clean up - * @param {object} o dataTables settings object - * @returns {node} Node to add to the DOM - * @memberof DataTable#oApi - */ - function _fnScrollDraw ( o ) - { - var - nScrollHeadInner = o.nScrollHead.getElementsByTagName('div')[0], - nScrollHeadTable = nScrollHeadInner.getElementsByTagName('table')[0], - nScrollBody = o.nTable.parentNode, - i, iLen, j, jLen, anHeadToSize, anHeadSizers, anFootSizers, anFootToSize, oStyle, iVis, - nTheadSize, nTfootSize, - iWidth, aApplied=[], aAppliedFooter=[], iSanityWidth, - nScrollFootInner = (o.nTFoot !== null) ? o.nScrollFoot.getElementsByTagName('div')[0] : null, - nScrollFootTable = (o.nTFoot !== null) ? nScrollFootInner.getElementsByTagName('table')[0] : null, - ie67 = o.oBrowser.bScrollOversize, - zeroOut = function(nSizer) { - oStyle = nSizer.style; - oStyle.paddingTop = "0"; - oStyle.paddingBottom = "0"; - oStyle.borderTopWidth = "0"; - oStyle.borderBottomWidth = "0"; - oStyle.height = 0; - }; - - /* - * 1. Re-create the table inside the scrolling div - */ - - /* Remove the old minimised thead and tfoot elements in the inner table */ - $(o.nTable).children('thead, tfoot').remove(); - - /* Clone the current header and footer elements and then place it into the inner table */ - nTheadSize = $(o.nTHead).clone()[0]; - o.nTable.insertBefore( nTheadSize, o.nTable.childNodes[0] ); - anHeadToSize = o.nTHead.getElementsByTagName('tr'); - anHeadSizers = nTheadSize.getElementsByTagName('tr'); - - if ( o.nTFoot !== null ) - { - nTfootSize = $(o.nTFoot).clone()[0]; - o.nTable.insertBefore( nTfootSize, o.nTable.childNodes[1] ); - anFootToSize = o.nTFoot.getElementsByTagName('tr'); - anFootSizers = nTfootSize.getElementsByTagName('tr'); - } - - /* - * 2. Take live measurements from the DOM - do not alter the DOM itself! - */ - - /* Remove old sizing and apply the calculated column widths - * Get the unique column headers in the newly created (cloned) header. We want to apply the - * calculated sizes to this header - */ - if ( o.oScroll.sX === "" ) - { - nScrollBody.style.width = '100%'; - nScrollHeadInner.parentNode.style.width = '100%'; - } - - var nThs = _fnGetUniqueThs( o, nTheadSize ); - for ( i=0, iLen=nThs.length ; i nScrollBody.offsetHeight || - $(nScrollBody).css('overflow-y') == "scroll") ) - { - o.nTable.style.width = _fnStringToCss( $(o.nTable).outerWidth() - o.oScroll.iBarWidth); - } - } - else - { - if ( o.oScroll.sXInner !== "" ) - { - /* x scroll inner has been given - use it */ - o.nTable.style.width = _fnStringToCss(o.oScroll.sXInner); - } - else if ( iSanityWidth == $(nScrollBody).width() && - $(nScrollBody).height() < $(o.nTable).height() ) - { - /* There is y-scrolling - try to take account of the y scroll bar */ - o.nTable.style.width = _fnStringToCss( iSanityWidth-o.oScroll.iBarWidth ); - if ( $(o.nTable).outerWidth() > iSanityWidth-o.oScroll.iBarWidth ) - { - /* Not possible to take account of it */ - o.nTable.style.width = _fnStringToCss( iSanityWidth ); - } - } - else - { - /* All else fails */ - o.nTable.style.width = _fnStringToCss( iSanityWidth ); - } - } - - /* Recalculate the sanity width - now that we've applied the required width, before it was - * a temporary variable. This is required because the column width calculation is done - * before this table DOM is created. - */ - iSanityWidth = $(o.nTable).outerWidth(); - - /* We want the hidden header to have zero height, so remove padding and borders. Then - * set the width based on the real headers - */ - - // Apply all styles in one pass. Invalidates layout only once because we don't read any - // DOM properties. - _fnApplyToChildren( zeroOut, anHeadSizers ); - - // Read all widths in next pass. Forces layout only once because we do not change - // any DOM properties. - _fnApplyToChildren( function(nSizer) { - aApplied.push( _fnStringToCss( $(nSizer).width() ) ); - }, anHeadSizers ); - - // Apply all widths in final pass. Invalidates layout only once because we do not - // read any DOM properties. - _fnApplyToChildren( function(nToSize, i) { - nToSize.style.width = aApplied[i]; - }, anHeadToSize ); - - $(anHeadSizers).height(0); - - /* Same again with the footer if we have one */ - if ( o.nTFoot !== null ) - { - _fnApplyToChildren( zeroOut, anFootSizers ); - - _fnApplyToChildren( function(nSizer) { - aAppliedFooter.push( _fnStringToCss( $(nSizer).width() ) ); - }, anFootSizers ); - - _fnApplyToChildren( function(nToSize, i) { - nToSize.style.width = aAppliedFooter[i]; - }, anFootToSize ); - - $(anFootSizers).height(0); - } - - /* - * 3. Apply the measurements - */ - - /* "Hide" the header and footer that we used for the sizing. We want to also fix their width - * to what they currently are - */ - _fnApplyToChildren( function(nSizer, i) { - nSizer.innerHTML = ""; - nSizer.style.width = aApplied[i]; - }, anHeadSizers ); - - if ( o.nTFoot !== null ) - { - _fnApplyToChildren( function(nSizer, i) { - nSizer.innerHTML = ""; - nSizer.style.width = aAppliedFooter[i]; - }, anFootSizers ); - } - - /* Sanity check that the table is of a sensible width. If not then we are going to get - * misalignment - try to prevent this by not allowing the table to shrink below its min width - */ - if ( $(o.nTable).outerWidth() < iSanityWidth ) - { - /* The min width depends upon if we have a vertical scrollbar visible or not */ - var iCorrection = ((nScrollBody.scrollHeight > nScrollBody.offsetHeight || - $(nScrollBody).css('overflow-y') == "scroll")) ? - iSanityWidth+o.oScroll.iBarWidth : iSanityWidth; - - /* IE6/7 are a law unto themselves... */ - if ( ie67 && (nScrollBody.scrollHeight > - nScrollBody.offsetHeight || $(nScrollBody).css('overflow-y') == "scroll") ) - { - o.nTable.style.width = _fnStringToCss( iCorrection-o.oScroll.iBarWidth ); - } - - /* Apply the calculated minimum width to the table wrappers */ - nScrollBody.style.width = _fnStringToCss( iCorrection ); - o.nScrollHead.style.width = _fnStringToCss( iCorrection ); - - if ( o.nTFoot !== null ) - { - o.nScrollFoot.style.width = _fnStringToCss( iCorrection ); - } - - /* And give the user a warning that we've stopped the table getting too small */ - if ( o.oScroll.sX === "" ) - { - _fnLog( o, 1, "The table cannot fit into the current element which will cause column"+ - " misalignment. The table has been drawn at its minimum possible width." ); - } - else if ( o.oScroll.sXInner !== "" ) - { - _fnLog( o, 1, "The table cannot fit into the current element which will cause column"+ - " misalignment. Increase the sScrollXInner value or remove it to allow automatic"+ - " calculation" ); - } - } - else - { - nScrollBody.style.width = _fnStringToCss( '100%' ); - o.nScrollHead.style.width = _fnStringToCss( '100%' ); - - if ( o.nTFoot !== null ) - { - o.nScrollFoot.style.width = _fnStringToCss( '100%' ); - } - } - - - /* - * 4. Clean up - */ - if ( o.oScroll.sY === "" ) - { - /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting - * the scrollbar height from the visible display, rather than adding it on. We need to - * set the height in order to sort this. Don't want to do it in any other browsers. - */ - if ( ie67 ) - { - nScrollBody.style.height = _fnStringToCss( o.nTable.offsetHeight+o.oScroll.iBarWidth ); - } - } - - if ( o.oScroll.sY !== "" && o.oScroll.bCollapse ) - { - nScrollBody.style.height = _fnStringToCss( o.oScroll.sY ); - - var iExtra = (o.oScroll.sX !== "" && o.nTable.offsetWidth > nScrollBody.offsetWidth) ? - o.oScroll.iBarWidth : 0; - if ( o.nTable.offsetHeight < nScrollBody.offsetHeight ) - { - nScrollBody.style.height = _fnStringToCss( o.nTable.offsetHeight+iExtra ); - } - } - - /* Finally set the width's of the header and footer tables */ - var iOuterWidth = $(o.nTable).outerWidth(); - nScrollHeadTable.style.width = _fnStringToCss( iOuterWidth ); - nScrollHeadInner.style.width = _fnStringToCss( iOuterWidth ); - - // Figure out if there are scrollbar present - if so then we need a the header and footer to - // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar) - var bScrolling = $(o.nTable).height() > nScrollBody.clientHeight || $(nScrollBody).css('overflow-y') == "scroll"; - nScrollHeadInner.style.paddingRight = bScrolling ? o.oScroll.iBarWidth+"px" : "0px"; - - if ( o.nTFoot !== null ) - { - nScrollFootTable.style.width = _fnStringToCss( iOuterWidth ); - nScrollFootInner.style.width = _fnStringToCss( iOuterWidth ); - nScrollFootInner.style.paddingRight = bScrolling ? o.oScroll.iBarWidth+"px" : "0px"; - } - - /* Adjust the position of the header in case we loose the y-scrollbar */ - $(nScrollBody).scroll(); - - /* If sorting or filtering has occurred, jump the scrolling back to the top */ - if ( o.bSorted || o.bFiltered ) - { - nScrollBody.scrollTop = 0; - } - } - - - /** - * Apply a given function to the display child nodes of an element array (typically - * TD children of TR rows - * @param {function} fn Method to apply to the objects - * @param array {nodes} an1 List of elements to look through for display children - * @param array {nodes} an2 Another list (identical structure to the first) - optional - * @memberof DataTable#oApi - */ - function _fnApplyToChildren( fn, an1, an2 ) - { - var index=0, i=0, iLen=an1.length; - var nNode1, nNode2; - - while ( i < iLen ) - { - nNode1 = an1[i].firstChild; - nNode2 = an2 ? an2[i].firstChild : null; - while ( nNode1 ) - { - if ( nNode1.nodeType === 1 ) - { - if ( an2 ) - { - fn( nNode1, nNode2, index ); - } - else - { - fn( nNode1, index ); - } - index++; - } - nNode1 = nNode1.nextSibling; - nNode2 = an2 ? nNode2.nextSibling : null; - } - i++; - } - } - - /** - * Convert a CSS unit width to pixels (e.g. 2em) - * @param {string} sWidth width to be converted - * @param {node} nParent parent to get the with for (required for relative widths) - optional - * @returns {int} iWidth width in pixels - * @memberof DataTable#oApi - */ - function _fnConvertToWidth ( sWidth, nParent ) - { - if ( !sWidth || sWidth === null || sWidth === '' ) - { - return 0; - } - - if ( !nParent ) - { - nParent = document.body; - } - - var iWidth; - var nTmp = document.createElement( "div" ); - nTmp.style.width = _fnStringToCss( sWidth ); - - nParent.appendChild( nTmp ); - iWidth = nTmp.offsetWidth; - nParent.removeChild( nTmp ); - - return ( iWidth ); - } - - - /** - * Calculate the width of columns for the table - * @param {object} oSettings dataTables settings object - * @memberof DataTable#oApi - */ - function _fnCalculateColumnWidths ( oSettings ) - { - var iTableWidth = oSettings.nTable.offsetWidth; - var iUserInputs = 0; - var iTmpWidth; - var iVisibleColumns = 0; - var iColums = oSettings.aoColumns.length; - var i, iIndex, iCorrector, iWidth; - var oHeaders = $('th', oSettings.nTHead); - var widthAttr = oSettings.nTable.getAttribute('width'); - var nWrapper = oSettings.nTable.parentNode; - - /* Convert any user input sizes into pixel sizes */ - for ( i=0 ; itd', nCalcTmp); - } - - /* Apply custom sizing to the cloned header */ - var nThs = _fnGetUniqueThs( oSettings, nTheadClone ); - iCorrector = 0; - for ( i=0 ; i 0 ) - { - oSettings.aoColumns[i].sWidth = _fnStringToCss( iWidth ); - } - iCorrector++; - } - } - - var cssWidth = $(nCalcTmp).css('width'); - oSettings.nTable.style.width = (cssWidth.indexOf('%') !== -1) ? - cssWidth : _fnStringToCss( $(nCalcTmp).outerWidth() ); - nCalcTmp.parentNode.removeChild( nCalcTmp ); - } - - if ( widthAttr ) - { - oSettings.nTable.style.width = _fnStringToCss( widthAttr ); - } - } - - - /** - * Adjust a table's width to take account of scrolling - * @param {object} oSettings dataTables settings object - * @param {node} n table node - * @memberof DataTable#oApi - */ - function _fnScrollingWidthAdjust ( oSettings, n ) - { - if ( oSettings.oScroll.sX === "" && oSettings.oScroll.sY !== "" ) - { - /* When y-scrolling only, we want to remove the width of the scroll bar so the table - * + scroll bar will fit into the area avaialble. - */ - var iOrigWidth = $(n).width(); - n.style.width = _fnStringToCss( $(n).outerWidth()-oSettings.oScroll.iBarWidth ); - } - else if ( oSettings.oScroll.sX !== "" ) - { - /* When x-scrolling both ways, fix the table at it's current size, without adjusting */ - n.style.width = _fnStringToCss( $(n).outerWidth() ); - } - } - - - /** - * Get the widest node - * @param {object} oSettings dataTables settings object - * @param {int} iCol column of interest - * @returns {node} widest table node - * @memberof DataTable#oApi - */ - function _fnGetWidestNode( oSettings, iCol ) - { - var iMaxIndex = _fnGetMaxLenString( oSettings, iCol ); - if ( iMaxIndex < 0 ) - { - return null; - } - - if ( oSettings.aoData[iMaxIndex].nTr === null ) - { - var n = document.createElement('td'); - n.innerHTML = _fnGetCellData( oSettings, iMaxIndex, iCol, '' ); - return n; - } - return _fnGetTdNodes(oSettings, iMaxIndex)[iCol]; - } - - - /** - * Get the maximum strlen for each data column - * @param {object} oSettings dataTables settings object - * @param {int} iCol column of interest - * @returns {string} max string length for each column - * @memberof DataTable#oApi - */ - function _fnGetMaxLenString( oSettings, iCol ) - { - var iMax = -1; - var iMaxIndex = -1; - - for ( var i=0 ; i/g, "" ); - if ( s.length > iMax ) - { - iMax = s.length; - iMaxIndex = i; - } - } - - return iMaxIndex; - } - - - /** - * Append a CSS unit (only if required) to a string - * @param {array} aArray1 first array - * @param {array} aArray2 second array - * @returns {int} 0 if match, 1 if length is different, 2 if no match - * @memberof DataTable#oApi - */ - function _fnStringToCss( s ) - { - if ( s === null ) - { - return "0px"; - } - - if ( typeof s == 'number' ) - { - if ( s < 0 ) - { - return "0px"; - } - return s+"px"; - } - - /* Check if the last character is not 0-9 */ - var c = s.charCodeAt( s.length-1 ); - if (c < 0x30 || c > 0x39) - { - return s; - } - return s+"px"; - } - - - /** - * Get the width of a scroll bar in this browser being used - * @returns {int} width in pixels - * @memberof DataTable#oApi - */ - function _fnScrollBarWidth () - { - var inner = document.createElement('p'); - var style = inner.style; - style.width = "100%"; - style.height = "200px"; - style.padding = "0px"; - - var outer = document.createElement('div'); - style = outer.style; - style.position = "absolute"; - style.top = "0px"; - style.left = "0px"; - style.visibility = "hidden"; - style.width = "200px"; - style.height = "150px"; - style.padding = "0px"; - style.overflow = "hidden"; - outer.appendChild(inner); - - document.body.appendChild(outer); - var w1 = inner.offsetWidth; - outer.style.overflow = 'scroll'; - var w2 = inner.offsetWidth; - if ( w1 == w2 ) - { - w2 = outer.clientWidth; - } - - document.body.removeChild(outer); - return (w1 - w2); - } - - /** - * Change the order of the table - * @param {object} oSettings dataTables settings object - * @param {bool} bApplyClasses optional - should we apply classes or not - * @memberof DataTable#oApi - */ - function _fnSort ( oSettings, bApplyClasses ) - { - var - i, iLen, j, jLen, k, kLen, - sDataType, nTh, - aaSort = [], - aiOrig = [], - oSort = DataTable.ext.oSort, - aoData = oSettings.aoData, - aoColumns = oSettings.aoColumns, - oAria = oSettings.oLanguage.oAria; - - /* No sorting required if server-side or no sorting array */ - if ( !oSettings.oFeatures.bServerSide && - (oSettings.aaSorting.length !== 0 || oSettings.aaSortingFixed !== null) ) - { - aaSort = ( oSettings.aaSortingFixed !== null ) ? - oSettings.aaSortingFixed.concat( oSettings.aaSorting ) : - oSettings.aaSorting.slice(); - - /* If there is a sorting data type, and a function belonging to it, then we need to - * get the data from the developer's function and apply it for this column - */ - for ( i=0 ; i/g, "" ); - nTh = aoColumns[i].nTh; - nTh.removeAttribute('aria-sort'); - nTh.removeAttribute('aria-label'); - - /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */ - if ( aoColumns[i].bSortable ) - { - if ( aaSort.length > 0 && aaSort[0][0] == i ) - { - nTh.setAttribute('aria-sort', aaSort[0][1]=="asc" ? "ascending" : "descending" ); - - var nextSort = (aoColumns[i].asSorting[ aaSort[0][2]+1 ]) ? - aoColumns[i].asSorting[ aaSort[0][2]+1 ] : aoColumns[i].asSorting[0]; - nTh.setAttribute('aria-label', sTitle+ - (nextSort=="asc" ? oAria.sSortAscending : oAria.sSortDescending) ); - } - else - { - nTh.setAttribute('aria-label', sTitle+ - (aoColumns[i].asSorting[0]=="asc" ? oAria.sSortAscending : oAria.sSortDescending) ); - } - } - else - { - nTh.setAttribute('aria-label', sTitle); - } - } - - /* Tell the draw function that we have sorted the data */ - oSettings.bSorted = true; - $(oSettings.oInstance).trigger('sort', oSettings); - - /* Copy the master data into the draw array and re-draw */ - if ( oSettings.oFeatures.bFilter ) - { - /* _fnFilter() will redraw the table for us */ - _fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 ); - } - else - { - oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); - oSettings._iDisplayStart = 0; /* reset display back to page 0 */ - _fnCalculateEnd( oSettings ); - _fnDraw( oSettings ); - } - } - - - /** - * Attach a sort handler (click) to a node - * @param {object} oSettings dataTables settings object - * @param {node} nNode node to attach the handler to - * @param {int} iDataIndex column sorting index - * @param {function} [fnCallback] callback function - * @memberof DataTable#oApi - */ - function _fnSortAttachListener ( oSettings, nNode, iDataIndex, fnCallback ) - { - _fnBindAction( nNode, {}, function (e) { - /* If the column is not sortable - don't to anything */ - if ( oSettings.aoColumns[iDataIndex].bSortable === false ) - { - return; - } - - /* - * This is a little bit odd I admit... I declare a temporary function inside the scope of - * _fnBuildHead and the click handler in order that the code presented here can be used - * twice - once for when bProcessing is enabled, and another time for when it is - * disabled, as we need to perform slightly different actions. - * Basically the issue here is that the Javascript engine in modern browsers don't - * appear to allow the rendering engine to update the display while it is still executing - * it's thread (well - it does but only after long intervals). This means that the - * 'processing' display doesn't appear for a table sort. To break the js thread up a bit - * I force an execution break by using setTimeout - but this breaks the expected - * thread continuation for the end-developer's point of view (their code would execute - * too early), so we only do it when we absolutely have to. - */ - var fnInnerSorting = function () { - var iColumn, iNextSort; - - /* If the shift key is pressed then we are multiple column sorting */ - if ( e.shiftKey ) - { - /* Are we already doing some kind of sort on this column? */ - var bFound = false; - for ( var i=0 ; i 0 && sCurrentClass.indexOf(sNewClass) == -1 ) - { - /* We need to add a class */ - nTds[i].className = sCurrentClass + " " + sNewClass; - } - } - } - } - - - - /** - * Save the state of a table in a cookie such that the page can be reloaded - * @param {object} oSettings dataTables settings object - * @memberof DataTable#oApi - */ - function _fnSaveState ( oSettings ) - { - if ( !oSettings.oFeatures.bStateSave || oSettings.bDestroying ) - { - return; - } - - /* Store the interesting variables */ - var i, iLen, bInfinite=oSettings.oScroll.bInfinite; - var oState = { - "iCreate": new Date().getTime(), - "iStart": (bInfinite ? 0 : oSettings._iDisplayStart), - "iEnd": (bInfinite ? oSettings._iDisplayLength : oSettings._iDisplayEnd), - "iLength": oSettings._iDisplayLength, - "aaSorting": $.extend( true, [], oSettings.aaSorting ), - "oSearch": $.extend( true, {}, oSettings.oPreviousSearch ), - "aoSearchCols": $.extend( true, [], oSettings.aoPreSearchCols ), - "abVisCols": [] - }; - - for ( i=0, iLen=oSettings.aoColumns.length ; i 4096 ) /* Magic 10 for padding */ - { - for ( var i=0, iLen=aCookies.length ; i 4096 ) { - if ( aOldCookies.length === 0 ) { - // Deleted all DT cookies and still not enough space. Can't state save - return; - } - - var old = aOldCookies.pop(); - document.cookie = old.name+"=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path="+ - aParts.join('/') + "/"; - } - } - - document.cookie = sFullCookie; - } - - - /** - * Read an old cookie to get a cookie with an old table state - * @param {string} sName name of the cookie to read - * @returns {string} contents of the cookie - or null if no cookie with that name found - * @memberof DataTable#oApi - */ - function _fnReadCookie ( sName ) - { - var - aParts = window.location.pathname.split('/'), - sNameEQ = sName + '_' + aParts[aParts.length-1].replace(/[\/:]/g,"").toLowerCase() + '=', - sCookieContents = document.cookie.split(';'); - - for( var i=0 ; i=0 ; i-- ) - { - aRet.push( aoStore[i].fn.apply( oSettings.oInstance, aArgs ) ); - } - - if ( sTrigger !== null ) - { - $(oSettings.oInstance).trigger(sTrigger, aArgs); - } - - return aRet; - } - - - /** - * JSON stringify. If JSON.stringify it provided by the browser, json2.js or any other - * library, then we use that as it is fast, safe and accurate. If the function isn't - * available then we need to built it ourselves - the inspiration for this function comes - * from Craig Buckler ( http://www.sitepoint.com/javascript-json-serialization/ ). It is - * not perfect and absolutely should not be used as a replacement to json2.js - but it does - * do what we need, without requiring a dependency for DataTables. - * @param {object} o JSON object to be converted - * @returns {string} JSON string - * @memberof DataTable#oApi - */ - var _fnJsonString = (window.JSON) ? JSON.stringify : function( o ) - { - /* Not an object or array */ - var sType = typeof o; - if (sType !== "object" || o === null) - { - // simple data type - if (sType === "string") - { - o = '"'+o+'"'; - } - return o+""; - } - - /* If object or array, need to recurse over it */ - var - sProp, mValue, - json = [], - bArr = $.isArray(o); - - for (sProp in o) - { - mValue = o[sProp]; - sType = typeof mValue; - - if (sType === "string") - { - mValue = '"'+mValue+'"'; - } - else if (sType === "object" && mValue !== null) - { - mValue = _fnJsonString(mValue); - } - - json.push((bArr ? "" : '"'+sProp+'":') + mValue); - } - - return (bArr ? "[" : "{") + json + (bArr ? "]" : "}"); - }; - - - /** - * From some browsers (specifically IE6/7) we need special handling to work around browser - * bugs - this function is used to detect when these workarounds are needed. - * @param {object} oSettings dataTables settings object - * @memberof DataTable#oApi - */ - function _fnBrowserDetect( oSettings ) - { - /* IE6/7 will oversize a width 100% element inside a scrolling element, to include the - * width of the scrollbar, while other browsers ensure the inner element is contained - * without forcing scrolling - */ - var n = $( - '
'+ - '
'+ - '
'+ - '
'+ - '
')[0]; - - document.body.appendChild( n ); - oSettings.oBrowser.bScrollOversize = $('#DT_BrowserTest', n)[0].offsetWidth === 100 ? true : false; - document.body.removeChild( n ); - } - - - /** - * Perform a jQuery selector action on the table's TR elements (from the tbody) and - * return the resulting jQuery object. - * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on - * @param {object} [oOpts] Optional parameters for modifying the rows to be included - * @param {string} [oOpts.filter=none] Select TR elements that meet the current filter - * criterion ("applied") or all TR elements (i.e. no filter). - * @param {string} [oOpts.order=current] Order of the TR elements in the processed array. - * Can be either 'current', whereby the current sorting of the table is used, or - * 'original' whereby the original order the data was read into the table is used. - * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page - * ("current") or not ("all"). If 'current' is given, then order is assumed to be - * 'current' and filter is 'applied', regardless of what they might be given as. - * @returns {object} jQuery object, filtered by the given selector. - * @dtopt API - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Highlight every second row - * oTable.$('tr:odd').css('backgroundColor', 'blue'); - * } ); - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Filter to rows with 'Webkit' in them, add a background colour and then - * // remove the filter, thus highlighting the 'Webkit' rows only. - * oTable.fnFilter('Webkit'); - * oTable.$('tr', {"filter": "applied"}).css('backgroundColor', 'blue'); - * oTable.fnFilter(''); - * } ); - */ - this.$ = function ( sSelector, oOpts ) - { - var i, iLen, a = [], tr; - var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] ); - var aoData = oSettings.aoData; - var aiDisplay = oSettings.aiDisplay; - var aiDisplayMaster = oSettings.aiDisplayMaster; - - if ( !oOpts ) - { - oOpts = {}; - } - - oOpts = $.extend( {}, { - "filter": "none", // applied - "order": "current", // "original" - "page": "all" // current - }, oOpts ); - - // Current page implies that order=current and fitler=applied, since it is fairly - // senseless otherwise - if ( oOpts.page == 'current' ) - { - for ( i=oSettings._iDisplayStart, iLen=oSettings.fnDisplayEnd() ; i - *
  • 1D array of data - add a single row with the data provided
  • - *
  • 2D array of arrays - add multiple rows in a single call
  • - *
  • object - data object when using mData
  • - *
  • array of objects - multiple data objects when using mData
  • - * - * @param {bool} [bRedraw=true] redraw the table or not - * @returns {array} An array of integers, representing the list of indexes in - * aoData ({@link DataTable.models.oSettings}) that have been added to - * the table. - * @dtopt API - * - * @example - * // Global var for counter - * var giCount = 2; - * - * $(document).ready(function() { - * $('#example').dataTable(); - * } ); - * - * function fnClickAddRow() { - * $('#example').dataTable().fnAddData( [ - * giCount+".1", - * giCount+".2", - * giCount+".3", - * giCount+".4" ] - * ); - * - * giCount++; - * } - */ - this.fnAddData = function( mData, bRedraw ) - { - if ( mData.length === 0 ) - { - return []; - } - - var aiReturn = []; - var iTest; - - /* Find settings from table node */ - var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] ); - - /* Check if we want to add multiple rows or not */ - if ( typeof mData[0] === "object" && mData[0] !== null ) - { - for ( var i=0 ; i= oSettings.fnRecordsDisplay() ) - { - oSettings._iDisplayStart -= oSettings._iDisplayLength; - if ( oSettings._iDisplayStart < 0 ) - { - oSettings._iDisplayStart = 0; - } - } - - if ( bRedraw === undefined || bRedraw ) - { - _fnCalculateEnd( oSettings ); - _fnDraw( oSettings ); - } - - return oData; - }; - - - /** - * Restore the table to it's original state in the DOM by removing all of DataTables - * enhancements, alterations to the DOM structure of the table and event listeners. - * @param {boolean} [bRemove=false] Completely remove the table from the DOM - * @dtopt API - * - * @example - * $(document).ready(function() { - * // This example is fairly pointless in reality, but shows how fnDestroy can be used - * var oTable = $('#example').dataTable(); - * oTable.fnDestroy(); - * } ); - */ - this.fnDestroy = function ( bRemove ) - { - var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] ); - var nOrig = oSettings.nTableWrapper.parentNode; - var nBody = oSettings.nTBody; - var i, iLen; - - bRemove = (bRemove===undefined) ? false : bRemove; - - /* Flag to note that the table is currently being destroyed - no action should be taken */ - oSettings.bDestroying = true; - - /* Fire off the destroy callbacks for plug-ins etc */ - _fnCallbackFire( oSettings, "aoDestroyCallback", "destroy", [oSettings] ); - - /* If the table is not being removed, restore the hidden columns */ - if ( !bRemove ) - { - for ( i=0, iLen=oSettings.aoColumns.length ; itr>td.'+oSettings.oClasses.sRowEmpty, oSettings.nTable).parent().remove(); - - /* When scrolling we had to break the table up - restore it */ - if ( oSettings.nTable != oSettings.nTHead.parentNode ) - { - $(oSettings.nTable).children('thead').remove(); - oSettings.nTable.appendChild( oSettings.nTHead ); - } - - if ( oSettings.nTFoot && oSettings.nTable != oSettings.nTFoot.parentNode ) - { - $(oSettings.nTable).children('tfoot').remove(); - oSettings.nTable.appendChild( oSettings.nTFoot ); - } - - /* Remove the DataTables generated nodes, events and classes */ - oSettings.nTable.parentNode.removeChild( oSettings.nTable ); - $(oSettings.nTableWrapper).remove(); - - oSettings.aaSorting = []; - oSettings.aaSortingFixed = []; - _fnSortingClasses( oSettings ); - - $(_fnGetTrNodes( oSettings )).removeClass( oSettings.asStripeClasses.join(' ') ); - - $('th, td', oSettings.nTHead).removeClass( [ - oSettings.oClasses.sSortable, - oSettings.oClasses.sSortableAsc, - oSettings.oClasses.sSortableDesc, - oSettings.oClasses.sSortableNone ].join(' ') - ); - if ( oSettings.bJUI ) - { - $('th span.'+oSettings.oClasses.sSortIcon - + ', td span.'+oSettings.oClasses.sSortIcon, oSettings.nTHead).remove(); - - $('th, td', oSettings.nTHead).each( function () { - var jqWrapper = $('div.'+oSettings.oClasses.sSortJUIWrapper, this); - var kids = jqWrapper.contents(); - $(this).append( kids ); - jqWrapper.remove(); - } ); - } - - /* Add the TR elements back into the table in their original order */ - if ( !bRemove && oSettings.nTableReinsertBefore ) - { - nOrig.insertBefore( oSettings.nTable, oSettings.nTableReinsertBefore ); - } - else if ( !bRemove ) - { - nOrig.appendChild( oSettings.nTable ); - } - - for ( i=0, iLen=oSettings.aoData.length ; i