diff options
60 files changed, 1377 insertions, 1962 deletions
@@ -5,3 +5,14 @@ test.* doc/ .yardoc/ coverage/ +.bundle/ +vendor/ +debian/*.debhelper.log +debian/*.debhelper +debian/*.substvars +debian/files +debian/debhelper-build-stamp +debian/mauvealert-client/ +debian/mauvealert-server/ +debian/mauvealert-server-dependencies/ +man/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..07b992b --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,57 @@ +stages: + - test + - package + - publish + +.test: &test + image: $CI_REGISTRY/docker-images/layers:$DISTRO-ruby + stage: test + script: + - apt install libsqlite3-dev zlib1g-dev libxml2-dev ruby-dev libpq-dev + - bundle install -j $(nproc) --path vendor/bundle --without development + - "POSTGRES_URL=postgres://$POSTGRES_USER:$POSTGRES_PASSWORD@postgres/$POSTGRES_DB bundle exec rake test" + services: + - postgres:$POSTGRES_VERSION + before_script: + - apt install postgresql-server-dev-$POSTGRES_VERSION + +test:jessie: + <<: *test + variables: + DISTRO: jessie + POSTGRES_USER: root + POSTGRES_VERSION: "9.4" + +test:stretch: + <<: *test + variables: + DISTRO: stretch + POSTGRES_USER: root + POSTGRES_VERSION: "9.6" + +.package: &package + image: $CI_REGISTRY/docker-images/layers:$DISTRO-deb + stage: package + script: + - package + artifacts: + paths: + - pkg/ + +package:jessie: + <<: *package + variables: + DISTRO: jessie + +package:stretch: + <<: *package + variables: + DISTRO: stretch + +publish: + stage: publish + tags: + - shell + script: + - publish + diff --git a/CHANGELOG b/CHANGELOG deleted file mode 100644 index 2b04e22..0000000 --- a/CHANGELOG +++ /dev/null @@ -1,21 +0,0 @@ -... Because it is needed. - -Version 1.0.13 - * Buttons now appear in a line. - * Function were called in wrong order on buttons. - * Alerts no longer sorted by source, just by subject. - * Alerts sorted by subject then summary. - * Page no longer needs to reload on status changes. No new conglomerate created. - * Bug #1111 fix: Alerts do not acknowledge for longer than maximum time. - * Bug #1116 fix: Ajax requests take too long, now spawn a new thread -- - see feature 1131 for full fix. - - -Version 1.0.12 - * Added time scales to bulk alerts acknowledgement. - * Change the web page header to be simpler and nicer. - * Added link to alert history. - - -Version 1.0.11 and below. - * Nothing documented. @@ -1,36 +1,38 @@ source "https://rubygems.org" +gem "ruby_protobuf", "~> 0.4.11" +gem "datamapper" +gem "dm-aggregates" +gem "dm-do-adapter" +gem "dm-migrations" +gem "dm-postgres-adapter" +gem "dm-sqlite-adapter" +gem "dm-transactions" +gem "dm-types" +gem "dm-validations" -gem "bcrypt-ruby", "=3.0.1" -gem "timecop", "=0.3.5" -gem "log4r", "=1.0.6" -gem "ipaddress", "=0.8.0" -gem "webmock", "=1.7.6" -gem "ruby_protobuf", "=0.4.11" -gem "json", "=1.5.4" -gem "datamapper", "=1.2.0" -gem "dm-aggregates", "=1.2.0" -gem "dm-do-adapter", "=1.2.0" -gem "dm-migrations", "=1.2.0" -gem "dm-postgres-adapter", "=1.2.0" -gem "dm-sqlite-adapter", "=1.2.0" -gem "dm-transactions", "=1.2.0" -gem "dm-types", "=1.2.0" -gem "dm-validations", "=1.2.0" -gem "sanitize", "=2.0.3" -gem "rmail", "=1.1.0" -gem "xmpp4r", "=0.5.6" -gem "thin", "=1.6.3" -gem "haml", "=3.0.15" -gem "RedCloth" -gem "rack" -gem "rack-protection" -gem "rack-test" -gem "rack-flash3" -gem "tilt", "=1.3.3" -gem "sinatra", "=1.4.6" -gem "locale", "=2.1.0" +# +# The versions here are to match Jessie +# +gem "log4r", "~> 1.1.10" +gem "ipaddress", "~> 0.8.0" +gem "json", "~> 1.8.1" +gem "sanitize", "~> 2.1.0" +gem "rmail", "~> 1.1.0" +gem "thin", "~> 1.6.3" +gem "haml", "~> 4.0.5" +gem "haml-contrib", "~> 1.0.0" +gem "RedCloth", "~> 4.2.9" +gem "rack", "~> 1.5.2" +gem "rack-protection", "~> 1.5.2" +gem "rack-flash3", "~> 1.0.5" +gem "tilt", "~> 1.4.1" +gem "sinatra", "~> 1.4.5" +gem "locale", "~> 2.1.0" group :test do gem "simplecov", :require => false + gem "rack-test", "~> 0.6.3" + gem "webmock", "~> 1.19.0" + gem "timecop", "~> 0.7.1" end diff --git a/Gemfile.lock b/Gemfile.lock index 8a4017f..716949f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,12 +2,15 @@ GEM remote: https://rubygems.org/ specs: RedCloth (4.2.9) - addressable (2.3.8) - bcrypt-ruby (3.0.1) - crack (0.4.2) + addressable (2.5.1) + public_suffix (~> 2.0, >= 2.0.2) + bcrypt (3.1.11) + bcrypt-ruby (3.1.5) + bcrypt (>= 3.1.3) + crack (0.4.3) safe_yaml (~> 1.0.0) - daemons (1.2.2) - data_objects (0.10.15) + daemons (1.2.4) + data_objects (0.10.17) addressable (~> 2.1) datamapper (1.2.0) dm-aggregates (~> 1.2.0) @@ -33,12 +36,12 @@ GEM dm-postgres-adapter (1.2.0) dm-do-adapter (~> 1.2.0) do_postgres (~> 0.10.6) - dm-serializer (1.2.0) + dm-serializer (1.2.2) dm-core (~> 1.2.0) - fastercsv (~> 1.5.4) - json (~> 1.5.4) - json_pure (~> 1.5.4) - multi_json (~> 1.0.3) + fastercsv (~> 1.5) + json (~> 1.6) + json_pure (~> 1.6) + multi_json (~> 1.0) dm-sqlite-adapter (1.2.0) dm-do-adapter (~> 1.2.0) do_sqlite3 (~> 0.10.6) @@ -46,98 +49,103 @@ GEM dm-core (~> 1.2.0) dm-transactions (1.2.0) dm-core (~> 1.2.0) - dm-types (1.2.0) - bcrypt-ruby (~> 3.0.0) + dm-types (1.2.2) + bcrypt-ruby (~> 3.0) dm-core (~> 1.2.0) - fastercsv (~> 1.5.4) - json (~> 1.5.4) - multi_json (~> 1.0.3) - stringex (~> 1.3.0) - uuidtools (~> 2.1.2) + fastercsv (~> 1.5) + json (~> 1.6) + multi_json (~> 1.0) + stringex (~> 1.4) + uuidtools (~> 2.1) dm-validations (1.2.0) dm-core (~> 1.2.0) - do_postgres (0.10.15) - data_objects (= 0.10.15) - do_sqlite3 (0.10.15) - data_objects (= 0.10.15) + do_postgres (0.10.17) + data_objects (= 0.10.17) + do_sqlite3 (0.10.17) + data_objects (= 0.10.17) docile (1.1.5) - eventmachine (1.0.7) + eventmachine (1.2.5) fastercsv (1.5.5) - haml (3.0.15) - ipaddress (0.8.0) - json (1.5.4) - json_pure (1.5.5) - spruz (~> 0.2.8) - locale (2.1.0) - log4r (1.0.6) - multi_json (1.0.4) - nokogiri (1.5.11) - rack (1.6.0) + haml (4.0.7) + tilt + haml-contrib (1.0.0.1) + haml (>= 3.2.0.alpha.13) + ipaddress (0.8.3) + json (1.8.6) + json_pure (1.8.6) + locale (2.1.2) + log4r (1.1.10) + mini_portile2 (2.2.0) + multi_json (1.12.1) + nokogiri (1.8.0) + mini_portile2 (~> 2.2.0) + public_suffix (2.0.5) + rack (1.5.5) rack-flash3 (1.0.5) rack rack-protection (1.5.3) rack rack-test (0.6.3) rack (>= 1.0) - rmail (1.1.0) + rmail (1.1.3) ruby_protobuf (0.4.11) safe_yaml (1.0.4) - sanitize (2.0.3) - nokogiri (>= 1.4.4, < 1.6) - simplecov (0.9.2) + sanitize (2.1.0) + nokogiri (>= 1.4.4) + simplecov (0.14.1) docile (~> 1.1.0) - multi_json (~> 1.0) - simplecov-html (~> 0.9.0) - simplecov-html (0.9.0) - sinatra (1.4.6) - rack (~> 1.4) + json (>= 1.8, < 3) + simplecov-html (~> 0.10.0) + simplecov-html (0.10.1) + sinatra (1.4.8) + rack (~> 1.5) rack-protection (~> 1.4) tilt (>= 1.3, < 3) - spruz (0.2.13) - stringex (1.3.3) - thin (1.6.3) + stringex (1.5.1) + thin (1.6.4) daemons (~> 1.0, >= 1.0.9) - eventmachine (~> 1.0) + eventmachine (~> 1.0, >= 1.0.4) rack (~> 1.0) - tilt (1.3.3) - timecop (0.3.5) + tilt (1.4.1) + timecop (0.7.4) uuidtools (2.1.5) - webmock (1.7.6) - addressable (~> 2.2, > 2.2.5) - crack (>= 0.1.7) - xmpp4r (0.5.6) + webmock (1.19.0) + addressable (>= 2.3.6) + crack (>= 0.3.2) PLATFORMS ruby DEPENDENCIES - RedCloth - bcrypt-ruby (= 3.0.1) - datamapper (= 1.2.0) - dm-aggregates (= 1.2.0) - dm-do-adapter (= 1.2.0) - dm-migrations (= 1.2.0) - dm-postgres-adapter (= 1.2.0) - dm-sqlite-adapter (= 1.2.0) - dm-transactions (= 1.2.0) - dm-types (= 1.2.0) - dm-validations (= 1.2.0) - haml (= 3.0.15) - ipaddress (= 0.8.0) - json (= 1.5.4) - locale (= 2.1.0) - log4r (= 1.0.6) - rack - rack-flash3 - rack-protection - rack-test - rmail (= 1.1.0) - ruby_protobuf (= 0.4.11) - sanitize (= 2.0.3) + RedCloth (~> 4.2.9) + datamapper + dm-aggregates + dm-do-adapter + dm-migrations + dm-postgres-adapter + dm-sqlite-adapter + dm-transactions + dm-types + dm-validations + haml (~> 4.0.5) + haml-contrib (~> 1.0.0) + ipaddress (~> 0.8.0) + json (~> 1.8.1) + locale (~> 2.1.0) + log4r (~> 1.1.10) + rack (~> 1.5.2) + rack-flash3 (~> 1.0.5) + rack-protection (~> 1.5.2) + rack-test (~> 0.6.3) + rmail (~> 1.1.0) + ruby_protobuf (~> 0.4.11) + sanitize (~> 2.1.0) simplecov - sinatra (= 1.4.6) - thin (= 1.6.3) - tilt (= 1.3.3) - timecop (= 0.3.5) - webmock (= 1.7.6) - xmpp4r (= 0.5.6) + sinatra (~> 1.4.5) + thin (~> 1.6.3) + tilt (~> 1.4.1) + timecop (~> 0.7.1) + webmock (~> 1.19.0) + +BUNDLED WITH + 1.13.6 @@ -4,27 +4,67 @@ ## OPENBSD_SETUP_FLAGS = --prefix=/usr/local --installdirs=site --ruby-path=/usr/local/bin/ruby18 --mandir=\$$prefix/man/man1 --siteruby=\$$libdir/ruby/site_ruby --siterubyver=\$$siteruby/1.8 +BUNDLER_FLAGS ?= --path=vendor/bundle --jobs=4 --no-deployment -all: man man/mauvesend.1 man/mauveserver.1 man/mauveconsole.1 +all: vendor/bundle man/mauvesend.1 man/mauveserver.1 man/mauveconsole.1 man: mkdir -p man -man/%.1: bin/% - ruby -I lib $< --manual | txt2man -t $(notdir $<) -s 1 > $@ +man/%.1: bin/% vendor/bundle man + bundle exec $< --manual | txt2man -t $(notdir $<) -s 1 > $@ + test -s $@ clean: $(RM) -r man $(RM) -r tmp -# NOP task to keep au happy -release: - true - distclean: clean + $(RM) -r OpenBSD + $(RM) -r vendor/bundle + +vendor/bundle: Gemfile + @bundle install $(BUNDLER_FLAGS) + +Gemfile.lock: Gemfile + @bundle update + +test: vendor/bundle + @bundle exec rake test + +OpenBSD: OpenBSD/sha256.asc + +OpenBSD/sha256: OpenBSD/ruby-mauvealert.tar.gz OpenBSD/ruby-protobuf.tar.gz + # + # rejig sha256sum to openbsd sha256 + # + $(RM) OpenBSD/sha256 + cd OpenBSD && sha256sum * | sed -e 's/\([^ ]\+\) \(.*\)$$/SHA256 (\2) = \1/' > sha256 + +OpenBSD/sha256.asc: OpenBSD/sha256 + # + # Sign it. + # + gpg --clearsign OpenBSD/sha256 + +OpenBSD/ruby-mauvealert.tar.gz: all setup.rb + mkdir -p tmp/ruby-mauvealert + ruby ./setup.rb config ${OPENBSD_SETUP_FLAGS} + ruby ./setup.rb setup + ruby ./setup.rb install --prefix=tmp/ruby-mauvealert + mkdir -p OpenBSD + tar -C tmp/ruby-mauvealert -czvf $@ . -test: - ruby -Ilib:test:. test/test_mauve.rb +OpenBSD/ruby-protobuf.tar.gz: + mkdir -p tmp/ruby-protobuf-source + git clone https://github.com/macks/ruby-protobuf.git tmp/ruby-protobuf-source + cd tmp/ruby-protobuf-source && git checkout -b v0.4.5 + ln -sf /usr/lib/ruby/1.8/setup.rb tmp/ruby-protobuf-source/ + cd tmp/ruby-protobuf-source && ruby ./setup.rb config ${OPENBSD_SETUP_FLAGS} + cd tmp/ruby-protobuf-source && ruby ./setup.rb setup + cd tmp/ruby-protobuf-source && ruby ./setup.rb install --prefix=../ruby-protobuf + mkdir -p OpenBSD + tar -C tmp/ruby-protobuf -czvf $@ . -.PHONY: all clean test distclean release +.PHONY: all clean openbsd_tarball test distclean OpenBSD @@ -1,14 +1,5 @@ require 'rake/testtask' -task :console do - $:.push("lib") - require 'irb' - require 'irb/completion' - require 'mauve/server' - ARGV.clear - IRB.start -end - Rake::TestTask.new do |t| t.libs << "test" t.libs << "lib" diff --git a/TODO-PJC b/TODO-PJC deleted file mode 100644 index e54435a..0000000 --- a/TODO-PJC +++ /dev/null @@ -1,3 +0,0 @@ - * Javascript ack_time update if ajax synchronicity a bit shite - * Template SMS bit better with the link URL to the actual alert. - * Add an internal heartbeat to send to another mauve instance. diff --git a/bin/mauveserver b/bin/mauveserver index 286b126..dcb51f6 100755 --- a/bin/mauveserver +++ b/bin/mauveserver @@ -34,6 +34,15 @@ # Patrick J Cherry <patrick@bytemark.co.uk> # +# Bundler boilerplate. +require 'pathname' +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', + Pathname.new(__FILE__).realpath) +require 'rubygems' +require 'bundler/setup' + +$LOAD_PATH << File.join(File.dirname(ENV['BUNDLE_GEMFILE']), 'lib') + help = manual = verbose = version = test = false while arg = ARGV.pop case arg diff --git a/bytemark_policy.txt b/bytemark_policy.txt deleted file mode 100644 index 22378ae..0000000 --- a/bytemark_policy.txt +++ /dev/null @@ -1,125 +0,0 @@ -server { - ip "0.0.0.0" - port 32741 - log_file STDOUT - log_level 0 - database "sqlite3:///tmp/mauve_test.db" - transmission_id_expire_time 600 - - web_interface { - port 1288 - } -} - -notification_method("xmpp") { - jid "mauveserv@chat.bytemark.co.uk" - password "foo" -} - -notification_method("email") { - # add in SMTP server, username, password etc. - # default to sending through localhost - from "matthew@bytemark.co.uk" - server "bytemail.bytemark.co.uk" - subject_prefix "[Bytemark alerts] " - #deliver_to_file "/tmp/alerts.txt" -} - -notification_method("sms") { - provider "AQL" - - username "bytemark" - password "foo" - from "01904890890" - max_messages_per_alert 3 -} - -person("office") { - #all { xmpp "office@conference.chat.bytemark.co.uk" } - all { email "matthew@bytemark.co.uk" } -} - -person("mbloch") { - urgent { sms("x") } - normal { xmpp("mbloch@chat.bytemark.co.uk") || email("matthew@bytemark.co.uk") } - low { email("matthew@bytemark.co.uk") } -} - -person("ptaphouse") { - urgent { sms("x") } - normal { xmpp("ptaphouse@chat.bytemark.co.uk") || email("pete@bytemark.co.uk") } - low { email("pete@bytemark.co.uk") } -} - -person("chris") { -} - -alert_group { - includes { - source == "supportbot" || source == "managed-monitor" - } - - acknowledgement_time 90.minutes - - level URGENT - - notify("office") { - every 30.minutes - } - - notify("mbloch") { - every 60.minutes - hours_in_day(0..8, 17..23) - unacknowledged(120.minutes) - } -} - -alert_group { - includes { source == "network-crit" } - level URGENT - acknowledgement_time 90.minutes - - notify("office") { every 15.minutes } - notify("ptaphouse") { every 30.minutes } - notify("mbloch") { - every 30.minutes - unacknowledged(95.minutes) - } -} - -alert_group { - includes { source == "networkmonitor" } - acknowledgement_time 7.days - - notify("office") { every 120.minutes } -} - -alert_group { - includes { source == "disks" } - acknowledgement_time 1.day - - notify("chris") { every 4.hours } - notify("mbloch","ptaphouse") { - every 12.hours - unacknowledged 24.hours - } - -} - -alert_group { - includes { source == "pxefs" } - level URGENT - - notify("office") { every 1.hour } - notify("mbloch") { - every 3.hours - unacknowledged 24.hours - } -} - -alert_group("default") { - level LOW - - notify("office") { every 3.hours } -} - diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..e4d42d3 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,734 @@ +mauvealert (3.16.0) UNRELEASED; urgency=medium + + * Re-packaged to install server into /srv, removing common package. (!2) + * Fixed a typo in the Depends field for mauvealert-client. (!2) + * Removed lots of old, unused scripts, and merged the example configuration + into /etc/mauvealert/mauveserver.conf. (!2) + * Changed supervision model to systemd. (!2) + * Removed XMPP support. (!2) + * Make authentication work for AJAX requests (#22, !3) + * Change 'Events' nav item to load day list instead of calendar (#18, !3) + + -- Patrick J Cherry <patrick@bytemark.co.uk> Mon, 01 Aug 2017 14:37:32 +0100 + +mauvealert (3.15.15-1) stable; urgency=medium + + * Tidied up iconv usage for ruby1.9+ + + -- Patrick J Cherry <patch@bytemark.co.uk> Thu, 10 Jul 2014 13:40:27 +0100 + +mauvealert (3.15.14-2) stable; urgency=low + + * Relaxed mauvealert-client ruby dependency. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Thu, 03 Jul 2014 11:43:00 +0100 + +mauvealert (3.15.14-1) stable; urgency=low + + * Web interface now defaults to wall-clock hours. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Wed, 22 May 2013 11:47:52 +0100 + +mauvealert (3.15.13-1) stable; urgency=low + + * Switched off global socket reverse lookups. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 21 May 2013 14:34:15 +0100 + +mauvealert (3.15.12-1) stable; urgency=low + + * Removed extra database lookup when receiving alert updates. + * Added extra to_a when checking to see if an alert should be suppressed to + force the database lookup at that point. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 21 May 2013 12:24:13 +0100 + +mauvealert (3.15.11-1) stable; urgency=low + + * Notifications now generate just one history entry to ensure that the + previous changes regarding notification supression work. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Thu, 16 May 2013 14:13:43 +0100 + +mauvealert (3.15.10-1ubuntu1) stable; urgency=low + + * Database is now queried to see if a notification should be suppressed. + * Notification suppression now takes urgency into account, such that more + urgent alerts are not smothered by less urgent ones. + * Templates now include which level the alert is. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Thu, 16 May 2013 11:47:33 +0100 + +mauvealert (3.15.9-1) stable; urgency=low + + * Added Mauve::QuickUpdate library + + -- Patrick J Cherry <patrick@bytemark.co.uk> Thu, 02 May 2013 14:23:34 +0100 + +mauvealert (3.15.8-1) stable; urgency=low + + * Added a further check to make sure the last alert of the same update type + was being updated by reminders. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Wed, 01 May 2013 15:41:10 +0100 + +mauvealert (3.15.7-1) stable; urgency=low + + * Altered alert_group to only set one reminder. + * Allow alert to clear raised_at/cleared_at times if none specified. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Wed, 01 May 2013 10:16:52 +0100 + +mauvealert (3.15.6-2) stable; urgency=low + + * Added generic_http library that had been missed off. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 25 Jan 2013 13:47:05 +0000 + +mauvealert (3.15.6-1) stable; urgency=low + + * Alert fields are now truncated if they're too long. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Wed, 12 Dec 2012 10:47:28 +0000 + +mauvealert (3.15.5-1) stable; urgency=low + + * Fixed bug whereby alerts cleared by a "replace" flag were not sending + notifications. + * The alert summary is now recorded in the history for an alert. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Wed, 05 Dec 2012 12:34:21 +0000 + +mauvealert (3.15.4-1) stable; urgency=low + + * Fixed typo in AlertGroup. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 04 Dec 2012 14:34:31 +0000 + +mauvealert (3.15.3-1) stable; urgency=low + + * Further fix-ups to the alert notification suppression code. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 04 Dec 2012 14:08:47 +0000 + +mauvealert (3.15.2-1) stable; urgency=low + + * Added "eager" loading when looking at event histories. + * Fixed event views to use dates, and to miss queries for days with no + events. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 04 Dec 2012 11:56:19 +0000 + +mauvealert (3.15.1-1) stable; urgency=low + + * Rationalised alert tables to look the same for both searches and + normal views. + * Events calendar is now faster as fewer queries are made when loading. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 30 Nov 2012 22:48:43 +0000 + +mauvealert (3.14.2-1) stable; urgency=low + + * Added search views to package + * Alert actions are now controlled by just one form, and two actions. + * Alerts in search results are now de-duped. + * Serch results are only coloured if they're raised. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 30 Nov 2012 14:55:16 +0000 + +mauvealert (3.14.1-1) stable; urgency=low + + * Web frontend now has a search box. + * Alert counts now reinstated. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 30 Nov 2012 12:35:44 +0000 + +mauvealert (3.13.2-1) stable; urgency=low + + * Raised time now set to unacknowledgment time when alerts become + unacknowledged + * Tidied up history entries + * Changed UNACKNOWLEDGED to RE-RAISED when alerts are no longer ack'd + * Added suppression information to views + + -- Patrick J Cherry <patrick@bytemark.co.uk> Thu, 29 Nov 2012 21:05:51 +0000 + +mauvealert (3.13.1-1) stable; urgency=low + + * Rewrote decision code for notifying on save for an alert. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Thu, 29 Nov 2012 16:17:11 +0000 + +mauvealert (3.12.1-1) stable; urgency=low + + * Added optional notification suppression for alerts. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Thu, 29 Nov 2012 10:35:04 +0000 + +mauvealert (3.11.8-1) stable; urgency=low + + * Re-wrote alert saving logic to use database transactions. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 27 Nov 2012 16:16:09 +0000 + +mauvealert (3.11.7-1) stable; urgency=low + + * Models are now more picky when setting created_at/updated_at times. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 27 Nov 2012 11:51:55 +0000 + +mauvealert (3.11.6-2) stable; urgency=low + + * Fixed missing library file. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 27 Nov 2012 10:16:35 +0000 + +mauvealert (3.11.6-1) stable; urgency=low + + * Added dm-validations monkey-patch to automatically save validation + failures, when no failures are given. + * Reverted change to AlertChanged. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 27 Nov 2012 09:55:44 +0000 + +mauvealert (3.11.5-1) stable; urgency=low + + * Now uses ruby-ipaddress instead of IPAddr. + * Now logs when AlertChanged fails, and bypassess validation when needed. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Mon, 26 Nov 2012 17:06:39 +0000 + +mauvealert (3.11.4-1) stable; urgency=low + + * AlertGroup is no longer looked for on raise/clear/ack. It is looked up on + notification. + * Update web_interface to use public_folder instead of just public + * Tidied notifier a little. Alert#cached_alert_group is reset before + notification. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 23 Nov 2012 15:43:11 +0000 + +mauvealert (3.11.3-3) stable; urgency=low + + * dm-aggregates now included. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 23 Nov 2012 13:05:26 +0000 + +mauvealert (3.11.3-2) stable; urgency=low + + * Updated dependencies to take new-style library names + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 23 Nov 2012 12:03:49 +0000 + +mauvealert (3.11.3) stable; urgency=low + + * minimal_dns_lookups configuration item added, preventing extra DNS lookups + when matching hosts to source lists. + * When the cached alert group is saved (in Notifier#notify) the callbacks + which trigger notifications are now skipped. + * Tidied up postgres test failure. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 23 Nov 2012 10:49:21 +0000 + +mauvealert (3.11.2) stable; urgency=low + + * Fixed typo in WebInterface. + * Make sure alerts only get sent on save if their status has changed. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Wed, 21 Nov 2012 16:38:00 +0000 + +mauvealert (3.11.1) stable; urgency=low + + * Added a sleep to the server main loop + * The alert gets saved after notification to make sure that the + cached_alert_group gets stored. + * xmmp errors now crash mauve completely. (deliberately). + + -- Patrick J Cherry <patrick@bytemark.co.uk> Wed, 21 Nov 2012 14:32:20 +0000 + +mauvealert (3.11.0) stable; urgency=low + + * Removed needless counting of alerts in the web interface + * Made alert queries more accurate + * Removed duplicate code in Alert#in_source_list + * Cut down on the number of DNS queries in SourceList#includes? + + -- Patrick J Cherry <patrick@bytemark.co.uk> Wed, 21 Nov 2012 14:20:42 +0000 + +mauvealert (3.10.0) stable; urgency=low + + * Added URLs which can be polled for source lists + * Removed freeze/thaw functionality from the threads which hasn't been used + for a while. + * Threads are now checked to make sure they're running, and not stuck. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Thu, 05 Jul 2012 14:00:55 +0100 + +mauvealert (3.9.4) stable; urgency=low + + * Clear cleared_at time if raised_at time is set on alert reception. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Mon, 02 Jul 2012 11:33:35 +0100 + +mauvealert (3.9.3) stable; urgency=low + + * no_one_in is now only called once per DuringRunner instance per + people_list to try and reduce hammering on the calendar. + * Fixed notify_when_on_holiday/off_sick to actually work. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 08 Jun 2012 10:32:23 +0100 + +mauvealert (3.9.2) stable; urgency=low + + * Removed destroy XMPP command -- replaced with clear. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Wed, 09 May 2012 17:05:43 +0100 + +mauvealert (3.9.1) stable; urgency=low + + * Web interface now uses an ajax call to work out ack times, falling back to + javascript if that takes too long. + * Default working hours are now 9.30 - 5.30 as per SLA. + * Fixed time tests + standardised dead zone + daylight hours. + * Fixed up PeopleList#resolve_notifications to check for recursion, and only + return notify clauses that refer to people. + * Changed Person#resolve_notification to match the signature for the + people_list version. + * Updated notifier only to try to connect to objects that respond to xmpp. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 08 May 2012 12:05:56 +0100 + +mauvealert (3.9.0) stable; urgency=low + + * Notify anyone who has contributed to an alert's history since its last + raise, either by acknowledging, or by being notified about it. + * Username is now recorded in the History wherever possible. + * Update Time.bank_holiday? etc. to work correctly. + * Mauve::Configuration#do_parse_time now a class method, and used elsewhere + to standardise ranges in the configuration file. + * Updated notification tests to test bank_holiday changes etc. + * Person and people lists have now become interchangeable, with + notifications only recording the username, rather than the + Person/PeopleList. This means that notifications in alert groups can use + default #during clauses and #every values from people lists and persons. + * Notify clauses can now take an array of persons/people_lists, and + people_lists can refer to other people lists. + * PeopleList names are now in the same namespace as usernames. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 04 May 2012 16:52:39 +0100 + +mauvealert (3.8.5) stable; urgency=low + + * Added configuration options for daytime_hours, dead_zone, and + working_hours. + * Updated views to be neater with respect to notice/errors and in the case + when no alerts are found in a table. + * Set default hostname to Socket.gethostname, and default base_url now + includes the port, if it is not port 80 + * Added ruby locale dependency for server. + * Symlink jquery if possible in server postinst. + * Updated history partial to be better. + * Updated sender to try and get things into UTF8 where poss. + * Fixed warn on nil method in person. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Wed, 02 May 2012 15:24:52 +0100 + +mauvealert (3.8.4) stable; urgency=low + + * Fixed notification when on holiday/off sick + * Strings are now stripped of any illegal/non-UTF8 characters + + -- Patrick J Cherry <patrick@bytemark.co.uk> Mon, 30 Apr 2012 12:53:33 +0100 + +mauvealert (3.8.3) stable; urgency=low + + * Reverted bad changes to mauveserver + example.conf + * Added default options for a person + * Added calendar check for a people_list + * Added a time at which to check the people in a people_list + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 27 Apr 2012 11:56:52 +0100 + +mauvealert (3.8.2) stable; urgency=low + + * Reverted a couple of duff changes. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 27 Apr 2012 11:00:21 +0100 + +mauvealert (3.8.1) stable; urgency=low + + * Calendar now honours http timeout + ssl verify settings. + * Added no_one_in test for DuringRunner + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 27 Apr 2012 09:32:02 +0100 + +mauvealert (3.8.0) stable; urgency=low + + * HTML sanitizing only takes place if the text looks like HTML. + * Tests improved to couple processor and notifier buffers. + * Added authentication tests + * Removed remote auth tests in from the web interface tests. + * If no bytemark_auth_url is set, then no remote auth takes place. + * SSL peer cert validation now takes place by default. + * People/PeopleLists can now specify individualy notification times/frequencies + * Notification now uses #during/#every from the Person/PeopleList if nothing was specified + * Max acknowledgement time is now specified in the config + * Calendar interface improved. + * holiday_url no longer used -- replaced by notify_when_on_holiday! + * added notify_when_off_sick! + * Added ability for the calendar to be queried for a list of bank holdays. + * Added ability for Time to be given a list of bank holidays to check + against. + * PeopleLists can now be a Proc, allowing downloading of lists. + * Testing is much improved. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Wed, 25 Apr 2012 17:16:56 +0100 + +mauvealert (3.7.8) stable; urgency=low + + * Reduced the liklihood of race conditions during evaluation of during {} + clauses. + * Added cache to DuringRunner to reduce the number of times now? is + evaluated. + * Added a debug! option to notification_method clauses for easier, erm, + debugging. + * Alert#notify and AlertChanged#notify now make use of the notification + buffer make the disconnection betweeen the processing loop and + notifications happen sooner. + * Manpage typo sorted out. + * - + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 27 Apr 2012 09:10:37 +0100 + +mauvealert (3.7.7) stable; urgency=low + + * Javascript refreshes now happen every two minutes. + * Alert now tries to find just one matching AlertGroup, not all. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 23 Mar 2012 15:02:53 +0000 + +mauvealert (3.7.6) stable; urgency=low + + * Tweaked alert display to show the alert_group properly. + * Bumped Mauve::Version correctly. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Thu, 22 Mar 2012 17:55:51 +0000 + +mauvealert (3.7.5) stable; urgency=low + + * Added tests for web interface. + * Tidied up HTTP auth codes when auth fails. + * Further tweaks to alert_group caching, and source_list searches. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Thu, 22 Mar 2012 17:44:58 +0000 + +mauvealert (3.7.4) stable; urgency=low + + * Possible improvements in alert group caching, in an attempt to fix + slowness in the interface. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Thu, 22 Mar 2012 10:06:07 +0000 + +mauvealert (3.7.3) stable; urgency=low + + * Added destroy command to XMPP interface. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 28 Feb 2012 14:35:17 +0000 + +mauvealert (3.7.2) stable; urgency=low + + * Fixed Hash/Array confusions when merging conditions for notification. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Thu, 02 Feb 2012 10:59:28 +0000 + +mauvealert (3.7.1) stable; urgency=low + + * Fixed debian packaging to install everything. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Mon, 30 Jan 2012 17:03:48 +0000 + +mauvealert (3.7.0) stable; urgency=low + + * Bundled timer in with processor. What could go wrong..? + * Updated example.conf to work + * Added possibility of using postgres databases. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Mon, 30 Jan 2012 12:31:06 +0000 + +mauvealert (3.6.3) stable; urgency=low + + * Logs received at time in Processor + * Added lsb-base dependency + * Added test-task to init script + * Added include, include_file, and include_directory directives to + configuration. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 06 Jan 2012 14:41:45 +0000 + +mauvealert (3.6.2) stable; urgency=low + + * Tinkered with dependencies + + -- Patrick J Cherry <patrick@bytemark.co.uk> Mon, 07 Nov 2011 10:09:54 +0000 + +mauvealert (3.6.1) stable; urgency=low + + * Fixed up alert ack parsing a bit. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Mon, 07 Nov 2011 09:38:05 +0000 + +mauvealert (3.6.0) stable; urgency=low + + * Changed ack command to split on spaces or commas or both + * Fixed initscripts to, erm, work when VERBOSE is not set + * Robustificated XMPP some more + * Moved all timestamps into epoch time from regular timestamps + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 04 Nov 2011 08:33:13 +0000 + +mauvealert (3.5.10) stable; urgency=low + + * Fixed up Pop3Client class to work. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 04 Oct 2011 15:29:22 +0100 + +mauvealert (3.5.9) stable; urgency=low + + * Improve documentation in code + * Fix XMPP crash problem properly + * Fix XMPP acknowledge syntax for minutes, seconds, etc. + * Update template to remove colon between the subject and summary. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 16 Sep 2011 13:20:51 +0100 + +mauvealert (3.5.8) stable; urgency=low + + * Added rescue clause to XMPP connect + + -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 13 Sep 2011 07:56:38 +0100 + +mauvealert (3.5.7) stable; urgency=low + + * Reminders are now unconditionally cleared when an alert is cleared. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Mon, 12 Sep 2011 11:18:09 +0100 + +mauvealert (3.5.6) stable; urgency=low + + * XMPP interface now saves note. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 06 Sep 2011 15:56:01 +0100 + +mauvealert (3.5.5) stable; urgency=low + + * Further updates to the suppression code. Should be much better now. + * XMPP interface fixed. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 06 Sep 2011 15:30:11 +0100 + +mauvealert (3.5.4) stable; urgency=low + + * Fixed up alert suppression to work better + * Also allow multiple suppression clauses in config + * Test for dual alerts when an alert unacknowledges. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 06 Sep 2011 11:06:09 +0100 + +mauvealert (3.5.3) stable; urgency=low + + * Fixed do_get timeout + * Added small cache for fetching external URIs. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Wed, 31 Aug 2011 14:03:45 +0100 + +mauvealert (3.5.2) stable; urgency=low + + * Added holiday checks + * Suppression now a bit less jumpy. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Wed, 31 Aug 2011 13:07:50 +0100 + +mauvealert (3.5.1) stable; urgency=low + + * Fixed XMPP interface to allow ack's from groupchat. + * Fixed XMPP interface to allow private chats to work. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Wed, 24 Aug 2011 16:55:30 +0100 + +mauvealert (3.5.0) stable; urgency=low + + * Added XMPP interface + + -- Patrick J Cherry <patrick@bytemark.co.uk> Wed, 24 Aug 2011 12:24:55 +0100 + +mauvealert (3.4.4) stable; urgency=low + + * Added events calendar + + -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 23 Aug 2011 18:24:52 +0100 + +mauvealert (3.4.3) stable; urgency=low + + * Fixed notification race-conditions. + * Reminders now get sent at the start of the next during period. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Mon, 22 Aug 2011 11:58:28 +0100 + +mauvealert (3.4.2) stable; urgency=low + + * Improved polling interval timing. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 19 Aug 2011 16:56:33 +0100 + +mauvealert (3.4.1) stable; urgency=low + + * Fixed note in alert acknowledgement box + * HTML now sanitized on save for Alert and History types + * Default thread interval now 0s for UDP server and Timer, 5s for everything + else + * Moar tests + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 19 Aug 2011 16:24:56 +0100 + +mauvealert (3.4.0) stable; urgency=low + + * More thread monitoring + * Altered alert path for reminders + * Lengthened cookie lifetime + * Added an initial logger + * Can now have one history with multiple alerts + * Changed DateTime to Time everywhere + * Moar tests + + -- Patrick J Cherry <patrick@bytemark.co.uk> Wed, 17 Aug 2011 14:26:18 +0100 + +mauvealert (3.3.2) stable; urgency=low + + * More pop3 tweaks + + -- Patrick J Cherry <patrick@bytemark.co.uk> Thu, 04 Aug 2011 17:18:13 +0100 + +mauvealert (3.3.1) stable; urgency=low + + * Fixed bug in Pop3 server + + -- Patrick J Cherry <patrick@bytemark.co.uk> Thu, 04 Aug 2011 15:18:27 +0100 + +mauvealert (3.3.0) stable; urgency=low + + * Added pop3 server + * Rejigged authentication + * Web interface improvements + + -- Patrick J Cherry <patrick@bytemark.co.uk> Thu, 04 Aug 2011 14:26:24 +0100 + +mauvealert (3.2.1) stable; urgency=low + + * Fixed up alert_group matching. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 22 Jul 2011 16:28:52 +0100 + +mauvealert (3.2.0) stable; urgency=low + + * Re-organisation of config + * Added lots of tests. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 22 Jul 2011 13:30:23 +0100 + +mauvealert (3.1.6) stable; urgency=low + + * Added new heartbeat to remote mauve + * Added alert URL to messages. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Wed, 20 Jul 2011 16:15:07 +0100 + +mauvealert (3.1.5) stable; urgency=low + + * mauvesend can now specify an absolute time + + -- Patrick J Cherry <patrick@bytemark.co.uk> Wed, 20 Jul 2011 10:54:17 +0100 + +mauvealert (3.1.4) stable; urgency=low + + * Fixed up recording of update_type for alerts + * Tidied up threading. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 19 Jul 2011 22:44:30 +0100 + +mauvealert (3.1.3) stable; urgency=low + + * Tidied up initscript + + -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 19 Jul 2011 17:59:01 +0100 + +mauvealert (3.1.2) stable; urgency=low + + * Fixed up some gruesome spelling errors. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 19 Jul 2011 16:42:19 +0100 + +mauvealert (3.1.1) stable; urgency=low + + * Fixed mauveserver config file searching + * Updated dependencies for lenny (ugh) + + -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 19 Jul 2011 15:48:53 +0100 + +mauvealert (3.1.0) stable; urgency=low + + * New version. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 15 Jul 2011 14:03:15 +0100 + +mauvealert (3.0.6) stable; urgency=low + + * Reminders to peple lists now work. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Sun, 19 Jun 2011 21:52:16 +0100 + +mauvealert (3.0.5) stable; urgency=low + + * Moar fiXXord. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 17 Jun 2011 18:08:21 +0100 + +mauvealert (3.0.4) stable; urgency=low + + * New release. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Fri, 17 Jun 2011 08:57:55 +0100 + +mauvealert (3.0.3) stable; urgency=low + + * Added get_all back to front-end + * Fixed up logging for more classes + * Catch empty notification lists + * Added console to server package + * Fixed document_root location + + -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 19 Apr 2011 11:31:10 +0100 + +mauvealert (3.0.2) stable; urgency=low + + * Now checks for transmission time set to zero. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 19 Apr 2011 10:27:13 +0100 + +mauvealert (3.0.1) stable; urgency=low + + * Fixed up logging for the web interface + * Added better catch for validation errors + + -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 19 Apr 2011 10:18:44 +0100 + +mauvealert (3.0.0) stable; urgency=low + + * New version. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Wed, 13 Apr 2011 17:02:22 +0100 + +mauvealert (0.1-1) stable; urgency=low + + * First release. + + -- Patrick J Cherry <patrick@bytemark.co.uk> Wed, 28 Oct 2009 10:34:17 +0000 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +9 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..5e913b3 --- /dev/null +++ b/debian/control @@ -0,0 +1,39 @@ +Source: mauvealert +Maintainer: Matthew Bloch <matthew@bytemark.co.uk> +Uploaders: Patrick J Cherry <patrick@bytemark.co.uk> +Section: ruby +Priority: optional +Build-Depends: ruby | ruby-interpreter, debhelper, rdoc, txt2man, dh-systemd, + bundler, libsqlite3-dev, zlib1g-dev, libxml2-dev, ruby-dev, libpq-dev, + postgresql-server-dev-9.4 | postgresql-server-dev-9.6 +Standards-Version: 3.9.1 + +Package: mauvealert-server +Architecture: any +Pre-Depends: libjs-jquery +Depends: adduser, lsb-base, ruby | ruby-interpreter, ruby-bundler, ${shlibs:Depends}, ${misc:Depends} +Suggests: mauvealert-client +Recommends: ruby-locale | liblocale-ruby1.8 +Description: Mauve network alert system -- server + Mauve is a network alert system for system and network administrators. You can + use it to quickly set up ad-hoc monitoring for a variety of services, and to + confidently replace all scripts that might otherwise send floods of emails or + text messages. + . + This is the server package. + +Package: mauvealert-client +Architecture: all +Depends: ruby | ruby-interpreter, ruby-protobuf, ${misc:Depends} +Recommends: liblocale-ruby1.8 | ruby-locale +Replaces: mauvealert-common +Conflicts: mauvealert-common +Provides: mauvealert-common +Description: Mauve network alert system -- client + Mauve is a network alert system for system and network administrators. You + can use it to quickly set up ad-hoc monitoring for a variety of services, and + to confidently replace all scripts that might otherwise send floods of emails + or text messages. + . + This is the client package. + diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..e0fe391 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,19 @@ +This is Mauve Alert, Bytemark's networked alert system. + +Copyright © 2009 Bytemark Computer Consulting Ltd <support@support.bytemark.co.uk> + +Licence: + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program 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 GNU +General Public License for more details. + +On Debian GNU/Linux systems, the complete text of the GNU General Public +License can be found in ‘/usr/share/common-licenses/GPL’ or in the dpkg +source as the file ‘COPYING’ diff --git a/debian/mauvealert-client.install b/debian/mauvealert-client.install new file mode 100644 index 0000000..839a24b --- /dev/null +++ b/debian/mauvealert-client.install @@ -0,0 +1,9 @@ +bin/mauvesend usr/bin/ +lib/mauve/sender.rb usr/lib/ruby/vendor_ruby/mauve/ +lib/mauve/quick_update.rb usr/lib/ruby/vendor_ruby/mauve/ +mauve.proto usr/lib/mauvealert/ +lib/mauve/proto.rb usr/lib/ruby/vendor_ruby/mauve/ +lib/mauve/mauve_time.rb usr/lib/ruby/vendor_ruby/mauve/ +lib/mauve/version.rb usr/lib/ruby/vendor_ruby/mauve/ +lib/mauve/mauve_resolv.rb usr/lib/ruby/vendor_ruby/mauve/ + diff --git a/debian/mauvealert-client.manpages b/debian/mauvealert-client.manpages new file mode 100644 index 0000000..d8c2d70 --- /dev/null +++ b/debian/mauvealert-client.manpages @@ -0,0 +1 @@ +man/mauvesend.1 diff --git a/debian/mauvealert-server.dirs b/debian/mauvealert-server.dirs new file mode 100644 index 0000000..ae8ddd8 --- /dev/null +++ b/debian/mauvealert-server.dirs @@ -0,0 +1,2 @@ +var/lib/mauvealert +var/log/mauvealert diff --git a/debian/mauvealert-server.docs b/debian/mauvealert-server.docs new file mode 100644 index 0000000..e845566 --- /dev/null +++ b/debian/mauvealert-server.docs @@ -0,0 +1 @@ +README diff --git a/debian/mauvealert-server.install b/debian/mauvealert-server.install new file mode 100644 index 0000000..089726b --- /dev/null +++ b/debian/mauvealert-server.install @@ -0,0 +1,9 @@ +bin srv/mauvealert +lib srv/mauvealert +static srv/mauvealert +views srv/mauvealert +vendor srv/mauvealert +.bundle srv/mauvealert +etc/mauveserver.conf etc/mauvealert/ +Gemfile srv/mauvealert +Gemfile.lock srv/mauvealert diff --git a/debian/mauvealert-server.links b/debian/mauvealert-server.links new file mode 100644 index 0000000..0a2fc11 --- /dev/null +++ b/debian/mauvealert-server.links @@ -0,0 +1 @@ +usr/share/javascript/jquery srv/mauvealert/static/javascript/jquery diff --git a/debian/mauvealert-server.logrotate b/debian/mauvealert-server.logrotate new file mode 100644 index 0000000..9d15453 --- /dev/null +++ b/debian/mauvealert-server.logrotate @@ -0,0 +1,10 @@ +/var/log/mauvealert/mauvealert.log { + rotate 7 + daily + compress + delaycompress + postrotate + /usr/sbin/invoke-rc.d mauvealert-server reopen-logs >/dev/null + endscript +} + diff --git a/debian/mauvealert-server.manpages b/debian/mauvealert-server.manpages new file mode 100644 index 0000000..5d0c618 --- /dev/null +++ b/debian/mauvealert-server.manpages @@ -0,0 +1,2 @@ +man/mauveserver.1 +man/mauveconsole.1 diff --git a/debian/mauvealert-server.postinst b/debian/mauvealert-server.postinst new file mode 100644 index 0000000..945cec4 --- /dev/null +++ b/debian/mauvealert-server.postinst @@ -0,0 +1,28 @@ +#!/bin/bash + +set -e + +## +# Add a mauve user +## +RUNASUSER=mauvealert + +adduser --system --no-create-home --home /nonexistent $RUNASUSER > /dev/null + +## +# Add default directories +## +for i in log lib ; do + mkdir -p /var/$i/mauvealert + chown -R $RUNASUSER:nogroup /var/$i/mauvealert +done + +# +# Symlink jquery libraries in. +# +if [ -e /usr/share/javascript/jquery -a ! -e /srv/mauvealert/static/javascript/jquery ] ; then + ln -s /usr/share/javascript/jquery /srv/mauvealert/static/javascript/ +fi + + +#DEBHELPER# diff --git a/debian/mauvealert-server.postrm b/debian/mauvealert-server.postrm new file mode 100644 index 0000000..d54e402 --- /dev/null +++ b/debian/mauvealert-server.postrm @@ -0,0 +1,20 @@ +#!/bin/bash + +set -e + +RUNASUSER=mauvealert + +if [ "$1" = "purge" ] ; then + ## + # Remove the system user + ## + deluser --system $RUNASUSER > /dev/null + + ## + # remove default directories + ## + rm -rf /var/log/mauvealert/ + rm -rf /var/lib/mauvealert/ +fi + +#DEBHELPER# diff --git a/debian/mauvealert-server.service b/debian/mauvealert-server.service new file mode 100644 index 0000000..7cd49ba --- /dev/null +++ b/debian/mauvealert-server.service @@ -0,0 +1,14 @@ +[Unit] +Description=Mauve Alert Server +After=network.target + +[Service] +Type=simple +Restart=always +WorkingDirectory=/srv/mauvealert +ExecStart=/srv/mauvealert/bin/mauvealert /etc/mauveserver.conf + +[Install] +WantedBy=multi-user.target + + diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..4097e2e --- /dev/null +++ b/debian/rules @@ -0,0 +1,13 @@ +#!/usr/bin/make -f + +export BUNDLER_FLAGS=--deployment --without='test development' --jobs=4 + +%: + dh $@ --with-systemd + +# +# We test separately in Gitlab. +# +override_dh_auto_test: + echo "NOP" + diff --git a/etc/mauveserver.conf b/etc/mauveserver.conf index 0a7a747..e0b74d0 100644 --- a/etc/mauveserver.conf +++ b/etc/mauveserver.conf @@ -1,56 +1,163 @@ # Example mauveserver.conf file, based on Bytemark's configuration. -# Probably still needs a bit of improvement and explanation. -# -# The service which listens for alert messages +# +# This is the main configuration clause for the server instance. # server { - # - # persistent data store, only sqlite3 supported at the moment # - database "sqlite3:///var/lib/mauvealert/alerts.db" + # This is where our database lives. Example strings are as follows: + # + # postgres://username:password@host/database + # sqlite3:///var/lib/mauvealert/alerts.db + # + database "sqlite3::memory:" + + # + # This is our hostname. It gets used when URLs are generated, and in the heartbeat alert. + # + hostname `hostname`.chomp + # + # This is the UDP listener. + # listener { - ip "0.0.0.0" + # + # This is the IP and port for that the UDP packets come in on. The IP can + # be IPv4 or 6. If "::" is specified, then packets will be received over + # both IPv4 and 6. The default port is 32741. + # + ip "::" port 32741 + + # + # This is how long the UDP server will sleep between looking for packets. + # + poll_every 1 + } + + + # + # This is the processing thread, which recieves packets, and updates the + # database as needed. + # + processor { + # + # This is the length of time the processor will sleep between checking for + # new packets from the UDP listener. + # + poll_every 1 + + # + # In order to make sure the same transmission isn't received more then + # once, a cache of transmission IDs is kept. This expire time below + # determines the interval over which this cache is expired. + # + transmission_cache_expire_time 300 } - - # default is probably more than enough! - # transmission_id_expire_time 600 - # few options for web interface, just the TCP port number + # + # The web interface + # web_interface { - ip "0.0.0.0" + # + # The listening IP and port. Can be IPv4 or IPv6. Bear in mind that for + # ports < 1024, mauveserver will need to be run as root, or have special + # dispensation. + # + ip "::" port 1288 - document_root "/usr/share/mauvealert" + + # + # This is where the template files live. + # + # document_root "/srv/mauvealert" + + # + # This is used in the cookie, to prevent session-stealing. + # + session_secret "PLEASE CHANGE ME" + } + + + # + # This is where the mauve server sends its own heartbeat. Useful for + # watching the watcher. + # + heartbeat { + # + # If no destination is specified, then the contents of + # /etc/mauvealert/mauvesend.destination are used. + # + destination "localhost" + # + # This is how long to wait before the alert is raised + # + raise_after 600 + # + # These two fields have sensible defaults set, but more informative + # messages can be set here. + # + summary "Mauve alert server is down" + detail "The Mauve alert server has failed to send a heartbeat" } + } +# +# The logger uses log4r +# logger { + # + # This is the default formatting string. It is a PatternFormatter, which is + # described at + # http://log4r.rubyforge.org/rdoc/Log4r/rdoc/patternformatter.html + # default_format "%d [ %6.6l ] [ %12.12c ] %m" - outputter("file") { - filename "/var/log/mauvealert/mauvealert.log" - trunc false + # + # This is the default logging level. It can be one of + # + # Log4r::DEBUG + # Log4r::INFO + # Log4r::NOTICE + # Log4r::WARN + # Log4r::ERROR + # Log4r::FATAL + # + default_level Log4r::INFO + + # + # An outputter can be any one of those listed at + # http://log4r.rubyforge.org/rdoc/Log4r/rdoc/outputter.html. The name must + # correspond to the class name. The options in each outputter correspond to + # the "hash" arguments for that particular outputter class. + # + # Additionally each outputter can have a level, and format assoicated. + # + outputter("stdout") { level Log4r::INFO } -# outputter("stdout") { -# level Log4r::DEBUG -# } + outputter("file") { + filename "/tmp/mauveserver.log" + trunc true + level Log4r::DEBUG + } # outputter("email") { -# server "localhost" +# server "smtp.example.com" # subject "Mauve logger output" -# from "mauvealert@localhost" -# to "boring@localhost" -# domain "localhost" +# from "#{ENV['USER']}@#{Socket.gethostname}" +# to "awooga@example.com" # level Log4r::WARN # } } +# +# Email messaging. +# notification_method("email") { # # email address to send from @@ -62,25 +169,18 @@ notification_method("email") { # server "localhost" - # + # # add this to the subject of any emails we send # subject_prefix "[mauvealert]" } -# How to log into a jabber server -# -# notification_method("xmpp") { -# jid "mauvealert@chat.example.com/boo" -# password "x" -# } - # How to notify by SMS - we use aql.com, a provider for clockworksms.com # is also provided. For another provider, you'll need to write a module. # # notification_method("sms") { # provider "AQL" -# +# # username "x" # password "x" # from "01234567890" @@ -91,7 +191,7 @@ notification_method("email") { # # notification_method("sms") { # provider "Clockwork" -# +# # apikey "sssseeeeeeccccccccrrrrreeeeeettttsssssss" # from "01234567890" # @@ -102,7 +202,7 @@ notification_method("email") { # Simple default notification preference for root at this machine, at all # alert levels. You probably want more people, see below for a more complete # example. -# +# person("root") { all { email("root@localhost") } } @@ -113,18 +213,13 @@ person("root") { # # sms "07111222333" # email "johnny@example.com" -# xmpp "johnny@example.com.jabber.org" # # # Johnny wants waking up 24/7 if anything urgent happens # urgent { sms } # -# # XMPP, or Email him for anything that's not urgent -# normal { xmpp or email } -# -# # Anything else can just be a jabber message, which he might miss. -# # Email instead if he's unavailable/offline - but give it a try if -# # we don't know his status. -# low { xmpp("johnny@example.com.jabber.org", :if_presence => [:available, unknown]) || email } +# # Email him for anything that's not urgent +# normal { email } +# low { email } # # # SMS messages are expensive, if we're sending more than 5 per minute, # # tell the user we're going to stop until it slows down. @@ -145,22 +240,22 @@ person("root") { # suppress_notifications_after 3 => 1.hour # } -# Here is a group of alerts generated by the core routers. -# +# Here is a group of alerts generated by the core routers. +# # alert_group { # level URGENT # includes { source == "core-router" } # # # Johnny should get up and fix these errors very quickly, we will # # bother him every 15 minutes until he does. -# # +# # # notify("johnny") { every 15.minutes } # # # Archie only wants to know about these alerts if they have gone # # unacknowledged for a whole hour - Johnny must be slacking! Even # # then he only needs to know during the working day. # # -# notify("archie") { +# notify("archie") { # every 6.hours # during { unacknowledged(1.hour); hours_in_day(9..17) } # } @@ -168,7 +263,7 @@ person("root") { # # Default notification - tell root about all alerts every hour -# +# alert_group { level NORMAL notify("root") { every 1.hour } diff --git a/example.conf b/example.conf deleted file mode 100644 index dc40de7..0000000 --- a/example.conf +++ /dev/null @@ -1,197 +0,0 @@ -# -# This is the main configuration clause for the server instance. -# -server { - # - # This is where our database lives. SQLite is the default. - # - database "sqlite3::memory:" - - # - # This is our hostname. It gets used when URLs are generated, and in the heartbeat alert. - # - hostname "mauve.example.com" - - # - # This is the UDP listener. - # - listener { - # - # This is the IP and port for that the UDP packets come in on. The IP can - # be IPv4 or 6. If "::" is specified, then packets will be received over - # both IPv4 and 6. The default port is 32741. - # - ip "::" - port 32741 - - # - # This is how long the UDP server will sleep between looking for packets. - # - poll_every 1 - } - - - # - # This is the processing thread, which recieves packets, and updates the - # database as needed. - # - processor { - # - # This is the length of time the processor will sleep between checking for - # new packets from the UDP listener. - # - poll_every 1 - - # - # In order to make sure the same transmission isn't received more then - # once, a cache of transmission IDs is kept. This expire time below - # determines the interval over which this cache is expired. - # - transmission_cache_expire_time 300 - } - - # - # The web interface - # - web_interface { - # - # The listening IP and port. Can be IPv4 or IPv6. Bear in mind that for - # ports < 1024, mauveserver will need to be run as root, or have special - # dispensation. - # - ip "::" - port 1288 - - # - # This is where the template files live. - # - # document_root "/usr/share/mauvealert/" - - # - # This is used in the cookie, to prevent session-stealing. - # - session_secret "PLEASE CHANGE ME" - } - - - # - # This is where the mauve server sends its own heartbeat. Useful for - # watching the watcher. - # - heartbeat { - # - # If no destination is specified, then the contents of - # /etc/mauvealert/mauvesend.destination are used. - # - destination "localhost" - # - # This is how long to wait before the alert is raised - # - raise_after 600 - # - # These two fields have sensible defaults set, but more informative - # messages can be set here. - # - summary "Mauve alert server is down" - detail "The Mauve alert server has failed to send a heartbeat" - } - -} - -# -# The logger uses log4r -# -logger { - - # - # This is the default formatting string. It is a PatternFormatter, which is - # described at - # http://log4r.rubyforge.org/rdoc/Log4r/rdoc/patternformatter.html - # - default_format "%d [ %6.6l ] [ %12.12c ] %m" - - # - # This is the default logging level. It can be one of - # - # Log4r::DEBUG - # Log4r::INFO - # Log4r::NOTICE - # Log4r::WARN - # Log4r::ERROR - # Log4r::FATAL - # - default_level Log4r::INFO - - # - # An outputter can be any one of those listed at - # http://log4r.rubyforge.org/rdoc/Log4r/rdoc/outputter.html. The name must - # correspond to the class name. The options in each outputter correspond to - # the "hash" arguments for that particular outputter class. - # - # Additionally each outputter can have a level, and format assoicated. - # - outputter("stdout") { - level Log4r::WARN - } - - outputter("file") { - filename "/tmp/mauveserver.log" - trunc true - level Log4r::DEBUG - } - -# outputter("email") { -# server "smtp.example.com" -# subject "Mauve logger output" -# from "#{ENV['USER']}@#{Socket.gethostname}" -# to "awooga@example.com" -# level Log4r::WARN -# } - -} - -# -# XMPP instant messaging. This are the credentials to log into the XMPP -# account that mauve will use. -# -#notification_method ("xmpp") { -# jid "mauve@chat.example.com/mauve" -# password "mauvespassword" -#} - -# -# Email messaging. -# -notification_method ("email") { - server "localhost" - from "mauve@desk1.tur.bytemark.co.uk" - subject_prefix "mauve-test" -} - -person("office_chat") { -# xmpp "muc:mauve-test@conference.chat.bytemark.co.uk/MauveAlert" -# all { xmpp } -# suppress_notifications_after(310 => 1.minute) -} - -person ("pcherry") { - password '82da4c33e3a5ae9e51def466745e2c8965fa1476' - all { true } -} - -people_list ("arse") { - list [ "office_chat" ] -} - -# -# Default notification - tell root about all alerts every hour -# -alert_group("default") { - level URGENT - - notify("arse") { - every 2.minutes - } -} - - diff --git a/lib/mauve/authentication.rb b/lib/mauve/authentication.rb index 73c503a..08e0bf0 100644 --- a/lib/mauve/authentication.rb +++ b/lib/mauve/authentication.rb @@ -49,9 +49,7 @@ module Mauve # @return [Boolean] Success or failure. # def self.authenticate(login, password) - result = false - - ORDER.any? do |klass| + auth_success = ORDER.any? do |klass| auth = klass.new result = begin @@ -67,13 +65,13 @@ module Mauve result end - unless true == result + unless auth_success logger.info "Authentication for #{login} failed" # Rate limit sleep Configuration.current.failed_login_delay end - result + auth_success end end diff --git a/lib/mauve/configuration_builders/person.rb b/lib/mauve/configuration_builders/person.rb index aac5f69..ccc67aa 100644 --- a/lib/mauve/configuration_builders/person.rb +++ b/lib/mauve/configuration_builders/person.rb @@ -23,7 +23,6 @@ module Mauve is_attribute "password" is_attribute "sms" is_attribute "email" - is_attribute "xmpp" is_attribute "hipchat" is_flag_attribute "notify_when_on_holiday" @@ -109,8 +108,8 @@ module Mauve # Set up some default notify levels. # if person.urgent.nil? and person.normal.nil? and person.low.nil? - person.urgent = Proc.new { sms ; xmpp ; email } - person.normal = Proc.new { xmpp ; email } + person.urgent = Proc.new { sms ; email } + person.normal = Proc.new { email } person.low = Proc.new { email } end diff --git a/lib/mauve/notifier.rb b/lib/mauve/notifier.rb index 35ac019..4939b03 100644 --- a/lib/mauve/notifier.rb +++ b/lib/mauve/notifier.rb @@ -1,6 +1,5 @@ require 'mauve/mauve_thread' require 'mauve/notifiers' -require 'mauve/notifiers/xmpp' module Mauve @@ -15,8 +14,7 @@ module Mauve include Singleton # Stop the notifier thread. This just makes sure that all the - # notifications in the buffer have been sent before closing the XMPP - # connection. + # notifications in the buffer have been sent. # def stop super @@ -25,11 +23,6 @@ module Mauve # Flush the queue. # main_loop - - if Configuration.current.notification_methods['xmpp'] - Configuration.current.notification_methods['xmpp'].close - end - end # @@ -69,43 +62,6 @@ module Mauve # # def main_loop - - # - # Make sure we're connected to the XMPP server if needed on every iteration. - # - xmpp = Configuration.current.notification_methods['xmpp'] - - if xmpp and !xmpp.ready? - # - # Connect to XMPP server - # - xmpp.connect - - # - # Join all chats and shit. Unless the connection failed. - # - Configuration.current.people.each do |username, person| - # - # Ignore people without XMPP stanzas. - # - next unless person.respond_to?(:xmpp) and person.xmpp - - # - # For each JID, either ensure they're on our roster, or that we're in - # that chat room. - # - jid = if xmpp.is_muc?(person.xmpp) - xmpp.join_muc(person.xmpp) - else - xmpp.ensure_roster_and_subscription!(person.xmpp) - end - - Configuration.current.people[username].xmpp = jid unless jid.nil? - - end if xmpp.ready? - - end - # # Cycle through the buffer. # diff --git a/lib/mauve/notifiers.rb b/lib/mauve/notifiers.rb index 383db5e..424b2c1 100644 --- a/lib/mauve/notifiers.rb +++ b/lib/mauve/notifiers.rb @@ -2,7 +2,6 @@ require 'mauve/notifiers/email' require 'mauve/notifiers/sms_default' require 'mauve/notifiers/sms_aql' require 'mauve/notifiers/sms_clockwork' -require 'mauve/notifiers/xmpp' require 'mauve/notifiers/hipchat' module Mauve diff --git a/lib/mauve/notifiers/templates/email_subject.txt.erb b/lib/mauve/notifiers/templates/email_subject.txt.erb index 802c711..45fda99 120000 --- a/lib/mauve/notifiers/templates/email_subject.txt.erb +++ b/lib/mauve/notifiers/templates/email_subject.txt.erb @@ -1 +1 @@ -xmpp.txt.erb
\ No newline at end of file +sms.txt.erb
\ No newline at end of file diff --git a/lib/mauve/notifiers/templates/hipchat.txt.erb b/lib/mauve/notifiers/templates/hipchat.txt.erb index 802c711..45fda99 120000 --- a/lib/mauve/notifiers/templates/hipchat.txt.erb +++ b/lib/mauve/notifiers/templates/hipchat.txt.erb @@ -1 +1 @@ -xmpp.txt.erb
\ No newline at end of file +sms.txt.erb
\ No newline at end of file diff --git a/lib/mauve/notifiers/templates/sms.txt.erb b/lib/mauve/notifiers/templates/sms.txt.erb index 802c711..c148f41 120000..100644 --- a/lib/mauve/notifiers/templates/sms.txt.erb +++ b/lib/mauve/notifiers/templates/sms.txt.erb @@ -1 +1,22 @@ -xmpp.txt.erb
\ No newline at end of file +<%=alert.id %>: <%= alert.update_type.upcase %> (<%= alert.level %>): <% +case alert.update_type +when "cleared" +%><%= alert.cleared_at.to_s_relative %><% +when "acknowledged" +%><%= alert.acknowledged_at.to_s_relative %> by <%= alert.acknowledged_by %> until <%= alert.will_unacknowledge_at.to_s_human %><% +else +%><%= alert.raised_at.to_s_relative %><% +end +%>: <%= alert.subject %> <%= alert.summary %><% +if alert.source != alert.subject +%> -- from <%= alert.source %><% +end +%>. <%=WebInterface.url_for(alert)%><% +if defined? was_suppressed and defined? will_suppress + if was_suppressed and not will_suppress +%> (Normal service for <%= alert.level %> alerts has resumed.)<% + elsif will_suppress and not was_suppressed +%> (Further <%= alert.level %> alerts suppressed until things calm down.)<% + end +end +%> diff --git a/lib/mauve/notifiers/templates/xmpp.html.erb b/lib/mauve/notifiers/templates/xmpp.html.erb deleted file mode 100644 index 7792bd9..0000000 --- a/lib/mauve/notifiers/templates/xmpp.html.erb +++ /dev/null @@ -1,23 +0,0 @@ - <html xmlns="http://jabber.org/protocol/xhtml-im"><body xmlns="http://www.w3.org/1999/xhtml"> -<a href="<%=WebInterface.url_for(alert)%>"><%= alert.id%>: <%= alert.update_type.upcase %></a> (<%= alert.level %>): <% -case alert.update_type -when "cleared" -%><%= alert.cleared_at.to_s_relative %><% -when "acknowledged" -%><%= alert.acknowledged_at.to_s_relative %> by <%= alert.acknowledged_by %> until <%= alert.will_unacknowledge_at.to_s_human %><% -else -%><%= alert.raised_at.to_s_relative %><% -end -%>: <strong><%= alert.subject %></strong> <%= alert.summary %><% -if alert.source != alert.subject -%> -- from <%= alert.source %><% -end -%>.<% -if defined? was_suppressed and defined? will_suppress - if was_suppressed and not will_suppress -%><br /><em>Normal service for <%= alert.level %> alerts has resumed.</em><% - elsif will_suppress and not was_suppressed -%><br /><em>Further <%= alert.level %> alerts suppressed until things calm down.</em><% - end -end -%></body></html> diff --git a/lib/mauve/notifiers/templates/xmpp.txt.erb b/lib/mauve/notifiers/templates/xmpp.txt.erb deleted file mode 100644 index c148f41..0000000 --- a/lib/mauve/notifiers/templates/xmpp.txt.erb +++ /dev/null @@ -1,22 +0,0 @@ -<%=alert.id %>: <%= alert.update_type.upcase %> (<%= alert.level %>): <% -case alert.update_type -when "cleared" -%><%= alert.cleared_at.to_s_relative %><% -when "acknowledged" -%><%= alert.acknowledged_at.to_s_relative %> by <%= alert.acknowledged_by %> until <%= alert.will_unacknowledge_at.to_s_human %><% -else -%><%= alert.raised_at.to_s_relative %><% -end -%>: <%= alert.subject %> <%= alert.summary %><% -if alert.source != alert.subject -%> -- from <%= alert.source %><% -end -%>. <%=WebInterface.url_for(alert)%><% -if defined? was_suppressed and defined? will_suppress - if was_suppressed and not will_suppress -%> (Normal service for <%= alert.level %> alerts has resumed.)<% - elsif will_suppress and not was_suppressed -%> (Further <%= alert.level %> alerts suppressed until things calm down.)<% - end -end -%> diff --git a/lib/mauve/notifiers/xmpp.rb b/lib/mauve/notifiers/xmpp.rb deleted file mode 100644 index 8ab6d48..0000000 --- a/lib/mauve/notifiers/xmpp.rb +++ /dev/null @@ -1,837 +0,0 @@ -require 'log4r' -require 'xmpp4r' -require 'xmpp4r/roster' -require 'xmpp4r/muc' -# require 'xmpp4r/xhtml' -# require 'xmpp4r/discovery/helper/helper' -require 'mauve/notifiers/debug' - - - -# -# A couple of monkey patches to fix up all this nonsense. -# -module Jabber - # - # Monkey patch of the close commands. For good reasons, though I can't - # remember why. - # - class Stream - def close - # - # Just close - # - close! - end - - def close! - 10.times do - pr = 0 - @tbcbmutex.synchronize { pr = @processing } - break if pr == 0 - Thread::pass if pr > 0 - sleep 1 - end - - # Order Matters here! If this method is called from within - # @parser_thread then killing @parser_thread first would - # mean the other parts of the method fail to execute. - # That would be bad. So kill parser_thread last - @tbcbmutex.synchronize { @processing = 0 } - if @fd and !@fd.closed? - @fd.close - stop - end - @status = DISCONNECTED - end - end -end - - - - -module Mauve - module Notifiers - - # - # This is the Jabber/XMMP notifiers module. - # - module Xmpp - - # - # The default provider is XMMP, although this should really be broken out - # into its own provider to allow multple ways of doing XMPP. - # - class Default - - include Jabber - - # Atrtribute. - attr_reader :name - - # Atrtribute. - attr_accessor :password - - def initialize(name) - Jabber::logger = self.logger -# Jabber::debug = true -# Jabber::warnings = true - - @name = name - @mucs = {} - @roster = nil - @closing = false - @client = nil - end - - # The logger instance - # - # @return [Log4r::Logger] - def logger - # Give the logger a sane name - @logger ||= Log4r::Logger.new self.class.to_s.sub(/::Default$/,"") - end - - # Sets the client's JID - # - # @param [String] jid The JID required. - # @return [Jabber::JID] The client JID. - def jid=(jid) - @jid = JID.new(jid) - end - - # Connects to the XMPP server, and sets up the roster - # - # @return [Jabber::Client, NilClass] The connected client, or nil in the case of failure - def connect - logger.debug "Starting connection to #{@jid}" - - # Make sure we're disconnected. - self.close if @client.is_a?(Client) - - @client = Client.new(@jid) - - @closing = false - @client.connect - @client.auth_nonsasl(@password, false) - @roster = Roster::Helper.new(@client) - - # Unconditionally accept all roster add requests, and respond with a - # roster add + subscription request of our own if we're not subscribed - # already - @roster.add_subscription_request_callback do |ri, presence| - Thread.new do - if is_known_contact?(presence.from) - logger.info("Accepting subscription request from #{presence.from}") - @roster.accept_subscription(presence.from) - ensure_roster_and_subscription!(presence.from) - else - logger.info("Declining subscription request from #{presence.from}") - @roster.decline_subscription(presence.from) - end - end.join - end - - @client.add_message_callback do |m| - receive_message(m) - end - - @roster.wait_for_roster - - @client.send(Presence.new(nil, "Woo!").set_type(nil)) - - logger.info "Connected as #{@jid}" - - # @client.on_exception do |ex, stream, where| - # - # The XMPP4R exception clauses in Stream all close the stream, so - # we just need to reconnect. - # - # unless ex.nil? or @closing - # logger.warn(["Caught",ex.class,ex.to_s,"during XMPP",where].join(" ")) - # logger.debug ex.backtrace.join("\n") - # self.close - # end - # end - rescue StandardError => ex - logger.error "Connect failed #{ex.to_s}" - logger.debug ex.backtrace.join("\n") - self.close - @client = nil - end - - def stop - @client.stop - end - - # - # Closes the XMPP connection, if possible. Sets @client to nil. - # - # @return [NilClass] - def close - @closing = true - if @client - if @client.is_connected? - @mucs.each do |jid, muc| - muc[:client].exit("Goodbye!") if muc[:client].active? - end - @client.send(Presence.new(nil, "Goodbye!").set_type(:unavailable)) - end - @client.close! - end - @client = nil - end - - # Determines if the client is ready. - # - # @return [Boolean] - def ready? - @client.is_a?(Jabber::Client) and @client.is_connected? - end - - # Attempt to send an alert using XMPP. - # - # @param [String] destination The JID you're sending the alert to. This should be - # a bare JID in the case of an individual, or +muc:room@server+ for - # chatrooms (XEP0045). - # - # @param [Mauve::Alert] alert This is turned into a pretty - # message and sent to the destination as a message, if +conditions+ - # are met. - # - # @param [Array] all_alerts Currently ignored. - # - # @param [Hash] conditions Conditions that determine if an alert should be sent - # - # @option conditions [Array] :if_presence Checks whether the jid in question - # has a presence matching one or more of the choices - see - # Mauve::Notifiers::Xmpp::Default#check_jid_has_presence for options. - # - # @return [Boolean] - def send_alert(destination, alert, all_alerts, conditions = {}) - destination_jid = JID.new(destination) - - was_suppressed = conditions[:was_suppressed] || false - will_suppress = conditions[:will_suppress] || false - - if conditions && !check_alert_conditions(destination_jid, conditions) - logger.info("Alert conditions not met, not sending XMPP alert to #{destination_jid}") - return false - end - - template_file = File.join(File.dirname(__FILE__),"templates","xmpp.txt.erb") - - txt = if File.exists?(template_file) - ERB.new(File.read(template_file)).result(binding).chomp - else - logger.error("Could not find xmpp.txt.erb template") - alert.to_s - end - - template_file = File.join(File.dirname(__FILE__),"templates","xmpp.html.erb") - - xhtml = if File.exists?(template_file) - ERB.new(File.read(template_file)).result(binding).chomp - else - logger.error("Could not find xmpp.txt.erb template") - alert.to_s - end - - msg_type = (is_muc?(destination_jid) ? :groupchat : :chat) - - send_message(destination_jid, txt, xhtml, msg_type) - end - - # Sends a message to the destionation. - def send_message(jid, msg, html_msg=nil, msg_type=:chat) - return false unless self.ready? - - jid = JID.new(jid) unless jid.is_a?(JID) - - message = Message.new(jid) - message.body = msg - if html_msg - begin - html_msg = REXML::Document.new(html_msg) unless html_msg.is_a?(REXML::Document) - message.add_element(html_msg) - rescue REXML::ParseException - logger.error "Bad XHTML: #{html_msg.inspect}" - end - end - - message.to = jid - message.type = msg_type - - if message.type == :groupchat and is_muc?(jid) - jid = join_muc(jid.strip) - muc = @mucs[jid][:client] - - if muc - muc.send(message) - true - else - logger.warn "Failed to join MUC #{jid} when trying to send a message" - false - end - else - # - # We aren't interested in sending things to people who aren't online. - # - ensure_roster_and_subscription!(jid) - - if check_jid_has_presence(jid) - @client.send(message) - true - else - false - end - end - end - - # - # Joins a chat, and returns the stripped JID of the chat joined. - # - def join_muc(jid, password=nil) - self.connect unless self.ready? - - return unless self.ready? - - if jid.is_a?(String) and jid =~ /^muc:(.*)/ - jid = JID.new($1) - end - - unless jid.is_a?(JID) - logger.warn "#{jid} is not a MUC" - return - end - - jid.resource = @client.jid.resource if jid.resource.to_s.empty? - - if !@mucs[jid.strip] - - logger.debug("Adding new MUC client for #{jid}") - - @mucs[jid.strip] = {:jid => jid, :password => password, :client => Jabber::MUC::MUCClient.new(@client)} - - # Add some callbacks - @mucs[jid.strip][:client].add_message_callback do |m| - receive_message(m) - end - - @mucs[jid.strip][:client].add_private_message_callback do |m| - receive_message(m) - end - - end - - if !@mucs[jid.strip][:client].active? - # - # Make sure we have a resource. - # - @mucs[jid.strip][:client].join(jid, password) - - logger.info("Joined #{jid.strip}") - else - logger.debug("Already joined #{jid.strip}.") - end - - # - # Return the JID object - # - jid.strip - end - - # - # Checks whether the destination JID is a MUC. - # - def is_muc?(jid) - (jid.is_a?(JID) and @mucs.keys.include?(jid.strip)) or - (jid.to_s =~ /^muc:(.*)/) - - # - # It would be nice to use service discovery to determin this, but it - # turns out that it is shite in xmpp4r. It doesn't return straight - # away with an answer, making it a bit useless. Some sort of weird - # threading issue, I think. - # - # begin - # logger.warn caller.join("\n") - # cl = Discovery::Helper.new(@client) - # res = cl.get_info_for(jid.strip) - # @client.wait - # logger.warn "hello #{res.inspect}" - # res.is_a?(Discovery::IqQueryDiscoInfo) and res.identity.category == :conference - # rescue Jabber::ServerError => ex - # false - # end - end - - # - # Checks to see if the JID is in our roster, and whether we are - # subscribed to it or not. Will add to the roster and subscribe as - # is necessary to ensure both are true. - # - def ensure_roster_and_subscription!(jid) - self.connect unless self.ready? - - return unless self.ready? - - return jid if is_muc?(jid) - - jid = JID.new(jid) unless jid.is_a?(JID) - - ri = @roster.find(jid).values.first - @roster.add(jid, nil, true) if ri.nil? - - ri = @roster.find(jid).values.first - ri.subscribe unless [:to, :both, :remove].include?(ri.subscription) - ri.jid - rescue StandardError => ex - logger.error("Problem ensuring that #{jid} is subscribed and in mauve's roster: #{ex.inspect}") - nil - end - - protected - - def receive_message(msg) - # - # Don't talk to self - # - if @jid == msg.from or @mucs.any?{|jid, muc| muc.is_a?(Hash) and muc.has_key?(:client) and muc[:client].jid == msg.from} - return nil - end - - # We only want to hear messages from known contacts. - unless is_known_contact?(msg.from) - # ignore message - logger.info "Ignoring message from unknown contact #{msg.from}" - return nil - end - - case msg.type - when :error - receive_error_message(msg) - when :groupchat - receive_groupchat_message(msg) - else - receive_normal_message(msg) - end - end - - def receive_error_message(msg) - logger.warn("Caught XMPP error #{msg}") - nil - end - - def receive_normal_message(msg) - # - # Treat invites specially - # - if msg.x("jabber:x:conference") - # - # recieved an invite. Need to mangle the jid. - # - jid =JID.new(msg.x("jabber:x:conference").attribute("jid")) - # jid.resource = @client.jid.resource - logger.info "Received an invite to #{jid}" - unless join_muc(jid) - logger.warn "Failed to join MUC #{jid} following invitation" - return nil - end - elsif msg.body - # - # Received a message with a body. - # - jid = msg.from - end - - if jid - reply = parse_command(msg) - send_message(jid, reply, nil, msg.type) - end - end - - def receive_groupchat_message(msg) - # - # We only want group chat messages from MUCs we're already joined to, - # that we've not sent ourselves, that are not historical, and that - # match our resource or node in the body. - # - if @mucs[msg.from.strip][:client].is_a?(MUC::MUCClient) and - msg.x("jabber:x:delay") == nil and - (msg.body =~ /\b#{Regexp.escape(@mucs[msg.from.strip][:client].jid.resource)}\b/i or - msg.body =~ /\b#{Regexp.escape(@client.jid.node)}\b/i) - - receive_normal_message(msg) - end - end - - def parse_command(msg) - case msg.body - when /help(\s+\w+)?/i - do_parse_help(msg) - when /show\s?/i - do_parse_show(msg) - when /ack/i - do_parse_ack(msg) - when /clear/i - do_parse_clear(msg) - when /destroy\s?/i - "Sorry -- destroy has been disabled. Try \"clear\" instead." - else - File.executable?('/usr/games/fortune') ? `/usr/games/fortune -s -n 60`.chomp : "I'd love to stay and chat, but I'm really quite busy" - end - end - - def do_parse_help(msg) - msg.body =~ /help\s+(\w+)/i - cmd = $1 - - return case cmd - when /^show/ - <<EOF -Show command: Lists all raised or acknowledged alerts, or the first or last few. - -e.g. - show -- shows all raised alerts - show ack -- shows all acknowledged alerts - show first 10 acknowledged -- shows first 10 acknowledged - show last 5 raised -- shows last 5 raised alerts -EOF - when /^ack/ - <<EOF -Acknowledge command: Acknowledges one or more alerts for a set period of time. - -The syntax is - - acknowledge <alert list> for <time period> because <note> - - * The alert list is a comma separated list. - * The time period can be spefied in terms of days, hours, minutes, seconds, - which can be wall-clock (default), working, or daytime (see the examples). - * The note is appended to the acknowledgement. - -e.g. - acknowledge 1 for 2 hours -- acknowledges alert no. 1 for 2 wall-clock hours - ack 1,2,3 for 2 working hours -- acknowledges alerts 1, 2, and 3 for 2 working hours - ack 4 for 3 days because something bad happened -- acknowledge alert 4 for 3 wall-clock days with the note "something bad happened" -EOF - when /^destroy/ - <<EOF -Destroy command: Destroys one or more alerts. - -The syntax is - - destroy <alert list> - -where <alert list> is a comma separated list of alert IDs. - -e.g. - destroy 1,2,3 -- destroys alerts 1, 2, and 3. -EOF - - else - "I am Mauve #{Mauve::VERSION}. I understand \"help\", \"show\", \"acknowledge\", and \"destroy\" commands. Try \"help show\"." - end - end - - def do_parse_show(msg) - return "Sorry -- I don't understand your show command." unless - msg.body =~ /show(?:\s+(first|last)\s+(\d+))?(?:\s+(events|raised|ack(?:d|nowledged)?))?/i - - first_or_last = $1 - n_items = ($2 || -1).to_i - - type = $3 || "raised" - type = "acknowledged" if type =~ /^ack/ - - msg = [] - - items = case type - when "acknowledged" - Alert.all_acknowledged.all(:order => [:acknowledged_at.asc]) - when "events" - History.all(:created_at.gte => Time.now - 24.hours) - else - Alert.all_unacknowledged.all(:order => [:raised_at.asc]) - end - - if first_or_last == "first" - items = items.first(n_items) if n_items >= 0 - elsif first_or_last == "last" - items = items.last(n_items) if n_items >= 0 - end - - return "Nothing to show" if items.length == 0 - - template_file = File.join(File.dirname(__FILE__),"templates","xmpp.txt.erb") - if File.exists?(template_file) - template = File.read(template_file) - else - logger.error("Could not find xmpp.txt.erb template") - template = nil - end - - (["Alerts #{type}:"] + items.collect do |alert| - ERB.new(template).result(binding).chomp - end).join("\n") - end - - def do_parse_ack(msg) - return "Sorry -- I don't understand your acknowledge command." unless - msg.body =~ /ack(?:nowledge)?\s+([\d\D]+)\s+for\s+(\d+(?:\.\d+)?)\s+(work(?:ing)?|day(?:time)?|wall(?:-?clock)?)?\s*(day|hour|min(?:ute)?|sec(?:ond))s?(?:\s+(?:cos|cause|as|because)?\s*(.*))?/i - - alerts, n_hours, type_hours, dhms, note = [$1,$2, $3, $4, $5] - - alerts = alerts.split(/\D+/) - - n_hours = case dhms - when /^day/ - n_hours.to_f * 24.0 - when /^min/ - n_hours.to_f / 60.0 - when /^sec/ - n_hours.to_f / 3600.0 - else - n_hours.to_f - end - - type_hours = case type_hours - when /^day/ - "daytime" - when /^work/ - "working" - else - "wallclock" - end - - begin - now = Time.now - ack_until = now.in_x_hours(n_hours, type_hours) - rescue RangeError - return "I'm sorry, you tried to acknowedge for far too long, and my buffers overflowed!" - end - - username = get_username_for(msg.from) - - if is_muc?(Configuration.current.people[username].xmpp) - return "I'm sorry -- if you want to acknowledge alerts, please do it from a private chat" - end - - msg = [] - msg << "Acknowledgement results:" if alerts.length > 1 - - succeeded = [] - - alerts.each do |alert_id| - alert = Alert.get(alert_id) - - if alert.nil? - msg << "#{alert_id}: alert not found" - next - end - - if alert.cleared? - msg << "#{alert_id}: alert already cleared" if alert.cleared? - next - end - - if alert.acknowledge!(Configuration.current.people[username], ack_until) - msg << "#{alert_id}: Acknowledged until #{alert.will_unacknowledge_at.to_s_human}" - succeeded << alert - else - msg << "#{alert_id}: Acknowledgement failed." - end - end - - # - # Add the note. - # - unless note.to_s.empty? - note = Alert.remove_html(note) - h = History.new(:alerts => succeeded, :type => "note", :event => note.to_s, :user => username) - logger.debug h.errors unless h.save - end - - return msg.join("\n") - end - - def do_parse_clear(msg) - return "Sorry -- I don't understand your clear command." unless - msg.body =~ /clear\s+([\d\D]+)(?:\s+(?:coz|cause|cos|because|as)?\s*(.*))?/i - - alerts = $1.split(/\D+/) - note = $2 - - username = get_username_for(msg.from) - - if is_muc?(Configuration.current.people[username].xmpp) - return "I'm sorry -- if you want to clear alerts, please do it from a private chat" - end - - msg = [] - msg << "Clearing results:" if alerts.length > 1 - - alerts.each do |alert_id| - alert = Alert.get(alert_id) - - if alert.nil? - msg << "#{alert_id}: alert not found." - next - end - - if alert.cleared? - msg << "#{alert_id}: alert already cleared." - next - end - - if alert.clear! - msg << "#{alert.to_s} cleared." - else - msg << "#{alert.to_s}: clearing failed." - end - end - - # - # Add the note. - # - unless note.to_s.empty? - note = Alert.remove_html(note) - h = History.new(:alerts => succeeded, :type => "note", :event => note.to_s, :user => username) - logger.debug h.errors unless h.save - end - - return msg.join("\n") - end - - def check_alert_conditions(destination, conditions) - any_failed = conditions.keys.collect do |key| - case key - when :if_presence - check_jid_has_presence(destination, conditions[:if_presence]) - else - #raise ArgumentError.new("Unknown alert condition, #{key} => #{conditions[key]}") - # FIXME - clean up this use of :conditions to pass arbitrary - # parameters to notifiers; for now we need to ignore this. - true - end - end.include?(false) - !any_failed - end - - # Checks our roster to see whether the jid has a resource with at least - # one of the included presences. Acceptable +presence+ types and their - # meanings for individuals: - # - # :online, :offline - user is logged in or out - # :available - jabber status is nil (available) or chat - # :unavailable - - jabber status is away, dnd or xa - # :unknown - don't know (not in roster) - # - # Returns true if at least one of the presence specifiers for the jid - # is met, false otherwise. Note that if the alerter can't see the alertee's - # presence, only 'unknown' will match - generally, you'll want [:online, :unknown] - def check_jid_has_presence(jid, presence_or_presences = [:online, :unknown]) - return true if is_muc?(jid) - - jid = JID.new(jid) unless jid.is_a?(JID) - - self.connect unless self.ready? - - return false unless self.ready? - - presences = [presence_or_presences].flatten - roster_item = @roster.find(jid) - roster_item = roster_item[roster_item.keys[0]] - resource_presences = [] - roster_item.each_presence {|p| resource_presences << p.show } if roster_item - - results = presences.collect do |need_presence| - case need_presence - when :online - (roster_item && - [:to, :both].include?(roster_item.subscription) && - roster_item.online?) - when :offline - (roster_item && - [:to, :both].include?(roster_item.subscription) && - !roster_item.online?) - when :available - (roster_item && - [:to, :both].include?(roster_item.subscription) && - (resource_presences.include?(nil) || - resource_presences.include?(:chat))) - # No resources are nil or chat - when :unavailable - (roster_item && - [:to, :both].include?(roster_item.subscription) && - (resource_presences - [:away, :dnd, :xa]).empty?) - # Not in roster or don't know subscription - when :unknown - (roster_item.nil? || - [:none, :from].include?(roster_item.subscription)) - else - raise ArgumentError.new("Unknown presence possibility: #{need_presence}") - end - end - results.include?(true) - end - - # - # Returns the username of the jid, if any - # - def get_username_for(jid) - jid = JID.new(jid) unless jid.is_a?(JID) - - # - # Resolve MUC JIDs. - # - if is_muc?(jid) - muc_jid = get_jid_from_muc_jid(jid) - jid = muc_jid unless muc_jid.nil? - end - - ans = Configuration.current.people.find do |username, person| - next unless person.xmpp.is_a?(JID) - person.xmpp.strip == jid.strip - end - - ans.nil? ? ans : ans.first - end - - # - # Tries to establish a real JID from a MUC JID. - # - def get_jid_from_muc_jid(jid) - # - # Resolve the JID for MUCs. - # - jid = JID.new(jid) unless jid.is_a?(JID) - return nil unless @mucs.has_key?(jid.strip) - return nil unless @mucs[jid.strip].has_key?(:client) - return nil unless @mucs[jid.strip][:client].active? - - roster = @mucs[jid.strip][:client].roster[jid.resource] - return nil unless roster - - x = roster.x('http://jabber.org/protocol/muc#user') - return nil unless x - - items = x.items - return nil if items.nil? or items.empty? - - jids = items.collect{|item| item.jid} - return nil if jids.empty? - - jids.first - end - - def is_known_contact?(jid) - !get_username_for(jid).nil? - end - - end - end - end -end - diff --git a/lib/mauve/people_list.rb b/lib/mauve/people_list.rb index 7124241..8178635 100644 --- a/lib/mauve/people_list.rb +++ b/lib/mauve/people_list.rb @@ -25,11 +25,6 @@ module Mauve alias username label - # - # A dummy XMPP method. - # - def xmpp; nil ; end - # Append an Array or String to a list # # @param [Array or String] arr diff --git a/lib/mauve/person.rb b/lib/mauve/person.rb index 1e26835..de8038c 100644 --- a/lib/mauve/person.rb +++ b/lib/mauve/person.rb @@ -5,7 +5,7 @@ require 'log4r' module Mauve class Person - attr_reader :username, :password, :urgent, :normal, :low, :email, :xmpp, :sms + attr_reader :username, :password, :urgent, :normal, :low, :email, :sms attr_reader :suppressed, :notifications attr_reader :notify_when_off_sick, :notify_when_on_holiday @@ -18,7 +18,7 @@ module Mauve @username = username @password = nil @urgent = @normal = @low = nil - @email = @sms = @xmpp = @hipchat = nil + @email = @sms = @hipchat = nil @notify_when_on_holiday = @notify_when_off_sick = false end @@ -76,14 +76,6 @@ module Mauve @sms = arg end - # Sets the xmpp parameter - # - # - def xmpp=(arg) - # raise ArgumentError, "xmpp expected a string, not a #{arg.class}" unless arg.is_a?(String) or arg.is_a?(Jabber::JID) - @xmpp = arg - end - # Sets up the hipchat parameter # # @@ -211,7 +203,7 @@ module Mauve # @return Log4r::Logger def logger ; @logger ||= Log4r::Logger.new self.class.to_s ; end - # This method makes sure things like +xmpp+ and +email+ work. + # This method makes sure things like +email+ work. # # @param [String] name The notification method to use # @param [Array or Hash] args Extra conditions to pass to this notification method diff --git a/lib/mauve/web_interface.rb b/lib/mauve/web_interface.rb index 5fa1fb8..3bad868 100644 --- a/lib/mauve/web_interface.rb +++ b/lib/mauve/web_interface.rb @@ -123,11 +123,15 @@ EOF # Uh-oh.. Intruder alert! # ok_urls = %w(/ /login /logout) - no_redirect_urls = %w(/ajax) - unless ok_urls.include?(request.path_info) - flash['error'] = "You must be logged in to access that page." - redirect "/login?next_page=#{request.path_info}" unless no_redirect_urls.any?{|u| /^#{u}/ =~ request.path_info } + unless ok_urls.include?(request.path_info) + # No auth, so we need to stop the page loading + if request.xhr? # No redirecting for AJAX requests + halt 403, {'Content-Type' => 'text/plain'}, 'You must be logged in to access this page.' + else + flash['error'] = 'You must be logged in to access that page.' + redirect "/login?next_page=#{request.path_info}" + end end end end diff --git a/static/javascript/jquery/jquery.min.js b/static/javascript/jquery/jquery.min.js deleted file mode 100644 index d02db07..0000000 --- a/static/javascript/jquery/jquery.min.js +++ /dev/null @@ -1,23 +0,0 @@ -/* - * jQuery JavaScript Library v1.4.2 - * http://jquery.com/ - * - * Copyright 2010, John Resig - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * Copyright 2010, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * - * Date: Sat Feb 13 22:33:48 2010 -0500 - */ -(function(aM,C){var a=function(aY,aZ){return new a.fn.init(aY,aZ)},n=aM.jQuery,R=aM.$,ab=aM.document,X,P=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,aW=/^.[^:#\[\.,]*$/,ax=/\S/,M=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,e=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,b=navigator.userAgent,u,K=false,ad=[],aG,at=Object.prototype.toString,ap=Object.prototype.hasOwnProperty,g=Array.prototype.push,F=Array.prototype.slice,s=Array.prototype.indexOf;a.fn=a.prototype={init:function(aY,a1){var a0,a2,aZ,a3;if(!aY){return this}if(aY.nodeType){this.context=this[0]=aY;this.length=1;return this}if(aY==="body"&&!a1){this.context=ab;this[0]=ab.body;this.selector="body";this.length=1;return this}if(typeof aY==="string"){a0=P.exec(aY);if(a0&&(a0[1]||!a1)){if(a0[1]){a3=(a1?a1.ownerDocument||a1:ab);aZ=e.exec(aY);if(aZ){if(a.isPlainObject(a1)){aY=[ab.createElement(aZ[1])];a.fn.attr.call(aY,a1,true)}else{aY=[a3.createElement(aZ[1])]}}else{aZ=J([a0[1]],[a3]);aY=(aZ.cacheable?aZ.fragment.cloneNode(true):aZ.fragment).childNodes}return a.merge(this,aY)}else{a2=ab.getElementById(a0[2]);if(a2){if(a2.id!==a0[2]){return X.find(aY)}this.length=1;this[0]=a2}this.context=ab;this.selector=aY;return this}}else{if(!a1&&/^\w+$/.test(aY)){this.selector=aY;this.context=ab;aY=ab.getElementsByTagName(aY);return a.merge(this,aY)}else{if(!a1||a1.jquery){return(a1||X).find(aY)}else{return a(a1).find(aY)}}}}else{if(a.isFunction(aY)){return X.ready(aY)}}if(aY.selector!==C){this.selector=aY.selector;this.context=aY.context}return a.makeArray(aY,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(aY){return aY==null?this.toArray():(aY<0?this.slice(aY)[0]:this[aY])},pushStack:function(aZ,a1,aY){var a0=a();if(a.isArray(aZ)){g.apply(a0,aZ)}else{a.merge(a0,aZ)}a0.prevObject=this;a0.context=this.context;if(a1==="find"){a0.selector=this.selector+(this.selector?" ":"")+aY}else{if(a1){a0.selector=this.selector+"."+a1+"("+aY+")"}}return a0},each:function(aZ,aY){return a.each(this,aZ,aY)},ready:function(aY){a.bindReady();if(a.isReady){aY.call(ab,a)}else{if(ad){ad.push(aY)}}return this},eq:function(aY){return aY===-1?this.slice(aY):this.slice(aY,+aY+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(aY){return this.pushStack(a.map(this,function(a0,aZ){return aY.call(a0,aZ,a0)}))},end:function(){return this.prevObject||a(null)},push:g,sort:[].sort,splice:[].splice};a.fn.init.prototype=a.fn;a.extend=a.fn.extend=function(){var a3=arguments[0]||{},a2=1,a1=arguments.length,a5=false,a6,a0,aY,aZ;if(typeof a3==="boolean"){a5=a3;a3=arguments[1]||{};a2=2}if(typeof a3!=="object"&&!a.isFunction(a3)){a3={}}if(a1===a2){a3=this;--a2}for(;a2<a1;a2++){if((a6=arguments[a2])!=null){for(a0 in a6){aY=a3[a0];aZ=a6[a0];if(a3===aZ){continue}if(a5&&aZ&&(a.isPlainObject(aZ)||a.isArray(aZ))){var a4=aY&&(a.isPlainObject(aY)||a.isArray(aY))?aY:a.isArray(aZ)?[]:{};a3[a0]=a.extend(a5,a4,aZ)}else{if(aZ!==C){a3[a0]=aZ}}}}}return a3};a.extend({noConflict:function(aY){aM.$=R;if(aY){aM.jQuery=n}return a},isReady:false,ready:function(){if(!a.isReady){if(!ab.body){return setTimeout(a.ready,13)}a.isReady=true;if(ad){var aZ,aY=0;while((aZ=ad[aY++])){aZ.call(ab,a)}ad=null}if(a.fn.triggerHandler){a(ab).triggerHandler("ready")}}},bindReady:function(){if(K){return}K=true;if(ab.readyState==="complete"){return a.ready()}if(ab.addEventListener){ab.addEventListener("DOMContentLoaded",aG,false);aM.addEventListener("load",a.ready,false)}else{if(ab.attachEvent){ab.attachEvent("onreadystatechange",aG);aM.attachEvent("onload",a.ready);var aY=false;try{aY=aM.frameElement==null}catch(aZ){}if(ab.documentElement.doScroll&&aY){x()}}}},isFunction:function(aY){return at.call(aY)==="[object Function]"},isArray:function(aY){return at.call(aY)==="[object Array]"},isPlainObject:function(aZ){if(!aZ||at.call(aZ)!=="[object Object]"||aZ.nodeType||aZ.setInterval){return false}if(aZ.constructor&&!ap.call(aZ,"constructor")&&!ap.call(aZ.constructor.prototype,"isPrototypeOf")){return false}var aY;for(aY in aZ){}return aY===C||ap.call(aZ,aY)},isEmptyObject:function(aZ){for(var aY in aZ){return false}return true},error:function(aY){throw aY},parseJSON:function(aY){if(typeof aY!=="string"||!aY){return null}aY=a.trim(aY);if(/^[\],:{}\s]*$/.test(aY.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){return aM.JSON&&aM.JSON.parse?aM.JSON.parse(aY):(new Function("return "+aY))()}else{a.error("Invalid JSON: "+aY)}},noop:function(){},globalEval:function(a0){if(a0&&ax.test(a0)){var aZ=ab.getElementsByTagName("head")[0]||ab.documentElement,aY=ab.createElement("script");aY.type="text/javascript";if(a.support.scriptEval){aY.appendChild(ab.createTextNode(a0))}else{aY.text=a0}aZ.insertBefore(aY,aZ.firstChild);aZ.removeChild(aY)}},nodeName:function(aZ,aY){return aZ.nodeName&&aZ.nodeName.toUpperCase()===aY.toUpperCase()},each:function(a1,a5,a0){var aZ,a2=0,a3=a1.length,aY=a3===C||a.isFunction(a1);if(a0){if(aY){for(aZ in a1){if(a5.apply(a1[aZ],a0)===false){break}}}else{for(;a2<a3;){if(a5.apply(a1[a2++],a0)===false){break}}}}else{if(aY){for(aZ in a1){if(a5.call(a1[aZ],aZ,a1[aZ])===false){break}}}else{for(var a4=a1[0];a2<a3&&a5.call(a4,a2,a4)!==false;a4=a1[++a2]){}}}return a1},trim:function(aY){return(aY||"").replace(M,"")},makeArray:function(a0,aZ){var aY=aZ||[];if(a0!=null){if(a0.length==null||typeof a0==="string"||a.isFunction(a0)||(typeof a0!=="function"&&a0.setInterval)){g.call(aY,a0)}else{a.merge(aY,a0)}}return aY},inArray:function(a0,a1){if(a1.indexOf){return a1.indexOf(a0)}for(var aY=0,aZ=a1.length;aY<aZ;aY++){if(a1[aY]===a0){return aY}}return -1},merge:function(a2,a0){var a1=a2.length,aZ=0;if(typeof a0.length==="number"){for(var aY=a0.length;aZ<aY;aZ++){a2[a1++]=a0[aZ]}}else{while(a0[aZ]!==C){a2[a1++]=a0[aZ++]}}a2.length=a1;return a2},grep:function(aZ,a3,aY){var a0=[];for(var a1=0,a2=aZ.length;a1<a2;a1++){if(!aY!==!a3(aZ[a1],a1)){a0.push(aZ[a1])}}return a0},map:function(aZ,a4,aY){var a0=[],a3;for(var a1=0,a2=aZ.length;a1<a2;a1++){a3=a4(aZ[a1],a1,aY);if(a3!=null){a0[a0.length]=a3}}return a0.concat.apply([],a0)},guid:1,proxy:function(a0,aZ,aY){if(arguments.length===2){if(typeof aZ==="string"){aY=a0;a0=aY[aZ];aZ=C}else{if(aZ&&!a.isFunction(aZ)){aY=aZ;aZ=C}}}if(!aZ&&a0){aZ=function(){return a0.apply(aY||this,arguments)}}if(a0){aZ.guid=a0.guid=a0.guid||aZ.guid||a.guid++}return aZ},uaMatch:function(aZ){aZ=aZ.toLowerCase();var aY=/(webkit)[ \/]([\w.]+)/.exec(aZ)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(aZ)||/(msie) ([\w.]+)/.exec(aZ)||!/compatible/.test(aZ)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(aZ)||[];return{browser:aY[1]||"",version:aY[2]||"0"}},browser:{}});u=a.uaMatch(b);if(u.browser){a.browser[u.browser]=true;a.browser.version=u.version}if(a.browser.webkit){a.browser.safari=true}if(s){a.inArray=function(aY,aZ){return s.call(aZ,aY)}}X=a(ab);if(ab.addEventListener){aG=function(){ab.removeEventListener("DOMContentLoaded",aG,false);a.ready()}}else{if(ab.attachEvent){aG=function(){if(ab.readyState==="complete"){ab.detachEvent("onreadystatechange",aG);a.ready()}}}}function x(){if(a.isReady){return}try{ab.documentElement.doScroll("left")}catch(aY){setTimeout(x,1);return}a.ready()}function aV(aY,aZ){if(aZ.src){a.ajax({url:aZ.src,async:false,dataType:"script"})}else{a.globalEval(aZ.text||aZ.textContent||aZ.innerHTML||"")}if(aZ.parentNode){aZ.parentNode.removeChild(aZ)}}function an(aY,a6,a4,a0,a3,a5){var aZ=aY.length;if(typeof a6==="object"){for(var a1 in a6){an(aY,a1,a6[a1],a0,a3,a4)}return aY}if(a4!==C){a0=!a5&&a0&&a.isFunction(a4);for(var a2=0;a2<aZ;a2++){a3(aY[a2],a6,a0?a4.call(aY[a2],a2,a3(aY[a2],a6)):a4,a5)}return aY}return aZ?a3(aY[0],a6):C}function aP(){return(new Date).getTime()}(function(){a.support={};var a4=ab.documentElement,a3=ab.createElement("script"),aY=ab.createElement("div"),aZ="script"+aP();aY.style.display="none";aY.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";var a6=aY.getElementsByTagName("*"),a5=aY.getElementsByTagName("a")[0];if(!a6||!a6.length||!a5){return}a.support={leadingWhitespace:aY.firstChild.nodeType===3,tbody:!aY.getElementsByTagName("tbody").length,htmlSerialize:!!aY.getElementsByTagName("link").length,style:/red/.test(a5.getAttribute("style")),hrefNormalized:a5.getAttribute("href")==="/a",opacity:/^0.55$/.test(a5.style.opacity),cssFloat:!!a5.style.cssFloat,checkOn:aY.getElementsByTagName("input")[0].value==="on",optSelected:ab.createElement("select").appendChild(ab.createElement("option")).selected,parentNode:aY.removeChild(aY.appendChild(ab.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};a3.type="text/javascript";try{a3.appendChild(ab.createTextNode("window."+aZ+"=1;"))}catch(a1){}a4.insertBefore(a3,a4.firstChild);if(aM[aZ]){a.support.scriptEval=true;delete aM[aZ]}try{delete a3.test}catch(a1){a.support.deleteExpando=false}a4.removeChild(a3);if(aY.attachEvent&&aY.fireEvent){aY.attachEvent("onclick",function a7(){a.support.noCloneEvent=false;aY.detachEvent("onclick",a7)});aY.cloneNode(true).fireEvent("onclick")}aY=ab.createElement("div");aY.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";var a0=ab.createDocumentFragment();a0.appendChild(aY.firstChild);a.support.checkClone=a0.cloneNode(true).cloneNode(true).lastChild.checked;a(function(){var a8=ab.createElement("div");a8.style.width=a8.style.paddingLeft="1px";ab.body.appendChild(a8);a.boxModel=a.support.boxModel=a8.offsetWidth===2;ab.body.removeChild(a8).style.display="none";a8=null});var a2=function(a8){var ba=ab.createElement("div");a8="on"+a8;var a9=(a8 in ba);if(!a9){ba.setAttribute(a8,"return;");a9=typeof ba[a8]==="function"}ba=null;return a9};a.support.submitBubbles=a2("submit");a.support.changeBubbles=a2("change");a4=a3=aY=a6=a5=null})();a.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var aI="jQuery"+aP(),aH=0,aT={};a.extend({cache:{},expando:aI,noData:{embed:true,object:true,applet:true},data:function(a0,aZ,a2){if(a0.nodeName&&a.noData[a0.nodeName.toLowerCase()]){return}a0=a0==aM?aT:a0;var a3=a0[aI],aY=a.cache,a1;if(!a3&&typeof aZ==="string"&&a2===C){return null}if(!a3){a3=++aH}if(typeof aZ==="object"){a0[aI]=a3;a1=aY[a3]=a.extend(true,{},aZ)}else{if(!aY[a3]){a0[aI]=a3;aY[a3]={}}}a1=aY[a3];if(a2!==C){a1[aZ]=a2}return typeof aZ==="string"?a1[aZ]:a1},removeData:function(a0,aZ){if(a0.nodeName&&a.noData[a0.nodeName.toLowerCase()]){return}a0=a0==aM?aT:a0;var a2=a0[aI],aY=a.cache,a1=aY[a2];if(aZ){if(a1){delete a1[aZ];if(a.isEmptyObject(a1)){a.removeData(a0)}}}else{if(a.support.deleteExpando){delete a0[a.expando]}else{if(a0.removeAttribute){a0.removeAttribute(a.expando)}}delete aY[a2]}}});a.fn.extend({data:function(aY,a0){if(typeof aY==="undefined"&&this.length){return a.data(this[0])}else{if(typeof aY==="object"){return this.each(function(){a.data(this,aY)})}}var a1=aY.split(".");a1[1]=a1[1]?"."+a1[1]:"";if(a0===C){var aZ=this.triggerHandler("getData"+a1[1]+"!",[a1[0]]);if(aZ===C&&this.length){aZ=a.data(this[0],aY)}return aZ===C&&a1[1]?this.data(a1[0]):aZ}else{return this.trigger("setData"+a1[1]+"!",[a1[0],a0]).each(function(){a.data(this,aY,a0)})}},removeData:function(aY){return this.each(function(){a.removeData(this,aY)})}});a.extend({queue:function(aZ,aY,a1){if(!aZ){return}aY=(aY||"fx")+"queue";var a0=a.data(aZ,aY);if(!a1){return a0||[]}if(!a0||a.isArray(a1)){a0=a.data(aZ,aY,a.makeArray(a1))}else{a0.push(a1)}return a0},dequeue:function(a1,a0){a0=a0||"fx";var aY=a.queue(a1,a0),aZ=aY.shift();if(aZ==="inprogress"){aZ=aY.shift()}if(aZ){if(a0==="fx"){aY.unshift("inprogress")}aZ.call(a1,function(){a.dequeue(a1,a0)})}}});a.fn.extend({queue:function(aY,aZ){if(typeof aY!=="string"){aZ=aY;aY="fx"}if(aZ===C){return a.queue(this[0],aY)}return this.each(function(a1,a2){var a0=a.queue(this,aY,aZ);if(aY==="fx"&&a0[0]!=="inprogress"){a.dequeue(this,aY)}})},dequeue:function(aY){return this.each(function(){a.dequeue(this,aY)})},delay:function(aZ,aY){aZ=a.fx?a.fx.speeds[aZ]||aZ:aZ;aY=aY||"fx";return this.queue(aY,function(){var a0=this;setTimeout(function(){a.dequeue(a0,aY)},aZ)})},clearQueue:function(aY){return this.queue(aY||"fx",[])}});var ao=/[\n\t]/g,S=/\s+/,av=/\r/g,aQ=/href|src|style/,d=/(button|input)/i,z=/(button|input|object|select|textarea)/i,j=/^(a|area)$/i,I=/radio|checkbox/;a.fn.extend({attr:function(aY,aZ){return an(this,aY,aZ,true,a.attr)},removeAttr:function(aY,aZ){return this.each(function(){a.attr(this,aY,"");if(this.nodeType===1){this.removeAttribute(aY)}})},addClass:function(a5){if(a.isFunction(a5)){return this.each(function(a8){var a7=a(this);a7.addClass(a5.call(this,a8,a7.attr("class")))})}if(a5&&typeof a5==="string"){var aY=(a5||"").split(S);for(var a1=0,a0=this.length;a1<a0;a1++){var aZ=this[a1];if(aZ.nodeType===1){if(!aZ.className){aZ.className=a5}else{var a2=" "+aZ.className+" ",a4=aZ.className;for(var a3=0,a6=aY.length;a3<a6;a3++){if(a2.indexOf(" "+aY[a3]+" ")<0){a4+=" "+aY[a3]}}aZ.className=a.trim(a4)}}}}return this},removeClass:function(a3){if(a.isFunction(a3)){return this.each(function(a7){var a6=a(this);a6.removeClass(a3.call(this,a7,a6.attr("class")))})}if((a3&&typeof a3==="string")||a3===C){var a4=(a3||"").split(S);for(var a0=0,aZ=this.length;a0<aZ;a0++){var a2=this[a0];if(a2.nodeType===1&&a2.className){if(a3){var a1=(" "+a2.className+" ").replace(ao," ");for(var a5=0,aY=a4.length;a5<aY;a5++){a1=a1.replace(" "+a4[a5]+" "," ")}a2.className=a.trim(a1)}else{a2.className=""}}}}return this},toggleClass:function(a1,aZ){var a0=typeof a1,aY=typeof aZ==="boolean";if(a.isFunction(a1)){return this.each(function(a3){var a2=a(this);a2.toggleClass(a1.call(this,a3,a2.attr("class"),aZ),aZ)})}return this.each(function(){if(a0==="string"){var a4,a3=0,a2=a(this),a5=aZ,a6=a1.split(S);while((a4=a6[a3++])){a5=aY?a5:!a2.hasClass(a4);a2[a5?"addClass":"removeClass"](a4)}}else{if(a0==="undefined"||a0==="boolean"){if(this.className){a.data(this,"__className__",this.className)}this.className=this.className||a1===false?"":a.data(this,"__className__")||""}}})},hasClass:function(aY){var a1=" "+aY+" ";for(var a0=0,aZ=this.length;a0<aZ;a0++){if((" "+this[a0].className+" ").replace(ao," ").indexOf(a1)>-1){return true}}return false},val:function(a5){if(a5===C){var aZ=this[0];if(aZ){if(a.nodeName(aZ,"option")){return(aZ.attributes.value||{}).specified?aZ.value:aZ.text}if(a.nodeName(aZ,"select")){var a3=aZ.selectedIndex,a6=[],a7=aZ.options,a2=aZ.type==="select-one";if(a3<0){return null}for(var a0=a2?a3:0,a4=a2?a3+1:a7.length;a0<a4;a0++){var a1=a7[a0];if(a1.selected){a5=a(a1).val();if(a2){return a5}a6.push(a5)}}return a6}if(I.test(aZ.type)&&!a.support.checkOn){return aZ.getAttribute("value")===null?"on":aZ.value}return(aZ.value||"").replace(av,"")}return C}var aY=a.isFunction(a5);return this.each(function(ba){var a9=a(this),bb=a5;if(this.nodeType!==1){return}if(aY){bb=a5.call(this,ba,a9.val())}if(typeof bb==="number"){bb+=""}if(a.isArray(bb)&&I.test(this.type)){this.checked=a.inArray(a9.val(),bb)>=0}else{if(a.nodeName(this,"select")){var a8=a.makeArray(bb);a("option",this).each(function(){this.selected=a.inArray(a(this).val(),a8)>=0});if(!a8.length){this.selectedIndex=-1}}else{this.value=bb}}})}});a.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(aZ,aY,a4,a7){if(!aZ||aZ.nodeType===3||aZ.nodeType===8){return C}if(a7&&aY in a.attrFn){return a(aZ)[aY](a4)}var a0=aZ.nodeType!==1||!a.isXMLDoc(aZ),a3=a4!==C;aY=a0&&a.props[aY]||aY;if(aZ.nodeType===1){var a2=aQ.test(aY);if(aY==="selected"&&!a.support.optSelected){var a5=aZ.parentNode;if(a5){a5.selectedIndex;if(a5.parentNode){a5.parentNode.selectedIndex}}}if(aY in aZ&&a0&&!a2){if(a3){if(aY==="type"&&d.test(aZ.nodeName)&&aZ.parentNode){a.error("type property can't be changed")}aZ[aY]=a4}if(a.nodeName(aZ,"form")&&aZ.getAttributeNode(aY)){return aZ.getAttributeNode(aY).nodeValue}if(aY==="tabIndex"){var a6=aZ.getAttributeNode("tabIndex");return a6&&a6.specified?a6.value:z.test(aZ.nodeName)||j.test(aZ.nodeName)&&aZ.href?0:C}return aZ[aY]}if(!a.support.style&&a0&&aY==="style"){if(a3){aZ.style.cssText=""+a4}return aZ.style.cssText}if(a3){aZ.setAttribute(aY,""+a4)}var a1=!a.support.hrefNormalized&&a0&&a2?aZ.getAttribute(aY,2):aZ.getAttribute(aY);return a1===null?C:a1}return a.style(aZ,aY,a4)}});var aC=/\.(.*)$/,A=function(aY){return aY.replace(/[^\w\s\.\|`]/g,function(aZ){return"\\"+aZ})};a.event={add:function(a1,a5,ba,a3){if(a1.nodeType===3||a1.nodeType===8){return}if(a1.setInterval&&(a1!==aM&&!a1.frameElement)){a1=aM}var aZ,a9;if(ba.handler){aZ=ba;ba=aZ.handler}if(!ba.guid){ba.guid=a.guid++}var a6=a.data(a1);if(!a6){return}var bb=a6.events=a6.events||{},a4=a6.handle,a4;if(!a4){a6.handle=a4=function(){return typeof a!=="undefined"&&!a.event.triggered?a.event.handle.apply(a4.elem,arguments):C}}a4.elem=a1;a5=a5.split(" ");var a8,a2=0,aY;while((a8=a5[a2++])){a9=aZ?a.extend({},aZ):{handler:ba,data:a3};if(a8.indexOf(".")>-1){aY=a8.split(".");a8=aY.shift();a9.namespace=aY.slice(0).sort().join(".")}else{aY=[];a9.namespace=""}a9.type=a8;a9.guid=ba.guid;var a0=bb[a8],a7=a.event.special[a8]||{};if(!a0){a0=bb[a8]=[];if(!a7.setup||a7.setup.call(a1,a3,aY,a4)===false){if(a1.addEventListener){a1.addEventListener(a8,a4,false)}else{if(a1.attachEvent){a1.attachEvent("on"+a8,a4)}}}}if(a7.add){a7.add.call(a1,a9);if(!a9.handler.guid){a9.handler.guid=ba.guid}}a0.push(a9);a.event.global[a8]=true}a1=null},global:{},remove:function(bd,a8,aZ,a4){if(bd.nodeType===3||bd.nodeType===8){return}var bg,a3,a5,bb=0,a1,a6,a9,a2,a7,aY,bf,bc=a.data(bd),a0=bc&&bc.events;if(!bc||!a0){return}if(a8&&a8.type){aZ=a8.handler;a8=a8.type}if(!a8||typeof a8==="string"&&a8.charAt(0)==="."){a8=a8||"";for(a3 in a0){a.event.remove(bd,a3+a8)}return}a8=a8.split(" ");while((a3=a8[bb++])){bf=a3;aY=null;a1=a3.indexOf(".")<0;a6=[];if(!a1){a6=a3.split(".");a3=a6.shift();a9=new RegExp("(^|\\.)"+a.map(a6.slice(0).sort(),A).join("\\.(?:.*\\.)?")+"(\\.|$)")}a7=a0[a3];if(!a7){continue}if(!aZ){for(var ba=0;ba<a7.length;ba++){aY=a7[ba];if(a1||a9.test(aY.namespace)){a.event.remove(bd,bf,aY.handler,ba);a7.splice(ba--,1)}}continue}a2=a.event.special[a3]||{};for(var ba=a4||0;ba<a7.length;ba++){aY=a7[ba];if(aZ.guid===aY.guid){if(a1||a9.test(aY.namespace)){if(a4==null){a7.splice(ba--,1)}if(a2.remove){a2.remove.call(bd,aY)}}if(a4!=null){break}}}if(a7.length===0||a4!=null&&a7.length===1){if(!a2.teardown||a2.teardown.call(bd,a6)===false){ag(bd,a3,bc.handle)}bg=null;delete a0[a3]}}if(a.isEmptyObject(a0)){var be=bc.handle;if(be){be.elem=null}delete bc.events;delete bc.handle;if(a.isEmptyObject(bc)){a.removeData(bd)}}},trigger:function(aY,a2,a0){var a7=aY.type||aY,a1=arguments[3];if(!a1){aY=typeof aY==="object"?aY[aI]?aY:a.extend(a.Event(a7),aY):a.Event(a7);if(a7.indexOf("!")>=0){aY.type=a7=a7.slice(0,-1);aY.exclusive=true}if(!a0){aY.stopPropagation();if(a.event.global[a7]){a.each(a.cache,function(){if(this.events&&this.events[a7]){a.event.trigger(aY,a2,this.handle.elem)}})}}if(!a0||a0.nodeType===3||a0.nodeType===8){return C}aY.result=C;aY.target=a0;a2=a.makeArray(a2);a2.unshift(aY)}aY.currentTarget=a0;var a3=a.data(a0,"handle");if(a3){a3.apply(a0,a2)}var a8=a0.parentNode||a0.ownerDocument;try{if(!(a0&&a0.nodeName&&a.noData[a0.nodeName.toLowerCase()])){if(a0["on"+a7]&&a0["on"+a7].apply(a0,a2)===false){aY.result=false}}}catch(a5){}if(!aY.isPropagationStopped()&&a8){a.event.trigger(aY,a2,a8,true)}else{if(!aY.isDefaultPrevented()){var a4=aY.target,aZ,a9=a.nodeName(a4,"a")&&a7==="click",a6=a.event.special[a7]||{};if((!a6._default||a6._default.call(a0,aY)===false)&&!a9&&!(a4&&a4.nodeName&&a.noData[a4.nodeName.toLowerCase()])){try{if(a4[a7]){aZ=a4["on"+a7];if(aZ){a4["on"+a7]=null}a.event.triggered=true;a4[a7]()}}catch(a5){}if(aZ){a4["on"+a7]=aZ}a.event.triggered=false}}}},handle:function(aY){var a6,a0,aZ,a1,a7;aY=arguments[0]=a.event.fix(aY||aM.event);aY.currentTarget=this;a6=aY.type.indexOf(".")<0&&!aY.exclusive;if(!a6){aZ=aY.type.split(".");aY.type=aZ.shift();a1=new RegExp("(^|\\.)"+aZ.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}var a7=a.data(this,"events"),a0=a7[aY.type];if(a7&&a0){a0=a0.slice(0);for(var a3=0,a2=a0.length;a3<a2;a3++){var a5=a0[a3];if(a6||a1.test(a5.namespace)){aY.handler=a5.handler;aY.data=a5.data;aY.handleObj=a5;var a4=a5.handler.apply(this,arguments);if(a4!==C){aY.result=a4;if(a4===false){aY.preventDefault();aY.stopPropagation()}}if(aY.isImmediatePropagationStopped()){break}}}}return aY.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(a1){if(a1[aI]){return a1}var aZ=a1;a1=a.Event(aZ);for(var a0=this.props.length,a3;a0;){a3=this.props[--a0];a1[a3]=aZ[a3]}if(!a1.target){a1.target=a1.srcElement||ab}if(a1.target.nodeType===3){a1.target=a1.target.parentNode}if(!a1.relatedTarget&&a1.fromElement){a1.relatedTarget=a1.fromElement===a1.target?a1.toElement:a1.fromElement}if(a1.pageX==null&&a1.clientX!=null){var a2=ab.documentElement,aY=ab.body;a1.pageX=a1.clientX+(a2&&a2.scrollLeft||aY&&aY.scrollLeft||0)-(a2&&a2.clientLeft||aY&&aY.clientLeft||0);a1.pageY=a1.clientY+(a2&&a2.scrollTop||aY&&aY.scrollTop||0)-(a2&&a2.clientTop||aY&&aY.clientTop||0)}if(!a1.which&&((a1.charCode||a1.charCode===0)?a1.charCode:a1.keyCode)){a1.which=a1.charCode||a1.keyCode}if(!a1.metaKey&&a1.ctrlKey){a1.metaKey=a1.ctrlKey}if(!a1.which&&a1.button!==C){a1.which=(a1.button&1?1:(a1.button&2?3:(a1.button&4?2:0)))}return a1},guid:100000000,proxy:a.proxy,special:{ready:{setup:a.bindReady,teardown:a.noop},live:{add:function(aY){a.event.add(this,aY.origType,a.extend({},aY,{handler:V}))},remove:function(aZ){var aY=true,a0=aZ.origType.replace(aC,"");a.each(a.data(this,"events").live||[],function(){if(a0===this.origType.replace(aC,"")){aY=false;return false}});if(aY){a.event.remove(this,aZ.origType,V)}}},beforeunload:{setup:function(a0,aZ,aY){if(this.setInterval){this.onbeforeunload=aY}return false},teardown:function(aZ,aY){if(this.onbeforeunload===aY){this.onbeforeunload=null}}}}};var ag=ab.removeEventListener?function(aZ,aY,a0){aZ.removeEventListener(aY,a0,false)}:function(aZ,aY,a0){aZ.detachEvent("on"+aY,a0)};a.Event=function(aY){if(!this.preventDefault){return new a.Event(aY)}if(aY&&aY.type){this.originalEvent=aY;this.type=aY.type}else{this.type=aY}this.timeStamp=aP();this[aI]=true};function aR(){return false}function f(){return true}a.Event.prototype={preventDefault:function(){this.isDefaultPrevented=f;var aY=this.originalEvent;if(!aY){return}if(aY.preventDefault){aY.preventDefault()}aY.returnValue=false},stopPropagation:function(){this.isPropagationStopped=f;var aY=this.originalEvent;if(!aY){return}if(aY.stopPropagation){aY.stopPropagation()}aY.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=f;this.stopPropagation()},isDefaultPrevented:aR,isPropagationStopped:aR,isImmediatePropagationStopped:aR};var Q=function(aZ){var aY=aZ.relatedTarget;try{while(aY&&aY!==this){aY=aY.parentNode}if(aY!==this){aZ.type=aZ.data;a.event.handle.apply(this,arguments)}}catch(a0){}},ay=function(aY){aY.type=aY.data;a.event.handle.apply(this,arguments)};a.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(aZ,aY){a.event.special[aZ]={setup:function(a0){a.event.add(this,aY,a0&&a0.selector?ay:Q,aZ)},teardown:function(a0){a.event.remove(this,aY,a0&&a0.selector?ay:Q)}}});if(!a.support.submitBubbles){a.event.special.submit={setup:function(aZ,aY){if(this.nodeName.toLowerCase()!=="form"){a.event.add(this,"click.specialSubmit",function(a2){var a1=a2.target,a0=a1.type;if((a0==="submit"||a0==="image")&&a(a1).closest("form").length){return aA("submit",this,arguments)}});a.event.add(this,"keypress.specialSubmit",function(a2){var a1=a2.target,a0=a1.type;if((a0==="text"||a0==="password")&&a(a1).closest("form").length&&a2.keyCode===13){return aA("submit",this,arguments)}})}else{return false}},teardown:function(aY){a.event.remove(this,".specialSubmit")}}}if(!a.support.changeBubbles){var aq=/textarea|input|select/i,aS,i=function(aZ){var aY=aZ.type,a0=aZ.value;if(aY==="radio"||aY==="checkbox"){a0=aZ.checked}else{if(aY==="select-multiple"){a0=aZ.selectedIndex>-1?a.map(aZ.options,function(a1){return a1.selected}).join("-"):""}else{if(aZ.nodeName.toLowerCase()==="select"){a0=aZ.selectedIndex}}}return a0},O=function O(a0){var aY=a0.target,aZ,a1;if(!aq.test(aY.nodeName)||aY.readOnly){return}aZ=a.data(aY,"_change_data");a1=i(aY);if(a0.type!=="focusout"||aY.type!=="radio"){a.data(aY,"_change_data",a1)}if(aZ===C||a1===aZ){return}if(aZ!=null||a1){a0.type="change";return a.event.trigger(a0,arguments[1],aY)}};a.event.special.change={filters:{focusout:O,click:function(a0){var aZ=a0.target,aY=aZ.type;if(aY==="radio"||aY==="checkbox"||aZ.nodeName.toLowerCase()==="select"){return O.call(this,a0)}},keydown:function(a0){var aZ=a0.target,aY=aZ.type;if((a0.keyCode===13&&aZ.nodeName.toLowerCase()!=="textarea")||(a0.keyCode===32&&(aY==="checkbox"||aY==="radio"))||aY==="select-multiple"){return O.call(this,a0)}},beforeactivate:function(aZ){var aY=aZ.target;a.data(aY,"_change_data",i(aY))}},setup:function(a0,aZ){if(this.type==="file"){return false}for(var aY in aS){a.event.add(this,aY+".specialChange",aS[aY])}return aq.test(this.nodeName)},teardown:function(aY){a.event.remove(this,".specialChange");return aq.test(this.nodeName)}};aS=a.event.special.change.filters}function aA(aZ,a0,aY){aY[0].type=aZ;return a.event.handle.apply(a0,aY)}if(ab.addEventListener){a.each({focus:"focusin",blur:"focusout"},function(a0,aY){a.event.special[aY]={setup:function(){this.addEventListener(a0,aZ,true)},teardown:function(){this.removeEventListener(a0,aZ,true)}};function aZ(a1){a1=a.event.fix(a1);a1.type=aY;return a.event.handle.call(this,a1)}})}a.each(["bind","one"],function(aZ,aY){a.fn[aY]=function(a5,a6,a4){if(typeof a5==="object"){for(var a2 in a5){this[aY](a2,a6,a5[a2],a4)}return this}if(a.isFunction(a6)){a4=a6;a6=C}var a3=aY==="one"?a.proxy(a4,function(a7){a(this).unbind(a7,a3);return a4.apply(this,arguments)}):a4;if(a5==="unload"&&aY!=="one"){this.one(a5,a6,a4)}else{for(var a1=0,a0=this.length;a1<a0;a1++){a.event.add(this[a1],a5,a3,a6)}}return this}});a.fn.extend({unbind:function(a2,a1){if(typeof a2==="object"&&!a2.preventDefault){for(var a0 in a2){this.unbind(a0,a2[a0])}}else{for(var aZ=0,aY=this.length;aZ<aY;aZ++){a.event.remove(this[aZ],a2,a1)}}return this},delegate:function(aY,aZ,a1,a0){return this.live(aZ,a1,a0,aY)},undelegate:function(aY,aZ,a0){if(arguments.length===0){return this.unbind("live")}else{return this.die(aZ,null,a0,aY)}},trigger:function(aY,aZ){return this.each(function(){a.event.trigger(aY,aZ,this)})},triggerHandler:function(aY,a0){if(this[0]){var aZ=a.Event(aY);aZ.preventDefault();aZ.stopPropagation();a.event.trigger(aZ,a0,this[0]);return aZ.result}},toggle:function(a0){var aY=arguments,aZ=1;while(aZ<aY.length){a.proxy(a0,aY[aZ++])}return this.click(a.proxy(a0,function(a1){var a2=(a.data(this,"lastToggle"+a0.guid)||0)%aZ;a.data(this,"lastToggle"+a0.guid,a2+1);a1.preventDefault();return aY[a2].apply(this,arguments)||false}))},hover:function(aY,aZ){return this.mouseenter(aY).mouseleave(aZ||aY)}});var aw={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};a.each(["live","die"],function(aZ,aY){a.fn[aY]=function(a7,a4,a9,a2){var a8,a5=0,a6,a1,ba,a3=a2||this.selector,a0=a2?this:a(this.context);if(a.isFunction(a4)){a9=a4;a4=C}a7=(a7||"").split(" ");while((a8=a7[a5++])!=null){a6=aC.exec(a8);a1="";if(a6){a1=a6[0];a8=a8.replace(aC,"")}if(a8==="hover"){a7.push("mouseenter"+a1,"mouseleave"+a1);continue}ba=a8;if(a8==="focus"||a8==="blur"){a7.push(aw[a8]+a1);a8=a8+a1}else{a8=(aw[a8]||a8)+a1}if(aY==="live"){a0.each(function(){a.event.add(this,m(a8,a3),{data:a4,selector:a3,handler:a9,origType:a8,origHandler:a9,preType:ba})})}else{a0.unbind(m(a8,a3),a9)}}return this}});function V(aY){var a8,aZ=[],bb=[],a7=arguments,ba,a6,a9,a1,a3,a5,a2,a4,bc=a.data(this,"events");if(aY.liveFired===this||!bc||!bc.live||aY.button&&aY.type==="click"){return}aY.liveFired=this;var a0=bc.live.slice(0);for(a3=0;a3<a0.length;a3++){a9=a0[a3];if(a9.origType.replace(aC,"")===aY.type){bb.push(a9.selector)}else{a0.splice(a3--,1)}}a6=a(aY.target).closest(bb,aY.currentTarget);for(a5=0,a2=a6.length;a5<a2;a5++){for(a3=0;a3<a0.length;a3++){a9=a0[a3];if(a6[a5].selector===a9.selector){a1=a6[a5].elem;ba=null;if(a9.preType==="mouseenter"||a9.preType==="mouseleave"){ba=a(aY.relatedTarget).closest(a9.selector)[0]}if(!ba||ba!==a1){aZ.push({elem:a1,handleObj:a9})}}}}for(a5=0,a2=aZ.length;a5<a2;a5++){a6=aZ[a5];aY.currentTarget=a6.elem;aY.data=a6.handleObj.data;aY.handleObj=a6.handleObj;if(a6.handleObj.origHandler.apply(a6.elem,a7)===false){a8=false;break}}return a8}function m(aZ,aY){return"live."+(aZ&&aZ!=="*"?aZ+".":"")+aY.replace(/\./g,"`").replace(/ /g,"&")}a.each(("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error").split(" "),function(aZ,aY){a.fn[aY]=function(a0){return a0?this.bind(aY,a0):this.trigger(aY)};if(a.attrFn){a.attrFn[aY]=true}});if(aM.attachEvent&&!aM.addEventListener){aM.attachEvent("onunload",function(){for(var aZ in a.cache){if(a.cache[aZ].handle){try{a.event.remove(a.cache[aZ].handle.elem)}catch(aY){}}}}); -/* - * Sizzle CSS Selector Engine - v1.0 - * Copyright 2009, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * More information: http://sizzlejs.com/ - */ -}(function(){var a9=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,ba=0,bc=Object.prototype.toString,a4=false,a3=true;[0,0].sort(function(){a3=false;return 0});var a0=function(bl,bg,bo,bp){bo=bo||[];var br=bg=bg||ab;if(bg.nodeType!==1&&bg.nodeType!==9){return[]}if(!bl||typeof bl!=="string"){return bo}var bm=[],bi,bt,bw,bh,bk=true,bj=a1(bg),bq=bl;while((a9.exec(""),bi=a9.exec(bq))!==null){bq=bi[3];bm.push(bi[1]);if(bi[2]){bh=bi[3];break}}if(bm.length>1&&a5.exec(bl)){if(bm.length===2&&a6.relative[bm[0]]){bt=bd(bm[0]+bm[1],bg)}else{bt=a6.relative[bm[0]]?[bg]:a0(bm.shift(),bg);while(bm.length){bl=bm.shift();if(a6.relative[bl]){bl+=bm.shift()}bt=bd(bl,bt)}}}else{if(!bp&&bm.length>1&&bg.nodeType===9&&!bj&&a6.match.ID.test(bm[0])&&!a6.match.ID.test(bm[bm.length-1])){var bs=a0.find(bm.shift(),bg,bj);bg=bs.expr?a0.filter(bs.expr,bs.set)[0]:bs.set[0]}if(bg){var bs=bp?{expr:bm.pop(),set:a8(bp)}:a0.find(bm.pop(),bm.length===1&&(bm[0]==="~"||bm[0]==="+")&&bg.parentNode?bg.parentNode:bg,bj);bt=bs.expr?a0.filter(bs.expr,bs.set):bs.set;if(bm.length>0){bw=a8(bt)}else{bk=false}while(bm.length){var bv=bm.pop(),bu=bv;if(!a6.relative[bv]){bv=""}else{bu=bm.pop()}if(bu==null){bu=bg}a6.relative[bv](bw,bu,bj)}}else{bw=bm=[]}}if(!bw){bw=bt}if(!bw){a0.error(bv||bl)}if(bc.call(bw)==="[object Array]"){if(!bk){bo.push.apply(bo,bw)}else{if(bg&&bg.nodeType===1){for(var bn=0;bw[bn]!=null;bn++){if(bw[bn]&&(bw[bn]===true||bw[bn].nodeType===1&&a7(bg,bw[bn]))){bo.push(bt[bn])}}}else{for(var bn=0;bw[bn]!=null;bn++){if(bw[bn]&&bw[bn].nodeType===1){bo.push(bt[bn])}}}}}else{a8(bw,bo)}if(bh){a0(bh,br,bo,bp);a0.uniqueSort(bo)}return bo};a0.uniqueSort=function(bh){if(bb){a4=a3;bh.sort(bb);if(a4){for(var bg=1;bg<bh.length;bg++){if(bh[bg]===bh[bg-1]){bh.splice(bg--,1)}}}}return bh};a0.matches=function(bg,bh){return a0(bg,null,null,bh)};a0.find=function(bn,bg,bo){var bm,bk;if(!bn){return[]}for(var bj=0,bi=a6.order.length;bj<bi;bj++){var bl=a6.order[bj],bk;if((bk=a6.leftMatch[bl].exec(bn))){var bh=bk[1];bk.splice(1,1);if(bh.substr(bh.length-1)!=="\\"){bk[1]=(bk[1]||"").replace(/\\/g,"");bm=a6.find[bl](bk,bg,bo);if(bm!=null){bn=bn.replace(a6.match[bl],"");break}}}}if(!bm){bm=bg.getElementsByTagName("*")}return{set:bm,expr:bn}};a0.filter=function(br,bq,bu,bk){var bi=br,bw=[],bo=bq,bm,bg,bn=bq&&bq[0]&&a1(bq[0]);while(br&&bq.length){for(var bp in a6.filter){if((bm=a6.leftMatch[bp].exec(br))!=null&&bm[2]){var bh=a6.filter[bp],bv,bt,bj=bm[1];bg=false;bm.splice(1,1);if(bj.substr(bj.length-1)==="\\"){continue}if(bo===bw){bw=[]}if(a6.preFilter[bp]){bm=a6.preFilter[bp](bm,bo,bu,bw,bk,bn);if(!bm){bg=bv=true}else{if(bm===true){continue}}}if(bm){for(var bl=0;(bt=bo[bl])!=null;bl++){if(bt){bv=bh(bt,bm,bl,bo);var bs=bk^!!bv;if(bu&&bv!=null){if(bs){bg=true}else{bo[bl]=false}}else{if(bs){bw.push(bt);bg=true}}}}}if(bv!==C){if(!bu){bo=bw}br=br.replace(a6.match[bp],"");if(!bg){return[]}break}}}if(br===bi){if(bg==null){a0.error(br)}else{break}}bi=br}return bo};a0.error=function(bg){throw"Syntax error, unrecognized expression: "+bg};var a6=a0.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(bg){return bg.getAttribute("href")}},relative:{"+":function(bm,bh){var bj=typeof bh==="string",bl=bj&&!/\W/.test(bh),bn=bj&&!bl;if(bl){bh=bh.toLowerCase()}for(var bi=0,bg=bm.length,bk;bi<bg;bi++){if((bk=bm[bi])){while((bk=bk.previousSibling)&&bk.nodeType!==1){}bm[bi]=bn||bk&&bk.nodeName.toLowerCase()===bh?bk||false:bk===bh}}if(bn){a0.filter(bh,bm,true)}},">":function(bm,bh){var bk=typeof bh==="string";if(bk&&!/\W/.test(bh)){bh=bh.toLowerCase();for(var bi=0,bg=bm.length;bi<bg;bi++){var bl=bm[bi];if(bl){var bj=bl.parentNode;bm[bi]=bj.nodeName.toLowerCase()===bh?bj:false}}}else{for(var bi=0,bg=bm.length;bi<bg;bi++){var bl=bm[bi];if(bl){bm[bi]=bk?bl.parentNode:bl.parentNode===bh}}if(bk){a0.filter(bh,bm,true)}}},"":function(bj,bh,bl){var bi=ba++,bg=be;if(typeof bh==="string"&&!/\W/.test(bh)){var bk=bh=bh.toLowerCase();bg=aY}bg("parentNode",bh,bi,bj,bk,bl)},"~":function(bj,bh,bl){var bi=ba++,bg=be;if(typeof bh==="string"&&!/\W/.test(bh)){var bk=bh=bh.toLowerCase();bg=aY}bg("previousSibling",bh,bi,bj,bk,bl)}},find:{ID:function(bh,bi,bj){if(typeof bi.getElementById!=="undefined"&&!bj){var bg=bi.getElementById(bh[1]);return bg?[bg]:[]}},NAME:function(bi,bl){if(typeof bl.getElementsByName!=="undefined"){var bh=[],bk=bl.getElementsByName(bi[1]);for(var bj=0,bg=bk.length;bj<bg;bj++){if(bk[bj].getAttribute("name")===bi[1]){bh.push(bk[bj])}}return bh.length===0?null:bh}},TAG:function(bg,bh){return bh.getElementsByTagName(bg[1])}},preFilter:{CLASS:function(bj,bh,bi,bg,bm,bn){bj=" "+bj[1].replace(/\\/g,"")+" ";if(bn){return bj}for(var bk=0,bl;(bl=bh[bk])!=null;bk++){if(bl){if(bm^(bl.className&&(" "+bl.className+" ").replace(/[\t\n]/g," ").indexOf(bj)>=0)){if(!bi){bg.push(bl)}}else{if(bi){bh[bk]=false}}}}return false},ID:function(bg){return bg[1].replace(/\\/g,"")},TAG:function(bh,bg){return bh[1].toLowerCase()},CHILD:function(bg){if(bg[1]==="nth"){var bh=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(bg[2]==="even"&&"2n"||bg[2]==="odd"&&"2n+1"||!/\D/.test(bg[2])&&"0n+"+bg[2]||bg[2]);bg[2]=(bh[1]+(bh[2]||1))-0;bg[3]=bh[3]-0}bg[0]=ba++;return bg},ATTR:function(bk,bh,bi,bg,bl,bm){var bj=bk[1].replace(/\\/g,"");if(!bm&&a6.attrMap[bj]){bk[1]=a6.attrMap[bj]}if(bk[2]==="~="){bk[4]=" "+bk[4]+" "}return bk},PSEUDO:function(bk,bh,bi,bg,bl){if(bk[1]==="not"){if((a9.exec(bk[3])||"").length>1||/^\w/.test(bk[3])){bk[3]=a0(bk[3],null,null,bh)}else{var bj=a0.filter(bk[3],bh,bi,true^bl);if(!bi){bg.push.apply(bg,bj)}return false}}else{if(a6.match.POS.test(bk[0])||a6.match.CHILD.test(bk[0])){return true}}return bk},POS:function(bg){bg.unshift(true);return bg}},filters:{enabled:function(bg){return bg.disabled===false&&bg.type!=="hidden"},disabled:function(bg){return bg.disabled===true},checked:function(bg){return bg.checked===true},selected:function(bg){bg.parentNode.selectedIndex;return bg.selected===true},parent:function(bg){return !!bg.firstChild},empty:function(bg){return !bg.firstChild},has:function(bi,bh,bg){return !!a0(bg[3],bi).length},header:function(bg){return/h\d/i.test(bg.nodeName)},text:function(bg){return"text"===bg.type},radio:function(bg){return"radio"===bg.type},checkbox:function(bg){return"checkbox"===bg.type},file:function(bg){return"file"===bg.type},password:function(bg){return"password"===bg.type},submit:function(bg){return"submit"===bg.type},image:function(bg){return"image"===bg.type},reset:function(bg){return"reset"===bg.type},button:function(bg){return"button"===bg.type||bg.nodeName.toLowerCase()==="button"},input:function(bg){return/input|select|textarea|button/i.test(bg.nodeName)}},setFilters:{first:function(bh,bg){return bg===0},last:function(bi,bh,bg,bj){return bh===bj.length-1},even:function(bh,bg){return bg%2===0},odd:function(bh,bg){return bg%2===1},lt:function(bi,bh,bg){return bh<bg[3]-0},gt:function(bi,bh,bg){return bh>bg[3]-0},nth:function(bi,bh,bg){return bg[3]-0===bh},eq:function(bi,bh,bg){return bg[3]-0===bh}},filter:{PSEUDO:function(bm,bi,bj,bn){var bh=bi[1],bk=a6.filters[bh];if(bk){return bk(bm,bj,bi,bn)}else{if(bh==="contains"){return(bm.textContent||bm.innerText||aZ([bm])||"").indexOf(bi[3])>=0}else{if(bh==="not"){var bl=bi[3];for(var bj=0,bg=bl.length;bj<bg;bj++){if(bl[bj]===bm){return false}}return true}else{a0.error("Syntax error, unrecognized expression: "+bh)}}}},CHILD:function(bg,bj){var bm=bj[1],bh=bg;switch(bm){case"only":case"first":while((bh=bh.previousSibling)){if(bh.nodeType===1){return false}}if(bm==="first"){return true}bh=bg;case"last":while((bh=bh.nextSibling)){if(bh.nodeType===1){return false}}return true;case"nth":var bi=bj[2],bp=bj[3];if(bi===1&&bp===0){return true}var bl=bj[0],bo=bg.parentNode;if(bo&&(bo.sizcache!==bl||!bg.nodeIndex)){var bk=0;for(bh=bo.firstChild;bh;bh=bh.nextSibling){if(bh.nodeType===1){bh.nodeIndex=++bk}}bo.sizcache=bl}var bn=bg.nodeIndex-bp;if(bi===0){return bn===0}else{return(bn%bi===0&&bn/bi>=0)}}},ID:function(bh,bg){return bh.nodeType===1&&bh.getAttribute("id")===bg},TAG:function(bh,bg){return(bg==="*"&&bh.nodeType===1)||bh.nodeName.toLowerCase()===bg},CLASS:function(bh,bg){return(" "+(bh.className||bh.getAttribute("class"))+" ").indexOf(bg)>-1},ATTR:function(bl,bj){var bi=bj[1],bg=a6.attrHandle[bi]?a6.attrHandle[bi](bl):bl[bi]!=null?bl[bi]:bl.getAttribute(bi),bm=bg+"",bk=bj[2],bh=bj[4];return bg==null?bk==="!=":bk==="="?bm===bh:bk==="*="?bm.indexOf(bh)>=0:bk==="~="?(" "+bm+" ").indexOf(bh)>=0:!bh?bm&&bg!==false:bk==="!="?bm!==bh:bk==="^="?bm.indexOf(bh)===0:bk==="$="?bm.substr(bm.length-bh.length)===bh:bk==="|="?bm===bh||bm.substr(0,bh.length+1)===bh+"-":false},POS:function(bk,bh,bi,bl){var bg=bh[2],bj=a6.setFilters[bg];if(bj){return bj(bk,bi,bh,bl)}}}};var a5=a6.match.POS;for(var a2 in a6.match){a6.match[a2]=new RegExp(a6.match[a2].source+/(?![^\[]*\])(?![^\(]*\))/.source);a6.leftMatch[a2]=new RegExp(/(^(?:.|\r|\n)*?)/.source+a6.match[a2].source.replace(/\\(\d+)/g,function(bh,bg){return"\\"+(bg-0+1)}))}var a8=function(bh,bg){bh=Array.prototype.slice.call(bh,0);if(bg){bg.push.apply(bg,bh);return bg}return bh};try{Array.prototype.slice.call(ab.documentElement.childNodes,0)[0].nodeType}catch(bf){a8=function(bk,bj){var bh=bj||[];if(bc.call(bk)==="[object Array]"){Array.prototype.push.apply(bh,bk)}else{if(typeof bk.length==="number"){for(var bi=0,bg=bk.length;bi<bg;bi++){bh.push(bk[bi])}}else{for(var bi=0;bk[bi];bi++){bh.push(bk[bi])}}}return bh}}var bb;if(ab.documentElement.compareDocumentPosition){bb=function(bh,bg){if(!bh.compareDocumentPosition||!bg.compareDocumentPosition){if(bh==bg){a4=true}return bh.compareDocumentPosition?-1:1}var bi=bh.compareDocumentPosition(bg)&4?-1:bh===bg?0:1;if(bi===0){a4=true}return bi}}else{if("sourceIndex" in ab.documentElement){bb=function(bh,bg){if(!bh.sourceIndex||!bg.sourceIndex){if(bh==bg){a4=true}return bh.sourceIndex?-1:1}var bi=bh.sourceIndex-bg.sourceIndex;if(bi===0){a4=true}return bi}}else{if(ab.createRange){bb=function(bj,bh){if(!bj.ownerDocument||!bh.ownerDocument){if(bj==bh){a4=true}return bj.ownerDocument?-1:1}var bi=bj.ownerDocument.createRange(),bg=bh.ownerDocument.createRange();bi.setStart(bj,0);bi.setEnd(bj,0);bg.setStart(bh,0);bg.setEnd(bh,0);var bk=bi.compareBoundaryPoints(Range.START_TO_END,bg);if(bk===0){a4=true}return bk}}}}function aZ(bg){var bh="",bj;for(var bi=0;bg[bi];bi++){bj=bg[bi];if(bj.nodeType===3||bj.nodeType===4){bh+=bj.nodeValue}else{if(bj.nodeType!==8){bh+=aZ(bj.childNodes)}}}return bh}(function(){var bh=ab.createElement("div"),bi="script"+(new Date).getTime();bh.innerHTML="<a name='"+bi+"'/>";var bg=ab.documentElement;bg.insertBefore(bh,bg.firstChild);if(ab.getElementById(bi)){a6.find.ID=function(bk,bl,bm){if(typeof bl.getElementById!=="undefined"&&!bm){var bj=bl.getElementById(bk[1]);return bj?bj.id===bk[1]||typeof bj.getAttributeNode!=="undefined"&&bj.getAttributeNode("id").nodeValue===bk[1]?[bj]:C:[]}};a6.filter.ID=function(bl,bj){var bk=typeof bl.getAttributeNode!=="undefined"&&bl.getAttributeNode("id");return bl.nodeType===1&&bk&&bk.nodeValue===bj}}bg.removeChild(bh);bg=bh=null})();(function(){var bg=ab.createElement("div");bg.appendChild(ab.createComment(""));if(bg.getElementsByTagName("*").length>0){a6.find.TAG=function(bh,bl){var bk=bl.getElementsByTagName(bh[1]);if(bh[1]==="*"){var bj=[];for(var bi=0;bk[bi];bi++){if(bk[bi].nodeType===1){bj.push(bk[bi])}}bk=bj}return bk}}bg.innerHTML="<a href='#'></a>";if(bg.firstChild&&typeof bg.firstChild.getAttribute!=="undefined"&&bg.firstChild.getAttribute("href")!=="#"){a6.attrHandle.href=function(bh){return bh.getAttribute("href",2)}}bg=null})();if(ab.querySelectorAll){(function(){var bg=a0,bi=ab.createElement("div");bi.innerHTML="<p class='TEST'></p>";if(bi.querySelectorAll&&bi.querySelectorAll(".TEST").length===0){return}a0=function(bm,bl,bj,bk){bl=bl||ab;if(!bk&&bl.nodeType===9&&!a1(bl)){try{return a8(bl.querySelectorAll(bm),bj)}catch(bn){}}return bg(bm,bl,bj,bk)};for(var bh in bg){a0[bh]=bg[bh]}bi=null})()}(function(){var bg=ab.createElement("div");bg.innerHTML="<div class='test e'></div><div class='test'></div>";if(!bg.getElementsByClassName||bg.getElementsByClassName("e").length===0){return}bg.lastChild.className="e";if(bg.getElementsByClassName("e").length===1){return}a6.order.splice(1,0,"CLASS");a6.find.CLASS=function(bh,bi,bj){if(typeof bi.getElementsByClassName!=="undefined"&&!bj){return bi.getElementsByClassName(bh[1])}};bg=null})();function aY(bh,bm,bl,bp,bn,bo){for(var bj=0,bi=bp.length;bj<bi;bj++){var bg=bp[bj];if(bg){bg=bg[bh];var bk=false;while(bg){if(bg.sizcache===bl){bk=bp[bg.sizset];break}if(bg.nodeType===1&&!bo){bg.sizcache=bl;bg.sizset=bj}if(bg.nodeName.toLowerCase()===bm){bk=bg;break}bg=bg[bh]}bp[bj]=bk}}}function be(bh,bm,bl,bp,bn,bo){for(var bj=0,bi=bp.length;bj<bi;bj++){var bg=bp[bj];if(bg){bg=bg[bh];var bk=false;while(bg){if(bg.sizcache===bl){bk=bp[bg.sizset];break}if(bg.nodeType===1){if(!bo){bg.sizcache=bl;bg.sizset=bj}if(typeof bm!=="string"){if(bg===bm){bk=true;break}}else{if(a0.filter(bm,[bg]).length>0){bk=bg;break}}}bg=bg[bh]}bp[bj]=bk}}}var a7=ab.compareDocumentPosition?function(bh,bg){return !!(bh.compareDocumentPosition(bg)&16)}:function(bh,bg){return bh!==bg&&(bh.contains?bh.contains(bg):true)};var a1=function(bg){var bh=(bg?bg.ownerDocument||bg:0).documentElement;return bh?bh.nodeName!=="HTML":false};var bd=function(bg,bn){var bj=[],bk="",bl,bi=bn.nodeType?[bn]:bn;while((bl=a6.match.PSEUDO.exec(bg))){bk+=bl[0];bg=bg.replace(a6.match.PSEUDO,"")}bg=a6.relative[bg]?bg+"*":bg;for(var bm=0,bh=bi.length;bm<bh;bm++){a0(bg,bi[bm],bj)}return a0.filter(bk,bj)};a.find=a0;a.expr=a0.selectors;a.expr[":"]=a.expr.filters;a.unique=a0.uniqueSort;a.text=aZ;a.isXMLDoc=a1;a.contains=a7;return;aM.Sizzle=a0})();var N=/Until$/,Y=/^(?:parents|prevUntil|prevAll)/,aL=/,/,F=Array.prototype.slice;var ai=function(a1,a0,aY){if(a.isFunction(a0)){return a.grep(a1,function(a3,a2){return !!a0.call(a3,a2,a3)===aY})}else{if(a0.nodeType){return a.grep(a1,function(a3,a2){return(a3===a0)===aY})}else{if(typeof a0==="string"){var aZ=a.grep(a1,function(a2){return a2.nodeType===1});if(aW.test(a0)){return a.filter(a0,aZ,!aY)}else{a0=a.filter(a0,aZ)}}}}return a.grep(a1,function(a3,a2){return(a.inArray(a3,a0)>=0)===aY})};a.fn.extend({find:function(aY){var a0=this.pushStack("","find",aY),a3=0;for(var a1=0,aZ=this.length;a1<aZ;a1++){a3=a0.length;a.find(aY,this[a1],a0);if(a1>0){for(var a4=a3;a4<a0.length;a4++){for(var a2=0;a2<a3;a2++){if(a0[a2]===a0[a4]){a0.splice(a4--,1);break}}}}}return a0},has:function(aZ){var aY=a(aZ);return this.filter(function(){for(var a1=0,a0=aY.length;a1<a0;a1++){if(a.contains(this,aY[a1])){return true}}})},not:function(aY){return this.pushStack(ai(this,aY,false),"not",aY)},filter:function(aY){return this.pushStack(ai(this,aY,true),"filter",aY)},is:function(aY){return !!aY&&a.filter(aY,this).length>0},closest:function(a7,aY){if(a.isArray(a7)){var a4=[],a6=this[0],a3,a2={},a0;if(a6&&a7.length){for(var a1=0,aZ=a7.length;a1<aZ;a1++){a0=a7[a1];if(!a2[a0]){a2[a0]=a.expr.match.POS.test(a0)?a(a0,aY||this.context):a0}}while(a6&&a6.ownerDocument&&a6!==aY){for(a0 in a2){a3=a2[a0];if(a3.jquery?a3.index(a6)>-1:a(a6).is(a3)){a4.push({selector:a0,elem:a6});delete a2[a0]}}a6=a6.parentNode}}return a4}var a5=a.expr.match.POS.test(a7)?a(a7,aY||this.context):null;return this.map(function(a8,a9){while(a9&&a9.ownerDocument&&a9!==aY){if(a5?a5.index(a9)>-1:a(a9).is(a7)){return a9}a9=a9.parentNode}return null})},index:function(aY){if(!aY||typeof aY==="string"){return a.inArray(this[0],aY?a(aY):this.parent().children())}return a.inArray(aY.jquery?aY[0]:aY,this)},add:function(aY,aZ){var a1=typeof aY==="string"?a(aY,aZ||this.context):a.makeArray(aY),a0=a.merge(this.get(),a1);return this.pushStack(y(a1[0])||y(a0[0])?a0:a.unique(a0))},andSelf:function(){return this.add(this.prevObject)}});function y(aY){return !aY||!aY.parentNode||aY.parentNode.nodeType===11}a.each({parent:function(aZ){var aY=aZ.parentNode;return aY&&aY.nodeType!==11?aY:null},parents:function(aY){return a.dir(aY,"parentNode")},parentsUntil:function(aZ,aY,a0){return a.dir(aZ,"parentNode",a0)},next:function(aY){return a.nth(aY,2,"nextSibling")},prev:function(aY){return a.nth(aY,2,"previousSibling")},nextAll:function(aY){return a.dir(aY,"nextSibling")},prevAll:function(aY){return a.dir(aY,"previousSibling")},nextUntil:function(aZ,aY,a0){return a.dir(aZ,"nextSibling",a0)},prevUntil:function(aZ,aY,a0){return a.dir(aZ,"previousSibling",a0)},siblings:function(aY){return a.sibling(aY.parentNode.firstChild,aY)},children:function(aY){return a.sibling(aY.firstChild)},contents:function(aY){return a.nodeName(aY,"iframe")?aY.contentDocument||aY.contentWindow.document:a.makeArray(aY.childNodes)}},function(aY,aZ){a.fn[aY]=function(a2,a0){var a1=a.map(this,aZ,a2);if(!N.test(aY)){a0=a2}if(a0&&typeof a0==="string"){a1=a.filter(a0,a1)}a1=this.length>1?a.unique(a1):a1;if((this.length>1||aL.test(a0))&&Y.test(aY)){a1=a1.reverse()}return this.pushStack(a1,aY,F.call(arguments).join(","))}});a.extend({filter:function(a0,aY,aZ){if(aZ){a0=":not("+a0+")"}return a.find.matches(a0,aY)},dir:function(a0,aZ,a2){var aY=[],a1=a0[aZ];while(a1&&a1.nodeType!==9&&(a2===C||a1.nodeType!==1||!a(a1).is(a2))){if(a1.nodeType===1){aY.push(a1)}a1=a1[aZ]}return aY},nth:function(a2,aY,a0,a1){aY=aY||1;var aZ=0;for(;a2;a2=a2[a0]){if(a2.nodeType===1&&++aZ===aY){break}}return a2},sibling:function(a0,aZ){var aY=[];for(;a0;a0=a0.nextSibling){if(a0.nodeType===1&&a0!==aZ){aY.push(a0)}}return aY}});var T=/ jQuery\d+="(?:\d+|null)"/g,Z=/^\s+/,H=/(<([\w:]+)[^>]*?)\/>/g,al=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,c=/<([\w:]+)/,t=/<tbody/i,L=/<|&#?\w+;/,E=/<script|<object|<embed|<option|<style/i,l=/checked\s*(?:[^=]|=\s*.checked.)/i,p=function(aZ,a0,aY){return al.test(aY)?aZ:a0+"></"+aY+">"},ac={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};ac.optgroup=ac.option;ac.tbody=ac.tfoot=ac.colgroup=ac.caption=ac.thead;ac.th=ac.td;if(!a.support.htmlSerialize){ac._default=[1,"div<div>","</div>"]}a.fn.extend({text:function(aY){if(a.isFunction(aY)){return this.each(function(a0){var aZ=a(this);aZ.text(aY.call(this,a0,aZ.text()))})}if(typeof aY!=="object"&&aY!==C){return this.empty().append((this[0]&&this[0].ownerDocument||ab).createTextNode(aY))}return a.text(this)},wrapAll:function(aY){if(a.isFunction(aY)){return this.each(function(a0){a(this).wrapAll(aY.call(this,a0))})}if(this[0]){var aZ=a(aY,this[0].ownerDocument).eq(0).clone(true);if(this[0].parentNode){aZ.insertBefore(this[0])}aZ.map(function(){var a0=this;while(a0.firstChild&&a0.firstChild.nodeType===1){a0=a0.firstChild}return a0}).append(this)}return this},wrapInner:function(aY){if(a.isFunction(aY)){return this.each(function(aZ){a(this).wrapInner(aY.call(this,aZ))})}return this.each(function(){var aZ=a(this),a0=aZ.contents();if(a0.length){a0.wrapAll(aY)}else{aZ.append(aY)}})},wrap:function(aY){return this.each(function(){a(this).wrapAll(aY)})},unwrap:function(){return this.parent().each(function(){if(!a.nodeName(this,"body")){a(this).replaceWith(this.childNodes)}}).end()},append:function(){return this.domManip(arguments,true,function(aY){if(this.nodeType===1){this.appendChild(aY)}})},prepend:function(){return this.domManip(arguments,true,function(aY){if(this.nodeType===1){this.insertBefore(aY,this.firstChild)}})},before:function(){if(this[0]&&this[0].parentNode){return this.domManip(arguments,false,function(aZ){this.parentNode.insertBefore(aZ,this)})}else{if(arguments.length){var aY=a(arguments[0]);aY.push.apply(aY,this.toArray());return this.pushStack(aY,"before",arguments)}}},after:function(){if(this[0]&&this[0].parentNode){return this.domManip(arguments,false,function(aZ){this.parentNode.insertBefore(aZ,this.nextSibling)})}else{if(arguments.length){var aY=this.pushStack(this,"after",arguments);aY.push.apply(aY,a(arguments[0]).toArray());return aY}}},remove:function(aY,a1){for(var aZ=0,a0;(a0=this[aZ])!=null;aZ++){if(!aY||a.filter(aY,[a0]).length){if(!a1&&a0.nodeType===1){a.cleanData(a0.getElementsByTagName("*"));a.cleanData([a0])}if(a0.parentNode){a0.parentNode.removeChild(a0)}}}return this},empty:function(){for(var aY=0,aZ;(aZ=this[aY])!=null;aY++){if(aZ.nodeType===1){a.cleanData(aZ.getElementsByTagName("*"))}while(aZ.firstChild){aZ.removeChild(aZ.firstChild)}}return this},clone:function(aZ){var aY=this.map(function(){if(!a.support.noCloneEvent&&!a.isXMLDoc(this)){var a1=this.outerHTML,a0=this.ownerDocument;if(!a1){var a2=a0.createElement("div");a2.appendChild(this.cloneNode(true));a1=a2.innerHTML}return a.clean([a1.replace(T,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(Z,"")],a0)[0]}else{return this.cloneNode(true)}});if(aZ===true){q(this,aY);q(this.find("*"),aY.find("*"))}return aY},html:function(a0){if(a0===C){return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(T,""):null}else{if(typeof a0==="string"&&!E.test(a0)&&(a.support.leadingWhitespace||!Z.test(a0))&&!ac[(c.exec(a0)||["",""])[1].toLowerCase()]){a0=a0.replace(H,p);try{for(var aZ=0,aY=this.length;aZ<aY;aZ++){if(this[aZ].nodeType===1){a.cleanData(this[aZ].getElementsByTagName("*"));this[aZ].innerHTML=a0}}}catch(a1){this.empty().append(a0)}}else{if(a.isFunction(a0)){this.each(function(a4){var a3=a(this),a2=a3.html();a3.empty().append(function(){return a0.call(this,a4,a2)})})}else{this.empty().append(a0)}}}return this},replaceWith:function(aY){if(this[0]&&this[0].parentNode){if(a.isFunction(aY)){return this.each(function(a1){var a0=a(this),aZ=a0.html();a0.replaceWith(aY.call(this,a1,aZ))})}if(typeof aY!=="string"){aY=a(aY).detach()}return this.each(function(){var a0=this.nextSibling,aZ=this.parentNode;a(this).remove();if(a0){a(a0).before(aY)}else{a(aZ).append(aY)}})}else{return this.pushStack(a(a.isFunction(aY)?aY():aY),"replaceWith",aY)}},detach:function(aY){return this.remove(aY,true)},domManip:function(a4,a9,a8){var a1,a2,a7=a4[0],aZ=[],a3,a6;if(!a.support.checkClone&&arguments.length===3&&typeof a7==="string"&&l.test(a7)){return this.each(function(){a(this).domManip(a4,a9,a8,true)})}if(a.isFunction(a7)){return this.each(function(bb){var ba=a(this);a4[0]=a7.call(this,bb,a9?ba.html():C);ba.domManip(a4,a9,a8)})}if(this[0]){a6=a7&&a7.parentNode;if(a.support.parentNode&&a6&&a6.nodeType===11&&a6.childNodes.length===this.length){a1={fragment:a6}}else{a1=J(a4,this,aZ)}a3=a1.fragment;if(a3.childNodes.length===1){a2=a3=a3.firstChild}else{a2=a3.firstChild}if(a2){a9=a9&&a.nodeName(a2,"tr");for(var a0=0,aY=this.length;a0<aY;a0++){a8.call(a9?a5(this[a0],a2):this[a0],a0>0||a1.cacheable||this.length>1?a3.cloneNode(true):a3)}}if(aZ.length){a.each(aZ,aV)}}return this;function a5(ba,bb){return a.nodeName(ba,"table")?(ba.getElementsByTagName("tbody")[0]||ba.appendChild(ba.ownerDocument.createElement("tbody"))):ba}}});function q(a0,aY){var aZ=0;aY.each(function(){if(this.nodeName!==(a0[aZ]&&a0[aZ].nodeName)){return}var a5=a.data(a0[aZ++]),a4=a.data(this,a5),a1=a5&&a5.events;if(a1){delete a4.handle;a4.events={};for(var a3 in a1){for(var a2 in a1[a3]){a.event.add(this,a3,a1[a3][a2],a1[a3][a2].data)}}}})}function J(a3,a1,aZ){var a2,aY,a0,a4=(a1&&a1[0]?a1[0].ownerDocument||a1[0]:ab);if(a3.length===1&&typeof a3[0]==="string"&&a3[0].length<512&&a4===ab&&!E.test(a3[0])&&(a.support.checkClone||!l.test(a3[0]))){aY=true;a0=a.fragments[a3[0]];if(a0){if(a0!==1){a2=a0}}}if(!a2){a2=a4.createDocumentFragment();a.clean(a3,a4,a2,aZ)}if(aY){a.fragments[a3[0]]=a0?a2:1}return{fragment:a2,cacheable:aY}}a.fragments={};a.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(aY,aZ){a.fn[aY]=function(a0){var a3=[],a6=a(a0),a5=this.length===1&&this[0].parentNode;if(a5&&a5.nodeType===11&&a5.childNodes.length===1&&a6.length===1){a6[aZ](this[0]);return this}else{for(var a4=0,a1=a6.length;a4<a1;a4++){var a2=(a4>0?this.clone(true):this).get();a.fn[aZ].apply(a(a6[a4]),a2);a3=a3.concat(a2)}return this.pushStack(a3,aY,a6.selector)}}});a.extend({clean:function(a0,a2,a9,a4){a2=a2||ab;if(typeof a2.createElement==="undefined"){a2=a2.ownerDocument||a2[0]&&a2[0].ownerDocument||ab}var ba=[];for(var a8=0,a3;(a3=a0[a8])!=null;a8++){if(typeof a3==="number"){a3+=""}if(!a3){continue}if(typeof a3==="string"&&!L.test(a3)){a3=a2.createTextNode(a3)}else{if(typeof a3==="string"){a3=a3.replace(H,p);var bb=(c.exec(a3)||["",""])[1].toLowerCase(),a1=ac[bb]||ac._default,a7=a1[0],aZ=a2.createElement("div");aZ.innerHTML=a1[1]+a3+a1[2];while(a7--){aZ=aZ.lastChild}if(!a.support.tbody){var aY=t.test(a3),a6=bb==="table"&&!aY?aZ.firstChild&&aZ.firstChild.childNodes:a1[1]==="<table>"&&!aY?aZ.childNodes:[];for(var a5=a6.length-1;a5>=0;--a5){if(a.nodeName(a6[a5],"tbody")&&!a6[a5].childNodes.length){a6[a5].parentNode.removeChild(a6[a5])}}}if(!a.support.leadingWhitespace&&Z.test(a3)){aZ.insertBefore(a2.createTextNode(Z.exec(a3)[0]),aZ.firstChild)}a3=aZ.childNodes}}if(a3.nodeType){ba.push(a3)}else{ba=a.merge(ba,a3)}}if(a9){for(var a8=0;ba[a8];a8++){if(a4&&a.nodeName(ba[a8],"script")&&(!ba[a8].type||ba[a8].type.toLowerCase()==="text/javascript")){a4.push(ba[a8].parentNode?ba[a8].parentNode.removeChild(ba[a8]):ba[a8])}else{if(ba[a8].nodeType===1){ba.splice.apply(ba,[a8+1,0].concat(a.makeArray(ba[a8].getElementsByTagName("script"))))}a9.appendChild(ba[a8])}}}return ba},cleanData:function(aZ){var a2,a0,aY=a.cache,a5=a.event.special,a4=a.support.deleteExpando;for(var a3=0,a1;(a1=aZ[a3])!=null;a3++){a0=a1[a.expando];if(a0){a2=aY[a0];if(a2.events){for(var a6 in a2.events){if(a5[a6]){a.event.remove(a1,a6)}else{ag(a1,a6,a2.handle)}}}if(a4){delete a1[a.expando]}else{if(a1.removeAttribute){a1.removeAttribute(a.expando)}}delete aY[a0]}}}});var ar=/z-?index|font-?weight|opacity|zoom|line-?height/i,U=/alpha\([^)]*\)/,aa=/opacity=([^)]*)/,ah=/float/i,az=/-([a-z])/ig,v=/([A-Z])/g,aO=/^-?\d+(?:px)?$/i,aU=/^-?\d/,aK={position:"absolute",visibility:"hidden",display:"block"},W=["Left","Right"],aE=["Top","Bottom"],ak=ab.defaultView&&ab.defaultView.getComputedStyle,aN=a.support.cssFloat?"cssFloat":"styleFloat",k=function(aY,aZ){return aZ.toUpperCase()};a.fn.css=function(aY,aZ){return an(this,aY,aZ,true,function(a1,a0,a2){if(a2===C){return a.curCSS(a1,a0)}if(typeof a2==="number"&&!ar.test(a0)){a2+="px"}a.style(a1,a0,a2)})};a.extend({style:function(a2,aZ,a3){if(!a2||a2.nodeType===3||a2.nodeType===8){return C}if((aZ==="width"||aZ==="height")&&parseFloat(a3)<0){a3=C}var a1=a2.style||a2,a4=a3!==C;if(!a.support.opacity&&aZ==="opacity"){if(a4){a1.zoom=1;var aY=parseInt(a3,10)+""==="NaN"?"":"alpha(opacity="+a3*100+")";var a0=a1.filter||a.curCSS(a2,"filter")||"";a1.filter=U.test(a0)?a0.replace(U,aY):aY}return a1.filter&&a1.filter.indexOf("opacity=")>=0?(parseFloat(aa.exec(a1.filter)[1])/100)+"":""}if(ah.test(aZ)){aZ=aN}aZ=aZ.replace(az,k);if(a4){a1[aZ]=a3}return a1[aZ]},css:function(a1,aZ,a3,aY){if(aZ==="width"||aZ==="height"){var a5,a0=aK,a4=aZ==="width"?W:aE;function a2(){a5=aZ==="width"?a1.offsetWidth:a1.offsetHeight;if(aY==="border"){return}a.each(a4,function(){if(!aY){a5-=parseFloat(a.curCSS(a1,"padding"+this,true))||0}if(aY==="margin"){a5+=parseFloat(a.curCSS(a1,"margin"+this,true))||0}else{a5-=parseFloat(a.curCSS(a1,"border"+this+"Width",true))||0}})}if(a1.offsetWidth!==0){a2()}else{a.swap(a1,a0,a2)}return Math.max(0,Math.round(a5))}return a.curCSS(a1,aZ,a3)},curCSS:function(a4,aZ,a0){var a7,aY=a4.style,a1;if(!a.support.opacity&&aZ==="opacity"&&a4.currentStyle){a7=aa.test(a4.currentStyle.filter||"")?(parseFloat(RegExp.$1)/100)+"":"";return a7===""?"1":a7}if(ah.test(aZ)){aZ=aN}if(!a0&&aY&&aY[aZ]){a7=aY[aZ]}else{if(ak){if(ah.test(aZ)){aZ="float"}aZ=aZ.replace(v,"-$1").toLowerCase();var a6=a4.ownerDocument.defaultView;if(!a6){return null}var a8=a6.getComputedStyle(a4,null);if(a8){a7=a8.getPropertyValue(aZ)}if(aZ==="opacity"&&a7===""){a7="1"}}else{if(a4.currentStyle){var a3=aZ.replace(az,k);a7=a4.currentStyle[aZ]||a4.currentStyle[a3];if(!aO.test(a7)&&aU.test(a7)){var a2=aY.left,a5=a4.runtimeStyle.left;a4.runtimeStyle.left=a4.currentStyle.left;aY.left=a3==="fontSize"?"1em":(a7||0);a7=aY.pixelLeft+"px";aY.left=a2;a4.runtimeStyle.left=a5}}}}return a7},swap:function(a1,a0,a2){var aY={};for(var aZ in a0){aY[aZ]=a1.style[aZ];a1.style[aZ]=a0[aZ]}a2.call(a1);for(var aZ in a0){a1.style[aZ]=aY[aZ]}}});if(a.expr&&a.expr.filters){a.expr.filters.hidden=function(a1){var aZ=a1.offsetWidth,aY=a1.offsetHeight,a0=a1.nodeName.toLowerCase()==="tr";return aZ===0&&aY===0&&!a0?true:aZ>0&&aY>0&&!a0?false:a.curCSS(a1,"display")==="none"};a.expr.filters.visible=function(aY){return !a.expr.filters.hidden(aY)}}var af=aP(),aJ=/<script(.|\s)*?\/script>/gi,o=/select|textarea/i,aB=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,r=/=\?(&|$)/,D=/\?/,aX=/(\?|&)_=.*?(&|$)/,B=/^(\w+:)?\/\/([^\/?#]+)/,h=/%20/g,w=a.fn.load;a.fn.extend({load:function(a0,a3,a4){if(typeof a0!=="string"){return w.call(this,a0)}else{if(!this.length){return this}}var a2=a0.indexOf(" ");if(a2>=0){var aY=a0.slice(a2,a0.length);a0=a0.slice(0,a2)}var a1="GET";if(a3){if(a.isFunction(a3)){a4=a3;a3=null}else{if(typeof a3==="object"){a3=a.param(a3,a.ajaxSettings.traditional);a1="POST"}}}var aZ=this;a.ajax({url:a0,type:a1,dataType:"html",data:a3,complete:function(a6,a5){if(a5==="success"||a5==="notmodified"){aZ.html(aY?a("<div />").append(a6.responseText.replace(aJ,"")).find(aY):a6.responseText)}if(a4){aZ.each(a4,[a6.responseText,a5,a6])}}});return this},serialize:function(){return a.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?a.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||o.test(this.nodeName)||aB.test(this.type))}).map(function(aY,aZ){var a0=a(this).val();return a0==null?null:a.isArray(a0)?a.map(a0,function(a2,a1){return{name:aZ.name,value:a2}}):{name:aZ.name,value:a0}}).get()}});a.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(aY,aZ){a.fn[aZ]=function(a0){return this.bind(aZ,a0)}});a.extend({get:function(aY,a0,a1,aZ){if(a.isFunction(a0)){aZ=aZ||a1;a1=a0;a0=null}return a.ajax({type:"GET",url:aY,data:a0,success:a1,dataType:aZ})},getScript:function(aY,aZ){return a.get(aY,null,aZ,"script")},getJSON:function(aY,aZ,a0){return a.get(aY,aZ,a0,"json")},post:function(aY,a0,a1,aZ){if(a.isFunction(a0)){aZ=aZ||a1;a1=a0;a0={}}return a.ajax({type:"POST",url:aY,data:a0,success:a1,dataType:aZ})},ajaxSetup:function(aY){a.extend(a.ajaxSettings,aY)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:aM.XMLHttpRequest&&(aM.location.protocol!=="file:"||!aM.ActiveXObject)?function(){return new aM.XMLHttpRequest()}:function(){try{return new aM.ActiveXObject("Microsoft.XMLHTTP")}catch(aY){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(bd){var a8=a.extend(true,{},a.ajaxSettings,bd);var bi,bc,bh,bj=bd&&bd.context||a8,a0=a8.type.toUpperCase();if(a8.data&&a8.processData&&typeof a8.data!=="string"){a8.data=a.param(a8.data,a8.traditional)}if(a8.dataType==="jsonp"){if(a0==="GET"){if(!r.test(a8.url)){a8.url+=(D.test(a8.url)?"&":"?")+(a8.jsonp||"callback")+"=?"}}else{if(!a8.data||!r.test(a8.data)){a8.data=(a8.data?a8.data+"&":"")+(a8.jsonp||"callback")+"=?"}}a8.dataType="json"}if(a8.dataType==="json"&&(a8.data&&r.test(a8.data)||r.test(a8.url))){bi=a8.jsonpCallback||("jsonp"+af++);if(a8.data){a8.data=(a8.data+"").replace(r,"="+bi+"$1")}a8.url=a8.url.replace(r,"="+bi+"$1");a8.dataType="script";aM[bi]=aM[bi]||function(bk){bh=bk;a3();a6();aM[bi]=C;try{delete aM[bi]}catch(bl){}if(a1){a1.removeChild(bf)}}}if(a8.dataType==="script"&&a8.cache===null){a8.cache=false}if(a8.cache===false&&a0==="GET"){var aY=aP();var bg=a8.url.replace(aX,"$1_="+aY+"$2");a8.url=bg+((bg===a8.url)?(D.test(a8.url)?"&":"?")+"_="+aY:"")}if(a8.data&&a0==="GET"){a8.url+=(D.test(a8.url)?"&":"?")+a8.data}if(a8.global&&!a.active++){a.event.trigger("ajaxStart")}var bb=B.exec(a8.url),a2=bb&&(bb[1]&&bb[1]!==location.protocol||bb[2]!==location.host);if(a8.dataType==="script"&&a0==="GET"&&a2){var a1=ab.getElementsByTagName("head")[0]||ab.documentElement;var bf=ab.createElement("script");bf.src=a8.url;if(a8.scriptCharset){bf.charset=a8.scriptCharset}if(!bi){var ba=false;bf.onload=bf.onreadystatechange=function(){if(!ba&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){ba=true;a3();a6();bf.onload=bf.onreadystatechange=null;if(a1&&bf.parentNode){a1.removeChild(bf)}}}}a1.insertBefore(bf,a1.firstChild);return C}var a5=false;var a4=a8.xhr();if(!a4){return}if(a8.username){a4.open(a0,a8.url,a8.async,a8.username,a8.password)}else{a4.open(a0,a8.url,a8.async)}try{if(a8.data||bd&&bd.contentType){a4.setRequestHeader("Content-Type",a8.contentType)}if(a8.ifModified){if(a.lastModified[a8.url]){a4.setRequestHeader("If-Modified-Since",a.lastModified[a8.url])}if(a.etag[a8.url]){a4.setRequestHeader("If-None-Match",a.etag[a8.url])}}if(!a2){a4.setRequestHeader("X-Requested-With","XMLHttpRequest")}a4.setRequestHeader("Accept",a8.dataType&&a8.accepts[a8.dataType]?a8.accepts[a8.dataType]+", */*":a8.accepts._default)}catch(be){}if(a8.beforeSend&&a8.beforeSend.call(bj,a4,a8)===false){if(a8.global&&!--a.active){a.event.trigger("ajaxStop")}a4.abort();return false}if(a8.global){a9("ajaxSend",[a4,a8])}var a7=a4.onreadystatechange=function(bk){if(!a4||a4.readyState===0||bk==="abort"){if(!a5){a6()}a5=true;if(a4){a4.onreadystatechange=a.noop}}else{if(!a5&&a4&&(a4.readyState===4||bk==="timeout")){a5=true;a4.onreadystatechange=a.noop;bc=bk==="timeout"?"timeout":!a.httpSuccess(a4)?"error":a8.ifModified&&a.httpNotModified(a4,a8.url)?"notmodified":"success";var bm;if(bc==="success"){try{bh=a.httpData(a4,a8.dataType,a8)}catch(bl){bc="parsererror";bm=bl}}if(bc==="success"||bc==="notmodified"){if(!bi){a3()}}else{a.handleError(a8,a4,bc,bm)}a6();if(bk==="timeout"){a4.abort()}if(a8.async){a4=null}}}};try{var aZ=a4.abort;a4.abort=function(){if(a4){aZ.call(a4)}a7("abort")}}catch(be){}if(a8.async&&a8.timeout>0){setTimeout(function(){if(a4&&!a5){a7("timeout")}},a8.timeout)}try{a4.send(a0==="POST"||a0==="PUT"||a0==="DELETE"?a8.data:null)}catch(be){a.handleError(a8,a4,null,be);a6()}if(!a8.async){a7()}function a3(){if(a8.success){a8.success.call(bj,bh,bc,a4)}if(a8.global){a9("ajaxSuccess",[a4,a8])}}function a6(){if(a8.complete){a8.complete.call(bj,a4,bc)}if(a8.global){a9("ajaxComplete",[a4,a8])}if(a8.global&&!--a.active){a.event.trigger("ajaxStop")}}function a9(bl,bk){(a8.context?a(a8.context):a.event).trigger(bl,bk)}return a4},handleError:function(aZ,a1,aY,a0){if(aZ.error){aZ.error.call(aZ.context||aZ,a1,aY,a0)}if(aZ.global){(aZ.context?a(aZ.context):a.event).trigger("ajaxError",[a1,aZ,a0])}},active:0,httpSuccess:function(aZ){try{return !aZ.status&&location.protocol==="file:"||(aZ.status>=200&&aZ.status<300)||aZ.status===304||aZ.status===1223||aZ.status===0}catch(aY){}return false},httpNotModified:function(a1,aY){var a0=a1.getResponseHeader("Last-Modified"),aZ=a1.getResponseHeader("Etag");if(a0){a.lastModified[aY]=a0}if(aZ){a.etag[aY]=aZ}return a1.status===304||a1.status===0},httpData:function(a3,a1,a0){var aZ=a3.getResponseHeader("content-type")||"",aY=a1==="xml"||!a1&&aZ.indexOf("xml")>=0,a2=aY?a3.responseXML:a3.responseText;if(aY&&a2.documentElement.nodeName==="parsererror"){a.error("parsererror")}if(a0&&a0.dataFilter){a2=a0.dataFilter(a2,a1)}if(typeof a2==="string"){if(a1==="json"||!a1&&aZ.indexOf("json")>=0){a2=a.parseJSON(a2)}else{if(a1==="script"||!a1&&aZ.indexOf("javascript")>=0){a.globalEval(a2)}}}return a2},param:function(aY,a1){var aZ=[];if(a1===C){a1=a.ajaxSettings.traditional}if(a.isArray(aY)||aY.jquery){a.each(aY,function(){a3(this.name,this.value)})}else{for(var a2 in aY){a0(a2,aY[a2])}}return aZ.join("&").replace(h,"+");function a0(a4,a5){if(a.isArray(a5)){a.each(a5,function(a7,a6){if(a1||/\[\]$/.test(a4)){a3(a4,a6)}else{a0(a4+"["+(typeof a6==="object"||a.isArray(a6)?a7:"")+"]",a6)}})}else{if(!a1&&a5!=null&&typeof a5==="object"){a.each(a5,function(a7,a6){a0(a4+"["+a7+"]",a6)})}else{a3(a4,a5)}}}function a3(a4,a5){a5=a.isFunction(a5)?a5():a5;aZ[aZ.length]=encodeURIComponent(a4)+"="+encodeURIComponent(a5)}}});var G={},ae=/toggle|show|hide/,au=/^([+-]=)?([\d+-.]+)(.*)$/,aF,aj=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];a.fn.extend({show:function(aZ,a7){if(aZ||aZ===0){return this.animate(aD("show",3),aZ,a7)}else{for(var a4=0,a1=this.length;a4<a1;a4++){var aY=a.data(this[a4],"olddisplay");this[a4].style.display=aY||"";if(a.css(this[a4],"display")==="none"){var a6=this[a4].nodeName,a5;if(G[a6]){a5=G[a6]}else{var a0=a("<"+a6+" />").appendTo("body");a5=a0.css("display");if(a5==="none"){a5="block"}a0.remove();G[a6]=a5}a.data(this[a4],"olddisplay",a5)}}for(var a3=0,a2=this.length;a3<a2;a3++){this[a3].style.display=a.data(this[a3],"olddisplay")||""}return this}},hide:function(a3,a4){if(a3||a3===0){return this.animate(aD("hide",3),a3,a4)}else{for(var a2=0,aZ=this.length;a2<aZ;a2++){var aY=a.data(this[a2],"olddisplay");if(!aY&&aY!=="none"){a.data(this[a2],"olddisplay",a.css(this[a2],"display"))}}for(var a1=0,a0=this.length;a1<a0;a1++){this[a1].style.display="none"}return this}},_toggle:a.fn.toggle,toggle:function(a0,aZ){var aY=typeof a0==="boolean";if(a.isFunction(a0)&&a.isFunction(aZ)){this._toggle.apply(this,arguments)}else{if(a0==null||aY){this.each(function(){var a1=aY?a0:a(this).is(":hidden");a(this)[a1?"show":"hide"]()})}else{this.animate(aD("toggle",3),a0,aZ)}}return this},fadeTo:function(aY,a0,aZ){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:a0},aY,aZ)},animate:function(a2,aZ,a1,a0){var aY=a.speed(aZ,a1,a0);if(a.isEmptyObject(a2)){return this.each(aY.complete)}return this[aY.queue===false?"each":"queue"](function(){var a5=a.extend({},aY),a7,a6=this.nodeType===1&&a(this).is(":hidden"),a3=this;for(a7 in a2){var a4=a7.replace(az,k);if(a7!==a4){a2[a4]=a2[a7];delete a2[a7];a7=a4}if(a2[a7]==="hide"&&a6||a2[a7]==="show"&&!a6){return a5.complete.call(this)}if((a7==="height"||a7==="width")&&this.style){a5.display=a.css(this,"display");a5.overflow=this.style.overflow}if(a.isArray(a2[a7])){(a5.specialEasing=a5.specialEasing||{})[a7]=a2[a7][1];a2[a7]=a2[a7][0]}}if(a5.overflow!=null){this.style.overflow="hidden"}a5.curAnim=a.extend({},a2);a.each(a2,function(a9,bd){var bc=new a.fx(a3,a5,a9);if(ae.test(bd)){bc[bd==="toggle"?a6?"show":"hide":bd](a2)}else{var bb=au.exec(bd),be=bc.cur(true)||0;if(bb){var a8=parseFloat(bb[2]),ba=bb[3]||"px";if(ba!=="px"){a3.style[a9]=(a8||1)+ba;be=((a8||1)/bc.cur(true))*be;a3.style[a9]=be+ba}if(bb[1]){a8=((bb[1]==="-="?-1:1)*a8)+be}bc.custom(be,a8,ba)}else{bc.custom(be,bd,"")}}});return true})},stop:function(aZ,aY){var a0=a.timers;if(aZ){this.queue([])}this.each(function(){for(var a1=a0.length-1;a1>=0;a1--){if(a0[a1].elem===this){if(aY){a0[a1](true)}a0.splice(a1,1)}}});if(!aY){this.dequeue()}return this}});a.each({slideDown:aD("show",1),slideUp:aD("hide",1),slideToggle:aD("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(aY,aZ){a.fn[aY]=function(a0,a1){return this.animate(aZ,a0,a1)}});a.extend({speed:function(a0,a1,aZ){var aY=a0&&typeof a0==="object"?a0:{complete:aZ||!aZ&&a1||a.isFunction(a0)&&a0,duration:a0,easing:aZ&&a1||a1&&!a.isFunction(a1)&&a1};aY.duration=a.fx.off?0:typeof aY.duration==="number"?aY.duration:a.fx.speeds[aY.duration]||a.fx.speeds._default;aY.old=aY.complete;aY.complete=function(){if(aY.queue!==false){a(this).dequeue()}if(a.isFunction(aY.old)){aY.old.call(this)}};return aY},easing:{linear:function(a0,a1,aY,aZ){return aY+aZ*a0},swing:function(a0,a1,aY,aZ){return((-Math.cos(a0*Math.PI)/2)+0.5)*aZ+aY}},timers:[],fx:function(aZ,aY,a0){this.options=aY;this.elem=aZ;this.prop=a0;if(!aY.orig){aY.orig={}}}});a.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(a.fx.step[this.prop]||a.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(aZ){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var aY=parseFloat(a.css(this.elem,this.prop,aZ));return aY&&aY>-10000?aY:parseFloat(a.curCSS(this.elem,this.prop))||0},custom:function(a2,a1,a0){this.startTime=aP();this.start=a2;this.end=a1;this.unit=a0||this.unit||"px";this.now=this.start;this.pos=this.state=0;var aY=this;function aZ(a3){return aY.step(a3)}aZ.elem=this.elem;if(aZ()&&a.timers.push(aZ)&&!aF){aF=setInterval(a.fx.tick,13)}},show:function(){this.options.orig[this.prop]=a.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());a(this.elem).show()},hide:function(){this.options.orig[this.prop]=a.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a1){var a6=aP(),a2=true;if(a1||a6>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var a3 in this.options.curAnim){if(this.options.curAnim[a3]!==true){a2=false}}if(a2){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;var a0=a.data(this.elem,"olddisplay");this.elem.style.display=a0?a0:this.options.display;if(a.css(this.elem,"display")==="none"){this.elem.style.display="block"}}if(this.options.hide){a(this.elem).hide()}if(this.options.hide||this.options.show){for(var aY in this.options.curAnim){a.style(this.elem,aY,this.options.orig[aY])}}this.options.complete.call(this.elem)}return false}else{var aZ=a6-this.startTime;this.state=aZ/this.options.duration;var a4=this.options.specialEasing&&this.options.specialEasing[this.prop];var a5=this.options.easing||(a.easing.swing?"swing":"linear");this.pos=a.easing[a4||a5](this.state,aZ,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};a.extend(a.fx,{tick:function(){var aZ=a.timers;for(var aY=0;aY<aZ.length;aY++){if(!aZ[aY]()){aZ.splice(aY--,1)}}if(!aZ.length){a.fx.stop()}},stop:function(){clearInterval(aF);aF=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(aY){a.style(aY.elem,"opacity",aY.now)},_default:function(aY){if(aY.elem.style&&aY.elem.style[aY.prop]!=null){aY.elem.style[aY.prop]=(aY.prop==="width"||aY.prop==="height"?Math.max(0,aY.now):aY.now)+aY.unit}else{aY.elem[aY.prop]=aY.now}}}});if(a.expr&&a.expr.filters){a.expr.filters.animated=function(aY){return a.grep(a.timers,function(aZ){return aY===aZ.elem}).length}}function aD(aZ,aY){var a0={};a.each(aj.concat.apply([],aj.slice(0,aY)),function(){a0[this]=aZ});return a0}if("getBoundingClientRect" in ab.documentElement){a.fn.offset=function(a7){var a0=this[0];if(a7){return this.each(function(a8){a.offset.setOffset(this,a7,a8)})}if(!a0||!a0.ownerDocument){return null}if(a0===a0.ownerDocument.body){return a.offset.bodyOffset(a0)}var a2=a0.getBoundingClientRect(),a6=a0.ownerDocument,a3=a6.body,aY=a6.documentElement,a1=aY.clientTop||a3.clientTop||0,a4=aY.clientLeft||a3.clientLeft||0,a5=a2.top+(self.pageYOffset||a.support.boxModel&&aY.scrollTop||a3.scrollTop)-a1,aZ=a2.left+(self.pageXOffset||a.support.boxModel&&aY.scrollLeft||a3.scrollLeft)-a4;return{top:a5,left:aZ}}}else{a.fn.offset=function(a9){var a3=this[0];if(a9){return this.each(function(ba){a.offset.setOffset(this,a9,ba)})}if(!a3||!a3.ownerDocument){return null}if(a3===a3.ownerDocument.body){return a.offset.bodyOffset(a3)}a.offset.initialize();var a0=a3.offsetParent,aZ=a3,a8=a3.ownerDocument,a6,a1=a8.documentElement,a4=a8.body,a5=a8.defaultView,aY=a5?a5.getComputedStyle(a3,null):a3.currentStyle,a7=a3.offsetTop,a2=a3.offsetLeft;while((a3=a3.parentNode)&&a3!==a4&&a3!==a1){if(a.offset.supportsFixedPosition&&aY.position==="fixed"){break}a6=a5?a5.getComputedStyle(a3,null):a3.currentStyle;a7-=a3.scrollTop;a2-=a3.scrollLeft;if(a3===a0){a7+=a3.offsetTop;a2+=a3.offsetLeft;if(a.offset.doesNotAddBorder&&!(a.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(a3.nodeName))){a7+=parseFloat(a6.borderTopWidth)||0;a2+=parseFloat(a6.borderLeftWidth)||0}aZ=a0,a0=a3.offsetParent}if(a.offset.subtractsBorderForOverflowNotVisible&&a6.overflow!=="visible"){a7+=parseFloat(a6.borderTopWidth)||0;a2+=parseFloat(a6.borderLeftWidth)||0}aY=a6}if(aY.position==="relative"||aY.position==="static"){a7+=a4.offsetTop;a2+=a4.offsetLeft}if(a.offset.supportsFixedPosition&&aY.position==="fixed"){a7+=Math.max(a1.scrollTop,a4.scrollTop);a2+=Math.max(a1.scrollLeft,a4.scrollLeft)}return{top:a7,left:a2}}}a.offset={initialize:function(){var aY=ab.body,aZ=ab.createElement("div"),a2,a4,a3,a5,a0=parseFloat(a.curCSS(aY,"marginTop",true))||0,a1="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";a.extend(aZ.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});aZ.innerHTML=a1;aY.insertBefore(aZ,aY.firstChild);a2=aZ.firstChild;a4=a2.firstChild;a5=a2.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(a4.offsetTop!==5);this.doesAddBorderForTableAndCells=(a5.offsetTop===5);a4.style.position="fixed",a4.style.top="20px";this.supportsFixedPosition=(a4.offsetTop===20||a4.offsetTop===15);a4.style.position=a4.style.top="";a2.style.overflow="hidden",a2.style.position="relative";this.subtractsBorderForOverflowNotVisible=(a4.offsetTop===-5);this.doesNotIncludeMarginInBodyOffset=(aY.offsetTop!==a0);aY.removeChild(aZ);aY=aZ=a2=a4=a3=a5=null;a.offset.initialize=a.noop},bodyOffset:function(aY){var a0=aY.offsetTop,aZ=aY.offsetLeft;a.offset.initialize();if(a.offset.doesNotIncludeMarginInBodyOffset){a0+=parseFloat(a.curCSS(aY,"marginTop",true))||0;aZ+=parseFloat(a.curCSS(aY,"marginLeft",true))||0}return{top:a0,left:aZ}},setOffset:function(a3,aZ,a0){if(/static/.test(a.curCSS(a3,"position"))){a3.style.position="relative"}var a2=a(a3),a5=a2.offset(),aY=parseInt(a.curCSS(a3,"top",true),10)||0,a4=parseInt(a.curCSS(a3,"left",true),10)||0;if(a.isFunction(aZ)){aZ=aZ.call(a3,a0,a5)}var a1={top:(aZ.top-a5.top)+aY,left:(aZ.left-a5.left)+a4};if("using" in aZ){aZ.using.call(a3,a1)}else{a2.css(a1)}}};a.fn.extend({position:function(){if(!this[0]){return null}var a0=this[0],aZ=this.offsetParent(),a1=this.offset(),aY=/^body|html$/i.test(aZ[0].nodeName)?{top:0,left:0}:aZ.offset();a1.top-=parseFloat(a.curCSS(a0,"marginTop",true))||0;a1.left-=parseFloat(a.curCSS(a0,"marginLeft",true))||0;aY.top+=parseFloat(a.curCSS(aZ[0],"borderTopWidth",true))||0;aY.left+=parseFloat(a.curCSS(aZ[0],"borderLeftWidth",true))||0;return{top:a1.top-aY.top,left:a1.left-aY.left}},offsetParent:function(){return this.map(function(){var aY=this.offsetParent||ab.body;while(aY&&(!/^body|html$/i.test(aY.nodeName)&&a.css(aY,"position")==="static")){aY=aY.offsetParent}return aY})}});a.each(["Left","Top"],function(aZ,aY){var a0="scroll"+aY;a.fn[a0]=function(a3){var a1=this[0],a2;if(!a1){return null}if(a3!==C){return this.each(function(){a2=am(this);if(a2){a2.scrollTo(!aZ?a3:a(a2).scrollLeft(),aZ?a3:a(a2).scrollTop())}else{this[a0]=a3}})}else{a2=am(a1);return a2?("pageXOffset" in a2)?a2[aZ?"pageYOffset":"pageXOffset"]:a.support.boxModel&&a2.document.documentElement[a0]||a2.document.body[a0]:a1[a0]}}});function am(aY){return("scrollTo" in aY&&aY.document)?aY:aY.nodeType===9?aY.defaultView||aY.parentWindow:false}a.each(["Height","Width"],function(aZ,aY){var a0=aY.toLowerCase();a.fn["inner"+aY]=function(){return this[0]?a.css(this[0],a0,false,"padding"):null};a.fn["outer"+aY]=function(a1){return this[0]?a.css(this[0],a0,false,a1?"margin":"border"):null};a.fn[a0]=function(a1){var a2=this[0];if(!a2){return a1==null?null:this}if(a.isFunction(a1)){return this.each(function(a4){var a3=a(this);a3[a0](a1.call(this,a4,a3[a0]()))})}return("scrollTo" in a2&&a2.document)?a2.document.compatMode==="CSS1Compat"&&a2.document.documentElement["client"+aY]||a2.document.body["client"+aY]:(a2.nodeType===9)?Math.max(a2.documentElement["client"+aY],a2.body["scroll"+aY],a2.documentElement["scroll"+aY],a2.body["offset"+aY],a2.documentElement["offset"+aY]):a1===C?a.css(a2,a0):this.css(a0,typeof a1==="string"?a1:a1+"px")}});aM.jQuery=aM.$=a})(window);
\ No newline at end of file diff --git a/test/mauve_test_helper.rb b/test/mauve_test_helper.rb index 8653bd5..ca2db61 100644 --- a/test/mauve_test_helper.rb +++ b/test/mauve_test_helper.rb @@ -74,7 +74,7 @@ module MauveTestHelper # # [destination, alert, other_alerts] # - # e.g. destination will be an email address, phone number or xmpp ID, just + # e.g. destination will be an email address, or phone number, just # as in the configuration file. alert will be the subject of this # alert, and other_alerts will be the other notifications that # are relevant for this person at this time. diff --git a/test/tc_mauve_authentication.rb b/test/tc_mauve_authentication.rb index a770bc9..0f1a158 100644 --- a/test/tc_mauve_authentication.rb +++ b/test/tc_mauve_authentication.rb @@ -67,7 +67,7 @@ EOF end - def test_local_auth + def test_local_auth_again config=<<EOF failed_login_delay 0 diff --git a/test/tc_mauve_configuration_builders_person.rb b/test/tc_mauve_configuration_builders_person.rb index 76841a3..77bae3b 100644 --- a/test/tc_mauve_configuration_builders_person.rb +++ b/test/tc_mauve_configuration_builders_person.rb @@ -13,7 +13,6 @@ person("test1") { email "test1@example.com" sms "01234567890" - xmpp "test1@chat.example.com" password "topsekrit" @@ -35,7 +34,6 @@ EOF person = x.people["test1"] assert_equal("test1@example.com", person.email) assert_equal("01234567890", person.sms) - assert_equal("test1@chat.example.com", person.xmpp) assert_equal("topsekrit", person.password) # assert_equal(300, x.people["test1"].every) @@ -59,7 +57,6 @@ EOF assert_equal(nil, person.sms) assert_equal(nil, person.email) - assert_equal(nil, person.xmpp) assert_kind_of(Proc, person.low) assert_kind_of(Proc, person.normal) diff --git a/test/tc_mauve_database_peculiarities.rb b/test/tc_mauve_database_peculiarities.rb index ceef9ad..8696ce7 100644 --- a/test/tc_mauve_database_peculiarities.rb +++ b/test/tc_mauve_database_peculiarities.rb @@ -1,5 +1,5 @@ # encoding: utf-8 -$:.unshift "../lib" +$LOAD_PATH.unshift '../lib' require 'th_mauve' require 'mauve/datamapper' @@ -14,7 +14,7 @@ class TcMauveDatabasePeculiarities < Mauve::UnitTest def setup super setup_database - @temp_db = "mauve_test.#{10.times.collect{ rand(36).to_s(36) }.join}" + @temp_db = "mauve_test.#{Array.new(10) { rand(36).to_s(36) }.join}" end def teardown @@ -28,7 +28,7 @@ class TcMauveDatabasePeculiarities < Mauve::UnitTest # return unless @db_url - config=<<EOF + config = <<EOF server { database "#{@db_url}" } @@ -37,21 +37,21 @@ EOF Configuration.current = ConfigurationBuilder.parse(config) Server.instance.setup - x = Hash.new - x["en"] = "Please rush me my portable walrus polishing kit!" - x["fi"] = "Ole hyvä kiirehtiä minulle kannettavan mursu kiillotukseen pakki!" - x["jp"] = "私に私のポータブルセイウチの研磨キットを急いでください!" + x = {} + x['en'] = 'Please rush me my portable walrus polishing kit!' + x['fi'] = 'Ole hyvä kiirehtiä minulle kannettavan mursu kiillotukseen pakki!' + x['jp'] = '私に私のポータブルセイウチの研磨キットを急いでください!' %w(UTF-8 WINDOWS-1252 SJIS).each do |enc| x.each do |lang, str| - assert_nothing_raised("Failed to use iconv to convert to #{enc}") { - str = str.encode(enc, :invalid => :replace, :undef => :replace, :replace => '?') - } + assert_nothing_raised("Failed to use iconv to convert to #{enc}") do + str = str.encode(enc, invalid: :replace, undef: :replace, replace: '?') + end alert = Alert.new( - :alert_id => "#{lang}:#{enc}", - :source => "test", - :subject => str + alert_id: "#{lang}:#{enc}", + source: 'test', + subject: str ) assert_nothing_raised("failed to insert #{enc}") { alert.save } @@ -60,30 +60,32 @@ EOF end end - - class TcMauveDatabasePostgresPeculiarities < TcMauveDatabasePeculiarities def setup super - system("createdb #{@temp_db} --encoding UTF8") - unless $?.success? - msg = "Skipping postgres tests, as DB creation (#{@temp_db}) failed." + + if ENV['POSTGRES_URL'] + @db_url = ENV['POSTGRES_URL'] @temp_db = nil - flunk(msg) + else + system("createdb #{@temp_db} --encoding UTF8") + unless $?.success? + msg = "Skipping postgres tests, as DB creation (#{@temp_db}) failed." + @temp_db = nil + omit(msg) + end + # @pg_conn = PGconn.open(:dbname => @temp_db) + @db_url = "postgres:///#{@temp_db}" end - # @pg_conn = PGconn.open(:dbname => @temp_db) - @db_url = "postgres:///#{@temp_db}" end def teardown - # @pg_conn.finish if @pg_conn.is_a?(PGconn) and @pg_conn.status == PGconn::CONNECTION_OK super (system("dropdb #{@temp_db}") || puts("Failed to drop #{@temp_db}")) if @temp_db end - def test_reminders_only_go_once - config=<<EOF + config = <<EOF server { database "#{@db_url}" use_notification_buffer false @@ -109,16 +111,16 @@ alert_group("default") { } EOF Configuration.current = ConfigurationBuilder.parse(config) - notification_buffer = Configuration.current.notification_methods["email"].deliver_to_queue + notification_buffer = Configuration.current.notification_methods['email'].deliver_to_queue Server.instance.setup - a = Alert.new( - :alert_id => "test", - :source => "test", - :subject => "test" + a = Alert.new( + alert_id: 'test', + source: 'test', + subject: 'test' ) - assert(a.raise!, "raise was not successful") + assert(a.raise!, 'raise was not successful') assert(a.saved?) assert_equal(1, notification_buffer.length) notification_buffer.pop @@ -133,17 +135,14 @@ EOF assert_equal(1, notification_buffer.length) notification_buffer.pop end - end - - end class TcMauveDatabaseSqlite3Peculiarities < TcMauveDatabasePeculiarities def setup super # @pg_conn = PGconn.open(:dbname => @temp_db) - @db_url = "sqlite3::memory:" + @db_url = 'sqlite3::memory:' end # @@ -153,6 +152,4 @@ class TcMauveDatabaseSqlite3Peculiarities < TcMauveDatabasePeculiarities assert DataMapper::Adapters::SqliteAdapter.private_instance_methods.include?(:with_connection_old) assert DataMapper::Adapters::SqliteAdapter.public_instance_methods.include?(:synchronize) end - end - diff --git a/test/tc_mauve_web_interface.rb b/test/tc_mauve_web_interface.rb index 54c9697..c93c25c 100644 --- a/test/tc_mauve_web_interface.rb +++ b/test/tc_mauve_web_interface.rb @@ -112,13 +112,21 @@ EOF assert last_response.body.include?("Mauve: Login") assert session['__FLASH__'].empty? - # Check we can access this page before logging in. + # Check we can't access this page before logging in. get '/alerts' assert(session['__FLASH__'].has_key?(:error),"The flash error wasn't set following forbidden access") follow_redirect! while last_response.redirect? assert_equal(403, last_response.status, "The HTTP status wasn't 403") assert last_response.body.include?("Mauve: Login") assert session['__FLASH__'].empty? + + # Check we can't access AJAX requests before logging in. + get '/ajax/alerts_table/raised/subject', {}, {:xhr => true} + refute(session['__FLASH__'].has_key?(:error), "The flash error shouldn't have been set from an AJAX call") + follow_redirect! while last_response.redirect? + assert_equal(403, last_response.status, "The HTTP status wasn't 403") + assert last_response.body.include?('You must be logged in to access this page') + assert session['__FLASH__'].empty? # # Try to falsify our login. diff --git a/utils/buffer_size.sh b/utils/buffer_size.sh deleted file mode 100755 index 02984b2..0000000 --- a/utils/buffer_size.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/sh - -NO_ARGS=0 -OPTERROR=65 -if [ $# -eq "$NO_ARGS" ] # Script invoked with no command-line args? -then - echo "Usage: `basename $0` File.log" - exit $OPTERROR # Exit and explain usage, if no argument(s) given. -fi -logFile=$1 - -egrep 'Buffer has [0-9]* packets left' $logFile |\ - awk 'BEGIN {print " date sz"} {print s++ " " $1 "::" $2 " " $10}' > data - -R --vanilla --no-save --slave <<RSCRIPT -lst <- read.table("data") -attach(lst) -summary(sz) -png(filename="buffer.png", width=1024) -dates <- strptime(as.character(date), "%Y-%m-%d::%H:%M:%S") -plot(dates, sz, type='l', - main="Mauve server: Maximum buffer size per second", - xlab="Time", - ylab="Maximum number of packets waiting to be processed") -abline(h=1.05, col="red") -abline(h=mean(sz), col="blue") -RSCRIPT -img=`which qiv` -if [ $? != 0 ] -then echo "Cannot display image here" -else $img buffer.png -fi diff --git a/utils/clear_wrong_reminder.sql b/utils/clear_wrong_reminder.sql deleted file mode 100644 index fde0504..0000000 --- a/utils/clear_wrong_reminder.sql +++ /dev/null @@ -1,4 +0,0 @@ -select * from 'mauve_alerts' where update_type='cleared' and will_unacknowledge_at != 0; - -update 'mauve_alerts' set will_unacknowledge_at=NULL where update_type='cleared' and will_unacknowledge_at != 0; - diff --git a/utils/get_last_test_db.sh b/utils/get_last_test_db.sh deleted file mode 100755 index 71ce4f1..0000000 --- a/utils/get_last_test_db.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -test=`echo $1 | sed s/://` -sqlitebrowser `/bin/ls -dtr /tmp/mauve_test/* | tail -1`/$test\(ZTestCases\)/mauve_test.db diff --git a/utils/get_last_test_log.sh b/utils/get_last_test_log.sh deleted file mode 100755 index 26597d0..0000000 --- a/utils/get_last_test_log.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -test=`echo $1 | sed s/://` -less `/bin/ls -dtr /tmp/mauve_test/* | tail -1`/$test\(ZTestCases\)/log000001 diff --git a/utils/get_live_db.sh b/utils/get_live_db.sh deleted file mode 100755 index 35c5aea..0000000 --- a/utils/get_live_db.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -scp one-eyed-jack:/var/lib/mauveserver/alerts.db . && \ - sqlitebrowser alerts.db diff --git a/heartbeat_hammer.sh b/utils/heartbeat_hammer.sh index 12b74bc..12b74bc 100644 --- a/heartbeat_hammer.sh +++ b/utils/heartbeat_hammer.sh diff --git a/utils/init_jmauve.sh b/utils/init_jmauve.sh deleted file mode 100755 index 3011d62..0000000 --- a/utils/init_jmauve.sh +++ /dev/null @@ -1,80 +0,0 @@ -#!/bin/sh -### BEGIN INIT INFO -# Provides: mauveserver -# Required-Start: $remote_fs $syslog -# Required-Stop: $remote_fs $syslog -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Start the mauve alerting daemon at boot time -# Description: Start the mauve alerting daemon at boot time -### END INIT INFO - -PATH=/bin:/sbin:/usr/bin:/usr/local/bin -DAEMON=/usr/bin/jmauveserver -DAEMON_OPTS=/etc/mauvealert/mauveserver.conf -DESC="mauvealert server" -PIDFILE=/var/run/jmauveserver.pid -LOG=/var/log/mauve - -test -x $DAEMON || exit 0 - -. /lib/lsb/init-functions - -case "$1" in - start) - log_begin_msg "Starting $DESC:" "$NAME" - if [ ! -d $LOG ]; then mkdir $LOG; fi - chown mauveserver $LOG /var/lib/mauveserver - $DAEMON $DAEMON_OPTS & - echo $! > $PIDFILE - for i in `seq 0 1 11`;do sleep 1; echo -n '.'; done - kill -0 $(cat $PIDFILE) - if [ $? != 0 ] ; then echo -n "failed"; else echo -n "success"; fi - - # Email on start. - address="yann@bytemark.co.uk" - lastLog=`/bin/ls -tr $LOG/*.log | tail -1` - logLastLines=`tail -101 $lastLog` - echo $logLastLines | mail -s "Mauve was started at `date`" $address - - log_end_msg $? - ;; - stop) - log_begin_msg "Stopping $DESC:" "$NAME" - if [ -f $PIDFILE ] ; then - kill `cat $PIDFILE` - rm $PIDFILE - - # Email on stop. - address="yann@bytemark.co.uk" - lastLog=`/bin/ls -tr $LOG/*.log | tail -1` - logLastLines=`tail -101 $lastLog` - echo $logLastLines | mail -s "Mauve was stopped at `date`" $address - - else - echo Not running to stop - exit 1 - fi - log_end_msg $? - ;; - reload) - if [ -f $PIDFILE ] ; then - echo Sending restart signal to mauveserver - kill -HUP `cat $PIDFILE` - else - echo Not running to reload - fi - ;; - restart|reload|force-reload) - $0 stop - sleep 1 - $0 start - ;; - *) - N=/etc/init.d/$NAME - echo "Usage: $N {start|stop|restart}" >&2 - exit 1 - ;; -esac - -exit 0 diff --git a/utils/init_mauve.sh b/utils/init_mauve.sh deleted file mode 100755 index 309562c..0000000 --- a/utils/init_mauve.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/bin/sh -### BEGIN INIT INFO -# Provides: mauveserver -# Required-Start: $remote_fs $syslog -# Required-Stop: $remote_fs $syslog -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Start the mauve alerting daemon at boot time -# Description: Start the mauve alerting daemon at boot time -### END INIT INFO - -PATH=/bin:/sbin:/usr/bin:/usr/local/bin -DAEMON=/usr/bin/mauveserver -DAEMON_OPTS=/etc/mauvealert/mauveserver.conf -DESC="mauvealert server" -PIDFILE=/var/run/mauveserver.pid -LOG=/var/log/mauve - -test -x $DAEMON || exit 0 - -. /lib/lsb/init-functions - -case "$1" in - start) - log_begin_msg "Starting $DESC:" "$NAME" - if [ ! -d $LOG ]; then mkdir $LOG; fi - chown mauveserver $LOG /var/lib/mauveserver - $DAEMON $DAEMON_OPTS & - echo $! > $PIDFILE - #start-stop-daemon --background --make-pidfile --pidfile $PIDFILE \ - # --start --quiet --chuid mauveserver \ - # --exec "$DAEMON" --oknodo -- $DAEMON_OPTS - sleep 3 - #kill -0 $(cat $PIDFILE) - #if [ $? != 0 ] ; then echo -n "failed"; else echo -n "success"; fi - - # Email on start. - address="yann@bytemark.co.uk" - lastLog=`/bin/ls -tr $LOG/*.log | tail -1` - logLastLines=`tail -101 $lastLog` - echo $logLastLines | mail -s "Mauve was started at `date`" $address - - log_end_msg $? - ;; - stop) - log_begin_msg "Stopping $DESC:" "$NAME" - if [ -f $PIDFILE ] ; then - kill `cat $PIDFILE` - rm $PIDFILE - - # Email on stop. - address="yann@bytemark.co.uk" - lastLog=`/bin/ls -tr $LOG/*.log | tail -1` - logLastLines=`tail -101 $lastLog` - echo $logLastLines | mail -s "Mauve was stopped at `date`" $address - - else - echo Not running to stop - exit 1 - fi - log_end_msg $? - ;; - reload) - if [ -f $PIDFILE ] ; then - echo Sending restart signal to mauveserver - kill -HUP `cat $PIDFILE` - else - echo Not running to reload - fi - ;; - restart|reload|force-reload) - $0 stop - sleep 1 - $0 start - ;; - *) - N=/etc/init.d/$NAME - echo "Usage: $N {start|stop|restart}" >&2 - exit 1 - ;; -esac - -exit 0 diff --git a/utils/packet_processing.sh b/utils/packet_processing.sh deleted file mode 100755 index d78a916..0000000 --- a/utils/packet_processing.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/sh - -NO_ARGS=0 -OPTERROR=65 -if [ $# -eq "$NO_ARGS" ] # Script invoked with no command-line args? -then - echo "Usage: `basename $0` File.log" - exit $OPTERROR # Exit and explain usage, if no argument(s) given. -fi -logFile=$1 - -egrep 'Packet processed in [\.0-9]* seconds' $logFile |\ - awk 'BEGIN {print " date sz"} {print s++ " " $1 "::" $2 " " $11}' > data - -R --vanilla --no-save --slave <<RSCRIPT -lst <- read.table("data") -attach(lst) -summary(sz) -png(filename="packets.png", width=1024) -dates <- strptime(as.character(date), "%Y-%m-%d::%H:%M:%S") -plot(dates, sz, type='l', - main="Mauve server: maximum processing time of a packet per second.", - xlab="Time", - ylab="Maximum processing time of one packet") -abline(h=1.05, col="red") -abline(h=mean(sz), col="blue") -RSCRIPT -img=`which qiv` -if [ $? != 0 ] -then echo "Cannot display image here" -else $img packets.png -fi diff --git a/utils/run_android.sh b/utils/run_android.sh deleted file mode 100755 index e932257..0000000 --- a/utils/run_android.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -/usr/local/android-sdk-linux_x86/tools/android& diff --git a/utils/supportbot-mauve b/utils/supportbot-mauve deleted file mode 100755 index 614c5f5..0000000 --- a/utils/supportbot-mauve +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/ruby -require 'fileutils' -require 'yaml' -require 'timeout' -require 'rubygems' -require 'cgi' - -class RT - class Ticket < Struct.new(:id, :subject, :content) - end - - def initialize(cli, ticket_query) - @cli = cli - @ticket_query = ticket_query - end - - def query(args) - IO.popen("#{@cli} #{args}", "r") do |io| - io.read - end - end - - def tickets - query(@ticket_query).split("\n").map do |line| - if /^(\d+): (.*)$/.match(line) - t = Ticket.new($1.to_i, $2, "") - query("show ticket/#{t.id}/attachments").split("\n")[-5..-1].each do |line2| - next unless /\s(\d+):\s+\(text\/(plain|html)/.match(line2) - is_html = $2 == "html" - - attachment = query("show -f Content ticket/#{t.id}/attachments/#{$1}"). - split("Content: ")[1]. - split("\n"). - map { |line3| line3.gsub(/^\s+/,"") }. - join("\n") - next unless attachment.match(/Action=Take/) - if is_html - t.content += attachment + "<hr/>" - else - t.content += "<pre>"+attachment+"</pre><hr/>" - end - end - t - else - nil - end - end.compact - end -end - -class String; def escape; gsub(/[^A-Za-z0-9]/) { |x| "\\#{x}" }; end end - -config = File.open("supportbot.config") { |fh| - YAML::parse(fh.read).transform } - -system("mauvesend -o supportbot -i heartbeat -r +10m -c now -s \"heartbeat not received\" -d \"Headtbeat send from supportbot-mauve code. It maybe down.\"") - -rt = RT.new(config['rt']['bin'], config['rt']['ticket_query2']) - -if 0 < rt.tickets.size - args = %w( mauvesend alert.bytemark.co.uk -o supportbot ) - args += rt.tickets.map do |ticket| - ["-i", ticket.id.to_s, - "-u", ticket.id.to_s, - "-s", ticket.subject, - "-d", ticket.content - ] - end.flatten - exec(*args) -else - args = %w( mauvesend alert.bytemark.co.uk --id supportbot -o supportbot -p -c now ) - exec(*args) -end diff --git a/utils/test-smack.rb b/utils/test-smack.rb deleted file mode 100755 index 022f9d0..0000000 --- a/utils/test-smack.rb +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/jruby -# CLASSPATH="$CLASSPATH:/home/yann/projects/mauvealert/jars/smack.jar:/home/yann/projects/mauvealert/jars/smackx.jar -# ./test-smack - -require 'java' -require '../jars/smack.jar' -require '../jars/smackx.jar' -require 'rubygems' -require 'rainbow' -require 'pp' - -include_class "org.jivesoftware.smack.XMPPConnection" -include_class "org.jivesoftware.smackx.muc.MultiUserChat" - -user_jid='mauvealert' -password='WojIsEv8ScaufOm1' -msg = "What fresh hell is this? -- Dorothy Parker." -begin - - print "XMPP object instanciated.\n".color(:green) - xmpp = XMPPConnection.new("chat.bytemark.co.uk") - - - print "Connection done.\n".color(:green) - xmpp.connect - if true != xmpp.isConnected - print "Failed to connect".color(:red) - return -1 - end - - - print "Login.\n".color(:green) - xmpp.login(user_jid, password, "Testing_smack") - if true != xmpp.isAuthenticated() - print "Failed to authenticate\n".color(:red) - return -1 - end - if true == xmpp.isSecureConnection() - print "Connection is secure\n".color(:green) - else - print "Connection is NOT secure.\n".color(:yellow) - end - - - print "Get chat manager.\n".color(:green) - chat = xmpp.getChatManager.createChat( - "yann@chat.bytemark.co.uk", nil) - - print "Sending message to #{chat.getParticipant}.\n".color(:green) - chat.sendMessage(msg) - - - print "Joining, sending a message and leaving a room.\n".color(:green) - #muc = MultiUserChat.new(xmpp, "office@conference.chat.bytemark.co.uk") - muc = MultiUserChat.new(xmpp, "test@conference.chat.bytemark.co.uk") - muc.join("Mauve alert bot") - muc.sendMessage(msg) - sleep 1 - #muc.leave() - - - print "Adieu monde cruel!\n".color(:green) - xmpp.disconnect - - - print "all done.\n".color(:green) -rescue => ex - print "EPIC FAIL: Raised #{ex.class} because #{ex.message}\n\n".color(:red) - raise ex -end - -=begin -require 'java' -require './jars/smack.jar' -require './jars/smackx.jar' -include_class "org.jivesoftware.smack.XMPPConnection" -include_class "org.jivesoftware.smackx.muc.MultiUserChat" -user_jid='mauvealert' -password='WojIsEv8ScaufOm1' -msg = "What fresh hell is this? -- Dorothy Parker." -xmpp = XMPPConnection.new("chat.bytemark.co.uk") -xmpp.connect -xmpp.login(user_jid, password, "mauve_test") - -jid="yann@chat.bytemark.co.uk" -chat = xmpp.getChatManager.createChat(jid, nil) -chat.sendMessage(msg) - -xmpp.getRoster().reload() -xmpp.getRoster().getPresence(jid).isAvailable() -xmpp.getRoster().getPresence(jid).getStatus() - -muc = MultiUserChat.new(xmpp, 'office@conference.chat.bytemark.co.uk/mauvealert') -muc.join("Mauve alert bot") -muc.sendMessage(msg) - -muc2 = MultiUserChat.new(xmpp, "test@conference.chat.bytemark.co.uk") -muc2.join("Mauve alert bot") -muc2.sendMessage(msg) - -=end diff --git a/views/_navbar.haml b/views/_navbar.haml index bd23308..840fa7e 100644 --- a/views/_navbar.haml +++ b/views/_navbar.haml @@ -16,7 +16,7 @@ %li{:class => [ request.path_info =~ /^\/search/ && "nav_selected"]} %a{:href => '/search'} Search %li{:class => [ request.path_info =~ /^\/events/ && "nav_selected"]} - %a{:href => '/events/calendar'} Events + %a{:href => '/events/list'} Events %li %a{:href => '/logout'} Log out %br |