Browse Source

First commit

Andrew Kane 9 years ago
commit
febad48ae1
47 changed files with 27500 additions and 0 deletions
  1. 14 0
      .gitignore
  2. 4 0
      Gemfile
  3. 22 0
      LICENSE.txt
  4. 144 0
      README.md
  5. 2 0
      Rakefile
  6. 0 0
      app/assets/javascripts/blazer/ace/ace.js
  7. 0 0
      app/assets/javascripts/blazer/ace/ext-language_tools.js
  8. 1 0
      app/assets/javascripts/blazer/ace/mode-sql.js
  9. 1 0
      app/assets/javascripts/blazer/ace/snippets/sql.js
  10. 1 0
      app/assets/javascripts/blazer/ace/snippets/text.js
  11. 0 0
      app/assets/javascripts/blazer/ace/theme-twilight.js
  12. 15 0
      app/assets/javascripts/blazer/application.js
  13. 1026 0
      app/assets/javascripts/blazer/daterangepicker.js
  14. 0 0
      app/assets/javascripts/blazer/highlight.pack.js
  15. 10308 0
      app/assets/javascripts/blazer/jquery.js
  16. 263 0
      app/assets/javascripts/blazer/jquery.stickytableheaders.js
  17. 469 0
      app/assets/javascripts/blazer/jquery_ujs.js
  18. 1474 0
      app/assets/javascripts/blazer/list.js
  19. 2400 0
      app/assets/javascripts/blazer/moment.js
  20. 3477 0
      app/assets/javascripts/blazer/selectize.js
  21. 114 0
      app/assets/javascripts/blazer/stupidtable.js
  22. 66 0
      app/assets/stylesheets/blazer/application.css
  23. 6203 0
      app/assets/stylesheets/blazer/bootstrap.css
  24. 0 0
      app/assets/stylesheets/blazer/bootstrap.css.map
  25. 267 0
      app/assets/stylesheets/blazer/daterangepicker-bs3.css
  26. 126 0
      app/assets/stylesheets/blazer/github.css
  27. 386 0
      app/assets/stylesheets/blazer/selectize.default.css
  28. 216 0
      app/controllers/blazer/queries_controller.rb
  29. 21 0
      app/helpers/blazer/queries_helper.rb
  30. 5 0
      app/models/blazer/audit.rb
  31. 5 0
      app/models/blazer/connection.rb
  32. 13 0
      app/models/blazer/query.rb
  33. 84 0
      app/views/blazer/queries/_form.html.erb
  34. 1 0
      app/views/blazer/queries/edit.html.erb
  35. 47 0
      app/views/blazer/queries/index.html.erb
  36. 1 0
      app/views/blazer/queries/new.html.erb
  37. 50 0
      app/views/blazer/queries/run.html.erb
  38. 138 0
      app/views/blazer/queries/show.html.erb
  39. 17 0
      app/views/layouts/blazer/application.html.erb
  40. 25 0
      blazer.gemspec
  41. 6 0
      config/routes.rb
  42. 16 0
      lib/blazer.rb
  43. 11 0
      lib/blazer/engine.rb
  44. 3 0
      lib/blazer/version.rb
  45. 33 0
      lib/generators/blazer/install_generator.rb
  46. 8 0
      lib/generators/blazer/templates/config.yml
  47. 17 0
      lib/generators/blazer/templates/install.rb

+ 14 - 0
.gitignore

@@ -0,0 +1,14 @@
+/.bundle/
+/.yardoc
+/Gemfile.lock
+/_yardoc/
+/coverage/
+/doc/
+/pkg/
+/spec/reports/
+/tmp/
+*.bundle
+*.so
+*.o
+*.a
+mkmf.log

+ 4 - 0
Gemfile

@@ -0,0 +1,4 @@
+source 'https://rubygems.org'
+
+# Specify your gem's dependencies in blazer.gemspec
+gemspec

+ 22 - 0
LICENSE.txt

@@ -0,0 +1,22 @@
+Copyright (c) 2014 Andrew Kane
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 144 - 0
README.md

@@ -0,0 +1,144 @@
+# Blazer
+
+Create and share SQL queries instantly
+
+[View the demo](https://blazerme.herokuapp.com)
+
+Works with PostgreSQL and MySQL
+
+:tangerine: Battle-tested at [Instacart](https://www.instacart.com)
+
+## Features
+
+- **Secure** - works with your authentication system
+- **Variables** - get the same insights for multiple values
+- **Linked Columns** - link to other pages in your apps or around the web
+- **Smart Columns** - get the data your want without all the joins
+- **Smart Variables** - no need to remember IDs
+- **Charts & Maps** - a picture is worth a thousand words
+
+Documentation coming soon.
+
+## Installation
+
+Add this line to your application’s Gemfile:
+
+```ruby
+gem 'blazer'
+```
+
+Run:
+
+```sh
+rails g blazer:install
+rake db:migrate
+```
+
+And mount the dashboard in your `config/routes.rb`:
+
+```ruby
+mount Blazer::Engine, at: "blazer"
+```
+
+Then, specify your database:
+
+```ruby
+ENV["BLAZER_DATABASE_URL"]
+```
+
+It is **highly, highly recommended** to use a read only user.  Keep reading to see how to create one.
+
+## Permissions
+
+### PostgreSQL
+
+Create a user with read only permissions:
+
+```sql
+BEGIN;
+CREATE ROLE blazer LOGIN PASSWORD 'secret123';
+GRANT CONNECT ON DATABASE database_name TO blazer;
+GRANT USAGE ON SCHEMA public TO blazer;
+COMMIT;
+```
+
+It’s highly recommended to protect sensitive information with views.  Documentation coming soon.
+
+### MySQL
+
+Create a user with read only permissions:
+
+```sql
+GRANT SELECT, SHOW VIEW ON database_name.* TO blazer@’127.0.0.1′ IDENTIFIED BY ‘secret123‘;
+FLUSH PRIVILEGES;
+```
+
+It’s highly recommended to protect sensitive information with views.  Documentation coming soon.
+
+## Authentication
+
+Don’t forget to protect the dashboard in production.
+
+### Basic Authentication
+
+Set the following variables in your environment or an initializer.
+
+```ruby
+ENV["BLAZER_USERNAME"] = "andrew"
+ENV["BLAZER_PASSWORD"] = "secret"
+```
+
+### Devise
+
+```ruby
+authenticate :user, lambda{|user| user.admin? } do
+  mount Blazer::Engine, at: "blazer"
+end
+```
+
+## Customization
+
+Change time zone
+
+```ruby
+Blazer.time_zone = "Pacific Time (US & Canada)"
+```
+
+Turn off audits
+
+```ruby
+Blazer.audit = false
+```
+
+## TODO
+
+- better readme
+- better navigation
+- standalone version
+- update lock
+- warn when database user has write permissions
+- advanced permissions
+- favorites
+- support for multiple data sources
+
+## Thanks
+
+Blazer uses a number of awesome, open source projects.
+
+- [Rails](https://github.com/rails/rails/)
+- [jQuery](https://github.com/jquery/jquery)
+- [Bootstrap](https://github.com/twbs/bootstrap)
+- [Selectize](https://github.com/brianreavis/selectize.js)
+- [List.js](https://github.com/javve/list.js)
+- [StickyTableHeaders](https://github.com/jmosbech/StickyTableHeaders)
+- [Stupid jQuery Table Sort](https://github.com/joequery/Stupid-Table-Plugin)
+- [Date Range Picker](https://github.com/dangrossman/bootstrap-daterangepicker)
+
+## Contributing
+
+Everyone is encouraged to help improve this project. Here are a few ways you can help:
+
+- [Report bugs](https://github.com/ankane/blazer/issues)
+- Fix bugs and [submit pull requests](https://github.com/ankane/blazer/pulls)
+- Write, clarify, or fix documentation
+- Suggest or add new features

+ 2 - 0
Rakefile

@@ -0,0 +1,2 @@
+require "bundler/gem_tasks"
+

File diff suppressed because it is too large
+ 0 - 0
app/assets/javascripts/blazer/ace/ace.js


File diff suppressed because it is too large
+ 0 - 0
app/assets/javascripts/blazer/ace/ext-language_tools.js


+ 1 - 0
app/assets/javascripts/blazer/ace/mode-sql.js

@@ -0,0 +1 @@
+ace.define("ace/mode/sql_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="select|insert|update|delete|from|where|and|or|group|by|order|limit|offset|having|as|case|when|else|end|type|left|right|join|on|outer|desc|asc",t="true|false|null",n="count|min|max|avg|sum|rank|now|coalesce",r=this.createKeywordMapper({"support.function":n,keyword:e,"constant.language":t},"identifier",!0);this.$rules={start:[{token:"comment",regex:"--.*$"},{token:"comment",start:"/\\*",end:"\\*/"},{token:"string",regex:'".*?"'},{token:"string",regex:"'.*?'"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:r,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"\\+|\\-|\\/|\\/\\/|%|<@>|@>|<@|&|\\^|~|<|>|<=|=>|==|!=|<>|="},{token:"paren.lparen",regex:"[\\(]"},{token:"paren.rparen",regex:"[\\)]"},{token:"text",regex:"\\s+"}]},this.normalizeRules()};r.inherits(s,i),t.SqlHighlightRules=s}),ace.define("ace/mode/sql",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/sql_highlight_rules","ace/range"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./sql_highlight_rules").SqlHighlightRules,o=e("../range").Range,u=function(){this.HighlightRules=s};r.inherits(u,i),function(){this.lineCommentStart="--",this.$id="ace/mode/sql"}.call(u.prototype),t.Mode=u})

+ 1 - 0
app/assets/javascripts/blazer/ace/snippets/sql.js

@@ -0,0 +1 @@
+ace.define("ace/snippets/sql",["require","exports","module"],function(e,t,n){"use strict";t.snippetText="snippet tbl\n	create table ${1:table} (\n		${2:columns}\n	);\nsnippet col\n	${1:name}	${2:type}	${3:default ''}	${4:not null}\nsnippet ccol\n	${1:name}	varchar2(${2:size})	${3:default ''}	${4:not null}\nsnippet ncol\n	${1:name}	number	${3:default 0}	${4:not null}\nsnippet dcol\n	${1:name}	date	${3:default sysdate}	${4:not null}\nsnippet ind\n	create index ${3:$1_$2} on ${1:table}(${2:column});\nsnippet uind\n	create unique index ${1:name} on ${2:table}(${3:column});\nsnippet tblcom\n	comment on table ${1:table} is '${2:comment}';\nsnippet colcom\n	comment on column ${1:table}.${2:column} is '${3:comment}';\nsnippet addcol\n	alter table ${1:table} add (${2:column} ${3:type});\nsnippet seq\n	create sequence ${1:name} start with ${2:1} increment by ${3:1} minvalue ${4:1};\nsnippet s*\n	select * from ${1:table}\n",t.scope="sql"})

+ 1 - 0
app/assets/javascripts/blazer/ace/snippets/text.js

@@ -0,0 +1 @@
+ace.define("ace/snippets/text",["require","exports","module"],function(e,t,n){"use strict";t.snippetText=undefined,t.scope="text"})

File diff suppressed because it is too large
+ 0 - 0
app/assets/javascripts/blazer/ace/theme-twilight.js


+ 15 - 0
app/assets/javascripts/blazer/application.js

@@ -0,0 +1,15 @@
+//= require ./jquery
+//= require ./jquery_ujs
+//= require ./list
+//= require ./stupidtable
+//= require ./jquery.stickytableheaders
+//= require ./selectize
+//= require ./highlight.pack
+//= require ./moment
+//= require ./daterangepicker
+//= require ./ace/ace
+//= require ./ace/ext-language_tools
+//= require ./ace/theme-twilight
+//= require ./ace/mode-sql
+//= require ./ace/snippets/text
+//= require ./ace/snippets/sql

+ 1026 - 0
app/assets/javascripts/blazer/daterangepicker.js

@@ -0,0 +1,1026 @@
+/**
+* @version: 1.3.7
+* @author: Dan Grossman http://www.dangrossman.info/
+* @date: 2014-04-29
+* @copyright: Copyright (c) 2012-2014 Dan Grossman. All rights reserved.
+* @license: Licensed under Apache License v2.0. See http://www.apache.org/licenses/LICENSE-2.0
+* @website: http://www.improvely.com/
+*/
+!function ($, moment) {
+
+    var DateRangePicker = function (element, options, cb) {
+
+        // by default, the daterangepicker element is placed at the bottom of HTML body
+        this.parentEl = 'body';
+
+        //element that triggered the date range picker
+        this.element = $(element);
+
+        //create the picker HTML object
+        var DRPTemplate = '<div class="daterangepicker dropdown-menu">' +
+                '<div class="calendar left"></div>' +
+                '<div class="calendar right"></div>' +
+                '<div class="ranges">' +
+                  '<div class="range_inputs">' +
+                    '<div class="daterangepicker_start_input">' +
+                      '<label for="daterangepicker_start"></label>' +
+                      '<input class="input-mini" type="text" name="daterangepicker_start" value="" disabled="disabled" />' +
+                    '</div>' +
+                    '<div class="daterangepicker_end_input">' +
+                      '<label for="daterangepicker_end"></label>' +
+                      '<input class="input-mini" type="text" name="daterangepicker_end" value="" disabled="disabled" />' +
+                    '</div>' +
+                    '<button class="applyBtn" disabled="disabled"></button>&nbsp;' +
+                    '<button class="cancelBtn"></button>' +
+                  '</div>' +
+                '</div>' +
+              '</div>';
+
+        //custom options
+        if (typeof options !== 'object' || options === null)
+            options = {};
+
+        this.parentEl = (typeof options === 'object' && options.parentEl && $(options.parentEl).length) ? $(options.parentEl) : $(this.parentEl);
+        this.container = $(DRPTemplate).appendTo(this.parentEl);
+
+        this.setOptions(options, cb);
+
+        //apply CSS classes and labels to buttons
+        var c = this.container;
+        $.each(this.buttonClasses, function (idx, val) {
+            c.find('button').addClass(val);
+        });
+        this.container.find('.daterangepicker_start_input label').html(this.locale.fromLabel);
+        this.container.find('.daterangepicker_end_input label').html(this.locale.toLabel);
+        if (this.applyClass.length)
+            this.container.find('.applyBtn').addClass(this.applyClass);
+        if (this.cancelClass.length)
+            this.container.find('.cancelBtn').addClass(this.cancelClass);
+        this.container.find('.applyBtn').html(this.locale.applyLabel);
+        this.container.find('.cancelBtn').html(this.locale.cancelLabel);
+
+        //event listeners
+
+        this.container.find('.calendar')
+            .on('click.daterangepicker', '.prev', $.proxy(this.clickPrev, this))
+            .on('click.daterangepicker', '.next', $.proxy(this.clickNext, this))
+            .on('click.daterangepicker', 'td.available', $.proxy(this.clickDate, this))
+            .on('mouseenter.daterangepicker', 'td.available', $.proxy(this.enterDate, this))
+            .on('mouseleave.daterangepicker', 'td.available', $.proxy(this.updateFormInputs, this))
+            .on('change.daterangepicker', 'select.yearselect', $.proxy(this.updateMonthYear, this))
+            .on('change.daterangepicker', 'select.monthselect', $.proxy(this.updateMonthYear, this))
+            .on('change.daterangepicker', 'select.hourselect,select.minuteselect,select.ampmselect', $.proxy(this.updateTime, this));
+
+        this.container.find('.ranges')
+            .on('click.daterangepicker', 'button.applyBtn', $.proxy(this.clickApply, this))
+            .on('click.daterangepicker', 'button.cancelBtn', $.proxy(this.clickCancel, this))
+            .on('click.daterangepicker', '.daterangepicker_start_input,.daterangepicker_end_input', $.proxy(this.showCalendars, this))
+            .on('click.daterangepicker', 'li', $.proxy(this.clickRange, this))
+            .on('mouseenter.daterangepicker', 'li', $.proxy(this.enterRange, this))
+            .on('mouseleave.daterangepicker', 'li', $.proxy(this.updateFormInputs, this));
+
+        if (this.element.is('input')) {
+            this.element.on({
+                'click.daterangepicker': $.proxy(this.show, this),
+                'focus.daterangepicker': $.proxy(this.show, this),
+                'keyup.daterangepicker': $.proxy(this.updateFromControl, this)
+            });
+        } else {
+            this.element.on('click.daterangepicker', $.proxy(this.toggle, this));
+        }
+
+    };
+
+    DateRangePicker.prototype = {
+
+        constructor: DateRangePicker,
+
+        setOptions: function(options, callback) {
+
+            this.startDate = moment().startOf('day');
+            this.endDate = moment().endOf('day');
+            this.minDate = false;
+            this.maxDate = false;
+            this.dateLimit = false;
+
+            this.showDropdowns = false;
+            this.showWeekNumbers = false;
+            this.timePicker = false;
+            this.timePickerIncrement = 30;
+            this.timePicker12Hour = true;
+            this.singleDatePicker = false;
+            this.ranges = {};
+
+            this.opens = 'right';
+            if (this.element.hasClass('pull-right'))
+                this.opens = 'left';
+
+            this.buttonClasses = ['btn', 'btn-small'];
+            this.applyClass = 'btn-success';
+            this.cancelClass = 'btn-default';
+
+            this.format = 'MM/DD/YYYY';
+            this.separator = ' - ';
+
+            this.locale = {
+                applyLabel: 'Apply',
+                cancelLabel: 'Cancel',
+                fromLabel: 'From',
+                toLabel: 'To',
+                weekLabel: 'W',
+                customRangeLabel: 'Custom Range',
+                daysOfWeek: moment()._lang._weekdaysMin.slice(),
+                monthNames: moment()._lang._monthsShort.slice(),
+                firstDay: 0
+            };
+
+            this.cb = function () { };
+
+            if (typeof options.format === 'string')
+                this.format = options.format;
+
+            if (typeof options.separator === 'string')
+                this.separator = options.separator;
+
+            if (typeof options.startDate === 'string')
+                this.startDate = moment(options.startDate, this.format);
+
+            if (typeof options.endDate === 'string')
+                this.endDate = moment(options.endDate, this.format);
+
+            if (typeof options.minDate === 'string')
+                this.minDate = moment(options.minDate, this.format);
+
+            if (typeof options.maxDate === 'string')
+                this.maxDate = moment(options.maxDate, this.format);
+
+            if (typeof options.startDate === 'object')
+                this.startDate = moment(options.startDate);
+
+            if (typeof options.endDate === 'object')
+                this.endDate = moment(options.endDate);
+
+            if (typeof options.minDate === 'object')
+                this.minDate = moment(options.minDate);
+
+            if (typeof options.maxDate === 'object')
+                this.maxDate = moment(options.maxDate);
+
+            if (typeof options.applyClass === 'string')
+                this.applyClass = options.applyClass;
+
+            if (typeof options.cancelClass === 'string')
+                this.cancelClass = options.cancelClass;
+
+            if (typeof options.dateLimit === 'object')
+                this.dateLimit = options.dateLimit;
+
+            // update day names order to firstDay
+            if (typeof options.locale === 'object') {
+
+                if (typeof options.locale.daysOfWeek === 'object') {
+                    // Create a copy of daysOfWeek to avoid modification of original
+                    // options object for reusability in multiple daterangepicker instances
+                    this.locale.daysOfWeek = options.locale.daysOfWeek.slice();
+                }
+
+                if (typeof options.locale.monthNames === 'object') {
+                  this.locale.monthNames = options.locale.monthNames.slice();
+                }
+
+                if (typeof options.locale.firstDay === 'number') {
+                    this.locale.firstDay = options.locale.firstDay;
+                    var iterator = options.locale.firstDay;
+                    while (iterator > 0) {
+                        this.locale.daysOfWeek.push(this.locale.daysOfWeek.shift());
+                        iterator--;
+                    }
+                }
+
+                if (typeof options.locale.applyLabel === 'string') {
+                  this.locale.applyLabel = options.locale.applyLabel;
+                }
+
+                if (typeof options.locale.cancelLabel === 'string') {
+                  this.locale.cancelLabel = options.locale.cancelLabel;
+                }
+
+                if (typeof options.locale.fromLabel === 'string') {
+                  this.locale.fromLabel = options.locale.fromLabel;
+                }
+
+                if (typeof options.locale.toLabel === 'string') {
+                  this.locale.toLabel = options.locale.toLabel;
+                }
+
+                if (typeof options.locale.weekLabel === 'string') {
+                  this.locale.weekLabel = options.locale.weekLabel;
+                }
+
+                if (typeof options.locale.customRangeLabel === 'string') {
+                  this.locale.customRangeLabel = options.locale.customRangeLabel;
+                }
+            }
+
+            if (typeof options.opens === 'string')
+                this.opens = options.opens;
+
+            if (typeof options.showWeekNumbers === 'boolean') {
+                this.showWeekNumbers = options.showWeekNumbers;
+            }
+
+            if (typeof options.buttonClasses === 'string') {
+                this.buttonClasses = [options.buttonClasses];
+            }
+
+            if (typeof options.buttonClasses === 'object') {
+                this.buttonClasses = options.buttonClasses;
+            }
+
+            if (typeof options.showDropdowns === 'boolean') {
+                this.showDropdowns = options.showDropdowns;
+            }
+
+            if (typeof options.singleDatePicker === 'boolean') {
+                this.singleDatePicker = options.singleDatePicker;
+            }
+
+            if (typeof options.timePicker === 'boolean') {
+                this.timePicker = options.timePicker;
+            }
+
+            if (typeof options.timePickerIncrement === 'number') {
+                this.timePickerIncrement = options.timePickerIncrement;
+            }
+
+            if (typeof options.timePicker12Hour === 'boolean') {
+                this.timePicker12Hour = options.timePicker12Hour;
+            }
+
+            var start, end, range;
+
+            //if no start/end dates set, check if an input element contains initial values
+            if (typeof options.startDate === 'undefined' && typeof options.endDate === 'undefined') {
+                if ($(this.element).is('input[type=text]')) {
+                    var val = $(this.element).val();
+                    var split = val.split(this.separator);
+                    start = end = null;
+                    if (split.length == 2) {
+                        start = moment(split[0], this.format);
+                        end = moment(split[1], this.format);
+                    } else if (this.singleDatePicker) {
+                        start = moment(val, this.format);
+                        end = moment(val, this.format);
+                    }
+                    if (start !== null && end !== null) {
+                        this.startDate = start;
+                        this.endDate = end;
+                    }
+                }
+            }
+
+            if (typeof options.ranges === 'object') {
+                for (range in options.ranges) {
+
+                    start = moment(options.ranges[range][0]);
+                    end = moment(options.ranges[range][1]);
+
+                    // If we have a min/max date set, bound this range
+                    // to it, but only if it would otherwise fall
+                    // outside of the min/max.
+                    if (this.minDate && start.isBefore(this.minDate))
+                        start = moment(this.minDate);
+
+                    if (this.maxDate && end.isAfter(this.maxDate))
+                        end = moment(this.maxDate);
+
+                    // If the end of the range is before the minimum (if min is set) OR
+                    // the start of the range is after the max (also if set) don't display this
+                    // range option.
+                    if ((this.minDate && end.isBefore(this.minDate)) || (this.maxDate && start.isAfter(this.maxDate))) {
+                        continue;
+                    }
+
+                    this.ranges[range] = [start, end];
+                }
+
+                var list = '<ul>';
+                for (range in this.ranges) {
+                    list += '<li>' + range + '</li>';
+                }
+                list += '<li>' + this.locale.customRangeLabel + '</li>';
+                list += '</ul>';
+                this.container.find('.ranges ul').remove();
+                this.container.find('.ranges').prepend(list);
+            }
+
+            if (typeof callback === 'function') {
+                this.cb = callback;
+            }
+
+            if (!this.timePicker) {
+                this.startDate = this.startDate.startOf('day');
+                this.endDate = this.endDate.endOf('day');
+            }
+
+            if (this.singleDatePicker) {
+                this.opens = 'right';
+                this.container.find('.calendar.right').show();
+                this.container.find('.calendar.left').hide();
+                this.container.find('.ranges').hide();
+                if (!this.container.find('.calendar.right').hasClass('single'))
+                    this.container.find('.calendar.right').addClass('single');
+            } else {
+                this.container.find('.calendar.right').removeClass('single');
+                this.container.find('.ranges').show();
+            }
+
+            this.oldStartDate = this.startDate.clone();
+            this.oldEndDate = this.endDate.clone();
+            this.oldChosenLabel = this.chosenLabel;
+
+            this.leftCalendar = {
+                month: moment([this.startDate.year(), this.startDate.month(), 1, this.startDate.hour(), this.startDate.minute()]),
+                calendar: []
+            };
+
+            this.rightCalendar = {
+                month: moment([this.endDate.year(), this.endDate.month(), 1, this.endDate.hour(), this.endDate.minute()]),
+                calendar: []
+            };
+
+            if (this.opens == 'right') {
+                //swap calendar positions
+                var left = this.container.find('.calendar.left');
+                var right = this.container.find('.calendar.right');
+                left.removeClass('left').addClass('right');
+                right.removeClass('right').addClass('left');
+            }
+
+            if (typeof options.ranges === 'undefined' && !this.singleDatePicker) {
+                this.container.addClass('show-calendar');
+            }
+
+            this.container.addClass('opens' + this.opens);
+
+            this.updateView();
+            this.updateCalendars();
+
+        },
+
+        setStartDate: function(startDate) {
+            if (typeof startDate === 'string')
+                this.startDate = moment(startDate, this.format);
+
+            if (typeof startDate === 'object')
+                this.startDate = moment(startDate);
+
+            if (!this.timePicker)
+                this.startDate = this.startDate.startOf('day');
+
+            this.oldStartDate = this.startDate.clone();
+
+            this.updateView();
+            this.updateCalendars();
+        },
+
+        setEndDate: function(endDate) {
+            if (typeof endDate === 'string')
+                this.endDate = moment(endDate, this.format);
+
+            if (typeof endDate === 'object')
+                this.endDate = moment(endDate);
+
+            if (!this.timePicker)
+                this.endDate = this.endDate.endOf('day');
+
+            this.oldEndDate = this.endDate.clone();
+
+            this.updateView();
+            this.updateCalendars();
+        },
+
+        updateView: function () {
+            this.leftCalendar.month.month(this.startDate.month()).year(this.startDate.year());
+            this.rightCalendar.month.month(this.endDate.month()).year(this.endDate.year());
+            this.updateFormInputs();
+        },
+
+        updateFormInputs: function () {
+            this.container.find('input[name=daterangepicker_start]').val(this.startDate.format(this.format));
+            this.container.find('input[name=daterangepicker_end]').val(this.endDate.format(this.format));
+
+            if (this.startDate.isSame(this.endDate) || this.startDate.isBefore(this.endDate)) {
+                this.container.find('button.applyBtn').removeAttr('disabled');
+            } else {
+                this.container.find('button.applyBtn').attr('disabled', 'disabled');
+            }
+        },
+
+        updateFromControl: function () {
+            if (!this.element.is('input')) return;
+            if (!this.element.val().length) return;
+
+            var dateString = this.element.val().split(this.separator),
+                start = null,
+                end = null;
+
+            if(dateString.length === 2) {
+                start = moment(dateString[0], this.format);
+                end = moment(dateString[1], this.format);
+            }
+
+            if (this.singleDatePicker || start === null || end === null) {
+                start = moment(this.element.val(), this.format);
+                end = start;
+            }
+
+            if (end.isBefore(start)) return;
+
+            this.oldStartDate = this.startDate.clone();
+            this.oldEndDate = this.endDate.clone();
+
+            this.startDate = start;
+            this.endDate = end;
+
+            if (!this.startDate.isSame(this.oldStartDate) || !this.endDate.isSame(this.oldEndDate))
+                this.notify();
+
+            this.updateCalendars();
+        },
+
+        notify: function () {
+            this.updateView();
+            this.cb(this.startDate, this.endDate, this.chosenLabel);
+        },
+
+        move: function () {
+            var parentOffset = { top: 0, left: 0 };
+            if (!this.parentEl.is('body')) {
+                parentOffset = {
+                    top: this.parentEl.offset().top - this.parentEl.scrollTop(),
+                    left: this.parentEl.offset().left - this.parentEl.scrollLeft()
+                };
+            }
+
+            if (this.opens == 'left') {
+                this.container.css({
+                    top: this.element.offset().top + this.element.outerHeight() - parentOffset.top,
+                    right: $(window).width() - this.element.offset().left - this.element.outerWidth() - parentOffset.left,
+                    left: 'auto'
+                });
+                if (this.container.offset().left < 0) {
+                    this.container.css({
+                        right: 'auto',
+                        left: 9
+                    });
+                }
+            } else {
+                this.container.css({
+                    top: this.element.offset().top + this.element.outerHeight() - parentOffset.top,
+                    left: this.element.offset().left - parentOffset.left,
+                    right: 'auto'
+                });
+                if (this.container.offset().left + this.container.outerWidth() > $(window).width()) {
+                    this.container.css({
+                        left: 'auto',
+                        right: 0
+                    });
+                }
+            }
+        },
+
+        toggle: function (e) {
+            if (this.element.hasClass('active')) {
+                this.hide();
+            } else {
+                this.show();
+            }
+        },
+
+        show: function (e) {
+            this.element.addClass('active');
+            this.container.show();
+            this.move();
+
+            // Create a click proxy that is private to this instance of datepicker, for unbinding
+            this._outsideClickProxy = $.proxy(function (e) { this.outsideClick(e); }, this);
+            // Bind global datepicker mousedown for hiding and
+            $(document)
+              .on('mousedown.daterangepicker', this._outsideClickProxy)
+              // also explicitly play nice with Bootstrap dropdowns, which stopPropagation when clicking them
+              .on('click.daterangepicker', '[data-toggle=dropdown]', this._outsideClickProxy)
+              // and also close when focus changes to outside the picker (eg. tabbing between controls)
+              .on('focusin.daterangepicker', this._outsideClickProxy);
+
+            this.element.trigger('show.daterangepicker', this);
+        },
+
+        outsideClick: function (e) {
+            var target = $(e.target);
+            // if the page is clicked anywhere except within the daterangerpicker/button
+            // itself then call this.hide()
+            if (
+                target.closest(this.element).length ||
+                target.closest(this.container).length ||
+                target.closest('.calendar-date').length
+                ) return;
+            this.hide();
+        },
+
+        hide: function (e) {
+            $(document)
+              .off('mousedown.daterangepicker', this._outsideClickProxy)
+              .off('click.daterangepicker', this._outsideClickProxy)
+              .off('focusin.daterangepicker', this._outsideClickProxy);
+
+            this.element.removeClass('active');
+            this.container.hide();
+
+            if (!this.startDate.isSame(this.oldStartDate) || !this.endDate.isSame(this.oldEndDate))
+                this.notify();
+
+            this.oldStartDate = this.startDate.clone();
+            this.oldEndDate = this.endDate.clone();
+
+            this.element.trigger('hide.daterangepicker', this);
+        },
+
+        enterRange: function (e) {
+            // mouse pointer has entered a range label
+            var label = e.target.innerHTML;
+            if (label == this.locale.customRangeLabel) {
+                this.updateView();
+            } else {
+                var dates = this.ranges[label];
+                this.container.find('input[name=daterangepicker_start]').val(dates[0].format(this.format));
+                this.container.find('input[name=daterangepicker_end]').val(dates[1].format(this.format));
+            }
+        },
+
+        showCalendars: function() {
+            this.container.addClass('show-calendar');
+            this.move();
+        },
+
+        hideCalendars: function() {
+            this.container.removeClass('show-calendar');
+        },
+
+        updateInputText: function() {
+            if (this.element.is('input') && !this.singleDatePicker) {
+                this.element.val(this.startDate.format(this.format) + this.separator + this.endDate.format(this.format));
+            } else if (this.element.is('input')) {
+                this.element.val(this.startDate.format(this.format));
+            }
+        },
+
+        clickRange: function (e) {
+            var label = e.target.innerHTML;
+            this.chosenLabel = label;
+            if (label == this.locale.customRangeLabel) {
+                this.showCalendars();
+            } else {
+                var dates = this.ranges[label];
+
+                this.startDate = dates[0];
+                this.endDate = dates[1];
+
+                if (!this.timePicker) {
+                    this.startDate.startOf('day');
+                    this.endDate.endOf('day');
+                }
+
+                this.leftCalendar.month.month(this.startDate.month()).year(this.startDate.year()).hour(this.startDate.hour()).minute(this.startDate.minute());
+                this.rightCalendar.month.month(this.endDate.month()).year(this.endDate.year()).hour(this.endDate.hour()).minute(this.endDate.minute());
+                this.updateCalendars();
+
+                this.updateInputText();
+
+                this.hideCalendars();
+                this.hide();
+                this.element.trigger('apply.daterangepicker', this);
+            }
+        },
+
+        clickPrev: function (e) {
+            var cal = $(e.target).parents('.calendar');
+            if (cal.hasClass('left')) {
+                this.leftCalendar.month.subtract('month', 1);
+            } else {
+                this.rightCalendar.month.subtract('month', 1);
+            }
+            this.updateCalendars();
+        },
+
+        clickNext: function (e) {
+            var cal = $(e.target).parents('.calendar');
+            if (cal.hasClass('left')) {
+                this.leftCalendar.month.add('month', 1);
+            } else {
+                this.rightCalendar.month.add('month', 1);
+            }
+            this.updateCalendars();
+        },
+
+        enterDate: function (e) {
+
+            var title = $(e.target).attr('data-title');
+            var row = title.substr(1, 1);
+            var col = title.substr(3, 1);
+            var cal = $(e.target).parents('.calendar');
+
+            if (cal.hasClass('left')) {
+                this.container.find('input[name=daterangepicker_start]').val(this.leftCalendar.calendar[row][col].format(this.format));
+            } else {
+                this.container.find('input[name=daterangepicker_end]').val(this.rightCalendar.calendar[row][col].format(this.format));
+            }
+
+        },
+
+        clickDate: function (e) {
+            var title = $(e.target).attr('data-title');
+            var row = title.substr(1, 1);
+            var col = title.substr(3, 1);
+            var cal = $(e.target).parents('.calendar');
+
+            var startDate, endDate;
+            if (cal.hasClass('left')) {
+                startDate = this.leftCalendar.calendar[row][col];
+                endDate = this.endDate;
+                if (typeof this.dateLimit === 'object') {
+                    var maxDate = moment(startDate).add(this.dateLimit).startOf('day');
+                    if (endDate.isAfter(maxDate)) {
+                        endDate = maxDate;
+                    }
+                }
+            } else {
+                startDate = this.startDate;
+                endDate = this.rightCalendar.calendar[row][col];
+                if (typeof this.dateLimit === 'object') {
+                    var minDate = moment(endDate).subtract(this.dateLimit).startOf('day');
+                    if (startDate.isBefore(minDate)) {
+                        startDate = minDate;
+                    }
+                }
+            }
+
+            if (this.singleDatePicker && cal.hasClass('left')) {
+                endDate = startDate.clone();
+            } else if (this.singleDatePicker && cal.hasClass('right')) {
+                startDate = endDate.clone();
+            }
+
+            cal.find('td').removeClass('active');
+
+            if (startDate.isSame(endDate) || startDate.isBefore(endDate)) {
+                $(e.target).addClass('active');
+                this.startDate = startDate;
+                this.endDate = endDate;
+                this.chosenLabel = this.locale.customRangeLabel;
+            } else if (startDate.isAfter(endDate)) {
+                $(e.target).addClass('active');
+                var difference = this.endDate.diff(this.startDate);
+                this.startDate = startDate;
+                this.endDate = moment(startDate).add('ms', difference);
+                this.chosenLabel = this.locale.customRangeLabel;
+            }
+
+            this.leftCalendar.month.month(this.startDate.month()).year(this.startDate.year());
+            this.rightCalendar.month.month(this.endDate.month()).year(this.endDate.year());
+            this.updateCalendars();
+
+            if (!this.timePicker)
+                endDate.endOf('day');
+
+            if (this.singleDatePicker)
+                this.clickApply();
+        },
+
+        clickApply: function (e) {
+            this.updateInputText();
+            this.hide();
+            this.element.trigger('apply.daterangepicker', this);
+        },
+
+        clickCancel: function (e) {
+            this.startDate = this.oldStartDate;
+            this.endDate = this.oldEndDate;
+            this.chosenLabel = this.oldChosenLabel;
+            this.updateView();
+            this.updateCalendars();
+            this.hide();
+            this.element.trigger('cancel.daterangepicker', this);
+        },
+
+        updateMonthYear: function (e) {
+            var isLeft = $(e.target).closest('.calendar').hasClass('left'),
+                leftOrRight = isLeft ? 'left' : 'right',
+                cal = this.container.find('.calendar.'+leftOrRight);
+
+            // Month must be Number for new moment versions
+            var month = parseInt(cal.find('.monthselect').val(), 10);
+            var year = cal.find('.yearselect').val();
+
+            this[leftOrRight+'Calendar'].month.month(month).year(year);
+            this.updateCalendars();
+        },
+
+        updateTime: function(e) {
+
+            var cal = $(e.target).closest('.calendar'),
+                isLeft = cal.hasClass('left');
+
+            var hour = parseInt(cal.find('.hourselect').val(), 10);
+            var minute = parseInt(cal.find('.minuteselect').val(), 10);
+
+            if (this.timePicker12Hour) {
+                var ampm = cal.find('.ampmselect').val();
+                if (ampm === 'PM' && hour < 12)
+                    hour += 12;
+                if (ampm === 'AM' && hour === 12)
+                    hour = 0;
+            }
+
+            if (isLeft) {
+                var start = this.startDate.clone();
+                start.hour(hour);
+                start.minute(minute);
+                this.startDate = start;
+                this.leftCalendar.month.hour(hour).minute(minute);
+            } else {
+                var end = this.endDate.clone();
+                end.hour(hour);
+                end.minute(minute);
+                this.endDate = end;
+                this.rightCalendar.month.hour(hour).minute(minute);
+            }
+
+            this.updateCalendars();
+        },
+
+        updateCalendars: function () {
+            this.leftCalendar.calendar = this.buildCalendar(this.leftCalendar.month.month(), this.leftCalendar.month.year(), this.leftCalendar.month.hour(), this.leftCalendar.month.minute(), 'left');
+            this.rightCalendar.calendar = this.buildCalendar(this.rightCalendar.month.month(), this.rightCalendar.month.year(), this.rightCalendar.month.hour(), this.rightCalendar.month.minute(), 'right');
+            this.container.find('.calendar.left').empty().html(this.renderCalendar(this.leftCalendar.calendar, this.startDate, this.minDate, this.maxDate));
+            this.container.find('.calendar.right').empty().html(this.renderCalendar(this.rightCalendar.calendar, this.endDate, this.startDate, this.maxDate));
+
+            this.container.find('.ranges li').removeClass('active');
+            var customRange = true;
+            var i = 0;
+            for (var range in this.ranges) {
+                if (this.timePicker) {
+                    if (this.startDate.isSame(this.ranges[range][0]) && this.endDate.isSame(this.ranges[range][1])) {
+                        customRange = false;
+                        this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')')
+                            .addClass('active').html();
+                    }
+                } else {
+                    //ignore times when comparing dates if time picker is not enabled
+                    if (this.startDate.format('YYYY-MM-DD') == this.ranges[range][0].format('YYYY-MM-DD') && this.endDate.format('YYYY-MM-DD') == this.ranges[range][1].format('YYYY-MM-DD')) {
+                        customRange = false;
+                        this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')')
+                            .addClass('active').html();
+                    }
+                }
+                i++;
+            }
+            if (customRange) {
+                this.chosenLabel = this.container.find('.ranges li:last')
+                    .addClass('active').html();
+            }
+        },
+
+        buildCalendar: function (month, year, hour, minute, side) {
+            var firstDay = moment([year, month, 1]);
+            var lastMonth = moment(firstDay).subtract('month', 1).month();
+            var lastYear = moment(firstDay).subtract('month', 1).year();
+
+            var daysInLastMonth = moment([lastYear, lastMonth]).daysInMonth();
+
+            var dayOfWeek = firstDay.day();
+
+            var i;
+
+            //initialize a 6 rows x 7 columns array for the calendar
+            var calendar = [];
+            for (i = 0; i < 6; i++) {
+                calendar[i] = [];
+            }
+
+            //populate the calendar with date objects
+            var startDay = daysInLastMonth - dayOfWeek + this.locale.firstDay + 1;
+            if (startDay > daysInLastMonth)
+                startDay -= 7;
+
+            if (dayOfWeek == this.locale.firstDay)
+                startDay = daysInLastMonth - 6;
+
+            var curDate = moment([lastYear, lastMonth, startDay, 12, minute]);
+            var col, row;
+            for (i = 0, col = 0, row = 0; i < 42; i++, col++, curDate = moment(curDate).add('hour', 24)) {
+                if (i > 0 && col % 7 === 0) {
+                    col = 0;
+                    row++;
+                }
+                calendar[row][col] = curDate.clone().hour(hour);
+                curDate.hour(12);
+            }
+
+            return calendar;
+        },
+
+        renderDropdowns: function (selected, minDate, maxDate) {
+            var currentMonth = selected.month();
+            var monthHtml = '<select class="monthselect">';
+            var inMinYear = false;
+            var inMaxYear = false;
+
+            for (var m = 0; m < 12; m++) {
+                if ((!inMinYear || m >= minDate.month()) && (!inMaxYear || m <= maxDate.month())) {
+                    monthHtml += "<option value='" + m + "'" +
+                        (m === currentMonth ? " selected='selected'" : "") +
+                        ">" + this.locale.monthNames[m] + "</option>";
+                }
+            }
+            monthHtml += "</select>";
+
+            var currentYear = selected.year();
+            var maxYear = (maxDate && maxDate.year()) || (currentYear + 5);
+            var minYear = (minDate && minDate.year()) || (currentYear - 50);
+            var yearHtml = '<select class="yearselect">';
+
+            for (var y = minYear; y <= maxYear; y++) {
+                yearHtml += '<option value="' + y + '"' +
+                    (y === currentYear ? ' selected="selected"' : '') +
+                    '>' + y + '</option>';
+            }
+
+            yearHtml += '</select>';
+
+            return monthHtml + yearHtml;
+        },
+
+        renderCalendar: function (calendar, selected, minDate, maxDate) {
+
+            var html = '<div class="calendar-date">';
+            html += '<table class="table-condensed">';
+            html += '<thead>';
+            html += '<tr>';
+
+            // add empty cell for week number
+            if (this.showWeekNumbers)
+                html += '<th></th>';
+
+            if (!minDate || minDate.isBefore(calendar[1][1])) {
+                html += '<th class="prev available"><i class="fa fa-arrow-left icon-arrow-left glyphicon glyphicon-arrow-left"></i></th>';
+            } else {
+                html += '<th></th>';
+            }
+
+            var dateHtml = this.locale.monthNames[calendar[1][1].month()] + calendar[1][1].format(" YYYY");
+
+            if (this.showDropdowns) {
+                dateHtml = this.renderDropdowns(calendar[1][1], minDate, maxDate);
+            }
+
+            html += '<th colspan="5" class="month">' + dateHtml + '</th>';
+            if (!maxDate || maxDate.isAfter(calendar[1][1])) {
+                html += '<th class="next available"><i class="fa fa-arrow-right icon-arrow-right glyphicon glyphicon-arrow-right"></i></th>';
+            } else {
+                html += '<th></th>';
+            }
+
+            html += '</tr>';
+            html += '<tr>';
+
+            // add week number label
+            if (this.showWeekNumbers)
+                html += '<th class="week">' + this.locale.weekLabel + '</th>';
+
+            $.each(this.locale.daysOfWeek, function (index, dayOfWeek) {
+                html += '<th>' + dayOfWeek + '</th>';
+            });
+
+            html += '</tr>';
+            html += '</thead>';
+            html += '<tbody>';
+
+            for (var row = 0; row < 6; row++) {
+                html += '<tr>';
+
+                // add week number
+                if (this.showWeekNumbers)
+                    html += '<td class="week">' + calendar[row][0].week() + '</td>';
+
+                for (var col = 0; col < 7; col++) {
+                    var cname = 'available ';
+                    cname += (calendar[row][col].month() == calendar[1][1].month()) ? '' : 'off';
+
+                    if ((minDate && calendar[row][col].isBefore(minDate, 'day')) || (maxDate && calendar[row][col].isAfter(maxDate, 'day'))) {
+                        cname = ' off disabled ';
+                    } else if (calendar[row][col].format('YYYY-MM-DD') == selected.format('YYYY-MM-DD')) {
+                        cname += ' active ';
+                        if (calendar[row][col].format('YYYY-MM-DD') == this.startDate.format('YYYY-MM-DD')) {
+                            cname += ' start-date ';
+                        }
+                        if (calendar[row][col].format('YYYY-MM-DD') == this.endDate.format('YYYY-MM-DD')) {
+                            cname += ' end-date ';
+                        }
+                    } else if (calendar[row][col] >= this.startDate && calendar[row][col] <= this.endDate) {
+                        cname += ' in-range ';
+                        if (calendar[row][col].isSame(this.startDate)) { cname += ' start-date '; }
+                        if (calendar[row][col].isSame(this.endDate)) { cname += ' end-date '; }
+                    }
+
+                    var title = 'r' + row + 'c' + col;
+                    html += '<td class="' + cname.replace(/\s+/g, ' ').replace(/^\s?(.*?)\s?$/, '$1') + '" data-title="' + title + '">' + calendar[row][col].date() + '</td>';
+                }
+                html += '</tr>';
+            }
+
+            html += '</tbody>';
+            html += '</table>';
+            html += '</div>';
+
+            var i;
+            if (this.timePicker) {
+
+                html += '<div class="calendar-time">';
+                html += '<select class="hourselect">';
+                var start = 0;
+                var end = 23;
+                var selected_hour = selected.hour();
+                if (this.timePicker12Hour) {
+                    start = 1;
+                    end = 12;
+                    if (selected_hour >= 12)
+                        selected_hour -= 12;
+                    if (selected_hour === 0)
+                        selected_hour = 12;
+                }
+
+                for (i = start; i <= end; i++) {
+                    if (i == selected_hour) {
+                        html += '<option value="' + i + '" selected="selected">' + i + '</option>';
+                    } else {
+                        html += '<option value="' + i + '">' + i + '</option>';
+                    }
+                }
+
+                html += '</select> : ';
+
+                html += '<select class="minuteselect">';
+
+                for (i = 0; i < 60; i += this.timePickerIncrement) {
+                    var num = i;
+                    if (num < 10)
+                        num = '0' + num;
+                    if (i == selected.minute()) {
+                        html += '<option value="' + i + '" selected="selected">' + num + '</option>';
+                    } else {
+                        html += '<option value="' + i + '">' + num + '</option>';
+                    }
+                }
+
+                html += '</select> ';
+
+                if (this.timePicker12Hour) {
+                    html += '<select class="ampmselect">';
+                    if (selected.hour() >= 12) {
+                        html += '<option value="AM">AM</option><option value="PM" selected="selected">PM</option>';
+                    } else {
+                        html += '<option value="AM" selected="selected">AM</option><option value="PM">PM</option>';
+                    }
+                    html += '</select>';
+                }
+
+                html += '</div>';
+
+            }
+
+            return html;
+
+        },
+
+        remove: function() {
+
+            this.container.remove();
+            this.element.off('.daterangepicker');
+            this.element.removeData('daterangepicker');
+
+        }
+
+    };
+
+    $.fn.daterangepicker = function (options, cb) {
+        this.each(function () {
+            var el = $(this);
+            if (el.data('daterangepicker'))
+                el.data('daterangepicker').remove();
+            el.data('daterangepicker', new DateRangePicker(el, options, cb));
+        });
+        return this;
+    };
+
+}(window.jQuery, window.moment);

File diff suppressed because it is too large
+ 0 - 0
app/assets/javascripts/blazer/highlight.pack.js


+ 10308 - 0
app/assets/javascripts/blazer/jquery.js

@@ -0,0 +1,10308 @@
+/*!
+ * jQuery JavaScript Library v1.11.1
+ * http://jquery.com/
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ *
+ * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2014-05-01T17:42Z
+ */
+
+(function( global, factory ) {
+
+  if ( typeof module === "object" && typeof module.exports === "object" ) {
+    // For CommonJS and CommonJS-like environments where a proper window is present,
+    // execute the factory and get jQuery
+    // For environments that do not inherently posses a window with a document
+    // (such as Node.js), expose a jQuery-making factory as module.exports
+    // This accentuates the need for the creation of a real window
+    // e.g. var jQuery = require("jquery")(window);
+    // See ticket #14549 for more info
+    module.exports = global.document ?
+      factory( global, true ) :
+      function( w ) {
+        if ( !w.document ) {
+          throw new Error( "jQuery requires a window with a document" );
+        }
+        return factory( w );
+      };
+  } else {
+    factory( global );
+  }
+
+// Pass this if window is not defined yet
+}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
+
+// Can't do this because several apps including ASP.NET trace
+// the stack via arguments.caller.callee and Firefox dies if
+// you try to trace through "use strict" call chains. (#13335)
+// Support: Firefox 18+
+//
+
+var deletedIds = [];
+
+var slice = deletedIds.slice;
+
+var concat = deletedIds.concat;
+
+var push = deletedIds.push;
+
+var indexOf = deletedIds.indexOf;
+
+var class2type = {};
+
+var toString = class2type.toString;
+
+var hasOwn = class2type.hasOwnProperty;
+
+var support = {};
+
+
+
+var
+  version = "1.11.1",
+
+  // Define a local copy of jQuery
+  jQuery = function( selector, context ) {
+    // The jQuery object is actually just the init constructor 'enhanced'
+    // Need init if jQuery is called (just allow error to be thrown if not included)
+    return new jQuery.fn.init( selector, context );
+  },
+
+  // Support: Android<4.1, IE<9
+  // Make sure we trim BOM and NBSP
+  rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
+
+  // Matches dashed string for camelizing
+  rmsPrefix = /^-ms-/,
+  rdashAlpha = /-([\da-z])/gi,
+
+  // Used by jQuery.camelCase as callback to replace()
+  fcamelCase = function( all, letter ) {
+    return letter.toUpperCase();
+  };
+
+jQuery.fn = jQuery.prototype = {
+  // The current version of jQuery being used
+  jquery: version,
+
+  constructor: jQuery,
+
+  // Start with an empty selector
+  selector: "",
+
+  // The default length of a jQuery object is 0
+  length: 0,
+
+  toArray: function() {
+    return slice.call( this );
+  },
+
+  // Get the Nth element in the matched element set OR
+  // Get the whole matched element set as a clean array
+  get: function( num ) {
+    return num != null ?
+
+      // Return just the one element from the set
+      ( num < 0 ? this[ num + this.length ] : this[ num ] ) :
+
+      // Return all the elements in a clean array
+      slice.call( this );
+  },
+
+  // Take an array of elements and push it onto the stack
+  // (returning the new matched element set)
+  pushStack: function( elems ) {
+
+    // Build a new jQuery matched element set
+    var ret = jQuery.merge( this.constructor(), elems );
+
+    // Add the old object onto the stack (as a reference)
+    ret.prevObject = this;
+    ret.context = this.context;
+
+    // Return the newly-formed element set
+    return ret;
+  },
+
+  // Execute a callback for every element in the matched set.
+  // (You can seed the arguments with an array of args, but this is
+  // only used internally.)
+  each: function( callback, args ) {
+    return jQuery.each( this, callback, args );
+  },
+
+  map: function( callback ) {
+    return this.pushStack( jQuery.map(this, function( elem, i ) {
+      return callback.call( elem, i, elem );
+    }));
+  },
+
+  slice: function() {
+    return this.pushStack( slice.apply( this, arguments ) );
+  },
+
+  first: function() {
+    return this.eq( 0 );
+  },
+
+  last: function() {
+    return this.eq( -1 );
+  },
+
+  eq: function( i ) {
+    var len = this.length,
+      j = +i + ( i < 0 ? len : 0 );
+    return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
+  },
+
+  end: function() {
+    return this.prevObject || this.constructor(null);
+  },
+
+  // For internal use only.
+  // Behaves like an Array's method, not like a jQuery method.
+  push: push,
+  sort: deletedIds.sort,
+  splice: deletedIds.splice
+};
+
+jQuery.extend = jQuery.fn.extend = function() {
+  var src, copyIsArray, copy, name, options, clone,
+    target = arguments[0] || {},
+    i = 1,
+    length = arguments.length,
+    deep = false;
+
+  // Handle a deep copy situation
+  if ( typeof target === "boolean" ) {
+    deep = target;
+
+    // skip the boolean and the target
+    target = arguments[ i ] || {};
+    i++;
+  }
+
+  // Handle case when target is a string or something (possible in deep copy)
+  if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+    target = {};
+  }
+
+  // extend jQuery itself if only one argument is passed
+  if ( i === length ) {
+    target = this;
+    i--;
+  }
+
+  for ( ; i < length; i++ ) {
+    // Only deal with non-null/undefined values
+    if ( (options = arguments[ i ]) != null ) {
+      // Extend the base object
+      for ( name in options ) {
+        src = target[ name ];
+        copy = options[ name ];
+
+        // Prevent never-ending loop
+        if ( target === copy ) {
+          continue;
+        }
+
+        // Recurse if we're merging plain objects or arrays
+        if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
+          if ( copyIsArray ) {
+            copyIsArray = false;
+            clone = src && jQuery.isArray(src) ? src : [];
+
+          } else {
+            clone = src && jQuery.isPlainObject(src) ? src : {};
+          }
+
+          // Never move original objects, clone them
+          target[ name ] = jQuery.extend( deep, clone, copy );
+
+        // Don't bring in undefined values
+        } else if ( copy !== undefined ) {
+          target[ name ] = copy;
+        }
+      }
+    }
+  }
+
+  // Return the modified object
+  return target;
+};
+
+jQuery.extend({
+  // Unique for each copy of jQuery on the page
+  expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
+
+  // Assume jQuery is ready without the ready module
+  isReady: true,
+
+  error: function( msg ) {
+    throw new Error( msg );
+  },
+
+  noop: function() {},
+
+  // See test/unit/core.js for details concerning isFunction.
+  // Since version 1.3, DOM methods and functions like alert
+  // aren't supported. They return false on IE (#2968).
+  isFunction: function( obj ) {
+    return jQuery.type(obj) === "function";
+  },
+
+  isArray: Array.isArray || function( obj ) {
+    return jQuery.type(obj) === "array";
+  },
+
+  isWindow: function( obj ) {
+    /* jshint eqeqeq: false */
+    return obj != null && obj == obj.window;
+  },
+
+  isNumeric: function( obj ) {
+    // parseFloat NaNs numeric-cast false positives (null|true|false|"")
+    // ...but misinterprets leading-number strings, particularly hex literals ("0x...")
+    // subtraction forces infinities to NaN
+    return !jQuery.isArray( obj ) && obj - parseFloat( obj ) >= 0;
+  },
+
+  isEmptyObject: function( obj ) {
+    var name;
+    for ( name in obj ) {
+      return false;
+    }
+    return true;
+  },
+
+  isPlainObject: function( obj ) {
+    var key;
+
+    // Must be an Object.
+    // Because of IE, we also have to check the presence of the constructor property.
+    // Make sure that DOM nodes and window objects don't pass through, as well
+    if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+      return false;
+    }
+
+    try {
+      // Not own constructor property must be Object
+      if ( obj.constructor &&
+        !hasOwn.call(obj, "constructor") &&
+        !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
+        return false;
+      }
+    } catch ( e ) {
+      // IE8,9 Will throw exceptions on certain host objects #9897
+      return false;
+    }
+
+    // Support: IE<9
+    // Handle iteration over inherited properties before own properties.
+    if ( support.ownLast ) {
+      for ( key in obj ) {
+        return hasOwn.call( obj, key );
+      }
+    }
+
+    // Own properties are enumerated firstly, so to speed up,
+    // if last one is own, then all properties are own.
+    for ( key in obj ) {}
+
+    return key === undefined || hasOwn.call( obj, key );
+  },
+
+  type: function( obj ) {
+    if ( obj == null ) {
+      return obj + "";
+    }
+    return typeof obj === "object" || typeof obj === "function" ?
+      class2type[ toString.call(obj) ] || "object" :
+      typeof obj;
+  },
+
+  // Evaluates a script in a global context
+  // Workarounds based on findings by Jim Driscoll
+  // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
+  globalEval: function( data ) {
+    if ( data && jQuery.trim( data ) ) {
+      // We use execScript on Internet Explorer
+      // We use an anonymous function so that context is window
+      // rather than jQuery in Firefox
+      ( window.execScript || function( data ) {
+        window[ "eval" ].call( window, data );
+      } )( data );
+    }
+  },
+
+  // Convert dashed to camelCase; used by the css and data modules
+  // Microsoft forgot to hump their vendor prefix (#9572)
+  camelCase: function( string ) {
+    return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
+  },
+
+  nodeName: function( elem, name ) {
+    return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
+  },
+
+  // args is for internal usage only
+  each: function( obj, callback, args ) {
+    var value,
+      i = 0,
+      length = obj.length,
+      isArray = isArraylike( obj );
+
+    if ( args ) {
+      if ( isArray ) {
+        for ( ; i < length; i++ ) {
+          value = callback.apply( obj[ i ], args );
+
+          if ( value === false ) {
+            break;
+          }
+        }
+      } else {
+        for ( i in obj ) {
+          value = callback.apply( obj[ i ], args );
+
+          if ( value === false ) {
+            break;
+          }
+        }
+      }
+
+    // A special, fast, case for the most common use of each
+    } else {
+      if ( isArray ) {
+        for ( ; i < length; i++ ) {
+          value = callback.call( obj[ i ], i, obj[ i ] );
+
+          if ( value === false ) {
+            break;
+          }
+        }
+      } else {
+        for ( i in obj ) {
+          value = callback.call( obj[ i ], i, obj[ i ] );
+
+          if ( value === false ) {
+            break;
+          }
+        }
+      }
+    }
+
+    return obj;
+  },
+
+  // Support: Android<4.1, IE<9
+  trim: function( text ) {
+    return text == null ?
+      "" :
+      ( text + "" ).replace( rtrim, "" );
+  },
+
+  // results is for internal usage only
+  makeArray: function( arr, results ) {
+    var ret = results || [];
+
+    if ( arr != null ) {
+      if ( isArraylike( Object(arr) ) ) {
+        jQuery.merge( ret,
+          typeof arr === "string" ?
+          [ arr ] : arr
+        );
+      } else {
+        push.call( ret, arr );
+      }
+    }
+
+    return ret;
+  },
+
+  inArray: function( elem, arr, i ) {
+    var len;
+
+    if ( arr ) {
+      if ( indexOf ) {
+        return indexOf.call( arr, elem, i );
+      }
+
+      len = arr.length;
+      i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
+
+      for ( ; i < len; i++ ) {
+        // Skip accessing in sparse arrays
+        if ( i in arr && arr[ i ] === elem ) {
+          return i;
+        }
+      }
+    }
+
+    return -1;
+  },
+
+  merge: function( first, second ) {
+    var len = +second.length,
+      j = 0,
+      i = first.length;
+
+    while ( j < len ) {
+      first[ i++ ] = second[ j++ ];
+    }
+
+    // Support: IE<9
+    // Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists)
+    if ( len !== len ) {
+      while ( second[j] !== undefined ) {
+        first[ i++ ] = second[ j++ ];
+      }
+    }
+
+    first.length = i;
+
+    return first;
+  },
+
+  grep: function( elems, callback, invert ) {
+    var callbackInverse,
+      matches = [],
+      i = 0,
+      length = elems.length,
+      callbackExpect = !invert;
+
+    // Go through the array, only saving the items
+    // that pass the validator function
+    for ( ; i < length; i++ ) {
+      callbackInverse = !callback( elems[ i ], i );
+      if ( callbackInverse !== callbackExpect ) {
+        matches.push( elems[ i ] );
+      }
+    }
+
+    return matches;
+  },
+
+  // arg is for internal usage only
+  map: function( elems, callback, arg ) {
+    var value,
+      i = 0,
+      length = elems.length,
+      isArray = isArraylike( elems ),
+      ret = [];
+
+    // Go through the array, translating each of the items to their new values
+    if ( isArray ) {
+      for ( ; i < length; i++ ) {
+        value = callback( elems[ i ], i, arg );
+
+        if ( value != null ) {
+          ret.push( value );
+        }
+      }
+
+    // Go through every key on the object,
+    } else {
+      for ( i in elems ) {
+        value = callback( elems[ i ], i, arg );
+
+        if ( value != null ) {
+          ret.push( value );
+        }
+      }
+    }
+
+    // Flatten any nested arrays
+    return concat.apply( [], ret );
+  },
+
+  // A global GUID counter for objects
+  guid: 1,
+
+  // Bind a function to a context, optionally partially applying any
+  // arguments.
+  proxy: function( fn, context ) {
+    var args, proxy, tmp;
+
+    if ( typeof context === "string" ) {
+      tmp = fn[ context ];
+      context = fn;
+      fn = tmp;
+    }
+
+    // Quick check to determine if target is callable, in the spec
+    // this throws a TypeError, but we will just return undefined.
+    if ( !jQuery.isFunction( fn ) ) {
+      return undefined;
+    }
+
+    // Simulated bind
+    args = slice.call( arguments, 2 );
+    proxy = function() {
+      return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
+    };
+
+    // Set the guid of unique handler to the same of original handler, so it can be removed
+    proxy.guid = fn.guid = fn.guid || jQuery.guid++;
+
+    return proxy;
+  },
+
+  now: function() {
+    return +( new Date() );
+  },
+
+  // jQuery.support is not used in Core but other projects attach their
+  // properties to it so it needs to exist.
+  support: support
+});
+
+// Populate the class2type map
+jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
+  class2type[ "[object " + name + "]" ] = name.toLowerCase();
+});
+
+function isArraylike( obj ) {
+  var length = obj.length,
+    type = jQuery.type( obj );
+
+  if ( type === "function" || jQuery.isWindow( obj ) ) {
+    return false;
+  }
+
+  if ( obj.nodeType === 1 && length ) {
+    return true;
+  }
+
+  return type === "array" || length === 0 ||
+    typeof length === "number" && length > 0 && ( length - 1 ) in obj;
+}
+var Sizzle =
+/*!
+ * Sizzle CSS Selector Engine v1.10.19
+ * http://sizzlejs.com/
+ *
+ * Copyright 2013 jQuery Foundation, Inc. and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2014-04-18
+ */
+(function( window ) {
+
+var i,
+  support,
+  Expr,
+  getText,
+  isXML,
+  tokenize,
+  compile,
+  select,
+  outermostContext,
+  sortInput,
+  hasDuplicate,
+
+  // Local document vars
+  setDocument,
+  document,
+  docElem,
+  documentIsHTML,
+  rbuggyQSA,
+  rbuggyMatches,
+  matches,
+  contains,
+
+  // Instance-specific data
+  expando = "sizzle" + -(new Date()),
+  preferredDoc = window.document,
+  dirruns = 0,
+  done = 0,
+  classCache = createCache(),
+  tokenCache = createCache(),
+  compilerCache = createCache(),
+  sortOrder = function( a, b ) {
+    if ( a === b ) {
+      hasDuplicate = true;
+    }
+    return 0;
+  },
+
+  // General-purpose constants
+  strundefined = typeof undefined,
+  MAX_NEGATIVE = 1 << 31,
+
+  // Instance methods
+  hasOwn = ({}).hasOwnProperty,
+  arr = [],
+  pop = arr.pop,
+  push_native = arr.push,
+  push = arr.push,
+  slice = arr.slice,
+  // Use a stripped-down indexOf if we can't use a native one
+  indexOf = arr.indexOf || function( elem ) {
+    var i = 0,
+      len = this.length;
+    for ( ; i < len; i++ ) {
+      if ( this[i] === elem ) {
+        return i;
+      }
+    }
+    return -1;
+  },
+
+  booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
+
+  // Regular expressions
+
+  // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
+  whitespace = "[\\x20\\t\\r\\n\\f]",
+  // http://www.w3.org/TR/css3-syntax/#characters
+  characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
+
+  // Loosely modeled on CSS identifier characters
+  // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
+  // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
+  identifier = characterEncoding.replace( "w", "w#" ),
+
+  // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
+  attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace +
+    // Operator (capture 2)
+    "*([*^$|!~]?=)" + whitespace +
+    // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
+    "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
+    "*\\]",
+
+  pseudos = ":(" + characterEncoding + ")(?:\\((" +
+    // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
+    // 1. quoted (capture 3; capture 4 or capture 5)
+    "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
+    // 2. simple (capture 6)
+    "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
+    // 3. anything else (capture 2)
+    ".*" +
+    ")\\)|)",
+
+  // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
+  rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
+
+  rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
+  rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
+
+  rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),
+
+  rpseudo = new RegExp( pseudos ),
+  ridentifier = new RegExp( "^" + identifier + "$" ),
+
+  matchExpr = {
+    "ID": new RegExp( "^#(" + characterEncoding + ")" ),
+    "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
+    "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
+    "ATTR": new RegExp( "^" + attributes ),
+    "PSEUDO": new RegExp( "^" + pseudos ),
+    "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
+      "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
+      "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
+    "bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
+    // For use in libraries implementing .is()
+    // We use this for POS matching in `select`
+    "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
+      whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
+  },
+
+  rinputs = /^(?:input|select|textarea|button)$/i,
+  rheader = /^h\d$/i,
+
+  rnative = /^[^{]+\{\s*\[native \w/,
+
+  // Easily-parseable/retrievable ID or TAG or CLASS selectors
+  rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
+
+  rsibling = /[+~]/,
+  rescape = /'|\\/g,
+
+  // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
+  runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
+  funescape = function( _, escaped, escapedWhitespace ) {
+    var high = "0x" + escaped - 0x10000;
+    // NaN means non-codepoint
+    // Support: Firefox<24
+    // Workaround erroneous numeric interpretation of +"0x"
+    return high !== high || escapedWhitespace ?
+      escaped :
+      high < 0 ?
+        // BMP codepoint
+        String.fromCharCode( high + 0x10000 ) :
+        // Supplemental Plane codepoint (surrogate pair)
+        String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
+  };
+
+// Optimize for push.apply( _, NodeList )
+try {
+  push.apply(
+    (arr = slice.call( preferredDoc.childNodes )),
+    preferredDoc.childNodes
+  );
+  // Support: Android<4.0
+  // Detect silently failing push.apply
+  arr[ preferredDoc.childNodes.length ].nodeType;
+} catch ( e ) {
+  push = { apply: arr.length ?
+
+    // Leverage slice if possible
+    function( target, els ) {
+      push_native.apply( target, slice.call(els) );
+    } :
+
+    // Support: IE<9
+    // Otherwise append directly
+    function( target, els ) {
+      var j = target.length,
+        i = 0;
+      // Can't trust NodeList.length
+      while ( (target[j++] = els[i++]) ) {}
+      target.length = j - 1;
+    }
+  };
+}
+
+function Sizzle( selector, context, results, seed ) {
+  var match, elem, m, nodeType,
+    // QSA vars
+    i, groups, old, nid, newContext, newSelector;
+
+  if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
+    setDocument( context );
+  }
+
+  context = context || document;
+  results = results || [];
+
+  if ( !selector || typeof selector !== "string" ) {
+    return results;
+  }
+
+  if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
+    return [];
+  }
+
+  if ( documentIsHTML && !seed ) {
+
+    // Shortcuts
+    if ( (match = rquickExpr.exec( selector )) ) {
+      // Speed-up: Sizzle("#ID")
+      if ( (m = match[1]) ) {
+        if ( nodeType === 9 ) {
+          elem = context.getElementById( m );
+          // Check parentNode to catch when Blackberry 4.6 returns
+          // nodes that are no longer in the document (jQuery #6963)
+          if ( elem && elem.parentNode ) {
+            // Handle the case where IE, Opera, and Webkit return items
+            // by name instead of ID
+            if ( elem.id === m ) {
+              results.push( elem );
+              return results;
+            }
+          } else {
+            return results;
+          }
+        } else {
+          // Context is not a document
+          if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
+            contains( context, elem ) && elem.id === m ) {
+            results.push( elem );
+            return results;
+          }
+        }
+
+      // Speed-up: Sizzle("TAG")
+      } else if ( match[2] ) {
+        push.apply( results, context.getElementsByTagName( selector ) );
+        return results;
+
+      // Speed-up: Sizzle(".CLASS")
+      } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
+        push.apply( results, context.getElementsByClassName( m ) );
+        return results;
+      }
+    }
+
+    // QSA path
+    if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
+      nid = old = expando;
+      newContext = context;
+      newSelector = nodeType === 9 && selector;
+
+      // qSA works strangely on Element-rooted queries
+      // We can work around this by specifying an extra ID on the root
+      // and working up from there (Thanks to Andrew Dupont for the technique)
+      // IE 8 doesn't work on object elements
+      if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+        groups = tokenize( selector );
+
+        if ( (old = context.getAttribute("id")) ) {
+          nid = old.replace( rescape, "\\$&" );
+        } else {
+          context.setAttribute( "id", nid );
+        }
+        nid = "[id='" + nid + "'] ";
+
+        i = groups.length;
+        while ( i-- ) {
+          groups[i] = nid + toSelector( groups[i] );
+        }
+        newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context;
+        newSelector = groups.join(",");
+      }
+
+      if ( newSelector ) {
+        try {
+          push.apply( results,
+            newContext.querySelectorAll( newSelector )
+          );
+          return results;
+        } catch(qsaError) {
+        } finally {
+          if ( !old ) {
+            context.removeAttribute("id");
+          }
+        }
+      }
+    }
+  }
+
+  // All others
+  return select( selector.replace( rtrim, "$1" ), context, results, seed );
+}
+
+/**
+ * Create key-value caches of limited size
+ * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
+ *  property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
+ *  deleting the oldest entry
+ */
+function createCache() {
+  var keys = [];
+
+  function cache( key, value ) {
+    // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
+    if ( keys.push( key + " " ) > Expr.cacheLength ) {
+      // Only keep the most recent entries
+      delete cache[ keys.shift() ];
+    }
+    return (cache[ key + " " ] = value);
+  }
+  return cache;
+}
+
+/**
+ * Mark a function for special use by Sizzle
+ * @param {Function} fn The function to mark
+ */
+function markFunction( fn ) {
+  fn[ expando ] = true;
+  return fn;
+}
+
+/**
+ * Support testing using an element
+ * @param {Function} fn Passed the created div and expects a boolean result
+ */
+function assert( fn ) {
+  var div = document.createElement("div");
+
+  try {
+    return !!fn( div );
+  } catch (e) {
+    return false;
+  } finally {
+    // Remove from its parent by default
+    if ( div.parentNode ) {
+      div.parentNode.removeChild( div );
+    }
+    // release memory in IE
+    div = null;
+  }
+}
+
+/**
+ * Adds the same handler for all of the specified attrs
+ * @param {String} attrs Pipe-separated list of attributes
+ * @param {Function} handler The method that will be applied
+ */
+function addHandle( attrs, handler ) {
+  var arr = attrs.split("|"),
+    i = attrs.length;
+
+  while ( i-- ) {
+    Expr.attrHandle[ arr[i] ] = handler;
+  }
+}
+
+/**
+ * Checks document order of two siblings
+ * @param {Element} a
+ * @param {Element} b
+ * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
+ */
+function siblingCheck( a, b ) {
+  var cur = b && a,
+    diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
+      ( ~b.sourceIndex || MAX_NEGATIVE ) -
+      ( ~a.sourceIndex || MAX_NEGATIVE );
+
+  // Use IE sourceIndex if available on both nodes
+  if ( diff ) {
+    return diff;
+  }
+
+  // Check if b follows a
+  if ( cur ) {
+    while ( (cur = cur.nextSibling) ) {
+      if ( cur === b ) {
+        return -1;
+      }
+    }
+  }
+
+  return a ? 1 : -1;
+}
+
+/**
+ * Returns a function to use in pseudos for input types
+ * @param {String} type
+ */
+function createInputPseudo( type ) {
+  return function( elem ) {
+    var name = elem.nodeName.toLowerCase();
+    return name === "input" && elem.type === type;
+  };
+}
+
+/**
+ * Returns a function to use in pseudos for buttons
+ * @param {String} type
+ */
+function createButtonPseudo( type ) {
+  return function( elem ) {
+    var name = elem.nodeName.toLowerCase();
+    return (name === "input" || name === "button") && elem.type === type;
+  };
+}
+
+/**
+ * Returns a function to use in pseudos for positionals
+ * @param {Function} fn
+ */
+function createPositionalPseudo( fn ) {
+  return markFunction(function( argument ) {
+    argument = +argument;
+    return markFunction(function( seed, matches ) {
+      var j,
+        matchIndexes = fn( [], seed.length, argument ),
+        i = matchIndexes.length;
+
+      // Match elements found at the specified indexes
+      while ( i-- ) {
+        if ( seed[ (j = matchIndexes[i]) ] ) {
+          seed[j] = !(matches[j] = seed[j]);
+        }
+      }
+    });
+  });
+}
+
+/**
+ * Checks a node for validity as a Sizzle context
+ * @param {Element|Object=} context
+ * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
+ */
+function testContext( context ) {
+  return context && typeof context.getElementsByTagName !== strundefined && context;
+}
+
+// Expose support vars for convenience
+support = Sizzle.support = {};
+
+/**
+ * Detects XML nodes
+ * @param {Element|Object} elem An element or a document
+ * @returns {Boolean} True iff elem is a non-HTML XML node
+ */
+isXML = Sizzle.isXML = function( elem ) {
+  // documentElement is verified for cases where it doesn't yet exist
+  // (such as loading iframes in IE - #4833)
+  var documentElement = elem && (elem.ownerDocument || elem).documentElement;
+  return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+/**
+ * Sets document-related variables once based on the current document
+ * @param {Element|Object} [doc] An element or document object to use to set the document
+ * @returns {Object} Returns the current document
+ */
+setDocument = Sizzle.setDocument = function( node ) {
+  var hasCompare,
+    doc = node ? node.ownerDocument || node : preferredDoc,
+    parent = doc.defaultView;
+
+  // If no document and documentElement is available, return
+  if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
+    return document;
+  }
+
+  // Set our document
+  document = doc;
+  docElem = doc.documentElement;
+
+  // Support tests
+  documentIsHTML = !isXML( doc );
+
+  // Support: IE>8
+  // If iframe document is assigned to "document" variable and if iframe has been reloaded,
+  // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
+  // IE6-8 do not support the defaultView property so parent will be undefined
+  if ( parent && parent !== parent.top ) {
+    // IE11 does not have attachEvent, so all must suffer
+    if ( parent.addEventListener ) {
+      parent.addEventListener( "unload", function() {
+        setDocument();
+      }, false );
+    } else if ( parent.attachEvent ) {
+      parent.attachEvent( "onunload", function() {
+        setDocument();
+      });
+    }
+  }
+
+  /* Attributes
+  ---------------------------------------------------------------------- */
+
+  // Support: IE<8
+  // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans)
+  support.attributes = assert(function( div ) {
+    div.className = "i";
+    return !div.getAttribute("className");
+  });
+
+  /* getElement(s)By*
+  ---------------------------------------------------------------------- */
+
+  // Check if getElementsByTagName("*") returns only elements
+  support.getElementsByTagName = assert(function( div ) {
+    div.appendChild( doc.createComment("") );
+    return !div.getElementsByTagName("*").length;
+  });
+
+  // Check if getElementsByClassName can be trusted
+  support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) {
+    div.innerHTML = "<div class='a'></div><div class='a i'></div>";
+
+    // Support: Safari<4
+    // Catch class over-caching
+    div.firstChild.className = "i";
+    // Support: Opera<10
+    // Catch gEBCN failure to find non-leading classes
+    return div.getElementsByClassName("i").length === 2;
+  });
+
+  // Support: IE<10
+  // Check if getElementById returns elements by name
+  // The broken getElementById methods don't pick up programatically-set names,
+  // so use a roundabout getElementsByName test
+  support.getById = assert(function( div ) {
+    docElem.appendChild( div ).id = expando;
+    return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
+  });
+
+  // ID find and filter
+  if ( support.getById ) {
+    Expr.find["ID"] = function( id, context ) {
+      if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
+        var m = context.getElementById( id );
+        // Check parentNode to catch when Blackberry 4.6 returns
+        // nodes that are no longer in the document #6963
+        return m && m.parentNode ? [ m ] : [];
+      }
+    };
+    Expr.filter["ID"] = function( id ) {
+      var attrId = id.replace( runescape, funescape );
+      return function( elem ) {
+        return elem.getAttribute("id") === attrId;
+      };
+    };
+  } else {
+    // Support: IE6/7
+    // getElementById is not reliable as a find shortcut
+    delete Expr.find["ID"];
+
+    Expr.filter["ID"] =  function( id ) {
+      var attrId = id.replace( runescape, funescape );
+      return function( elem ) {
+        var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
+        return node && node.value === attrId;
+      };
+    };
+  }
+
+  // Tag
+  Expr.find["TAG"] = support.getElementsByTagName ?
+    function( tag, context ) {
+      if ( typeof context.getElementsByTagName !== strundefined ) {
+        return context.getElementsByTagName( tag );
+      }
+    } :
+    function( tag, context ) {
+      var elem,
+        tmp = [],
+        i = 0,
+        results = context.getElementsByTagName( tag );
+
+      // Filter out possible comments
+      if ( tag === "*" ) {
+        while ( (elem = results[i++]) ) {
+          if ( elem.nodeType === 1 ) {
+            tmp.push( elem );
+          }
+        }
+
+        return tmp;
+      }
+      return results;
+    };
+
+  // Class
+  Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
+    if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
+      return context.getElementsByClassName( className );
+    }
+  };
+
+  /* QSA/matchesSelector
+  ---------------------------------------------------------------------- */
+
+  // QSA and matchesSelector support
+
+  // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
+  rbuggyMatches = [];
+
+  // qSa(:focus) reports false when true (Chrome 21)
+  // We allow this because of a bug in IE8/9 that throws an error
+  // whenever `document.activeElement` is accessed on an iframe
+  // So, we allow :focus to pass through QSA all the time to avoid the IE error
+  // See http://bugs.jquery.com/ticket/13378
+  rbuggyQSA = [];
+
+  if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) {
+    // Build QSA regex
+    // Regex strategy adopted from Diego Perini
+    assert(function( div ) {
+      // Select is set to empty string on purpose
+      // This is to test IE's treatment of not explicitly
+      // setting a boolean content attribute,
+      // since its presence should be enough
+      // http://bugs.jquery.com/ticket/12359
+      div.innerHTML = "<select msallowclip=''><option selected=''></option></select>";
+
+      // Support: IE8, Opera 11-12.16
+      // Nothing should be selected when empty strings follow ^= or $= or *=
+      // The test attribute must be unknown in Opera but "safe" for WinRT
+      // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
+      if ( div.querySelectorAll("[msallowclip^='']").length ) {
+        rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
+      }
+
+      // Support: IE8
+      // Boolean attributes and "value" are not treated correctly
+      if ( !div.querySelectorAll("[selected]").length ) {
+        rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
+      }
+
+      // Webkit/Opera - :checked should return selected option elements
+      // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+      // IE8 throws error here and will not see later tests
+      if ( !div.querySelectorAll(":checked").length ) {
+        rbuggyQSA.push(":checked");
+      }
+    });
+
+    assert(function( div ) {
+      // Support: Windows 8 Native Apps
+      // The type and name attributes are restricted during .innerHTML assignment
+      var input = doc.createElement("input");
+      input.setAttribute( "type", "hidden" );
+      div.appendChild( input ).setAttribute( "name", "D" );
+
+      // Support: IE8
+      // Enforce case-sensitivity of name attribute
+      if ( div.querySelectorAll("[name=d]").length ) {
+        rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
+      }
+
+      // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
+      // IE8 throws error here and will not see later tests
+      if ( !div.querySelectorAll(":enabled").length ) {
+        rbuggyQSA.push( ":enabled", ":disabled" );
+      }
+
+      // Opera 10-11 does not throw on post-comma invalid pseudos
+      div.querySelectorAll("*,:x");
+      rbuggyQSA.push(",.*:");
+    });
+  }
+
+  if ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||
+    docElem.webkitMatchesSelector ||
+    docElem.mozMatchesSelector ||
+    docElem.oMatchesSelector ||
+    docElem.msMatchesSelector) )) ) {
+
+    assert(function( div ) {
+      // Check to see if it's possible to do matchesSelector
+      // on a disconnected node (IE 9)
+      support.disconnectedMatch = matches.call( div, "div" );
+
+      // This should fail with an exception
+      // Gecko does not error, returns false instead
+      matches.call( div, "[s!='']:x" );
+      rbuggyMatches.push( "!=", pseudos );
+    });
+  }
+
+  rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
+  rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
+
+  /* Contains
+  ---------------------------------------------------------------------- */
+  hasCompare = rnative.test( docElem.compareDocumentPosition );
+
+  // Element contains another
+  // Purposefully does not implement inclusive descendent
+  // As in, an element does not contain itself
+  contains = hasCompare || rnative.test( docElem.contains ) ?
+    function( a, b ) {
+      var adown = a.nodeType === 9 ? a.documentElement : a,
+        bup = b && b.parentNode;
+      return a === bup || !!( bup && bup.nodeType === 1 && (
+        adown.contains ?
+          adown.contains( bup ) :
+          a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
+      ));
+    } :
+    function( a, b ) {
+      if ( b ) {
+        while ( (b = b.parentNode) ) {
+          if ( b === a ) {
+            return true;
+          }
+        }
+      }
+      return false;
+    };
+
+  /* Sorting
+  ---------------------------------------------------------------------- */
+
+  // Document order sorting
+  sortOrder = hasCompare ?
+  function( a, b ) {
+
+    // Flag for duplicate removal
+    if ( a === b ) {
+      hasDuplicate = true;
+      return 0;
+    }
+
+    // Sort on method existence if only one input has compareDocumentPosition
+    var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
+    if ( compare ) {
+      return compare;
+    }
+
+    // Calculate position if both inputs belong to the same document
+    compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
+      a.compareDocumentPosition( b ) :
+
+      // Otherwise we know they are disconnected
+      1;
+
+    // Disconnected nodes
+    if ( compare & 1 ||
+      (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
+
+      // Choose the first element that is related to our preferred document
+      if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
+        return -1;
+      }
+      if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
+        return 1;
+      }
+
+      // Maintain original order
+      return sortInput ?
+        ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
+        0;
+    }
+
+    return compare & 4 ? -1 : 1;
+  } :
+  function( a, b ) {
+    // Exit early if the nodes are identical
+    if ( a === b ) {
+      hasDuplicate = true;
+      return 0;
+    }
+
+    var cur,
+      i = 0,
+      aup = a.parentNode,
+      bup = b.parentNode,
+      ap = [ a ],
+      bp = [ b ];
+
+    // Parentless nodes are either documents or disconnected
+    if ( !aup || !bup ) {
+      return a === doc ? -1 :
+        b === doc ? 1 :
+        aup ? -1 :
+        bup ? 1 :
+        sortInput ?
+        ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
+        0;
+
+    // If the nodes are siblings, we can do a quick check
+    } else if ( aup === bup ) {
+      return siblingCheck( a, b );
+    }
+
+    // Otherwise we need full lists of their ancestors for comparison
+    cur = a;
+    while ( (cur = cur.parentNode) ) {
+      ap.unshift( cur );
+    }
+    cur = b;
+    while ( (cur = cur.parentNode) ) {
+      bp.unshift( cur );
+    }
+
+    // Walk down the tree looking for a discrepancy
+    while ( ap[i] === bp[i] ) {
+      i++;
+    }
+
+    return i ?
+      // Do a sibling check if the nodes have a common ancestor
+      siblingCheck( ap[i], bp[i] ) :
+
+      // Otherwise nodes in our document sort first
+      ap[i] === preferredDoc ? -1 :
+      bp[i] === preferredDoc ? 1 :
+      0;
+  };
+
+  return doc;
+};
+
+Sizzle.matches = function( expr, elements ) {
+  return Sizzle( expr, null, null, elements );
+};
+
+Sizzle.matchesSelector = function( elem, expr ) {
+  // Set document vars if needed
+  if ( ( elem.ownerDocument || elem ) !== document ) {
+    setDocument( elem );
+  }
+
+  // Make sure that attribute selectors are quoted
+  expr = expr.replace( rattributeQuotes, "='$1']" );
+
+  if ( support.matchesSelector && documentIsHTML &&
+    ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
+    ( !rbuggyQSA     || !rbuggyQSA.test( expr ) ) ) {
+
+    try {
+      var ret = matches.call( elem, expr );
+
+      // IE 9's matchesSelector returns false on disconnected nodes
+      if ( ret || support.disconnectedMatch ||
+          // As well, disconnected nodes are said to be in a document
+          // fragment in IE 9
+          elem.document && elem.document.nodeType !== 11 ) {
+        return ret;
+      }
+    } catch(e) {}
+  }
+
+  return Sizzle( expr, document, null, [ elem ] ).length > 0;
+};
+
+Sizzle.contains = function( context, elem ) {
+  // Set document vars if needed
+  if ( ( context.ownerDocument || context ) !== document ) {
+    setDocument( context );
+  }
+  return contains( context, elem );
+};
+
+Sizzle.attr = function( elem, name ) {
+  // Set document vars if needed
+  if ( ( elem.ownerDocument || elem ) !== document ) {
+    setDocument( elem );
+  }
+
+  var fn = Expr.attrHandle[ name.toLowerCase() ],
+    // Don't get fooled by Object.prototype properties (jQuery #13807)
+    val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
+      fn( elem, name, !documentIsHTML ) :
+      undefined;
+
+  return val !== undefined ?
+    val :
+    support.attributes || !documentIsHTML ?
+      elem.getAttribute( name ) :
+      (val = elem.getAttributeNode(name)) && val.specified ?
+        val.value :
+        null;
+};
+
+Sizzle.error = function( msg ) {
+  throw new Error( "Syntax error, unrecognized expression: " + msg );
+};
+
+/**
+ * Document sorting and removing duplicates
+ * @param {ArrayLike} results
+ */
+Sizzle.uniqueSort = function( results ) {
+  var elem,
+    duplicates = [],
+    j = 0,
+    i = 0;
+
+  // Unless we *know* we can detect duplicates, assume their presence
+  hasDuplicate = !support.detectDuplicates;
+  sortInput = !support.sortStable && results.slice( 0 );
+  results.sort( sortOrder );
+
+  if ( hasDuplicate ) {
+    while ( (elem = results[i++]) ) {
+      if ( elem === results[ i ] ) {
+        j = duplicates.push( i );
+      }
+    }
+    while ( j-- ) {
+      results.splice( duplicates[ j ], 1 );
+    }
+  }
+
+  // Clear input after sorting to release objects
+  // See https://github.com/jquery/sizzle/pull/225
+  sortInput = null;
+
+  return results;
+};
+
+/**
+ * Utility function for retrieving the text value of an array of DOM nodes
+ * @param {Array|Element} elem
+ */
+getText = Sizzle.getText = function( elem ) {
+  var node,
+    ret = "",
+    i = 0,
+    nodeType = elem.nodeType;
+
+  if ( !nodeType ) {
+    // If no nodeType, this is expected to be an array
+    while ( (node = elem[i++]) ) {
+      // Do not traverse comment nodes
+      ret += getText( node );
+    }
+  } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
+    // Use textContent for elements
+    // innerText usage removed for consistency of new lines (jQuery #11153)
+    if ( typeof elem.textContent === "string" ) {
+      return elem.textContent;
+    } else {
+      // Traverse its children
+      for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+        ret += getText( elem );
+      }
+    }
+  } else if ( nodeType === 3 || nodeType === 4 ) {
+    return elem.nodeValue;
+  }
+  // Do not include comment or processing instruction nodes
+
+  return ret;
+};
+
+Expr = Sizzle.selectors = {
+
+  // Can be adjusted by the user
+  cacheLength: 50,
+
+  createPseudo: markFunction,
+
+  match: matchExpr,
+
+  attrHandle: {},
+
+  find: {},
+
+  relative: {
+    ">": { dir: "parentNode", first: true },
+    " ": { dir: "parentNode" },
+    "+": { dir: "previousSibling", first: true },
+    "~": { dir: "previousSibling" }
+  },
+
+  preFilter: {
+    "ATTR": function( match ) {
+      match[1] = match[1].replace( runescape, funescape );
+
+      // Move the given value to match[3] whether quoted or unquoted
+      match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape );
+
+      if ( match[2] === "~=" ) {
+        match[3] = " " + match[3] + " ";
+      }
+
+      return match.slice( 0, 4 );
+    },
+
+    "CHILD": function( match ) {
+      /* matches from matchExpr["CHILD"]
+        1 type (only|nth|...)
+        2 what (child|of-type)
+        3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
+        4 xn-component of xn+y argument ([+-]?\d*n|)
+        5 sign of xn-component
+        6 x of xn-component
+        7 sign of y-component
+        8 y of y-component
+      */
+      match[1] = match[1].toLowerCase();
+
+      if ( match[1].slice( 0, 3 ) === "nth" ) {
+        // nth-* requires argument
+        if ( !match[3] ) {
+          Sizzle.error( match[0] );
+        }
+
+        // numeric x and y parameters for Expr.filter.CHILD
+        // remember that false/true cast respectively to 0/1
+        match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
+        match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
+
+      // other types prohibit arguments
+      } else if ( match[3] ) {
+        Sizzle.error( match[0] );
+      }
+
+      return match;
+    },
+
+    "PSEUDO": function( match ) {
+      var excess,
+        unquoted = !match[6] && match[2];
+
+      if ( matchExpr["CHILD"].test( match[0] ) ) {
+        return null;
+      }
+
+      // Accept quoted arguments as-is
+      if ( match[3] ) {
+        match[2] = match[4] || match[5] || "";
+
+      // Strip excess characters from unquoted arguments
+      } else if ( unquoted && rpseudo.test( unquoted ) &&
+        // Get excess from tokenize (recursively)
+        (excess = tokenize( unquoted, true )) &&
+        // advance to the next closing parenthesis
+        (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
+
+        // excess is a negative index
+        match[0] = match[0].slice( 0, excess );
+        match[2] = unquoted.slice( 0, excess );
+      }
+
+      // Return only captures needed by the pseudo filter method (type and argument)
+      return match.slice( 0, 3 );
+    }
+  },
+
+  filter: {
+
+    "TAG": function( nodeNameSelector ) {
+      var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
+      return nodeNameSelector === "*" ?
+        function() { return true; } :
+        function( elem ) {
+          return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
+        };
+    },
+
+    "CLASS": function( className ) {
+      var pattern = classCache[ className + " " ];
+
+      return pattern ||
+        (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
+        classCache( className, function( elem ) {
+          return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" );
+        });
+    },
+
+    "ATTR": function( name, operator, check ) {
+      return function( elem ) {
+        var result = Sizzle.attr( elem, name );
+
+        if ( result == null ) {
+          return operator === "!=";
+        }
+        if ( !operator ) {
+          return true;
+        }
+
+        result += "";
+
+        return operator === "=" ? result === check :
+          operator === "!=" ? result !== check :
+          operator === "^=" ? check && result.indexOf( check ) === 0 :
+          operator === "*=" ? check && result.indexOf( check ) > -1 :
+          operator === "$=" ? check && result.slice( -check.length ) === check :
+          operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
+          operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
+          false;
+      };
+    },
+
+    "CHILD": function( type, what, argument, first, last ) {
+      var simple = type.slice( 0, 3 ) !== "nth",
+        forward = type.slice( -4 ) !== "last",
+        ofType = what === "of-type";
+
+      return first === 1 && last === 0 ?
+
+        // Shortcut for :nth-*(n)
+        function( elem ) {
+          return !!elem.parentNode;
+        } :
+
+        function( elem, context, xml ) {
+          var cache, outerCache, node, diff, nodeIndex, start,
+            dir = simple !== forward ? "nextSibling" : "previousSibling",
+            parent = elem.parentNode,
+            name = ofType && elem.nodeName.toLowerCase(),
+            useCache = !xml && !ofType;
+
+          if ( parent ) {
+
+            // :(first|last|only)-(child|of-type)
+            if ( simple ) {
+              while ( dir ) {
+                node = elem;
+                while ( (node = node[ dir ]) ) {
+                  if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
+                    return false;
+                  }
+                }
+                // Reverse direction for :only-* (if we haven't yet done so)
+                start = dir = type === "only" && !start && "nextSibling";
+              }
+              return true;
+            }
+
+            start = [ forward ? parent.firstChild : parent.lastChild ];
+
+            // non-xml :nth-child(...) stores cache data on `parent`
+            if ( forward && useCache ) {
+              // Seek `elem` from a previously-cached index
+              outerCache = parent[ expando ] || (parent[ expando ] = {});
+              cache = outerCache[ type ] || [];
+              nodeIndex = cache[0] === dirruns && cache[1];
+              diff = cache[0] === dirruns && cache[2];
+              node = nodeIndex && parent.childNodes[ nodeIndex ];
+
+              while ( (node = ++nodeIndex && node && node[ dir ] ||
+
+                // Fallback to seeking `elem` from the start
+                (diff = nodeIndex = 0) || start.pop()) ) {
+
+                // When found, cache indexes on `parent` and break
+                if ( node.nodeType === 1 && ++diff && node === elem ) {
+                  outerCache[ type ] = [ dirruns, nodeIndex, diff ];
+                  break;
+                }
+              }
+
+            // Use previously-cached element index if available
+            } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
+              diff = cache[1];
+
+            // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
+            } else {
+              // Use the same loop as above to seek `elem` from the start
+              while ( (node = ++nodeIndex && node && node[ dir ] ||
+                (diff = nodeIndex = 0) || start.pop()) ) {
+
+                if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
+                  // Cache the index of each encountered element
+                  if ( useCache ) {
+                    (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
+                  }
+
+                  if ( node === elem ) {
+                    break;
+                  }
+                }
+              }
+            }
+
+            // Incorporate the offset, then check against cycle size
+            diff -= last;
+            return diff === first || ( diff % first === 0 && diff / first >= 0 );
+          }
+        };
+    },
+
+    "PSEUDO": function( pseudo, argument ) {
+      // pseudo-class names are case-insensitive
+      // http://www.w3.org/TR/selectors/#pseudo-classes
+      // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
+      // Remember that setFilters inherits from pseudos
+      var args,
+        fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
+          Sizzle.error( "unsupported pseudo: " + pseudo );
+
+      // The user may use createPseudo to indicate that
+      // arguments are needed to create the filter function
+      // just as Sizzle does
+      if ( fn[ expando ] ) {
+        return fn( argument );
+      }
+
+      // But maintain support for old signatures
+      if ( fn.length > 1 ) {
+        args = [ pseudo, pseudo, "", argument ];
+        return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
+          markFunction(function( seed, matches ) {
+            var idx,
+              matched = fn( seed, argument ),
+              i = matched.length;
+            while ( i-- ) {
+              idx = indexOf.call( seed, matched[i] );
+              seed[ idx ] = !( matches[ idx ] = matched[i] );
+            }
+          }) :
+          function( elem ) {
+            return fn( elem, 0, args );
+          };
+      }
+
+      return fn;
+    }
+  },
+
+  pseudos: {
+    // Potentially complex pseudos
+    "not": markFunction(function( selector ) {
+      // Trim the selector passed to compile
+      // to avoid treating leading and trailing
+      // spaces as combinators
+      var input = [],
+        results = [],
+        matcher = compile( selector.replace( rtrim, "$1" ) );
+
+      return matcher[ expando ] ?
+        markFunction(function( seed, matches, context, xml ) {
+          var elem,
+            unmatched = matcher( seed, null, xml, [] ),
+            i = seed.length;
+
+          // Match elements unmatched by `matcher`
+          while ( i-- ) {
+            if ( (elem = unmatched[i]) ) {
+              seed[i] = !(matches[i] = elem);
+            }
+          }
+        }) :
+        function( elem, context, xml ) {
+          input[0] = elem;
+          matcher( input, null, xml, results );
+          return !results.pop();
+        };
+    }),
+
+    "has": markFunction(function( selector ) {
+      return function( elem ) {
+        return Sizzle( selector, elem ).length > 0;
+      };
+    }),
+
+    "contains": markFunction(function( text ) {
+      return function( elem ) {
+        return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
+      };
+    }),
+
+    // "Whether an element is represented by a :lang() selector
+    // is based solely on the element's language value
+    // being equal to the identifier C,
+    // or beginning with the identifier C immediately followed by "-".
+    // The matching of C against the element's language value is performed case-insensitively.
+    // The identifier C does not have to be a valid language name."
+    // http://www.w3.org/TR/selectors/#lang-pseudo
+    "lang": markFunction( function( lang ) {
+      // lang value must be a valid identifier
+      if ( !ridentifier.test(lang || "") ) {
+        Sizzle.error( "unsupported lang: " + lang );
+      }
+      lang = lang.replace( runescape, funescape ).toLowerCase();
+      return function( elem ) {
+        var elemLang;
+        do {
+          if ( (elemLang = documentIsHTML ?
+            elem.lang :
+            elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
+
+            elemLang = elemLang.toLowerCase();
+            return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
+          }
+        } while ( (elem = elem.parentNode) && elem.nodeType === 1 );
+        return false;
+      };
+    }),
+
+    // Miscellaneous
+    "target": function( elem ) {
+      var hash = window.location && window.location.hash;
+      return hash && hash.slice( 1 ) === elem.id;
+    },
+
+    "root": function( elem ) {
+      return elem === docElem;
+    },
+
+    "focus": function( elem ) {
+      return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
+    },
+
+    // Boolean properties
+    "enabled": function( elem ) {
+      return elem.disabled === false;
+    },
+
+    "disabled": function( elem ) {
+      return elem.disabled === true;
+    },
+
+    "checked": function( elem ) {
+      // In CSS3, :checked should return both checked and selected elements
+      // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+      var nodeName = elem.nodeName.toLowerCase();
+      return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
+    },
+
+    "selected": function( elem ) {
+      // Accessing this property makes selected-by-default
+      // options in Safari work properly
+      if ( elem.parentNode ) {
+        elem.parentNode.selectedIndex;
+      }
+
+      return elem.selected === true;
+    },
+
+    // Contents
+    "empty": function( elem ) {
+      // http://www.w3.org/TR/selectors/#empty-pseudo
+      // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
+      //   but not by others (comment: 8; processing instruction: 7; etc.)
+      // nodeType < 6 works because attributes (2) do not appear as children
+      for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+        if ( elem.nodeType < 6 ) {
+          return false;
+        }
+      }
+      return true;
+    },
+
+    "parent": function( elem ) {
+      return !Expr.pseudos["empty"]( elem );
+    },
+
+    // Element/input types
+    "header": function( elem ) {
+      return rheader.test( elem.nodeName );
+    },
+
+    "input": function( elem ) {
+      return rinputs.test( elem.nodeName );
+    },
+
+    "button": function( elem ) {
+      var name = elem.nodeName.toLowerCase();
+      return name === "input" && elem.type === "button" || name === "button";
+    },
+
+    "text": function( elem ) {
+      var attr;
+      return elem.nodeName.toLowerCase() === "input" &&
+        elem.type === "text" &&
+
+        // Support: IE<8
+        // New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
+        ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
+    },
+
+    // Position-in-collection
+    "first": createPositionalPseudo(function() {
+      return [ 0 ];
+    }),
+
+    "last": createPositionalPseudo(function( matchIndexes, length ) {
+      return [ length - 1 ];
+    }),
+
+    "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
+      return [ argument < 0 ? argument + length : argument ];
+    }),
+
+    "even": createPositionalPseudo(function( matchIndexes, length ) {
+      var i = 0;
+      for ( ; i < length; i += 2 ) {
+        matchIndexes.push( i );
+      }
+      return matchIndexes;
+    }),
+
+    "odd": createPositionalPseudo(function( matchIndexes, length ) {
+      var i = 1;
+      for ( ; i < length; i += 2 ) {
+        matchIndexes.push( i );
+      }
+      return matchIndexes;
+    }),
+
+    "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+      var i = argument < 0 ? argument + length : argument;
+      for ( ; --i >= 0; ) {
+        matchIndexes.push( i );
+      }
+      return matchIndexes;
+    }),
+
+    "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+      var i = argument < 0 ? argument + length : argument;
+      for ( ; ++i < length; ) {
+        matchIndexes.push( i );
+      }
+      return matchIndexes;
+    })
+  }
+};
+
+Expr.pseudos["nth"] = Expr.pseudos["eq"];
+
+// Add button/input type pseudos
+for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
+  Expr.pseudos[ i ] = createInputPseudo( i );
+}
+for ( i in { submit: true, reset: true } ) {
+  Expr.pseudos[ i ] = createButtonPseudo( i );
+}
+
+// Easy API for creating new setFilters
+function setFilters() {}
+setFilters.prototype = Expr.filters = Expr.pseudos;
+Expr.setFilters = new setFilters();
+
+tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
+  var matched, match, tokens, type,
+    soFar, groups, preFilters,
+    cached = tokenCache[ selector + " " ];
+
+  if ( cached ) {
+    return parseOnly ? 0 : cached.slice( 0 );
+  }
+
+  soFar = selector;
+  groups = [];
+  preFilters = Expr.preFilter;
+
+  while ( soFar ) {
+
+    // Comma and first run
+    if ( !matched || (match = rcomma.exec( soFar )) ) {
+      if ( match ) {
+        // Don't consume trailing commas as valid
+        soFar = soFar.slice( match[0].length ) || soFar;
+      }
+      groups.push( (tokens = []) );
+    }
+
+    matched = false;
+
+    // Combinators
+    if ( (match = rcombinators.exec( soFar )) ) {
+      matched = match.shift();
+      tokens.push({
+        value: matched,
+        // Cast descendant combinators to space
+        type: match[0].replace( rtrim, " " )
+      });
+      soFar = soFar.slice( matched.length );
+    }
+
+    // Filters
+    for ( type in Expr.filter ) {
+      if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
+        (match = preFilters[ type ]( match ))) ) {
+        matched = match.shift();
+        tokens.push({
+          value: matched,
+          type: type,
+          matches: match
+        });
+        soFar = soFar.slice( matched.length );
+      }
+    }
+
+    if ( !matched ) {
+      break;
+    }
+  }
+
+  // Return the length of the invalid excess
+  // if we're just parsing
+  // Otherwise, throw an error or return tokens
+  return parseOnly ?
+    soFar.length :
+    soFar ?
+      Sizzle.error( selector ) :
+      // Cache the tokens
+      tokenCache( selector, groups ).slice( 0 );
+};
+
+function toSelector( tokens ) {
+  var i = 0,
+    len = tokens.length,
+    selector = "";
+  for ( ; i < len; i++ ) {
+    selector += tokens[i].value;
+  }
+  return selector;
+}
+
+function addCombinator( matcher, combinator, base ) {
+  var dir = combinator.dir,
+    checkNonElements = base && dir === "parentNode",
+    doneName = done++;
+
+  return combinator.first ?
+    // Check against closest ancestor/preceding element
+    function( elem, context, xml ) {
+      while ( (elem = elem[ dir ]) ) {
+        if ( elem.nodeType === 1 || checkNonElements ) {
+          return matcher( elem, context, xml );
+        }
+      }
+    } :
+
+    // Check against all ancestor/preceding elements
+    function( elem, context, xml ) {
+      var oldCache, outerCache,
+        newCache = [ dirruns, doneName ];
+
+      // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
+      if ( xml ) {
+        while ( (elem = elem[ dir ]) ) {
+          if ( elem.nodeType === 1 || checkNonElements ) {
+            if ( matcher( elem, context, xml ) ) {
+              return true;
+            }
+          }
+        }
+      } else {
+        while ( (elem = elem[ dir ]) ) {
+          if ( elem.nodeType === 1 || checkNonElements ) {
+            outerCache = elem[ expando ] || (elem[ expando ] = {});
+            if ( (oldCache = outerCache[ dir ]) &&
+              oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
+
+              // Assign to newCache so results back-propagate to previous elements
+              return (newCache[ 2 ] = oldCache[ 2 ]);
+            } else {
+              // Reuse newcache so results back-propagate to previous elements
+              outerCache[ dir ] = newCache;
+
+              // A match means we're done; a fail means we have to keep checking
+              if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
+                return true;
+              }
+            }
+          }
+        }
+      }
+    };
+}
+
+function elementMatcher( matchers ) {
+  return matchers.length > 1 ?
+    function( elem, context, xml ) {
+      var i = matchers.length;
+      while ( i-- ) {
+        if ( !matchers[i]( elem, context, xml ) ) {
+          return false;
+        }
+      }
+      return true;
+    } :
+    matchers[0];
+}
+
+function multipleContexts( selector, contexts, results ) {
+  var i = 0,
+    len = contexts.length;
+  for ( ; i < len; i++ ) {
+    Sizzle( selector, contexts[i], results );
+  }
+  return results;
+}
+
+function condense( unmatched, map, filter, context, xml ) {
+  var elem,
+    newUnmatched = [],
+    i = 0,
+    len = unmatched.length,
+    mapped = map != null;
+
+  for ( ; i < len; i++ ) {
+    if ( (elem = unmatched[i]) ) {
+      if ( !filter || filter( elem, context, xml ) ) {
+        newUnmatched.push( elem );
+        if ( mapped ) {
+          map.push( i );
+        }
+      }
+    }
+  }
+
+  return newUnmatched;
+}
+
+function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
+  if ( postFilter && !postFilter[ expando ] ) {
+    postFilter = setMatcher( postFilter );
+  }
+  if ( postFinder && !postFinder[ expando ] ) {
+    postFinder = setMatcher( postFinder, postSelector );
+  }
+  return markFunction(function( seed, results, context, xml ) {
+    var temp, i, elem,
+      preMap = [],
+      postMap = [],
+      preexisting = results.length,
+
+      // Get initial elements from seed or context
+      elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
+
+      // Prefilter to get matcher input, preserving a map for seed-results synchronization
+      matcherIn = preFilter && ( seed || !selector ) ?
+        condense( elems, preMap, preFilter, context, xml ) :
+        elems,
+
+      matcherOut = matcher ?
+        // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
+        postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
+
+          // ...intermediate processing is necessary
+          [] :
+
+          // ...otherwise use results directly
+          results :
+        matcherIn;
+
+    // Find primary matches
+    if ( matcher ) {
+      matcher( matcherIn, matcherOut, context, xml );
+    }
+
+    // Apply postFilter
+    if ( postFilter ) {
+      temp = condense( matcherOut, postMap );
+      postFilter( temp, [], context, xml );
+
+      // Un-match failing elements by moving them back to matcherIn
+      i = temp.length;
+      while ( i-- ) {
+        if ( (elem = temp[i]) ) {
+          matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
+        }
+      }
+    }
+
+    if ( seed ) {
+      if ( postFinder || preFilter ) {
+        if ( postFinder ) {
+          // Get the final matcherOut by condensing this intermediate into postFinder contexts
+          temp = [];
+          i = matcherOut.length;
+          while ( i-- ) {
+            if ( (elem = matcherOut[i]) ) {
+              // Restore matcherIn since elem is not yet a final match
+              temp.push( (matcherIn[i] = elem) );
+            }
+          }
+          postFinder( null, (matcherOut = []), temp, xml );
+        }
+
+        // Move matched elements from seed to results to keep them synchronized
+        i = matcherOut.length;
+        while ( i-- ) {
+          if ( (elem = matcherOut[i]) &&
+            (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
+
+            seed[temp] = !(results[temp] = elem);
+          }
+        }
+      }
+
+    // Add elements to results, through postFinder if defined
+    } else {
+      matcherOut = condense(
+        matcherOut === results ?
+          matcherOut.splice( preexisting, matcherOut.length ) :
+          matcherOut
+      );
+      if ( postFinder ) {
+        postFinder( null, results, matcherOut, xml );
+      } else {
+        push.apply( results, matcherOut );
+      }
+    }
+  });
+}
+
+function matcherFromTokens( tokens ) {
+  var checkContext, matcher, j,
+    len = tokens.length,
+    leadingRelative = Expr.relative[ tokens[0].type ],
+    implicitRelative = leadingRelative || Expr.relative[" "],
+    i = leadingRelative ? 1 : 0,
+
+    // The foundational matcher ensures that elements are reachable from top-level context(s)
+    matchContext = addCombinator( function( elem ) {
+      return elem === checkContext;
+    }, implicitRelative, true ),
+    matchAnyContext = addCombinator( function( elem ) {
+      return indexOf.call( checkContext, elem ) > -1;
+    }, implicitRelative, true ),
+    matchers = [ function( elem, context, xml ) {
+      return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
+        (checkContext = context).nodeType ?
+          matchContext( elem, context, xml ) :
+          matchAnyContext( elem, context, xml ) );
+    } ];
+
+  for ( ; i < len; i++ ) {
+    if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
+      matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
+    } else {
+      matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
+
+      // Return special upon seeing a positional matcher
+      if ( matcher[ expando ] ) {
+        // Find the next relative operator (if any) for proper handling
+        j = ++i;
+        for ( ; j < len; j++ ) {
+          if ( Expr.relative[ tokens[j].type ] ) {
+            break;
+          }
+        }
+        return setMatcher(
+          i > 1 && elementMatcher( matchers ),
+          i > 1 && toSelector(
+            // If the preceding token was a descendant combinator, insert an implicit any-element `*`
+            tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
+          ).replace( rtrim, "$1" ),
+          matcher,
+          i < j && matcherFromTokens( tokens.slice( i, j ) ),
+          j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
+          j < len && toSelector( tokens )
+        );
+      }
+      matchers.push( matcher );
+    }
+  }
+
+  return elementMatcher( matchers );
+}
+
+function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
+  var bySet = setMatchers.length > 0,
+    byElement = elementMatchers.length > 0,
+    superMatcher = function( seed, context, xml, results, outermost ) {
+      var elem, j, matcher,
+        matchedCount = 0,
+        i = "0",
+        unmatched = seed && [],
+        setMatched = [],
+        contextBackup = outermostContext,
+        // We must always have either seed elements or outermost context
+        elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
+        // Use integer dirruns iff this is the outermost matcher
+        dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
+        len = elems.length;
+
+      if ( outermost ) {
+        outermostContext = context !== document && context;
+      }
+
+      // Add elements passing elementMatchers directly to results
+      // Keep `i` a string if there are no elements so `matchedCount` will be "00" below
+      // Support: IE<9, Safari
+      // Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
+      for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
+        if ( byElement && elem ) {
+          j = 0;
+          while ( (matcher = elementMatchers[j++]) ) {
+            if ( matcher( elem, context, xml ) ) {
+              results.push( elem );
+              break;
+            }
+          }
+          if ( outermost ) {
+            dirruns = dirrunsUnique;
+          }
+        }
+
+        // Track unmatched elements for set filters
+        if ( bySet ) {
+          // They will have gone through all possible matchers
+          if ( (elem = !matcher && elem) ) {
+            matchedCount--;
+          }
+
+          // Lengthen the array for every element, matched or not
+          if ( seed ) {
+            unmatched.push( elem );
+          }
+        }
+      }
+
+      // Apply set filters to unmatched elements
+      matchedCount += i;
+      if ( bySet && i !== matchedCount ) {
+        j = 0;
+        while ( (matcher = setMatchers[j++]) ) {
+          matcher( unmatched, setMatched, context, xml );
+        }
+
+        if ( seed ) {
+          // Reintegrate element matches to eliminate the need for sorting
+          if ( matchedCount > 0 ) {
+            while ( i-- ) {
+              if ( !(unmatched[i] || setMatched[i]) ) {
+                setMatched[i] = pop.call( results );
+              }
+            }
+          }
+
+          // Discard index placeholder values to get only actual matches
+          setMatched = condense( setMatched );
+        }
+
+        // Add matches to results
+        push.apply( results, setMatched );
+
+        // Seedless set matches succeeding multiple successful matchers stipulate sorting
+        if ( outermost && !seed && setMatched.length > 0 &&
+          ( matchedCount + setMatchers.length ) > 1 ) {
+
+          Sizzle.uniqueSort( results );
+        }
+      }
+
+      // Override manipulation of globals by nested matchers
+      if ( outermost ) {
+        dirruns = dirrunsUnique;
+        outermostContext = contextBackup;
+      }
+
+      return unmatched;
+    };
+
+  return bySet ?
+    markFunction( superMatcher ) :
+    superMatcher;
+}
+
+compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
+  var i,
+    setMatchers = [],
+    elementMatchers = [],
+    cached = compilerCache[ selector + " " ];
+
+  if ( !cached ) {
+    // Generate a function of recursive functions that can be used to check each element
+    if ( !match ) {
+      match = tokenize( selector );
+    }
+    i = match.length;
+    while ( i-- ) {
+      cached = matcherFromTokens( match[i] );
+      if ( cached[ expando ] ) {
+        setMatchers.push( cached );
+      } else {
+        elementMatchers.push( cached );
+      }
+    }
+
+    // Cache the compiled function
+    cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
+
+    // Save selector and tokenization
+    cached.selector = selector;
+  }
+  return cached;
+};
+
+/**
+ * A low-level selection function that works with Sizzle's compiled
+ *  selector functions
+ * @param {String|Function} selector A selector or a pre-compiled
+ *  selector function built with Sizzle.compile
+ * @param {Element} context
+ * @param {Array} [results]
+ * @param {Array} [seed] A set of elements to match against
+ */
+select = Sizzle.select = function( selector, context, results, seed ) {
+  var i, tokens, token, type, find,
+    compiled = typeof selector === "function" && selector,
+    match = !seed && tokenize( (selector = compiled.selector || selector) );
+
+  results = results || [];
+
+  // Try to minimize operations if there is no seed and only one group
+  if ( match.length === 1 ) {
+
+    // Take a shortcut and set the context if the root selector is an ID
+    tokens = match[0] = match[0].slice( 0 );
+    if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
+        support.getById && context.nodeType === 9 && documentIsHTML &&
+        Expr.relative[ tokens[1].type ] ) {
+
+      context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
+      if ( !context ) {
+        return results;
+
+      // Precompiled matchers will still verify ancestry, so step up a level
+      } else if ( compiled ) {
+        context = context.parentNode;
+      }
+
+      selector = selector.slice( tokens.shift().value.length );
+    }
+
+    // Fetch a seed set for right-to-left matching
+    i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
+    while ( i-- ) {
+      token = tokens[i];
+
+      // Abort if we hit a combinator
+      if ( Expr.relative[ (type = token.type) ] ) {
+        break;
+      }
+      if ( (find = Expr.find[ type ]) ) {
+        // Search, expanding context for leading sibling combinators
+        if ( (seed = find(
+          token.matches[0].replace( runescape, funescape ),
+          rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
+        )) ) {
+
+          // If seed is empty or no tokens remain, we can return early
+          tokens.splice( i, 1 );
+          selector = seed.length && toSelector( tokens );
+          if ( !selector ) {
+            push.apply( results, seed );
+            return results;
+          }
+
+          break;
+        }
+      }
+    }
+  }
+
+  // Compile and execute a filtering function if one is not provided
+  // Provide `match` to avoid retokenization if we modified the selector above
+  ( compiled || compile( selector, match ) )(
+    seed,
+    context,
+    !documentIsHTML,
+    results,
+    rsibling.test( selector ) && testContext( context.parentNode ) || context
+  );
+  return results;
+};
+
+// One-time assignments
+
+// Sort stability
+support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
+
+// Support: Chrome<14
+// Always assume duplicates if they aren't passed to the comparison function
+support.detectDuplicates = !!hasDuplicate;
+
+// Initialize against the default document
+setDocument();
+
+// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
+// Detached nodes confoundingly follow *each other*
+support.sortDetached = assert(function( div1 ) {
+  // Should return 1, but returns 4 (following)
+  return div1.compareDocumentPosition( document.createElement("div") ) & 1;
+});
+
+// Support: IE<8
+// Prevent attribute/property "interpolation"
+// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
+if ( !assert(function( div ) {
+  div.innerHTML = "<a href='#'></a>";
+  return div.firstChild.getAttribute("href") === "#" ;
+}) ) {
+  addHandle( "type|href|height|width", function( elem, name, isXML ) {
+    if ( !isXML ) {
+      return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
+    }
+  });
+}
+
+// Support: IE<9
+// Use defaultValue in place of getAttribute("value")
+if ( !support.attributes || !assert(function( div ) {
+  div.innerHTML = "<input/>";
+  div.firstChild.setAttribute( "value", "" );
+  return div.firstChild.getAttribute( "value" ) === "";
+}) ) {
+  addHandle( "value", function( elem, name, isXML ) {
+    if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
+      return elem.defaultValue;
+    }
+  });
+}
+
+// Support: IE<9
+// Use getAttributeNode to fetch booleans when getAttribute lies
+if ( !assert(function( div ) {
+  return div.getAttribute("disabled") == null;
+}) ) {
+  addHandle( booleans, function( elem, name, isXML ) {
+    var val;
+    if ( !isXML ) {
+      return elem[ name ] === true ? name.toLowerCase() :
+          (val = elem.getAttributeNode( name )) && val.specified ?
+          val.value :
+        null;
+    }
+  });
+}
+
+return Sizzle;
+
+})( window );
+
+
+
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.pseudos;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = Sizzle.getText;
+jQuery.isXMLDoc = Sizzle.isXML;
+jQuery.contains = Sizzle.contains;
+
+
+
+var rneedsContext = jQuery.expr.match.needsContext;
+
+var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/);
+
+
+
+var risSimple = /^.[^:#\[\.,]*$/;
+
+// Implement the identical functionality for filter and not
+function winnow( elements, qualifier, not ) {
+  if ( jQuery.isFunction( qualifier ) ) {
+    return jQuery.grep( elements, function( elem, i ) {
+      /* jshint -W018 */
+      return !!qualifier.call( elem, i, elem ) !== not;
+    });
+
+  }
+
+  if ( qualifier.nodeType ) {
+    return jQuery.grep( elements, function( elem ) {
+      return ( elem === qualifier ) !== not;
+    });
+
+  }
+
+  if ( typeof qualifier === "string" ) {
+    if ( risSimple.test( qualifier ) ) {
+      return jQuery.filter( qualifier, elements, not );
+    }
+
+    qualifier = jQuery.filter( qualifier, elements );
+  }
+
+  return jQuery.grep( elements, function( elem ) {
+    return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not;
+  });
+}
+
+jQuery.filter = function( expr, elems, not ) {
+  var elem = elems[ 0 ];
+
+  if ( not ) {
+    expr = ":not(" + expr + ")";
+  }
+
+  return elems.length === 1 && elem.nodeType === 1 ?
+    jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
+    jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
+      return elem.nodeType === 1;
+    }));
+};
+
+jQuery.fn.extend({
+  find: function( selector ) {
+    var i,
+      ret = [],
+      self = this,
+      len = self.length;
+
+    if ( typeof selector !== "string" ) {
+      return this.pushStack( jQuery( selector ).filter(function() {
+        for ( i = 0; i < len; i++ ) {
+          if ( jQuery.contains( self[ i ], this ) ) {
+            return true;
+          }
+        }
+      }) );
+    }
+
+    for ( i = 0; i < len; i++ ) {
+      jQuery.find( selector, self[ i ], ret );
+    }
+
+    // Needed because $( selector, context ) becomes $( context ).find( selector )
+    ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
+    ret.selector = this.selector ? this.selector + " " + selector : selector;
+    return ret;
+  },
+  filter: function( selector ) {
+    return this.pushStack( winnow(this, selector || [], false) );
+  },
+  not: function( selector ) {
+    return this.pushStack( winnow(this, selector || [], true) );
+  },
+  is: function( selector ) {
+    return !!winnow(
+      this,
+
+      // If this is a positional/relative selector, check membership in the returned set
+      // so $("p:first").is("p:last") won't return true for a doc with two "p".
+      typeof selector === "string" && rneedsContext.test( selector ) ?
+        jQuery( selector ) :
+        selector || [],
+      false
+    ).length;
+  }
+});
+
+
+// Initialize a jQuery object
+
+
+// A central reference to the root jQuery(document)
+var rootjQuery,
+
+  // Use the correct document accordingly with window argument (sandbox)
+  document = window.document,
+
+  // A simple way to check for HTML strings
+  // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
+  // Strict HTML recognition (#11290: must start with <)
+  rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
+
+  init = jQuery.fn.init = function( selector, context ) {
+    var match, elem;
+
+    // HANDLE: $(""), $(null), $(undefined), $(false)
+    if ( !selector ) {
+      return this;
+    }
+
+    // Handle HTML strings
+    if ( typeof selector === "string" ) {
+      if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
+        // Assume that strings that start and end with <> are HTML and skip the regex check
+        match = [ null, selector, null ];
+
+      } else {
+        match = rquickExpr.exec( selector );
+      }
+
+      // Match html or make sure no context is specified for #id
+      if ( match && (match[1] || !context) ) {
+
+        // HANDLE: $(html) -> $(array)
+        if ( match[1] ) {
+          context = context instanceof jQuery ? context[0] : context;
+
+          // scripts is true for back-compat
+          // Intentionally let the error be thrown if parseHTML is not present
+          jQuery.merge( this, jQuery.parseHTML(
+            match[1],
+            context && context.nodeType ? context.ownerDocument || context : document,
+            true
+          ) );
+
+          // HANDLE: $(html, props)
+          if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
+            for ( match in context ) {
+              // Properties of context are called as methods if possible
+              if ( jQuery.isFunction( this[ match ] ) ) {
+                this[ match ]( context[ match ] );
+
+              // ...and otherwise set as attributes
+              } else {
+                this.attr( match, context[ match ] );
+              }
+            }
+          }
+
+          return this;
+
+        // HANDLE: $(#id)
+        } else {
+          elem = document.getElementById( match[2] );
+
+          // Check parentNode to catch when Blackberry 4.6 returns
+          // nodes that are no longer in the document #6963
+          if ( elem && elem.parentNode ) {
+            // Handle the case where IE and Opera return items
+            // by name instead of ID
+            if ( elem.id !== match[2] ) {
+              return rootjQuery.find( selector );
+            }
+
+            // Otherwise, we inject the element directly into the jQuery object
+            this.length = 1;
+            this[0] = elem;
+          }
+
+          this.context = document;
+          this.selector = selector;
+          return this;
+        }
+
+      // HANDLE: $(expr, $(...))
+      } else if ( !context || context.jquery ) {
+        return ( context || rootjQuery ).find( selector );
+
+      // HANDLE: $(expr, context)
+      // (which is just equivalent to: $(context).find(expr)
+      } else {
+        return this.constructor( context ).find( selector );
+      }
+
+    // HANDLE: $(DOMElement)
+    } else if ( selector.nodeType ) {
+      this.context = this[0] = selector;
+      this.length = 1;
+      return this;
+
+    // HANDLE: $(function)
+    // Shortcut for document ready
+    } else if ( jQuery.isFunction( selector ) ) {
+      return typeof rootjQuery.ready !== "undefined" ?
+        rootjQuery.ready( selector ) :
+        // Execute immediately if ready is not present
+        selector( jQuery );
+    }
+
+    if ( selector.selector !== undefined ) {
+      this.selector = selector.selector;
+      this.context = selector.context;
+    }
+
+    return jQuery.makeArray( selector, this );
+  };
+
+// Give the init function the jQuery prototype for later instantiation
+init.prototype = jQuery.fn;
+
+// Initialize central reference
+rootjQuery = jQuery( document );
+
+
+var rparentsprev = /^(?:parents|prev(?:Until|All))/,
+  // methods guaranteed to produce a unique set when starting from a unique set
+  guaranteedUnique = {
+    children: true,
+    contents: true,
+    next: true,
+    prev: true
+  };
+
+jQuery.extend({
+  dir: function( elem, dir, until ) {
+    var matched = [],
+      cur = elem[ dir ];
+
+    while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
+      if ( cur.nodeType === 1 ) {
+        matched.push( cur );
+      }
+      cur = cur[dir];
+    }
+    return matched;
+  },
+
+  sibling: function( n, elem ) {
+    var r = [];
+
+    for ( ; n; n = n.nextSibling ) {
+      if ( n.nodeType === 1 && n !== elem ) {
+        r.push( n );
+      }
+    }
+
+    return r;
+  }
+});
+
+jQuery.fn.extend({
+  has: function( target ) {
+    var i,
+      targets = jQuery( target, this ),
+      len = targets.length;
+
+    return this.filter(function() {
+      for ( i = 0; i < len; i++ ) {
+        if ( jQuery.contains( this, targets[i] ) ) {
+          return true;
+        }
+      }
+    });
+  },
+
+  closest: function( selectors, context ) {
+    var cur,
+      i = 0,
+      l = this.length,
+      matched = [],
+      pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
+        jQuery( selectors, context || this.context ) :
+        0;
+
+    for ( ; i < l; i++ ) {
+      for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) {
+        // Always skip document fragments
+        if ( cur.nodeType < 11 && (pos ?
+          pos.index(cur) > -1 :
+
+          // Don't pass non-elements to Sizzle
+          cur.nodeType === 1 &&
+            jQuery.find.matchesSelector(cur, selectors)) ) {
+
+          matched.push( cur );
+          break;
+        }
+      }
+    }
+
+    return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched );
+  },
+
+  // Determine the position of an element within
+  // the matched set of elements
+  index: function( elem ) {
+
+    // No argument, return index in parent
+    if ( !elem ) {
+      return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1;
+    }
+
+    // index in selector
+    if ( typeof elem === "string" ) {
+      return jQuery.inArray( this[0], jQuery( elem ) );
+    }
+
+    // Locate the position of the desired element
+    return jQuery.inArray(
+      // If it receives a jQuery object, the first element is used
+      elem.jquery ? elem[0] : elem, this );
+  },
+
+  add: function( selector, context ) {
+    return this.pushStack(
+      jQuery.unique(
+        jQuery.merge( this.get(), jQuery( selector, context ) )
+      )
+    );
+  },
+
+  addBack: function( selector ) {
+    return this.add( selector == null ?
+      this.prevObject : this.prevObject.filter(selector)
+    );
+  }
+});
+
+function sibling( cur, dir ) {
+  do {
+    cur = cur[ dir ];
+  } while ( cur && cur.nodeType !== 1 );
+
+  return cur;
+}
+
+jQuery.each({
+  parent: function( elem ) {
+    var parent = elem.parentNode;
+    return parent && parent.nodeType !== 11 ? parent : null;
+  },
+  parents: function( elem ) {
+    return jQuery.dir( elem, "parentNode" );
+  },
+  parentsUntil: function( elem, i, until ) {
+    return jQuery.dir( elem, "parentNode", until );
+  },
+  next: function( elem ) {
+    return sibling( elem, "nextSibling" );
+  },
+  prev: function( elem ) {
+    return sibling( elem, "previousSibling" );
+  },
+  nextAll: function( elem ) {
+    return jQuery.dir( elem, "nextSibling" );
+  },
+  prevAll: function( elem ) {
+    return jQuery.dir( elem, "previousSibling" );
+  },
+  nextUntil: function( elem, i, until ) {
+    return jQuery.dir( elem, "nextSibling", until );
+  },
+  prevUntil: function( elem, i, until ) {
+    return jQuery.dir( elem, "previousSibling", until );
+  },
+  siblings: function( elem ) {
+    return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
+  },
+  children: function( elem ) {
+    return jQuery.sibling( elem.firstChild );
+  },
+  contents: function( elem ) {
+    return jQuery.nodeName( elem, "iframe" ) ?
+      elem.contentDocument || elem.contentWindow.document :
+      jQuery.merge( [], elem.childNodes );
+  }
+}, function( name, fn ) {
+  jQuery.fn[ name ] = function( until, selector ) {
+    var ret = jQuery.map( this, fn, until );
+
+    if ( name.slice( -5 ) !== "Until" ) {
+      selector = until;
+    }
+
+    if ( selector && typeof selector === "string" ) {
+      ret = jQuery.filter( selector, ret );
+    }
+
+    if ( this.length > 1 ) {
+      // Remove duplicates
+      if ( !guaranteedUnique[ name ] ) {
+        ret = jQuery.unique( ret );
+      }
+
+      // Reverse order for parents* and prev-derivatives
+      if ( rparentsprev.test( name ) ) {
+        ret = ret.reverse();
+      }
+    }
+
+    return this.pushStack( ret );
+  };
+});
+var rnotwhite = (/\S+/g);
+
+
+
+// String to Object options format cache
+var optionsCache = {};
+
+// Convert String-formatted options into Object-formatted ones and store in cache
+function createOptions( options ) {
+  var object = optionsCache[ options ] = {};
+  jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
+    object[ flag ] = true;
+  });
+  return object;
+}
+
+/*
+ * Create a callback list using the following parameters:
+ *
+ *  options: an optional list of space-separated options that will change how
+ *      the callback list behaves or a more traditional option object
+ *
+ * By default a callback list will act like an event callback list and can be
+ * "fired" multiple times.
+ *
+ * Possible options:
+ *
+ *  once:     will ensure the callback list can only be fired once (like a Deferred)
+ *
+ *  memory:     will keep track of previous values and will call any callback added
+ *          after the list has been fired right away with the latest "memorized"
+ *          values (like a Deferred)
+ *
+ *  unique:     will ensure a callback can only be added once (no duplicate in the list)
+ *
+ *  stopOnFalse:  interrupt callings when a callback returns false
+ *
+ */
+jQuery.Callbacks = function( options ) {
+
+  // Convert options from String-formatted to Object-formatted if needed
+  // (we check in cache first)
+  options = typeof options === "string" ?
+    ( optionsCache[ options ] || createOptions( options ) ) :
+    jQuery.extend( {}, options );
+
+  var // Flag to know if list is currently firing
+    firing,
+    // Last fire value (for non-forgettable lists)
+    memory,
+    // Flag to know if list was already fired
+    fired,
+    // End of the loop when firing
+    firingLength,
+    // Index of currently firing callback (modified by remove if needed)
+    firingIndex,
+    // First callback to fire (used internally by add and fireWith)
+    firingStart,
+    // Actual callback list
+    list = [],
+    // Stack of fire calls for repeatable lists
+    stack = !options.once && [],
+    // Fire callbacks
+    fire = function( data ) {
+      memory = options.memory && data;
+      fired = true;
+      firingIndex = firingStart || 0;
+      firingStart = 0;
+      firingLength = list.length;
+      firing = true;
+      for ( ; list && firingIndex < firingLength; firingIndex++ ) {
+        if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
+          memory = false; // To prevent further calls using add
+          break;
+        }
+      }
+      firing = false;
+      if ( list ) {
+        if ( stack ) {
+          if ( stack.length ) {
+            fire( stack.shift() );
+          }
+        } else if ( memory ) {
+          list = [];
+        } else {
+          self.disable();
+        }
+      }
+    },
+    // Actual Callbacks object
+    self = {
+      // Add a callback or a collection of callbacks to the list
+      add: function() {
+        if ( list ) {
+          // First, we save the current length
+          var start = list.length;
+          (function add( args ) {
+            jQuery.each( args, function( _, arg ) {
+              var type = jQuery.type( arg );
+              if ( type === "function" ) {
+                if ( !options.unique || !self.has( arg ) ) {
+                  list.push( arg );
+                }
+              } else if ( arg && arg.length && type !== "string" ) {
+                // Inspect recursively
+                add( arg );
+              }
+            });
+          })( arguments );
+          // Do we need to add the callbacks to the
+          // current firing batch?
+          if ( firing ) {
+            firingLength = list.length;
+          // With memory, if we're not firing then
+          // we should call right away
+          } else if ( memory ) {
+            firingStart = start;
+            fire( memory );
+          }
+        }
+        return this;
+      },
+      // Remove a callback from the list
+      remove: function() {
+        if ( list ) {
+          jQuery.each( arguments, function( _, arg ) {
+            var index;
+            while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
+              list.splice( index, 1 );
+              // Handle firing indexes
+              if ( firing ) {
+                if ( index <= firingLength ) {
+                  firingLength--;
+                }
+                if ( index <= firingIndex ) {
+                  firingIndex--;
+                }
+              }
+            }
+          });
+        }
+        return this;
+      },
+      // Check if a given callback is in the list.
+      // If no argument is given, return whether or not list has callbacks attached.
+      has: function( fn ) {
+        return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
+      },
+      // Remove all callbacks from the list
+      empty: function() {
+        list = [];
+        firingLength = 0;
+        return this;
+      },
+      // Have the list do nothing anymore
+      disable: function() {
+        list = stack = memory = undefined;
+        return this;
+      },
+      // Is it disabled?
+      disabled: function() {
+        return !list;
+      },
+      // Lock the list in its current state
+      lock: function() {
+        stack = undefined;
+        if ( !memory ) {
+          self.disable();
+        }
+        return this;
+      },
+      // Is it locked?
+      locked: function() {
+        return !stack;
+      },
+      // Call all callbacks with the given context and arguments
+      fireWith: function( context, args ) {
+        if ( list && ( !fired || stack ) ) {
+          args = args || [];
+          args = [ context, args.slice ? args.slice() : args ];
+          if ( firing ) {
+            stack.push( args );
+          } else {
+            fire( args );
+          }
+        }
+        return this;
+      },
+      // Call all the callbacks with the given arguments
+      fire: function() {
+        self.fireWith( this, arguments );
+        return this;
+      },
+      // To know if the callbacks have already been called at least once
+      fired: function() {
+        return !!fired;
+      }
+    };
+
+  return self;
+};
+
+
+jQuery.extend({
+
+  Deferred: function( func ) {
+    var tuples = [
+        // action, add listener, listener list, final state
+        [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
+        [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
+        [ "notify", "progress", jQuery.Callbacks("memory") ]
+      ],
+      state = "pending",
+      promise = {
+        state: function() {
+          return state;
+        },
+        always: function() {
+          deferred.done( arguments ).fail( arguments );
+          return this;
+        },
+        then: function( /* fnDone, fnFail, fnProgress */ ) {
+          var fns = arguments;
+          return jQuery.Deferred(function( newDefer ) {
+            jQuery.each( tuples, function( i, tuple ) {
+              var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
+              // deferred[ done | fail | progress ] for forwarding actions to newDefer
+              deferred[ tuple[1] ](function() {
+                var returned = fn && fn.apply( this, arguments );
+                if ( returned && jQuery.isFunction( returned.promise ) ) {
+                  returned.promise()
+                    .done( newDefer.resolve )
+                    .fail( newDefer.reject )
+                    .progress( newDefer.notify );
+                } else {
+                  newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
+                }
+              });
+            });
+            fns = null;
+          }).promise();
+        },
+        // Get a promise for this deferred
+        // If obj is provided, the promise aspect is added to the object
+        promise: function( obj ) {
+          return obj != null ? jQuery.extend( obj, promise ) : promise;
+        }
+      },
+      deferred = {};
+
+    // Keep pipe for back-compat
+    promise.pipe = promise.then;
+
+    // Add list-specific methods
+    jQuery.each( tuples, function( i, tuple ) {
+      var list = tuple[ 2 ],
+        stateString = tuple[ 3 ];
+
+      // promise[ done | fail | progress ] = list.add
+      promise[ tuple[1] ] = list.add;
+
+      // Handle state
+      if ( stateString ) {
+        list.add(function() {
+          // state = [ resolved | rejected ]
+          state = stateString;
+
+        // [ reject_list | resolve_list ].disable; progress_list.lock
+        }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
+      }
+
+      // deferred[ resolve | reject | notify ]
+      deferred[ tuple[0] ] = function() {
+        deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
+        return this;
+      };
+      deferred[ tuple[0] + "With" ] = list.fireWith;
+    });
+
+    // Make the deferred a promise
+    promise.promise( deferred );
+
+    // Call given func if any
+    if ( func ) {
+      func.call( deferred, deferred );
+    }
+
+    // All done!
+    return deferred;
+  },
+
+  // Deferred helper
+  when: function( subordinate /* , ..., subordinateN */ ) {
+    var i = 0,
+      resolveValues = slice.call( arguments ),
+      length = resolveValues.length,
+
+      // the count of uncompleted subordinates
+      remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
+
+      // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
+      deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
+
+      // Update function for both resolve and progress values
+      updateFunc = function( i, contexts, values ) {
+        return function( value ) {
+          contexts[ i ] = this;
+          values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
+          if ( values === progressValues ) {
+            deferred.notifyWith( contexts, values );
+
+          } else if ( !(--remaining) ) {
+            deferred.resolveWith( contexts, values );
+          }
+        };
+      },
+
+      progressValues, progressContexts, resolveContexts;
+
+    // add listeners to Deferred subordinates; treat others as resolved
+    if ( length > 1 ) {
+      progressValues = new Array( length );
+      progressContexts = new Array( length );
+      resolveContexts = new Array( length );
+      for ( ; i < length; i++ ) {
+        if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
+          resolveValues[ i ].promise()
+            .done( updateFunc( i, resolveContexts, resolveValues ) )
+            .fail( deferred.reject )
+            .progress( updateFunc( i, progressContexts, progressValues ) );
+        } else {
+          --remaining;
+        }
+      }
+    }
+
+    // if we're not waiting on anything, resolve the master
+    if ( !remaining ) {
+      deferred.resolveWith( resolveContexts, resolveValues );
+    }
+
+    return deferred.promise();
+  }
+});
+
+
+// The deferred used on DOM ready
+var readyList;
+
+jQuery.fn.ready = function( fn ) {
+  // Add the callback
+  jQuery.ready.promise().done( fn );
+
+  return this;
+};
+
+jQuery.extend({
+  // Is the DOM ready to be used? Set to true once it occurs.
+  isReady: false,
+
+  // A counter to track how many items to wait for before
+  // the ready event fires. See #6781
+  readyWait: 1,
+
+  // Hold (or release) the ready event
+  holdReady: function( hold ) {
+    if ( hold ) {
+      jQuery.readyWait++;
+    } else {
+      jQuery.ready( true );
+    }
+  },
+
+  // Handle when the DOM is ready
+  ready: function( wait ) {
+
+    // Abort if there are pending holds or we're already ready
+    if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
+      return;
+    }
+
+    // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+    if ( !document.body ) {
+      return setTimeout( jQuery.ready );
+    }
+
+    // Remember that the DOM is ready
+    jQuery.isReady = true;
+
+    // If a normal DOM Ready event fired, decrement, and wait if need be
+    if ( wait !== true && --jQuery.readyWait > 0 ) {
+      return;
+    }
+
+    // If there are functions bound, to execute
+    readyList.resolveWith( document, [ jQuery ] );
+
+    // Trigger any bound ready events
+    if ( jQuery.fn.triggerHandler ) {
+      jQuery( document ).triggerHandler( "ready" );
+      jQuery( document ).off( "ready" );
+    }
+  }
+});
+
+/**
+ * Clean-up method for dom ready events
+ */
+function detach() {
+  if ( document.addEventListener ) {
+    document.removeEventListener( "DOMContentLoaded", completed, false );
+    window.removeEventListener( "load", completed, false );
+
+  } else {
+    document.detachEvent( "onreadystatechange", completed );
+    window.detachEvent( "onload", completed );
+  }
+}
+
+/**
+ * The ready event handler and self cleanup method
+ */
+function completed() {
+  // readyState === "complete" is good enough for us to call the dom ready in oldIE
+  if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) {
+    detach();
+    jQuery.ready();
+  }
+}
+
+jQuery.ready.promise = function( obj ) {
+  if ( !readyList ) {
+
+    readyList = jQuery.Deferred();
+
+    // Catch cases where $(document).ready() is called after the browser event has already occurred.
+    // we once tried to use readyState "interactive" here, but it caused issues like the one
+    // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
+    if ( document.readyState === "complete" ) {
+      // Handle it asynchronously to allow scripts the opportunity to delay ready
+      setTimeout( jQuery.ready );
+
+    // Standards-based browsers support DOMContentLoaded
+    } else if ( document.addEventListener ) {
+      // Use the handy event callback
+      document.addEventListener( "DOMContentLoaded", completed, false );
+
+      // A fallback to window.onload, that will always work
+      window.addEventListener( "load", completed, false );
+
+    // If IE event model is used
+    } else {
+      // Ensure firing before onload, maybe late but safe also for iframes
+      document.attachEvent( "onreadystatechange", completed );
+
+      // A fallback to window.onload, that will always work
+      window.attachEvent( "onload", completed );
+
+      // If IE and not a frame
+      // continually check to see if the document is ready
+      var top = false;
+
+      try {
+        top = window.frameElement == null && document.documentElement;
+      } catch(e) {}
+
+      if ( top && top.doScroll ) {
+        (function doScrollCheck() {
+          if ( !jQuery.isReady ) {
+
+            try {
+              // Use the trick by Diego Perini
+              // http://javascript.nwbox.com/IEContentLoaded/
+              top.doScroll("left");
+            } catch(e) {
+              return setTimeout( doScrollCheck, 50 );
+            }
+
+            // detach all dom ready events
+            detach();
+
+            // and execute any waiting functions
+            jQuery.ready();
+          }
+        })();
+      }
+    }
+  }
+  return readyList.promise( obj );
+};
+
+
+var strundefined = typeof undefined;
+
+
+
+// Support: IE<9
+// Iteration over object's inherited properties before its own
+var i;
+for ( i in jQuery( support ) ) {
+  break;
+}
+support.ownLast = i !== "0";
+
+// Note: most support tests are defined in their respective modules.
+// false until the test is run
+support.inlineBlockNeedsLayout = false;
+
+// Execute ASAP in case we need to set body.style.zoom
+jQuery(function() {
+  // Minified: var a,b,c,d
+  var val, div, body, container;
+
+  body = document.getElementsByTagName( "body" )[ 0 ];
+  if ( !body || !body.style ) {
+    // Return for frameset docs that don't have a body
+    return;
+  }
+
+  // Setup
+  div = document.createElement( "div" );
+  container = document.createElement( "div" );
+  container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px";
+  body.appendChild( container ).appendChild( div );
+
+  if ( typeof div.style.zoom !== strundefined ) {
+    // Support: IE<8
+    // Check if natively block-level elements act like inline-block
+    // elements when setting their display to 'inline' and giving
+    // them layout
+    div.style.cssText = "display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1";
+
+    support.inlineBlockNeedsLayout = val = div.offsetWidth === 3;
+    if ( val ) {
+      // Prevent IE 6 from affecting layout for positioned elements #11048
+      // Prevent IE from shrinking the body in IE 7 mode #12869
+      // Support: IE<8
+      body.style.zoom = 1;
+    }
+  }
+
+  body.removeChild( container );
+});
+
+
+
+
+(function() {
+  var div = document.createElement( "div" );
+
+  // Execute the test only if not already executed in another module.
+  if (support.deleteExpando == null) {
+    // Support: IE<9
+    support.deleteExpando = true;
+    try {
+      delete div.test;
+    } catch( e ) {
+      support.deleteExpando = false;
+    }
+  }
+
+  // Null elements to avoid leaks in IE.
+  div = null;
+})();
+
+
+/**
+ * Determines whether an object can have data
+ */
+jQuery.acceptData = function( elem ) {
+  var noData = jQuery.noData[ (elem.nodeName + " ").toLowerCase() ],
+    nodeType = +elem.nodeType || 1;
+
+  // Do not set data on non-element DOM nodes because it will not be cleared (#8335).
+  return nodeType !== 1 && nodeType !== 9 ?
+    false :
+
+    // Nodes accept data unless otherwise specified; rejection can be conditional
+    !noData || noData !== true && elem.getAttribute("classid") === noData;
+};
+
+
+var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
+  rmultiDash = /([A-Z])/g;
+
+function dataAttr( elem, key, data ) {
+  // If nothing was found internally, try to fetch any
+  // data from the HTML5 data-* attribute
+  if ( data === undefined && elem.nodeType === 1 ) {
+
+    var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
+
+    data = elem.getAttribute( name );
+
+    if ( typeof data === "string" ) {
+      try {
+        data = data === "true" ? true :
+          data === "false" ? false :
+          data === "null" ? null :
+          // Only convert to a number if it doesn't change the string
+          +data + "" === data ? +data :
+          rbrace.test( data ) ? jQuery.parseJSON( data ) :
+          data;
+      } catch( e ) {}
+
+      // Make sure we set the data so it isn't changed later
+      jQuery.data( elem, key, data );
+
+    } else {
+      data = undefined;
+    }
+  }
+
+  return data;
+}
+
+// checks a cache object for emptiness
+function isEmptyDataObject( obj ) {
+  var name;
+  for ( name in obj ) {
+
+    // if the public data object is empty, the private is still empty
+    if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
+      continue;
+    }
+    if ( name !== "toJSON" ) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+function internalData( elem, name, data, pvt /* Internal Use Only */ ) {
+  if ( !jQuery.acceptData( elem ) ) {
+    return;
+  }
+
+  var ret, thisCache,
+    internalKey = jQuery.expando,
+
+    // We have to handle DOM nodes and JS objects differently because IE6-7
+    // can't GC object references properly across the DOM-JS boundary
+    isNode = elem.nodeType,
+
+    // Only DOM nodes need the global jQuery cache; JS object data is
+    // attached directly to the object so GC can occur automatically
+    cache = isNode ? jQuery.cache : elem,
+
+    // Only defining an ID for JS objects if its cache already exists allows
+    // the code to shortcut on the same path as a DOM node with no cache
+    id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
+
+  // Avoid doing any more work than we need to when trying to get data on an
+  // object that has no data at all
+  if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) {
+    return;
+  }
+
+  if ( !id ) {
+    // Only DOM nodes need a new unique ID for each element since their data
+    // ends up in the global cache
+    if ( isNode ) {
+      id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++;
+    } else {
+      id = internalKey;
+    }
+  }
+
+  if ( !cache[ id ] ) {
+    // Avoid exposing jQuery metadata on plain JS objects when the object
+    // is serialized using JSON.stringify
+    cache[ id ] = isNode ? {} : { toJSON: jQuery.noop };
+  }
+
+  // An object can be passed to jQuery.data instead of a key/value pair; this gets
+  // shallow copied over onto the existing cache
+  if ( typeof name === "object" || typeof name === "function" ) {
+    if ( pvt ) {
+      cache[ id ] = jQuery.extend( cache[ id ], name );
+    } else {
+      cache[ id ].data = jQuery.extend( cache[ id ].data, name );
+    }
+  }
+
+  thisCache = cache[ id ];
+
+  // jQuery data() is stored in a separate object inside the object's internal data
+  // cache in order to avoid key collisions between internal data and user-defined
+  // data.
+  if ( !pvt ) {
+    if ( !thisCache.data ) {
+      thisCache.data = {};
+    }
+
+    thisCache = thisCache.data;
+  }
+
+  if ( data !== undefined ) {
+    thisCache[ jQuery.camelCase( name ) ] = data;
+  }
+
+  // Check for both converted-to-camel and non-converted data property names
+  // If a data property was specified
+  if ( typeof name === "string" ) {
+
+    // First Try to find as-is property data
+    ret = thisCache[ name ];
+
+    // Test for null|undefined property data
+    if ( ret == null ) {
+
+      // Try to find the camelCased property
+      ret = thisCache[ jQuery.camelCase( name ) ];
+    }
+  } else {
+    ret = thisCache;
+  }
+
+  return ret;
+}
+
+function internalRemoveData( elem, name, pvt ) {
+  if ( !jQuery.acceptData( elem ) ) {
+    return;
+  }
+
+  var thisCache, i,
+    isNode = elem.nodeType,
+
+    // See jQuery.data for more information
+    cache = isNode ? jQuery.cache : elem,
+    id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
+
+  // If there is already no cache entry for this object, there is no
+  // purpose in continuing
+  if ( !cache[ id ] ) {
+    return;
+  }
+
+  if ( name ) {
+
+    thisCache = pvt ? cache[ id ] : cache[ id ].data;
+
+    if ( thisCache ) {
+
+      // Support array or space separated string names for data keys
+      if ( !jQuery.isArray( name ) ) {
+
+        // try the string as a key before any manipulation
+        if ( name in thisCache ) {
+          name = [ name ];
+        } else {
+
+          // split the camel cased version by spaces unless a key with the spaces exists
+          name = jQuery.camelCase( name );
+          if ( name in thisCache ) {
+            name = [ name ];
+          } else {
+            name = name.split(" ");
+          }
+        }
+      } else {
+        // If "name" is an array of keys...
+        // When data is initially created, via ("key", "val") signature,
+        // keys will be converted to camelCase.
+        // Since there is no way to tell _how_ a key was added, remove
+        // both plain key and camelCase key. #12786
+        // This will only penalize the array argument path.
+        name = name.concat( jQuery.map( name, jQuery.camelCase ) );
+      }
+
+      i = name.length;
+      while ( i-- ) {
+        delete thisCache[ name[i] ];
+      }
+
+      // If there is no data left in the cache, we want to continue
+      // and let the cache object itself get destroyed
+      if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) {
+        return;
+      }
+    }
+  }
+
+  // See jQuery.data for more information
+  if ( !pvt ) {
+    delete cache[ id ].data;
+
+    // Don't destroy the parent cache unless the internal data object
+    // had been the only thing left in it
+    if ( !isEmptyDataObject( cache[ id ] ) ) {
+      return;
+    }
+  }
+
+  // Destroy the cache
+  if ( isNode ) {
+    jQuery.cleanData( [ elem ], true );
+
+  // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
+  /* jshint eqeqeq: false */
+  } else if ( support.deleteExpando || cache != cache.window ) {
+    /* jshint eqeqeq: true */
+    delete cache[ id ];
+
+  // When all else fails, null
+  } else {
+    cache[ id ] = null;
+  }
+}
+
+jQuery.extend({
+  cache: {},
+
+  // The following elements (space-suffixed to avoid Object.prototype collisions)
+  // throw uncatchable exceptions if you attempt to set expando properties
+  noData: {
+    "applet ": true,
+    "embed ": true,
+    // ...but Flash objects (which have this classid) *can* handle expandos
+    "object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
+  },
+
+  hasData: function( elem ) {
+    elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
+    return !!elem && !isEmptyDataObject( elem );
+  },
+
+  data: function( elem, name, data ) {
+    return internalData( elem, name, data );
+  },
+
+  removeData: function( elem, name ) {
+    return internalRemoveData( elem, name );
+  },
+
+  // For internal use only.
+  _data: function( elem, name, data ) {
+    return internalData( elem, name, data, true );
+  },
+
+  _removeData: function( elem, name ) {
+    return internalRemoveData( elem, name, true );
+  }
+});
+
+jQuery.fn.extend({
+  data: function( key, value ) {
+    var i, name, data,
+      elem = this[0],
+      attrs = elem && elem.attributes;
+
+    // Special expections of .data basically thwart jQuery.access,
+    // so implement the relevant behavior ourselves
+
+    // Gets all values
+    if ( key === undefined ) {
+      if ( this.length ) {
+        data = jQuery.data( elem );
+
+        if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
+          i = attrs.length;
+          while ( i-- ) {
+
+            // Support: IE11+
+            // The attrs elements can be null (#14894)
+            if ( attrs[ i ] ) {
+              name = attrs[ i ].name;
+              if ( name.indexOf( "data-" ) === 0 ) {
+                name = jQuery.camelCase( name.slice(5) );
+                dataAttr( elem, name, data[ name ] );
+              }
+            }
+          }
+          jQuery._data( elem, "parsedAttrs", true );
+        }
+      }
+
+      return data;
+    }
+
+    // Sets multiple values
+    if ( typeof key === "object" ) {
+      return this.each(function() {
+        jQuery.data( this, key );
+      });
+    }
+
+    return arguments.length > 1 ?
+
+      // Sets one value
+      this.each(function() {
+        jQuery.data( this, key, value );
+      }) :
+
+      // Gets one value
+      // Try to fetch any internally stored data first
+      elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined;
+  },
+
+  removeData: function( key ) {
+    return this.each(function() {
+      jQuery.removeData( this, key );
+    });
+  }
+});
+
+
+jQuery.extend({
+  queue: function( elem, type, data ) {
+    var queue;
+
+    if ( elem ) {
+      type = ( type || "fx" ) + "queue";
+      queue = jQuery._data( elem, type );
+
+      // Speed up dequeue by getting out quickly if this is just a lookup
+      if ( data ) {
+        if ( !queue || jQuery.isArray(data) ) {
+          queue = jQuery._data( elem, type, jQuery.makeArray(data) );
+        } else {
+          queue.push( data );
+        }
+      }
+      return queue || [];
+    }
+  },
+
+  dequeue: function( elem, type ) {
+    type = type || "fx";
+
+    var queue = jQuery.queue( elem, type ),
+      startLength = queue.length,
+      fn = queue.shift(),
+      hooks = jQuery._queueHooks( elem, type ),
+      next = function() {
+        jQuery.dequeue( elem, type );
+      };
+
+    // If the fx queue is dequeued, always remove the progress sentinel
+    if ( fn === "inprogress" ) {
+      fn = queue.shift();
+      startLength--;
+    }
+
+    if ( fn ) {
+
+      // Add a progress sentinel to prevent the fx queue from being
+      // automatically dequeued
+      if ( type === "fx" ) {
+        queue.unshift( "inprogress" );
+      }
+
+      // clear up the last queue stop function
+      delete hooks.stop;
+      fn.call( elem, next, hooks );
+    }
+
+    if ( !startLength && hooks ) {
+      hooks.empty.fire();
+    }
+  },
+
+  // not intended for public consumption - generates a queueHooks object, or returns the current one
+  _queueHooks: function( elem, type ) {
+    var key = type + "queueHooks";
+    return jQuery._data( elem, key ) || jQuery._data( elem, key, {
+      empty: jQuery.Callbacks("once memory").add(function() {
+        jQuery._removeData( elem, type + "queue" );
+        jQuery._removeData( elem, key );
+      })
+    });
+  }
+});
+
+jQuery.fn.extend({
+  queue: function( type, data ) {
+    var setter = 2;
+
+    if ( typeof type !== "string" ) {
+      data = type;
+      type = "fx";
+      setter--;
+    }
+
+    if ( arguments.length < setter ) {
+      return jQuery.queue( this[0], type );
+    }
+
+    return data === undefined ?
+      this :
+      this.each(function() {
+        var queue = jQuery.queue( this, type, data );
+
+        // ensure a hooks for this queue
+        jQuery._queueHooks( this, type );
+
+        if ( type === "fx" && queue[0] !== "inprogress" ) {
+          jQuery.dequeue( this, type );
+        }
+      });
+  },
+  dequeue: function( type ) {
+    return this.each(function() {
+      jQuery.dequeue( this, type );
+    });
+  },
+  clearQueue: function( type ) {
+    return this.queue( type || "fx", [] );
+  },
+  // Get a promise resolved when queues of a certain type
+  // are emptied (fx is the type by default)
+  promise: function( type, obj ) {
+    var tmp,
+      count = 1,
+      defer = jQuery.Deferred(),
+      elements = this,
+      i = this.length,
+      resolve = function() {
+        if ( !( --count ) ) {
+          defer.resolveWith( elements, [ elements ] );
+        }
+      };
+
+    if ( typeof type !== "string" ) {
+      obj = type;
+      type = undefined;
+    }
+    type = type || "fx";
+
+    while ( i-- ) {
+      tmp = jQuery._data( elements[ i ], type + "queueHooks" );
+      if ( tmp && tmp.empty ) {
+        count++;
+        tmp.empty.add( resolve );
+      }
+    }
+    resolve();
+    return defer.promise( obj );
+  }
+});
+var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source;
+
+var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
+
+var isHidden = function( elem, el ) {
+    // isHidden might be called from jQuery#filter function;
+    // in that case, element will be second argument
+    elem = el || elem;
+    return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
+  };
+
+
+
+// Multifunctional method to get and set values of a collection
+// The value/s can optionally be executed if it's a function
+var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
+  var i = 0,
+    length = elems.length,
+    bulk = key == null;
+
+  // Sets many values
+  if ( jQuery.type( key ) === "object" ) {
+    chainable = true;
+    for ( i in key ) {
+      jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
+    }
+
+  // Sets one value
+  } else if ( value !== undefined ) {
+    chainable = true;
+
+    if ( !jQuery.isFunction( value ) ) {
+      raw = true;
+    }
+
+    if ( bulk ) {
+      // Bulk operations run against the entire set
+      if ( raw ) {
+        fn.call( elems, value );
+        fn = null;
+
+      // ...except when executing function values
+      } else {
+        bulk = fn;
+        fn = function( elem, key, value ) {
+          return bulk.call( jQuery( elem ), value );
+        };
+      }
+    }
+
+    if ( fn ) {
+      for ( ; i < length; i++ ) {
+        fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
+      }
+    }
+  }
+
+  return chainable ?
+    elems :
+
+    // Gets
+    bulk ?
+      fn.call( elems ) :
+      length ? fn( elems[0], key ) : emptyGet;
+};
+var rcheckableType = (/^(?:checkbox|radio)$/i);
+
+
+
+(function() {
+  // Minified: var a,b,c
+  var input = document.createElement( "input" ),
+    div = document.createElement( "div" ),
+    fragment = document.createDocumentFragment();
+
+  // Setup
+  div.innerHTML = "  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
+
+  // IE strips leading whitespace when .innerHTML is used
+  support.leadingWhitespace = div.firstChild.nodeType === 3;
+
+  // Make sure that tbody elements aren't automatically inserted
+  // IE will insert them into empty tables
+  support.tbody = !div.getElementsByTagName( "tbody" ).length;
+
+  // Make sure that link elements get serialized correctly by innerHTML
+  // This requires a wrapper element in IE
+  support.htmlSerialize = !!div.getElementsByTagName( "link" ).length;
+
+  // Makes sure cloning an html5 element does not cause problems
+  // Where outerHTML is undefined, this still works
+  support.html5Clone =
+    document.createElement( "nav" ).cloneNode( true ).outerHTML !== "<:nav></:nav>";
+
+  // Check if a disconnected checkbox will retain its checked
+  // value of true after appended to the DOM (IE6/7)
+  input.type = "checkbox";
+  input.checked = true;
+  fragment.appendChild( input );
+  support.appendChecked = input.checked;
+
+  // Make sure textarea (and checkbox) defaultValue is properly cloned
+  // Support: IE6-IE11+
+  div.innerHTML = "<textarea>x</textarea>";
+  support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
+
+  // #11217 - WebKit loses check when the name is after the checked attribute
+  fragment.appendChild( div );
+  div.innerHTML = "<input type='radio' checked='checked' name='t'/>";
+
+  // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3
+  // old WebKit doesn't clone checked state correctly in fragments
+  support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
+
+  // Support: IE<9
+  // Opera does not clone events (and typeof div.attachEvent === undefined).
+  // IE9-10 clones events bound via attachEvent, but they don't trigger with .click()
+  support.noCloneEvent = true;
+  if ( div.attachEvent ) {
+    div.attachEvent( "onclick", function() {
+      support.noCloneEvent = false;
+    });
+
+    div.cloneNode( true ).click();
+  }
+
+  // Execute the test only if not already executed in another module.
+  if (support.deleteExpando == null) {
+    // Support: IE<9
+    support.deleteExpando = true;
+    try {
+      delete div.test;
+    } catch( e ) {
+      support.deleteExpando = false;
+    }
+  }
+})();
+
+
+(function() {
+  var i, eventName,
+    div = document.createElement( "div" );
+
+  // Support: IE<9 (lack submit/change bubble), Firefox 23+ (lack focusin event)
+  for ( i in { submit: true, change: true, focusin: true }) {
+    eventName = "on" + i;
+
+    if ( !(support[ i + "Bubbles" ] = eventName in window) ) {
+      // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP)
+      div.setAttribute( eventName, "t" );
+      support[ i + "Bubbles" ] = div.attributes[ eventName ].expando === false;
+    }
+  }
+
+  // Null elements to avoid leaks in IE.
+  div = null;
+})();
+
+
+var rformElems = /^(?:input|select|textarea)$/i,
+  rkeyEvent = /^key/,
+  rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/,
+  rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
+  rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
+
+function returnTrue() {
+  return true;
+}
+
+function returnFalse() {
+  return false;
+}
+
+function safeActiveElement() {
+  try {
+    return document.activeElement;
+  } catch ( err ) { }
+}
+
+/*
+ * Helper functions for managing events -- not part of the public interface.
+ * Props to Dean Edwards' addEvent library for many of the ideas.
+ */
+jQuery.event = {
+
+  global: {},
+
+  add: function( elem, types, handler, data, selector ) {
+    var tmp, events, t, handleObjIn,
+      special, eventHandle, handleObj,
+      handlers, type, namespaces, origType,
+      elemData = jQuery._data( elem );
+
+    // Don't attach events to noData or text/comment nodes (but allow plain objects)
+    if ( !elemData ) {
+      return;
+    }
+
+    // Caller can pass in an object of custom data in lieu of the handler
+    if ( handler.handler ) {
+      handleObjIn = handler;
+      handler = handleObjIn.handler;
+      selector = handleObjIn.selector;
+    }
+
+    // Make sure that the handler has a unique ID, used to find/remove it later
+    if ( !handler.guid ) {
+      handler.guid = jQuery.guid++;
+    }
+
+    // Init the element's event structure and main handler, if this is the first
+    if ( !(events = elemData.events) ) {
+      events = elemData.events = {};
+    }
+    if ( !(eventHandle = elemData.handle) ) {
+      eventHandle = elemData.handle = function( e ) {
+        // Discard the second event of a jQuery.event.trigger() and
+        // when an event is called after a page has unloaded
+        return typeof jQuery !== strundefined && (!e || jQuery.event.triggered !== e.type) ?
+          jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
+          undefined;
+      };
+      // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
+      eventHandle.elem = elem;
+    }
+
+    // Handle multiple events separated by a space
+    types = ( types || "" ).match( rnotwhite ) || [ "" ];
+    t = types.length;
+    while ( t-- ) {
+      tmp = rtypenamespace.exec( types[t] ) || [];
+      type = origType = tmp[1];
+      namespaces = ( tmp[2] || "" ).split( "." ).sort();
+
+      // There *must* be a type, no attaching namespace-only handlers
+      if ( !type ) {
+        continue;
+      }
+
+      // If event changes its type, use the special event handlers for the changed type
+      special = jQuery.event.special[ type ] || {};
+
+      // If selector defined, determine special event api type, otherwise given type
+      type = ( selector ? special.delegateType : special.bindType ) || type;
+
+      // Update special based on newly reset type
+      special = jQuery.event.special[ type ] || {};
+
+      // handleObj is passed to all event handlers
+      handleObj = jQuery.extend({
+        type: type,
+        origType: origType,
+        data: data,
+        handler: handler,
+        guid: handler.guid,
+        selector: selector,
+        needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
+        namespace: namespaces.join(".")
+      }, handleObjIn );
+
+      // Init the event handler queue if we're the first
+      if ( !(handlers = events[ type ]) ) {
+        handlers = events[ type ] = [];
+        handlers.delegateCount = 0;
+
+        // Only use addEventListener/attachEvent if the special events handler returns false
+        if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+          // Bind the global event handler to the element
+          if ( elem.addEventListener ) {
+            elem.addEventListener( type, eventHandle, false );
+
+          } else if ( elem.attachEvent ) {
+            elem.attachEvent( "on" + type, eventHandle );
+          }
+        }
+      }
+
+      if ( special.add ) {
+        special.add.call( elem, handleObj );
+
+        if ( !handleObj.handler.guid ) {
+          handleObj.handler.guid = handler.guid;
+        }
+      }
+
+      // Add to the element's handler list, delegates in front
+      if ( selector ) {
+        handlers.splice( handlers.delegateCount++, 0, handleObj );
+      } else {
+        handlers.push( handleObj );
+      }
+
+      // Keep track of which events have ever been used, for event optimization
+      jQuery.event.global[ type ] = true;
+    }
+
+    // Nullify elem to prevent memory leaks in IE
+    elem = null;
+  },
+
+  // Detach an event or set of events from an element
+  remove: function( elem, types, handler, selector, mappedTypes ) {
+    var j, handleObj, tmp,
+      origCount, t, events,
+      special, handlers, type,
+      namespaces, origType,
+      elemData = jQuery.hasData( elem ) && jQuery._data( elem );
+
+    if ( !elemData || !(events = elemData.events) ) {
+      return;
+    }
+
+    // Once for each type.namespace in types; type may be omitted
+    types = ( types || "" ).match( rnotwhite ) || [ "" ];
+    t = types.length;
+    while ( t-- ) {
+      tmp = rtypenamespace.exec( types[t] ) || [];
+      type = origType = tmp[1];
+      namespaces = ( tmp[2] || "" ).split( "." ).sort();
+
+      // Unbind all events (on this namespace, if provided) for the element
+      if ( !type ) {
+        for ( type in events ) {
+          jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
+        }
+        continue;
+      }
+
+      special = jQuery.event.special[ type ] || {};
+      type = ( selector ? special.delegateType : special.bindType ) || type;
+      handlers = events[ type ] || [];
+      tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );
+
+      // Remove matching events
+      origCount = j = handlers.length;
+      while ( j-- ) {
+        handleObj = handlers[ j ];
+
+        if ( ( mappedTypes || origType === handleObj.origType ) &&
+          ( !handler || handler.guid === handleObj.guid ) &&
+          ( !tmp || tmp.test( handleObj.namespace ) ) &&
+          ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
+          handlers.splice( j, 1 );
+
+          if ( handleObj.selector ) {
+            handlers.delegateCount--;
+          }
+          if ( special.remove ) {
+            special.remove.call( elem, handleObj );
+          }
+        }
+      }
+
+      // Remove generic event handler if we removed something and no more handlers exist
+      // (avoids potential for endless recursion during removal of special event handlers)
+      if ( origCount && !handlers.length ) {
+        if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
+          jQuery.removeEvent( elem, type, elemData.handle );
+        }
+
+        delete events[ type ];
+      }
+    }
+
+    // Remove the expando if it's no longer used
+    if ( jQuery.isEmptyObject( events ) ) {
+      delete elemData.handle;
+
+      // removeData also checks for emptiness and clears the expando if empty
+      // so use it instead of delete
+      jQuery._removeData( elem, "events" );
+    }
+  },
+
+  trigger: function( event, data, elem, onlyHandlers ) {
+    var handle, ontype, cur,
+      bubbleType, special, tmp, i,
+      eventPath = [ elem || document ],
+      type = hasOwn.call( event, "type" ) ? event.type : event,
+      namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];
+
+    cur = tmp = elem = elem || document;
+
+    // Don't do events on text and comment nodes
+    if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+      return;
+    }
+
+    // focus/blur morphs to focusin/out; ensure we're not firing them right now
+    if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
+      return;
+    }
+
+    if ( type.indexOf(".") >= 0 ) {
+      // Namespaced trigger; create a regexp to match event type in handle()
+      namespaces = type.split(".");
+      type = namespaces.shift();
+      namespaces.sort();
+    }
+    ontype = type.indexOf(":") < 0 && "on" + type;
+
+    // Caller can pass in a jQuery.Event object, Object, or just an event type string
+    event = event[ jQuery.expando ] ?
+      event :
+      new jQuery.Event( type, typeof event === "object" && event );
+
+    // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
+    event.isTrigger = onlyHandlers ? 2 : 3;
+    event.namespace = namespaces.join(".");
+    event.namespace_re = event.namespace ?
+      new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
+      null;
+
+    // Clean up the event in case it is being reused
+    event.result = undefined;
+    if ( !event.target ) {
+      event.target = elem;
+    }
+
+    // Clone any incoming data and prepend the event, creating the handler arg list
+    data = data == null ?
+      [ event ] :
+      jQuery.makeArray( data, [ event ] );
+
+    // Allow special events to draw outside the lines
+    special = jQuery.event.special[ type ] || {};
+    if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
+      return;
+    }
+
+    // Determine event propagation path in advance, per W3C events spec (#9951)
+    // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
+    if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
+
+      bubbleType = special.delegateType || type;
+      if ( !rfocusMorph.test( bubbleType + type ) ) {
+        cur = cur.parentNode;
+      }
+      for ( ; cur; cur = cur.parentNode ) {
+        eventPath.push( cur );
+        tmp = cur;
+      }
+
+      // Only add window if we got to document (e.g., not plain obj or detached DOM)
+      if ( tmp === (elem.ownerDocument || document) ) {
+        eventPath.push( tmp.defaultView || tmp.parentWindow || window );
+      }
+    }
+
+    // Fire handlers on the event path
+    i = 0;
+    while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {
+
+      event.type = i > 1 ?
+        bubbleType :
+        special.bindType || type;
+
+      // jQuery handler
+      handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
+      if ( handle ) {
+        handle.apply( cur, data );
+      }
+
+      // Native handler
+      handle = ontype && cur[ ontype ];
+      if ( handle && handle.apply && jQuery.acceptData( cur ) ) {
+        event.result = handle.apply( cur, data );
+        if ( event.result === false ) {
+          event.preventDefault();
+        }
+      }
+    }
+    event.type = type;
+
+    // If nobody prevented the default action, do it now
+    if ( !onlyHandlers && !event.isDefaultPrevented() ) {
+
+      if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) &&
+        jQuery.acceptData( elem ) ) {
+
+        // Call a native DOM method on the target with the same name name as the event.
+        // Can't use an .isFunction() check here because IE6/7 fails that test.
+        // Don't do default actions on window, that's where global variables be (#6170)
+        if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) {
+
+          // Don't re-trigger an onFOO event when we call its FOO() method
+          tmp = elem[ ontype ];
+
+          if ( tmp ) {
+            elem[ ontype ] = null;
+          }
+
+          // Prevent re-triggering of the same event, since we already bubbled it above
+          jQuery.event.triggered = type;
+          try {
+            elem[ type ]();
+          } catch ( e ) {
+            // IE<9 dies on focus/blur to hidden element (#1486,#12518)
+            // only reproducible on winXP IE8 native, not IE9 in IE8 mode
+          }
+          jQuery.event.triggered = undefined;
+
+          if ( tmp ) {
+            elem[ ontype ] = tmp;
+          }
+        }
+      }
+    }
+
+    return event.result;
+  },
+
+  dispatch: function( event ) {
+
+    // Make a writable jQuery.Event from the native event object
+    event = jQuery.event.fix( event );
+
+    var i, ret, handleObj, matched, j,
+      handlerQueue = [],
+      args = slice.call( arguments ),
+      handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [],
+      special = jQuery.event.special[ event.type ] || {};
+
+    // Use the fix-ed jQuery.Event rather than the (read-only) native event
+    args[0] = event;
+    event.delegateTarget = this;
+
+    // Call the preDispatch hook for the mapped type, and let it bail if desired
+    if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
+      return;
+    }
+
+    // Determine handlers
+    handlerQueue = jQuery.event.handlers.call( this, event, handlers );
+
+    // Run delegates first; they may want to stop propagation beneath us
+    i = 0;
+    while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
+      event.currentTarget = matched.elem;
+
+      j = 0;
+      while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
+
+        // Triggered event must either 1) have no namespace, or
+        // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
+        if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {
+
+          event.handleObj = handleObj;
+          event.data = handleObj.data;
+
+          ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
+              .apply( matched.elem, args );
+
+          if ( ret !== undefined ) {
+            if ( (event.result = ret) === false ) {
+              event.preventDefault();
+              event.stopPropagation();
+            }
+          }
+        }
+      }
+    }
+
+    // Call the postDispatch hook for the mapped type
+    if ( special.postDispatch ) {
+      special.postDispatch.call( this, event );
+    }
+
+    return event.result;
+  },
+
+  handlers: function( event, handlers ) {
+    var sel, handleObj, matches, i,
+      handlerQueue = [],
+      delegateCount = handlers.delegateCount,
+      cur = event.target;
+
+    // Find delegate handlers
+    // Black-hole SVG <use> instance trees (#13180)
+    // Avoid non-left-click bubbling in Firefox (#3861)
+    if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {
+
+      /* jshint eqeqeq: false */
+      for ( ; cur != this; cur = cur.parentNode || this ) {
+        /* jshint eqeqeq: true */
+
+        // Don't check non-elements (#13208)
+        // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
+        if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) {
+          matches = [];
+          for ( i = 0; i < delegateCount; i++ ) {
+            handleObj = handlers[ i ];
+
+            // Don't conflict with Object.prototype properties (#13203)
+            sel = handleObj.selector + " ";
+
+            if ( matches[ sel ] === undefined ) {
+              matches[ sel ] = handleObj.needsContext ?
+                jQuery( sel, this ).index( cur ) >= 0 :
+                jQuery.find( sel, this, null, [ cur ] ).length;
+            }
+            if ( matches[ sel ] ) {
+              matches.push( handleObj );
+            }
+          }
+          if ( matches.length ) {
+            handlerQueue.push({ elem: cur, handlers: matches });
+          }
+        }
+      }
+    }
+
+    // Add the remaining (directly-bound) handlers
+    if ( delegateCount < handlers.length ) {
+      handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
+    }
+
+    return handlerQueue;
+  },
+
+  fix: function( event ) {
+    if ( event[ jQuery.expando ] ) {
+      return event;
+    }
+
+    // Create a writable copy of the event object and normalize some properties
+    var i, prop, copy,
+      type = event.type,
+      originalEvent = event,
+      fixHook = this.fixHooks[ type ];
+
+    if ( !fixHook ) {
+      this.fixHooks[ type ] = fixHook =
+        rmouseEvent.test( type ) ? this.mouseHooks :
+        rkeyEvent.test( type ) ? this.keyHooks :
+        {};
+    }
+    copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
+
+    event = new jQuery.Event( originalEvent );
+
+    i = copy.length;
+    while ( i-- ) {
+      prop = copy[ i ];
+      event[ prop ] = originalEvent[ prop ];
+    }
+
+    // Support: IE<9
+    // Fix target property (#1925)
+    if ( !event.target ) {
+      event.target = originalEvent.srcElement || document;
+    }
+
+    // Support: Chrome 23+, Safari?
+    // Target should not be a text node (#504, #13143)
+    if ( event.target.nodeType === 3 ) {
+      event.target = event.target.parentNode;
+    }
+
+    // Support: IE<9
+    // For mouse/key events, metaKey==false if it's undefined (#3368, #11328)
+    event.metaKey = !!event.metaKey;
+
+    return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
+  },
+
+  // Includes some event props shared by KeyEvent and MouseEvent
+  props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
+
+  fixHooks: {},
+
+  keyHooks: {
+    props: "char charCode key keyCode".split(" "),
+    filter: function( event, original ) {
+
+      // Add which for key events
+      if ( event.which == null ) {
+        event.which = original.charCode != null ? original.charCode : original.keyCode;
+      }
+
+      return event;
+    }
+  },
+
+  mouseHooks: {
+    props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
+    filter: function( event, original ) {
+      var body, eventDoc, doc,
+        button = original.button,
+        fromElement = original.fromElement;
+
+      // Calculate pageX/Y if missing and clientX/Y available
+      if ( event.pageX == null && original.clientX != null ) {
+        eventDoc = event.target.ownerDocument || document;
+        doc = eventDoc.documentElement;
+        body = eventDoc.body;
+
+        event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
+        event.pageY = original.clientY + ( doc && doc.scrollTop  || body && body.scrollTop  || 0 ) - ( doc && doc.clientTop  || body && body.clientTop  || 0 );
+      }
+
+      // Add relatedTarget, if necessary
+      if ( !event.relatedTarget && fromElement ) {
+        event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
+      }
+
+      // Add which for click: 1 === left; 2 === middle; 3 === right
+      // Note: button is not normalized, so don't use it
+      if ( !event.which && button !== undefined ) {
+        event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
+      }
+
+      return event;
+    }
+  },
+
+  special: {
+    load: {
+      // Prevent triggered image.load events from bubbling to window.load
+      noBubble: true
+    },
+    focus: {
+      // Fire native event if possible so blur/focus sequence is correct
+      trigger: function() {
+        if ( this !== safeActiveElement() && this.focus ) {
+          try {
+            this.focus();
+            return false;
+          } catch ( e ) {
+            // Support: IE<9
+            // If we error on focus to hidden element (#1486, #12518),
+            // let .trigger() run the handlers
+          }
+        }
+      },
+      delegateType: "focusin"
+    },
+    blur: {
+      trigger: function() {
+        if ( this === safeActiveElement() && this.blur ) {
+          this.blur();
+          return false;
+        }
+      },
+      delegateType: "focusout"
+    },
+    click: {
+      // For checkbox, fire native event so checked state will be right
+      trigger: function() {
+        if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) {
+          this.click();
+          return false;
+        }
+      },
+
+      // For cross-browser consistency, don't fire native .click() on links
+      _default: function( event ) {
+        return jQuery.nodeName( event.target, "a" );
+      }
+    },
+
+    beforeunload: {
+      postDispatch: function( event ) {
+
+        // Support: Firefox 20+
+        // Firefox doesn't alert if the returnValue field is not set.
+        if ( event.result !== undefined && event.originalEvent ) {
+          event.originalEvent.returnValue = event.result;
+        }
+      }
+    }
+  },
+
+  simulate: function( type, elem, event, bubble ) {
+    // Piggyback on a donor event to simulate a different one.
+    // Fake originalEvent to avoid donor's stopPropagation, but if the
+    // simulated event prevents default then we do the same on the donor.
+    var e = jQuery.extend(
+      new jQuery.Event(),
+      event,
+      {
+        type: type,
+        isSimulated: true,
+        originalEvent: {}
+      }
+    );
+    if ( bubble ) {
+      jQuery.event.trigger( e, null, elem );
+    } else {
+      jQuery.event.dispatch.call( elem, e );
+    }
+    if ( e.isDefaultPrevented() ) {
+      event.preventDefault();
+    }
+  }
+};
+
+jQuery.removeEvent = document.removeEventListener ?
+  function( elem, type, handle ) {
+    if ( elem.removeEventListener ) {
+      elem.removeEventListener( type, handle, false );
+    }
+  } :
+  function( elem, type, handle ) {
+    var name = "on" + type;
+
+    if ( elem.detachEvent ) {
+
+      // #8545, #7054, preventing memory leaks for custom events in IE6-8
+      // detachEvent needed property on element, by name of that event, to properly expose it to GC
+      if ( typeof elem[ name ] === strundefined ) {
+        elem[ name ] = null;
+      }
+
+      elem.detachEvent( name, handle );
+    }
+  };
+
+jQuery.Event = function( src, props ) {
+  // Allow instantiation without the 'new' keyword
+  if ( !(this instanceof jQuery.Event) ) {
+    return new jQuery.Event( src, props );
+  }
+
+  // Event object
+  if ( src && src.type ) {
+    this.originalEvent = src;
+    this.type = src.type;
+
+    // Events bubbling up the document may have been marked as prevented
+    // by a handler lower down the tree; reflect the correct value.
+    this.isDefaultPrevented = src.defaultPrevented ||
+        src.defaultPrevented === undefined &&
+        // Support: IE < 9, Android < 4.0
+        src.returnValue === false ?
+      returnTrue :
+      returnFalse;
+
+  // Event type
+  } else {
+    this.type = src;
+  }
+
+  // Put explicitly provided properties onto the event object
+  if ( props ) {
+    jQuery.extend( this, props );
+  }
+
+  // Create a timestamp if incoming event doesn't have one
+  this.timeStamp = src && src.timeStamp || jQuery.now();
+
+  // Mark it as fixed
+  this[ jQuery.expando ] = true;
+};
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+  isDefaultPrevented: returnFalse,
+  isPropagationStopped: returnFalse,
+  isImmediatePropagationStopped: returnFalse,
+
+  preventDefault: function() {
+    var e = this.originalEvent;
+
+    this.isDefaultPrevented = returnTrue;
+    if ( !e ) {
+      return;
+    }
+
+    // If preventDefault exists, run it on the original event
+    if ( e.preventDefault ) {
+      e.preventDefault();
+
+    // Support: IE
+    // Otherwise set the returnValue property of the original event to false
+    } else {
+      e.returnValue = false;
+    }
+  },
+  stopPropagation: function() {
+    var e = this.originalEvent;
+
+    this.isPropagationStopped = returnTrue;
+    if ( !e ) {
+      return;
+    }
+    // If stopPropagation exists, run it on the original event
+    if ( e.stopPropagation ) {
+      e.stopPropagation();
+    }
+
+    // Support: IE
+    // Set the cancelBubble property of the original event to true
+    e.cancelBubble = true;
+  },
+  stopImmediatePropagation: function() {
+    var e = this.originalEvent;
+
+    this.isImmediatePropagationStopped = returnTrue;
+
+    if ( e && e.stopImmediatePropagation ) {
+      e.stopImmediatePropagation();
+    }
+
+    this.stopPropagation();
+  }
+};
+
+// Create mouseenter/leave events using mouseover/out and event-time checks
+jQuery.each({
+  mouseenter: "mouseover",
+  mouseleave: "mouseout",
+  pointerenter: "pointerover",
+  pointerleave: "pointerout"
+}, function( orig, fix ) {
+  jQuery.event.special[ orig ] = {
+    delegateType: fix,
+    bindType: fix,
+
+    handle: function( event ) {
+      var ret,
+        target = this,
+        related = event.relatedTarget,
+        handleObj = event.handleObj;
+
+      // For mousenter/leave call the handler if related is outside the target.
+      // NB: No relatedTarget if the mouse left/entered the browser window
+      if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
+        event.type = handleObj.origType;
+        ret = handleObj.handler.apply( this, arguments );
+        event.type = fix;
+      }
+      return ret;
+    }
+  };
+});
+
+// IE submit delegation
+if ( !support.submitBubbles ) {
+
+  jQuery.event.special.submit = {
+    setup: function() {
+      // Only need this for delegated form submit events
+      if ( jQuery.nodeName( this, "form" ) ) {
+        return false;
+      }
+
+      // Lazy-add a submit handler when a descendant form may potentially be submitted
+      jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
+        // Node name check avoids a VML-related crash in IE (#9807)
+        var elem = e.target,
+          form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
+        if ( form && !jQuery._data( form, "submitBubbles" ) ) {
+          jQuery.event.add( form, "submit._submit", function( event ) {
+            event._submit_bubble = true;
+          });
+          jQuery._data( form, "submitBubbles", true );
+        }
+      });
+      // return undefined since we don't need an event listener
+    },
+
+    postDispatch: function( event ) {
+      // If form was submitted by the user, bubble the event up the tree
+      if ( event._submit_bubble ) {
+        delete event._submit_bubble;
+        if ( this.parentNode && !event.isTrigger ) {
+          jQuery.event.simulate( "submit", this.parentNode, event, true );
+        }
+      }
+    },
+
+    teardown: function() {
+      // Only need this for delegated form submit events
+      if ( jQuery.nodeName( this, "form" ) ) {
+        return false;
+      }
+
+      // Remove delegated handlers; cleanData eventually reaps submit handlers attached above
+      jQuery.event.remove( this, "._submit" );
+    }
+  };
+}
+
+// IE change delegation and checkbox/radio fix
+if ( !support.changeBubbles ) {
+
+  jQuery.event.special.change = {
+
+    setup: function() {
+
+      if ( rformElems.test( this.nodeName ) ) {
+        // IE doesn't fire change on a check/radio until blur; trigger it on click
+        // after a propertychange. Eat the blur-change in special.change.handle.
+        // This still fires onchange a second time for check/radio after blur.
+        if ( this.type === "checkbox" || this.type === "radio" ) {
+          jQuery.event.add( this, "propertychange._change", function( event ) {
+            if ( event.originalEvent.propertyName === "checked" ) {
+              this._just_changed = true;
+            }
+          });
+          jQuery.event.add( this, "click._change", function( event ) {
+            if ( this._just_changed && !event.isTrigger ) {
+              this._just_changed = false;
+            }
+            // Allow triggered, simulated change events (#11500)
+            jQuery.event.simulate( "change", this, event, true );
+          });
+        }
+        return false;
+      }
+      // Delegated event; lazy-add a change handler on descendant inputs
+      jQuery.event.add( this, "beforeactivate._change", function( e ) {
+        var elem = e.target;
+
+        if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) {
+          jQuery.event.add( elem, "change._change", function( event ) {
+            if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
+              jQuery.event.simulate( "change", this.parentNode, event, true );
+            }
+          });
+          jQuery._data( elem, "changeBubbles", true );
+        }
+      });
+    },
+
+    handle: function( event ) {
+      var elem = event.target;
+
+      // Swallow native change events from checkbox/radio, we already triggered them above
+      if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
+        return event.handleObj.handler.apply( this, arguments );
+      }
+    },
+
+    teardown: function() {
+      jQuery.event.remove( this, "._change" );
+
+      return !rformElems.test( this.nodeName );
+    }
+  };
+}
+
+// Create "bubbling" focus and blur events
+if ( !support.focusinBubbles ) {
+  jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+
+    // Attach a single capturing handler on the document while someone wants focusin/focusout
+    var handler = function( event ) {
+        jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
+      };
+
+    jQuery.event.special[ fix ] = {
+      setup: function() {
+        var doc = this.ownerDocument || this,
+          attaches = jQuery._data( doc, fix );
+
+        if ( !attaches ) {
+          doc.addEventListener( orig, handler, true );
+        }
+        jQuery._data( doc, fix, ( attaches || 0 ) + 1 );
+      },
+      teardown: function() {
+        var doc = this.ownerDocument || this,
+          attaches = jQuery._data( doc, fix ) - 1;
+
+        if ( !attaches ) {
+          doc.removeEventListener( orig, handler, true );
+          jQuery._removeData( doc, fix );
+        } else {
+          jQuery._data( doc, fix, attaches );
+        }
+      }
+    };
+  });
+}
+
+jQuery.fn.extend({
+
+  on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
+    var type, origFn;
+
+    // Types can be a map of types/handlers
+    if ( typeof types === "object" ) {
+      // ( types-Object, selector, data )
+      if ( typeof selector !== "string" ) {
+        // ( types-Object, data )
+        data = data || selector;
+        selector = undefined;
+      }
+      for ( type in types ) {
+        this.on( type, selector, data, types[ type ], one );
+      }
+      return this;
+    }
+
+    if ( data == null && fn == null ) {
+      // ( types, fn )
+      fn = selector;
+      data = selector = undefined;
+    } else if ( fn == null ) {
+      if ( typeof selector === "string" ) {
+        // ( types, selector, fn )
+        fn = data;
+        data = undefined;
+      } else {
+        // ( types, data, fn )
+        fn = data;
+        data = selector;
+        selector = undefined;
+      }
+    }
+    if ( fn === false ) {
+      fn = returnFalse;
+    } else if ( !fn ) {
+      return this;
+    }
+
+    if ( one === 1 ) {
+      origFn = fn;
+      fn = function( event ) {
+        // Can use an empty set, since event contains the info
+        jQuery().off( event );
+        return origFn.apply( this, arguments );
+      };
+      // Use same guid so caller can remove using origFn
+      fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
+    }
+    return this.each( function() {
+      jQuery.event.add( this, types, fn, data, selector );
+    });
+  },
+  one: function( types, selector, data, fn ) {
+    return this.on( types, selector, data, fn, 1 );
+  },
+  off: function( types, selector, fn ) {
+    var handleObj, type;
+    if ( types && types.preventDefault && types.handleObj ) {
+      // ( event )  dispatched jQuery.Event
+      handleObj = types.handleObj;
+      jQuery( types.delegateTarget ).off(
+        handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
+        handleObj.selector,
+        handleObj.handler
+      );
+      return this;
+    }
+    if ( typeof types === "object" ) {
+      // ( types-object [, selector] )
+      for ( type in types ) {
+        this.off( type, selector, types[ type ] );
+      }
+      return this;
+    }
+    if ( selector === false || typeof selector === "function" ) {
+      // ( types [, fn] )
+      fn = selector;
+      selector = undefined;
+    }
+    if ( fn === false ) {
+      fn = returnFalse;
+    }
+    return this.each(function() {
+      jQuery.event.remove( this, types, fn, selector );
+    });
+  },
+
+  trigger: function( type, data ) {
+    return this.each(function() {
+      jQuery.event.trigger( type, data, this );
+    });
+  },
+  triggerHandler: function( type, data ) {
+    var elem = this[0];
+    if ( elem ) {
+      return jQuery.event.trigger( type, data, elem, true );
+    }
+  }
+});
+
+
+function createSafeFragment( document ) {
+  var list = nodeNames.split( "|" ),
+    safeFrag = document.createDocumentFragment();
+
+  if ( safeFrag.createElement ) {
+    while ( list.length ) {
+      safeFrag.createElement(
+        list.pop()
+      );
+    }
+  }
+  return safeFrag;
+}
+
+var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
+    "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
+  rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g,
+  rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"),
+  rleadingWhitespace = /^\s+/,
+  rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
+  rtagName = /<([\w:]+)/,
+  rtbody = /<tbody/i,
+  rhtml = /<|&#?\w+;/,
+  rnoInnerhtml = /<(?:script|style|link)/i,
+  // checked="checked" or checked
+  rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
+  rscriptType = /^$|\/(?:java|ecma)script/i,
+  rscriptTypeMasked = /^true\/(.*)/,
+  rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,
+
+  // We have to close these tags to support XHTML (#13200)
+  wrapMap = {
+    option: [ 1, "<select multiple='multiple'>", "</select>" ],
+    legend: [ 1, "<fieldset>", "</fieldset>" ],
+    area: [ 1, "<map>", "</map>" ],
+    param: [ 1, "<object>", "</object>" ],
+    thead: [ 1, "<table>", "</table>" ],
+    tr: [ 2, "<table><tbody>", "</tbody></table>" ],
+    col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
+    td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
+
+    // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags,
+    // unless wrapped in a div with non-breaking characters in front of it.
+    _default: support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X<div>", "</div>"  ]
+  },
+  safeFragment = createSafeFragment( document ),
+  fragmentDiv = safeFragment.appendChild( document.createElement("div") );
+
+wrapMap.optgroup = wrapMap.option;
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+function getAll( context, tag ) {
+  var elems, elem,
+    i = 0,
+    found = typeof context.getElementsByTagName !== strundefined ? context.getElementsByTagName( tag || "*" ) :
+      typeof context.querySelectorAll !== strundefined ? context.querySelectorAll( tag || "*" ) :
+      undefined;
+
+  if ( !found ) {
+    for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) {
+      if ( !tag || jQuery.nodeName( elem, tag ) ) {
+        found.push( elem );
+      } else {
+        jQuery.merge( found, getAll( elem, tag ) );
+      }
+    }
+  }
+
+  return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
+    jQuery.merge( [ context ], found ) :
+    found;
+}
+
+// Used in buildFragment, fixes the defaultChecked property
+function fixDefaultChecked( elem ) {
+  if ( rcheckableType.test( elem.type ) ) {
+    elem.defaultChecked = elem.checked;
+  }
+}
+
+// Support: IE<8
+// Manipulating tables requires a tbody
+function manipulationTarget( elem, content ) {
+  return jQuery.nodeName( elem, "table" ) &&
+    jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ?
+
+    elem.getElementsByTagName("tbody")[0] ||
+      elem.appendChild( elem.ownerDocument.createElement("tbody") ) :
+    elem;
+}
+
+// Replace/restore the type attribute of script elements for safe DOM manipulation
+function disableScript( elem ) {
+  elem.type = (jQuery.find.attr( elem, "type" ) !== null) + "/" + elem.type;
+  return elem;
+}
+function restoreScript( elem ) {
+  var match = rscriptTypeMasked.exec( elem.type );
+  if ( match ) {
+    elem.type = match[1];
+  } else {
+    elem.removeAttribute("type");
+  }
+  return elem;
+}
+
+// Mark scripts as having already been evaluated
+function setGlobalEval( elems, refElements ) {
+  var elem,
+    i = 0;
+  for ( ; (elem = elems[i]) != null; i++ ) {
+    jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) );
+  }
+}
+
+function cloneCopyEvent( src, dest ) {
+
+  if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
+    return;
+  }
+
+  var type, i, l,
+    oldData = jQuery._data( src ),
+    curData = jQuery._data( dest, oldData ),
+    events = oldData.events;
+
+  if ( events ) {
+    delete curData.handle;
+    curData.events = {};
+
+    for ( type in events ) {
+      for ( i = 0, l = events[ type ].length; i < l; i++ ) {
+        jQuery.event.add( dest, type, events[ type ][ i ] );
+      }
+    }
+  }
+
+  // make the cloned public data object a copy from the original
+  if ( curData.data ) {
+    curData.data = jQuery.extend( {}, curData.data );
+  }
+}
+
+function fixCloneNodeIssues( src, dest ) {
+  var nodeName, e, data;
+
+  // We do not need to do anything for non-Elements
+  if ( dest.nodeType !== 1 ) {
+    return;
+  }
+
+  nodeName = dest.nodeName.toLowerCase();
+
+  // IE6-8 copies events bound via attachEvent when using cloneNode.
+  if ( !support.noCloneEvent && dest[ jQuery.expando ] ) {
+    data = jQuery._data( dest );
+
+    for ( e in data.events ) {
+      jQuery.removeEvent( dest, e, data.handle );
+    }
+
+    // Event data gets referenced instead of copied if the expando gets copied too
+    dest.removeAttribute( jQuery.expando );
+  }
+
+  // IE blanks contents when cloning scripts, and tries to evaluate newly-set text
+  if ( nodeName === "script" && dest.text !== src.text ) {
+    disableScript( dest ).text = src.text;
+    restoreScript( dest );
+
+  // IE6-10 improperly clones children of object elements using classid.
+  // IE10 throws NoModificationAllowedError if parent is null, #12132.
+  } else if ( nodeName === "object" ) {
+    if ( dest.parentNode ) {
+      dest.outerHTML = src.outerHTML;
+    }
+
+    // This path appears unavoidable for IE9. When cloning an object
+    // element in IE9, the outerHTML strategy above is not sufficient.
+    // If the src has innerHTML and the destination does not,
+    // copy the src.innerHTML into the dest.innerHTML. #10324
+    if ( support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) {
+      dest.innerHTML = src.innerHTML;
+    }
+
+  } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
+    // IE6-8 fails to persist the checked state of a cloned checkbox
+    // or radio button. Worse, IE6-7 fail to give the cloned element
+    // a checked appearance if the defaultChecked value isn't also set
+
+    dest.defaultChecked = dest.checked = src.checked;
+
+    // IE6-7 get confused and end up setting the value of a cloned
+    // checkbox/radio button to an empty string instead of "on"
+    if ( dest.value !== src.value ) {
+      dest.value = src.value;
+    }
+
+  // IE6-8 fails to return the selected option to the default selected
+  // state when cloning options
+  } else if ( nodeName === "option" ) {
+    dest.defaultSelected = dest.selected = src.defaultSelected;
+
+  // IE6-8 fails to set the defaultValue to the correct value when
+  // cloning other types of input fields
+  } else if ( nodeName === "input" || nodeName === "textarea" ) {
+    dest.defaultValue = src.defaultValue;
+  }
+}
+
+jQuery.extend({
+  clone: function( elem, dataAndEvents, deepDataAndEvents ) {
+    var destElements, node, clone, i, srcElements,
+      inPage = jQuery.contains( elem.ownerDocument, elem );
+
+    if ( support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) {
+      clone = elem.cloneNode( true );
+
+    // IE<=8 does not properly clone detached, unknown element nodes
+    } else {
+      fragmentDiv.innerHTML = elem.outerHTML;
+      fragmentDiv.removeChild( clone = fragmentDiv.firstChild );
+    }
+
+    if ( (!support.noCloneEvent || !support.noCloneChecked) &&
+        (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
+
+      // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2
+      destElements = getAll( clone );
+      srcElements = getAll( elem );
+
+      // Fix all IE cloning issues
+      for ( i = 0; (node = srcElements[i]) != null; ++i ) {
+        // Ensure that the destination node is not null; Fixes #9587
+        if ( destElements[i] ) {
+          fixCloneNodeIssues( node, destElements[i] );
+        }
+      }
+    }
+
+    // Copy the events from the original to the clone
+    if ( dataAndEvents ) {
+      if ( deepDataAndEvents ) {
+        srcElements = srcElements || getAll( elem );
+        destElements = destElements || getAll( clone );
+
+        for ( i = 0; (node = srcElements[i]) != null; i++ ) {
+          cloneCopyEvent( node, destElements[i] );
+        }
+      } else {
+        cloneCopyEvent( elem, clone );
+      }
+    }
+
+    // Preserve script evaluation history
+    destElements = getAll( clone, "script" );
+    if ( destElements.length > 0 ) {
+      setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
+    }
+
+    destElements = srcElements = node = null;
+
+    // Return the cloned set
+    return clone;
+  },
+
+  buildFragment: function( elems, context, scripts, selection ) {
+    var j, elem, contains,
+      tmp, tag, tbody, wrap,
+      l = elems.length,
+
+      // Ensure a safe fragment
+      safe = createSafeFragment( context ),
+
+      nodes = [],
+      i = 0;
+
+    for ( ; i < l; i++ ) {
+      elem = elems[ i ];
+
+      if ( elem || elem === 0 ) {
+
+        // Add nodes directly
+        if ( jQuery.type( elem ) === "object" ) {
+          jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
+
+        // Convert non-html into a text node
+        } else if ( !rhtml.test( elem ) ) {
+          nodes.push( context.createTextNode( elem ) );
+
+        // Convert html into DOM nodes
+        } else {
+          tmp = tmp || safe.appendChild( context.createElement("div") );
+
+          // Deserialize a standard representation
+          tag = (rtagName.exec( elem ) || [ "", "" ])[ 1 ].toLowerCase();
+          wrap = wrapMap[ tag ] || wrapMap._default;
+
+          tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[2];
+
+          // Descend through wrappers to the right content
+          j = wrap[0];
+          while ( j-- ) {
+            tmp = tmp.lastChild;
+          }
+
+          // Manually add leading whitespace removed by IE
+          if ( !support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
+            nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) );
+          }
+
+          // Remove IE's autoinserted <tbody> from table fragments
+          if ( !support.tbody ) {
+
+            // String was a <table>, *may* have spurious <tbody>
+            elem = tag === "table" && !rtbody.test( elem ) ?
+              tmp.firstChild :
+
+              // String was a bare <thead> or <tfoot>
+              wrap[1] === "<table>" && !rtbody.test( elem ) ?
+                tmp :
+                0;
+
+            j = elem && elem.childNodes.length;
+            while ( j-- ) {
+              if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) {
+                elem.removeChild( tbody );
+              }
+            }
+          }
+
+          jQuery.merge( nodes, tmp.childNodes );
+
+          // Fix #12392 for WebKit and IE > 9
+          tmp.textContent = "";
+
+          // Fix #12392 for oldIE
+          while ( tmp.firstChild ) {
+            tmp.removeChild( tmp.firstChild );
+          }
+
+          // Remember the top-level container for proper cleanup
+          tmp = safe.lastChild;
+        }
+      }
+    }
+
+    // Fix #11356: Clear elements from fragment
+    if ( tmp ) {
+      safe.removeChild( tmp );
+    }
+
+    // Reset defaultChecked for any radios and checkboxes
+    // about to be appended to the DOM in IE 6/7 (#8060)
+    if ( !support.appendChecked ) {
+      jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked );
+    }
+
+    i = 0;
+    while ( (elem = nodes[ i++ ]) ) {
+
+      // #4087 - If origin and destination elements are the same, and this is
+      // that element, do not do anything
+      if ( selection && jQuery.inArray( elem, selection ) !== -1 ) {
+        continue;
+      }
+
+      contains = jQuery.contains( elem.ownerDocument, elem );
+
+      // Append to fragment
+      tmp = getAll( safe.appendChild( elem ), "script" );
+
+      // Preserve script evaluation history
+      if ( contains ) {
+        setGlobalEval( tmp );
+      }
+
+      // Capture executables
+      if ( scripts ) {
+        j = 0;
+        while ( (elem = tmp[ j++ ]) ) {
+          if ( rscriptType.test( elem.type || "" ) ) {
+            scripts.push( elem );
+          }
+        }
+      }
+    }
+
+    tmp = null;
+
+    return safe;
+  },
+
+  cleanData: function( elems, /* internal */ acceptData ) {
+    var elem, type, id, data,
+      i = 0,
+      internalKey = jQuery.expando,
+      cache = jQuery.cache,
+      deleteExpando = support.deleteExpando,
+      special = jQuery.event.special;
+
+    for ( ; (elem = elems[i]) != null; i++ ) {
+      if ( acceptData || jQuery.acceptData( elem ) ) {
+
+        id = elem[ internalKey ];
+        data = id && cache[ id ];
+
+        if ( data ) {
+          if ( data.events ) {
+            for ( type in data.events ) {
+              if ( special[ type ] ) {
+                jQuery.event.remove( elem, type );
+
+              // This is a shortcut to avoid jQuery.event.remove's overhead
+              } else {
+                jQuery.removeEvent( elem, type, data.handle );
+              }
+            }
+          }
+
+          // Remove cache only if it was not already removed by jQuery.event.remove
+          if ( cache[ id ] ) {
+
+            delete cache[ id ];
+
+            // IE does not allow us to delete expando properties from nodes,
+            // nor does it have a removeAttribute function on Document nodes;
+            // we must handle all of these cases
+            if ( deleteExpando ) {
+              delete elem[ internalKey ];
+
+            } else if ( typeof elem.removeAttribute !== strundefined ) {
+              elem.removeAttribute( internalKey );
+
+            } else {
+              elem[ internalKey ] = null;
+            }
+
+            deletedIds.push( id );
+          }
+        }
+      }
+    }
+  }
+});
+
+jQuery.fn.extend({
+  text: function( value ) {
+    return access( this, function( value ) {
+      return value === undefined ?
+        jQuery.text( this ) :
+        this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
+    }, null, value, arguments.length );
+  },
+
+  append: function() {
+    return this.domManip( arguments, function( elem ) {
+      if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
+        var target = manipulationTarget( this, elem );
+        target.appendChild( elem );
+      }
+    });
+  },
+
+  prepend: function() {
+    return this.domManip( arguments, function( elem ) {
+      if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
+        var target = manipulationTarget( this, elem );
+        target.insertBefore( elem, target.firstChild );
+      }
+    });
+  },
+
+  before: function() {
+    return this.domManip( arguments, function( elem ) {
+      if ( this.parentNode ) {
+        this.parentNode.insertBefore( elem, this );
+      }
+    });
+  },
+
+  after: function() {
+    return this.domManip( arguments, function( elem ) {
+      if ( this.parentNode ) {
+        this.parentNode.insertBefore( elem, this.nextSibling );
+      }
+    });
+  },
+
+  remove: function( selector, keepData /* Internal Use Only */ ) {
+    var elem,
+      elems = selector ? jQuery.filter( selector, this ) : this,
+      i = 0;
+
+    for ( ; (elem = elems[i]) != null; i++ ) {
+
+      if ( !keepData && elem.nodeType === 1 ) {
+        jQuery.cleanData( getAll( elem ) );
+      }
+
+      if ( elem.parentNode ) {
+        if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) {
+          setGlobalEval( getAll( elem, "script" ) );
+        }
+        elem.parentNode.removeChild( elem );
+      }
+    }
+
+    return this;
+  },
+
+  empty: function() {
+    var elem,
+      i = 0;
+
+    for ( ; (elem = this[i]) != null; i++ ) {
+      // Remove element nodes and prevent memory leaks
+      if ( elem.nodeType === 1 ) {
+        jQuery.cleanData( getAll( elem, false ) );
+      }
+
+      // Remove any remaining nodes
+      while ( elem.firstChild ) {
+        elem.removeChild( elem.firstChild );
+      }
+
+      // If this is a select, ensure that it displays empty (#12336)
+      // Support: IE<9
+      if ( elem.options && jQuery.nodeName( elem, "select" ) ) {
+        elem.options.length = 0;
+      }
+    }
+
+    return this;
+  },
+
+  clone: function( dataAndEvents, deepDataAndEvents ) {
+    dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
+    deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
+
+    return this.map(function() {
+      return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
+    });
+  },
+
+  html: function( value ) {
+    return access( this, function( value ) {
+      var elem = this[ 0 ] || {},
+        i = 0,
+        l = this.length;
+
+      if ( value === undefined ) {
+        return elem.nodeType === 1 ?
+          elem.innerHTML.replace( rinlinejQuery, "" ) :
+          undefined;
+      }
+
+      // See if we can take a shortcut and just use innerHTML
+      if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
+        ( support.htmlSerialize || !rnoshimcache.test( value )  ) &&
+        ( support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
+        !wrapMap[ (rtagName.exec( value ) || [ "", "" ])[ 1 ].toLowerCase() ] ) {
+
+        value = value.replace( rxhtmlTag, "<$1></$2>" );
+
+        try {
+          for (; i < l; i++ ) {
+            // Remove element nodes and prevent memory leaks
+            elem = this[i] || {};
+            if ( elem.nodeType === 1 ) {
+              jQuery.cleanData( getAll( elem, false ) );
+              elem.innerHTML = value;
+            }
+          }
+
+          elem = 0;
+
+        // If using innerHTML throws an exception, use the fallback method
+        } catch(e) {}
+      }
+
+      if ( elem ) {
+        this.empty().append( value );
+      }
+    }, null, value, arguments.length );
+  },
+
+  replaceWith: function() {
+    var arg = arguments[ 0 ];
+
+    // Make the changes, replacing each context element with the new content
+    this.domManip( arguments, function( elem ) {
+      arg = this.parentNode;
+
+      jQuery.cleanData( getAll( this ) );
+
+      if ( arg ) {
+        arg.replaceChild( elem, this );
+      }
+    });
+
+    // Force removal if there was no new content (e.g., from empty arguments)
+    return arg && (arg.length || arg.nodeType) ? this : this.remove();
+  },
+
+  detach: function( selector ) {
+    return this.remove( selector, true );
+  },
+
+  domManip: function( args, callback ) {
+
+    // Flatten any nested arrays
+    args = concat.apply( [], args );
+
+    var first, node, hasScripts,
+      scripts, doc, fragment,
+      i = 0,
+      l = this.length,
+      set = this,
+      iNoClone = l - 1,
+      value = args[0],
+      isFunction = jQuery.isFunction( value );
+
+    // We can't cloneNode fragments that contain checked, in WebKit
+    if ( isFunction ||
+        ( l > 1 && typeof value === "string" &&
+          !support.checkClone && rchecked.test( value ) ) ) {
+      return this.each(function( index ) {
+        var self = set.eq( index );
+        if ( isFunction ) {
+          args[0] = value.call( this, index, self.html() );
+        }
+        self.domManip( args, callback );
+      });
+    }
+
+    if ( l ) {
+      fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this );
+      first = fragment.firstChild;
+
+      if ( fragment.childNodes.length === 1 ) {
+        fragment = first;
+      }
+
+      if ( first ) {
+        scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
+        hasScripts = scripts.length;
+
+        // Use the original fragment for the last item instead of the first because it can end up
+        // being emptied incorrectly in certain situations (#8070).
+        for ( ; i < l; i++ ) {
+          node = fragment;
+
+          if ( i !== iNoClone ) {
+            node = jQuery.clone( node, true, true );
+
+            // Keep references to cloned scripts for later restoration
+            if ( hasScripts ) {
+              jQuery.merge( scripts, getAll( node, "script" ) );
+            }
+          }
+
+          callback.call( this[i], node, i );
+        }
+
+        if ( hasScripts ) {
+          doc = scripts[ scripts.length - 1 ].ownerDocument;
+
+          // Reenable scripts
+          jQuery.map( scripts, restoreScript );
+
+          // Evaluate executable scripts on first document insertion
+          for ( i = 0; i < hasScripts; i++ ) {
+            node = scripts[ i ];
+            if ( rscriptType.test( node.type || "" ) &&
+              !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) {
+
+              if ( node.src ) {
+                // Optional AJAX dependency, but won't run scripts if not present
+                if ( jQuery._evalUrl ) {
+                  jQuery._evalUrl( node.src );
+                }
+              } else {
+                jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) );
+              }
+            }
+          }
+        }
+
+        // Fix #11809: Avoid leaking memory
+        fragment = first = null;
+      }
+    }
+
+    return this;
+  }
+});
+
+jQuery.each({
+  appendTo: "append",
+  prependTo: "prepend",
+  insertBefore: "before",
+  insertAfter: "after",
+  replaceAll: "replaceWith"
+}, function( name, original ) {
+  jQuery.fn[ name ] = function( selector ) {
+    var elems,
+      i = 0,
+      ret = [],
+      insert = jQuery( selector ),
+      last = insert.length - 1;
+
+    for ( ; i <= last; i++ ) {
+      elems = i === last ? this : this.clone(true);
+      jQuery( insert[i] )[ original ]( elems );
+
+      // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get()
+      push.apply( ret, elems.get() );
+    }
+
+    return this.pushStack( ret );
+  };
+});
+
+
+var iframe,
+  elemdisplay = {};
+
+/**
+ * Retrieve the actual display of a element
+ * @param {String} name nodeName of the element
+ * @param {Object} doc Document object
+ */
+// Called only from within defaultDisplay
+function actualDisplay( name, doc ) {
+  var style,
+    elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),
+
+    // getDefaultComputedStyle might be reliably used only on attached element
+    display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ?
+
+      // Use of this method is a temporary fix (more like optmization) until something better comes along,
+      // since it was removed from specification and supported only in FF
+      style.display : jQuery.css( elem[ 0 ], "display" );
+
+  // We don't have any data stored on the element,
+  // so use "detach" method as fast way to get rid of the element
+  elem.detach();
+
+  return display;
+}
+
+/**
+ * Try to determine the default display value of an element
+ * @param {String} nodeName
+ */
+function defaultDisplay( nodeName ) {
+  var doc = document,
+    display = elemdisplay[ nodeName ];
+
+  if ( !display ) {
+    display = actualDisplay( nodeName, doc );
+
+    // If the simple way fails, read from inside an iframe
+    if ( display === "none" || !display ) {
+
+      // Use the already-created iframe if possible
+      iframe = (iframe || jQuery( "<iframe frameborder='0' width='0' height='0'/>" )).appendTo( doc.documentElement );
+
+      // Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
+      doc = ( iframe[ 0 ].contentWindow || iframe[ 0 ].contentDocument ).document;
+
+      // Support: IE
+      doc.write();
+      doc.close();
+
+      display = actualDisplay( nodeName, doc );
+      iframe.detach();
+    }
+
+    // Store the correct default display
+    elemdisplay[ nodeName ] = display;
+  }
+
+  return display;
+}
+
+
+(function() {
+  var shrinkWrapBlocksVal;
+
+  support.shrinkWrapBlocks = function() {
+    if ( shrinkWrapBlocksVal != null ) {
+      return shrinkWrapBlocksVal;
+    }
+
+    // Will be changed later if needed.
+    shrinkWrapBlocksVal = false;
+
+    // Minified: var b,c,d
+    var div, body, container;
+
+    body = document.getElementsByTagName( "body" )[ 0 ];
+    if ( !body || !body.style ) {
+      // Test fired too early or in an unsupported environment, exit.
+      return;
+    }
+
+    // Setup
+    div = document.createElement( "div" );
+    container = document.createElement( "div" );
+    container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px";
+    body.appendChild( container ).appendChild( div );
+
+    // Support: IE6
+    // Check if elements with layout shrink-wrap their children
+    if ( typeof div.style.zoom !== strundefined ) {
+      // Reset CSS: box-sizing; display; margin; border
+      div.style.cssText =
+        // Support: Firefox<29, Android 2.3
+        // Vendor-prefix box-sizing
+        "-webkit-box-sizing:content-box;-moz-box-sizing:content-box;" +
+        "box-sizing:content-box;display:block;margin:0;border:0;" +
+        "padding:1px;width:1px;zoom:1";
+      div.appendChild( document.createElement( "div" ) ).style.width = "5px";
+      shrinkWrapBlocksVal = div.offsetWidth !== 3;
+    }
+
+    body.removeChild( container );
+
+    return shrinkWrapBlocksVal;
+  };
+
+})();
+var rmargin = (/^margin/);
+
+var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
+
+
+
+var getStyles, curCSS,
+  rposition = /^(top|right|bottom|left)$/;
+
+if ( window.getComputedStyle ) {
+  getStyles = function( elem ) {
+    return elem.ownerDocument.defaultView.getComputedStyle( elem, null );
+  };
+
+  curCSS = function( elem, name, computed ) {
+    var width, minWidth, maxWidth, ret,
+      style = elem.style;
+
+    computed = computed || getStyles( elem );
+
+    // getPropertyValue is only needed for .css('filter') in IE9, see #12537
+    ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined;
+
+    if ( computed ) {
+
+      if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
+        ret = jQuery.style( elem, name );
+      }
+
+      // A tribute to the "awesome hack by Dean Edwards"
+      // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right
+      // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
+      // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
+      if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
+
+        // Remember the original values
+        width = style.width;
+        minWidth = style.minWidth;
+        maxWidth = style.maxWidth;
+
+        // Put in the new values to get a computed value out
+        style.minWidth = style.maxWidth = style.width = ret;
+        ret = computed.width;
+
+        // Revert the changed values
+        style.width = width;
+        style.minWidth = minWidth;
+        style.maxWidth = maxWidth;
+      }
+    }
+
+    // Support: IE
+    // IE returns zIndex value as an integer.
+    return ret === undefined ?
+      ret :
+      ret + "";
+  };
+} else if ( document.documentElement.currentStyle ) {
+  getStyles = function( elem ) {
+    return elem.currentStyle;
+  };
+
+  curCSS = function( elem, name, computed ) {
+    var left, rs, rsLeft, ret,
+      style = elem.style;
+
+    computed = computed || getStyles( elem );
+    ret = computed ? computed[ name ] : undefined;
+
+    // Avoid setting ret to empty string here
+    // so we don't default to auto
+    if ( ret == null && style && style[ name ] ) {
+      ret = style[ name ];
+    }
+
+    // From the awesome hack by Dean Edwards
+    // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+    // If we're not dealing with a regular pixel number
+    // but a number that has a weird ending, we need to convert it to pixels
+    // but not position css attributes, as those are proportional to the parent element instead
+    // and we can't measure the parent instead because it might trigger a "stacking dolls" problem
+    if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) {
+
+      // Remember the original values
+      left = style.left;
+      rs = elem.runtimeStyle;
+      rsLeft = rs && rs.left;
+
+      // Put in the new values to get a computed value out
+      if ( rsLeft ) {
+        rs.left = elem.currentStyle.left;
+      }
+      style.left = name === "fontSize" ? "1em" : ret;
+      ret = style.pixelLeft + "px";
+
+      // Revert the changed values
+      style.left = left;
+      if ( rsLeft ) {
+        rs.left = rsLeft;
+      }
+    }
+
+    // Support: IE
+    // IE returns zIndex value as an integer.
+    return ret === undefined ?
+      ret :
+      ret + "" || "auto";
+  };
+}
+
+
+
+
+function addGetHookIf( conditionFn, hookFn ) {
+  // Define the hook, we'll check on the first run if it's really needed.
+  return {
+    get: function() {
+      var condition = conditionFn();
+
+      if ( condition == null ) {
+        // The test was not ready at this point; screw the hook this time
+        // but check again when needed next time.
+        return;
+      }
+
+      if ( condition ) {
+        // Hook not needed (or it's not possible to use it due to missing dependency),
+        // remove it.
+        // Since there are no other hooks for marginRight, remove the whole object.
+        delete this.get;
+        return;
+      }
+
+      // Hook needed; redefine it so that the support test is not executed again.
+
+      return (this.get = hookFn).apply( this, arguments );
+    }
+  };
+}
+
+
+(function() {
+  // Minified: var b,c,d,e,f,g, h,i
+  var div, style, a, pixelPositionVal, boxSizingReliableVal,
+    reliableHiddenOffsetsVal, reliableMarginRightVal;
+
+  // Setup
+  div = document.createElement( "div" );
+  div.innerHTML = "  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
+  a = div.getElementsByTagName( "a" )[ 0 ];
+  style = a && a.style;
+
+  // Finish early in limited (non-browser) environments
+  if ( !style ) {
+    return;
+  }
+
+  style.cssText = "float:left;opacity:.5";
+
+  // Support: IE<9
+  // Make sure that element opacity exists (as opposed to filter)
+  support.opacity = style.opacity === "0.5";
+
+  // Verify style float existence
+  // (IE uses styleFloat instead of cssFloat)
+  support.cssFloat = !!style.cssFloat;
+
+  div.style.backgroundClip = "content-box";
+  div.cloneNode( true ).style.backgroundClip = "";
+  support.clearCloneStyle = div.style.backgroundClip === "content-box";
+
+  // Support: Firefox<29, Android 2.3
+  // Vendor-prefix box-sizing
+  support.boxSizing = style.boxSizing === "" || style.MozBoxSizing === "" ||
+    style.WebkitBoxSizing === "";
+
+  jQuery.extend(support, {
+    reliableHiddenOffsets: function() {
+      if ( reliableHiddenOffsetsVal == null ) {
+        computeStyleTests();
+      }
+      return reliableHiddenOffsetsVal;
+    },
+
+    boxSizingReliable: function() {
+      if ( boxSizingReliableVal == null ) {
+        computeStyleTests();
+      }
+      return boxSizingReliableVal;
+    },
+
+    pixelPosition: function() {
+      if ( pixelPositionVal == null ) {
+        computeStyleTests();
+      }
+      return pixelPositionVal;
+    },
+
+    // Support: Android 2.3
+    reliableMarginRight: function() {
+      if ( reliableMarginRightVal == null ) {
+        computeStyleTests();
+      }
+      return reliableMarginRightVal;
+    }
+  });
+
+  function computeStyleTests() {
+    // Minified: var b,c,d,j
+    var div, body, container, contents;
+
+    body = document.getElementsByTagName( "body" )[ 0 ];
+    if ( !body || !body.style ) {
+      // Test fired too early or in an unsupported environment, exit.
+      return;
+    }
+
+    // Setup
+    div = document.createElement( "div" );
+    container = document.createElement( "div" );
+    container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px";
+    body.appendChild( container ).appendChild( div );
+
+    div.style.cssText =
+      // Support: Firefox<29, Android 2.3
+      // Vendor-prefix box-sizing
+      "-webkit-box-sizing:border-box;-moz-box-sizing:border-box;" +
+      "box-sizing:border-box;display:block;margin-top:1%;top:1%;" +
+      "border:1px;padding:1px;width:4px;position:absolute";
+
+    // Support: IE<9
+    // Assume reasonable values in the absence of getComputedStyle
+    pixelPositionVal = boxSizingReliableVal = false;
+    reliableMarginRightVal = true;
+
+    // Check for getComputedStyle so that this code is not run in IE<9.
+    if ( window.getComputedStyle ) {
+      pixelPositionVal = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
+      boxSizingReliableVal =
+        ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";
+
+      // Support: Android 2.3
+      // Div with explicit width and no margin-right incorrectly
+      // gets computed margin-right based on width of container (#3333)
+      // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+      contents = div.appendChild( document.createElement( "div" ) );
+
+      // Reset CSS: box-sizing; display; margin; border; padding
+      contents.style.cssText = div.style.cssText =
+        // Support: Firefox<29, Android 2.3
+        // Vendor-prefix box-sizing
+        "-webkit-box-sizing:content-box;-moz-box-sizing:content-box;" +
+        "box-sizing:content-box;display:block;margin:0;border:0;padding:0";
+      contents.style.marginRight = contents.style.width = "0";
+      div.style.width = "1px";
+
+      reliableMarginRightVal =
+        !parseFloat( ( window.getComputedStyle( contents, null ) || {} ).marginRight );
+    }
+
+    // Support: IE8
+    // Check if table cells still have offsetWidth/Height when they are set
+    // to display:none and there are still other visible table cells in a
+    // table row; if so, offsetWidth/Height are not reliable for use when
+    // determining if an element has been hidden directly using
+    // display:none (it is still safe to use offsets if a parent element is
+    // hidden; don safety goggles and see bug #4512 for more information).
+    div.innerHTML = "<table><tr><td></td><td>t</td></tr></table>";
+    contents = div.getElementsByTagName( "td" );
+    contents[ 0 ].style.cssText = "margin:0;border:0;padding:0;display:none";
+    reliableHiddenOffsetsVal = contents[ 0 ].offsetHeight === 0;
+    if ( reliableHiddenOffsetsVal ) {
+      contents[ 0 ].style.display = "";
+      contents[ 1 ].style.display = "none";
+      reliableHiddenOffsetsVal = contents[ 0 ].offsetHeight === 0;
+    }
+
+    body.removeChild( container );
+  }
+
+})();
+
+
+// A method for quickly swapping in/out CSS properties to get correct calculations.
+jQuery.swap = function( elem, options, callback, args ) {
+  var ret, name,
+    old = {};
+
+  // Remember the old values, and insert the new ones
+  for ( name in options ) {
+    old[ name ] = elem.style[ name ];
+    elem.style[ name ] = options[ name ];
+  }
+
+  ret = callback.apply( elem, args || [] );
+
+  // Revert the old values
+  for ( name in options ) {
+    elem.style[ name ] = old[ name ];
+  }
+
+  return ret;
+};
+
+
+var
+    ralpha = /alpha\([^)]*\)/i,
+  ropacity = /opacity\s*=\s*([^)]*)/,
+
+  // swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
+  // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
+  rdisplayswap = /^(none|table(?!-c[ea]).+)/,
+  rnumsplit = new RegExp( "^(" + pnum + ")(.*)$", "i" ),
+  rrelNum = new RegExp( "^([+-])=(" + pnum + ")", "i" ),
+
+  cssShow = { position: "absolute", visibility: "hidden", display: "block" },
+  cssNormalTransform = {
+    letterSpacing: "0",
+    fontWeight: "400"
+  },
+
+  cssPrefixes = [ "Webkit", "O", "Moz", "ms" ];
+
+
+// return a css property mapped to a potentially vendor prefixed property
+function vendorPropName( style, name ) {
+
+  // shortcut for names that are not vendor prefixed
+  if ( name in style ) {
+    return name;
+  }
+
+  // check for vendor prefixed names
+  var capName = name.charAt(0).toUpperCase() + name.slice(1),
+    origName = name,
+    i = cssPrefixes.length;
+
+  while ( i-- ) {
+    name = cssPrefixes[ i ] + capName;
+    if ( name in style ) {
+      return name;
+    }
+  }
+
+  return origName;
+}
+
+function showHide( elements, show ) {
+  var display, elem, hidden,
+    values = [],
+    index = 0,
+    length = elements.length;
+
+  for ( ; index < length; index++ ) {
+    elem = elements[ index ];
+    if ( !elem.style ) {
+      continue;
+    }
+
+    values[ index ] = jQuery._data( elem, "olddisplay" );
+    display = elem.style.display;
+    if ( show ) {
+      // Reset the inline display of this element to learn if it is
+      // being hidden by cascaded rules or not
+      if ( !values[ index ] && display === "none" ) {
+        elem.style.display = "";
+      }
+
+      // Set elements which have been overridden with display: none
+      // in a stylesheet to whatever the default browser style is
+      // for such an element
+      if ( elem.style.display === "" && isHidden( elem ) ) {
+        values[ index ] = jQuery._data( elem, "olddisplay", defaultDisplay(elem.nodeName) );
+      }
+    } else {
+      hidden = isHidden( elem );
+
+      if ( display && display !== "none" || !hidden ) {
+        jQuery._data( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) );
+      }
+    }
+  }
+
+  // Set the display of most of the elements in a second loop
+  // to avoid the constant reflow
+  for ( index = 0; index < length; index++ ) {
+    elem = elements[ index ];
+    if ( !elem.style ) {
+      continue;
+    }
+    if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
+      elem.style.display = show ? values[ index ] || "" : "none";
+    }
+  }
+
+  return elements;
+}
+
+function setPositiveNumber( elem, value, subtract ) {
+  var matches = rnumsplit.exec( value );
+  return matches ?
+    // Guard against undefined "subtract", e.g., when used as in cssHooks
+    Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
+    value;
+}
+
+function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
+  var i = extra === ( isBorderBox ? "border" : "content" ) ?
+    // If we already have the right measurement, avoid augmentation
+    4 :
+    // Otherwise initialize for horizontal or vertical properties
+    name === "width" ? 1 : 0,
+
+    val = 0;
+
+  for ( ; i < 4; i += 2 ) {
+    // both box models exclude margin, so add it if we want it
+    if ( extra === "margin" ) {
+      val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
+    }
+
+    if ( isBorderBox ) {
+      // border-box includes padding, so remove it if we want content
+      if ( extra === "content" ) {
+        val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
+      }
+
+      // at this point, extra isn't border nor margin, so remove border
+      if ( extra !== "margin" ) {
+        val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
+      }
+    } else {
+      // at this point, extra isn't content, so add padding
+      val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
+
+      // at this point, extra isn't content nor padding, so add border
+      if ( extra !== "padding" ) {
+        val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
+      }
+    }
+  }
+
+  return val;
+}
+
+function getWidthOrHeight( elem, name, extra ) {
+
+  // Start with offset property, which is equivalent to the border-box value
+  var valueIsBorderBox = true,
+    val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
+    styles = getStyles( elem ),
+    isBorderBox = support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
+
+  // some non-html elements return undefined for offsetWidth, so check for null/undefined
+  // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
+  // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
+  if ( val <= 0 || val == null ) {
+    // Fall back to computed then uncomputed css if necessary
+    val = curCSS( elem, name, styles );
+    if ( val < 0 || val == null ) {
+      val = elem.style[ name ];
+    }
+
+    // Computed unit is not pixels. Stop here and return.
+    if ( rnumnonpx.test(val) ) {
+      return val;
+    }
+
+    // we need the check for style in case a browser which returns unreliable values
+    // for getComputedStyle silently falls back to the reliable elem.style
+    valueIsBorderBox = isBorderBox && ( support.boxSizingReliable() || val === elem.style[ name ] );
+
+    // Normalize "", auto, and prepare for extra
+    val = parseFloat( val ) || 0;
+  }
+
+  // use the active box-sizing model to add/subtract irrelevant styles
+  return ( val +
+    augmentWidthOrHeight(
+      elem,
+      name,
+      extra || ( isBorderBox ? "border" : "content" ),
+      valueIsBorderBox,
+      styles
+    )
+  ) + "px";
+}
+
+jQuery.extend({
+  // Add in style property hooks for overriding the default
+  // behavior of getting and setting a style property
+  cssHooks: {
+    opacity: {
+      get: function( elem, computed ) {
+        if ( computed ) {
+          // We should always get a number back from opacity
+          var ret = curCSS( elem, "opacity" );
+          return ret === "" ? "1" : ret;
+        }
+      }
+    }
+  },
+
+  // Don't automatically add "px" to these possibly-unitless properties
+  cssNumber: {
+    "columnCount": true,
+    "fillOpacity": true,
+    "flexGrow": true,
+    "flexShrink": true,
+    "fontWeight": true,
+    "lineHeight": true,
+    "opacity": true,
+    "order": true,
+    "orphans": true,
+    "widows": true,
+    "zIndex": true,
+    "zoom": true
+  },
+
+  // Add in properties whose names you wish to fix before
+  // setting or getting the value
+  cssProps: {
+    // normalize float css property
+    "float": support.cssFloat ? "cssFloat" : "styleFloat"
+  },
+
+  // Get and set the style property on a DOM Node
+  style: function( elem, name, value, extra ) {
+    // Don't set styles on text and comment nodes
+    if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
+      return;
+    }
+
+    // Make sure that we're working with the right name
+    var ret, type, hooks,
+      origName = jQuery.camelCase( name ),
+      style = elem.style;
+
+    name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
+
+    // gets hook for the prefixed version
+    // followed by the unprefixed version
+    hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
+
+    // Check if we're setting a value
+    if ( value !== undefined ) {
+      type = typeof value;
+
+      // convert relative number strings (+= or -=) to relative numbers. #7345
+      if ( type === "string" && (ret = rrelNum.exec( value )) ) {
+        value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
+        // Fixes bug #9237
+        type = "number";
+      }
+
+      // Make sure that null and NaN values aren't set. See: #7116
+      if ( value == null || value !== value ) {
+        return;
+      }
+
+      // If a number was passed in, add 'px' to the (except for certain CSS properties)
+      if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
+        value += "px";
+      }
+
+      // Fixes #8908, it can be done more correctly by specifing setters in cssHooks,
+      // but it would mean to define eight (for every problematic property) identical functions
+      if ( !support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) {
+        style[ name ] = "inherit";
+      }
+
+      // If a hook was provided, use that value, otherwise just set the specified value
+      if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
+
+        // Support: IE
+        // Swallow errors from 'invalid' CSS values (#5509)
+        try {
+          style[ name ] = value;
+        } catch(e) {}
+      }
+
+    } else {
+      // If a hook was provided get the non-computed value from there
+      if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
+        return ret;
+      }
+
+      // Otherwise just get the value from the style object
+      return style[ name ];
+    }
+  },
+
+  css: function( elem, name, extra, styles ) {
+    var num, val, hooks,
+      origName = jQuery.camelCase( name );
+
+    // Make sure that we're working with the right name
+    name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
+
+    // gets hook for the prefixed version
+    // followed by the unprefixed version
+    hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
+
+    // If a hook was provided get the computed value from there
+    if ( hooks && "get" in hooks ) {
+      val = hooks.get( elem, true, extra );
+    }
+
+    // Otherwise, if a way to get the computed value exists, use that
+    if ( val === undefined ) {
+      val = curCSS( elem, name, styles );
+    }
+
+    //convert "normal" to computed value
+    if ( val === "normal" && name in cssNormalTransform ) {
+      val = cssNormalTransform[ name ];
+    }
+
+    // Return, converting to number if forced or a qualifier was provided and val looks numeric
+    if ( extra === "" || extra ) {
+      num = parseFloat( val );
+      return extra === true || jQuery.isNumeric( num ) ? num || 0 : val;
+    }
+    return val;
+  }
+});
+
+jQuery.each([ "height", "width" ], function( i, name ) {
+  jQuery.cssHooks[ name ] = {
+    get: function( elem, computed, extra ) {
+      if ( computed ) {
+        // certain elements can have dimension info if we invisibly show them
+        // however, it must have a current display style that would benefit from this
+        return rdisplayswap.test( jQuery.css( elem, "display" ) ) && elem.offsetWidth === 0 ?
+          jQuery.swap( elem, cssShow, function() {
+            return getWidthOrHeight( elem, name, extra );
+          }) :
+          getWidthOrHeight( elem, name, extra );
+      }
+    },
+
+    set: function( elem, value, extra ) {
+      var styles = extra && getStyles( elem );
+      return setPositiveNumber( elem, value, extra ?
+        augmentWidthOrHeight(
+          elem,
+          name,
+          extra,
+          support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
+          styles
+        ) : 0
+      );
+    }
+  };
+});
+
+if ( !support.opacity ) {
+  jQuery.cssHooks.opacity = {
+    get: function( elem, computed ) {
+      // IE uses filters for opacity
+      return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
+        ( 0.01 * parseFloat( RegExp.$1 ) ) + "" :
+        computed ? "1" : "";
+    },
+
+    set: function( elem, value ) {
+      var style = elem.style,
+        currentStyle = elem.currentStyle,
+        opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
+        filter = currentStyle && currentStyle.filter || style.filter || "";
+
+      // IE has trouble with opacity if it does not have layout
+      // Force it by setting the zoom level
+      style.zoom = 1;
+
+      // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652
+      // if value === "", then remove inline opacity #12685
+      if ( ( value >= 1 || value === "" ) &&
+          jQuery.trim( filter.replace( ralpha, "" ) ) === "" &&
+          style.removeAttribute ) {
+
+        // Setting style.filter to null, "" & " " still leave "filter:" in the cssText
+        // if "filter:" is present at all, clearType is disabled, we want to avoid this
+        // style.removeAttribute is IE Only, but so apparently is this code path...
+        style.removeAttribute( "filter" );
+
+        // if there is no filter style applied in a css rule or unset inline opacity, we are done
+        if ( value === "" || currentStyle && !currentStyle.filter ) {
+          return;
+        }
+      }
+
+      // otherwise, set new filter values
+      style.filter = ralpha.test( filter ) ?
+        filter.replace( ralpha, opacity ) :
+        filter + " " + opacity;
+    }
+  };
+}
+
+jQuery.cssHooks.marginRight = addGetHookIf( support.reliableMarginRight,
+  function( elem, computed ) {
+    if ( computed ) {
+      // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+      // Work around by temporarily setting element display to inline-block
+      return jQuery.swap( elem, { "display": "inline-block" },
+        curCSS, [ elem, "marginRight" ] );
+    }
+  }
+);
+
+// These hooks are used by animate to expand properties
+jQuery.each({
+  margin: "",
+  padding: "",
+  border: "Width"
+}, function( prefix, suffix ) {
+  jQuery.cssHooks[ prefix + suffix ] = {
+    expand: function( value ) {
+      var i = 0,
+        expanded = {},
+
+        // assumes a single number if not a string
+        parts = typeof value === "string" ? value.split(" ") : [ value ];
+
+      for ( ; i < 4; i++ ) {
+        expanded[ prefix + cssExpand[ i ] + suffix ] =
+          parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
+      }
+
+      return expanded;
+    }
+  };
+
+  if ( !rmargin.test( prefix ) ) {
+    jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
+  }
+});
+
+jQuery.fn.extend({
+  css: function( name, value ) {
+    return access( this, function( elem, name, value ) {
+      var styles, len,
+        map = {},
+        i = 0;
+
+      if ( jQuery.isArray( name ) ) {
+        styles = getStyles( elem );
+        len = name.length;
+
+        for ( ; i < len; i++ ) {
+          map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
+        }
+
+        return map;
+      }
+
+      return value !== undefined ?
+        jQuery.style( elem, name, value ) :
+        jQuery.css( elem, name );
+    }, name, value, arguments.length > 1 );
+  },
+  show: function() {
+    return showHide( this, true );
+  },
+  hide: function() {
+    return showHide( this );
+  },
+  toggle: function( state ) {
+    if ( typeof state === "boolean" ) {
+      return state ? this.show() : this.hide();
+    }
+
+    return this.each(function() {
+      if ( isHidden( this ) ) {
+        jQuery( this ).show();
+      } else {
+        jQuery( this ).hide();
+      }
+    });
+  }
+});
+
+
+function Tween( elem, options, prop, end, easing ) {
+  return new Tween.prototype.init( elem, options, prop, end, easing );
+}
+jQuery.Tween = Tween;
+
+Tween.prototype = {
+  constructor: Tween,
+  init: function( elem, options, prop, end, easing, unit ) {
+    this.elem = elem;
+    this.prop = prop;
+    this.easing = easing || "swing";
+    this.options = options;
+    this.start = this.now = this.cur();
+    this.end = end;
+    this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
+  },
+  cur: function() {
+    var hooks = Tween.propHooks[ this.prop ];
+
+    return hooks && hooks.get ?
+      hooks.get( this ) :
+      Tween.propHooks._default.get( this );
+  },
+  run: function( percent ) {
+    var eased,
+      hooks = Tween.propHooks[ this.prop ];
+
+    if ( this.options.duration ) {
+      this.pos = eased = jQuery.easing[ this.easing ](
+        percent, this.options.duration * percent, 0, 1, this.options.duration
+      );
+    } else {
+      this.pos = eased = percent;
+    }
+    this.now = ( this.end - this.start ) * eased + this.start;
+
+    if ( this.options.step ) {
+      this.options.step.call( this.elem, this.now, this );
+    }
+
+    if ( hooks && hooks.set ) {
+      hooks.set( this );
+    } else {
+      Tween.propHooks._default.set( this );
+    }
+    return this;
+  }
+};
+
+Tween.prototype.init.prototype = Tween.prototype;
+
+Tween.propHooks = {
+  _default: {
+    get: function( tween ) {
+      var result;
+
+      if ( tween.elem[ tween.prop ] != null &&
+        (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
+        return tween.elem[ tween.prop ];
+      }
+
+      // passing an empty string as a 3rd parameter to .css will automatically
+      // attempt a parseFloat and fallback to a string if the parse fails
+      // so, simple values such as "10px" are parsed to Float.
+      // complex values such as "rotate(1rad)" are returned as is.
+      result = jQuery.css( tween.elem, tween.prop, "" );
+      // Empty strings, null, undefined and "auto" are converted to 0.
+      return !result || result === "auto" ? 0 : result;
+    },
+    set: function( tween ) {
+      // use step hook for back compat - use cssHook if its there - use .style if its
+      // available and use plain properties where available
+      if ( jQuery.fx.step[ tween.prop ] ) {
+        jQuery.fx.step[ tween.prop ]( tween );
+      } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
+        jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
+      } else {
+        tween.elem[ tween.prop ] = tween.now;
+      }
+    }
+  }
+};
+
+// Support: IE <=9
+// Panic based approach to setting things on disconnected nodes
+
+Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
+  set: function( tween ) {
+    if ( tween.elem.nodeType && tween.elem.parentNode ) {
+      tween.elem[ tween.prop ] = tween.now;
+    }
+  }
+};
+
+jQuery.easing = {
+  linear: function( p ) {
+    return p;
+  },
+  swing: function( p ) {
+    return 0.5 - Math.cos( p * Math.PI ) / 2;
+  }
+};
+
+jQuery.fx = Tween.prototype.init;
+
+// Back Compat <1.8 extension point
+jQuery.fx.step = {};
+
+
+
+
+var
+  fxNow, timerId,
+  rfxtypes = /^(?:toggle|show|hide)$/,
+  rfxnum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ),
+  rrun = /queueHooks$/,
+  animationPrefilters = [ defaultPrefilter ],
+  tweeners = {
+    "*": [ function( prop, value ) {
+      var tween = this.createTween( prop, value ),
+        target = tween.cur(),
+        parts = rfxnum.exec( value ),
+        unit = parts && parts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
+
+        // Starting value computation is required for potential unit mismatches
+        start = ( jQuery.cssNumber[ prop ] || unit !== "px" && +target ) &&
+          rfxnum.exec( jQuery.css( tween.elem, prop ) ),
+        scale = 1,
+        maxIterations = 20;
+
+      if ( start && start[ 3 ] !== unit ) {
+        // Trust units reported by jQuery.css
+        unit = unit || start[ 3 ];
+
+        // Make sure we update the tween properties later on
+        parts = parts || [];
+
+        // Iteratively approximate from a nonzero starting point
+        start = +target || 1;
+
+        do {
+          // If previous iteration zeroed out, double until we get *something*
+          // Use a string for doubling factor so we don't accidentally see scale as unchanged below
+          scale = scale || ".5";
+
+          // Adjust and apply
+          start = start / scale;
+          jQuery.style( tween.elem, prop, start + unit );
+
+        // Update scale, tolerating zero or NaN from tween.cur()
+        // And breaking the loop if scale is unchanged or perfect, or if we've just had enough
+        } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
+      }
+
+      // Update tween properties
+      if ( parts ) {
+        start = tween.start = +start || +target || 0;
+        tween.unit = unit;
+        // If a +=/-= token was provided, we're doing a relative animation
+        tween.end = parts[ 1 ] ?
+          start + ( parts[ 1 ] + 1 ) * parts[ 2 ] :
+          +parts[ 2 ];
+      }
+
+      return tween;
+    } ]
+  };
+
+// Animations created synchronously will run synchronously
+function createFxNow() {
+  setTimeout(function() {
+    fxNow = undefined;
+  });
+  return ( fxNow = jQuery.now() );
+}
+
+// Generate parameters to create a standard animation
+function genFx( type, includeWidth ) {
+  var which,
+    attrs = { height: type },
+    i = 0;
+
+  // if we include width, step value is 1 to do all cssExpand values,
+  // if we don't include width, step value is 2 to skip over Left and Right
+  includeWidth = includeWidth ? 1 : 0;
+  for ( ; i < 4 ; i += 2 - includeWidth ) {
+    which = cssExpand[ i ];
+    attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
+  }
+
+  if ( includeWidth ) {
+    attrs.opacity = attrs.width = type;
+  }
+
+  return attrs;
+}
+
+function createTween( value, prop, animation ) {
+  var tween,
+    collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
+    index = 0,
+    length = collection.length;
+  for ( ; index < length; index++ ) {
+    if ( (tween = collection[ index ].call( animation, prop, value )) ) {
+
+      // we're done with this property
+      return tween;
+    }
+  }
+}
+
+function defaultPrefilter( elem, props, opts ) {
+  /* jshint validthis: true */
+  var prop, value, toggle, tween, hooks, oldfire, display, checkDisplay,
+    anim = this,
+    orig = {},
+    style = elem.style,
+    hidden = elem.nodeType && isHidden( elem ),
+    dataShow = jQuery._data( elem, "fxshow" );
+
+  // handle queue: false promises
+  if ( !opts.queue ) {
+    hooks = jQuery._queueHooks( elem, "fx" );
+    if ( hooks.unqueued == null ) {
+      hooks.unqueued = 0;
+      oldfire = hooks.empty.fire;
+      hooks.empty.fire = function() {
+        if ( !hooks.unqueued ) {
+          oldfire();
+        }
+      };
+    }
+    hooks.unqueued++;
+
+    anim.always(function() {
+      // doing this makes sure that the complete handler will be called
+      // before this completes
+      anim.always(function() {
+        hooks.unqueued--;
+        if ( !jQuery.queue( elem, "fx" ).length ) {
+          hooks.empty.fire();
+        }
+      });
+    });
+  }
+
+  // height/width overflow pass
+  if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
+    // Make sure that nothing sneaks out
+    // Record all 3 overflow attributes because IE does not
+    // change the overflow attribute when overflowX and
+    // overflowY are set to the same value
+    opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
+
+    // Set display property to inline-block for height/width
+    // animations on inline elements that are having width/height animated
+    display = jQuery.css( elem, "display" );
+
+    // Test default display if display is currently "none"
+    checkDisplay = display === "none" ?
+      jQuery._data( elem, "olddisplay" ) || defaultDisplay( elem.nodeName ) : display;
+
+    if ( checkDisplay === "inline" && jQuery.css( elem, "float" ) === "none" ) {
+
+      // inline-level elements accept inline-block;
+      // block-level elements need to be inline with layout
+      if ( !support.inlineBlockNeedsLayout || defaultDisplay( elem.nodeName ) === "inline" ) {
+        style.display = "inline-block";
+      } else {
+        style.zoom = 1;
+      }
+    }
+  }
+
+  if ( opts.overflow ) {
+    style.overflow = "hidden";
+    if ( !support.shrinkWrapBlocks() ) {
+      anim.always(function() {
+        style.overflow = opts.overflow[ 0 ];
+        style.overflowX = opts.overflow[ 1 ];
+        style.overflowY = opts.overflow[ 2 ];
+      });
+    }
+  }
+
+  // show/hide pass
+  for ( prop in props ) {
+    value = props[ prop ];
+    if ( rfxtypes.exec( value ) ) {
+      delete props[ prop ];
+      toggle = toggle || value === "toggle";
+      if ( value === ( hidden ? "hide" : "show" ) ) {
+
+        // If there is dataShow left over from a stopped hide or show and we are going to proceed with show, we should pretend to be hidden
+        if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
+          hidden = true;
+        } else {
+          continue;
+        }
+      }
+      orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
+
+    // Any non-fx value stops us from restoring the original display value
+    } else {
+      display = undefined;
+    }
+  }
+
+  if ( !jQuery.isEmptyObject( orig ) ) {
+    if ( dataShow ) {
+      if ( "hidden" in dataShow ) {
+        hidden = dataShow.hidden;
+      }
+    } else {
+      dataShow = jQuery._data( elem, "fxshow", {} );
+    }
+
+    // store state if its toggle - enables .stop().toggle() to "reverse"
+    if ( toggle ) {
+      dataShow.hidden = !hidden;
+    }
+    if ( hidden ) {
+      jQuery( elem ).show();
+    } else {
+      anim.done(function() {
+        jQuery( elem ).hide();
+      });
+    }
+    anim.done(function() {
+      var prop;
+      jQuery._removeData( elem, "fxshow" );
+      for ( prop in orig ) {
+        jQuery.style( elem, prop, orig[ prop ] );
+      }
+    });
+    for ( prop in orig ) {
+      tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
+
+      if ( !( prop in dataShow ) ) {
+        dataShow[ prop ] = tween.start;
+        if ( hidden ) {
+          tween.end = tween.start;
+          tween.start = prop === "width" || prop === "height" ? 1 : 0;
+        }
+      }
+    }
+
+  // If this is a noop like .hide().hide(), restore an overwritten display value
+  } else if ( (display === "none" ? defaultDisplay( elem.nodeName ) : display) === "inline" ) {
+    style.display = display;
+  }
+}
+
+function propFilter( props, specialEasing ) {
+  var index, name, easing, value, hooks;
+
+  // camelCase, specialEasing and expand cssHook pass
+  for ( index in props ) {
+    name = jQuery.camelCase( index );
+    easing = specialEasing[ name ];
+    value = props[ index ];
+    if ( jQuery.isArray( value ) ) {
+      easing = value[ 1 ];
+      value = props[ index ] = value[ 0 ];
+    }
+
+    if ( index !== name ) {
+      props[ name ] = value;
+      delete props[ index ];
+    }
+
+    hooks = jQuery.cssHooks[ name ];
+    if ( hooks && "expand" in hooks ) {
+      value = hooks.expand( value );
+      delete props[ name ];
+
+      // not quite $.extend, this wont overwrite keys already present.
+      // also - reusing 'index' from above because we have the correct "name"
+      for ( index in value ) {
+        if ( !( index in props ) ) {
+          props[ index ] = value[ index ];
+          specialEasing[ index ] = easing;
+        }
+      }
+    } else {
+      specialEasing[ name ] = easing;
+    }
+  }
+}
+
+function Animation( elem, properties, options ) {
+  var result,
+    stopped,
+    index = 0,
+    length = animationPrefilters.length,
+    deferred = jQuery.Deferred().always( function() {
+      // don't match elem in the :animated selector
+      delete tick.elem;
+    }),
+    tick = function() {
+      if ( stopped ) {
+        return false;
+      }
+      var currentTime = fxNow || createFxNow(),
+        remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
+        // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
+        temp = remaining / animation.duration || 0,
+        percent = 1 - temp,
+        index = 0,
+        length = animation.tweens.length;
+
+      for ( ; index < length ; index++ ) {
+        animation.tweens[ index ].run( percent );
+      }
+
+      deferred.notifyWith( elem, [ animation, percent, remaining ]);
+
+      if ( percent < 1 && length ) {
+        return remaining;
+      } else {
+        deferred.resolveWith( elem, [ animation ] );
+        return false;
+      }
+    },
+    animation = deferred.promise({
+      elem: elem,
+      props: jQuery.extend( {}, properties ),
+      opts: jQuery.extend( true, { specialEasing: {} }, options ),
+      originalProperties: properties,
+      originalOptions: options,
+      startTime: fxNow || createFxNow(),
+      duration: options.duration,
+      tweens: [],
+      createTween: function( prop, end ) {
+        var tween = jQuery.Tween( elem, animation.opts, prop, end,
+            animation.opts.specialEasing[ prop ] || animation.opts.easing );
+        animation.tweens.push( tween );
+        return tween;
+      },
+      stop: function( gotoEnd ) {
+        var index = 0,
+          // if we are going to the end, we want to run all the tweens
+          // otherwise we skip this part
+          length = gotoEnd ? animation.tweens.length : 0;
+        if ( stopped ) {
+          return this;
+        }
+        stopped = true;
+        for ( ; index < length ; index++ ) {
+          animation.tweens[ index ].run( 1 );
+        }
+
+        // resolve when we played the last frame
+        // otherwise, reject
+        if ( gotoEnd ) {
+          deferred.resolveWith( elem, [ animation, gotoEnd ] );
+        } else {
+          deferred.rejectWith( elem, [ animation, gotoEnd ] );
+        }
+        return this;
+      }
+    }),
+    props = animation.props;
+
+  propFilter( props, animation.opts.specialEasing );
+
+  for ( ; index < length ; index++ ) {
+    result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
+    if ( result ) {
+      return result;
+    }
+  }
+
+  jQuery.map( props, createTween, animation );
+
+  if ( jQuery.isFunction( animation.opts.start ) ) {
+    animation.opts.start.call( elem, animation );
+  }
+
+  jQuery.fx.timer(
+    jQuery.extend( tick, {
+      elem: elem,
+      anim: animation,
+      queue: animation.opts.queue
+    })
+  );
+
+  // attach callbacks from options
+  return animation.progress( animation.opts.progress )
+    .done( animation.opts.done, animation.opts.complete )
+    .fail( animation.opts.fail )
+    .always( animation.opts.always );
+}
+
+jQuery.Animation = jQuery.extend( Animation, {
+  tweener: function( props, callback ) {
+    if ( jQuery.isFunction( props ) ) {
+      callback = props;
+      props = [ "*" ];
+    } else {
+      props = props.split(" ");
+    }
+
+    var prop,
+      index = 0,
+      length = props.length;
+
+    for ( ; index < length ; index++ ) {
+      prop = props[ index ];
+      tweeners[ prop ] = tweeners[ prop ] || [];
+      tweeners[ prop ].unshift( callback );
+    }
+  },
+
+  prefilter: function( callback, prepend ) {
+    if ( prepend ) {
+      animationPrefilters.unshift( callback );
+    } else {
+      animationPrefilters.push( callback );
+    }
+  }
+});
+
+jQuery.speed = function( speed, easing, fn ) {
+  var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
+    complete: fn || !fn && easing ||
+      jQuery.isFunction( speed ) && speed,
+    duration: speed,
+    easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
+  };
+
+  opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+    opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
+
+  // normalize opt.queue - true/undefined/null -> "fx"
+  if ( opt.queue == null || opt.queue === true ) {
+    opt.queue = "fx";
+  }
+
+  // Queueing
+  opt.old = opt.complete;
+
+  opt.complete = function() {
+    if ( jQuery.isFunction( opt.old ) ) {
+      opt.old.call( this );
+    }
+
+    if ( opt.queue ) {
+      jQuery.dequeue( this, opt.queue );
+    }
+  };
+
+  return opt;
+};
+
+jQuery.fn.extend({
+  fadeTo: function( speed, to, easing, callback ) {
+
+    // show any hidden elements after setting opacity to 0
+    return this.filter( isHidden ).css( "opacity", 0 ).show()
+
+      // animate to the value specified
+      .end().animate({ opacity: to }, speed, easing, callback );
+  },
+  animate: function( prop, speed, easing, callback ) {
+    var empty = jQuery.isEmptyObject( prop ),
+      optall = jQuery.speed( speed, easing, callback ),
+      doAnimation = function() {
+        // Operate on a copy of prop so per-property easing won't be lost
+        var anim = Animation( this, jQuery.extend( {}, prop ), optall );
+
+        // Empty animations, or finishing resolves immediately
+        if ( empty || jQuery._data( this, "finish" ) ) {
+          anim.stop( true );
+        }
+      };
+      doAnimation.finish = doAnimation;
+
+    return empty || optall.queue === false ?
+      this.each( doAnimation ) :
+      this.queue( optall.queue, doAnimation );
+  },
+  stop: function( type, clearQueue, gotoEnd ) {
+    var stopQueue = function( hooks ) {
+      var stop = hooks.stop;
+      delete hooks.stop;
+      stop( gotoEnd );
+    };
+
+    if ( typeof type !== "string" ) {
+      gotoEnd = clearQueue;
+      clearQueue = type;
+      type = undefined;
+    }
+    if ( clearQueue && type !== false ) {
+      this.queue( type || "fx", [] );
+    }
+
+    return this.each(function() {
+      var dequeue = true,
+        index = type != null && type + "queueHooks",
+        timers = jQuery.timers,
+        data = jQuery._data( this );
+
+      if ( index ) {
+        if ( data[ index ] && data[ index ].stop ) {
+          stopQueue( data[ index ] );
+        }
+      } else {
+        for ( index in data ) {
+          if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
+            stopQueue( data[ index ] );
+          }
+        }
+      }
+
+      for ( index = timers.length; index--; ) {
+        if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
+          timers[ index ].anim.stop( gotoEnd );
+          dequeue = false;
+          timers.splice( index, 1 );
+        }
+      }
+
+      // start the next in the queue if the last step wasn't forced
+      // timers currently will call their complete callbacks, which will dequeue
+      // but only if they were gotoEnd
+      if ( dequeue || !gotoEnd ) {
+        jQuery.dequeue( this, type );
+      }
+    });
+  },
+  finish: function( type ) {
+    if ( type !== false ) {
+      type = type || "fx";
+    }
+    return this.each(function() {
+      var index,
+        data = jQuery._data( this ),
+        queue = data[ type + "queue" ],
+        hooks = data[ type + "queueHooks" ],
+        timers = jQuery.timers,
+        length = queue ? queue.length : 0;
+
+      // enable finishing flag on private data
+      data.finish = true;
+
+      // empty the queue first
+      jQuery.queue( this, type, [] );
+
+      if ( hooks && hooks.stop ) {
+        hooks.stop.call( this, true );
+      }
+
+      // look for any active animations, and finish them
+      for ( index = timers.length; index--; ) {
+        if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
+          timers[ index ].anim.stop( true );
+          timers.splice( index, 1 );
+        }
+      }
+
+      // look for any animations in the old queue and finish them
+      for ( index = 0; index < length; index++ ) {
+        if ( queue[ index ] && queue[ index ].finish ) {
+          queue[ index ].finish.call( this );
+        }
+      }
+
+      // turn off finishing flag
+      delete data.finish;
+    });
+  }
+});
+
+jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
+  var cssFn = jQuery.fn[ name ];
+  jQuery.fn[ name ] = function( speed, easing, callback ) {
+    return speed == null || typeof speed === "boolean" ?
+      cssFn.apply( this, arguments ) :
+      this.animate( genFx( name, true ), speed, easing, callback );
+  };
+});
+
+// Generate shortcuts for custom animations
+jQuery.each({
+  slideDown: genFx("show"),
+  slideUp: genFx("hide"),
+  slideToggle: genFx("toggle"),
+  fadeIn: { opacity: "show" },
+  fadeOut: { opacity: "hide" },
+  fadeToggle: { opacity: "toggle" }
+}, function( name, props ) {
+  jQuery.fn[ name ] = function( speed, easing, callback ) {
+    return this.animate( props, speed, easing, callback );
+  };
+});
+
+jQuery.timers = [];
+jQuery.fx.tick = function() {
+  var timer,
+    timers = jQuery.timers,
+    i = 0;
+
+  fxNow = jQuery.now();
+
+  for ( ; i < timers.length; i++ ) {
+    timer = timers[ i ];
+    // Checks the timer has not already been removed
+    if ( !timer() && timers[ i ] === timer ) {
+      timers.splice( i--, 1 );
+    }
+  }
+
+  if ( !timers.length ) {
+    jQuery.fx.stop();
+  }
+  fxNow = undefined;
+};
+
+jQuery.fx.timer = function( timer ) {
+  jQuery.timers.push( timer );
+  if ( timer() ) {
+    jQuery.fx.start();
+  } else {
+    jQuery.timers.pop();
+  }
+};
+
+jQuery.fx.interval = 13;
+
+jQuery.fx.start = function() {
+  if ( !timerId ) {
+    timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
+  }
+};
+
+jQuery.fx.stop = function() {
+  clearInterval( timerId );
+  timerId = null;
+};
+
+jQuery.fx.speeds = {
+  slow: 600,
+  fast: 200,
+  // Default speed
+  _default: 400
+};
+
+
+// Based off of the plugin by Clint Helfers, with permission.
+// http://blindsignals.com/index.php/2009/07/jquery-delay/
+jQuery.fn.delay = function( time, type ) {
+  time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
+  type = type || "fx";
+
+  return this.queue( type, function( next, hooks ) {
+    var timeout = setTimeout( next, time );
+    hooks.stop = function() {
+      clearTimeout( timeout );
+    };
+  });
+};
+
+
+(function() {
+  // Minified: var a,b,c,d,e
+  var input, div, select, a, opt;
+
+  // Setup
+  div = document.createElement( "div" );
+  div.setAttribute( "className", "t" );
+  div.innerHTML = "  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
+  a = div.getElementsByTagName("a")[ 0 ];
+
+  // First batch of tests.
+  select = document.createElement("select");
+  opt = select.appendChild( document.createElement("option") );
+  input = div.getElementsByTagName("input")[ 0 ];
+
+  a.style.cssText = "top:1px";
+
+  // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
+  support.getSetAttribute = div.className !== "t";
+
+  // Get the style information from getAttribute
+  // (IE uses .cssText instead)
+  support.style = /top/.test( a.getAttribute("style") );
+
+  // Make sure that URLs aren't manipulated
+  // (IE normalizes it by default)
+  support.hrefNormalized = a.getAttribute("href") === "/a";
+
+  // Check the default checkbox/radio value ("" on WebKit; "on" elsewhere)
+  support.checkOn = !!input.value;
+
+  // Make sure that a selected-by-default option has a working selected property.
+  // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
+  support.optSelected = opt.selected;
+
+  // Tests for enctype support on a form (#6743)
+  support.enctype = !!document.createElement("form").enctype;
+
+  // Make sure that the options inside disabled selects aren't marked as disabled
+  // (WebKit marks them as disabled)
+  select.disabled = true;
+  support.optDisabled = !opt.disabled;
+
+  // Support: IE8 only
+  // Check if we can trust getAttribute("value")
+  input = document.createElement( "input" );
+  input.setAttribute( "value", "" );
+  support.input = input.getAttribute( "value" ) === "";
+
+  // Check if an input maintains its value after becoming a radio
+  input.value = "t";
+  input.setAttribute( "type", "radio" );
+  support.radioValue = input.value === "t";
+})();
+
+
+var rreturn = /\r/g;
+
+jQuery.fn.extend({
+  val: function( value ) {
+    var hooks, ret, isFunction,
+      elem = this[0];
+
+    if ( !arguments.length ) {
+      if ( elem ) {
+        hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
+
+        if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
+          return ret;
+        }
+
+        ret = elem.value;
+
+        return typeof ret === "string" ?
+          // handle most common string cases
+          ret.replace(rreturn, "") :
+          // handle cases where value is null/undef or number
+          ret == null ? "" : ret;
+      }
+
+      return;
+    }
+
+    isFunction = jQuery.isFunction( value );
+
+    return this.each(function( i ) {
+      var val;
+
+      if ( this.nodeType !== 1 ) {
+        return;
+      }
+
+      if ( isFunction ) {
+        val = value.call( this, i, jQuery( this ).val() );
+      } else {
+        val = value;
+      }
+
+      // Treat null/undefined as ""; convert numbers to string
+      if ( val == null ) {
+        val = "";
+      } else if ( typeof val === "number" ) {
+        val += "";
+      } else if ( jQuery.isArray( val ) ) {
+        val = jQuery.map( val, function( value ) {
+          return value == null ? "" : value + "";
+        });
+      }
+
+      hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
+
+      // If set returns undefined, fall back to normal setting
+      if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
+        this.value = val;
+      }
+    });
+  }
+});
+
+jQuery.extend({
+  valHooks: {
+    option: {
+      get: function( elem ) {
+        var val = jQuery.find.attr( elem, "value" );
+        return val != null ?
+          val :
+          // Support: IE10-11+
+          // option.text throws exceptions (#14686, #14858)
+          jQuery.trim( jQuery.text( elem ) );
+      }
+    },
+    select: {
+      get: function( elem ) {
+        var value, option,
+          options = elem.options,
+          index = elem.selectedIndex,
+          one = elem.type === "select-one" || index < 0,
+          values = one ? null : [],
+          max = one ? index + 1 : options.length,
+          i = index < 0 ?
+            max :
+            one ? index : 0;
+
+        // Loop through all the selected options
+        for ( ; i < max; i++ ) {
+          option = options[ i ];
+
+          // oldIE doesn't update selected after form reset (#2551)
+          if ( ( option.selected || i === index ) &&
+              // Don't return options that are disabled or in a disabled optgroup
+              ( support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
+              ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
+
+            // Get the specific value for the option
+            value = jQuery( option ).val();
+
+            // We don't need an array for one selects
+            if ( one ) {
+              return value;
+            }
+
+            // Multi-Selects return an array
+            values.push( value );
+          }
+        }
+
+        return values;
+      },
+
+      set: function( elem, value ) {
+        var optionSet, option,
+          options = elem.options,
+          values = jQuery.makeArray( value ),
+          i = options.length;
+
+        while ( i-- ) {
+          option = options[ i ];
+
+          if ( jQuery.inArray( jQuery.valHooks.option.get( option ), values ) >= 0 ) {
+
+            // Support: IE6
+            // When new option element is added to select box we need to
+            // force reflow of newly added node in order to workaround delay
+            // of initialization properties
+            try {
+              option.selected = optionSet = true;
+
+            } catch ( _ ) {
+
+              // Will be executed only in IE6
+              option.scrollHeight;
+            }
+
+          } else {
+            option.selected = false;
+          }
+        }
+
+        // Force browsers to behave consistently when non-matching value is set
+        if ( !optionSet ) {
+          elem.selectedIndex = -1;
+        }
+
+        return options;
+      }
+    }
+  }
+});
+
+// Radios and checkboxes getter/setter
+jQuery.each([ "radio", "checkbox" ], function() {
+  jQuery.valHooks[ this ] = {
+    set: function( elem, value ) {
+      if ( jQuery.isArray( value ) ) {
+        return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
+      }
+    }
+  };
+  if ( !support.checkOn ) {
+    jQuery.valHooks[ this ].get = function( elem ) {
+      // Support: Webkit
+      // "" is returned instead of "on" if a value isn't specified
+      return elem.getAttribute("value") === null ? "on" : elem.value;
+    };
+  }
+});
+
+
+
+
+var nodeHook, boolHook,
+  attrHandle = jQuery.expr.attrHandle,
+  ruseDefault = /^(?:checked|selected)$/i,
+  getSetAttribute = support.getSetAttribute,
+  getSetInput = support.input;
+
+jQuery.fn.extend({
+  attr: function( name, value ) {
+    return access( this, jQuery.attr, name, value, arguments.length > 1 );
+  },
+
+  removeAttr: function( name ) {
+    return this.each(function() {
+      jQuery.removeAttr( this, name );
+    });
+  }
+});
+
+jQuery.extend({
+  attr: function( elem, name, value ) {
+    var hooks, ret,
+      nType = elem.nodeType;
+
+    // don't get/set attributes on text, comment and attribute nodes
+    if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+      return;
+    }
+
+    // Fallback to prop when attributes are not supported
+    if ( typeof elem.getAttribute === strundefined ) {
+      return jQuery.prop( elem, name, value );
+    }
+
+    // All attributes are lowercase
+    // Grab necessary hook if one is defined
+    if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
+      name = name.toLowerCase();
+      hooks = jQuery.attrHooks[ name ] ||
+        ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook );
+    }
+
+    if ( value !== undefined ) {
+
+      if ( value === null ) {
+        jQuery.removeAttr( elem, name );
+
+      } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
+        return ret;
+
+      } else {
+        elem.setAttribute( name, value + "" );
+        return value;
+      }
+
+    } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
+      return ret;
+
+    } else {
+      ret = jQuery.find.attr( elem, name );
+
+      // Non-existent attributes return null, we normalize to undefined
+      return ret == null ?
+        undefined :
+        ret;
+    }
+  },
+
+  removeAttr: function( elem, value ) {
+    var name, propName,
+      i = 0,
+      attrNames = value && value.match( rnotwhite );
+
+    if ( attrNames && elem.nodeType === 1 ) {
+      while ( (name = attrNames[i++]) ) {
+        propName = jQuery.propFix[ name ] || name;
+
+        // Boolean attributes get special treatment (#10870)
+        if ( jQuery.expr.match.bool.test( name ) ) {
+          // Set corresponding property to false
+          if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
+            elem[ propName ] = false;
+          // Support: IE<9
+          // Also clear defaultChecked/defaultSelected (if appropriate)
+          } else {
+            elem[ jQuery.camelCase( "default-" + name ) ] =
+              elem[ propName ] = false;
+          }
+
+        // See #9699 for explanation of this approach (setting first, then removal)
+        } else {
+          jQuery.attr( elem, name, "" );
+        }
+
+        elem.removeAttribute( getSetAttribute ? name : propName );
+      }
+    }
+  },
+
+  attrHooks: {
+    type: {
+      set: function( elem, value ) {
+        if ( !support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
+          // Setting the type on a radio button after the value resets the value in IE6-9
+          // Reset value to default in case type is set after value during creation
+          var val = elem.value;
+          elem.setAttribute( "type", value );
+          if ( val ) {
+            elem.value = val;
+          }
+          return value;
+        }
+      }
+    }
+  }
+});
+
+// Hook for boolean attributes
+boolHook = {
+  set: function( elem, value, name ) {
+    if ( value === false ) {
+      // Remove boolean attributes when set to false
+      jQuery.removeAttr( elem, name );
+    } else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
+      // IE<8 needs the *property* name
+      elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name );
+
+    // Use defaultChecked and defaultSelected for oldIE
+    } else {
+      elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true;
+    }
+
+    return name;
+  }
+};
+
+// Retrieve booleans specially
+jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
+
+  var getter = attrHandle[ name ] || jQuery.find.attr;
+
+  attrHandle[ name ] = getSetInput && getSetAttribute || !ruseDefault.test( name ) ?
+    function( elem, name, isXML ) {
+      var ret, handle;
+      if ( !isXML ) {
+        // Avoid an infinite loop by temporarily removing this function from the getter
+        handle = attrHandle[ name ];
+        attrHandle[ name ] = ret;
+        ret = getter( elem, name, isXML ) != null ?
+          name.toLowerCase() :
+          null;
+        attrHandle[ name ] = handle;
+      }
+      return ret;
+    } :
+    function( elem, name, isXML ) {
+      if ( !isXML ) {
+        return elem[ jQuery.camelCase( "default-" + name ) ] ?
+          name.toLowerCase() :
+          null;
+      }
+    };
+});
+
+// fix oldIE attroperties
+if ( !getSetInput || !getSetAttribute ) {
+  jQuery.attrHooks.value = {
+    set: function( elem, value, name ) {
+      if ( jQuery.nodeName( elem, "input" ) ) {
+        // Does not return so that setAttribute is also used
+        elem.defaultValue = value;
+      } else {
+        // Use nodeHook if defined (#1954); otherwise setAttribute is fine
+        return nodeHook && nodeHook.set( elem, value, name );
+      }
+    }
+  };
+}
+
+// IE6/7 do not support getting/setting some attributes with get/setAttribute
+if ( !getSetAttribute ) {
+
+  // Use this for any attribute in IE6/7
+  // This fixes almost every IE6/7 issue
+  nodeHook = {
+    set: function( elem, value, name ) {
+      // Set the existing or create a new attribute node
+      var ret = elem.getAttributeNode( name );
+      if ( !ret ) {
+        elem.setAttributeNode(
+          (ret = elem.ownerDocument.createAttribute( name ))
+        );
+      }
+
+      ret.value = value += "";
+
+      // Break association with cloned elements by also using setAttribute (#9646)
+      if ( name === "value" || value === elem.getAttribute( name ) ) {
+        return value;
+      }
+    }
+  };
+
+  // Some attributes are constructed with empty-string values when not defined
+  attrHandle.id = attrHandle.name = attrHandle.coords =
+    function( elem, name, isXML ) {
+      var ret;
+      if ( !isXML ) {
+        return (ret = elem.getAttributeNode( name )) && ret.value !== "" ?
+          ret.value :
+          null;
+      }
+    };
+
+  // Fixing value retrieval on a button requires this module
+  jQuery.valHooks.button = {
+    get: function( elem, name ) {
+      var ret = elem.getAttributeNode( name );
+      if ( ret && ret.specified ) {
+        return ret.value;
+      }
+    },
+    set: nodeHook.set
+  };
+
+  // Set contenteditable to false on removals(#10429)
+  // Setting to empty string throws an error as an invalid value
+  jQuery.attrHooks.contenteditable = {
+    set: function( elem, value, name ) {
+      nodeHook.set( elem, value === "" ? false : value, name );
+    }
+  };
+
+  // Set width and height to auto instead of 0 on empty string( Bug #8150 )
+  // This is for removals
+  jQuery.each([ "width", "height" ], function( i, name ) {
+    jQuery.attrHooks[ name ] = {
+      set: function( elem, value ) {
+        if ( value === "" ) {
+          elem.setAttribute( name, "auto" );
+          return value;
+        }
+      }
+    };
+  });
+}
+
+if ( !support.style ) {
+  jQuery.attrHooks.style = {
+    get: function( elem ) {
+      // Return undefined in the case of empty string
+      // Note: IE uppercases css property names, but if we were to .toLowerCase()
+      // .cssText, that would destroy case senstitivity in URL's, like in "background"
+      return elem.style.cssText || undefined;
+    },
+    set: function( elem, value ) {
+      return ( elem.style.cssText = value + "" );
+    }
+  };
+}
+
+
+
+
+var rfocusable = /^(?:input|select|textarea|button|object)$/i,
+  rclickable = /^(?:a|area)$/i;
+
+jQuery.fn.extend({
+  prop: function( name, value ) {
+    return access( this, jQuery.prop, name, value, arguments.length > 1 );
+  },
+
+  removeProp: function( name ) {
+    name = jQuery.propFix[ name ] || name;
+    return this.each(function() {
+      // try/catch handles cases where IE balks (such as removing a property on window)
+      try {
+        this[ name ] = undefined;
+        delete this[ name ];
+      } catch( e ) {}
+    });
+  }
+});
+
+jQuery.extend({
+  propFix: {
+    "for": "htmlFor",
+    "class": "className"
+  },
+
+  prop: function( elem, name, value ) {
+    var ret, hooks, notxml,
+      nType = elem.nodeType;
+
+    // don't get/set properties on text, comment and attribute nodes
+    if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+      return;
+    }
+
+    notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+    if ( notxml ) {
+      // Fix name and attach hooks
+      name = jQuery.propFix[ name ] || name;
+      hooks = jQuery.propHooks[ name ];
+    }
+
+    if ( value !== undefined ) {
+      return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ?
+        ret :
+        ( elem[ name ] = value );
+
+    } else {
+      return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ?
+        ret :
+        elem[ name ];
+    }
+  },
+
+  propHooks: {
+    tabIndex: {
+      get: function( elem ) {
+        // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+        // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+        // Use proper attribute retrieval(#12072)
+        var tabindex = jQuery.find.attr( elem, "tabindex" );
+
+        return tabindex ?
+          parseInt( tabindex, 10 ) :
+          rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
+            0 :
+            -1;
+      }
+    }
+  }
+});
+
+// Some attributes require a special call on IE
+// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
+if ( !support.hrefNormalized ) {
+  // href/src property should get the full normalized URL (#10299/#12915)
+  jQuery.each([ "href", "src" ], function( i, name ) {
+    jQuery.propHooks[ name ] = {
+      get: function( elem ) {
+        return elem.getAttribute( name, 4 );
+      }
+    };
+  });
+}
+
+// Support: Safari, IE9+
+// mis-reports the default selected property of an option
+// Accessing the parent's selectedIndex property fixes it
+if ( !support.optSelected ) {
+  jQuery.propHooks.selected = {
+    get: function( elem ) {
+      var parent = elem.parentNode;
+
+      if ( parent ) {
+        parent.selectedIndex;
+
+        // Make sure that it also works with optgroups, see #5701
+        if ( parent.parentNode ) {
+          parent.parentNode.selectedIndex;
+        }
+      }
+      return null;
+    }
+  };
+}
+
+jQuery.each([
+  "tabIndex",
+  "readOnly",
+  "maxLength",
+  "cellSpacing",
+  "cellPadding",
+  "rowSpan",
+  "colSpan",
+  "useMap",
+  "frameBorder",
+  "contentEditable"
+], function() {
+  jQuery.propFix[ this.toLowerCase() ] = this;
+});
+
+// IE6/7 call enctype encoding
+if ( !support.enctype ) {
+  jQuery.propFix.enctype = "encoding";
+}
+
+
+
+
+var rclass = /[\t\r\n\f]/g;
+
+jQuery.fn.extend({
+  addClass: function( value ) {
+    var classes, elem, cur, clazz, j, finalValue,
+      i = 0,
+      len = this.length,
+      proceed = typeof value === "string" && value;
+
+    if ( jQuery.isFunction( value ) ) {
+      return this.each(function( j ) {
+        jQuery( this ).addClass( value.call( this, j, this.className ) );
+      });
+    }
+
+    if ( proceed ) {
+      // The disjunction here is for better compressibility (see removeClass)
+      classes = ( value || "" ).match( rnotwhite ) || [];
+
+      for ( ; i < len; i++ ) {
+        elem = this[ i ];
+        cur = elem.nodeType === 1 && ( elem.className ?
+          ( " " + elem.className + " " ).replace( rclass, " " ) :
+          " "
+        );
+
+        if ( cur ) {
+          j = 0;
+          while ( (clazz = classes[j++]) ) {
+            if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
+              cur += clazz + " ";
+            }
+          }
+
+          // only assign if different to avoid unneeded rendering.
+          finalValue = jQuery.trim( cur );
+          if ( elem.className !== finalValue ) {
+            elem.className = finalValue;
+          }
+        }
+      }
+    }
+
+    return this;
+  },
+
+  removeClass: function( value ) {
+    var classes, elem, cur, clazz, j, finalValue,
+      i = 0,
+      len = this.length,
+      proceed = arguments.length === 0 || typeof value === "string" && value;
+
+    if ( jQuery.isFunction( value ) ) {
+      return this.each(function( j ) {
+        jQuery( this ).removeClass( value.call( this, j, this.className ) );
+      });
+    }
+    if ( proceed ) {
+      classes = ( value || "" ).match( rnotwhite ) || [];
+
+      for ( ; i < len; i++ ) {
+        elem = this[ i ];
+        // This expression is here for better compressibility (see addClass)
+        cur = elem.nodeType === 1 && ( elem.className ?
+          ( " " + elem.className + " " ).replace( rclass, " " ) :
+          ""
+        );
+
+        if ( cur ) {
+          j = 0;
+          while ( (clazz = classes[j++]) ) {
+            // Remove *all* instances
+            while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
+              cur = cur.replace( " " + clazz + " ", " " );
+            }
+          }
+
+          // only assign if different to avoid unneeded rendering.
+          finalValue = value ? jQuery.trim( cur ) : "";
+          if ( elem.className !== finalValue ) {
+            elem.className = finalValue;
+          }
+        }
+      }
+    }
+
+    return this;
+  },
+
+  toggleClass: function( value, stateVal ) {
+    var type = typeof value;
+
+    if ( typeof stateVal === "boolean" && type === "string" ) {
+      return stateVal ? this.addClass( value ) : this.removeClass( value );
+    }
+
+    if ( jQuery.isFunction( value ) ) {
+      return this.each(function( i ) {
+        jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
+      });
+    }
+
+    return this.each(function() {
+      if ( type === "string" ) {
+        // toggle individual class names
+        var className,
+          i = 0,
+          self = jQuery( this ),
+          classNames = value.match( rnotwhite ) || [];
+
+        while ( (className = classNames[ i++ ]) ) {
+          // check each className given, space separated list
+          if ( self.hasClass( className ) ) {
+            self.removeClass( className );
+          } else {
+            self.addClass( className );
+          }
+        }
+
+      // Toggle whole class name
+      } else if ( type === strundefined || type === "boolean" ) {
+        if ( this.className ) {
+          // store className if set
+          jQuery._data( this, "__className__", this.className );
+        }
+
+        // If the element has a class name or if we're passed "false",
+        // then remove the whole classname (if there was one, the above saved it).
+        // Otherwise bring back whatever was previously saved (if anything),
+        // falling back to the empty string if nothing was stored.
+        this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
+      }
+    });
+  },
+
+  hasClass: function( selector ) {
+    var className = " " + selector + " ",
+      i = 0,
+      l = this.length;
+    for ( ; i < l; i++ ) {
+      if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+});
+
+
+
+
+// Return jQuery for attributes-only inclusion
+
+
+jQuery.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 contextmenu").split(" "), function( i, name ) {
+
+  // Handle event binding
+  jQuery.fn[ name ] = function( data, fn ) {
+    return arguments.length > 0 ?
+      this.on( name, null, data, fn ) :
+      this.trigger( name );
+  };
+});
+
+jQuery.fn.extend({
+  hover: function( fnOver, fnOut ) {
+    return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+  },
+
+  bind: function( types, data, fn ) {
+    return this.on( types, null, data, fn );
+  },
+  unbind: function( types, fn ) {
+    return this.off( types, null, fn );
+  },
+
+  delegate: function( selector, types, data, fn ) {
+    return this.on( types, selector, data, fn );
+  },
+  undelegate: function( selector, types, fn ) {
+    // ( namespace ) or ( selector, types [, fn] )
+    return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
+  }
+});
+
+
+var nonce = jQuery.now();
+
+var rquery = (/\?/);
+
+
+
+var rvalidtokens = /(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;
+
+jQuery.parseJSON = function( data ) {
+  // Attempt to parse using the native JSON parser first
+  if ( window.JSON && window.JSON.parse ) {
+    // Support: Android 2.3
+    // Workaround failure to string-cast null input
+    return window.JSON.parse( data + "" );
+  }
+
+  var requireNonComma,
+    depth = null,
+    str = jQuery.trim( data + "" );
+
+  // Guard against invalid (and possibly dangerous) input by ensuring that nothing remains
+  // after removing valid tokens
+  return str && !jQuery.trim( str.replace( rvalidtokens, function( token, comma, open, close ) {
+
+    // Force termination if we see a misplaced comma
+    if ( requireNonComma && comma ) {
+      depth = 0;
+    }
+
+    // Perform no more replacements after returning to outermost depth
+    if ( depth === 0 ) {
+      return token;
+    }
+
+    // Commas must not follow "[", "{", or ","
+    requireNonComma = open || comma;
+
+    // Determine new depth
+    // array/object open ("[" or "{"): depth += true - false (increment)
+    // array/object close ("]" or "}"): depth += false - true (decrement)
+    // other cases ("," or primitive): depth += true - true (numeric cast)
+    depth += !close - !open;
+
+    // Remove this token
+    return "";
+  }) ) ?
+    ( Function( "return " + str ) )() :
+    jQuery.error( "Invalid JSON: " + data );
+};
+
+
+// Cross-browser xml parsing
+jQuery.parseXML = function( data ) {
+  var xml, tmp;
+  if ( !data || typeof data !== "string" ) {
+    return null;
+  }
+  try {
+    if ( window.DOMParser ) { // Standard
+      tmp = new DOMParser();
+      xml = tmp.parseFromString( data, "text/xml" );
+    } else { // IE
+      xml = new ActiveXObject( "Microsoft.XMLDOM" );
+      xml.async = "false";
+      xml.loadXML( data );
+    }
+  } catch( e ) {
+    xml = undefined;
+  }
+  if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
+    jQuery.error( "Invalid XML: " + data );
+  }
+  return xml;
+};
+
+
+var
+  // Document location
+  ajaxLocParts,
+  ajaxLocation,
+
+  rhash = /#.*$/,
+  rts = /([?&])_=[^&]*/,
+  rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
+  // #7653, #8125, #8152: local protocol detection
+  rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
+  rnoContent = /^(?:GET|HEAD)$/,
+  rprotocol = /^\/\//,
+  rurl = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,
+
+  /* Prefilters
+   * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
+   * 2) These are called:
+   *    - BEFORE asking for a transport
+   *    - AFTER param serialization (s.data is a string if s.processData is true)
+   * 3) key is the dataType
+   * 4) the catchall symbol "*" can be used
+   * 5) execution will start with transport dataType and THEN continue down to "*" if needed
+   */
+  prefilters = {},
+
+  /* Transports bindings
+   * 1) key is the dataType
+   * 2) the catchall symbol "*" can be used
+   * 3) selection will start with transport dataType and THEN go to "*" if needed
+   */
+  transports = {},
+
+  // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
+  allTypes = "*/".concat("*");
+
+// #8138, IE may throw an exception when accessing
+// a field from window.location if document.domain has been set
+try {
+  ajaxLocation = location.href;
+} catch( e ) {
+  // Use the href attribute of an A element
+  // since IE will modify it given document.location
+  ajaxLocation = document.createElement( "a" );
+  ajaxLocation.href = "";
+  ajaxLocation = ajaxLocation.href;
+}
+
+// Segment location into parts
+ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
+
+// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
+function addToPrefiltersOrTransports( structure ) {
+
+  // dataTypeExpression is optional and defaults to "*"
+  return function( dataTypeExpression, func ) {
+
+    if ( typeof dataTypeExpression !== "string" ) {
+      func = dataTypeExpression;
+      dataTypeExpression = "*";
+    }
+
+    var dataType,
+      i = 0,
+      dataTypes = dataTypeExpression.toLowerCase().match( rnotwhite ) || [];
+
+    if ( jQuery.isFunction( func ) ) {
+      // For each dataType in the dataTypeExpression
+      while ( (dataType = dataTypes[i++]) ) {
+        // Prepend if requested
+        if ( dataType.charAt( 0 ) === "+" ) {
+          dataType = dataType.slice( 1 ) || "*";
+          (structure[ dataType ] = structure[ dataType ] || []).unshift( func );
+
+        // Otherwise append
+        } else {
+          (structure[ dataType ] = structure[ dataType ] || []).push( func );
+        }
+      }
+    }
+  };
+}
+
+// Base inspection function for prefilters and transports
+function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
+
+  var inspected = {},
+    seekingTransport = ( structure === transports );
+
+  function inspect( dataType ) {
+    var selected;
+    inspected[ dataType ] = true;
+    jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
+      var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
+      if ( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
+        options.dataTypes.unshift( dataTypeOrTransport );
+        inspect( dataTypeOrTransport );
+        return false;
+      } else if ( seekingTransport ) {
+        return !( selected = dataTypeOrTransport );
+      }
+    });
+    return selected;
+  }
+
+  return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
+}
+
+// A special extend for ajax options
+// that takes "flat" options (not to be deep extended)
+// Fixes #9887
+function ajaxExtend( target, src ) {
+  var deep, key,
+    flatOptions = jQuery.ajaxSettings.flatOptions || {};
+
+  for ( key in src ) {
+    if ( src[ key ] !== undefined ) {
+      ( flatOptions[ key ] ? target : ( deep || (deep = {}) ) )[ key ] = src[ key ];
+    }
+  }
+  if ( deep ) {
+    jQuery.extend( true, target, deep );
+  }
+
+  return target;
+}
+
+/* Handles responses to an ajax request:
+ * - finds the right dataType (mediates between content-type and expected dataType)
+ * - returns the corresponding response
+ */
+function ajaxHandleResponses( s, jqXHR, responses ) {
+  var firstDataType, ct, finalDataType, type,
+    contents = s.contents,
+    dataTypes = s.dataTypes;
+
+  // Remove auto dataType and get content-type in the process
+  while ( dataTypes[ 0 ] === "*" ) {
+    dataTypes.shift();
+    if ( ct === undefined ) {
+      ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
+    }
+  }
+
+  // Check if we're dealing with a known content-type
+  if ( ct ) {
+    for ( type in contents ) {
+      if ( contents[ type ] && contents[ type ].test( ct ) ) {
+        dataTypes.unshift( type );
+        break;
+      }
+    }
+  }
+
+  // Check to see if we have a response for the expected dataType
+  if ( dataTypes[ 0 ] in responses ) {
+    finalDataType = dataTypes[ 0 ];
+  } else {
+    // Try convertible dataTypes
+    for ( type in responses ) {
+      if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
+        finalDataType = type;
+        break;
+      }
+      if ( !firstDataType ) {
+        firstDataType = type;
+      }
+    }
+    // Or just use first one
+    finalDataType = finalDataType || firstDataType;
+  }
+
+  // If we found a dataType
+  // We add the dataType to the list if needed
+  // and return the corresponding response
+  if ( finalDataType ) {
+    if ( finalDataType !== dataTypes[ 0 ] ) {
+      dataTypes.unshift( finalDataType );
+    }
+    return responses[ finalDataType ];
+  }
+}
+
+/* Chain conversions given the request and the original response
+ * Also sets the responseXXX fields on the jqXHR instance
+ */
+function ajaxConvert( s, response, jqXHR, isSuccess ) {
+  var conv2, current, conv, tmp, prev,
+    converters = {},
+    // Work with a copy of dataTypes in case we need to modify it for conversion
+    dataTypes = s.dataTypes.slice();
+
+  // Create converters map with lowercased keys
+  if ( dataTypes[ 1 ] ) {
+    for ( conv in s.converters ) {
+      converters[ conv.toLowerCase() ] = s.converters[ conv ];
+    }
+  }
+
+  current = dataTypes.shift();
+
+  // Convert to each sequential dataType
+  while ( current ) {
+
+    if ( s.responseFields[ current ] ) {
+      jqXHR[ s.responseFields[ current ] ] = response;
+    }
+
+    // Apply the dataFilter if provided
+    if ( !prev && isSuccess && s.dataFilter ) {
+      response = s.dataFilter( response, s.dataType );
+    }
+
+    prev = current;
+    current = dataTypes.shift();
+
+    if ( current ) {
+
+      // There's only work to do if current dataType is non-auto
+      if ( current === "*" ) {
+
+        current = prev;
+
+      // Convert response if prev dataType is non-auto and differs from current
+      } else if ( prev !== "*" && prev !== current ) {
+
+        // Seek a direct converter
+        conv = converters[ prev + " " + current ] || converters[ "* " + current ];
+
+        // If none found, seek a pair
+        if ( !conv ) {
+          for ( conv2 in converters ) {
+
+            // If conv2 outputs current
+            tmp = conv2.split( " " );
+            if ( tmp[ 1 ] === current ) {
+
+              // If prev can be converted to accepted input
+              conv = converters[ prev + " " + tmp[ 0 ] ] ||
+                converters[ "* " + tmp[ 0 ] ];
+              if ( conv ) {
+                // Condense equivalence converters
+                if ( conv === true ) {
+                  conv = converters[ conv2 ];
+
+                // Otherwise, insert the intermediate dataType
+                } else if ( converters[ conv2 ] !== true ) {
+                  current = tmp[ 0 ];
+                  dataTypes.unshift( tmp[ 1 ] );
+                }
+                break;
+              }
+            }
+          }
+        }
+
+        // Apply converter (if not an equivalence)
+        if ( conv !== true ) {
+
+          // Unless errors are allowed to bubble, catch and return them
+          if ( conv && s[ "throws" ] ) {
+            response = conv( response );
+          } else {
+            try {
+              response = conv( response );
+            } catch ( e ) {
+              return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
+            }
+          }
+        }
+      }
+    }
+  }
+
+  return { state: "success", data: response };
+}
+
+jQuery.extend({
+
+  // Counter for holding the number of active queries
+  active: 0,
+
+  // Last-Modified header cache for next request
+  lastModified: {},
+  etag: {},
+
+  ajaxSettings: {
+    url: ajaxLocation,
+    type: "GET",
+    isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
+    global: true,
+    processData: true,
+    async: true,
+    contentType: "application/x-www-form-urlencoded; charset=UTF-8",
+    /*
+    timeout: 0,
+    data: null,
+    dataType: null,
+    username: null,
+    password: null,
+    cache: null,
+    throws: false,
+    traditional: false,
+    headers: {},
+    */
+
+    accepts: {
+      "*": allTypes,
+      text: "text/plain",
+      html: "text/html",
+      xml: "application/xml, text/xml",
+      json: "application/json, text/javascript"
+    },
+
+    contents: {
+      xml: /xml/,
+      html: /html/,
+      json: /json/
+    },
+
+    responseFields: {
+      xml: "responseXML",
+      text: "responseText",
+      json: "responseJSON"
+    },
+
+    // Data converters
+    // Keys separate source (or catchall "*") and destination types with a single space
+    converters: {
+
+      // Convert anything to text
+      "* text": String,
+
+      // Text to html (true = no transformation)
+      "text html": true,
+
+      // Evaluate text as a json expression
+      "text json": jQuery.parseJSON,
+
+      // Parse text as xml
+      "text xml": jQuery.parseXML
+    },
+
+    // For options that shouldn't be deep extended:
+    // you can add your own custom options here if
+    // and when you create one that shouldn't be
+    // deep extended (see ajaxExtend)
+    flatOptions: {
+      url: true,
+      context: true
+    }
+  },
+
+  // Creates a full fledged settings object into target
+  // with both ajaxSettings and settings fields.
+  // If target is omitted, writes into ajaxSettings.
+  ajaxSetup: function( target, settings ) {
+    return settings ?
+
+      // Building a settings object
+      ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
+
+      // Extending ajaxSettings
+      ajaxExtend( jQuery.ajaxSettings, target );
+  },
+
+  ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
+  ajaxTransport: addToPrefiltersOrTransports( transports ),
+
+  // Main method
+  ajax: function( url, options ) {
+
+    // If url is an object, simulate pre-1.5 signature
+    if ( typeof url === "object" ) {
+      options = url;
+      url = undefined;
+    }
+
+    // Force options to be an object
+    options = options || {};
+
+    var // Cross-domain detection vars
+      parts,
+      // Loop variable
+      i,
+      // URL without anti-cache param
+      cacheURL,
+      // Response headers as string
+      responseHeadersString,
+      // timeout handle
+      timeoutTimer,
+
+      // To know if global events are to be dispatched
+      fireGlobals,
+
+      transport,
+      // Response headers
+      responseHeaders,
+      // Create the final options object
+      s = jQuery.ajaxSetup( {}, options ),
+      // Callbacks context
+      callbackContext = s.context || s,
+      // Context for global events is callbackContext if it is a DOM node or jQuery collection
+      globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
+        jQuery( callbackContext ) :
+        jQuery.event,
+      // Deferreds
+      deferred = jQuery.Deferred(),
+      completeDeferred = jQuery.Callbacks("once memory"),
+      // Status-dependent callbacks
+      statusCode = s.statusCode || {},
+      // Headers (they are sent all at once)
+      requestHeaders = {},
+      requestHeadersNames = {},
+      // The jqXHR state
+      state = 0,
+      // Default abort message
+      strAbort = "canceled",
+      // Fake xhr
+      jqXHR = {
+        readyState: 0,
+
+        // Builds headers hashtable if needed
+        getResponseHeader: function( key ) {
+          var match;
+          if ( state === 2 ) {
+            if ( !responseHeaders ) {
+              responseHeaders = {};
+              while ( (match = rheaders.exec( responseHeadersString )) ) {
+                responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
+              }
+            }
+            match = responseHeaders[ key.toLowerCase() ];
+          }
+          return match == null ? null : match;
+        },
+
+        // Raw string
+        getAllResponseHeaders: function() {
+          return state === 2 ? responseHeadersString : null;
+        },
+
+        // Caches the header
+        setRequestHeader: function( name, value ) {
+          var lname = name.toLowerCase();
+          if ( !state ) {
+            name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
+            requestHeaders[ name ] = value;
+          }
+          return this;
+        },
+
+        // Overrides response content-type header
+        overrideMimeType: function( type ) {
+          if ( !state ) {
+            s.mimeType = type;
+          }
+          return this;
+        },
+
+        // Status-dependent callbacks
+        statusCode: function( map ) {
+          var code;
+          if ( map ) {
+            if ( state < 2 ) {
+              for ( code in map ) {
+                // Lazy-add the new callback in a way that preserves old ones
+                statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
+              }
+            } else {
+              // Execute the appropriate callbacks
+              jqXHR.always( map[ jqXHR.status ] );
+            }
+          }
+          return this;
+        },
+
+        // Cancel the request
+        abort: function( statusText ) {
+          var finalText = statusText || strAbort;
+          if ( transport ) {
+            transport.abort( finalText );
+          }
+          done( 0, finalText );
+          return this;
+        }
+      };
+
+    // Attach deferreds
+    deferred.promise( jqXHR ).complete = completeDeferred.add;
+    jqXHR.success = jqXHR.done;
+    jqXHR.error = jqXHR.fail;
+
+    // Remove hash character (#7531: and string promotion)
+    // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
+    // Handle falsy url in the settings object (#10093: consistency with old signature)
+    // We also use the url parameter if available
+    s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
+
+    // Alias method option to type as per ticket #12004
+    s.type = options.method || options.type || s.method || s.type;
+
+    // Extract dataTypes list
+    s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( rnotwhite ) || [ "" ];
+
+    // A cross-domain request is in order when we have a protocol:host:port mismatch
+    if ( s.crossDomain == null ) {
+      parts = rurl.exec( s.url.toLowerCase() );
+      s.crossDomain = !!( parts &&
+        ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
+          ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !==
+            ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) )
+      );
+    }
+
+    // Convert data if not already a string
+    if ( s.data && s.processData && typeof s.data !== "string" ) {
+      s.data = jQuery.param( s.data, s.traditional );
+    }
+
+    // Apply prefilters
+    inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
+
+    // If request was aborted inside a prefilter, stop there
+    if ( state === 2 ) {
+      return jqXHR;
+    }
+
+    // We can fire global events as of now if asked to
+    fireGlobals = s.global;
+
+    // Watch for a new set of requests
+    if ( fireGlobals && jQuery.active++ === 0 ) {
+      jQuery.event.trigger("ajaxStart");
+    }
+
+    // Uppercase the type
+    s.type = s.type.toUpperCase();
+
+    // Determine if request has content
+    s.hasContent = !rnoContent.test( s.type );
+
+    // Save the URL in case we're toying with the If-Modified-Since
+    // and/or If-None-Match header later on
+    cacheURL = s.url;
+
+    // More options handling for requests with no content
+    if ( !s.hasContent ) {
+
+      // If data is available, append data to url
+      if ( s.data ) {
+        cacheURL = ( s.url += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
+        // #9682: remove data so that it's not used in an eventual retry
+        delete s.data;
+      }
+
+      // Add anti-cache in url if needed
+      if ( s.cache === false ) {
+        s.url = rts.test( cacheURL ) ?
+
+          // If there is already a '_' parameter, set its value
+          cacheURL.replace( rts, "$1_=" + nonce++ ) :
+
+          // Otherwise add one to the end
+          cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++;
+      }
+    }
+
+    // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+    if ( s.ifModified ) {
+      if ( jQuery.lastModified[ cacheURL ] ) {
+        jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
+      }
+      if ( jQuery.etag[ cacheURL ] ) {
+        jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
+      }
+    }
+
+    // Set the correct header, if data is being sent
+    if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
+      jqXHR.setRequestHeader( "Content-Type", s.contentType );
+    }
+
+    // Set the Accepts header for the server, depending on the dataType
+    jqXHR.setRequestHeader(
+      "Accept",
+      s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
+        s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
+        s.accepts[ "*" ]
+    );
+
+    // Check for headers option
+    for ( i in s.headers ) {
+      jqXHR.setRequestHeader( i, s.headers[ i ] );
+    }
+
+    // Allow custom headers/mimetypes and early abort
+    if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
+      // Abort if not done already and return
+      return jqXHR.abort();
+    }
+
+    // aborting is no longer a cancellation
+    strAbort = "abort";
+
+    // Install callbacks on deferreds
+    for ( i in { success: 1, error: 1, complete: 1 } ) {
+      jqXHR[ i ]( s[ i ] );
+    }
+
+    // Get transport
+    transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
+
+    // If no transport, we auto-abort
+    if ( !transport ) {
+      done( -1, "No Transport" );
+    } else {
+      jqXHR.readyState = 1;
+
+      // Send global event
+      if ( fireGlobals ) {
+        globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
+      }
+      // Timeout
+      if ( s.async && s.timeout > 0 ) {
+        timeoutTimer = setTimeout(function() {
+          jqXHR.abort("timeout");
+        }, s.timeout );
+      }
+
+      try {
+        state = 1;
+        transport.send( requestHeaders, done );
+      } catch ( e ) {
+        // Propagate exception as error if not done
+        if ( state < 2 ) {
+          done( -1, e );
+        // Simply rethrow otherwise
+        } else {
+          throw e;
+        }
+      }
+    }
+
+    // Callback for when everything is done
+    function done( status, nativeStatusText, responses, headers ) {
+      var isSuccess, success, error, response, modified,
+        statusText = nativeStatusText;
+
+      // Called once
+      if ( state === 2 ) {
+        return;
+      }
+
+      // State is "done" now
+      state = 2;
+
+      // Clear timeout if it exists
+      if ( timeoutTimer ) {
+        clearTimeout( timeoutTimer );
+      }
+
+      // Dereference transport for early garbage collection
+      // (no matter how long the jqXHR object will be used)
+      transport = undefined;
+
+      // Cache response headers
+      responseHeadersString = headers || "";
+
+      // Set readyState
+      jqXHR.readyState = status > 0 ? 4 : 0;
+
+      // Determine if successful
+      isSuccess = status >= 200 && status < 300 || status === 304;
+
+      // Get response data
+      if ( responses ) {
+        response = ajaxHandleResponses( s, jqXHR, responses );
+      }
+
+      // Convert no matter what (that way responseXXX fields are always set)
+      response = ajaxConvert( s, response, jqXHR, isSuccess );
+
+      // If successful, handle type chaining
+      if ( isSuccess ) {
+
+        // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+        if ( s.ifModified ) {
+          modified = jqXHR.getResponseHeader("Last-Modified");
+          if ( modified ) {
+            jQuery.lastModified[ cacheURL ] = modified;
+          }
+          modified = jqXHR.getResponseHeader("etag");
+          if ( modified ) {
+            jQuery.etag[ cacheURL ] = modified;
+          }
+        }
+
+        // if no content
+        if ( status === 204 || s.type === "HEAD" ) {
+          statusText = "nocontent";
+
+        // if not modified
+        } else if ( status === 304 ) {
+          statusText = "notmodified";
+
+        // If we have data, let's convert it
+        } else {
+          statusText = response.state;
+          success = response.data;
+          error = response.error;
+          isSuccess = !error;
+        }
+      } else {
+        // We extract error from statusText
+        // then normalize statusText and status for non-aborts
+        error = statusText;
+        if ( status || !statusText ) {
+          statusText = "error";
+          if ( status < 0 ) {
+            status = 0;
+          }
+        }
+      }
+
+      // Set data for the fake xhr object
+      jqXHR.status = status;
+      jqXHR.statusText = ( nativeStatusText || statusText ) + "";
+
+      // Success/Error
+      if ( isSuccess ) {
+        deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
+      } else {
+        deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
+      }
+
+      // Status-dependent callbacks
+      jqXHR.statusCode( statusCode );
+      statusCode = undefined;
+
+      if ( fireGlobals ) {
+        globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
+          [ jqXHR, s, isSuccess ? success : error ] );
+      }
+
+      // Complete
+      completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
+
+      if ( fireGlobals ) {
+        globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
+        // Handle the global AJAX counter
+        if ( !( --jQuery.active ) ) {
+          jQuery.event.trigger("ajaxStop");
+        }
+      }
+    }
+
+    return jqXHR;
+  },
+
+  getJSON: function( url, data, callback ) {
+    return jQuery.get( url, data, callback, "json" );
+  },
+
+  getScript: function( url, callback ) {
+    return jQuery.get( url, undefined, callback, "script" );
+  }
+});
+
+jQuery.each( [ "get", "post" ], function( i, method ) {
+  jQuery[ method ] = function( url, data, callback, type ) {
+    // shift arguments if data argument was omitted
+    if ( jQuery.isFunction( data ) ) {
+      type = type || callback;
+      callback = data;
+      data = undefined;
+    }
+
+    return jQuery.ajax({
+      url: url,
+      type: method,
+      dataType: type,
+      data: data,
+      success: callback
+    });
+  };
+});
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ) {
+  jQuery.fn[ type ] = function( fn ) {
+    return this.on( type, fn );
+  };
+});
+
+
+jQuery._evalUrl = function( url ) {
+  return jQuery.ajax({
+    url: url,
+    type: "GET",
+    dataType: "script",
+    async: false,
+    global: false,
+    "throws": true
+  });
+};
+
+
+jQuery.fn.extend({
+  wrapAll: function( html ) {
+    if ( jQuery.isFunction( html ) ) {
+      return this.each(function(i) {
+        jQuery(this).wrapAll( html.call(this, i) );
+      });
+    }
+
+    if ( this[0] ) {
+      // The elements to wrap the target around
+      var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
+
+      if ( this[0].parentNode ) {
+        wrap.insertBefore( this[0] );
+      }
+
+      wrap.map(function() {
+        var elem = this;
+
+        while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
+          elem = elem.firstChild;
+        }
+
+        return elem;
+      }).append( this );
+    }
+
+    return this;
+  },
+
+  wrapInner: function( html ) {
+    if ( jQuery.isFunction( html ) ) {
+      return this.each(function(i) {
+        jQuery(this).wrapInner( html.call(this, i) );
+      });
+    }
+
+    return this.each(function() {
+      var self = jQuery( this ),
+        contents = self.contents();
+
+      if ( contents.length ) {
+        contents.wrapAll( html );
+
+      } else {
+        self.append( html );
+      }
+    });
+  },
+
+  wrap: function( html ) {
+    var isFunction = jQuery.isFunction( html );
+
+    return this.each(function(i) {
+      jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
+    });
+  },
+
+  unwrap: function() {
+    return this.parent().each(function() {
+      if ( !jQuery.nodeName( this, "body" ) ) {
+        jQuery( this ).replaceWith( this.childNodes );
+      }
+    }).end();
+  }
+});
+
+
+jQuery.expr.filters.hidden = function( elem ) {
+  // Support: Opera <= 12.12
+  // Opera reports offsetWidths and offsetHeights less than zero on some elements
+  return elem.offsetWidth <= 0 && elem.offsetHeight <= 0 ||
+    (!support.reliableHiddenOffsets() &&
+      ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none");
+};
+
+jQuery.expr.filters.visible = function( elem ) {
+  return !jQuery.expr.filters.hidden( elem );
+};
+
+
+
+
+var r20 = /%20/g,
+  rbracket = /\[\]$/,
+  rCRLF = /\r?\n/g,
+  rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
+  rsubmittable = /^(?:input|select|textarea|keygen)/i;
+
+function buildParams( prefix, obj, traditional, add ) {
+  var name;
+
+  if ( jQuery.isArray( obj ) ) {
+    // Serialize array item.
+    jQuery.each( obj, function( i, v ) {
+      if ( traditional || rbracket.test( prefix ) ) {
+        // Treat each array item as a scalar.
+        add( prefix, v );
+
+      } else {
+        // Item is non-scalar (array or object), encode its numeric index.
+        buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
+      }
+    });
+
+  } else if ( !traditional && jQuery.type( obj ) === "object" ) {
+    // Serialize object item.
+    for ( name in obj ) {
+      buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
+    }
+
+  } else {
+    // Serialize scalar item.
+    add( prefix, obj );
+  }
+}
+
+// Serialize an array of form elements or a set of
+// key/values into a query string
+jQuery.param = function( a, traditional ) {
+  var prefix,
+    s = [],
+    add = function( key, value ) {
+      // If value is a function, invoke it and return its value
+      value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
+      s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
+    };
+
+  // Set traditional to true for jQuery <= 1.3.2 behavior.
+  if ( traditional === undefined ) {
+    traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
+  }
+
+  // If an array was passed in, assume that it is an array of form elements.
+  if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
+    // Serialize the form elements
+    jQuery.each( a, function() {
+      add( this.name, this.value );
+    });
+
+  } else {
+    // If traditional, encode the "old" way (the way 1.3.2 or older
+    // did it), otherwise encode params recursively.
+    for ( prefix in a ) {
+      buildParams( prefix, a[ prefix ], traditional, add );
+    }
+  }
+
+  // Return the resulting serialization
+  return s.join( "&" ).replace( r20, "+" );
+};
+
+jQuery.fn.extend({
+  serialize: function() {
+    return jQuery.param( this.serializeArray() );
+  },
+  serializeArray: function() {
+    return this.map(function() {
+      // Can add propHook for "elements" to filter or add form elements
+      var elements = jQuery.prop( this, "elements" );
+      return elements ? jQuery.makeArray( elements ) : this;
+    })
+    .filter(function() {
+      var type = this.type;
+      // Use .is(":disabled") so that fieldset[disabled] works
+      return this.name && !jQuery( this ).is( ":disabled" ) &&
+        rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
+        ( this.checked || !rcheckableType.test( type ) );
+    })
+    .map(function( i, elem ) {
+      var val = jQuery( this ).val();
+
+      return val == null ?
+        null :
+        jQuery.isArray( val ) ?
+          jQuery.map( val, function( val ) {
+            return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+          }) :
+          { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+    }).get();
+  }
+});
+
+
+// Create the request object
+// (This is still attached to ajaxSettings for backward compatibility)
+jQuery.ajaxSettings.xhr = window.ActiveXObject !== undefined ?
+  // Support: IE6+
+  function() {
+
+    // XHR cannot access local files, always use ActiveX for that case
+    return !this.isLocal &&
+
+      // Support: IE7-8
+      // oldIE XHR does not support non-RFC2616 methods (#13240)
+      // See http://msdn.microsoft.com/en-us/library/ie/ms536648(v=vs.85).aspx
+      // and http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9
+      // Although this check for six methods instead of eight
+      // since IE also does not support "trace" and "connect"
+      /^(get|post|head|put|delete|options)$/i.test( this.type ) &&
+
+      createStandardXHR() || createActiveXHR();
+  } :
+  // For all other browsers, use the standard XMLHttpRequest object
+  createStandardXHR;
+
+var xhrId = 0,
+  xhrCallbacks = {},
+  xhrSupported = jQuery.ajaxSettings.xhr();
+
+// Support: IE<10
+// Open requests must be manually aborted on unload (#5280)
+if ( window.ActiveXObject ) {
+  jQuery( window ).on( "unload", function() {
+    for ( var key in xhrCallbacks ) {
+      xhrCallbacks[ key ]( undefined, true );
+    }
+  });
+}
+
+// Determine support properties
+support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
+xhrSupported = support.ajax = !!xhrSupported;
+
+// Create transport if the browser can provide an xhr
+if ( xhrSupported ) {
+
+  jQuery.ajaxTransport(function( options ) {
+    // Cross domain only allowed if supported through XMLHttpRequest
+    if ( !options.crossDomain || support.cors ) {
+
+      var callback;
+
+      return {
+        send: function( headers, complete ) {
+          var i,
+            xhr = options.xhr(),
+            id = ++xhrId;
+
+          // Open the socket
+          xhr.open( options.type, options.url, options.async, options.username, options.password );
+
+          // Apply custom fields if provided
+          if ( options.xhrFields ) {
+            for ( i in options.xhrFields ) {
+              xhr[ i ] = options.xhrFields[ i ];
+            }
+          }
+
+          // Override mime type if needed
+          if ( options.mimeType && xhr.overrideMimeType ) {
+            xhr.overrideMimeType( options.mimeType );
+          }
+
+          // X-Requested-With header
+          // For cross-domain requests, seeing as conditions for a preflight are
+          // akin to a jigsaw puzzle, we simply never set it to be sure.
+          // (it can always be set on a per-request basis or even using ajaxSetup)
+          // For same-domain requests, won't change header if already provided.
+          if ( !options.crossDomain && !headers["X-Requested-With"] ) {
+            headers["X-Requested-With"] = "XMLHttpRequest";
+          }
+
+          // Set headers
+          for ( i in headers ) {
+            // Support: IE<9
+            // IE's ActiveXObject throws a 'Type Mismatch' exception when setting
+            // request header to a null-value.
+            //
+            // To keep consistent with other XHR implementations, cast the value
+            // to string and ignore `undefined`.
+            if ( headers[ i ] !== undefined ) {
+              xhr.setRequestHeader( i, headers[ i ] + "" );
+            }
+          }
+
+          // Do send the request
+          // This may raise an exception which is actually
+          // handled in jQuery.ajax (so no try/catch here)
+          xhr.send( ( options.hasContent && options.data ) || null );
+
+          // Listener
+          callback = function( _, isAbort ) {
+            var status, statusText, responses;
+
+            // Was never called and is aborted or complete
+            if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
+              // Clean up
+              delete xhrCallbacks[ id ];
+              callback = undefined;
+              xhr.onreadystatechange = jQuery.noop;
+
+              // Abort manually if needed
+              if ( isAbort ) {
+                if ( xhr.readyState !== 4 ) {
+                  xhr.abort();
+                }
+              } else {
+                responses = {};
+                status = xhr.status;
+
+                // Support: IE<10
+                // Accessing binary-data responseText throws an exception
+                // (#11426)
+                if ( typeof xhr.responseText === "string" ) {
+                  responses.text = xhr.responseText;
+                }
+
+                // Firefox throws an exception when accessing
+                // statusText for faulty cross-domain requests
+                try {
+                  statusText = xhr.statusText;
+                } catch( e ) {
+                  // We normalize with Webkit giving an empty statusText
+                  statusText = "";
+                }
+
+                // Filter status for non standard behaviors
+
+                // If the request is local and we have data: assume a success
+                // (success with no data won't get notified, that's the best we
+                // can do given current implementations)
+                if ( !status && options.isLocal && !options.crossDomain ) {
+                  status = responses.text ? 200 : 404;
+                // IE - #1450: sometimes returns 1223 when it should be 204
+                } else if ( status === 1223 ) {
+                  status = 204;
+                }
+              }
+            }
+
+            // Call complete if needed
+            if ( responses ) {
+              complete( status, statusText, responses, xhr.getAllResponseHeaders() );
+            }
+          };
+
+          if ( !options.async ) {
+            // if we're in sync mode we fire the callback
+            callback();
+          } else if ( xhr.readyState === 4 ) {
+            // (IE6 & IE7) if it's in cache and has been
+            // retrieved directly we need to fire the callback
+            setTimeout( callback );
+          } else {
+            // Add to the list of active xhr callbacks
+            xhr.onreadystatechange = xhrCallbacks[ id ] = callback;
+          }
+        },
+
+        abort: function() {
+          if ( callback ) {
+            callback( undefined, true );
+          }
+        }
+      };
+    }
+  });
+}
+
+// Functions to create xhrs
+function createStandardXHR() {
+  try {
+    return new window.XMLHttpRequest();
+  } catch( e ) {}
+}
+
+function createActiveXHR() {
+  try {
+    return new window.ActiveXObject( "Microsoft.XMLHTTP" );
+  } catch( e ) {}
+}
+
+
+
+
+// Install script dataType
+jQuery.ajaxSetup({
+  accepts: {
+    script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
+  },
+  contents: {
+    script: /(?:java|ecma)script/
+  },
+  converters: {
+    "text script": function( text ) {
+      jQuery.globalEval( text );
+      return text;
+    }
+  }
+});
+
+// Handle cache's special case and global
+jQuery.ajaxPrefilter( "script", function( s ) {
+  if ( s.cache === undefined ) {
+    s.cache = false;
+  }
+  if ( s.crossDomain ) {
+    s.type = "GET";
+    s.global = false;
+  }
+});
+
+// Bind script tag hack transport
+jQuery.ajaxTransport( "script", function(s) {
+
+  // This transport only deals with cross domain requests
+  if ( s.crossDomain ) {
+
+    var script,
+      head = document.head || jQuery("head")[0] || document.documentElement;
+
+    return {
+
+      send: function( _, callback ) {
+
+        script = document.createElement("script");
+
+        script.async = true;
+
+        if ( s.scriptCharset ) {
+          script.charset = s.scriptCharset;
+        }
+
+        script.src = s.url;
+
+        // Attach handlers for all browsers
+        script.onload = script.onreadystatechange = function( _, isAbort ) {
+
+          if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
+
+            // Handle memory leak in IE
+            script.onload = script.onreadystatechange = null;
+
+            // Remove the script
+            if ( script.parentNode ) {
+              script.parentNode.removeChild( script );
+            }
+
+            // Dereference the script
+            script = null;
+
+            // Callback if not abort
+            if ( !isAbort ) {
+              callback( 200, "success" );
+            }
+          }
+        };
+
+        // Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending
+        // Use native DOM manipulation to avoid our domManip AJAX trickery
+        head.insertBefore( script, head.firstChild );
+      },
+
+      abort: function() {
+        if ( script ) {
+          script.onload( undefined, true );
+        }
+      }
+    };
+  }
+});
+
+
+
+
+var oldCallbacks = [],
+  rjsonp = /(=)\?(?=&|$)|\?\?/;
+
+// Default jsonp settings
+jQuery.ajaxSetup({
+  jsonp: "callback",
+  jsonpCallback: function() {
+    var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
+    this[ callback ] = true;
+    return callback;
+  }
+});
+
+// Detect, normalize options and install callbacks for jsonp requests
+jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
+
+  var callbackName, overwritten, responseContainer,
+    jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
+      "url" :
+      typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data"
+    );
+
+  // Handle iff the expected data type is "jsonp" or we have a parameter to set
+  if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
+
+    // Get callback name, remembering preexisting value associated with it
+    callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
+      s.jsonpCallback() :
+      s.jsonpCallback;
+
+    // Insert callback into url or form data
+    if ( jsonProp ) {
+      s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
+    } else if ( s.jsonp !== false ) {
+      s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
+    }
+
+    // Use data converter to retrieve json after script execution
+    s.converters["script json"] = function() {
+      if ( !responseContainer ) {
+        jQuery.error( callbackName + " was not called" );
+      }
+      return responseContainer[ 0 ];
+    };
+
+    // force json dataType
+    s.dataTypes[ 0 ] = "json";
+
+    // Install callback
+    overwritten = window[ callbackName ];
+    window[ callbackName ] = function() {
+      responseContainer = arguments;
+    };
+
+    // Clean-up function (fires after converters)
+    jqXHR.always(function() {
+      // Restore preexisting value
+      window[ callbackName ] = overwritten;
+
+      // Save back as free
+      if ( s[ callbackName ] ) {
+        // make sure that re-using the options doesn't screw things around
+        s.jsonpCallback = originalSettings.jsonpCallback;
+
+        // save the callback name for future use
+        oldCallbacks.push( callbackName );
+      }
+
+      // Call if it was a function and we have a response
+      if ( responseContainer && jQuery.isFunction( overwritten ) ) {
+        overwritten( responseContainer[ 0 ] );
+      }
+
+      responseContainer = overwritten = undefined;
+    });
+
+    // Delegate to script
+    return "script";
+  }
+});
+
+
+
+
+// data: string of html
+// context (optional): If specified, the fragment will be created in this context, defaults to document
+// keepScripts (optional): If true, will include scripts passed in the html string
+jQuery.parseHTML = function( data, context, keepScripts ) {
+  if ( !data || typeof data !== "string" ) {
+    return null;
+  }
+  if ( typeof context === "boolean" ) {
+    keepScripts = context;
+    context = false;
+  }
+  context = context || document;
+
+  var parsed = rsingleTag.exec( data ),
+    scripts = !keepScripts && [];
+
+  // Single tag
+  if ( parsed ) {
+    return [ context.createElement( parsed[1] ) ];
+  }
+
+  parsed = jQuery.buildFragment( [ data ], context, scripts );
+
+  if ( scripts && scripts.length ) {
+    jQuery( scripts ).remove();
+  }
+
+  return jQuery.merge( [], parsed.childNodes );
+};
+
+
+// Keep a copy of the old load method
+var _load = jQuery.fn.load;
+
+/**
+ * Load a url into a page
+ */
+jQuery.fn.load = function( url, params, callback ) {
+  if ( typeof url !== "string" && _load ) {
+    return _load.apply( this, arguments );
+  }
+
+  var selector, response, type,
+    self = this,
+    off = url.indexOf(" ");
+
+  if ( off >= 0 ) {
+    selector = jQuery.trim( url.slice( off, url.length ) );
+    url = url.slice( 0, off );
+  }
+
+  // If it's a function
+  if ( jQuery.isFunction( params ) ) {
+
+    // We assume that it's the callback
+    callback = params;
+    params = undefined;
+
+  // Otherwise, build a param string
+  } else if ( params && typeof params === "object" ) {
+    type = "POST";
+  }
+
+  // If we have elements to modify, make the request
+  if ( self.length > 0 ) {
+    jQuery.ajax({
+      url: url,
+
+      // if "type" variable is undefined, then "GET" method will be used
+      type: type,
+      dataType: "html",
+      data: params
+    }).done(function( responseText ) {
+
+      // Save response for use in complete callback
+      response = arguments;
+
+      self.html( selector ?
+
+        // If a selector was specified, locate the right elements in a dummy div
+        // Exclude scripts to avoid IE 'Permission Denied' errors
+        jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) :
+
+        // Otherwise use the full result
+        responseText );
+
+    }).complete( callback && function( jqXHR, status ) {
+      self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
+    });
+  }
+
+  return this;
+};
+
+
+
+
+jQuery.expr.filters.animated = function( elem ) {
+  return jQuery.grep(jQuery.timers, function( fn ) {
+    return elem === fn.elem;
+  }).length;
+};
+
+
+
+
+
+var docElem = window.document.documentElement;
+
+/**
+ * Gets a window from an element
+ */
+function getWindow( elem ) {
+  return jQuery.isWindow( elem ) ?
+    elem :
+    elem.nodeType === 9 ?
+      elem.defaultView || elem.parentWindow :
+      false;
+}
+
+jQuery.offset = {
+  setOffset: function( elem, options, i ) {
+    var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
+      position = jQuery.css( elem, "position" ),
+      curElem = jQuery( elem ),
+      props = {};
+
+    // set position first, in-case top/left are set even on static elem
+    if ( position === "static" ) {
+      elem.style.position = "relative";
+    }
+
+    curOffset = curElem.offset();
+    curCSSTop = jQuery.css( elem, "top" );
+    curCSSLeft = jQuery.css( elem, "left" );
+    calculatePosition = ( position === "absolute" || position === "fixed" ) &&
+      jQuery.inArray("auto", [ curCSSTop, curCSSLeft ] ) > -1;
+
+    // need to be able to calculate position if either top or left is auto and position is either absolute or fixed
+    if ( calculatePosition ) {
+      curPosition = curElem.position();
+      curTop = curPosition.top;
+      curLeft = curPosition.left;
+    } else {
+      curTop = parseFloat( curCSSTop ) || 0;
+      curLeft = parseFloat( curCSSLeft ) || 0;
+    }
+
+    if ( jQuery.isFunction( options ) ) {
+      options = options.call( elem, i, curOffset );
+    }
+
+    if ( options.top != null ) {
+      props.top = ( options.top - curOffset.top ) + curTop;
+    }
+    if ( options.left != null ) {
+      props.left = ( options.left - curOffset.left ) + curLeft;
+    }
+
+    if ( "using" in options ) {
+      options.using.call( elem, props );
+    } else {
+      curElem.css( props );
+    }
+  }
+};
+
+jQuery.fn.extend({
+  offset: function( options ) {
+    if ( arguments.length ) {
+      return options === undefined ?
+        this :
+        this.each(function( i ) {
+          jQuery.offset.setOffset( this, options, i );
+        });
+    }
+
+    var docElem, win,
+      box = { top: 0, left: 0 },
+      elem = this[ 0 ],
+      doc = elem && elem.ownerDocument;
+
+    if ( !doc ) {
+      return;
+    }
+
+    docElem = doc.documentElement;
+
+    // Make sure it's not a disconnected DOM node
+    if ( !jQuery.contains( docElem, elem ) ) {
+      return box;
+    }
+
+    // If we don't have gBCR, just use 0,0 rather than error
+    // BlackBerry 5, iOS 3 (original iPhone)
+    if ( typeof elem.getBoundingClientRect !== strundefined ) {
+      box = elem.getBoundingClientRect();
+    }
+    win = getWindow( doc );
+    return {
+      top: box.top  + ( win.pageYOffset || docElem.scrollTop )  - ( docElem.clientTop  || 0 ),
+      left: box.left + ( win.pageXOffset || docElem.scrollLeft ) - ( docElem.clientLeft || 0 )
+    };
+  },
+
+  position: function() {
+    if ( !this[ 0 ] ) {
+      return;
+    }
+
+    var offsetParent, offset,
+      parentOffset = { top: 0, left: 0 },
+      elem = this[ 0 ];
+
+    // fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is its only offset parent
+    if ( jQuery.css( elem, "position" ) === "fixed" ) {
+      // we assume that getBoundingClientRect is available when computed position is fixed
+      offset = elem.getBoundingClientRect();
+    } else {
+      // Get *real* offsetParent
+      offsetParent = this.offsetParent();
+
+      // Get correct offsets
+      offset = this.offset();
+      if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
+        parentOffset = offsetParent.offset();
+      }
+
+      // Add offsetParent borders
+      parentOffset.top  += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
+      parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
+    }
+
+    // Subtract parent offsets and element margins
+    // note: when an element has margin: auto the offsetLeft and marginLeft
+    // are the same in Safari causing offset.left to incorrectly be 0
+    return {
+      top:  offset.top  - parentOffset.top - jQuery.css( elem, "marginTop", true ),
+      left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true)
+    };
+  },
+
+  offsetParent: function() {
+    return this.map(function() {
+      var offsetParent = this.offsetParent || docElem;
+
+      while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position" ) === "static" ) ) {
+        offsetParent = offsetParent.offsetParent;
+      }
+      return offsetParent || docElem;
+    });
+  }
+});
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
+  var top = /Y/.test( prop );
+
+  jQuery.fn[ method ] = function( val ) {
+    return access( this, function( elem, method, val ) {
+      var win = getWindow( elem );
+
+      if ( val === undefined ) {
+        return win ? (prop in win) ? win[ prop ] :
+          win.document.documentElement[ method ] :
+          elem[ method ];
+      }
+
+      if ( win ) {
+        win.scrollTo(
+          !top ? val : jQuery( win ).scrollLeft(),
+          top ? val : jQuery( win ).scrollTop()
+        );
+
+      } else {
+        elem[ method ] = val;
+      }
+    }, method, val, arguments.length, null );
+  };
+});
+
+// Add the top/left cssHooks using jQuery.fn.position
+// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
+// getComputedStyle returns percent when specified for top/left/bottom/right
+// rather than make the css module depend on the offset module, we just check for it here
+jQuery.each( [ "top", "left" ], function( i, prop ) {
+  jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
+    function( elem, computed ) {
+      if ( computed ) {
+        computed = curCSS( elem, prop );
+        // if curCSS returns percentage, fallback to offset
+        return rnumnonpx.test( computed ) ?
+          jQuery( elem ).position()[ prop ] + "px" :
+          computed;
+      }
+    }
+  );
+});
+
+
+// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
+jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
+  jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
+    // margin is only for outerHeight, outerWidth
+    jQuery.fn[ funcName ] = function( margin, value ) {
+      var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
+        extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
+
+      return access( this, function( elem, type, value ) {
+        var doc;
+
+        if ( jQuery.isWindow( elem ) ) {
+          // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
+          // isn't a whole lot we can do. See pull request at this URL for discussion:
+          // https://github.com/jquery/jquery/pull/764
+          return elem.document.documentElement[ "client" + name ];
+        }
+
+        // Get document width or height
+        if ( elem.nodeType === 9 ) {
+          doc = elem.documentElement;
+
+          // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest
+          // unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.
+          return Math.max(
+            elem.body[ "scroll" + name ], doc[ "scroll" + name ],
+            elem.body[ "offset" + name ], doc[ "offset" + name ],
+            doc[ "client" + name ]
+          );
+        }
+
+        return value === undefined ?
+          // Get width or height on the element, requesting but not forcing parseFloat
+          jQuery.css( elem, type, extra ) :
+
+          // Set width or height on the element
+          jQuery.style( elem, type, value, extra );
+      }, type, chainable ? margin : undefined, chainable, null );
+    };
+  });
+});
+
+
+// The number of elements contained in the matched element set
+jQuery.fn.size = function() {
+  return this.length;
+};
+
+jQuery.fn.andSelf = jQuery.fn.addBack;
+
+
+
+
+// Register as a named AMD module, since jQuery can be concatenated with other
+// files that may use define, but not via a proper concatenation script that
+// understands anonymous AMD modules. A named AMD is safest and most robust
+// way to register. Lowercase jquery is used because AMD module names are
+// derived from file names, and jQuery is normally delivered in a lowercase
+// file name. Do this after creating the global so that if an AMD module wants
+// to call noConflict to hide this version of jQuery, it will work.
+
+// Note that for maximum portability, libraries that are not jQuery should
+// declare themselves as anonymous modules, and avoid setting a global if an
+// AMD loader is present. jQuery is a special case. For more information, see
+// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
+
+if ( typeof define === "function" && define.amd ) {
+  define( "jquery", [], function() {
+    return jQuery;
+  });
+}
+
+
+
+
+var
+  // Map over jQuery in case of overwrite
+  _jQuery = window.jQuery,
+
+  // Map over the $ in case of overwrite
+  _$ = window.$;
+
+jQuery.noConflict = function( deep ) {
+  if ( window.$ === jQuery ) {
+    window.$ = _$;
+  }
+
+  if ( deep && window.jQuery === jQuery ) {
+    window.jQuery = _jQuery;
+  }
+
+  return jQuery;
+};
+
+// Expose jQuery and $ identifiers, even in
+// AMD (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
+// and CommonJS for browser emulators (#13566)
+if ( typeof noGlobal === strundefined ) {
+  window.jQuery = window.$ = jQuery;
+}
+
+
+
+
+return jQuery;
+
+}));

+ 263 - 0
app/assets/javascripts/blazer/jquery.stickytableheaders.js

@@ -0,0 +1,263 @@
+/*! Copyright (c) 2011 by Jonas Mosbech - https://github.com/jmosbech/StickyTableHeaders
+  MIT license info: https://github.com/jmosbech/StickyTableHeaders/blob/master/license.txt */
+
+;(function ($, window, undefined) {
+  'use strict';
+
+  var name = 'stickyTableHeaders',
+    id = 0,
+    defaults = {
+      fixedOffset: 0,
+      leftOffset: 0,
+      scrollableArea: window
+    };
+
+  function Plugin (el, options) {
+    // To avoid scope issues, use 'base' instead of 'this'
+    // to reference this class from internal events and functions.
+    var base = this;
+
+    // Access to jQuery and DOM versions of element
+    base.$el = $(el);
+    base.el = el;
+    base.id = id++;
+
+    // Listen for destroyed, call teardown
+    base.$el.bind('destroyed',
+      $.proxy(base.teardown, base));
+
+    // Cache DOM refs for performance reasons
+    base.$clonedHeader = null;
+    base.$originalHeader = null;
+
+    // Keep track of state
+    base.isSticky = false;
+    base.hasBeenSticky = false;
+    base.leftOffset = null;
+    base.topOffset = null;
+
+    base.init = function () {
+      base.options = $.extend({}, defaults, options);
+
+      base.$el.each(function () {
+        var $this = $(this);
+
+        // remove padding on <table> to fix issue #7
+        $this.css('padding', 0);
+
+        base.$scrollableArea = $(base.options.scrollableArea);
+
+        base.$originalHeader = $('thead:first', this);
+        base.$clonedHeader = base.$originalHeader.clone();
+        $this.trigger('clonedHeader.' + name, [base.$clonedHeader]);
+
+        base.$clonedHeader.addClass('tableFloatingHeader');
+        base.$clonedHeader.css('display', 'none');
+
+        base.$originalHeader.addClass('tableFloatingHeaderOriginal');
+
+        base.$originalHeader.after(base.$clonedHeader);
+
+        base.$printStyle = $('<style type="text/css" media="print">' +
+          '.tableFloatingHeader{display:none !important;}' +
+          '.tableFloatingHeaderOriginal{position:static !important;}' +
+          '</style>');
+        $('head').append(base.$printStyle);
+      });
+
+      base.updateWidth();
+      base.toggleHeaders();
+
+      base.bind();
+    };
+
+    base.destroy = function (){
+      base.$el.unbind('destroyed', base.teardown);
+      base.teardown();
+    };
+
+    base.teardown = function(){
+      if (base.isSticky) {
+        base.$originalHeader.css('position', 'static');
+      }
+      $.removeData(base.el, 'plugin_' + name);
+      base.unbind();
+
+      base.$clonedHeader.remove();
+      base.$originalHeader.removeClass('tableFloatingHeaderOriginal');
+      base.$originalHeader.css('visibility', 'visible');
+      base.$printStyle.remove();
+
+      base.el = null;
+      base.$el = null;
+    };
+
+    base.bind = function(){
+      base.$scrollableArea.on('scroll.' + name, base.toggleHeaders);
+      if (!base.isWindowScrolling()) {
+        $(window).on('scroll.' + name + base.id, base.setPositionValues);
+        $(window).on('resize.' + name + base.id, base.toggleHeaders);
+      }
+      base.$scrollableArea.on('resize.' + name, base.toggleHeaders);
+      base.$scrollableArea.on('resize.' + name, base.updateWidth);
+    };
+
+    base.unbind = function(){
+      // unbind window events by specifying handle so we don't remove too much
+      base.$scrollableArea.off('.' + name, base.toggleHeaders);
+      if (!base.isWindowScrolling()) {
+        $(window).off('.' + name + base.id, base.setPositionValues);
+        $(window).off('.' + name + base.id, base.toggleHeaders);
+      }
+      base.$scrollableArea.off('.' + name, base.updateWidth);
+      base.$el.off('.' + name);
+      base.$el.find('*').off('.' + name);
+    };
+
+    base.toggleHeaders = function () {
+      if (base.$el) {
+        base.$el.each(function () {
+          var $this = $(this),
+            newLeft,
+            newTopOffset = base.isWindowScrolling() ? (
+                  isNaN(base.options.fixedOffset) ?
+                  base.options.fixedOffset.outerHeight() :
+                  base.options.fixedOffset
+                ) :
+                base.$scrollableArea.offset().top + (!isNaN(base.options.fixedOffset) ? base.options.fixedOffset : 0),
+            offset = $this.offset(),
+
+            scrollTop = base.$scrollableArea.scrollTop() + newTopOffset,
+            scrollLeft = base.$scrollableArea.scrollLeft(),
+
+            scrolledPastTop = base.isWindowScrolling() ?
+                scrollTop > offset.top :
+                newTopOffset > offset.top,
+            notScrolledPastBottom = (base.isWindowScrolling() ? scrollTop : 0) <
+                (offset.top + $this.height() - base.$clonedHeader.height() - (base.isWindowScrolling() ? 0 : newTopOffset));
+
+          if (scrolledPastTop && notScrolledPastBottom) {
+            newLeft = offset.left - scrollLeft + base.options.leftOffset;
+            base.$originalHeader.css({
+              'position': 'fixed',
+              'margin-top': 0,
+              'left': newLeft,
+              'z-index': 1 // #18: opacity bug
+            });
+            base.isSticky = true;
+            base.leftOffset = newLeft;
+            base.topOffset = newTopOffset;
+            base.$clonedHeader.css('display', '');
+            base.setPositionValues();
+            // make sure the width is correct: the user might have resized the browser while in static mode
+            base.updateWidth();
+          } else if (base.isSticky) {
+            base.$originalHeader.css('position', 'static');
+            base.$clonedHeader.css('display', 'none');
+            base.isSticky = false;
+            base.resetWidth($("td,th", base.$clonedHeader), $("td,th", base.$originalHeader));
+          }
+        });
+      }
+    };
+
+    base.isWindowScrolling = function() {
+      return base.$scrollableArea[0] === window;
+    };
+
+    base.setPositionValues = function () {
+      var winScrollTop = $(window).scrollTop(),
+        winScrollLeft = $(window).scrollLeft();
+      if (!base.isSticky ||
+          winScrollTop < 0 || winScrollTop + $(window).height() > $(document).height() ||
+          winScrollLeft < 0 || winScrollLeft + $(window).width() > $(document).width()) {
+        return;
+      }
+      base.$originalHeader.css({
+        'top': base.topOffset - (base.isWindowScrolling() ? 0 : winScrollTop),
+        'left': base.leftOffset - (base.isWindowScrolling() ? 0 : winScrollLeft)
+      });
+    };
+
+    base.updateWidth = function () {
+      if (!base.isSticky) {
+        return;
+      }
+      // Copy cell widths from clone
+      if (!base.$originalHeaderCells) {
+        base.$originalHeaderCells = $('th,td', base.$originalHeader);
+      }
+      if (!base.$clonedHeaderCells) {
+        base.$clonedHeaderCells = $('th,td', base.$clonedHeader);
+      }
+      var cellWidths = base.getWidth(base.$clonedHeaderCells);
+      base.setWidth(cellWidths, base.$clonedHeaderCells, base.$originalHeaderCells);
+
+      // Copy row width from whole table
+      base.$originalHeader.css('width', base.$clonedHeader.width());
+    };
+
+    base.getWidth = function ($clonedHeaders) {
+      var widths = [];
+      $clonedHeaders.each(function (index) {
+        var width, $this = $(this);
+
+        if ($this.css('box-sizing') === 'border-box') {
+          width = $this.outerWidth(); // #39: border-box bug
+        } else {
+          width = $this.width();
+        }
+
+        widths[index] = width;
+      });
+      return widths;
+    };
+
+    base.setWidth = function (widths, $clonedHeaders, $origHeaders) {
+      $clonedHeaders.each(function (index) {
+        var width = widths[index];
+        $origHeaders.eq(index).css({
+          'min-width': width,
+          'max-width': width
+        });
+      });
+    };
+
+    base.resetWidth = function ($clonedHeaders, $origHeaders) {
+      $clonedHeaders.each(function (index) {
+        var $this = $(this);
+        $origHeaders.eq(index).css({
+          'min-width': $this.css("min-width"),
+          'max-width': $this.css("max-width")
+        });
+      });
+    };
+
+    base.updateOptions = function(options) {
+      base.options = $.extend({}, defaults, options);
+      base.updateWidth();
+      base.toggleHeaders();
+    };
+
+    // Run initializer
+    base.init();
+  }
+
+  // A plugin wrapper around the constructor,
+  // preventing against multiple instantiations
+  $.fn[name] = function ( options ) {
+    return this.each(function () {
+      var instance = $.data(this, 'plugin_' + name);
+      if (instance) {
+        if (typeof options === "string") {
+          instance[options].apply(instance);
+        } else {
+          instance.updateOptions(options);
+        }
+      } else if(options !== 'destroy') {
+        $.data(this, 'plugin_' + name, new Plugin( this, options ));
+      }
+    });
+  };
+
+})(jQuery, window);

+ 469 - 0
app/assets/javascripts/blazer/jquery_ujs.js

@@ -0,0 +1,469 @@
+(function($, undefined) {
+
+/**
+ * Unobtrusive scripting adapter for jQuery
+ * https://github.com/rails/jquery-ujs
+ *
+ * Requires jQuery 1.8.0 or later.
+ *
+ * Released under the MIT license
+ *
+ */
+
+  // Cut down on the number of issues from people inadvertently including jquery_ujs twice
+  // by detecting and raising an error when it happens.
+  if ( $.rails !== undefined ) {
+    $.error('jquery-ujs has already been loaded!');
+  }
+
+  // Shorthand to make it a little easier to call public rails functions from within rails.js
+  var rails;
+  var $document = $(document);
+
+  $.rails = rails = {
+    // Link elements bound by jquery-ujs
+    linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote], a[data-disable-with], a[data-disable]',
+
+    // Button elements bound by jquery-ujs
+    buttonClickSelector: 'button[data-remote]:not(form button), button[data-confirm]:not(form button)',
+
+    // Select elements bound by jquery-ujs
+    inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]',
+
+    // Form elements bound by jquery-ujs
+    formSubmitSelector: 'form',
+
+    // Form input elements bound by jquery-ujs
+    formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not([type]), input[type=submit][form], input[type=image][form], button[type=submit][form], button[form]:not([type])',
+
+    // Form input elements disabled during form submission
+    disableSelector: 'input[data-disable-with]:enabled, button[data-disable-with]:enabled, textarea[data-disable-with]:enabled, input[data-disable]:enabled, button[data-disable]:enabled, textarea[data-disable]:enabled',
+
+    // Form input elements re-enabled after form submission
+    enableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled, input[data-disable]:disabled, button[data-disable]:disabled, textarea[data-disable]:disabled',
+
+    // Form required input elements
+    requiredInputSelector: 'input[name][required]:not([disabled]),textarea[name][required]:not([disabled])',
+
+    // Form file input elements
+    fileInputSelector: 'input[type=file]',
+
+    // Link onClick disable selector with possible reenable after remote submission
+    linkDisableSelector: 'a[data-disable-with], a[data-disable]',
+
+    // Button onClick disable selector with possible reenable after remote submission
+    buttonDisableSelector: 'button[data-remote][data-disable-with], button[data-remote][data-disable]',
+
+    // Make sure that every Ajax request sends the CSRF token
+    CSRFProtection: function(xhr) {
+      var token = $('meta[name="csrf-token"]').attr('content');
+      if (token) xhr.setRequestHeader('X-CSRF-Token', token);
+    },
+
+    // making sure that all forms have actual up-to-date token(cached forms contain old one)
+    refreshCSRFTokens: function(){
+      var csrfToken = $('meta[name=csrf-token]').attr('content');
+      var csrfParam = $('meta[name=csrf-param]').attr('content');
+      $('form input[name="' + csrfParam + '"]').val(csrfToken);
+    },
+
+    // Triggers an event on an element and returns false if the event result is false
+    fire: function(obj, name, data) {
+      var event = $.Event(name);
+      obj.trigger(event, data);
+      return event.result !== false;
+    },
+
+    // Default confirm dialog, may be overridden with custom confirm dialog in $.rails.confirm
+    confirm: function(message) {
+      return confirm(message);
+    },
+
+    // Default ajax function, may be overridden with custom function in $.rails.ajax
+    ajax: function(options) {
+      return $.ajax(options);
+    },
+
+    // Default way to get an element's href. May be overridden at $.rails.href.
+    href: function(element) {
+      return element.attr('href');
+    },
+
+    // Submits "remote" forms and links with ajax
+    handleRemote: function(element) {
+      var method, url, data, elCrossDomain, crossDomain, withCredentials, dataType, options;
+
+      if (rails.fire(element, 'ajax:before')) {
+        elCrossDomain = element.data('cross-domain');
+        crossDomain = elCrossDomain === undefined ? null : elCrossDomain;
+        withCredentials = element.data('with-credentials') || null;
+        dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType);
+
+        if (element.is('form')) {
+          method = element.attr('method');
+          url = element.attr('action');
+          data = element.serializeArray();
+          // memoized value from clicked submit button
+          var button = element.data('ujs:submit-button');
+          if (button) {
+            data.push(button);
+            element.data('ujs:submit-button', null);
+          }
+        } else if (element.is(rails.inputChangeSelector)) {
+          method = element.data('method');
+          url = element.data('url');
+          data = element.serialize();
+          if (element.data('params')) data = data + "&" + element.data('params');
+        } else if (element.is(rails.buttonClickSelector)) {
+          method = element.data('method') || 'get';
+          url = element.data('url');
+          data = element.serialize();
+          if (element.data('params')) data = data + "&" + element.data('params');
+        } else {
+          method = element.data('method');
+          url = rails.href(element);
+          data = element.data('params') || null;
+        }
+
+        options = {
+          type: method || 'GET', data: data, dataType: dataType,
+          // stopping the "ajax:beforeSend" event will cancel the ajax request
+          beforeSend: function(xhr, settings) {
+            if (settings.dataType === undefined) {
+              xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script);
+            }
+            if (rails.fire(element, 'ajax:beforeSend', [xhr, settings])) {
+              element.trigger('ajax:send', xhr);
+            } else {
+              return false;
+            }
+          },
+          success: function(data, status, xhr) {
+            element.trigger('ajax:success', [data, status, xhr]);
+          },
+          complete: function(xhr, status) {
+            element.trigger('ajax:complete', [xhr, status]);
+          },
+          error: function(xhr, status, error) {
+            element.trigger('ajax:error', [xhr, status, error]);
+          },
+          crossDomain: crossDomain
+        };
+
+        // There is no withCredentials for IE6-8 when
+        // "Enable native XMLHTTP support" is disabled
+        if (withCredentials) {
+          options.xhrFields = {
+            withCredentials: withCredentials
+          };
+        }
+
+        // Only pass url to `ajax` options if not blank
+        if (url) { options.url = url; }
+
+        return rails.ajax(options);
+      } else {
+        return false;
+      }
+    },
+
+    // Handles "data-method" on links such as:
+    // <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a>
+    handleMethod: function(link) {
+      var href = rails.href(link),
+        method = link.data('method'),
+        target = link.attr('target'),
+        csrfToken = $('meta[name=csrf-token]').attr('content'),
+        csrfParam = $('meta[name=csrf-param]').attr('content'),
+        form = $('<form method="post" action="' + href + '"></form>'),
+        metadataInput = '<input name="_method" value="' + method + '" type="hidden" />';
+
+      if (csrfParam !== undefined && csrfToken !== undefined) {
+        metadataInput += '<input name="' + csrfParam + '" value="' + csrfToken + '" type="hidden" />';
+      }
+
+      if (target) { form.attr('target', target); }
+
+      form.hide().append(metadataInput).appendTo('body');
+      form.submit();
+    },
+
+    // Helper function that returns form elements that match the specified CSS selector
+    // If form is actually a "form" element this will return associated elements outside the from that have
+    // the html form attribute set
+    formElements: function(form, selector) {
+      return form.is('form') ? $(form[0].elements).filter(selector) : form.find(selector);
+    },
+
+    /* Disables form elements:
+      - Caches element value in 'ujs:enable-with' data store
+      - Replaces element text with value of 'data-disable-with' attribute
+      - Sets disabled property to true
+    */
+    disableFormElements: function(form) {
+      rails.formElements(form, rails.disableSelector).each(function() {
+        rails.disableFormElement($(this));
+      });
+    },
+
+    disableFormElement: function(element) {
+      var method, replacement;
+
+      method = element.is('button') ? 'html' : 'val';
+      replacement = element.data('disable-with');
+
+      element.data('ujs:enable-with', element[method]());
+      if (replacement !== undefined) {
+        element[method](replacement);
+      }
+
+      element.prop('disabled', true);
+    },
+
+    /* Re-enables disabled form elements:
+      - Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`)
+      - Sets disabled property to false
+    */
+    enableFormElements: function(form) {
+      rails.formElements(form, rails.enableSelector).each(function() {
+        rails.enableFormElement($(this));
+      });
+    },
+
+    enableFormElement: function(element) {
+      var method = element.is('button') ? 'html' : 'val';
+      if (element.data('ujs:enable-with')) element[method](element.data('ujs:enable-with'));
+      element.prop('disabled', false);
+    },
+
+   /* For 'data-confirm' attribute:
+      - Fires `confirm` event
+      - Shows the confirmation dialog
+      - Fires the `confirm:complete` event
+
+      Returns `true` if no function stops the chain and user chose yes; `false` otherwise.
+      Attaching a handler to the element's `confirm` event that returns a `falsy` value cancels the confirmation dialog.
+      Attaching a handler to the element's `confirm:complete` event that returns a `falsy` value makes this function
+      return false. The `confirm:complete` event is fired whether or not the user answered true or false to the dialog.
+   */
+    allowAction: function(element) {
+      var message = element.data('confirm'),
+          answer = false, callback;
+      if (!message) { return true; }
+
+      if (rails.fire(element, 'confirm')) {
+        answer = rails.confirm(message);
+        callback = rails.fire(element, 'confirm:complete', [answer]);
+      }
+      return answer && callback;
+    },
+
+    // Helper function which checks for blank inputs in a form that match the specified CSS selector
+    blankInputs: function(form, specifiedSelector, nonBlank) {
+      var inputs = $(), input, valueToCheck,
+          selector = specifiedSelector || 'input,textarea',
+          allInputs = form.find(selector);
+
+      allInputs.each(function() {
+        input = $(this);
+        valueToCheck = input.is('input[type=checkbox],input[type=radio]') ? input.is(':checked') : input.val();
+        // If nonBlank and valueToCheck are both truthy, or nonBlank and valueToCheck are both falsey
+        if (!valueToCheck === !nonBlank) {
+
+          // Don't count unchecked required radio if other radio with same name is checked
+          if (input.is('input[type=radio]') && allInputs.filter('input[type=radio]:checked[name="' + input.attr('name') + '"]').length) {
+            return true; // Skip to next input
+          }
+
+          inputs = inputs.add(input);
+        }
+      });
+      return inputs.length ? inputs : false;
+    },
+
+    // Helper function which checks for non-blank inputs in a form that match the specified CSS selector
+    nonBlankInputs: function(form, specifiedSelector) {
+      return rails.blankInputs(form, specifiedSelector, true); // true specifies nonBlank
+    },
+
+    // Helper function, needed to provide consistent behavior in IE
+    stopEverything: function(e) {
+      $(e.target).trigger('ujs:everythingStopped');
+      e.stopImmediatePropagation();
+      return false;
+    },
+
+    //  replace element's html with the 'data-disable-with' after storing original html
+    //  and prevent clicking on it
+    disableElement: function(element) {
+      var replacement = element.data('disable-with');
+
+      element.data('ujs:enable-with', element.html()); // store enabled state
+      if (replacement !== undefined) {
+        element.html(replacement);
+      }
+
+      element.bind('click.railsDisable', function(e) { // prevent further clicking
+        return rails.stopEverything(e);
+      });
+    },
+
+    // restore element to its original state which was disabled by 'disableElement' above
+    enableElement: function(element) {
+      if (element.data('ujs:enable-with') !== undefined) {
+        element.html(element.data('ujs:enable-with')); // set to old enabled state
+        element.removeData('ujs:enable-with'); // clean up cache
+      }
+      element.unbind('click.railsDisable'); // enable element
+    }
+  };
+
+  if (rails.fire($document, 'rails:attachBindings')) {
+
+    $.ajaxPrefilter(function(options, originalOptions, xhr){ if ( !options.crossDomain ) { rails.CSRFProtection(xhr); }});
+
+    // This event works the same as the load event, except that it fires every
+    // time the page is loaded.
+    //
+    // See https://github.com/rails/jquery-ujs/issues/357
+    // See https://developer.mozilla.org/en-US/docs/Using_Firefox_1.5_caching
+    $(window).on("pageshow.rails", function () {
+      $($.rails.enableSelector).each(function () {
+        var element = $(this);
+
+        if (element.data("ujs:enable-with")) {
+          $.rails.enableFormElement(element);
+        }
+      });
+
+      $($.rails.linkDisableSelector).each(function () {
+        var element = $(this);
+
+        if (element.data("ujs:enable-with")) {
+          $.rails.enableElement(element);
+        }
+      });
+    });
+
+    $document.delegate(rails.linkDisableSelector, 'ajax:complete', function() {
+        rails.enableElement($(this));
+    });
+
+    $document.delegate(rails.buttonDisableSelector, 'ajax:complete', function() {
+        rails.enableFormElement($(this));
+    });
+
+    $document.delegate(rails.linkClickSelector, 'click.rails', function(e) {
+      var link = $(this), method = link.data('method'), data = link.data('params'), metaClick = e.metaKey || e.ctrlKey;
+      if (!rails.allowAction(link)) return rails.stopEverything(e);
+
+      if (!metaClick && link.is(rails.linkDisableSelector)) rails.disableElement(link);
+
+      if (link.data('remote') !== undefined) {
+        if (metaClick && (!method || method === 'GET') && !data) { return true; }
+
+        var handleRemote = rails.handleRemote(link);
+        // response from rails.handleRemote() will either be false or a deferred object promise.
+        if (handleRemote === false) {
+          rails.enableElement(link);
+        } else {
+          handleRemote.error( function() { rails.enableElement(link); } );
+        }
+        return false;
+
+      } else if (link.data('method')) {
+        rails.handleMethod(link);
+        return false;
+      }
+    });
+
+    $document.delegate(rails.buttonClickSelector, 'click.rails', function(e) {
+      var button = $(this);
+
+      if (!rails.allowAction(button)) return rails.stopEverything(e);
+
+      if (button.is(rails.buttonDisableSelector)) rails.disableFormElement(button);
+
+      var handleRemote = rails.handleRemote(button);
+      // response from rails.handleRemote() will either be false or a deferred object promise.
+      if (handleRemote === false) {
+        rails.enableFormElement(button);
+      } else {
+        handleRemote.error( function() { rails.enableFormElement(button); } );
+      }
+      return false;
+    });
+
+    $document.delegate(rails.inputChangeSelector, 'change.rails', function(e) {
+      var link = $(this);
+      if (!rails.allowAction(link)) return rails.stopEverything(e);
+
+      rails.handleRemote(link);
+      return false;
+    });
+
+    $document.delegate(rails.formSubmitSelector, 'submit.rails', function(e) {
+      var form = $(this),
+        remote = form.data('remote') !== undefined,
+        blankRequiredInputs,
+        nonBlankFileInputs;
+
+      if (!rails.allowAction(form)) return rails.stopEverything(e);
+
+      // skip other logic when required values are missing or file upload is present
+      if (form.attr('novalidate') == undefined) {
+        blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector);
+        if (blankRequiredInputs && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) {
+          return rails.stopEverything(e);
+        }
+      }
+
+      if (remote) {
+        nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector);
+        if (nonBlankFileInputs) {
+          // slight timeout so that the submit button gets properly serialized
+          // (make it easy for event handler to serialize form without disabled values)
+          setTimeout(function(){ rails.disableFormElements(form); }, 13);
+          var aborted = rails.fire(form, 'ajax:aborted:file', [nonBlankFileInputs]);
+
+          // re-enable form elements if event bindings return false (canceling normal form submission)
+          if (!aborted) { setTimeout(function(){ rails.enableFormElements(form); }, 13); }
+
+          return aborted;
+        }
+
+        rails.handleRemote(form);
+        return false;
+
+      } else {
+        // slight timeout so that the submit button gets properly serialized
+        setTimeout(function(){ rails.disableFormElements(form); }, 13);
+      }
+    });
+
+    $document.delegate(rails.formInputClickSelector, 'click.rails', function(event) {
+      var button = $(this);
+
+      if (!rails.allowAction(button)) return rails.stopEverything(event);
+
+      // register the pressed submit button
+      var name = button.attr('name'),
+        data = name ? {name:name, value:button.val()} : null;
+
+      button.closest('form').data('ujs:submit-button', data);
+    });
+
+    $document.delegate(rails.formSubmitSelector, 'ajax:send.rails', function(event) {
+      if (this == event.target) rails.disableFormElements($(this));
+    });
+
+    $document.delegate(rails.formSubmitSelector, 'ajax:complete.rails', function(event) {
+      if (this == event.target) rails.enableFormElements($(this));
+    });
+
+    $(function(){
+      rails.refreshCSRFTokens();
+    });
+  }
+
+})( jQuery );

+ 1474 - 0
app/assets/javascripts/blazer/list.js

@@ -0,0 +1,1474 @@
+;(function(){
+
+/**
+ * Require the given path.
+ *
+ * @param {String} path
+ * @return {Object} exports
+ * @api public
+ */
+
+function require(path, parent, orig) {
+  var resolved = require.resolve(path);
+
+  // lookup failed
+  if (null == resolved) {
+    orig = orig || path;
+    parent = parent || 'root';
+    var err = new Error('Failed to require "' + orig + '" from "' + parent + '"');
+    err.path = orig;
+    err.parent = parent;
+    err.require = true;
+    throw err;
+  }
+
+  var module = require.modules[resolved];
+
+  // perform real require()
+  // by invoking the module's
+  // registered function
+  if (!module._resolving && !module.exports) {
+    var mod = {};
+    mod.exports = {};
+    mod.client = mod.component = true;
+    module._resolving = true;
+    module.call(this, mod.exports, require.relative(resolved), mod);
+    delete module._resolving;
+    module.exports = mod.exports;
+  }
+
+  return module.exports;
+}
+
+/**
+ * Registered modules.
+ */
+
+require.modules = {};
+
+/**
+ * Registered aliases.
+ */
+
+require.aliases = {};
+
+/**
+ * Resolve `path`.
+ *
+ * Lookup:
+ *
+ *   - PATH/index.js
+ *   - PATH.js
+ *   - PATH
+ *
+ * @param {String} path
+ * @return {String} path or null
+ * @api private
+ */
+
+require.resolve = function(path) {
+  if (path.charAt(0) === '/') path = path.slice(1);
+
+  var paths = [
+    path,
+    path + '.js',
+    path + '.json',
+    path + '/index.js',
+    path + '/index.json'
+  ];
+
+  for (var i = 0; i < paths.length; i++) {
+    var path = paths[i];
+    if (require.modules.hasOwnProperty(path)) return path;
+    if (require.aliases.hasOwnProperty(path)) return require.aliases[path];
+  }
+};
+
+/**
+ * Normalize `path` relative to the current path.
+ *
+ * @param {String} curr
+ * @param {String} path
+ * @return {String}
+ * @api private
+ */
+
+require.normalize = function(curr, path) {
+  var segs = [];
+
+  if ('.' != path.charAt(0)) return path;
+
+  curr = curr.split('/');
+  path = path.split('/');
+
+  for (var i = 0; i < path.length; ++i) {
+    if ('..' == path[i]) {
+      curr.pop();
+    } else if ('.' != path[i] && '' != path[i]) {
+      segs.push(path[i]);
+    }
+  }
+
+  return curr.concat(segs).join('/');
+};
+
+/**
+ * Register module at `path` with callback `definition`.
+ *
+ * @param {String} path
+ * @param {Function} definition
+ * @api private
+ */
+
+require.register = function(path, definition) {
+  require.modules[path] = definition;
+};
+
+/**
+ * Alias a module definition.
+ *
+ * @param {String} from
+ * @param {String} to
+ * @api private
+ */
+
+require.alias = function(from, to) {
+  if (!require.modules.hasOwnProperty(from)) {
+    throw new Error('Failed to alias "' + from + '", it does not exist');
+  }
+  require.aliases[to] = from;
+};
+
+/**
+ * Return a require function relative to the `parent` path.
+ *
+ * @param {String} parent
+ * @return {Function}
+ * @api private
+ */
+
+require.relative = function(parent) {
+  var p = require.normalize(parent, '..');
+
+  /**
+   * lastIndexOf helper.
+   */
+
+  function lastIndexOf(arr, obj) {
+    var i = arr.length;
+    while (i--) {
+      if (arr[i] === obj) return i;
+    }
+    return -1;
+  }
+
+  /**
+   * The relative require() itself.
+   */
+
+  function localRequire(path) {
+    var resolved = localRequire.resolve(path);
+    return require(resolved, parent, path);
+  }
+
+  /**
+   * Resolve relative to the parent.
+   */
+
+  localRequire.resolve = function(path) {
+    var c = path.charAt(0);
+    if ('/' == c) return path.slice(1);
+    if ('.' == c) return require.normalize(p, path);
+
+    // resolve deps by returning
+    // the dep in the nearest "deps"
+    // directory
+    var segs = parent.split('/');
+    var i = lastIndexOf(segs, 'deps') + 1;
+    if (!i) i = 0;
+    path = segs.slice(0, i + 1).join('/') + '/deps/' + path;
+    return path;
+  };
+
+  /**
+   * Check if module is defined at `path`.
+   */
+
+  localRequire.exists = function(path) {
+    return require.modules.hasOwnProperty(localRequire.resolve(path));
+  };
+
+  return localRequire;
+};
+require.register("component-classes/index.js", function(exports, require, module){
+/**
+ * Module dependencies.
+ */
+
+var index = require('indexof');
+
+/**
+ * Whitespace regexp.
+ */
+
+var re = /\s+/;
+
+/**
+ * toString reference.
+ */
+
+var toString = Object.prototype.toString;
+
+/**
+ * Wrap `el` in a `ClassList`.
+ *
+ * @param {Element} el
+ * @return {ClassList}
+ * @api public
+ */
+
+module.exports = function(el){
+  return new ClassList(el);
+};
+
+/**
+ * Initialize a new ClassList for `el`.
+ *
+ * @param {Element} el
+ * @api private
+ */
+
+function ClassList(el) {
+  if (!el) throw new Error('A DOM element reference is required');
+  this.el = el;
+  this.list = el.classList;
+}
+
+/**
+ * Add class `name` if not already present.
+ *
+ * @param {String} name
+ * @return {ClassList}
+ * @api public
+ */
+
+ClassList.prototype.add = function(name){
+  // classList
+  if (this.list) {
+    this.list.add(name);
+    return this;
+  }
+
+  // fallback
+  var arr = this.array();
+  var i = index(arr, name);
+  if (!~i) arr.push(name);
+  this.el.className = arr.join(' ');
+  return this;
+};
+
+/**
+ * Remove class `name` when present, or
+ * pass a regular expression to remove
+ * any which match.
+ *
+ * @param {String|RegExp} name
+ * @return {ClassList}
+ * @api public
+ */
+
+ClassList.prototype.remove = function(name){
+  if ('[object RegExp]' == toString.call(name)) {
+    return this.removeMatching(name);
+  }
+
+  // classList
+  if (this.list) {
+    this.list.remove(name);
+    return this;
+  }
+
+  // fallback
+  var arr = this.array();
+  var i = index(arr, name);
+  if (~i) arr.splice(i, 1);
+  this.el.className = arr.join(' ');
+  return this;
+};
+
+/**
+ * Remove all classes matching `re`.
+ *
+ * @param {RegExp} re
+ * @return {ClassList}
+ * @api private
+ */
+
+ClassList.prototype.removeMatching = function(re){
+  var arr = this.array();
+  for (var i = 0; i < arr.length; i++) {
+    if (re.test(arr[i])) {
+      this.remove(arr[i]);
+    }
+  }
+  return this;
+};
+
+/**
+ * Toggle class `name`, can force state via `force`.
+ *
+ * For browsers that support classList, but do not support `force` yet,
+ * the mistake will be detected and corrected.
+ *
+ * @param {String} name
+ * @param {Boolean} force
+ * @return {ClassList}
+ * @api public
+ */
+
+ClassList.prototype.toggle = function(name, force){
+  // classList
+  if (this.list) {
+    if ("undefined" !== typeof force) {
+      if (force !== this.list.toggle(name, force)) {
+        this.list.toggle(name); // toggle again to correct
+      }
+    } else {
+      this.list.toggle(name);
+    }
+    return this;
+  }
+
+  // fallback
+  if ("undefined" !== typeof force) {
+    if (!force) {
+      this.remove(name);
+    } else {
+      this.add(name);
+    }
+  } else {
+    if (this.has(name)) {
+      this.remove(name);
+    } else {
+      this.add(name);
+    }
+  }
+
+  return this;
+};
+
+/**
+ * Return an array of classes.
+ *
+ * @return {Array}
+ * @api public
+ */
+
+ClassList.prototype.array = function(){
+  var str = this.el.className.replace(/^\s+|\s+$/g, '');
+  var arr = str.split(re);
+  if ('' === arr[0]) arr.shift();
+  return arr;
+};
+
+/**
+ * Check if class `name` is present.
+ *
+ * @param {String} name
+ * @return {ClassList}
+ * @api public
+ */
+
+ClassList.prototype.has =
+ClassList.prototype.contains = function(name){
+  return this.list
+    ? this.list.contains(name)
+    : !! ~index(this.array(), name);
+};
+
+});
+require.register("segmentio-extend/index.js", function(exports, require, module){
+
+module.exports = function extend (object) {
+    // Takes an unlimited number of extenders.
+    var args = Array.prototype.slice.call(arguments, 1);
+
+    // For each extender, copy their properties on our object.
+    for (var i = 0, source; source = args[i]; i++) {
+        if (!source) continue;
+        for (var property in source) {
+            object[property] = source[property];
+        }
+    }
+
+    return object;
+};
+});
+require.register("component-indexof/index.js", function(exports, require, module){
+module.exports = function(arr, obj){
+  if (arr.indexOf) return arr.indexOf(obj);
+  for (var i = 0; i < arr.length; ++i) {
+    if (arr[i] === obj) return i;
+  }
+  return -1;
+};
+});
+require.register("component-event/index.js", function(exports, require, module){
+var bind = window.addEventListener ? 'addEventListener' : 'attachEvent',
+    unbind = window.removeEventListener ? 'removeEventListener' : 'detachEvent',
+    prefix = bind !== 'addEventListener' ? 'on' : '';
+
+/**
+ * Bind `el` event `type` to `fn`.
+ *
+ * @param {Element} el
+ * @param {String} type
+ * @param {Function} fn
+ * @param {Boolean} capture
+ * @return {Function}
+ * @api public
+ */
+
+exports.bind = function(el, type, fn, capture){
+  el[bind](prefix + type, fn, capture || false);
+  return fn;
+};
+
+/**
+ * Unbind `el` event `type`'s callback `fn`.
+ *
+ * @param {Element} el
+ * @param {String} type
+ * @param {Function} fn
+ * @param {Boolean} capture
+ * @return {Function}
+ * @api public
+ */
+
+exports.unbind = function(el, type, fn, capture){
+  el[unbind](prefix + type, fn, capture || false);
+  return fn;
+};
+});
+require.register("timoxley-to-array/index.js", function(exports, require, module){
+/**
+ * Convert an array-like object into an `Array`.
+ * If `collection` is already an `Array`, then will return a clone of `collection`.
+ *
+ * @param {Array | Mixed} collection An `Array` or array-like object to convert e.g. `arguments` or `NodeList`
+ * @return {Array} Naive conversion of `collection` to a new `Array`.
+ * @api public
+ */
+
+module.exports = function toArray(collection) {
+  if (typeof collection === 'undefined') return []
+  if (collection === null) return [null]
+  if (collection === window) return [window]
+  if (typeof collection === 'string') return [collection]
+  if (isArray(collection)) return collection
+  if (typeof collection.length != 'number') return [collection]
+  if (typeof collection === 'function' && collection instanceof Function) return [collection]
+
+  var arr = []
+  for (var i = 0; i < collection.length; i++) {
+    if (Object.prototype.hasOwnProperty.call(collection, i) || i in collection) {
+      arr.push(collection[i])
+    }
+  }
+  if (!arr.length) return []
+  return arr
+}
+
+function isArray(arr) {
+  return Object.prototype.toString.call(arr) === "[object Array]";
+}
+
+});
+require.register("javve-events/index.js", function(exports, require, module){
+var events = require('event'),
+  toArray = require('to-array');
+
+/**
+ * Bind `el` event `type` to `fn`.
+ *
+ * @param {Element} el, NodeList, HTMLCollection or Array
+ * @param {String} type
+ * @param {Function} fn
+ * @param {Boolean} capture
+ * @api public
+ */
+
+exports.bind = function(el, type, fn, capture){
+  el = toArray(el);
+  for ( var i = 0; i < el.length; i++ ) {
+    events.bind(el[i], type, fn, capture);
+  }
+};
+
+/**
+ * Unbind `el` event `type`'s callback `fn`.
+ *
+ * @param {Element} el, NodeList, HTMLCollection or Array
+ * @param {String} type
+ * @param {Function} fn
+ * @param {Boolean} capture
+ * @api public
+ */
+
+exports.unbind = function(el, type, fn, capture){
+  el = toArray(el);
+  for ( var i = 0; i < el.length; i++ ) {
+    events.unbind(el[i], type, fn, capture);
+  }
+};
+
+});
+require.register("javve-get-by-class/index.js", function(exports, require, module){
+/**
+ * Find all elements with class `className` inside `container`.
+ * Use `single = true` to increase performance in older browsers
+ * when only one element is needed.
+ *
+ * @param {String} className
+ * @param {Element} container
+ * @param {Boolean} single
+ * @api public
+ */
+
+module.exports = (function() {
+  if (document.getElementsByClassName) {
+    return function(container, className, single) {
+      if (single) {
+        return container.getElementsByClassName(className)[0];
+      } else {
+        return container.getElementsByClassName(className);
+      }
+    };
+  } else if (document.querySelector) {
+    return function(container, className, single) {
+      className = '.' + className;
+      if (single) {
+        return container.querySelector(className);
+      } else {
+        return container.querySelectorAll(className);
+      }
+    };
+  } else {
+    return function(container, className, single) {
+      var classElements = [],
+        tag = '*';
+      if (container == null) {
+        container = document;
+      }
+      var els = container.getElementsByTagName(tag);
+      var elsLen = els.length;
+      var pattern = new RegExp("(^|\\s)"+className+"(\\s|$)");
+      for (var i = 0, j = 0; i < elsLen; i++) {
+        if ( pattern.test(els[i].className) ) {
+          if (single) {
+            return els[i];
+          } else {
+            classElements[j] = els[i];
+            j++;
+          }
+        }
+      }
+      return classElements;
+    };
+  }
+})();
+
+});
+require.register("javve-get-attribute/index.js", function(exports, require, module){
+/**
+ * Return the value for `attr` at `element`.
+ *
+ * @param {Element} el
+ * @param {String} attr
+ * @api public
+ */
+
+module.exports = function(el, attr) {
+  var result = (el.getAttribute && el.getAttribute(attr)) || null;
+  if( !result ) {
+    var attrs = el.attributes;
+    var length = attrs.length;
+    for(var i = 0; i < length; i++) {
+      if (attr[i] !== undefined) {
+        if(attr[i].nodeName === attr) {
+          result = attr[i].nodeValue;
+        }
+      }
+    }
+  }
+  return result;
+}
+});
+require.register("javve-natural-sort/index.js", function(exports, require, module){
+/*
+ * Natural Sort algorithm for Javascript - Version 0.7 - Released under MIT license
+ * Author: Jim Palmer (based on chunking idea from Dave Koelle)
+ */
+
+module.exports = function(a, b, options) {
+  var re = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi,
+    sre = /(^[ ]*|[ ]*$)/g,
+    dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,
+    hre = /^0x[0-9a-f]+$/i,
+    ore = /^0/,
+    options = options || {},
+    i = function(s) { return options.insensitive && (''+s).toLowerCase() || ''+s },
+    // convert all to strings strip whitespace
+    x = i(a).replace(sre, '') || '',
+    y = i(b).replace(sre, '') || '',
+    // chunk/tokenize
+    xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
+    yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
+    // numeric, hex or date detection
+    xD = parseInt(x.match(hre)) || (xN.length != 1 && x.match(dre) && Date.parse(x)),
+    yD = parseInt(y.match(hre)) || xD && y.match(dre) && Date.parse(y) || null,
+    oFxNcL, oFyNcL,
+    mult = options.desc ? -1 : 1;
+  // first try and sort Hex codes or Dates
+  if (yD)
+    if ( xD < yD ) return -1 * mult;
+    else if ( xD > yD ) return 1 * mult;
+  // natural sorting through split numeric strings and default strings
+  for(var cLoc=0, numS=Math.max(xN.length, yN.length); cLoc < numS; cLoc++) {
+    // find floats not starting with '0', string or 0 if not defined (Clint Priest)
+    oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0;
+    oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0;
+    // handle numeric vs string comparison - number < string - (Kyle Adams)
+    if (isNaN(oFxNcL) !== isNaN(oFyNcL)) { return (isNaN(oFxNcL)) ? 1 : -1; }
+    // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
+    else if (typeof oFxNcL !== typeof oFyNcL) {
+      oFxNcL += '';
+      oFyNcL += '';
+    }
+    if (oFxNcL < oFyNcL) return -1 * mult;
+    if (oFxNcL > oFyNcL) return 1 * mult;
+  }
+  return 0;
+};
+
+/*
+var defaultSort = getSortFunction();
+
+module.exports = function(a, b, options) {
+  if (arguments.length == 1) {
+    options = a;
+    return getSortFunction(options);
+  } else {
+    return defaultSort(a,b);
+  }
+}
+*/
+});
+require.register("javve-to-string/index.js", function(exports, require, module){
+module.exports = function(s) {
+    s = (s === undefined) ? "" : s;
+    s = (s === null) ? "" : s;
+    s = s.toString();
+    return s;
+};
+
+});
+require.register("component-type/index.js", function(exports, require, module){
+/**
+ * toString ref.
+ */
+
+var toString = Object.prototype.toString;
+
+/**
+ * Return the type of `val`.
+ *
+ * @param {Mixed} val
+ * @return {String}
+ * @api public
+ */
+
+module.exports = function(val){
+  switch (toString.call(val)) {
+    case '[object Date]': return 'date';
+    case '[object RegExp]': return 'regexp';
+    case '[object Arguments]': return 'arguments';
+    case '[object Array]': return 'array';
+    case '[object Error]': return 'error';
+  }
+
+  if (val === null) return 'null';
+  if (val === undefined) return 'undefined';
+  if (val !== val) return 'nan';
+  if (val && val.nodeType === 1) return 'element';
+
+  return typeof val.valueOf();
+};
+
+});
+require.register("list.js/index.js", function(exports, require, module){
+/*
+ListJS with beta 1.0.0
+By Jonny Strömberg (www.jonnystromberg.com, www.listjs.com)
+*/
+(function( window, undefined ) {
+"use strict";
+
+var document = window.document,
+    getByClass = require('get-by-class'),
+    extend = require('extend'),
+    indexOf = require('indexof');
+
+var List = function(id, options, values) {
+
+    var self = this,
+    init,
+        Item = require('./src/item')(self),
+        addAsync = require('./src/add-async')(self),
+        parse = require('./src/parse')(self);
+
+    init = {
+        start: function() {
+            self.listClass      = "list";
+            self.searchClass    = "search";
+            self.sortClass      = "sort";
+            self.page           = 1024;
+            self.i              = 1;
+            self.items          = [];
+            self.visibleItems   = [];
+            self.matchingItems  = [];
+            self.searched       = false;
+            self.filtered       = false;
+            self.handlers       = { 'updated': [] };
+            self.plugins        = {};
+            self.helpers        = {
+                getByClass: getByClass,
+                extend: extend,
+                indexOf: indexOf
+            };
+
+            extend(self, options);
+
+            self.listContainer = (typeof(id) === 'string') ? document.getElementById(id) : id;
+            if (!self.listContainer) { return; }
+            self.list           = getByClass(self.listContainer, self.listClass, true);
+
+            self.templater      = require('./src/templater')(self);
+            self.search         = require('./src/search')(self);
+            self.filter         = require('./src/filter')(self);
+            self.sort           = require('./src/sort')(self);
+
+            this.items();
+            self.update();
+            this.plugins();
+        },
+        items: function() {
+            parse(self.list);
+            if (values !== undefined) {
+                self.add(values);
+            }
+        },
+        plugins: function() {
+            for (var i = 0; i < self.plugins.length; i++) {
+                var plugin = self.plugins[i];
+                self[plugin.name] = plugin;
+                plugin.init(self);
+            }
+        }
+    };
+
+
+    /*
+    * Add object to list
+    */
+    this.add = function(values, callback) {
+        if (callback) {
+            addAsync(values, callback);
+            return;
+        }
+        var added = [],
+            notCreate = false;
+        if (values[0] === undefined){
+            values = [values];
+        }
+        for (var i = 0, il = values.length; i < il; i++) {
+            var item = null;
+            if (values[i] instanceof Item) {
+                item = values[i];
+                item.reload();
+            } else {
+                notCreate = (self.items.length > self.page) ? true : false;
+                item = new Item(values[i], undefined, notCreate);
+            }
+            self.items.push(item);
+            added.push(item);
+        }
+        self.update();
+        return added;
+    };
+
+  this.show = function(i, page) {
+    this.i = i;
+    this.page = page;
+    self.update();
+        return self;
+  };
+
+    /* Removes object from list.
+    * Loops through the list and removes objects where
+    * property "valuename" === value
+    */
+    this.remove = function(valueName, value, options) {
+        var found = 0;
+        for (var i = 0, il = self.items.length; i < il; i++) {
+            if (self.items[i].values()[valueName] == value) {
+                self.templater.remove(self.items[i], options);
+                self.items.splice(i,1);
+                il--;
+                i--;
+                found++;
+            }
+        }
+        self.update();
+        return found;
+    };
+
+    /* Gets the objects in the list which
+    * property "valueName" === value
+    */
+    this.get = function(valueName, value) {
+        var matchedItems = [];
+        for (var i = 0, il = self.items.length; i < il; i++) {
+            var item = self.items[i];
+            if (item.values()[valueName] == value) {
+                matchedItems.push(item);
+            }
+        }
+        return matchedItems;
+    };
+
+    /*
+    * Get size of the list
+    */
+    this.size = function() {
+        return self.items.length;
+    };
+
+    /*
+    * Removes all items from the list
+    */
+    this.clear = function() {
+        self.templater.clear();
+        self.items = [];
+        return self;
+    };
+
+    this.on = function(event, callback) {
+        self.handlers[event].push(callback);
+        return self;
+    };
+
+    this.off = function(event, callback) {
+        var e = self.handlers[event];
+        var index = indexOf(e, callback);
+        if (index > -1) {
+            e.splice(index, 1);
+        }
+        return self;
+    };
+
+    this.trigger = function(event) {
+        var i = self.handlers[event].length;
+        while(i--) {
+            self.handlers[event][i](self);
+        }
+        return self;
+    };
+
+    this.reset = {
+        filter: function() {
+            var is = self.items,
+                il = is.length;
+            while (il--) {
+                is[il].filtered = false;
+            }
+            return self;
+        },
+        search: function() {
+            var is = self.items,
+                il = is.length;
+            while (il--) {
+                is[il].found = false;
+            }
+            return self;
+        }
+    };
+
+    this.update = function() {
+        var is = self.items,
+      il = is.length;
+
+        self.visibleItems = [];
+        self.matchingItems = [];
+        self.templater.clear();
+        for (var i = 0; i < il; i++) {
+            if (is[i].matching() && ((self.matchingItems.length+1) >= self.i && self.visibleItems.length < self.page)) {
+                is[i].show();
+                self.visibleItems.push(is[i]);
+                self.matchingItems.push(is[i]);
+      } else if (is[i].matching()) {
+                self.matchingItems.push(is[i]);
+                is[i].hide();
+      } else {
+                is[i].hide();
+      }
+        }
+        self.trigger('updated');
+        return self;
+    };
+
+    init.start();
+};
+
+module.exports = List;
+
+})(window);
+
+});
+require.register("list.js/src/search.js", function(exports, require, module){
+var events = require('events'),
+    getByClass = require('get-by-class'),
+    toString = require('to-string');
+
+module.exports = function(list) {
+    var item,
+        text,
+        columns,
+        searchString,
+        customSearch;
+
+    var prepare = {
+        resetList: function() {
+            list.i = 1;
+            list.templater.clear();
+            customSearch = undefined;
+        },
+        setOptions: function(args) {
+            if (args.length == 2 && args[1] instanceof Array) {
+                columns = args[1];
+            } else if (args.length == 2 && typeof(args[1]) == "function") {
+                customSearch = args[1];
+            } else if (args.length == 3) {
+                columns = args[1];
+                customSearch = args[2];
+            }
+        },
+        setColumns: function() {
+            columns = (columns === undefined) ? prepare.toArray(list.items[0].values()) : columns;
+        },
+        setSearchString: function(s) {
+            s = toString(s).toLowerCase();
+            s = s.replace(/[-[\]{}()*+?.,\\^$|#]/g, "\\$&"); // Escape regular expression characters
+            searchString = s;
+        },
+        toArray: function(values) {
+            var tmpColumn = [];
+            for (var name in values) {
+                tmpColumn.push(name);
+            }
+            return tmpColumn;
+        }
+    };
+    var search = {
+        list: function() {
+            for (var k = 0, kl = list.items.length; k < kl; k++) {
+                search.item(list.items[k]);
+            }
+        },
+        item: function(item) {
+            item.found = false;
+            for (var j = 0, jl = columns.length; j < jl; j++) {
+                if (search.values(item.values(), columns[j])) {
+                    item.found = true;
+                    return;
+                }
+            }
+        },
+        values: function(values, column) {
+            if (values.hasOwnProperty(column)) {
+                text = toString(values[column]).toLowerCase();
+                if ((searchString !== "") && (text.search(searchString) > -1)) {
+                    return true;
+                }
+            }
+            return false;
+        },
+        reset: function() {
+            list.reset.search();
+            list.searched = false;
+        }
+    };
+
+    var searchMethod = function(str) {
+        list.trigger('searchStart');
+
+        prepare.resetList();
+        prepare.setSearchString(str);
+        prepare.setOptions(arguments); // str, cols|searchFunction, searchFunction
+        prepare.setColumns();
+
+        if (searchString === "" ) {
+            search.reset();
+        } else {
+            list.searched = true;
+            if (customSearch) {
+                customSearch(searchString, columns);
+            } else {
+                search.list();
+            }
+        }
+
+        list.update();
+        list.trigger('searchComplete');
+        return list.visibleItems;
+    };
+
+    list.handlers.searchStart = list.handlers.searchStart || [];
+    list.handlers.searchComplete = list.handlers.searchComplete || [];
+
+    events.bind(getByClass(list.listContainer, list.searchClass), 'keyup', function(e) {
+        var target = e.target || e.srcElement, // IE have srcElement
+            alreadyCleared = (target.value === "" && !list.searched);
+        if (!alreadyCleared) { // If oninput already have resetted the list, do nothing
+            searchMethod(target.value);
+        }
+    });
+
+    // Used to detect click on HTML5 clear button
+    events.bind(getByClass(list.listContainer, list.searchClass), 'input', function(e) {
+        var target = e.target || e.srcElement;
+        if (target.value === "") {
+            searchMethod('');
+        }
+    });
+
+    list.helpers.toString = toString;
+    return searchMethod;
+};
+
+});
+require.register("list.js/src/sort.js", function(exports, require, module){
+var naturalSort = require('natural-sort'),
+    classes = require('classes'),
+    events = require('events'),
+    getByClass = require('get-by-class'),
+    getAttribute = require('get-attribute');
+
+module.exports = function(list) {
+    list.sortFunction = list.sortFunction || function(itemA, itemB, options) {
+        options.desc = options.order == "desc" ? true : false; // Natural sort uses this format
+        return naturalSort(itemA.values()[options.valueName], itemB.values()[options.valueName], options);
+    };
+
+    var buttons = {
+        els: undefined,
+        clear: function() {
+            for (var i = 0, il = buttons.els.length; i < il; i++) {
+                classes(buttons.els[i]).remove('asc');
+                classes(buttons.els[i]).remove('desc');
+            }
+        },
+        getOrder: function(btn) {
+            var predefinedOrder = getAttribute(btn, 'data-order');
+            if (predefinedOrder == "asc" || predefinedOrder == "desc") {
+                return predefinedOrder;
+            } else if (classes(btn).has('desc')) {
+                return "asc";
+            } else if (classes(btn).has('asc')) {
+                return "desc";
+            } else {
+                return "asc";
+            }
+        },
+        getInSensitive: function(btn, options) {
+            var insensitive = getAttribute(btn, 'data-insensitive');
+            if (insensitive === "true") {
+                options.insensitive = true;
+            } else {
+                options.insensitive = false;
+            }
+        },
+        setOrder: function(options) {
+            for (var i = 0, il = buttons.els.length; i < il; i++) {
+                var btn = buttons.els[i];
+                if (getAttribute(btn, 'data-sort') !== options.valueName) {
+                    continue;
+                }
+                var predefinedOrder = getAttribute(btn, 'data-order');
+                if (predefinedOrder == "asc" || predefinedOrder == "desc") {
+                    if (predefinedOrder == options.order) {
+                        classes(btn).add(options.order);
+                    }
+                } else {
+                    classes(btn).add(options.order);
+                }
+            }
+        }
+    };
+    var sort = function() {
+        list.trigger('sortStart');
+        options = {};
+
+        var target = arguments[0].currentTarget || arguments[0].srcElement || undefined;
+
+        if (target) {
+            options.valueName = getAttribute(target, 'data-sort');
+            buttons.getInSensitive(target, options);
+            options.order = buttons.getOrder(target);
+        } else {
+            options = arguments[1] || options;
+            options.valueName = arguments[0];
+            options.order = options.order || "asc";
+            options.insensitive = (typeof options.insensitive == "undefined") ? true : options.insensitive;
+        }
+        buttons.clear();
+        buttons.setOrder(options);
+
+        options.sortFunction = options.sortFunction || list.sortFunction;
+        list.items.sort(function(a, b) {
+            return options.sortFunction(a, b, options);
+        });
+        list.update();
+        list.trigger('sortComplete');
+    };
+
+    // Add handlers
+    list.handlers.sortStart = list.handlers.sortStart || [];
+    list.handlers.sortComplete = list.handlers.sortComplete || [];
+
+    buttons.els = getByClass(list.listContainer, list.sortClass);
+    events.bind(buttons.els, 'click', sort);
+    list.on('searchStart', buttons.clear);
+    list.on('filterStart', buttons.clear);
+
+    // Helpers
+    list.helpers.classes = classes;
+    list.helpers.naturalSort = naturalSort;
+    list.helpers.events = events;
+    list.helpers.getAttribute = getAttribute;
+
+    return sort;
+};
+
+});
+require.register("list.js/src/item.js", function(exports, require, module){
+module.exports = function(list) {
+    return function(initValues, element, notCreate) {
+        var item = this;
+
+        this._values = {};
+
+        this.found = false; // Show if list.searched == true and this.found == true
+        this.filtered = false;// Show if list.filtered == true and this.filtered == true
+
+        var init = function(initValues, element, notCreate) {
+            if (element === undefined) {
+                if (notCreate) {
+                    item.values(initValues, notCreate);
+                } else {
+                    item.values(initValues);
+                }
+            } else {
+                item.elm = element;
+                var values = list.templater.get(item, initValues);
+                item.values(values);
+            }
+        };
+        this.values = function(newValues, notCreate) {
+            if (newValues !== undefined) {
+                for(var name in newValues) {
+                    item._values[name] = newValues[name];
+                }
+                if (notCreate !== true) {
+                    list.templater.set(item, item.values());
+                }
+            } else {
+                return item._values;
+            }
+        };
+        this.show = function() {
+            list.templater.show(item);
+        };
+        this.hide = function() {
+            list.templater.hide(item);
+        };
+        this.matching = function() {
+            return (
+                (list.filtered && list.searched && item.found && item.filtered) ||
+                (list.filtered && !list.searched && item.filtered) ||
+                (!list.filtered && list.searched && item.found) ||
+                (!list.filtered && !list.searched)
+            );
+        };
+        this.visible = function() {
+            return (item.elm.parentNode == list.list) ? true : false;
+        };
+        init(initValues, element, notCreate);
+    };
+};
+
+});
+require.register("list.js/src/templater.js", function(exports, require, module){
+var getByClass = require('get-by-class');
+
+var Templater = function(list) {
+    var itemSource = getItemSource(list.item),
+        templater = this;
+
+    function getItemSource(item) {
+        if (item === undefined) {
+            var nodes = list.list.childNodes,
+                items = [];
+
+            for (var i = 0, il = nodes.length; i < il; i++) {
+                // Only textnodes have a data attribute
+                if (nodes[i].data === undefined) {
+                    return nodes[i];
+                }
+            }
+            return null;
+        } else if (item.indexOf("<") !== -1) { // Try create html element of list, do not work for tables!!
+            var div = document.createElement('div');
+            div.innerHTML = item;
+            return div.firstChild;
+        } else {
+            return document.getElementById(list.item);
+        }
+    }
+
+    /* Get values from element */
+    this.get = function(item, valueNames) {
+        templater.create(item);
+        var values = {};
+        for(var i = 0, il = valueNames.length; i < il; i++) {
+            var elm = getByClass(item.elm, valueNames[i], true);
+            values[valueNames[i]] = elm ? elm.innerHTML : "";
+        }
+        return values;
+    };
+
+    /* Sets values at element */
+    this.set = function(item, values) {
+        if (!templater.create(item)) {
+            for(var v in values) {
+                if (values.hasOwnProperty(v)) {
+                    // TODO speed up if possible
+                    var elm = getByClass(item.elm, v, true);
+                    if (elm) {
+                        /* src attribute for image tag & text for other tags */
+                        if (elm.tagName === "IMG" && values[v] !== "") {
+                            elm.src = values[v];
+                        } else {
+                            elm.innerHTML = values[v];
+                        }
+                    }
+                }
+            }
+        }
+    };
+
+    this.create = function(item) {
+        if (item.elm !== undefined) {
+            return false;
+        }
+        /* If item source does not exists, use the first item in list as
+        source for new items */
+        var newItem = itemSource.cloneNode(true);
+        newItem.removeAttribute('id');
+        item.elm = newItem;
+        templater.set(item, item.values());
+        return true;
+    };
+    this.remove = function(item) {
+        list.list.removeChild(item.elm);
+    };
+    this.show = function(item) {
+        templater.create(item);
+        list.list.appendChild(item.elm);
+    };
+    this.hide = function(item) {
+        if (item.elm !== undefined && item.elm.parentNode === list.list) {
+            list.list.removeChild(item.elm);
+        }
+    };
+    this.clear = function() {
+        /* .innerHTML = ''; fucks up IE */
+        if (list.list.hasChildNodes()) {
+            while (list.list.childNodes.length >= 1)
+            {
+                list.list.removeChild(list.list.firstChild);
+            }
+        }
+    };
+};
+
+module.exports = function(list) {
+    return new Templater(list);
+};
+
+});
+require.register("list.js/src/filter.js", function(exports, require, module){
+module.exports = function(list) {
+
+    // Add handlers
+    list.handlers.filterStart = list.handlers.filterStart || [];
+    list.handlers.filterComplete = list.handlers.filterComplete || [];
+
+    return function(filterFunction) {
+        list.trigger('filterStart');
+        list.i = 1; // Reset paging
+        list.reset.filter();
+        if (filterFunction === undefined) {
+            list.filtered = false;
+        } else {
+            list.filtered = true;
+            var is = list.items;
+            for (var i = 0, il = is.length; i < il; i++) {
+                var item = is[i];
+                if (filterFunction(item)) {
+                    item.filtered = true;
+                } else {
+                    item.filtered = false;
+                }
+            }
+        }
+        list.update();
+        list.trigger('filterComplete');
+        return list.visibleItems;
+    };
+};
+
+});
+require.register("list.js/src/add-async.js", function(exports, require, module){
+module.exports = function(list) {
+    return function(values, callback, items) {
+        var valuesToAdd = values.splice(0, 100);
+        items = items || [];
+        items = items.concat(list.add(valuesToAdd));
+        if (values.length > 0) {
+            setTimeout(function() {
+                addAsync(values, callback, items);
+            }, 10);
+        } else {
+            list.update();
+            callback(items);
+        }
+    };
+};
+});
+require.register("list.js/src/parse.js", function(exports, require, module){
+module.exports = function(list) {
+
+    var Item = require('./item')(list);
+
+    var getChildren = function(parent) {
+        var nodes = parent.childNodes,
+            items = [];
+        for (var i = 0, il = nodes.length; i < il; i++) {
+            // Only textnodes have a data attribute
+            if (nodes[i].data === undefined) {
+                items.push(nodes[i]);
+            }
+        }
+        return items;
+    };
+
+    var parse = function(itemElements, valueNames) {
+        for (var i = 0, il = itemElements.length; i < il; i++) {
+            list.items.push(new Item(valueNames, itemElements[i]));
+        }
+    };
+    var parseAsync = function(itemElements, valueNames) {
+        var itemsToIndex = itemElements.splice(0, 100); // TODO: If < 100 items, what happens in IE etc?
+        parse(itemsToIndex, valueNames);
+        if (itemElements.length > 0) {
+            setTimeout(function() {
+                init.items.indexAsync(itemElements, valueNames);
+            }, 10);
+        } else {
+            list.update();
+            // TODO: Add indexed callback
+        }
+    };
+
+    return function() {
+        var itemsToIndex = getChildren(list.list),
+            valueNames = list.valueNames;
+
+        if (list.indexAsync) {
+            parseAsync(itemsToIndex, valueNames);
+        } else {
+            parse(itemsToIndex, valueNames);
+        }
+    };
+};
+
+});
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+require.alias("component-classes/index.js", "list.js/deps/classes/index.js");
+require.alias("component-classes/index.js", "classes/index.js");
+require.alias("component-indexof/index.js", "component-classes/deps/indexof/index.js");
+
+require.alias("segmentio-extend/index.js", "list.js/deps/extend/index.js");
+require.alias("segmentio-extend/index.js", "extend/index.js");
+
+require.alias("component-indexof/index.js", "list.js/deps/indexof/index.js");
+require.alias("component-indexof/index.js", "indexof/index.js");
+
+require.alias("javve-events/index.js", "list.js/deps/events/index.js");
+require.alias("javve-events/index.js", "events/index.js");
+require.alias("component-event/index.js", "javve-events/deps/event/index.js");
+
+require.alias("timoxley-to-array/index.js", "javve-events/deps/to-array/index.js");
+
+require.alias("javve-get-by-class/index.js", "list.js/deps/get-by-class/index.js");
+require.alias("javve-get-by-class/index.js", "get-by-class/index.js");
+
+require.alias("javve-get-attribute/index.js", "list.js/deps/get-attribute/index.js");
+require.alias("javve-get-attribute/index.js", "get-attribute/index.js");
+
+require.alias("javve-natural-sort/index.js", "list.js/deps/natural-sort/index.js");
+require.alias("javve-natural-sort/index.js", "natural-sort/index.js");
+
+require.alias("javve-to-string/index.js", "list.js/deps/to-string/index.js");
+require.alias("javve-to-string/index.js", "list.js/deps/to-string/index.js");
+require.alias("javve-to-string/index.js", "to-string/index.js");
+require.alias("javve-to-string/index.js", "javve-to-string/index.js");
+require.alias("component-type/index.js", "list.js/deps/type/index.js");
+require.alias("component-type/index.js", "type/index.js");
+if (typeof exports == "object") {
+  module.exports = require("list.js");
+} else if (typeof define == "function" && define.amd) {
+  define(function(){ return require("list.js"); });
+} else {
+  this["List"] = require("list.js");
+}})();

+ 2400 - 0
app/assets/javascripts/blazer/moment.js

@@ -0,0 +1,2400 @@
+//! moment.js
+//! version : 2.5.1
+//! authors : Tim Wood, Iskren Chernev, Moment.js contributors
+//! license : MIT
+//! momentjs.com
+
+(function (undefined) {
+
+    /************************************
+        Constants
+    ************************************/
+
+    var moment,
+        VERSION = "2.5.1",
+        global = this,
+        round = Math.round,
+        i,
+
+        YEAR = 0,
+        MONTH = 1,
+        DATE = 2,
+        HOUR = 3,
+        MINUTE = 4,
+        SECOND = 5,
+        MILLISECOND = 6,
+
+        // internal storage for language config files
+        languages = {},
+
+        // moment internal properties
+        momentProperties = {
+            _isAMomentObject: null,
+            _i : null,
+            _f : null,
+            _l : null,
+            _strict : null,
+            _isUTC : null,
+            _offset : null,  // optional. Combine with _isUTC
+            _pf : null,
+            _lang : null  // optional
+        },
+
+        // check for nodeJS
+        hasModule = (typeof module !== 'undefined' && module.exports && typeof require !== 'undefined'),
+
+        // ASP.NET json date format regex
+        aspNetJsonRegex = /^\/?Date\((\-?\d+)/i,
+        aspNetTimeSpanJsonRegex = /(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,
+
+        // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html
+        // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere
+        isoDurationRegex = /^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/,
+
+        // format tokens
+        formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|X|zz?|ZZ?|.)/g,
+        localFormattingTokens = /(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,
+
+        // parsing token regexes
+        parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99
+        parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999
+        parseTokenOneToFourDigits = /\d{1,4}/, // 0 - 9999
+        parseTokenOneToSixDigits = /[+\-]?\d{1,6}/, // -999,999 - 999,999
+        parseTokenDigits = /\d+/, // nonzero number of digits
+        parseTokenWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i, // any word (or two) characters or numbers including two/three word month in arabic.
+        parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/gi, // +00:00 -00:00 +0000 -0000 or Z
+        parseTokenT = /T/i, // T (ISO separator)
+        parseTokenTimestampMs = /[\+\-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123
+
+        //strict parsing regexes
+        parseTokenOneDigit = /\d/, // 0 - 9
+        parseTokenTwoDigits = /\d\d/, // 00 - 99
+        parseTokenThreeDigits = /\d{3}/, // 000 - 999
+        parseTokenFourDigits = /\d{4}/, // 0000 - 9999
+        parseTokenSixDigits = /[+-]?\d{6}/, // -999,999 - 999,999
+        parseTokenSignedNumber = /[+-]?\d+/, // -inf - inf
+
+        // iso 8601 regex
+        // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00)
+        isoRegex = /^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,
+
+        isoFormat = 'YYYY-MM-DDTHH:mm:ssZ',
+
+        isoDates = [
+            ['YYYYYY-MM-DD', /[+-]\d{6}-\d{2}-\d{2}/],
+            ['YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/],
+            ['GGGG-[W]WW-E', /\d{4}-W\d{2}-\d/],
+            ['GGGG-[W]WW', /\d{4}-W\d{2}/],
+            ['YYYY-DDD', /\d{4}-\d{3}/]
+        ],
+
+        // iso time formats and regexes
+        isoTimes = [
+            ['HH:mm:ss.SSSS', /(T| )\d\d:\d\d:\d\d\.\d{1,3}/],
+            ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/],
+            ['HH:mm', /(T| )\d\d:\d\d/],
+            ['HH', /(T| )\d\d/]
+        ],
+
+        // timezone chunker "+10:00" > ["10", "00"] or "-1530" > ["-15", "30"]
+        parseTimezoneChunker = /([\+\-]|\d\d)/gi,
+
+        // getter and setter names
+        proxyGettersAndSetters = 'Date|Hours|Minutes|Seconds|Milliseconds'.split('|'),
+        unitMillisecondFactors = {
+            'Milliseconds' : 1,
+            'Seconds' : 1e3,
+            'Minutes' : 6e4,
+            'Hours' : 36e5,
+            'Days' : 864e5,
+            'Months' : 2592e6,
+            'Years' : 31536e6
+        },
+
+        unitAliases = {
+            ms : 'millisecond',
+            s : 'second',
+            m : 'minute',
+            h : 'hour',
+            d : 'day',
+            D : 'date',
+            w : 'week',
+            W : 'isoWeek',
+            M : 'month',
+            y : 'year',
+            DDD : 'dayOfYear',
+            e : 'weekday',
+            E : 'isoWeekday',
+            gg: 'weekYear',
+            GG: 'isoWeekYear'
+        },
+
+        camelFunctions = {
+            dayofyear : 'dayOfYear',
+            isoweekday : 'isoWeekday',
+            isoweek : 'isoWeek',
+            weekyear : 'weekYear',
+            isoweekyear : 'isoWeekYear'
+        },
+
+        // format function strings
+        formatFunctions = {},
+
+        // tokens to ordinalize and pad
+        ordinalizeTokens = 'DDD w W M D d'.split(' '),
+        paddedTokens = 'M D H h m s w W'.split(' '),
+
+        formatTokenFunctions = {
+            M    : function () {
+                return this.month() + 1;
+            },
+            MMM  : function (format) {
+                return this.lang().monthsShort(this, format);
+            },
+            MMMM : function (format) {
+                return this.lang().months(this, format);
+            },
+            D    : function () {
+                return this.date();
+            },
+            DDD  : function () {
+                return this.dayOfYear();
+            },
+            d    : function () {
+                return this.day();
+            },
+            dd   : function (format) {
+                return this.lang().weekdaysMin(this, format);
+            },
+            ddd  : function (format) {
+                return this.lang().weekdaysShort(this, format);
+            },
+            dddd : function (format) {
+                return this.lang().weekdays(this, format);
+            },
+            w    : function () {
+                return this.week();
+            },
+            W    : function () {
+                return this.isoWeek();
+            },
+            YY   : function () {
+                return leftZeroFill(this.year() % 100, 2);
+            },
+            YYYY : function () {
+                return leftZeroFill(this.year(), 4);
+            },
+            YYYYY : function () {
+                return leftZeroFill(this.year(), 5);
+            },
+            YYYYYY : function () {
+                var y = this.year(), sign = y >= 0 ? '+' : '-';
+                return sign + leftZeroFill(Math.abs(y), 6);
+            },
+            gg   : function () {
+                return leftZeroFill(this.weekYear() % 100, 2);
+            },
+            gggg : function () {
+                return leftZeroFill(this.weekYear(), 4);
+            },
+            ggggg : function () {
+                return leftZeroFill(this.weekYear(), 5);
+            },
+            GG   : function () {
+                return leftZeroFill(this.isoWeekYear() % 100, 2);
+            },
+            GGGG : function () {
+                return leftZeroFill(this.isoWeekYear(), 4);
+            },
+            GGGGG : function () {
+                return leftZeroFill(this.isoWeekYear(), 5);
+            },
+            e : function () {
+                return this.weekday();
+            },
+            E : function () {
+                return this.isoWeekday();
+            },
+            a    : function () {
+                return this.lang().meridiem(this.hours(), this.minutes(), true);
+            },
+            A    : function () {
+                return this.lang().meridiem(this.hours(), this.minutes(), false);
+            },
+            H    : function () {
+                return this.hours();
+            },
+            h    : function () {
+                return this.hours() % 12 || 12;
+            },
+            m    : function () {
+                return this.minutes();
+            },
+            s    : function () {
+                return this.seconds();
+            },
+            S    : function () {
+                return toInt(this.milliseconds() / 100);
+            },
+            SS   : function () {
+                return leftZeroFill(toInt(this.milliseconds() / 10), 2);
+            },
+            SSS  : function () {
+                return leftZeroFill(this.milliseconds(), 3);
+            },
+            SSSS : function () {
+                return leftZeroFill(this.milliseconds(), 3);
+            },
+            Z    : function () {
+                var a = -this.zone(),
+                    b = "+";
+                if (a < 0) {
+                    a = -a;
+                    b = "-";
+                }
+                return b + leftZeroFill(toInt(a / 60), 2) + ":" + leftZeroFill(toInt(a) % 60, 2);
+            },
+            ZZ   : function () {
+                var a = -this.zone(),
+                    b = "+";
+                if (a < 0) {
+                    a = -a;
+                    b = "-";
+                }
+                return b + leftZeroFill(toInt(a / 60), 2) + leftZeroFill(toInt(a) % 60, 2);
+            },
+            z : function () {
+                return this.zoneAbbr();
+            },
+            zz : function () {
+                return this.zoneName();
+            },
+            X    : function () {
+                return this.unix();
+            },
+            Q : function () {
+                return this.quarter();
+            }
+        },
+
+        lists = ['months', 'monthsShort', 'weekdays', 'weekdaysShort', 'weekdaysMin'];
+
+    function defaultParsingFlags() {
+        // We need to deep clone this object, and es5 standard is not very
+        // helpful.
+        return {
+            empty : false,
+            unusedTokens : [],
+            unusedInput : [],
+            overflow : -2,
+            charsLeftOver : 0,
+            nullInput : false,
+            invalidMonth : null,
+            invalidFormat : false,
+            userInvalidated : false,
+            iso: false
+        };
+    }
+
+    function padToken(func, count) {
+        return function (a) {
+            return leftZeroFill(func.call(this, a), count);
+        };
+    }
+    function ordinalizeToken(func, period) {
+        return function (a) {
+            return this.lang().ordinal(func.call(this, a), period);
+        };
+    }
+
+    while (ordinalizeTokens.length) {
+        i = ordinalizeTokens.pop();
+        formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i], i);
+    }
+    while (paddedTokens.length) {
+        i = paddedTokens.pop();
+        formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2);
+    }
+    formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3);
+
+
+    /************************************
+        Constructors
+    ************************************/
+
+    function Language() {
+
+    }
+
+    // Moment prototype object
+    function Moment(config) {
+        checkOverflow(config);
+        extend(this, config);
+    }
+
+    // Duration Constructor
+    function Duration(duration) {
+        var normalizedInput = normalizeObjectUnits(duration),
+            years = normalizedInput.year || 0,
+            months = normalizedInput.month || 0,
+            weeks = normalizedInput.week || 0,
+            days = normalizedInput.day || 0,
+            hours = normalizedInput.hour || 0,
+            minutes = normalizedInput.minute || 0,
+            seconds = normalizedInput.second || 0,
+            milliseconds = normalizedInput.millisecond || 0;
+
+        // representation for dateAddRemove
+        this._milliseconds = +milliseconds +
+            seconds * 1e3 + // 1000
+            minutes * 6e4 + // 1000 * 60
+            hours * 36e5; // 1000 * 60 * 60
+        // Because of dateAddRemove treats 24 hours as different from a
+        // day when working around DST, we need to store them separately
+        this._days = +days +
+            weeks * 7;
+        // It is impossible translate months into days without knowing
+        // which months you are are talking about, so we have to store
+        // it separately.
+        this._months = +months +
+            years * 12;
+
+        this._data = {};
+
+        this._bubble();
+    }
+
+    /************************************
+        Helpers
+    ************************************/
+
+
+    function extend(a, b) {
+        for (var i in b) {
+            if (b.hasOwnProperty(i)) {
+                a[i] = b[i];
+            }
+        }
+
+        if (b.hasOwnProperty("toString")) {
+            a.toString = b.toString;
+        }
+
+        if (b.hasOwnProperty("valueOf")) {
+            a.valueOf = b.valueOf;
+        }
+
+        return a;
+    }
+
+    function cloneMoment(m) {
+        var result = {}, i;
+        for (i in m) {
+            if (m.hasOwnProperty(i) && momentProperties.hasOwnProperty(i)) {
+                result[i] = m[i];
+            }
+        }
+
+        return result;
+    }
+
+    function absRound(number) {
+        if (number < 0) {
+            return Math.ceil(number);
+        } else {
+            return Math.floor(number);
+        }
+    }
+
+    // left zero fill a number
+    // see http://jsperf.com/left-zero-filling for performance comparison
+    function leftZeroFill(number, targetLength, forceSign) {
+        var output = '' + Math.abs(number),
+            sign = number >= 0;
+
+        while (output.length < targetLength) {
+            output = '0' + output;
+        }
+        return (sign ? (forceSign ? '+' : '') : '-') + output;
+    }
+
+    // helper function for _.addTime and _.subtractTime
+    function addOrSubtractDurationFromMoment(mom, duration, isAdding, ignoreUpdateOffset) {
+        var milliseconds = duration._milliseconds,
+            days = duration._days,
+            months = duration._months,
+            minutes,
+            hours;
+
+        if (milliseconds) {
+            mom._d.setTime(+mom._d + milliseconds * isAdding);
+        }
+        // store the minutes and hours so we can restore them
+        if (days || months) {
+            minutes = mom.minute();
+            hours = mom.hour();
+        }
+        if (days) {
+            mom.date(mom.date() + days * isAdding);
+        }
+        if (months) {
+            mom.month(mom.month() + months * isAdding);
+        }
+        if (milliseconds && !ignoreUpdateOffset) {
+            moment.updateOffset(mom);
+        }
+        // restore the minutes and hours after possibly changing dst
+        if (days || months) {
+            mom.minute(minutes);
+            mom.hour(hours);
+        }
+    }
+
+    // check if is an array
+    function isArray(input) {
+        return Object.prototype.toString.call(input) === '[object Array]';
+    }
+
+    function isDate(input) {
+        return  Object.prototype.toString.call(input) === '[object Date]' ||
+                input instanceof Date;
+    }
+
+    // compare two arrays, return the number of differences
+    function compareArrays(array1, array2, dontConvert) {
+        var len = Math.min(array1.length, array2.length),
+            lengthDiff = Math.abs(array1.length - array2.length),
+            diffs = 0,
+            i;
+        for (i = 0; i < len; i++) {
+            if ((dontConvert && array1[i] !== array2[i]) ||
+                (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) {
+                diffs++;
+            }
+        }
+        return diffs + lengthDiff;
+    }
+
+    function normalizeUnits(units) {
+        if (units) {
+            var lowered = units.toLowerCase().replace(/(.)s$/, '$1');
+            units = unitAliases[units] || camelFunctions[lowered] || lowered;
+        }
+        return units;
+    }
+
+    function normalizeObjectUnits(inputObject) {
+        var normalizedInput = {},
+            normalizedProp,
+            prop;
+
+        for (prop in inputObject) {
+            if (inputObject.hasOwnProperty(prop)) {
+                normalizedProp = normalizeUnits(prop);
+                if (normalizedProp) {
+                    normalizedInput[normalizedProp] = inputObject[prop];
+                }
+            }
+        }
+
+        return normalizedInput;
+    }
+
+    function makeList(field) {
+        var count, setter;
+
+        if (field.indexOf('week') === 0) {
+            count = 7;
+            setter = 'day';
+        }
+        else if (field.indexOf('month') === 0) {
+            count = 12;
+            setter = 'month';
+        }
+        else {
+            return;
+        }
+
+        moment[field] = function (format, index) {
+            var i, getter,
+                method = moment.fn._lang[field],
+                results = [];
+
+            if (typeof format === 'number') {
+                index = format;
+                format = undefined;
+            }
+
+            getter = function (i) {
+                var m = moment().utc().set(setter, i);
+                return method.call(moment.fn._lang, m, format || '');
+            };
+
+            if (index != null) {
+                return getter(index);
+            }
+            else {
+                for (i = 0; i < count; i++) {
+                    results.push(getter(i));
+                }
+                return results;
+            }
+        };
+    }
+
+    function toInt(argumentForCoercion) {
+        var coercedNumber = +argumentForCoercion,
+            value = 0;
+
+        if (coercedNumber !== 0 && isFinite(coercedNumber)) {
+            if (coercedNumber >= 0) {
+                value = Math.floor(coercedNumber);
+            } else {
+                value = Math.ceil(coercedNumber);
+            }
+        }
+
+        return value;
+    }
+
+    function daysInMonth(year, month) {
+        return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
+    }
+
+    function daysInYear(year) {
+        return isLeapYear(year) ? 366 : 365;
+    }
+
+    function isLeapYear(year) {
+        return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
+    }
+
+    function checkOverflow(m) {
+        var overflow;
+        if (m._a && m._pf.overflow === -2) {
+            overflow =
+                m._a[MONTH] < 0 || m._a[MONTH] > 11 ? MONTH :
+                m._a[DATE] < 1 || m._a[DATE] > daysInMonth(m._a[YEAR], m._a[MONTH]) ? DATE :
+                m._a[HOUR] < 0 || m._a[HOUR] > 23 ? HOUR :
+                m._a[MINUTE] < 0 || m._a[MINUTE] > 59 ? MINUTE :
+                m._a[SECOND] < 0 || m._a[SECOND] > 59 ? SECOND :
+                m._a[MILLISECOND] < 0 || m._a[MILLISECOND] > 999 ? MILLISECOND :
+                -1;
+
+            if (m._pf._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) {
+                overflow = DATE;
+            }
+
+            m._pf.overflow = overflow;
+        }
+    }
+
+    function isValid(m) {
+        if (m._isValid == null) {
+            m._isValid = !isNaN(m._d.getTime()) &&
+                m._pf.overflow < 0 &&
+                !m._pf.empty &&
+                !m._pf.invalidMonth &&
+                !m._pf.nullInput &&
+                !m._pf.invalidFormat &&
+                !m._pf.userInvalidated;
+
+            if (m._strict) {
+                m._isValid = m._isValid &&
+                    m._pf.charsLeftOver === 0 &&
+                    m._pf.unusedTokens.length === 0;
+            }
+        }
+        return m._isValid;
+    }
+
+    function normalizeLanguage(key) {
+        return key ? key.toLowerCase().replace('_', '-') : key;
+    }
+
+    // Return a moment from input, that is local/utc/zone equivalent to model.
+    function makeAs(input, model) {
+        return model._isUTC ? moment(input).zone(model._offset || 0) :
+            moment(input).local();
+    }
+
+    /************************************
+        Languages
+    ************************************/
+
+
+    extend(Language.prototype, {
+
+        set : function (config) {
+            var prop, i;
+            for (i in config) {
+                prop = config[i];
+                if (typeof prop === 'function') {
+                    this[i] = prop;
+                } else {
+                    this['_' + i] = prop;
+                }
+            }
+        },
+
+        _months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"),
+        months : function (m) {
+            return this._months[m.month()];
+        },
+
+        _monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),
+        monthsShort : function (m) {
+            return this._monthsShort[m.month()];
+        },
+
+        monthsParse : function (monthName) {
+            var i, mom, regex;
+
+            if (!this._monthsParse) {
+                this._monthsParse = [];
+            }
+
+            for (i = 0; i < 12; i++) {
+                // make the regex if we don't have it already
+                if (!this._monthsParse[i]) {
+                    mom = moment.utc([2000, i]);
+                    regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
+                    this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
+                }
+                // test the regex
+                if (this._monthsParse[i].test(monthName)) {
+                    return i;
+                }
+            }
+        },
+
+        _weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),
+        weekdays : function (m) {
+            return this._weekdays[m.day()];
+        },
+
+        _weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),
+        weekdaysShort : function (m) {
+            return this._weekdaysShort[m.day()];
+        },
+
+        _weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"),
+        weekdaysMin : function (m) {
+            return this._weekdaysMin[m.day()];
+        },
+
+        weekdaysParse : function (weekdayName) {
+            var i, mom, regex;
+
+            if (!this._weekdaysParse) {
+                this._weekdaysParse = [];
+            }
+
+            for (i = 0; i < 7; i++) {
+                // make the regex if we don't have it already
+                if (!this._weekdaysParse[i]) {
+                    mom = moment([2000, 1]).day(i);
+                    regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, '');
+                    this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
+                }
+                // test the regex
+                if (this._weekdaysParse[i].test(weekdayName)) {
+                    return i;
+                }
+            }
+        },
+
+        _longDateFormat : {
+            LT : "h:mm A",
+            L : "MM/DD/YYYY",
+            LL : "MMMM D YYYY",
+            LLL : "MMMM D YYYY LT",
+            LLLL : "dddd, MMMM D YYYY LT"
+        },
+        longDateFormat : function (key) {
+            var output = this._longDateFormat[key];
+            if (!output && this._longDateFormat[key.toUpperCase()]) {
+                output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) {
+                    return val.slice(1);
+                });
+                this._longDateFormat[key] = output;
+            }
+            return output;
+        },
+
+        isPM : function (input) {
+            // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays
+            // Using charAt should be more compatible.
+            return ((input + '').toLowerCase().charAt(0) === 'p');
+        },
+
+        _meridiemParse : /[ap]\.?m?\.?/i,
+        meridiem : function (hours, minutes, isLower) {
+            if (hours > 11) {
+                return isLower ? 'pm' : 'PM';
+            } else {
+                return isLower ? 'am' : 'AM';
+            }
+        },
+
+        _calendar : {
+            sameDay : '[Today at] LT',
+            nextDay : '[Tomorrow at] LT',
+            nextWeek : 'dddd [at] LT',
+            lastDay : '[Yesterday at] LT',
+            lastWeek : '[Last] dddd [at] LT',
+            sameElse : 'L'
+        },
+        calendar : function (key, mom) {
+            var output = this._calendar[key];
+            return typeof output === 'function' ? output.apply(mom) : output;
+        },
+
+        _relativeTime : {
+            future : "in %s",
+            past : "%s ago",
+            s : "a few seconds",
+            m : "a minute",
+            mm : "%d minutes",
+            h : "an hour",
+            hh : "%d hours",
+            d : "a day",
+            dd : "%d days",
+            M : "a month",
+            MM : "%d months",
+            y : "a year",
+            yy : "%d years"
+        },
+        relativeTime : function (number, withoutSuffix, string, isFuture) {
+            var output = this._relativeTime[string];
+            return (typeof output === 'function') ?
+                output(number, withoutSuffix, string, isFuture) :
+                output.replace(/%d/i, number);
+        },
+        pastFuture : function (diff, output) {
+            var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
+            return typeof format === 'function' ? format(output) : format.replace(/%s/i, output);
+        },
+
+        ordinal : function (number) {
+            return this._ordinal.replace("%d", number);
+        },
+        _ordinal : "%d",
+
+        preparse : function (string) {
+            return string;
+        },
+
+        postformat : function (string) {
+            return string;
+        },
+
+        week : function (mom) {
+            return weekOfYear(mom, this._week.dow, this._week.doy).week;
+        },
+
+        _week : {
+            dow : 0, // Sunday is the first day of the week.
+            doy : 6  // The week that contains Jan 1st is the first week of the year.
+        },
+
+        _invalidDate: 'Invalid date',
+        invalidDate: function () {
+            return this._invalidDate;
+        }
+    });
+
+    // Loads a language definition into the `languages` cache.  The function
+    // takes a key and optionally values.  If not in the browser and no values
+    // are provided, it will load the language file module.  As a convenience,
+    // this function also returns the language values.
+    function loadLang(key, values) {
+        values.abbr = key;
+        if (!languages[key]) {
+            languages[key] = new Language();
+        }
+        languages[key].set(values);
+        return languages[key];
+    }
+
+    // Remove a language from the `languages` cache. Mostly useful in tests.
+    function unloadLang(key) {
+        delete languages[key];
+    }
+
+    // Determines which language definition to use and returns it.
+    //
+    // With no parameters, it will return the global language.  If you
+    // pass in a language key, such as 'en', it will return the
+    // definition for 'en', so long as 'en' has already been loaded using
+    // moment.lang.
+    function getLangDefinition(key) {
+        var i = 0, j, lang, next, split,
+            get = function (k) {
+                if (!languages[k] && hasModule) {
+                    try {
+                        require('./lang/' + k);
+                    } catch (e) { }
+                }
+                return languages[k];
+            };
+
+        if (!key) {
+            return moment.fn._lang;
+        }
+
+        if (!isArray(key)) {
+            //short-circuit everything else
+            lang = get(key);
+            if (lang) {
+                return lang;
+            }
+            key = [key];
+        }
+
+        //pick the language from the array
+        //try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
+        //substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
+        while (i < key.length) {
+            split = normalizeLanguage(key[i]).split('-');
+            j = split.length;
+            next = normalizeLanguage(key[i + 1]);
+            next = next ? next.split('-') : null;
+            while (j > 0) {
+                lang = get(split.slice(0, j).join('-'));
+                if (lang) {
+                    return lang;
+                }
+                if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) {
+                    //the next array item is better than a shallower substring of this one
+                    break;
+                }
+                j--;
+            }
+            i++;
+        }
+        return moment.fn._lang;
+    }
+
+    /************************************
+        Formatting
+    ************************************/
+
+
+    function removeFormattingTokens(input) {
+        if (input.match(/\[[\s\S]/)) {
+            return input.replace(/^\[|\]$/g, "");
+        }
+        return input.replace(/\\/g, "");
+    }
+
+    function makeFormatFunction(format) {
+        var array = format.match(formattingTokens), i, length;
+
+        for (i = 0, length = array.length; i < length; i++) {
+            if (formatTokenFunctions[array[i]]) {
+                array[i] = formatTokenFunctions[array[i]];
+            } else {
+                array[i] = removeFormattingTokens(array[i]);
+            }
+        }
+
+        return function (mom) {
+            var output = "";
+            for (i = 0; i < length; i++) {
+                output += array[i] instanceof Function ? array[i].call(mom, format) : array[i];
+            }
+            return output;
+        };
+    }
+
+    // format date using native date object
+    function formatMoment(m, format) {
+
+        if (!m.isValid()) {
+            return m.lang().invalidDate();
+        }
+
+        format = expandFormat(format, m.lang());
+
+        if (!formatFunctions[format]) {
+            formatFunctions[format] = makeFormatFunction(format);
+        }
+
+        return formatFunctions[format](m);
+    }
+
+    function expandFormat(format, lang) {
+        var i = 5;
+
+        function replaceLongDateFormatTokens(input) {
+            return lang.longDateFormat(input) || input;
+        }
+
+        localFormattingTokens.lastIndex = 0;
+        while (i >= 0 && localFormattingTokens.test(format)) {
+            format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
+            localFormattingTokens.lastIndex = 0;
+            i -= 1;
+        }
+
+        return format;
+    }
+
+
+    /************************************
+        Parsing
+    ************************************/
+
+
+    // get the regex to find the next token
+    function getParseRegexForToken(token, config) {
+        var a, strict = config._strict;
+        switch (token) {
+        case 'DDDD':
+            return parseTokenThreeDigits;
+        case 'YYYY':
+        case 'GGGG':
+        case 'gggg':
+            return strict ? parseTokenFourDigits : parseTokenOneToFourDigits;
+        case 'Y':
+        case 'G':
+        case 'g':
+            return parseTokenSignedNumber;
+        case 'YYYYYY':
+        case 'YYYYY':
+        case 'GGGGG':
+        case 'ggggg':
+            return strict ? parseTokenSixDigits : parseTokenOneToSixDigits;
+        case 'S':
+            if (strict) { return parseTokenOneDigit; }
+            /* falls through */
+        case 'SS':
+            if (strict) { return parseTokenTwoDigits; }
+            /* falls through */
+        case 'SSS':
+            if (strict) { return parseTokenThreeDigits; }
+            /* falls through */
+        case 'DDD':
+            return parseTokenOneToThreeDigits;
+        case 'MMM':
+        case 'MMMM':
+        case 'dd':
+        case 'ddd':
+        case 'dddd':
+            return parseTokenWord;
+        case 'a':
+        case 'A':
+            return getLangDefinition(config._l)._meridiemParse;
+        case 'X':
+            return parseTokenTimestampMs;
+        case 'Z':
+        case 'ZZ':
+            return parseTokenTimezone;
+        case 'T':
+            return parseTokenT;
+        case 'SSSS':
+            return parseTokenDigits;
+        case 'MM':
+        case 'DD':
+        case 'YY':
+        case 'GG':
+        case 'gg':
+        case 'HH':
+        case 'hh':
+        case 'mm':
+        case 'ss':
+        case 'ww':
+        case 'WW':
+            return strict ? parseTokenTwoDigits : parseTokenOneOrTwoDigits;
+        case 'M':
+        case 'D':
+        case 'd':
+        case 'H':
+        case 'h':
+        case 'm':
+        case 's':
+        case 'w':
+        case 'W':
+        case 'e':
+        case 'E':
+            return parseTokenOneOrTwoDigits;
+        default :
+            a = new RegExp(regexpEscape(unescapeFormat(token.replace('\\', '')), "i"));
+            return a;
+        }
+    }
+
+    function timezoneMinutesFromString(string) {
+        string = string || "";
+        var possibleTzMatches = (string.match(parseTokenTimezone) || []),
+            tzChunk = possibleTzMatches[possibleTzMatches.length - 1] || [],
+            parts = (tzChunk + '').match(parseTimezoneChunker) || ['-', 0, 0],
+            minutes = +(parts[1] * 60) + toInt(parts[2]);
+
+        return parts[0] === '+' ? -minutes : minutes;
+    }
+
+    // function to convert string input to date
+    function addTimeToArrayFromToken(token, input, config) {
+        var a, datePartArray = config._a;
+
+        switch (token) {
+        // MONTH
+        case 'M' : // fall through to MM
+        case 'MM' :
+            if (input != null) {
+                datePartArray[MONTH] = toInt(input) - 1;
+            }
+            break;
+        case 'MMM' : // fall through to MMMM
+        case 'MMMM' :
+            a = getLangDefinition(config._l).monthsParse(input);
+            // if we didn't find a month name, mark the date as invalid.
+            if (a != null) {
+                datePartArray[MONTH] = a;
+            } else {
+                config._pf.invalidMonth = input;
+            }
+            break;
+        // DAY OF MONTH
+        case 'D' : // fall through to DD
+        case 'DD' :
+            if (input != null) {
+                datePartArray[DATE] = toInt(input);
+            }
+            break;
+        // DAY OF YEAR
+        case 'DDD' : // fall through to DDDD
+        case 'DDDD' :
+            if (input != null) {
+                config._dayOfYear = toInt(input);
+            }
+
+            break;
+        // YEAR
+        case 'YY' :
+            datePartArray[YEAR] = toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
+            break;
+        case 'YYYY' :
+        case 'YYYYY' :
+        case 'YYYYYY' :
+            datePartArray[YEAR] = toInt(input);
+            break;
+        // AM / PM
+        case 'a' : // fall through to A
+        case 'A' :
+            config._isPm = getLangDefinition(config._l).isPM(input);
+            break;
+        // 24 HOUR
+        case 'H' : // fall through to hh
+        case 'HH' : // fall through to hh
+        case 'h' : // fall through to hh
+        case 'hh' :
+            datePartArray[HOUR] = toInt(input);
+            break;
+        // MINUTE
+        case 'm' : // fall through to mm
+        case 'mm' :
+            datePartArray[MINUTE] = toInt(input);
+            break;
+        // SECOND
+        case 's' : // fall through to ss
+        case 'ss' :
+            datePartArray[SECOND] = toInt(input);
+            break;
+        // MILLISECOND
+        case 'S' :
+        case 'SS' :
+        case 'SSS' :
+        case 'SSSS' :
+            datePartArray[MILLISECOND] = toInt(('0.' + input) * 1000);
+            break;
+        // UNIX TIMESTAMP WITH MS
+        case 'X':
+            config._d = new Date(parseFloat(input) * 1000);
+            break;
+        // TIMEZONE
+        case 'Z' : // fall through to ZZ
+        case 'ZZ' :
+            config._useUTC = true;
+            config._tzm = timezoneMinutesFromString(input);
+            break;
+        case 'w':
+        case 'ww':
+        case 'W':
+        case 'WW':
+        case 'd':
+        case 'dd':
+        case 'ddd':
+        case 'dddd':
+        case 'e':
+        case 'E':
+            token = token.substr(0, 1);
+            /* falls through */
+        case 'gg':
+        case 'gggg':
+        case 'GG':
+        case 'GGGG':
+        case 'GGGGG':
+            token = token.substr(0, 2);
+            if (input) {
+                config._w = config._w || {};
+                config._w[token] = input;
+            }
+            break;
+        }
+    }
+
+    // convert an array to a date.
+    // the array should mirror the parameters below
+    // note: all values past the year are optional and will default to the lowest possible value.
+    // [year, month, day , hour, minute, second, millisecond]
+    function dateFromConfig(config) {
+        var i, date, input = [], currentDate,
+            yearToUse, fixYear, w, temp, lang, weekday, week;
+
+        if (config._d) {
+            return;
+        }
+
+        currentDate = currentDateArray(config);
+
+        //compute day of the year from weeks and weekdays
+        if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
+            fixYear = function (val) {
+                var int_val = parseInt(val, 10);
+                return val ?
+                  (val.length < 3 ? (int_val > 68 ? 1900 + int_val : 2000 + int_val) : int_val) :
+                  (config._a[YEAR] == null ? moment().weekYear() : config._a[YEAR]);
+            };
+
+            w = config._w;
+            if (w.GG != null || w.W != null || w.E != null) {
+                temp = dayOfYearFromWeeks(fixYear(w.GG), w.W || 1, w.E, 4, 1);
+            }
+            else {
+                lang = getLangDefinition(config._l);
+                weekday = w.d != null ?  parseWeekday(w.d, lang) :
+                  (w.e != null ?  parseInt(w.e, 10) + lang._week.dow : 0);
+
+                week = parseInt(w.w, 10) || 1;
+
+                //if we're parsing 'd', then the low day numbers may be next week
+                if (w.d != null && weekday < lang._week.dow) {
+                    week++;
+                }
+
+                temp = dayOfYearFromWeeks(fixYear(w.gg), week, weekday, lang._week.doy, lang._week.dow);
+            }
+
+            config._a[YEAR] = temp.year;
+            config._dayOfYear = temp.dayOfYear;
+        }
+
+        //if the day of the year is set, figure out what it is
+        if (config._dayOfYear) {
+            yearToUse = config._a[YEAR] == null ? currentDate[YEAR] : config._a[YEAR];
+
+            if (config._dayOfYear > daysInYear(yearToUse)) {
+                config._pf._overflowDayOfYear = true;
+            }
+
+            date = makeUTCDate(yearToUse, 0, config._dayOfYear);
+            config._a[MONTH] = date.getUTCMonth();
+            config._a[DATE] = date.getUTCDate();
+        }
+
+        // Default to current date.
+        // * if no year, month, day of month are given, default to today
+        // * if day of month is given, default month and year
+        // * if month is given, default only year
+        // * if year is given, don't default anything
+        for (i = 0; i < 3 && config._a[i] == null; ++i) {
+            config._a[i] = input[i] = currentDate[i];
+        }
+
+        // Zero out whatever was not defaulted, including time
+        for (; i < 7; i++) {
+            config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
+        }
+
+        // add the offsets to the time to be parsed so that we can have a clean array for checking isValid
+        input[HOUR] += toInt((config._tzm || 0) / 60);
+        input[MINUTE] += toInt((config._tzm || 0) % 60);
+
+        config._d = (config._useUTC ? makeUTCDate : makeDate).apply(null, input);
+    }
+
+    function dateFromObject(config) {
+        var normalizedInput;
+
+        if (config._d) {
+            return;
+        }
+
+        normalizedInput = normalizeObjectUnits(config._i);
+        config._a = [
+            normalizedInput.year,
+            normalizedInput.month,
+            normalizedInput.day,
+            normalizedInput.hour,
+            normalizedInput.minute,
+            normalizedInput.second,
+            normalizedInput.millisecond
+        ];
+
+        dateFromConfig(config);
+    }
+
+    function currentDateArray(config) {
+        var now = new Date();
+        if (config._useUTC) {
+            return [
+                now.getUTCFullYear(),
+                now.getUTCMonth(),
+                now.getUTCDate()
+            ];
+        } else {
+            return [now.getFullYear(), now.getMonth(), now.getDate()];
+        }
+    }
+
+    // date from string and format string
+    function makeDateFromStringAndFormat(config) {
+
+        config._a = [];
+        config._pf.empty = true;
+
+        // This array is used to make a Date, either with `new Date` or `Date.UTC`
+        var lang = getLangDefinition(config._l),
+            string = '' + config._i,
+            i, parsedInput, tokens, token, skipped,
+            stringLength = string.length,
+            totalParsedInputLength = 0;
+
+        tokens = expandFormat(config._f, lang).match(formattingTokens) || [];
+
+        for (i = 0; i < tokens.length; i++) {
+            token = tokens[i];
+            parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0];
+            if (parsedInput) {
+                skipped = string.substr(0, string.indexOf(parsedInput));
+                if (skipped.length > 0) {
+                    config._pf.unusedInput.push(skipped);
+                }
+                string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
+                totalParsedInputLength += parsedInput.length;
+            }
+            // don't parse if it's not a known token
+            if (formatTokenFunctions[token]) {
+                if (parsedInput) {
+                    config._pf.empty = false;
+                }
+                else {
+                    config._pf.unusedTokens.push(token);
+                }
+                addTimeToArrayFromToken(token, parsedInput, config);
+            }
+            else if (config._strict && !parsedInput) {
+                config._pf.unusedTokens.push(token);
+            }
+        }
+
+        // add remaining unparsed input length to the string
+        config._pf.charsLeftOver = stringLength - totalParsedInputLength;
+        if (string.length > 0) {
+            config._pf.unusedInput.push(string);
+        }
+
+        // handle am pm
+        if (config._isPm && config._a[HOUR] < 12) {
+            config._a[HOUR] += 12;
+        }
+        // if is 12 am, change hours to 0
+        if (config._isPm === false && config._a[HOUR] === 12) {
+            config._a[HOUR] = 0;
+        }
+
+        dateFromConfig(config);
+        checkOverflow(config);
+    }
+
+    function unescapeFormat(s) {
+        return s.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) {
+            return p1 || p2 || p3 || p4;
+        });
+    }
+
+    // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
+    function regexpEscape(s) {
+        return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
+    }
+
+    // date from string and array of format strings
+    function makeDateFromStringAndArray(config) {
+        var tempConfig,
+            bestMoment,
+
+            scoreToBeat,
+            i,
+            currentScore;
+
+        if (config._f.length === 0) {
+            config._pf.invalidFormat = true;
+            config._d = new Date(NaN);
+            return;
+        }
+
+        for (i = 0; i < config._f.length; i++) {
+            currentScore = 0;
+            tempConfig = extend({}, config);
+            tempConfig._pf = defaultParsingFlags();
+            tempConfig._f = config._f[i];
+            makeDateFromStringAndFormat(tempConfig);
+
+            if (!isValid(tempConfig)) {
+                continue;
+            }
+
+            // if there is any input that was not parsed add a penalty for that format
+            currentScore += tempConfig._pf.charsLeftOver;
+
+            //or tokens
+            currentScore += tempConfig._pf.unusedTokens.length * 10;
+
+            tempConfig._pf.score = currentScore;
+
+            if (scoreToBeat == null || currentScore < scoreToBeat) {
+                scoreToBeat = currentScore;
+                bestMoment = tempConfig;
+            }
+        }
+
+        extend(config, bestMoment || tempConfig);
+    }
+
+    // date from iso format
+    function makeDateFromString(config) {
+        var i, l,
+            string = config._i,
+            match = isoRegex.exec(string);
+
+        if (match) {
+            config._pf.iso = true;
+            for (i = 0, l = isoDates.length; i < l; i++) {
+                if (isoDates[i][1].exec(string)) {
+                    // match[5] should be "T" or undefined
+                    config._f = isoDates[i][0] + (match[6] || " ");
+                    break;
+                }
+            }
+            for (i = 0, l = isoTimes.length; i < l; i++) {
+                if (isoTimes[i][1].exec(string)) {
+                    config._f += isoTimes[i][0];
+                    break;
+                }
+            }
+            if (string.match(parseTokenTimezone)) {
+                config._f += "Z";
+            }
+            makeDateFromStringAndFormat(config);
+        }
+        else {
+            config._d = new Date(string);
+        }
+    }
+
+    function makeDateFromInput(config) {
+        var input = config._i,
+            matched = aspNetJsonRegex.exec(input);
+
+        if (input === undefined) {
+            config._d = new Date();
+        } else if (matched) {
+            config._d = new Date(+matched[1]);
+        } else if (typeof input === 'string') {
+            makeDateFromString(config);
+        } else if (isArray(input)) {
+            config._a = input.slice(0);
+            dateFromConfig(config);
+        } else if (isDate(input)) {
+            config._d = new Date(+input);
+        } else if (typeof(input) === 'object') {
+            dateFromObject(config);
+        } else {
+            config._d = new Date(input);
+        }
+    }
+
+    function makeDate(y, m, d, h, M, s, ms) {
+        //can't just apply() to create a date:
+        //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply
+        var date = new Date(y, m, d, h, M, s, ms);
+
+        //the date constructor doesn't accept years < 1970
+        if (y < 1970) {
+            date.setFullYear(y);
+        }
+        return date;
+    }
+
+    function makeUTCDate(y) {
+        var date = new Date(Date.UTC.apply(null, arguments));
+        if (y < 1970) {
+            date.setUTCFullYear(y);
+        }
+        return date;
+    }
+
+    function parseWeekday(input, language) {
+        if (typeof input === 'string') {
+            if (!isNaN(input)) {
+                input = parseInt(input, 10);
+            }
+            else {
+                input = language.weekdaysParse(input);
+                if (typeof input !== 'number') {
+                    return null;
+                }
+            }
+        }
+        return input;
+    }
+
+    /************************************
+        Relative Time
+    ************************************/
+
+
+    // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
+    function substituteTimeAgo(string, number, withoutSuffix, isFuture, lang) {
+        return lang.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
+    }
+
+    function relativeTime(milliseconds, withoutSuffix, lang) {
+        var seconds = round(Math.abs(milliseconds) / 1000),
+            minutes = round(seconds / 60),
+            hours = round(minutes / 60),
+            days = round(hours / 24),
+            years = round(days / 365),
+            args = seconds < 45 && ['s', seconds] ||
+                minutes === 1 && ['m'] ||
+                minutes < 45 && ['mm', minutes] ||
+                hours === 1 && ['h'] ||
+                hours < 22 && ['hh', hours] ||
+                days === 1 && ['d'] ||
+                days <= 25 && ['dd', days] ||
+                days <= 45 && ['M'] ||
+                days < 345 && ['MM', round(days / 30)] ||
+                years === 1 && ['y'] || ['yy', years];
+        args[2] = withoutSuffix;
+        args[3] = milliseconds > 0;
+        args[4] = lang;
+        return substituteTimeAgo.apply({}, args);
+    }
+
+
+    /************************************
+        Week of Year
+    ************************************/
+
+
+    // firstDayOfWeek       0 = sun, 6 = sat
+    //                      the day of the week that starts the week
+    //                      (usually sunday or monday)
+    // firstDayOfWeekOfYear 0 = sun, 6 = sat
+    //                      the first week is the week that contains the first
+    //                      of this day of the week
+    //                      (eg. ISO weeks use thursday (4))
+    function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) {
+        var end = firstDayOfWeekOfYear - firstDayOfWeek,
+            daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(),
+            adjustedMoment;
+
+
+        if (daysToDayOfWeek > end) {
+            daysToDayOfWeek -= 7;
+        }
+
+        if (daysToDayOfWeek < end - 7) {
+            daysToDayOfWeek += 7;
+        }
+
+        adjustedMoment = moment(mom).add('d', daysToDayOfWeek);
+        return {
+            week: Math.ceil(adjustedMoment.dayOfYear() / 7),
+            year: adjustedMoment.year()
+        };
+    }
+
+    //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
+    function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) {
+        var d = makeUTCDate(year, 0, 1).getUTCDay(), daysToAdd, dayOfYear;
+
+        weekday = weekday != null ? weekday : firstDayOfWeek;
+        daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0) - (d < firstDayOfWeek ? 7 : 0);
+        dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1;
+
+        return {
+            year: dayOfYear > 0 ? year : year - 1,
+            dayOfYear: dayOfYear > 0 ?  dayOfYear : daysInYear(year - 1) + dayOfYear
+        };
+    }
+
+    /************************************
+        Top Level Functions
+    ************************************/
+
+    function makeMoment(config) {
+        var input = config._i,
+            format = config._f;
+
+        if (input === null) {
+            return moment.invalid({nullInput: true});
+        }
+
+        if (typeof input === 'string') {
+            config._i = input = getLangDefinition().preparse(input);
+        }
+
+        if (moment.isMoment(input)) {
+            config = cloneMoment(input);
+
+            config._d = new Date(+input._d);
+        } else if (format) {
+            if (isArray(format)) {
+                makeDateFromStringAndArray(config);
+            } else {
+                makeDateFromStringAndFormat(config);
+            }
+        } else {
+            makeDateFromInput(config);
+        }
+
+        return new Moment(config);
+    }
+
+    moment = function (input, format, lang, strict) {
+        var c;
+
+        if (typeof(lang) === "boolean") {
+            strict = lang;
+            lang = undefined;
+        }
+        // object construction must be done this way.
+        // https://github.com/moment/moment/issues/1423
+        c = {};
+        c._isAMomentObject = true;
+        c._i = input;
+        c._f = format;
+        c._l = lang;
+        c._strict = strict;
+        c._isUTC = false;
+        c._pf = defaultParsingFlags();
+
+        return makeMoment(c);
+    };
+
+    // creating with utc
+    moment.utc = function (input, format, lang, strict) {
+        var c;
+
+        if (typeof(lang) === "boolean") {
+            strict = lang;
+            lang = undefined;
+        }
+        // object construction must be done this way.
+        // https://github.com/moment/moment/issues/1423
+        c = {};
+        c._isAMomentObject = true;
+        c._useUTC = true;
+        c._isUTC = true;
+        c._l = lang;
+        c._i = input;
+        c._f = format;
+        c._strict = strict;
+        c._pf = defaultParsingFlags();
+
+        return makeMoment(c).utc();
+    };
+
+    // creating with unix timestamp (in seconds)
+    moment.unix = function (input) {
+        return moment(input * 1000);
+    };
+
+    // duration
+    moment.duration = function (input, key) {
+        var duration = input,
+            // matching against regexp is expensive, do it on demand
+            match = null,
+            sign,
+            ret,
+            parseIso;
+
+        if (moment.isDuration(input)) {
+            duration = {
+                ms: input._milliseconds,
+                d: input._days,
+                M: input._months
+            };
+        } else if (typeof input === 'number') {
+            duration = {};
+            if (key) {
+                duration[key] = input;
+            } else {
+                duration.milliseconds = input;
+            }
+        } else if (!!(match = aspNetTimeSpanJsonRegex.exec(input))) {
+            sign = (match[1] === "-") ? -1 : 1;
+            duration = {
+                y: 0,
+                d: toInt(match[DATE]) * sign,
+                h: toInt(match[HOUR]) * sign,
+                m: toInt(match[MINUTE]) * sign,
+                s: toInt(match[SECOND]) * sign,
+                ms: toInt(match[MILLISECOND]) * sign
+            };
+        } else if (!!(match = isoDurationRegex.exec(input))) {
+            sign = (match[1] === "-") ? -1 : 1;
+            parseIso = function (inp) {
+                // We'd normally use ~~inp for this, but unfortunately it also
+                // converts floats to ints.
+                // inp may be undefined, so careful calling replace on it.
+                var res = inp && parseFloat(inp.replace(',', '.'));
+                // apply sign while we're at it
+                return (isNaN(res) ? 0 : res) * sign;
+            };
+            duration = {
+                y: parseIso(match[2]),
+                M: parseIso(match[3]),
+                d: parseIso(match[4]),
+                h: parseIso(match[5]),
+                m: parseIso(match[6]),
+                s: parseIso(match[7]),
+                w: parseIso(match[8])
+            };
+        }
+
+        ret = new Duration(duration);
+
+        if (moment.isDuration(input) && input.hasOwnProperty('_lang')) {
+            ret._lang = input._lang;
+        }
+
+        return ret;
+    };
+
+    // version number
+    moment.version = VERSION;
+
+    // default format
+    moment.defaultFormat = isoFormat;
+
+    // This function will be called whenever a moment is mutated.
+    // It is intended to keep the offset in sync with the timezone.
+    moment.updateOffset = function () {};
+
+    // This function will load languages and then set the global language.  If
+    // no arguments are passed in, it will simply return the current global
+    // language key.
+    moment.lang = function (key, values) {
+        var r;
+        if (!key) {
+            return moment.fn._lang._abbr;
+        }
+        if (values) {
+            loadLang(normalizeLanguage(key), values);
+        } else if (values === null) {
+            unloadLang(key);
+            key = 'en';
+        } else if (!languages[key]) {
+            getLangDefinition(key);
+        }
+        r = moment.duration.fn._lang = moment.fn._lang = getLangDefinition(key);
+        return r._abbr;
+    };
+
+    // returns language data
+    moment.langData = function (key) {
+        if (key && key._lang && key._lang._abbr) {
+            key = key._lang._abbr;
+        }
+        return getLangDefinition(key);
+    };
+
+    // compare moment object
+    moment.isMoment = function (obj) {
+        return obj instanceof Moment ||
+            (obj != null &&  obj.hasOwnProperty('_isAMomentObject'));
+    };
+
+    // for typechecking Duration objects
+    moment.isDuration = function (obj) {
+        return obj instanceof Duration;
+    };
+
+    for (i = lists.length - 1; i >= 0; --i) {
+        makeList(lists[i]);
+    }
+
+    moment.normalizeUnits = function (units) {
+        return normalizeUnits(units);
+    };
+
+    moment.invalid = function (flags) {
+        var m = moment.utc(NaN);
+        if (flags != null) {
+            extend(m._pf, flags);
+        }
+        else {
+            m._pf.userInvalidated = true;
+        }
+
+        return m;
+    };
+
+    moment.parseZone = function (input) {
+        return moment(input).parseZone();
+    };
+
+    /************************************
+        Moment Prototype
+    ************************************/
+
+
+    extend(moment.fn = Moment.prototype, {
+
+        clone : function () {
+            return moment(this);
+        },
+
+        valueOf : function () {
+            return +this._d + ((this._offset || 0) * 60000);
+        },
+
+        unix : function () {
+            return Math.floor(+this / 1000);
+        },
+
+        toString : function () {
+            return this.clone().lang('en').format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ");
+        },
+
+        toDate : function () {
+            return this._offset ? new Date(+this) : this._d;
+        },
+
+        toISOString : function () {
+            var m = moment(this).utc();
+            if (0 < m.year() && m.year() <= 9999) {
+                return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
+            } else {
+                return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
+            }
+        },
+
+        toArray : function () {
+            var m = this;
+            return [
+                m.year(),
+                m.month(),
+                m.date(),
+                m.hours(),
+                m.minutes(),
+                m.seconds(),
+                m.milliseconds()
+            ];
+        },
+
+        isValid : function () {
+            return isValid(this);
+        },
+
+        isDSTShifted : function () {
+
+            if (this._a) {
+                return this.isValid() && compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()) > 0;
+            }
+
+            return false;
+        },
+
+        parsingFlags : function () {
+            return extend({}, this._pf);
+        },
+
+        invalidAt: function () {
+            return this._pf.overflow;
+        },
+
+        utc : function () {
+            return this.zone(0);
+        },
+
+        local : function () {
+            this.zone(0);
+            this._isUTC = false;
+            return this;
+        },
+
+        format : function (inputString) {
+            var output = formatMoment(this, inputString || moment.defaultFormat);
+            return this.lang().postformat(output);
+        },
+
+        add : function (input, val) {
+            var dur;
+            // switch args to support add('s', 1) and add(1, 's')
+            if (typeof input === 'string') {
+                dur = moment.duration(+val, input);
+            } else {
+                dur = moment.duration(input, val);
+            }
+            addOrSubtractDurationFromMoment(this, dur, 1);
+            return this;
+        },
+
+        subtract : function (input, val) {
+            var dur;
+            // switch args to support subtract('s', 1) and subtract(1, 's')
+            if (typeof input === 'string') {
+                dur = moment.duration(+val, input);
+            } else {
+                dur = moment.duration(input, val);
+            }
+            addOrSubtractDurationFromMoment(this, dur, -1);
+            return this;
+        },
+
+        diff : function (input, units, asFloat) {
+            var that = makeAs(input, this),
+                zoneDiff = (this.zone() - that.zone()) * 6e4,
+                diff, output;
+
+            units = normalizeUnits(units);
+
+            if (units === 'year' || units === 'month') {
+                // average number of days in the months in the given dates
+                diff = (this.daysInMonth() + that.daysInMonth()) * 432e5; // 24 * 60 * 60 * 1000 / 2
+                // difference in months
+                output = ((this.year() - that.year()) * 12) + (this.month() - that.month());
+                // adjust by taking difference in days, average number of days
+                // and dst in the given months.
+                output += ((this - moment(this).startOf('month')) -
+                        (that - moment(that).startOf('month'))) / diff;
+                // same as above but with zones, to negate all dst
+                output -= ((this.zone() - moment(this).startOf('month').zone()) -
+                        (that.zone() - moment(that).startOf('month').zone())) * 6e4 / diff;
+                if (units === 'year') {
+                    output = output / 12;
+                }
+            } else {
+                diff = (this - that);
+                output = units === 'second' ? diff / 1e3 : // 1000
+                    units === 'minute' ? diff / 6e4 : // 1000 * 60
+                    units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60
+                    units === 'day' ? (diff - zoneDiff) / 864e5 : // 1000 * 60 * 60 * 24, negate dst
+                    units === 'week' ? (diff - zoneDiff) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst
+                    diff;
+            }
+            return asFloat ? output : absRound(output);
+        },
+
+        from : function (time, withoutSuffix) {
+            return moment.duration(this.diff(time)).lang(this.lang()._abbr).humanize(!withoutSuffix);
+        },
+
+        fromNow : function (withoutSuffix) {
+            return this.from(moment(), withoutSuffix);
+        },
+
+        calendar : function () {
+            // We want to compare the start of today, vs this.
+            // Getting start-of-today depends on whether we're zone'd or not.
+            var sod = makeAs(moment(), this).startOf('day'),
+                diff = this.diff(sod, 'days', true),
+                format = diff < -6 ? 'sameElse' :
+                    diff < -1 ? 'lastWeek' :
+                    diff < 0 ? 'lastDay' :
+                    diff < 1 ? 'sameDay' :
+                    diff < 2 ? 'nextDay' :
+                    diff < 7 ? 'nextWeek' : 'sameElse';
+            return this.format(this.lang().calendar(format, this));
+        },
+
+        isLeapYear : function () {
+            return isLeapYear(this.year());
+        },
+
+        isDST : function () {
+            return (this.zone() < this.clone().month(0).zone() ||
+                this.zone() < this.clone().month(5).zone());
+        },
+
+        day : function (input) {
+            var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
+            if (input != null) {
+                input = parseWeekday(input, this.lang());
+                return this.add({ d : input - day });
+            } else {
+                return day;
+            }
+        },
+
+        month : function (input) {
+            var utc = this._isUTC ? 'UTC' : '',
+                dayOfMonth;
+
+            if (input != null) {
+                if (typeof input === 'string') {
+                    input = this.lang().monthsParse(input);
+                    if (typeof input !== 'number') {
+                        return this;
+                    }
+                }
+
+                dayOfMonth = this.date();
+                this.date(1);
+                this._d['set' + utc + 'Month'](input);
+                this.date(Math.min(dayOfMonth, this.daysInMonth()));
+
+                moment.updateOffset(this);
+                return this;
+            } else {
+                return this._d['get' + utc + 'Month']();
+            }
+        },
+
+        startOf: function (units) {
+            units = normalizeUnits(units);
+            // the following switch intentionally omits break keywords
+            // to utilize falling through the cases.
+            switch (units) {
+            case 'year':
+                this.month(0);
+                /* falls through */
+            case 'month':
+                this.date(1);
+                /* falls through */
+            case 'week':
+            case 'isoWeek':
+            case 'day':
+                this.hours(0);
+                /* falls through */
+            case 'hour':
+                this.minutes(0);
+                /* falls through */
+            case 'minute':
+                this.seconds(0);
+                /* falls through */
+            case 'second':
+                this.milliseconds(0);
+                /* falls through */
+            }
+
+            // weeks are a special case
+            if (units === 'week') {
+                this.weekday(0);
+            } else if (units === 'isoWeek') {
+                this.isoWeekday(1);
+            }
+
+            return this;
+        },
+
+        endOf: function (units) {
+            units = normalizeUnits(units);
+            return this.startOf(units).add((units === 'isoWeek' ? 'week' : units), 1).subtract('ms', 1);
+        },
+
+        isAfter: function (input, units) {
+            units = typeof units !== 'undefined' ? units : 'millisecond';
+            return +this.clone().startOf(units) > +moment(input).startOf(units);
+        },
+
+        isBefore: function (input, units) {
+            units = typeof units !== 'undefined' ? units : 'millisecond';
+            return +this.clone().startOf(units) < +moment(input).startOf(units);
+        },
+
+        isSame: function (input, units) {
+            units = units || 'ms';
+            return +this.clone().startOf(units) === +makeAs(input, this).startOf(units);
+        },
+
+        min: function (other) {
+            other = moment.apply(null, arguments);
+            return other < this ? this : other;
+        },
+
+        max: function (other) {
+            other = moment.apply(null, arguments);
+            return other > this ? this : other;
+        },
+
+        zone : function (input) {
+            var offset = this._offset || 0;
+            if (input != null) {
+                if (typeof input === "string") {
+                    input = timezoneMinutesFromString(input);
+                }
+                if (Math.abs(input) < 16) {
+                    input = input * 60;
+                }
+                this._offset = input;
+                this._isUTC = true;
+                if (offset !== input) {
+                    addOrSubtractDurationFromMoment(this, moment.duration(offset - input, 'm'), 1, true);
+                }
+            } else {
+                return this._isUTC ? offset : this._d.getTimezoneOffset();
+            }
+            return this;
+        },
+
+        zoneAbbr : function () {
+            return this._isUTC ? "UTC" : "";
+        },
+
+        zoneName : function () {
+            return this._isUTC ? "Coordinated Universal Time" : "";
+        },
+
+        parseZone : function () {
+            if (this._tzm) {
+                this.zone(this._tzm);
+            } else if (typeof this._i === 'string') {
+                this.zone(this._i);
+            }
+            return this;
+        },
+
+        hasAlignedHourOffset : function (input) {
+            if (!input) {
+                input = 0;
+            }
+            else {
+                input = moment(input).zone();
+            }
+
+            return (this.zone() - input) % 60 === 0;
+        },
+
+        daysInMonth : function () {
+            return daysInMonth(this.year(), this.month());
+        },
+
+        dayOfYear : function (input) {
+            var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1;
+            return input == null ? dayOfYear : this.add("d", (input - dayOfYear));
+        },
+
+        quarter : function () {
+            return Math.ceil((this.month() + 1.0) / 3.0);
+        },
+
+        weekYear : function (input) {
+            var year = weekOfYear(this, this.lang()._week.dow, this.lang()._week.doy).year;
+            return input == null ? year : this.add("y", (input - year));
+        },
+
+        isoWeekYear : function (input) {
+            var year = weekOfYear(this, 1, 4).year;
+            return input == null ? year : this.add("y", (input - year));
+        },
+
+        week : function (input) {
+            var week = this.lang().week(this);
+            return input == null ? week : this.add("d", (input - week) * 7);
+        },
+
+        isoWeek : function (input) {
+            var week = weekOfYear(this, 1, 4).week;
+            return input == null ? week : this.add("d", (input - week) * 7);
+        },
+
+        weekday : function (input) {
+            var weekday = (this.day() + 7 - this.lang()._week.dow) % 7;
+            return input == null ? weekday : this.add("d", input - weekday);
+        },
+
+        isoWeekday : function (input) {
+            // behaves the same as moment#day except
+            // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
+            // as a setter, sunday should belong to the previous week.
+            return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7);
+        },
+
+        get : function (units) {
+            units = normalizeUnits(units);
+            return this[units]();
+        },
+
+        set : function (units, value) {
+            units = normalizeUnits(units);
+            if (typeof this[units] === 'function') {
+                this[units](value);
+            }
+            return this;
+        },
+
+        // If passed a language key, it will set the language for this
+        // instance.  Otherwise, it will return the language configuration
+        // variables for this instance.
+        lang : function (key) {
+            if (key === undefined) {
+                return this._lang;
+            } else {
+                this._lang = getLangDefinition(key);
+                return this;
+            }
+        }
+    });
+
+    // helper for adding shortcuts
+    function makeGetterAndSetter(name, key) {
+        moment.fn[name] = moment.fn[name + 's'] = function (input) {
+            var utc = this._isUTC ? 'UTC' : '';
+            if (input != null) {
+                this._d['set' + utc + key](input);
+                moment.updateOffset(this);
+                return this;
+            } else {
+                return this._d['get' + utc + key]();
+            }
+        };
+    }
+
+    // loop through and add shortcuts (Month, Date, Hours, Minutes, Seconds, Milliseconds)
+    for (i = 0; i < proxyGettersAndSetters.length; i ++) {
+        makeGetterAndSetter(proxyGettersAndSetters[i].toLowerCase().replace(/s$/, ''), proxyGettersAndSetters[i]);
+    }
+
+    // add shortcut for year (uses different syntax than the getter/setter 'year' == 'FullYear')
+    makeGetterAndSetter('year', 'FullYear');
+
+    // add plural methods
+    moment.fn.days = moment.fn.day;
+    moment.fn.months = moment.fn.month;
+    moment.fn.weeks = moment.fn.week;
+    moment.fn.isoWeeks = moment.fn.isoWeek;
+
+    // add aliased format methods
+    moment.fn.toJSON = moment.fn.toISOString;
+
+    /************************************
+        Duration Prototype
+    ************************************/
+
+
+    extend(moment.duration.fn = Duration.prototype, {
+
+        _bubble : function () {
+            var milliseconds = this._milliseconds,
+                days = this._days,
+                months = this._months,
+                data = this._data,
+                seconds, minutes, hours, years;
+
+            // The following code bubbles up values, see the tests for
+            // examples of what that means.
+            data.milliseconds = milliseconds % 1000;
+
+            seconds = absRound(milliseconds / 1000);
+            data.seconds = seconds % 60;
+
+            minutes = absRound(seconds / 60);
+            data.minutes = minutes % 60;
+
+            hours = absRound(minutes / 60);
+            data.hours = hours % 24;
+
+            days += absRound(hours / 24);
+            data.days = days % 30;
+
+            months += absRound(days / 30);
+            data.months = months % 12;
+
+            years = absRound(months / 12);
+            data.years = years;
+        },
+
+        weeks : function () {
+            return absRound(this.days() / 7);
+        },
+
+        valueOf : function () {
+            return this._milliseconds +
+              this._days * 864e5 +
+              (this._months % 12) * 2592e6 +
+              toInt(this._months / 12) * 31536e6;
+        },
+
+        humanize : function (withSuffix) {
+            var difference = +this,
+                output = relativeTime(difference, !withSuffix, this.lang());
+
+            if (withSuffix) {
+                output = this.lang().pastFuture(difference, output);
+            }
+
+            return this.lang().postformat(output);
+        },
+
+        add : function (input, val) {
+            // supports only 2.0-style add(1, 's') or add(moment)
+            var dur = moment.duration(input, val);
+
+            this._milliseconds += dur._milliseconds;
+            this._days += dur._days;
+            this._months += dur._months;
+
+            this._bubble();
+
+            return this;
+        },
+
+        subtract : function (input, val) {
+            var dur = moment.duration(input, val);
+
+            this._milliseconds -= dur._milliseconds;
+            this._days -= dur._days;
+            this._months -= dur._months;
+
+            this._bubble();
+
+            return this;
+        },
+
+        get : function (units) {
+            units = normalizeUnits(units);
+            return this[units.toLowerCase() + 's']();
+        },
+
+        as : function (units) {
+            units = normalizeUnits(units);
+            return this['as' + units.charAt(0).toUpperCase() + units.slice(1) + 's']();
+        },
+
+        lang : moment.fn.lang,
+
+        toIsoString : function () {
+            // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
+            var years = Math.abs(this.years()),
+                months = Math.abs(this.months()),
+                days = Math.abs(this.days()),
+                hours = Math.abs(this.hours()),
+                minutes = Math.abs(this.minutes()),
+                seconds = Math.abs(this.seconds() + this.milliseconds() / 1000);
+
+            if (!this.asSeconds()) {
+                // this is the same as C#'s (Noda) and python (isodate)...
+                // but not other JS (goog.date)
+                return 'P0D';
+            }
+
+            return (this.asSeconds() < 0 ? '-' : '') +
+                'P' +
+                (years ? years + 'Y' : '') +
+                (months ? months + 'M' : '') +
+                (days ? days + 'D' : '') +
+                ((hours || minutes || seconds) ? 'T' : '') +
+                (hours ? hours + 'H' : '') +
+                (minutes ? minutes + 'M' : '') +
+                (seconds ? seconds + 'S' : '');
+        }
+    });
+
+    function makeDurationGetter(name) {
+        moment.duration.fn[name] = function () {
+            return this._data[name];
+        };
+    }
+
+    function makeDurationAsGetter(name, factor) {
+        moment.duration.fn['as' + name] = function () {
+            return +this / factor;
+        };
+    }
+
+    for (i in unitMillisecondFactors) {
+        if (unitMillisecondFactors.hasOwnProperty(i)) {
+            makeDurationAsGetter(i, unitMillisecondFactors[i]);
+            makeDurationGetter(i.toLowerCase());
+        }
+    }
+
+    makeDurationAsGetter('Weeks', 6048e5);
+    moment.duration.fn.asMonths = function () {
+        return (+this - this.years() * 31536e6) / 2592e6 + this.years() * 12;
+    };
+
+
+    /************************************
+        Default Lang
+    ************************************/
+
+
+    // Set default language, other languages will inherit from English.
+    moment.lang('en', {
+        ordinal : function (number) {
+            var b = number % 10,
+                output = (toInt(number % 100 / 10) === 1) ? 'th' :
+                (b === 1) ? 'st' :
+                (b === 2) ? 'nd' :
+                (b === 3) ? 'rd' : 'th';
+            return number + output;
+        }
+    });
+
+    /* EMBED_LANGUAGES */
+
+    /************************************
+        Exposing Moment
+    ************************************/
+
+    function makeGlobal(deprecate) {
+        var warned = false, local_moment = moment;
+        /*global ender:false */
+        if (typeof ender !== 'undefined') {
+            return;
+        }
+        // here, `this` means `window` in the browser, or `global` on the server
+        // add `moment` as a global object via a string identifier,
+        // for Closure Compiler "advanced" mode
+        if (deprecate) {
+            global.moment = function () {
+                if (!warned && console && console.warn) {
+                    warned = true;
+                    console.warn(
+                            "Accessing Moment through the global scope is " +
+                            "deprecated, and will be removed in an upcoming " +
+                            "release.");
+                }
+                return local_moment.apply(null, arguments);
+            };
+            extend(global.moment, local_moment);
+        } else {
+            global['moment'] = moment;
+        }
+    }
+
+    // CommonJS module is defined
+    if (hasModule) {
+        module.exports = moment;
+        makeGlobal(true);
+    } else if (typeof define === "function" && define.amd) {
+        define("moment", function (require, exports, module) {
+            if (module.config && module.config() && module.config().noGlobal !== true) {
+                // If user provided noGlobal, he is aware of global
+                makeGlobal(module.config().noGlobal === undefined);
+            }
+
+            return moment;
+        });
+    } else {
+        makeGlobal();
+    }
+}).call(this);

+ 3477 - 0
app/assets/javascripts/blazer/selectize.js

@@ -0,0 +1,3477 @@
+/**
+ * sifter.js
+ * Copyright (c) 2013 Brian Reavis & contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
+ * file except in compliance with the License. You may obtain a copy of the License at:
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
+ * ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ *
+ * @author Brian Reavis <brian@thirdroute.com>
+ */
+
+(function(root, factory) {
+  if (typeof define === 'function' && define.amd) {
+    define('sifter', factory);
+  } else if (typeof exports === 'object') {
+    module.exports = factory();
+  } else {
+    root.Sifter = factory();
+  }
+}(this, function() {
+
+  /**
+   * Textually searches arrays and hashes of objects
+   * by property (or multiple properties). Designed
+   * specifically for autocomplete.
+   *
+   * @constructor
+   * @param {array|object} items
+   * @param {object} items
+   */
+  var Sifter = function(items, settings) {
+    this.items = items;
+    this.settings = settings || {diacritics: true};
+  };
+
+  /**
+   * Splits a search string into an array of individual
+   * regexps to be used to match results.
+   *
+   * @param {string} query
+   * @returns {array}
+   */
+  Sifter.prototype.tokenize = function(query) {
+    query = trim(String(query || '').toLowerCase());
+    if (!query || !query.length) return [];
+
+    var i, n, regex, letter;
+    var tokens = [];
+    var words = query.split(/ +/);
+
+    for (i = 0, n = words.length; i < n; i++) {
+      regex = escape_regex(words[i]);
+      if (this.settings.diacritics) {
+        for (letter in DIACRITICS) {
+          if (DIACRITICS.hasOwnProperty(letter)) {
+            regex = regex.replace(new RegExp(letter, 'g'), DIACRITICS[letter]);
+          }
+        }
+      }
+      tokens.push({
+        string : words[i],
+        regex  : new RegExp(regex, 'i')
+      });
+    }
+
+    return tokens;
+  };
+
+  /**
+   * Iterates over arrays and hashes.
+   *
+   * ```
+   * this.iterator(this.items, function(item, id) {
+   *    // invoked for each item
+   * });
+   * ```
+   *
+   * @param {array|object} object
+   */
+  Sifter.prototype.iterator = function(object, callback) {
+    var iterator;
+    if (is_array(object)) {
+      iterator = Array.prototype.forEach || function(callback) {
+        for (var i = 0, n = this.length; i < n; i++) {
+          callback(this[i], i, this);
+        }
+      };
+    } else {
+      iterator = function(callback) {
+        for (var key in this) {
+          if (this.hasOwnProperty(key)) {
+            callback(this[key], key, this);
+          }
+        }
+      };
+    }
+
+    iterator.apply(object, [callback]);
+  };
+
+  /**
+   * Returns a function to be used to score individual results.
+   *
+   * Good matches will have a higher score than poor matches.
+   * If an item is not a match, 0 will be returned by the function.
+   *
+   * @param {object|string} search
+   * @param {object} options (optional)
+   * @returns {function}
+   */
+  Sifter.prototype.getScoreFunction = function(search, options) {
+    var self, fields, tokens, token_count;
+
+    self        = this;
+    search      = self.prepareSearch(search, options);
+    tokens      = search.tokens;
+    fields      = search.options.fields;
+    token_count = tokens.length;
+
+    /**
+     * Calculates how close of a match the
+     * given value is against a search token.
+     *
+     * @param {mixed} value
+     * @param {object} token
+     * @return {number}
+     */
+    var scoreValue = function(value, token) {
+      var score, pos;
+
+      if (!value) return 0;
+      value = String(value || '');
+      pos = value.search(token.regex);
+      if (pos === -1) return 0;
+      score = token.string.length / value.length;
+      if (pos === 0) score += 0.5;
+      return score;
+    };
+
+    /**
+     * Calculates the score of an object
+     * against the search query.
+     *
+     * @param {object} token
+     * @param {object} data
+     * @return {number}
+     */
+    var scoreObject = (function() {
+      var field_count = fields.length;
+      if (!field_count) {
+        return function() { return 0; };
+      }
+      if (field_count === 1) {
+        return function(token, data) {
+          return scoreValue(data[fields[0]], token);
+        };
+      }
+      return function(token, data) {
+        for (var i = 0, sum = 0; i < field_count; i++) {
+          sum += scoreValue(data[fields[i]], token);
+        }
+        return sum / field_count;
+      };
+    })();
+
+    if (!token_count) {
+      return function() { return 0; };
+    }
+    if (token_count === 1) {
+      return function(data) {
+        return scoreObject(tokens[0], data);
+      };
+    }
+
+    if (search.options.conjunction === 'and') {
+      return function(data) {
+        var score;
+        for (var i = 0, sum = 0; i < token_count; i++) {
+          score = scoreObject(tokens[i], data);
+          if (score <= 0) return 0;
+          sum += score;
+        }
+        return sum / token_count;
+      };
+    } else {
+      return function(data) {
+        for (var i = 0, sum = 0; i < token_count; i++) {
+          sum += scoreObject(tokens[i], data);
+        }
+        return sum / token_count;
+      };
+    }
+  };
+
+  /**
+   * Returns a function that can be used to compare two
+   * results, for sorting purposes. If no sorting should
+   * be performed, `null` will be returned.
+   *
+   * @param {string|object} search
+   * @param {object} options
+   * @return function(a,b)
+   */
+  Sifter.prototype.getSortFunction = function(search, options) {
+    var i, n, self, field, fields, fields_count, multiplier, multipliers, get_field, implicit_score, sort;
+
+    self   = this;
+    search = self.prepareSearch(search, options);
+    sort   = (!search.query && options.sort_empty) || options.sort;
+
+    /**
+     * Fetches the specified sort field value
+     * from a search result item.
+     *
+     * @param  {string} name
+     * @param  {object} result
+     * @return {mixed}
+     */
+    get_field  = function(name, result) {
+      if (name === '$score') return result.score;
+      return self.items[result.id][name];
+    };
+
+    // parse options
+    fields = [];
+    if (sort) {
+      for (i = 0, n = sort.length; i < n; i++) {
+        if (search.query || sort[i].field !== '$score') {
+          fields.push(sort[i]);
+        }
+      }
+    }
+
+    // the "$score" field is implied to be the primary
+    // sort field, unless it's manually specified
+    if (search.query) {
+      implicit_score = true;
+      for (i = 0, n = fields.length; i < n; i++) {
+        if (fields[i].field === '$score') {
+          implicit_score = false;
+          break;
+        }
+      }
+      if (implicit_score) {
+        fields.unshift({field: '$score', direction: 'desc'});
+      }
+    } else {
+      for (i = 0, n = fields.length; i < n; i++) {
+        if (fields[i].field === '$score') {
+          fields.splice(i, 1);
+          break;
+        }
+      }
+    }
+
+    multipliers = [];
+    for (i = 0, n = fields.length; i < n; i++) {
+      multipliers.push(fields[i].direction === 'desc' ? -1 : 1);
+    }
+
+    // build function
+    fields_count = fields.length;
+    if (!fields_count) {
+      return null;
+    } else if (fields_count === 1) {
+      field = fields[0].field;
+      multiplier = multipliers[0];
+      return function(a, b) {
+        return multiplier * cmp(
+          get_field(field, a),
+          get_field(field, b)
+        );
+      };
+    } else {
+      return function(a, b) {
+        var i, result, a_value, b_value, field;
+        for (i = 0; i < fields_count; i++) {
+          field = fields[i].field;
+          result = multipliers[i] * cmp(
+            get_field(field, a),
+            get_field(field, b)
+          );
+          if (result) return result;
+        }
+        return 0;
+      };
+    }
+  };
+
+  /**
+   * Parses a search query and returns an object
+   * with tokens and fields ready to be populated
+   * with results.
+   *
+   * @param {string} query
+   * @param {object} options
+   * @returns {object}
+   */
+  Sifter.prototype.prepareSearch = function(query, options) {
+    if (typeof query === 'object') return query;
+
+    options = extend({}, options);
+
+    var option_fields     = options.fields;
+    var option_sort       = options.sort;
+    var option_sort_empty = options.sort_empty;
+
+    if (option_fields && !is_array(option_fields)) options.fields = [option_fields];
+    if (option_sort && !is_array(option_sort)) options.sort = [option_sort];
+    if (option_sort_empty && !is_array(option_sort_empty)) options.sort_empty = [option_sort_empty];
+
+    return {
+      options : options,
+      query   : String(query || '').toLowerCase(),
+      tokens  : this.tokenize(query),
+      total   : 0,
+      items   : []
+    };
+  };
+
+  /**
+   * Searches through all items and returns a sorted array of matches.
+   *
+   * The `options` parameter can contain:
+   *
+   *   - fields {string|array}
+   *   - sort {array}
+   *   - score {function}
+   *   - filter {bool}
+   *   - limit {integer}
+   *
+   * Returns an object containing:
+   *
+   *   - options {object}
+   *   - query {string}
+   *   - tokens {array}
+   *   - total {int}
+   *   - items {array}
+   *
+   * @param {string} query
+   * @param {object} options
+   * @returns {object}
+   */
+  Sifter.prototype.search = function(query, options) {
+    var self = this, value, score, search, calculateScore;
+    var fn_sort;
+    var fn_score;
+
+    search  = this.prepareSearch(query, options);
+    options = search.options;
+    query   = search.query;
+
+    // generate result scoring function
+    fn_score = options.score || self.getScoreFunction(search);
+
+    // perform search and sort
+    if (query.length) {
+      self.iterator(self.items, function(item, id) {
+        score = fn_score(item);
+        if (options.filter === false || score > 0) {
+          search.items.push({'score': score, 'id': id});
+        }
+      });
+    } else {
+      self.iterator(self.items, function(item, id) {
+        search.items.push({'score': 1, 'id': id});
+      });
+    }
+
+    fn_sort = self.getSortFunction(search, options);
+    if (fn_sort) search.items.sort(fn_sort);
+
+    // apply limits
+    search.total = search.items.length;
+    if (typeof options.limit === 'number') {
+      search.items = search.items.slice(0, options.limit);
+    }
+
+    return search;
+  };
+
+  // utilities
+  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+  var cmp = function(a, b) {
+    if (typeof a === 'number' && typeof b === 'number') {
+      return a > b ? 1 : (a < b ? -1 : 0);
+    }
+    a = String(a || '').toLowerCase();
+    b = String(b || '').toLowerCase();
+    if (a > b) return 1;
+    if (b > a) return -1;
+    return 0;
+  };
+
+  var extend = function(a, b) {
+    var i, n, k, object;
+    for (i = 1, n = arguments.length; i < n; i++) {
+      object = arguments[i];
+      if (!object) continue;
+      for (k in object) {
+        if (object.hasOwnProperty(k)) {
+          a[k] = object[k];
+        }
+      }
+    }
+    return a;
+  };
+
+  var trim = function(str) {
+    return (str + '').replace(/^\s+|\s+$|/g, '');
+  };
+
+  var escape_regex = function(str) {
+    return (str + '').replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
+  };
+
+  var is_array = Array.isArray || ($ && $.isArray) || function(object) {
+    return Object.prototype.toString.call(object) === '[object Array]';
+  };
+
+  var DIACRITICS = {
+    'a': '[aÀÁÂÃÄÅàáâãäå]',
+    'c': '[cÇçćĆčČ]',
+    'd': '[dđĐďĎ]',
+    'e': '[eÈÉÊËèéêëěĚ]',
+    'i': '[iÌÍÎÏìíîï]',
+    'n': '[nÑñňŇ]',
+    'o': '[oÒÓÔÕÕÖØòóôõöø]',
+    'r': '[rřŘ]',
+    's': '[sŠš]',
+    't': '[tťŤ]',
+    'u': '[uÙÚÛÜùúûüůŮ]',
+    'y': '[yŸÿýÝ]',
+    'z': '[zŽž]'
+  };
+
+  // export
+  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+  return Sifter;
+}));
+
+
+
+/**
+ * microplugin.js
+ * Copyright (c) 2013 Brian Reavis & contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
+ * file except in compliance with the License. You may obtain a copy of the License at:
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
+ * ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ *
+ * @author Brian Reavis <brian@thirdroute.com>
+ */
+
+(function(root, factory) {
+  if (typeof define === 'function' && define.amd) {
+    define('microplugin', factory);
+  } else if (typeof exports === 'object') {
+    module.exports = factory();
+  } else {
+    root.MicroPlugin = factory();
+  }
+}(this, function() {
+  var MicroPlugin = {};
+
+  MicroPlugin.mixin = function(Interface) {
+    Interface.plugins = {};
+
+    /**
+     * Initializes the listed plugins (with options).
+     * Acceptable formats:
+     *
+     * List (without options):
+     *   ['a', 'b', 'c']
+     *
+     * List (with options):
+     *   [{'name': 'a', options: {}}, {'name': 'b', options: {}}]
+     *
+     * Hash (with options):
+     *   {'a': { ... }, 'b': { ... }, 'c': { ... }}
+     *
+     * @param {mixed} plugins
+     */
+    Interface.prototype.initializePlugins = function(plugins) {
+      var i, n, key;
+      var self  = this;
+      var queue = [];
+
+      self.plugins = {
+        names     : [],
+        settings  : {},
+        requested : {},
+        loaded    : {}
+      };
+
+      if (utils.isArray(plugins)) {
+        for (i = 0, n = plugins.length; i < n; i++) {
+          if (typeof plugins[i] === 'string') {
+            queue.push(plugins[i]);
+          } else {
+            self.plugins.settings[plugins[i].name] = plugins[i].options;
+            queue.push(plugins[i].name);
+          }
+        }
+      } else if (plugins) {
+        for (key in plugins) {
+          if (plugins.hasOwnProperty(key)) {
+            self.plugins.settings[key] = plugins[key];
+            queue.push(key);
+          }
+        }
+      }
+
+      while (queue.length) {
+        self.require(queue.shift());
+      }
+    };
+
+    Interface.prototype.loadPlugin = function(name) {
+      var self    = this;
+      var plugins = self.plugins;
+      var plugin  = Interface.plugins[name];
+
+      if (!Interface.plugins.hasOwnProperty(name)) {
+        throw new Error('Unable to find "' +  name + '" plugin');
+      }
+
+      plugins.requested[name] = true;
+      plugins.loaded[name] = plugin.fn.apply(self, [self.plugins.settings[name] || {}]);
+      plugins.names.push(name);
+    };
+
+    /**
+     * Initializes a plugin.
+     *
+     * @param {string} name
+     */
+    Interface.prototype.require = function(name) {
+      var self = this;
+      var plugins = self.plugins;
+
+      if (!self.plugins.loaded.hasOwnProperty(name)) {
+        if (plugins.requested[name]) {
+          throw new Error('Plugin has circular dependency ("' + name + '")');
+        }
+        self.loadPlugin(name);
+      }
+
+      return plugins.loaded[name];
+    };
+
+    /**
+     * Registers a plugin.
+     *
+     * @param {string} name
+     * @param {function} fn
+     */
+    Interface.define = function(name, fn) {
+      Interface.plugins[name] = {
+        'name' : name,
+        'fn'   : fn
+      };
+    };
+  };
+
+  var utils = {
+    isArray: Array.isArray || function(vArg) {
+      return Object.prototype.toString.call(vArg) === '[object Array]';
+    }
+  };
+
+  return MicroPlugin;
+}));
+
+/**
+ * selectize.js (v0.10.1)
+ * Copyright (c) 2013 Brian Reavis & contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
+ * file except in compliance with the License. You may obtain a copy of the License at:
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
+ * ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ *
+ * @author Brian Reavis <brian@thirdroute.com>
+ */
+
+/*jshint curly:false */
+/*jshint browser:true */
+
+(function(root, factory) {
+  if (typeof define === 'function' && define.amd) {
+    define('selectize', ['jquery','sifter','microplugin'], factory);
+  } else if (typeof exports === 'object') {
+    module.exports = factory(require('jquery'), require('sifter'), require('microplugin'));
+  } else {
+    root.Selectize = factory(root.jQuery, root.Sifter, root.MicroPlugin);
+  }
+}(this, function($, Sifter, MicroPlugin) {
+  'use strict';
+
+  var highlight = function($element, pattern) {
+    if (typeof pattern === 'string' && !pattern.length) return;
+    var regex = (typeof pattern === 'string') ? new RegExp(pattern, 'i') : pattern;
+
+    var highlight = function(node) {
+      var skip = 0;
+      if (node.nodeType === 3) {
+        var pos = node.data.search(regex);
+        if (pos >= 0 && node.data.length > 0) {
+          var match = node.data.match(regex);
+          var spannode = document.createElement('span');
+          spannode.className = 'highlight';
+          var middlebit = node.splitText(pos);
+          var endbit = middlebit.splitText(match[0].length);
+          var middleclone = middlebit.cloneNode(true);
+          spannode.appendChild(middleclone);
+          middlebit.parentNode.replaceChild(spannode, middlebit);
+          skip = 1;
+        }
+      } else if (node.nodeType === 1 && node.childNodes && !/(script|style)/i.test(node.tagName)) {
+        for (var i = 0; i < node.childNodes.length; ++i) {
+          i += highlight(node.childNodes[i]);
+        }
+      }
+      return skip;
+    };
+
+    return $element.each(function() {
+      highlight(this);
+    });
+  };
+
+  var MicroEvent = function() {};
+  MicroEvent.prototype = {
+    on: function(event, fct){
+      this._events = this._events || {};
+      this._events[event] = this._events[event] || [];
+      this._events[event].push(fct);
+    },
+    off: function(event, fct){
+      var n = arguments.length;
+      if (n === 0) return delete this._events;
+      if (n === 1) return delete this._events[event];
+
+      this._events = this._events || {};
+      if (event in this._events === false) return;
+      this._events[event].splice(this._events[event].indexOf(fct), 1);
+    },
+    trigger: function(event /* , args... */){
+      this._events = this._events || {};
+      if (event in this._events === false) return;
+      for (var i = 0; i < this._events[event].length; i++){
+        this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1));
+      }
+    }
+  };
+
+  /**
+   * Mixin will delegate all MicroEvent.js function in the destination object.
+   *
+   * - MicroEvent.mixin(Foobar) will make Foobar able to use MicroEvent
+   *
+   * @param {object} the object which will support MicroEvent
+   */
+  MicroEvent.mixin = function(destObject){
+    var props = ['on', 'off', 'trigger'];
+    for (var i = 0; i < props.length; i++){
+      destObject.prototype[props[i]] = MicroEvent.prototype[props[i]];
+    }
+  };
+
+  var IS_MAC        = /Mac/.test(navigator.userAgent);
+
+  var KEY_A         = 65;
+  var KEY_COMMA     = 188;
+  var KEY_RETURN    = 13;
+  var KEY_ESC       = 27;
+  var KEY_LEFT      = 37;
+  var KEY_UP        = 38;
+  var KEY_P         = 80;
+  var KEY_RIGHT     = 39;
+  var KEY_DOWN      = 40;
+  var KEY_N         = 78;
+  var KEY_BACKSPACE = 8;
+  var KEY_DELETE    = 46;
+  var KEY_SHIFT     = 16;
+  var KEY_CMD       = IS_MAC ? 91 : 17;
+  var KEY_CTRL      = IS_MAC ? 18 : 17;
+  var KEY_TAB       = 9;
+
+  var TAG_SELECT    = 1;
+  var TAG_INPUT     = 2;
+
+
+  var isset = function(object) {
+    return typeof object !== 'undefined';
+  };
+
+  /**
+   * Converts a scalar to its best string representation
+   * for hash keys and HTML attribute values.
+   *
+   * Transformations:
+   *   'str'     -> 'str'
+   *   null      -> ''
+   *   undefined -> ''
+   *   true      -> '1'
+   *   false     -> '0'
+   *   0         -> '0'
+   *   1         -> '1'
+   *
+   * @param {string} value
+   * @returns {string}
+   */
+  var hash_key = function(value) {
+    if (typeof value === 'undefined' || value === null) return '';
+    if (typeof value === 'boolean') return value ? '1' : '0';
+    return value + '';
+  };
+
+  /**
+   * Escapes a string for use within HTML.
+   *
+   * @param {string} str
+   * @returns {string}
+   */
+  var escape_html = function(str) {
+    return (str + '')
+      .replace(/&/g, '&amp;')
+      .replace(/</g, '&lt;')
+      .replace(/>/g, '&gt;')
+      .replace(/"/g, '&quot;');
+  };
+
+  /**
+   * Escapes "$" characters in replacement strings.
+   *
+   * @param {string} str
+   * @returns {string}
+   */
+  var escape_replace = function(str) {
+    return (str + '').replace(/\$/g, '$$$$');
+  };
+
+  var hook = {};
+
+  /**
+   * Wraps `method` on `self` so that `fn`
+   * is invoked before the original method.
+   *
+   * @param {object} self
+   * @param {string} method
+   * @param {function} fn
+   */
+  hook.before = function(self, method, fn) {
+    var original = self[method];
+    self[method] = function() {
+      fn.apply(self, arguments);
+      return original.apply(self, arguments);
+    };
+  };
+
+  /**
+   * Wraps `method` on `self` so that `fn`
+   * is invoked after the original method.
+   *
+   * @param {object} self
+   * @param {string} method
+   * @param {function} fn
+   */
+  hook.after = function(self, method, fn) {
+    var original = self[method];
+    self[method] = function() {
+      var result = original.apply(self, arguments);
+      fn.apply(self, arguments);
+      return result;
+    };
+  };
+
+  /**
+   * Builds a hash table out of an array of
+   * objects, using the specified `key` within
+   * each object.
+   *
+   * @param {string} key
+   * @param {mixed} objects
+   */
+  var build_hash_table = function(key, objects) {
+    if (!$.isArray(objects)) return objects;
+    var i, n, table = {};
+    for (i = 0, n = objects.length; i < n; i++) {
+      if (objects[i].hasOwnProperty(key)) {
+        table[objects[i][key]] = objects[i];
+      }
+    }
+    return table;
+  };
+
+  /**
+   * Wraps `fn` so that it can only be invoked once.
+   *
+   * @param {function} fn
+   * @returns {function}
+   */
+  var once = function(fn) {
+    var called = false;
+    return function() {
+      if (called) return;
+      called = true;
+      fn.apply(this, arguments);
+    };
+  };
+
+  /**
+   * Wraps `fn` so that it can only be called once
+   * every `delay` milliseconds (invoked on the falling edge).
+   *
+   * @param {function} fn
+   * @param {int} delay
+   * @returns {function}
+   */
+  var debounce = function(fn, delay) {
+    var timeout;
+    return function() {
+      var self = this;
+      var args = arguments;
+      window.clearTimeout(timeout);
+      timeout = window.setTimeout(function() {
+        fn.apply(self, args);
+      }, delay);
+    };
+  };
+
+  /**
+   * Debounce all fired events types listed in `types`
+   * while executing the provided `fn`.
+   *
+   * @param {object} self
+   * @param {array} types
+   * @param {function} fn
+   */
+  var debounce_events = function(self, types, fn) {
+    var type;
+    var trigger = self.trigger;
+    var event_args = {};
+
+    // override trigger method
+    self.trigger = function() {
+      var type = arguments[0];
+      if (types.indexOf(type) !== -1) {
+        event_args[type] = arguments;
+      } else {
+        return trigger.apply(self, arguments);
+      }
+    };
+
+    // invoke provided function
+    fn.apply(self, []);
+    self.trigger = trigger;
+
+    // trigger queued events
+    for (type in event_args) {
+      if (event_args.hasOwnProperty(type)) {
+        trigger.apply(self, event_args[type]);
+      }
+    }
+  };
+
+  /**
+   * A workaround for http://bugs.jquery.com/ticket/6696
+   *
+   * @param {object} $parent - Parent element to listen on.
+   * @param {string} event - Event name.
+   * @param {string} selector - Descendant selector to filter by.
+   * @param {function} fn - Event handler.
+   */
+  var watchChildEvent = function($parent, event, selector, fn) {
+    $parent.on(event, selector, function(e) {
+      var child = e.target;
+      while (child && child.parentNode !== $parent[0]) {
+        child = child.parentNode;
+      }
+      e.currentTarget = child;
+      return fn.apply(this, [e]);
+    });
+  };
+
+  /**
+   * Determines the current selection within a text input control.
+   * Returns an object containing:
+   *   - start
+   *   - length
+   *
+   * @param {object} input
+   * @returns {object}
+   */
+  var getSelection = function(input) {
+    var result = {};
+    if ('selectionStart' in input) {
+      result.start = input.selectionStart;
+      result.length = input.selectionEnd - result.start;
+    } else if (document.selection) {
+      input.focus();
+      var sel = document.selection.createRange();
+      var selLen = document.selection.createRange().text.length;
+      sel.moveStart('character', -input.value.length);
+      result.start = sel.text.length - selLen;
+      result.length = selLen;
+    }
+    return result;
+  };
+
+  /**
+   * Copies CSS properties from one element to another.
+   *
+   * @param {object} $from
+   * @param {object} $to
+   * @param {array} properties
+   */
+  var transferStyles = function($from, $to, properties) {
+    var i, n, styles = {};
+    if (properties) {
+      for (i = 0, n = properties.length; i < n; i++) {
+        styles[properties[i]] = $from.css(properties[i]);
+      }
+    } else {
+      styles = $from.css();
+    }
+    $to.css(styles);
+  };
+
+  /**
+   * Measures the width of a string within a
+   * parent element (in pixels).
+   *
+   * @param {string} str
+   * @param {object} $parent
+   * @returns {int}
+   */
+  var measureString = function(str, $parent) {
+    if (!str) {
+      return 0;
+    }
+
+    var $test = $('<test>').css({
+      position: 'absolute',
+      top: -99999,
+      left: -99999,
+      width: 'auto',
+      padding: 0,
+      whiteSpace: 'pre'
+    }).text(str).appendTo('body');
+
+    transferStyles($parent, $test, [
+      'letterSpacing',
+      'fontSize',
+      'fontFamily',
+      'fontWeight',
+      'textTransform'
+    ]);
+
+    var width = $test.width();
+    $test.remove();
+
+    return width;
+  };
+
+  /**
+   * Sets up an input to grow horizontally as the user
+   * types. If the value is changed manually, you can
+   * trigger the "update" handler to resize:
+   *
+   * $input.trigger('update');
+   *
+   * @param {object} $input
+   */
+  var autoGrow = function($input) {
+    var currentWidth = null;
+
+    var update = function(e, options) {
+      var value, keyCode, printable, placeholder, width;
+      var shift, character, selection;
+      e = e || window.event || {};
+      options = options || {};
+
+      if (e.metaKey || e.altKey) return;
+      if (!options.force && $input.data('grow') === false) return;
+
+      value = $input.val();
+      if (e.type && e.type.toLowerCase() === 'keydown') {
+        keyCode = e.keyCode;
+        printable = (
+          (keyCode >= 97 && keyCode <= 122) || // a-z
+          (keyCode >= 65 && keyCode <= 90)  || // A-Z
+          (keyCode >= 48 && keyCode <= 57)  || // 0-9
+          keyCode === 32 // space
+        );
+
+        if (keyCode === KEY_DELETE || keyCode === KEY_BACKSPACE) {
+          selection = getSelection($input[0]);
+          if (selection.length) {
+            value = value.substring(0, selection.start) + value.substring(selection.start + selection.length);
+          } else if (keyCode === KEY_BACKSPACE && selection.start) {
+            value = value.substring(0, selection.start - 1) + value.substring(selection.start + 1);
+          } else if (keyCode === KEY_DELETE && typeof selection.start !== 'undefined') {
+            value = value.substring(0, selection.start) + value.substring(selection.start + 1);
+          }
+        } else if (printable) {
+          shift = e.shiftKey;
+          character = String.fromCharCode(e.keyCode);
+          if (shift) character = character.toUpperCase();
+          else character = character.toLowerCase();
+          value += character;
+        }
+      }
+
+      placeholder = $input.attr('placeholder');
+      if (!value && placeholder) {
+        value = placeholder;
+      }
+
+      width = measureString(value, $input) + 4;
+      if (width !== currentWidth) {
+        currentWidth = width;
+        $input.width(width);
+        $input.triggerHandler('resize');
+      }
+    };
+
+    $input.on('keydown keyup update blur', update);
+    update();
+  };
+
+  var Selectize = function($input, settings) {
+    var key, i, n, dir, input, self = this;
+    input = $input[0];
+    input.selectize = self;
+
+    // detect rtl environment
+    dir = window.getComputedStyle ? window.getComputedStyle(input, null).getPropertyValue('direction') : input.currentStyle && input.currentStyle.direction;
+    dir = dir || $input.parents('[dir]:first').attr('dir') || '';
+
+    // setup default state
+    $.extend(self, {
+      settings         : settings,
+      $input           : $input,
+      tagType          : input.tagName.toLowerCase() === 'select' ? TAG_SELECT : TAG_INPUT,
+      rtl              : /rtl/i.test(dir),
+
+      eventNS          : '.selectize' + (++Selectize.count),
+      highlightedValue : null,
+      isOpen           : false,
+      isDisabled       : false,
+      isRequired       : $input.is('[required]'),
+      isInvalid        : false,
+      isLocked         : false,
+      isFocused        : false,
+      isInputHidden    : false,
+      isSetup          : false,
+      isShiftDown      : false,
+      isCmdDown        : false,
+      isCtrlDown       : false,
+      ignoreFocus      : false,
+      ignoreBlur       : false,
+      ignoreHover      : false,
+      hasOptions       : false,
+      currentResults   : null,
+      lastValue        : '',
+      caretPos         : 0,
+      loading          : 0,
+      loadedSearches   : {},
+
+      $activeOption    : null,
+      $activeItems     : [],
+
+      optgroups        : {},
+      options          : {},
+      userOptions      : {},
+      items            : [],
+      renderCache      : {},
+      onSearchChange   : settings.loadThrottle === null ? self.onSearchChange : debounce(self.onSearchChange, settings.loadThrottle)
+    });
+
+    // search system
+    self.sifter = new Sifter(this.options, {diacritics: settings.diacritics});
+
+    // build options table
+    $.extend(self.options, build_hash_table(settings.valueField, settings.options));
+    delete self.settings.options;
+
+    // build optgroup table
+    $.extend(self.optgroups, build_hash_table(settings.optgroupValueField, settings.optgroups));
+    delete self.settings.optgroups;
+
+    // option-dependent defaults
+    self.settings.mode = self.settings.mode || (self.settings.maxItems === 1 ? 'single' : 'multi');
+    if (typeof self.settings.hideSelected !== 'boolean') {
+      self.settings.hideSelected = self.settings.mode === 'multi';
+    }
+
+    if (self.settings.create) {
+      self.canCreate = function(input) {
+        var filter = self.settings.createFilter;
+        return input.length
+          && (typeof filter !== 'function' || filter.apply(self, [input]))
+          && (typeof filter !== 'string' || new RegExp(filter).test(input))
+          && (!(filter instanceof RegExp) || filter.test(input));
+      };
+    }
+
+    self.initializePlugins(self.settings.plugins);
+    self.setupCallbacks();
+    self.setupTemplates();
+    self.setup();
+  };
+
+  // mixins
+  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+  MicroEvent.mixin(Selectize);
+  MicroPlugin.mixin(Selectize);
+
+  // methods
+  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+  $.extend(Selectize.prototype, {
+
+    /**
+     * Creates all elements and sets up event bindings.
+     */
+    setup: function() {
+      var self      = this;
+      var settings  = self.settings;
+      var eventNS   = self.eventNS;
+      var $window   = $(window);
+      var $document = $(document);
+      var $input    = self.$input;
+
+      var $wrapper;
+      var $control;
+      var $control_input;
+      var $dropdown;
+      var $dropdown_content;
+      var $dropdown_parent;
+      var inputMode;
+      var timeout_blur;
+      var timeout_focus;
+      var tab_index;
+      var classes;
+      var classes_plugins;
+
+      inputMode         = self.settings.mode;
+      tab_index         = $input.attr('tabindex') || '';
+      classes           = $input.attr('class') || '';
+
+      $wrapper          = $('<div>').addClass(settings.wrapperClass).addClass(classes).addClass(inputMode);
+      $control          = $('<div>').addClass(settings.inputClass).addClass('items').appendTo($wrapper);
+      $control_input    = $('<input type="text" autocomplete="off" />').appendTo($control).attr('tabindex', tab_index);
+      $dropdown_parent  = $(settings.dropdownParent || $wrapper);
+      $dropdown         = $('<div>').addClass(settings.dropdownClass).addClass(classes).addClass(inputMode).hide().appendTo($dropdown_parent);
+      $dropdown_content = $('<div>').addClass(settings.dropdownContentClass).appendTo($dropdown);
+
+      $wrapper.css({
+        width: $input[0].style.width
+      });
+
+      if (self.plugins.names.length) {
+        classes_plugins = 'plugin-' + self.plugins.names.join(' plugin-');
+        $wrapper.addClass(classes_plugins);
+        $dropdown.addClass(classes_plugins);
+      }
+
+      if ((settings.maxItems === null || settings.maxItems > 1) && self.tagType === TAG_SELECT) {
+        $input.attr('multiple', 'multiple');
+      }
+
+      if (self.settings.placeholder) {
+        $control_input.attr('placeholder', settings.placeholder);
+      }
+
+      if ($input.attr('autocorrect')) {
+        $control_input.attr('autocorrect', $input.attr('autocorrect'));
+      }
+
+      if ($input.attr('autocapitalize')) {
+        $control_input.attr('autocapitalize', $input.attr('autocapitalize'));
+      }
+
+      self.$wrapper          = $wrapper;
+      self.$control          = $control;
+      self.$control_input    = $control_input;
+      self.$dropdown         = $dropdown;
+      self.$dropdown_content = $dropdown_content;
+
+      $dropdown.on('mouseenter', '[data-selectable]', function() { return self.onOptionHover.apply(self, arguments); });
+      $dropdown.on('mousedown', '[data-selectable]', function() { return self.onOptionSelect.apply(self, arguments); });
+      watchChildEvent($control, 'mousedown', '*:not(input)', function() { return self.onItemSelect.apply(self, arguments); });
+      autoGrow($control_input);
+
+      $control.on({
+        mousedown : function() { return self.onMouseDown.apply(self, arguments); },
+        click     : function() { return self.onClick.apply(self, arguments); }
+      });
+
+      $control_input.on({
+        mousedown : function(e) { e.stopPropagation(); },
+        keydown   : function() { return self.onKeyDown.apply(self, arguments); },
+        keyup     : function() { return self.onKeyUp.apply(self, arguments); },
+        keypress  : function() { return self.onKeyPress.apply(self, arguments); },
+        resize    : function() { self.positionDropdown.apply(self, []); },
+        blur      : function() { return self.onBlur.apply(self, arguments); },
+        focus     : function() { self.ignoreBlur = false; return self.onFocus.apply(self, arguments); },
+        paste     : function() { return self.onPaste.apply(self, arguments); }
+      });
+
+      $document.on('keydown' + eventNS, function(e) {
+        self.isCmdDown = e[IS_MAC ? 'metaKey' : 'ctrlKey'];
+        self.isCtrlDown = e[IS_MAC ? 'altKey' : 'ctrlKey'];
+        self.isShiftDown = e.shiftKey;
+      });
+
+      $document.on('keyup' + eventNS, function(e) {
+        if (e.keyCode === KEY_CTRL) self.isCtrlDown = false;
+        if (e.keyCode === KEY_SHIFT) self.isShiftDown = false;
+        if (e.keyCode === KEY_CMD) self.isCmdDown = false;
+      });
+
+      $document.on('mousedown' + eventNS, function(e) {
+        if (self.isFocused) {
+          // prevent events on the dropdown scrollbar from causing the control to blur
+          if (e.target === self.$dropdown[0] || e.target.parentNode === self.$dropdown[0]) {
+            return false;
+          }
+          // blur on click outside
+          if (!self.$control.has(e.target).length && e.target !== self.$control[0]) {
+            self.blur();
+          }
+        }
+      });
+
+      $window.on(['scroll' + eventNS, 'resize' + eventNS].join(' '), function() {
+        if (self.isOpen) {
+          self.positionDropdown.apply(self, arguments);
+        }
+      });
+      $window.on('mousemove' + eventNS, function() {
+        self.ignoreHover = false;
+      });
+
+      // store original children and tab index so that they can be
+      // restored when the destroy() method is called.
+      this.revertSettings = {
+        $children : $input.children().detach(),
+        tabindex  : $input.attr('tabindex')
+      };
+
+      $input.attr('tabindex', -1).hide().after(self.$wrapper);
+
+      if ($.isArray(settings.items)) {
+        self.setValue(settings.items);
+        delete settings.items;
+      }
+
+      // feature detect for the validation API
+      if ($input[0].validity) {
+        $input.on('invalid' + eventNS, function(e) {
+          e.preventDefault();
+          self.isInvalid = true;
+          self.refreshState();
+        });
+      }
+
+      self.updateOriginalInput();
+      self.refreshItems();
+      self.refreshState();
+      self.updatePlaceholder();
+      self.isSetup = true;
+
+      if ($input.is(':disabled')) {
+        self.disable();
+      }
+
+      self.on('change', this.onChange);
+
+      $input.data('selectize', self);
+      $input.addClass('selectized');
+      self.trigger('initialize');
+
+      // preload options
+      if (settings.preload === true) {
+        self.onSearchChange('');
+      }
+
+    },
+
+    /**
+     * Sets up default rendering functions.
+     */
+    setupTemplates: function() {
+      var self = this;
+      var field_label = self.settings.labelField;
+      var field_optgroup = self.settings.optgroupLabelField;
+
+      var templates = {
+        'optgroup': function(data) {
+          return '<div class="optgroup">' + data.html + '</div>';
+        },
+        'optgroup_header': function(data, escape) {
+          return '<div class="optgroup-header">' + escape(data[field_optgroup]) + '</div>';
+        },
+        'option': function(data, escape) {
+          return '<div class="option">' + escape(data[field_label]) + '</div>';
+        },
+        'item': function(data, escape) {
+          return '<div class="item">' + escape(data[field_label]) + '</div>';
+        },
+        'option_create': function(data, escape) {
+          return '<div class="create">Add <strong>' + escape(data.input) + '</strong>&hellip;</div>';
+        }
+      };
+
+      self.settings.render = $.extend({}, templates, self.settings.render);
+    },
+
+    /**
+     * Maps fired events to callbacks provided
+     * in the settings used when creating the control.
+     */
+    setupCallbacks: function() {
+      var key, fn, callbacks = {
+        'initialize'     : 'onInitialize',
+        'change'         : 'onChange',
+        'item_add'       : 'onItemAdd',
+        'item_remove'    : 'onItemRemove',
+        'clear'          : 'onClear',
+        'option_add'     : 'onOptionAdd',
+        'option_remove'  : 'onOptionRemove',
+        'option_clear'   : 'onOptionClear',
+        'dropdown_open'  : 'onDropdownOpen',
+        'dropdown_close' : 'onDropdownClose',
+        'type'           : 'onType'
+      };
+
+      for (key in callbacks) {
+        if (callbacks.hasOwnProperty(key)) {
+          fn = this.settings[callbacks[key]];
+          if (fn) this.on(key, fn);
+        }
+      }
+    },
+
+    /**
+     * Triggered when the main control element
+     * has a click event.
+     *
+     * @param {object} e
+     * @return {boolean}
+     */
+    onClick: function(e) {
+      var self = this;
+
+      // necessary for mobile webkit devices (manual focus triggering
+      // is ignored unless invoked within a click event)
+      if (!self.isFocused) {
+        self.focus();
+        e.preventDefault();
+      }
+    },
+
+    /**
+     * Triggered when the main control element
+     * has a mouse down event.
+     *
+     * @param {object} e
+     * @return {boolean}
+     */
+    onMouseDown: function(e) {
+      var self = this;
+      var defaultPrevented = e.isDefaultPrevented();
+      var $target = $(e.target);
+
+      if (self.isFocused) {
+        // retain focus by preventing native handling. if the
+        // event target is the input it should not be modified.
+        // otherwise, text selection within the input won't work.
+        if (e.target !== self.$control_input[0]) {
+          if (self.settings.mode === 'single') {
+            // toggle dropdown
+            self.isOpen ? self.close() : self.open();
+          } else if (!defaultPrevented) {
+            self.setActiveItem(null);
+          }
+          return false;
+        }
+      } else {
+        // give control focus
+        if (!defaultPrevented) {
+          window.setTimeout(function() {
+            self.focus();
+          }, 0);
+        }
+      }
+    },
+
+    /**
+     * Triggered when the value of the control has been changed.
+     * This should propagate the event to the original DOM
+     * input / select element.
+     */
+    onChange: function() {
+      this.$input.trigger('change');
+    },
+
+
+    /**
+     * Triggered on <input> paste.
+     *
+     * @param {object} e
+     * @returns {boolean}
+     */
+    onPaste: function(e) {
+      var self = this;
+      if (self.isFull() || self.isInputHidden || self.isLocked) {
+        e.preventDefault();
+      }
+    },
+
+    /**
+     * Triggered on <input> keypress.
+     *
+     * @param {object} e
+     * @returns {boolean}
+     */
+    onKeyPress: function(e) {
+      if (this.isLocked) return e && e.preventDefault();
+      var character = String.fromCharCode(e.keyCode || e.which);
+      if (this.settings.create && character === this.settings.delimiter) {
+        this.createItem();
+        e.preventDefault();
+        return false;
+      }
+    },
+
+    /**
+     * Triggered on <input> keydown.
+     *
+     * @param {object} e
+     * @returns {boolean}
+     */
+    onKeyDown: function(e) {
+      var isInput = e.target === this.$control_input[0];
+      var self = this;
+
+      if (self.isLocked) {
+        if (e.keyCode !== KEY_TAB) {
+          e.preventDefault();
+        }
+        return;
+      }
+
+      switch (e.keyCode) {
+        case KEY_A:
+          if (self.isCmdDown) {
+            self.selectAll();
+            return;
+          }
+          break;
+        case KEY_ESC:
+          self.close();
+          return;
+        case KEY_N:
+          if (!e.ctrlKey || e.altKey) break;
+        case KEY_DOWN:
+          if (!self.isOpen && self.hasOptions) {
+            self.open();
+          } else if (self.$activeOption) {
+            self.ignoreHover = true;
+            var $next = self.getAdjacentOption(self.$activeOption, 1);
+            if ($next.length) self.setActiveOption($next, true, true);
+          }
+          e.preventDefault();
+          return;
+        case KEY_P:
+          if (!e.ctrlKey || e.altKey) break;
+        case KEY_UP:
+          if (self.$activeOption) {
+            self.ignoreHover = true;
+            var $prev = self.getAdjacentOption(self.$activeOption, -1);
+            if ($prev.length) self.setActiveOption($prev, true, true);
+          }
+          e.preventDefault();
+          return;
+        case KEY_RETURN:
+          if (self.isOpen && self.$activeOption) {
+            self.onOptionSelect({currentTarget: self.$activeOption});
+          }
+          e.preventDefault();
+          return;
+        case KEY_LEFT:
+          self.advanceSelection(-1, e);
+          return;
+        case KEY_RIGHT:
+          self.advanceSelection(1, e);
+          return;
+        case KEY_TAB:
+          if (self.settings.selectOnTab && self.isOpen && self.$activeOption) {
+            self.onOptionSelect({currentTarget: self.$activeOption});
+            e.preventDefault();
+          }
+          if (self.settings.create && self.createItem()) {
+            e.preventDefault();
+          }
+          return;
+        case KEY_BACKSPACE:
+        case KEY_DELETE:
+          self.deleteSelection(e);
+          return;
+      }
+
+      if ((self.isFull() || self.isInputHidden) && !(IS_MAC ? e.metaKey : e.ctrlKey)) {
+        e.preventDefault();
+        return;
+      }
+    },
+
+    /**
+     * Triggered on <input> keyup.
+     *
+     * @param {object} e
+     * @returns {boolean}
+     */
+    onKeyUp: function(e) {
+      var self = this;
+
+      if (self.isLocked) return e && e.preventDefault();
+      var value = self.$control_input.val() || '';
+      if (self.lastValue !== value) {
+        self.lastValue = value;
+        self.onSearchChange(value);
+        self.refreshOptions();
+        self.trigger('type', value);
+      }
+    },
+
+    /**
+     * Invokes the user-provide option provider / loader.
+     *
+     * Note: this function is debounced in the Selectize
+     * constructor (by `settings.loadDelay` milliseconds)
+     *
+     * @param {string} value
+     */
+    onSearchChange: function(value) {
+      var self = this;
+      var fn = self.settings.load;
+      if (!fn) return;
+      if (self.loadedSearches.hasOwnProperty(value)) return;
+      self.loadedSearches[value] = true;
+      self.load(function(callback) {
+        fn.apply(self, [value, callback]);
+      });
+    },
+
+    /**
+     * Triggered on <input> focus.
+     *
+     * @param {object} e (optional)
+     * @returns {boolean}
+     */
+    onFocus: function(e) {
+      var self = this;
+
+      self.isFocused = true;
+      if (self.isDisabled) {
+        self.blur();
+        e && e.preventDefault();
+        return false;
+      }
+
+      if (self.ignoreFocus) return;
+      if (self.settings.preload === 'focus') self.onSearchChange('');
+
+      if (!self.$activeItems.length) {
+        self.showInput();
+        self.setActiveItem(null);
+        self.refreshOptions(!!self.settings.openOnFocus);
+      }
+
+      self.refreshState();
+    },
+
+    /**
+     * Triggered on <input> blur.
+     *
+     * @param {object} e
+     * @returns {boolean}
+     */
+    onBlur: function(e) {
+      var self = this;
+      self.isFocused = false;
+      if (self.ignoreFocus) return;
+
+      // necessary to prevent IE closing the dropdown when the scrollbar is clicked
+      if (!self.ignoreBlur && document.activeElement === self.$dropdown_content[0]) {
+        self.ignoreBlur = true;
+        self.onFocus(e);
+
+        return;
+      }
+
+      if (self.settings.create && self.settings.createOnBlur) {
+        self.createItem(false);
+      }
+
+      self.close();
+      self.setTextboxValue('');
+      self.setActiveItem(null);
+      self.setActiveOption(null);
+      self.setCaret(self.items.length);
+      self.refreshState();
+    },
+
+    /**
+     * Triggered when the user rolls over
+     * an option in the autocomplete dropdown menu.
+     *
+     * @param {object} e
+     * @returns {boolean}
+     */
+    onOptionHover: function(e) {
+      if (this.ignoreHover) return;
+      this.setActiveOption(e.currentTarget, false);
+    },
+
+    /**
+     * Triggered when the user clicks on an option
+     * in the autocomplete dropdown menu.
+     *
+     * @param {object} e
+     * @returns {boolean}
+     */
+    onOptionSelect: function(e) {
+      var value, $target, $option, self = this;
+
+      if (e.preventDefault) {
+        e.preventDefault();
+        e.stopPropagation();
+      }
+
+      $target = $(e.currentTarget);
+      if ($target.hasClass('create')) {
+        self.createItem();
+      } else {
+        value = $target.attr('data-value');
+        if (value) {
+          self.lastQuery = null;
+          self.setTextboxValue('');
+          self.addItem(value);
+          if (!self.settings.hideSelected && e.type && /mouse/.test(e.type)) {
+            self.setActiveOption(self.getOption(value));
+          }
+        }
+      }
+    },
+
+    /**
+     * Triggered when the user clicks on an item
+     * that has been selected.
+     *
+     * @param {object} e
+     * @returns {boolean}
+     */
+    onItemSelect: function(e) {
+      var self = this;
+
+      if (self.isLocked) return;
+      if (self.settings.mode === 'multi') {
+        e.preventDefault();
+        self.setActiveItem(e.currentTarget, e);
+      }
+    },
+
+    /**
+     * Invokes the provided method that provides
+     * results to a callback---which are then added
+     * as options to the control.
+     *
+     * @param {function} fn
+     */
+    load: function(fn) {
+      var self = this;
+      var $wrapper = self.$wrapper.addClass('loading');
+
+      self.loading++;
+      fn.apply(self, [function(results) {
+        self.loading = Math.max(self.loading - 1, 0);
+        if (results && results.length) {
+          self.addOption(results);
+          self.refreshOptions(self.isFocused && !self.isInputHidden);
+        }
+        if (!self.loading) {
+          $wrapper.removeClass('loading');
+        }
+        self.trigger('load', results);
+      }]);
+    },
+
+    /**
+     * Sets the input field of the control to the specified value.
+     *
+     * @param {string} value
+     */
+    setTextboxValue: function(value) {
+      var $input = this.$control_input;
+      var changed = $input.val() !== value;
+      if (changed) {
+        $input.val(value).triggerHandler('update');
+        this.lastValue = value;
+      }
+    },
+
+    /**
+     * Returns the value of the control. If multiple items
+     * can be selected (e.g. <select multiple>), this returns
+     * an array. If only one item can be selected, this
+     * returns a string.
+     *
+     * @returns {mixed}
+     */
+    getValue: function() {
+      if (this.tagType === TAG_SELECT && this.$input.attr('multiple')) {
+        return this.items;
+      } else {
+        return this.items.join(this.settings.delimiter);
+      }
+    },
+
+    /**
+     * Resets the selected items to the given value.
+     *
+     * @param {mixed} value
+     */
+    setValue: function(value) {
+      debounce_events(this, ['change'], function() {
+        this.clear();
+        this.addItems(value);
+      });
+    },
+
+    /**
+     * Sets the selected item.
+     *
+     * @param {object} $item
+     * @param {object} e (optional)
+     */
+    setActiveItem: function($item, e) {
+      var self = this;
+      var eventName;
+      var i, idx, begin, end, item, swap;
+      var $last;
+
+      if (self.settings.mode === 'single') return;
+      $item = $($item);
+
+      // clear the active selection
+      if (!$item.length) {
+        $(self.$activeItems).removeClass('active');
+        self.$activeItems = [];
+        if (self.isFocused) {
+          self.showInput();
+        }
+        return;
+      }
+
+      // modify selection
+      eventName = e && e.type.toLowerCase();
+
+      if (eventName === 'mousedown' && self.isShiftDown && self.$activeItems.length) {
+        $last = self.$control.children('.active:last');
+        begin = Array.prototype.indexOf.apply(self.$control[0].childNodes, [$last[0]]);
+        end   = Array.prototype.indexOf.apply(self.$control[0].childNodes, [$item[0]]);
+        if (begin > end) {
+          swap  = begin;
+          begin = end;
+          end   = swap;
+        }
+        for (i = begin; i <= end; i++) {
+          item = self.$control[0].childNodes[i];
+          if (self.$activeItems.indexOf(item) === -1) {
+            $(item).addClass('active');
+            self.$activeItems.push(item);
+          }
+        }
+        e.preventDefault();
+      } else if ((eventName === 'mousedown' && self.isCtrlDown) || (eventName === 'keydown' && this.isShiftDown)) {
+        if ($item.hasClass('active')) {
+          idx = self.$activeItems.indexOf($item[0]);
+          self.$activeItems.splice(idx, 1);
+          $item.removeClass('active');
+        } else {
+          self.$activeItems.push($item.addClass('active')[0]);
+        }
+      } else {
+        $(self.$activeItems).removeClass('active');
+        self.$activeItems = [$item.addClass('active')[0]];
+      }
+
+      // ensure control has focus
+      self.hideInput();
+      if (!this.isFocused) {
+        self.focus();
+      }
+    },
+
+    /**
+     * Sets the selected item in the dropdown menu
+     * of available options.
+     *
+     * @param {object} $object
+     * @param {boolean} scroll
+     * @param {boolean} animate
+     */
+    setActiveOption: function($option, scroll, animate) {
+      var height_menu, height_item, y;
+      var scroll_top, scroll_bottom;
+      var self = this;
+
+      if (self.$activeOption) self.$activeOption.removeClass('active');
+      self.$activeOption = null;
+
+      $option = $($option);
+      if (!$option.length) return;
+
+      self.$activeOption = $option.addClass('active');
+
+      if (scroll || !isset(scroll)) {
+
+        height_menu   = self.$dropdown_content.height();
+        height_item   = self.$activeOption.outerHeight(true);
+        scroll        = self.$dropdown_content.scrollTop() || 0;
+        y             = self.$activeOption.offset().top - self.$dropdown_content.offset().top + scroll;
+        scroll_top    = y;
+        scroll_bottom = y - height_menu + height_item;
+
+        if (y + height_item > height_menu + scroll) {
+          self.$dropdown_content.stop().animate({scrollTop: scroll_bottom}, animate ? self.settings.scrollDuration : 0);
+        } else if (y < scroll) {
+          self.$dropdown_content.stop().animate({scrollTop: scroll_top}, animate ? self.settings.scrollDuration : 0);
+        }
+
+      }
+    },
+
+    /**
+     * Selects all items (CTRL + A).
+     */
+    selectAll: function() {
+      var self = this;
+      if (self.settings.mode === 'single') return;
+
+      self.$activeItems = Array.prototype.slice.apply(self.$control.children(':not(input)').addClass('active'));
+      if (self.$activeItems.length) {
+        self.hideInput();
+        self.close();
+      }
+      self.focus();
+    },
+
+    /**
+     * Hides the input element out of view, while
+     * retaining its focus.
+     */
+    hideInput: function() {
+      var self = this;
+
+      self.setTextboxValue('');
+      self.$control_input.css({opacity: 0, position: 'absolute', left: self.rtl ? 10000 : -10000});
+      self.isInputHidden = true;
+    },
+
+    /**
+     * Restores input visibility.
+     */
+    showInput: function() {
+      this.$control_input.css({opacity: 1, position: 'relative', left: 0});
+      this.isInputHidden = false;
+    },
+
+    /**
+     * Gives the control focus. If "trigger" is falsy,
+     * focus handlers won't be fired--causing the focus
+     * to happen silently in the background.
+     *
+     * @param {boolean} trigger
+     */
+    focus: function() {
+      var self = this;
+      if (self.isDisabled) return;
+
+      self.ignoreFocus = true;
+      self.$control_input[0].focus();
+      window.setTimeout(function() {
+        self.ignoreFocus = false;
+        self.onFocus();
+      }, 0);
+    },
+
+    /**
+     * Forces the control out of focus.
+     */
+    blur: function() {
+      this.$control_input.trigger('blur');
+    },
+
+    /**
+     * Returns a function that scores an object
+     * to show how good of a match it is to the
+     * provided query.
+     *
+     * @param {string} query
+     * @param {object} options
+     * @return {function}
+     */
+    getScoreFunction: function(query) {
+      return this.sifter.getScoreFunction(query, this.getSearchOptions());
+    },
+
+    /**
+     * Returns search options for sifter (the system
+     * for scoring and sorting results).
+     *
+     * @see https://github.com/brianreavis/sifter.js
+     * @return {object}
+     */
+    getSearchOptions: function() {
+      var settings = this.settings;
+      var sort = settings.sortField;
+      if (typeof sort === 'string') {
+        sort = {field: sort};
+      }
+
+      return {
+        fields      : settings.searchField,
+        conjunction : settings.searchConjunction,
+        sort        : sort
+      };
+    },
+
+    /**
+     * Searches through available options and returns
+     * a sorted array of matches.
+     *
+     * Returns an object containing:
+     *
+     *   - query {string}
+     *   - tokens {array}
+     *   - total {int}
+     *   - items {array}
+     *
+     * @param {string} query
+     * @returns {object}
+     */
+    search: function(query) {
+      var i, value, score, result, calculateScore;
+      var self     = this;
+      var settings = self.settings;
+      var options  = this.getSearchOptions();
+
+      // validate user-provided result scoring function
+      if (settings.score) {
+        calculateScore = self.settings.score.apply(this, [query]);
+        if (typeof calculateScore !== 'function') {
+          throw new Error('Selectize "score" setting must be a function that returns a function');
+        }
+      }
+
+      // perform search
+      if (query !== self.lastQuery) {
+        self.lastQuery = query;
+        result = self.sifter.search(query, $.extend(options, {score: calculateScore}));
+        self.currentResults = result;
+      } else {
+        result = $.extend(true, {}, self.currentResults);
+      }
+
+      // filter out selected items
+      if (settings.hideSelected) {
+        for (i = result.items.length - 1; i >= 0; i--) {
+          if (self.items.indexOf(hash_key(result.items[i].id)) !== -1) {
+            result.items.splice(i, 1);
+          }
+        }
+      }
+
+      return result;
+    },
+
+    /**
+     * Refreshes the list of available options shown
+     * in the autocomplete dropdown menu.
+     *
+     * @param {boolean} triggerDropdown
+     */
+    refreshOptions: function(triggerDropdown) {
+      var i, j, k, n, groups, groups_order, option, option_html, optgroup, optgroups, html, html_children, has_create_option;
+      var $active, $active_before, $create;
+
+      if (typeof triggerDropdown === 'undefined') {
+        triggerDropdown = true;
+      }
+
+      var self              = this;
+      var query             = self.$control_input.val();
+      var results           = self.search(query);
+      var $dropdown_content = self.$dropdown_content;
+      var active_before     = self.$activeOption && hash_key(self.$activeOption.attr('data-value'));
+
+      // build markup
+      n = results.items.length;
+      if (typeof self.settings.maxOptions === 'number') {
+        n = Math.min(n, self.settings.maxOptions);
+      }
+
+      // render and group available options individually
+      groups = {};
+
+      if (self.settings.optgroupOrder) {
+        groups_order = self.settings.optgroupOrder;
+        for (i = 0; i < groups_order.length; i++) {
+          groups[groups_order[i]] = [];
+        }
+      } else {
+        groups_order = [];
+      }
+
+      for (i = 0; i < n; i++) {
+        option      = self.options[results.items[i].id];
+        option_html = self.render('option', option);
+        optgroup    = option[self.settings.optgroupField] || '';
+        optgroups   = $.isArray(optgroup) ? optgroup : [optgroup];
+
+        for (j = 0, k = optgroups && optgroups.length; j < k; j++) {
+          optgroup = optgroups[j];
+          if (!self.optgroups.hasOwnProperty(optgroup)) {
+            optgroup = '';
+          }
+          if (!groups.hasOwnProperty(optgroup)) {
+            groups[optgroup] = [];
+            groups_order.push(optgroup);
+          }
+          groups[optgroup].push(option_html);
+        }
+      }
+
+      // render optgroup headers & join groups
+      html = [];
+      for (i = 0, n = groups_order.length; i < n; i++) {
+        optgroup = groups_order[i];
+        if (self.optgroups.hasOwnProperty(optgroup) && groups[optgroup].length) {
+          // render the optgroup header and options within it,
+          // then pass it to the wrapper template
+          html_children = self.render('optgroup_header', self.optgroups[optgroup]) || '';
+          html_children += groups[optgroup].join('');
+          html.push(self.render('optgroup', $.extend({}, self.optgroups[optgroup], {
+            html: html_children
+          })));
+        } else {
+          html.push(groups[optgroup].join(''));
+        }
+      }
+
+      $dropdown_content.html(html.join(''));
+
+      // highlight matching terms inline
+      if (self.settings.highlight && results.query.length && results.tokens.length) {
+        for (i = 0, n = results.tokens.length; i < n; i++) {
+          highlight($dropdown_content, results.tokens[i].regex);
+        }
+      }
+
+      // add "selected" class to selected options
+      if (!self.settings.hideSelected) {
+        for (i = 0, n = self.items.length; i < n; i++) {
+          self.getOption(self.items[i]).addClass('selected');
+        }
+      }
+
+      // add create option
+      has_create_option = self.settings.create && self.canCreate(results.query);
+      if (has_create_option) {
+        $dropdown_content.prepend(self.render('option_create', {input: query}));
+        $create = $($dropdown_content[0].childNodes[0]);
+      }
+
+      // activate
+      self.hasOptions = results.items.length > 0 || has_create_option;
+      if (self.hasOptions) {
+        if (results.items.length > 0) {
+          $active_before = active_before && self.getOption(active_before);
+          if ($active_before && $active_before.length) {
+            $active = $active_before;
+          } else if (self.settings.mode === 'single' && self.items.length) {
+            $active = self.getOption(self.items[0]);
+          }
+          if (!$active || !$active.length) {
+            if ($create && !self.settings.addPrecedence) {
+              $active = self.getAdjacentOption($create, 1);
+            } else {
+              $active = $dropdown_content.find('[data-selectable]:first');
+            }
+          }
+        } else {
+          $active = $create;
+        }
+        self.setActiveOption($active);
+        if (triggerDropdown && !self.isOpen) { self.open(); }
+      } else {
+        self.setActiveOption(null);
+        if (triggerDropdown && self.isOpen) { self.close(); }
+      }
+    },
+
+    /**
+     * Adds an available option. If it already exists,
+     * nothing will happen. Note: this does not refresh
+     * the options list dropdown (use `refreshOptions`
+     * for that).
+     *
+     * Usage:
+     *
+     *   this.addOption(data)
+     *
+     * @param {object} data
+     */
+    addOption: function(data) {
+      var i, n, optgroup, value, self = this;
+
+      if ($.isArray(data)) {
+        for (i = 0, n = data.length; i < n; i++) {
+          self.addOption(data[i]);
+        }
+        return;
+      }
+
+      value = hash_key(data[self.settings.valueField]);
+      if (!value || self.options.hasOwnProperty(value)) return;
+
+      self.userOptions[value] = true;
+      self.options[value] = data;
+      self.lastQuery = null;
+      self.trigger('option_add', value, data);
+    },
+
+    /**
+     * Registers a new optgroup for options
+     * to be bucketed into.
+     *
+     * @param {string} id
+     * @param {object} data
+     */
+    addOptionGroup: function(id, data) {
+      this.optgroups[id] = data;
+      this.trigger('optgroup_add', id, data);
+    },
+
+    /**
+     * Updates an option available for selection. If
+     * it is visible in the selected items or options
+     * dropdown, it will be re-rendered automatically.
+     *
+     * @param {string} value
+     * @param {object} data
+     */
+    updateOption: function(value, data) {
+      var self = this;
+      var $item, $item_new;
+      var value_new, index_item, cache_items, cache_options;
+
+      value     = hash_key(value);
+      value_new = hash_key(data[self.settings.valueField]);
+
+      // sanity checks
+      if (!self.options.hasOwnProperty(value)) return;
+      if (!value_new) throw new Error('Value must be set in option data');
+
+      // update references
+      if (value_new !== value) {
+        delete self.options[value];
+        index_item = self.items.indexOf(value);
+        if (index_item !== -1) {
+          self.items.splice(index_item, 1, value_new);
+        }
+      }
+      self.options[value_new] = data;
+
+      // invalidate render cache
+      cache_items = self.renderCache['item'];
+      cache_options = self.renderCache['option'];
+
+      if (cache_items) {
+        delete cache_items[value];
+        delete cache_items[value_new];
+      }
+      if (cache_options) {
+        delete cache_options[value];
+        delete cache_options[value_new];
+      }
+
+      // update the item if it's selected
+      if (self.items.indexOf(value_new) !== -1) {
+        $item = self.getItem(value);
+        $item_new = $(self.render('item', data));
+        if ($item.hasClass('active')) $item_new.addClass('active');
+        $item.replaceWith($item_new);
+      }
+
+      // update dropdown contents
+      if (self.isOpen) {
+        self.refreshOptions(false);
+      }
+    },
+
+    /**
+     * Removes a single option.
+     *
+     * @param {string} value
+     */
+    removeOption: function(value) {
+      var self = this;
+      value = hash_key(value);
+
+      var cache_items = self.renderCache['item'];
+      var cache_options = self.renderCache['option'];
+      if (cache_items) delete cache_items[value];
+      if (cache_options) delete cache_options[value];
+
+      delete self.userOptions[value];
+      delete self.options[value];
+      self.lastQuery = null;
+      self.trigger('option_remove', value);
+      self.removeItem(value);
+    },
+
+    /**
+     * Clears all options.
+     */
+    clearOptions: function() {
+      var self = this;
+
+      self.loadedSearches = {};
+      self.userOptions = {};
+      self.renderCache = {};
+      self.options = self.sifter.items = {};
+      self.lastQuery = null;
+      self.trigger('option_clear');
+      self.clear();
+    },
+
+    /**
+     * Returns the jQuery element of the option
+     * matching the given value.
+     *
+     * @param {string} value
+     * @returns {object}
+     */
+    getOption: function(value) {
+      return this.getElementWithValue(value, this.$dropdown_content.find('[data-selectable]'));
+    },
+
+    /**
+     * Returns the jQuery element of the next or
+     * previous selectable option.
+     *
+     * @param {object} $option
+     * @param {int} direction  can be 1 for next or -1 for previous
+     * @return {object}
+     */
+    getAdjacentOption: function($option, direction) {
+      var $options = this.$dropdown.find('[data-selectable]');
+      var index    = $options.index($option) + direction;
+
+      return index >= 0 && index < $options.length ? $options.eq(index) : $();
+    },
+
+    /**
+     * Finds the first element with a "data-value" attribute
+     * that matches the given value.
+     *
+     * @param {mixed} value
+     * @param {object} $els
+     * @return {object}
+     */
+    getElementWithValue: function(value, $els) {
+      value = hash_key(value);
+
+      if (value) {
+        for (var i = 0, n = $els.length; i < n; i++) {
+          if ($els[i].getAttribute('data-value') === value) {
+            return $($els[i]);
+          }
+        }
+      }
+
+      return $();
+    },
+
+    /**
+     * Returns the jQuery element of the item
+     * matching the given value.
+     *
+     * @param {string} value
+     * @returns {object}
+     */
+    getItem: function(value) {
+      return this.getElementWithValue(value, this.$control.children());
+    },
+
+    /**
+     * "Selects" multiple items at once. Adds them to the list
+     * at the current caret position.
+     *
+     * @param {string} value
+     */
+    addItems: function(values) {
+      var items = $.isArray(values) ? values : [values];
+      for (var i = 0, n = items.length; i < n; i++) {
+        this.isPending = (i < n - 1);
+        this.addItem(items[i]);
+      }
+    },
+
+    /**
+     * "Selects" an item. Adds it to the list
+     * at the current caret position.
+     *
+     * @param {string} value
+     */
+    addItem: function(value) {
+      debounce_events(this, ['change'], function() {
+        var $item, $option, $options;
+        var self = this;
+        var inputMode = self.settings.mode;
+        var i, active, value_next, wasFull;
+        value = hash_key(value);
+
+        if (self.items.indexOf(value) !== -1) {
+          if (inputMode === 'single') self.close();
+          return;
+        }
+
+        if (!self.options.hasOwnProperty(value)) return;
+        if (inputMode === 'single') self.clear();
+        if (inputMode === 'multi' && self.isFull()) return;
+
+        $item = $(self.render('item', self.options[value]));
+        wasFull = self.isFull();
+        self.items.splice(self.caretPos, 0, value);
+        self.insertAtCaret($item);
+        if (!self.isPending || (!wasFull && self.isFull())) {
+          self.refreshState();
+        }
+
+        if (self.isSetup) {
+          $options = self.$dropdown_content.find('[data-selectable]');
+
+          // update menu / remove the option (if this is not one item being added as part of series)
+          if (!self.isPending) {
+            $option = self.getOption(value);
+            value_next = self.getAdjacentOption($option, 1).attr('data-value');
+            self.refreshOptions(self.isFocused && inputMode !== 'single');
+            if (value_next) {
+              self.setActiveOption(self.getOption(value_next));
+            }
+          }
+
+          // hide the menu if the maximum number of items have been selected or no options are left
+          if (!$options.length || self.isFull()) {
+            self.close();
+          } else {
+            self.positionDropdown();
+          }
+
+          self.updatePlaceholder();
+          self.trigger('item_add', value, $item);
+          self.updateOriginalInput();
+        }
+      });
+    },
+
+    /**
+     * Removes the selected item matching
+     * the provided value.
+     *
+     * @param {string} value
+     */
+    removeItem: function(value) {
+      var self = this;
+      var $item, i, idx;
+
+      $item = (typeof value === 'object') ? value : self.getItem(value);
+      value = hash_key($item.attr('data-value'));
+      i = self.items.indexOf(value);
+
+      if (i !== -1) {
+        $item.remove();
+        if ($item.hasClass('active')) {
+          idx = self.$activeItems.indexOf($item[0]);
+          self.$activeItems.splice(idx, 1);
+        }
+
+        self.items.splice(i, 1);
+        self.lastQuery = null;
+        if (!self.settings.persist && self.userOptions.hasOwnProperty(value)) {
+          self.removeOption(value);
+        }
+
+        if (i < self.caretPos) {
+          self.setCaret(self.caretPos - 1);
+        }
+
+        self.refreshState();
+        self.updatePlaceholder();
+        self.updateOriginalInput();
+        self.positionDropdown();
+        self.trigger('item_remove', value);
+      }
+    },
+
+    /**
+     * Invokes the `create` method provided in the
+     * selectize options that should provide the data
+     * for the new item, given the user input.
+     *
+     * Once this completes, it will be added
+     * to the item list.
+     *
+     * @return {boolean}
+     */
+    createItem: function(triggerDropdown) {
+      var self  = this;
+      var input = $.trim(self.$control_input.val() || '');
+      var caret = self.caretPos;
+      if (!self.canCreate(input)) return false;
+      self.lock();
+
+      if (typeof triggerDropdown === 'undefined') {
+        triggerDropdown = true;
+      }
+
+      var setup = (typeof self.settings.create === 'function') ? this.settings.create : function(input) {
+        var data = {};
+        data[self.settings.labelField] = input;
+        data[self.settings.valueField] = input;
+        return data;
+      };
+
+      var create = once(function(data) {
+        self.unlock();
+
+        if (!data || typeof data !== 'object') return;
+        var value = hash_key(data[self.settings.valueField]);
+        if (!value) return;
+
+        self.setTextboxValue('');
+        self.addOption(data);
+        self.setCaret(caret);
+        self.addItem(value);
+        self.refreshOptions(triggerDropdown && self.settings.mode !== 'single');
+      });
+
+      var output = setup.apply(this, [input, create]);
+      if (typeof output !== 'undefined') {
+        create(output);
+      }
+
+      return true;
+    },
+
+    /**
+     * Re-renders the selected item lists.
+     */
+    refreshItems: function() {
+      this.lastQuery = null;
+
+      if (this.isSetup) {
+        for (var i = 0; i < this.items.length; i++) {
+          this.addItem(this.items);
+        }
+      }
+
+      this.refreshState();
+      this.updateOriginalInput();
+    },
+
+    /**
+     * Updates all state-dependent attributes
+     * and CSS classes.
+     */
+    refreshState: function() {
+      var invalid, self = this;
+      if (self.isRequired) {
+        if (self.items.length) self.isInvalid = false;
+        self.$control_input.prop('required', invalid);
+      }
+      self.refreshClasses();
+    },
+
+    /**
+     * Updates all state-dependent CSS classes.
+     */
+    refreshClasses: function() {
+      var self     = this;
+      var isFull   = self.isFull();
+      var isLocked = self.isLocked;
+
+      self.$wrapper
+        .toggleClass('rtl', self.rtl);
+
+      self.$control
+        .toggleClass('focus', self.isFocused)
+        .toggleClass('disabled', self.isDisabled)
+        .toggleClass('required', self.isRequired)
+        .toggleClass('invalid', self.isInvalid)
+        .toggleClass('locked', isLocked)
+        .toggleClass('full', isFull).toggleClass('not-full', !isFull)
+        .toggleClass('input-active', self.isFocused && !self.isInputHidden)
+        .toggleClass('dropdown-active', self.isOpen)
+        .toggleClass('has-options', !$.isEmptyObject(self.options))
+        .toggleClass('has-items', self.items.length > 0);
+
+      self.$control_input.data('grow', !isFull && !isLocked);
+    },
+
+    /**
+     * Determines whether or not more items can be added
+     * to the control without exceeding the user-defined maximum.
+     *
+     * @returns {boolean}
+     */
+    isFull: function() {
+      return this.settings.maxItems !== null && this.items.length >= this.settings.maxItems;
+    },
+
+    /**
+     * Refreshes the original <select> or <input>
+     * element to reflect the current state.
+     */
+    updateOriginalInput: function() {
+      var i, n, options, self = this;
+
+      if (self.tagType === TAG_SELECT) {
+        options = [];
+        for (i = 0, n = self.items.length; i < n; i++) {
+          options.push('<option value="' + escape_html(self.items[i]) + '" selected="selected"></option>');
+        }
+        if (!options.length && !this.$input.attr('multiple')) {
+          options.push('<option value="" selected="selected"></option>');
+        }
+        self.$input.html(options.join(''));
+      } else {
+        self.$input.val(self.getValue());
+        self.$input.attr('value',self.$input.val());
+      }
+
+      if (self.isSetup) {
+        self.trigger('change', self.$input.val());
+      }
+    },
+
+    /**
+     * Shows/hide the input placeholder depending
+     * on if there items in the list already.
+     */
+    updatePlaceholder: function() {
+      if (!this.settings.placeholder) return;
+      var $input = this.$control_input;
+
+      if (this.items.length) {
+        $input.removeAttr('placeholder');
+      } else {
+        $input.attr('placeholder', this.settings.placeholder);
+      }
+      $input.triggerHandler('update', {force: true});
+    },
+
+    /**
+     * Shows the autocomplete dropdown containing
+     * the available options.
+     */
+    open: function() {
+      var self = this;
+
+      if (self.isLocked || self.isOpen || (self.settings.mode === 'multi' && self.isFull())) return;
+      self.focus();
+      self.isOpen = true;
+      self.refreshState();
+      self.$dropdown.css({visibility: 'hidden', display: 'block'});
+      self.positionDropdown();
+      self.$dropdown.css({visibility: 'visible'});
+      self.trigger('dropdown_open', self.$dropdown);
+    },
+
+    /**
+     * Closes the autocomplete dropdown menu.
+     */
+    close: function() {
+      var self = this;
+      var trigger = self.isOpen;
+
+      if (self.settings.mode === 'single' && self.items.length) {
+        self.hideInput();
+      }
+
+      self.isOpen = false;
+      self.$dropdown.hide();
+      self.setActiveOption(null);
+      self.refreshState();
+
+      if (trigger) self.trigger('dropdown_close', self.$dropdown);
+    },
+
+    /**
+     * Calculates and applies the appropriate
+     * position of the dropdown.
+     */
+    positionDropdown: function() {
+      var $control = this.$control;
+      var offset = this.settings.dropdownParent === 'body' ? $control.offset() : $control.position();
+      offset.top += $control.outerHeight(true);
+
+      this.$dropdown.css({
+        width : $control.outerWidth(),
+        top   : offset.top,
+        left  : offset.left
+      });
+    },
+
+    /**
+     * Resets / clears all selected items
+     * from the control.
+     */
+    clear: function() {
+      var self = this;
+
+      if (!self.items.length) return;
+      self.$control.children(':not(input)').remove();
+      self.items = [];
+      self.lastQuery = null;
+      self.setCaret(0);
+      self.setActiveItem(null);
+      self.updatePlaceholder();
+      self.updateOriginalInput();
+      self.refreshState();
+      self.showInput();
+      self.trigger('clear');
+    },
+
+    /**
+     * A helper method for inserting an element
+     * at the current caret position.
+     *
+     * @param {object} $el
+     */
+    insertAtCaret: function($el) {
+      var caret = Math.min(this.caretPos, this.items.length);
+      if (caret === 0) {
+        this.$control.prepend($el);
+      } else {
+        $(this.$control[0].childNodes[caret]).before($el);
+      }
+      this.setCaret(caret + 1);
+    },
+
+    /**
+     * Removes the current selected item(s).
+     *
+     * @param {object} e (optional)
+     * @returns {boolean}
+     */
+    deleteSelection: function(e) {
+      var i, n, direction, selection, values, caret, option_select, $option_select, $tail;
+      var self = this;
+
+      direction = (e && e.keyCode === KEY_BACKSPACE) ? -1 : 1;
+      selection = getSelection(self.$control_input[0]);
+
+      if (self.$activeOption && !self.settings.hideSelected) {
+        option_select = self.getAdjacentOption(self.$activeOption, -1).attr('data-value');
+      }
+
+      // determine items that will be removed
+      values = [];
+
+      if (self.$activeItems.length) {
+        $tail = self.$control.children('.active:' + (direction > 0 ? 'last' : 'first'));
+        caret = self.$control.children(':not(input)').index($tail);
+        if (direction > 0) { caret++; }
+
+        for (i = 0, n = self.$activeItems.length; i < n; i++) {
+          values.push($(self.$activeItems[i]).attr('data-value'));
+        }
+        if (e) {
+          e.preventDefault();
+          e.stopPropagation();
+        }
+      } else if ((self.isFocused || self.settings.mode === 'single') && self.items.length) {
+        if (direction < 0 && selection.start === 0 && selection.length === 0) {
+          values.push(self.items[self.caretPos - 1]);
+        } else if (direction > 0 && selection.start === self.$control_input.val().length) {
+          values.push(self.items[self.caretPos]);
+        }
+      }
+
+      // allow the callback to abort
+      if (!values.length || (typeof self.settings.onDelete === 'function' && self.settings.onDelete.apply(self, [values]) === false)) {
+        return false;
+      }
+
+      // perform removal
+      if (typeof caret !== 'undefined') {
+        self.setCaret(caret);
+      }
+      while (values.length) {
+        self.removeItem(values.pop());
+      }
+
+      self.showInput();
+      self.positionDropdown();
+      self.refreshOptions(true);
+
+      // select previous option
+      if (option_select) {
+        $option_select = self.getOption(option_select);
+        if ($option_select.length) {
+          self.setActiveOption($option_select);
+        }
+      }
+
+      return true;
+    },
+
+    /**
+     * Selects the previous / next item (depending
+     * on the `direction` argument).
+     *
+     * > 0 - right
+     * < 0 - left
+     *
+     * @param {int} direction
+     * @param {object} e (optional)
+     */
+    advanceSelection: function(direction, e) {
+      var tail, selection, idx, valueLength, cursorAtEdge, $tail;
+      var self = this;
+
+      if (direction === 0) return;
+      if (self.rtl) direction *= -1;
+
+      tail = direction > 0 ? 'last' : 'first';
+      selection = getSelection(self.$control_input[0]);
+
+      if (self.isFocused && !self.isInputHidden) {
+        valueLength = self.$control_input.val().length;
+        cursorAtEdge = direction < 0
+          ? selection.start === 0 && selection.length === 0
+          : selection.start === valueLength;
+
+        if (cursorAtEdge && !valueLength) {
+          self.advanceCaret(direction, e);
+        }
+      } else {
+        $tail = self.$control.children('.active:' + tail);
+        if ($tail.length) {
+          idx = self.$control.children(':not(input)').index($tail);
+          self.setActiveItem(null);
+          self.setCaret(direction > 0 ? idx + 1 : idx);
+        }
+      }
+    },
+
+    /**
+     * Moves the caret left / right.
+     *
+     * @param {int} direction
+     * @param {object} e (optional)
+     */
+    advanceCaret: function(direction, e) {
+      var self = this, fn, $adj;
+
+      if (direction === 0) return;
+
+      fn = direction > 0 ? 'next' : 'prev';
+      if (self.isShiftDown) {
+        $adj = self.$control_input[fn]();
+        if ($adj.length) {
+          self.hideInput();
+          self.setActiveItem($adj);
+          e && e.preventDefault();
+        }
+      } else {
+        self.setCaret(self.caretPos + direction);
+      }
+    },
+
+    /**
+     * Moves the caret to the specified index.
+     *
+     * @param {int} i
+     */
+    setCaret: function(i) {
+      var self = this;
+
+      if (self.settings.mode === 'single') {
+        i = self.items.length;
+      } else {
+        i = Math.max(0, Math.min(self.items.length, i));
+      }
+
+      if(!self.isPending) {
+        // the input must be moved by leaving it in place and moving the
+        // siblings, due to the fact that focus cannot be restored once lost
+        // on mobile webkit devices
+        var j, n, fn, $children, $child;
+        $children = self.$control.children(':not(input)');
+        for (j = 0, n = $children.length; j < n; j++) {
+          $child = $($children[j]).detach();
+          if (j <  i) {
+            self.$control_input.before($child);
+          } else {
+            self.$control.append($child);
+          }
+        }
+      }
+
+      self.caretPos = i;
+    },
+
+    /**
+     * Disables user input on the control. Used while
+     * items are being asynchronously created.
+     */
+    lock: function() {
+      this.close();
+      this.isLocked = true;
+      this.refreshState();
+    },
+
+    /**
+     * Re-enables user input on the control.
+     */
+    unlock: function() {
+      this.isLocked = false;
+      this.refreshState();
+    },
+
+    /**
+     * Disables user input on the control completely.
+     * While disabled, it cannot receive focus.
+     */
+    disable: function() {
+      var self = this;
+      self.$input.prop('disabled', true);
+      self.isDisabled = true;
+      self.lock();
+    },
+
+    /**
+     * Enables the control so that it can respond
+     * to focus and user input.
+     */
+    enable: function() {
+      var self = this;
+      self.$input.prop('disabled', false);
+      self.isDisabled = false;
+      self.unlock();
+    },
+
+    /**
+     * Completely destroys the control and
+     * unbinds all event listeners so that it can
+     * be garbage collected.
+     */
+    destroy: function() {
+      var self = this;
+      var eventNS = self.eventNS;
+      var revertSettings = self.revertSettings;
+
+      self.trigger('destroy');
+      self.off();
+      self.$wrapper.remove();
+      self.$dropdown.remove();
+
+      self.$input
+        .html('')
+        .append(revertSettings.$children)
+        .removeAttr('tabindex')
+        .removeClass('selectized')
+        .attr({tabindex: revertSettings.tabindex})
+        .show();
+
+      self.$control_input.removeData('grow');
+      self.$input.removeData('selectize');
+
+      $(window).off(eventNS);
+      $(document).off(eventNS);
+      $(document.body).off(eventNS);
+
+      delete self.$input[0].selectize;
+    },
+
+    /**
+     * A helper method for rendering "item" and
+     * "option" templates, given the data.
+     *
+     * @param {string} templateName
+     * @param {object} data
+     * @returns {string}
+     */
+    render: function(templateName, data) {
+      var value, id, label;
+      var html = '';
+      var cache = false;
+      var self = this;
+      var regex_tag = /^[\t ]*<([a-z][a-z0-9\-_]*(?:\:[a-z][a-z0-9\-_]*)?)/i;
+
+      if (templateName === 'option' || templateName === 'item') {
+        value = hash_key(data[self.settings.valueField]);
+        cache = !!value;
+      }
+
+      // pull markup from cache if it exists
+      if (cache) {
+        if (!isset(self.renderCache[templateName])) {
+          self.renderCache[templateName] = {};
+        }
+        if (self.renderCache[templateName].hasOwnProperty(value)) {
+          return self.renderCache[templateName][value];
+        }
+      }
+
+      // render markup
+      html = self.settings.render[templateName].apply(this, [data, escape_html]);
+
+      // add mandatory attributes
+      if (templateName === 'option' || templateName === 'option_create') {
+        html = html.replace(regex_tag, '<$1 data-selectable');
+      }
+      if (templateName === 'optgroup') {
+        id = data[self.settings.optgroupValueField] || '';
+        html = html.replace(regex_tag, '<$1 data-group="' + escape_replace(escape_html(id)) + '"');
+      }
+      if (templateName === 'option' || templateName === 'item') {
+        html = html.replace(regex_tag, '<$1 data-value="' + escape_replace(escape_html(value || '')) + '"');
+      }
+
+      // update cache
+      if (cache) {
+        self.renderCache[templateName][value] = html;
+      }
+
+      return html;
+    },
+
+    /**
+     * Clears the render cache for a template. If
+     * no template is given, clears all render
+     * caches.
+     *
+     * @param {string} templateName
+     */
+    clearCache: function(templateName) {
+      var self = this;
+      if (typeof templateName === 'undefined') {
+        self.renderCache = {};
+      } else {
+        delete self.renderCache[templateName];
+      }
+    }
+
+
+  });
+
+
+  Selectize.count = 0;
+  Selectize.defaults = {
+    plugins: [],
+    delimiter: ',',
+    persist: true,
+    diacritics: true,
+    create: false,
+    createOnBlur: false,
+    createFilter: null,
+    highlight: true,
+    openOnFocus: true,
+    maxOptions: 1000,
+    maxItems: null,
+    hideSelected: null,
+    addPrecedence: false,
+    selectOnTab: false,
+    preload: false,
+
+    scrollDuration: 60,
+    loadThrottle: 300,
+
+    dataAttr: 'data-data',
+    optgroupField: 'optgroup',
+    valueField: 'value',
+    labelField: 'text',
+    optgroupLabelField: 'label',
+    optgroupValueField: 'value',
+    optgroupOrder: null,
+
+    sortField: '$order',
+    searchField: ['text'],
+    searchConjunction: 'and',
+
+    mode: null,
+    wrapperClass: 'selectize-control',
+    inputClass: 'selectize-input',
+    dropdownClass: 'selectize-dropdown',
+    dropdownContentClass: 'selectize-dropdown-content',
+
+    dropdownParent: null,
+
+    /*
+    load            : null, // function(query, callback) { ... }
+    score           : null, // function(search) { ... }
+    onInitialize    : null, // function() { ... }
+    onChange        : null, // function(value) { ... }
+    onItemAdd       : null, // function(value, $item) { ... }
+    onItemRemove    : null, // function(value) { ... }
+    onClear         : null, // function() { ... }
+    onOptionAdd     : null, // function(value, data) { ... }
+    onOptionRemove  : null, // function(value) { ... }
+    onOptionClear   : null, // function() { ... }
+    onDropdownOpen  : null, // function($dropdown) { ... }
+    onDropdownClose : null, // function($dropdown) { ... }
+    onType          : null, // function(str) { ... }
+    onDelete        : null, // function(values) { ... }
+    */
+
+    render: {
+      /*
+      item: null,
+      optgroup: null,
+      optgroup_header: null,
+      option: null,
+      option_create: null
+      */
+    }
+  };
+
+  $.fn.selectize = function(settings_user) {
+    var defaults             = $.fn.selectize.defaults;
+    var settings             = $.extend({}, defaults, settings_user);
+    var attr_data            = settings.dataAttr;
+    var field_label          = settings.labelField;
+    var field_value          = settings.valueField;
+    var field_optgroup       = settings.optgroupField;
+    var field_optgroup_label = settings.optgroupLabelField;
+    var field_optgroup_value = settings.optgroupValueField;
+
+    /**
+     * Initializes selectize from a <input type="text"> element.
+     *
+     * @param {object} $input
+     * @param {object} settings_element
+     */
+    var init_textbox = function($input, settings_element) {
+      var i, n, values, option, value = $.trim($input.val() || '');
+      if (!value.length) return;
+
+      values = value.split(settings.delimiter);
+      for (i = 0, n = values.length; i < n; i++) {
+        option = {};
+        option[field_label] = values[i];
+        option[field_value] = values[i];
+
+        settings_element.options[values[i]] = option;
+      }
+
+      settings_element.items = values;
+    };
+
+    /**
+     * Initializes selectize from a <select> element.
+     *
+     * @param {object} $input
+     * @param {object} settings_element
+     */
+    var init_select = function($input, settings_element) {
+      var i, n, tagName, $children, order = 0;
+      var options = settings_element.options;
+
+      var readData = function($el) {
+        var data = attr_data && $el.attr(attr_data);
+        if (typeof data === 'string' && data.length) {
+          return JSON.parse(data);
+        }
+        return null;
+      };
+
+      var addOption = function($option, group) {
+        var value, option;
+
+        $option = $($option);
+
+        value = $option.attr('value') || '';
+        if (!value.length) return;
+
+        // if the option already exists, it's probably been
+        // duplicated in another optgroup. in this case, push
+        // the current group to the "optgroup" property on the
+        // existing option so that it's rendered in both places.
+        if (options.hasOwnProperty(value)) {
+          if (group) {
+            if (!options[value].optgroup) {
+              options[value].optgroup = group;
+            } else if (!$.isArray(options[value].optgroup)) {
+              options[value].optgroup = [options[value].optgroup, group];
+            } else {
+              options[value].optgroup.push(group);
+            }
+          }
+          return;
+        }
+
+        option                 = readData($option) || {};
+        option[field_label]    = option[field_label] || $option.text();
+        option[field_value]    = option[field_value] || value;
+        option[field_optgroup] = option[field_optgroup] || group;
+
+        option.$order = ++order;
+        options[value] = option;
+
+        if ($option.is(':selected')) {
+          settings_element.items.push(value);
+        }
+      };
+
+      var addGroup = function($optgroup) {
+        var i, n, id, optgroup, $options;
+
+        $optgroup = $($optgroup);
+        id = $optgroup.attr('label');
+
+        if (id) {
+          optgroup = readData($optgroup) || {};
+          optgroup[field_optgroup_label] = id;
+          optgroup[field_optgroup_value] = id;
+          settings_element.optgroups[id] = optgroup;
+        }
+
+        $options = $('option', $optgroup);
+        for (i = 0, n = $options.length; i < n; i++) {
+          addOption($options[i], id);
+        }
+      };
+
+      settings_element.maxItems = $input.attr('multiple') ? null : 1;
+
+      $children = $input.children();
+      for (i = 0, n = $children.length; i < n; i++) {
+        tagName = $children[i].tagName.toLowerCase();
+        if (tagName === 'optgroup') {
+          addGroup($children[i]);
+        } else if (tagName === 'option') {
+          addOption($children[i]);
+        }
+      }
+    };
+
+    return this.each(function() {
+      if (this.selectize) return;
+
+      var instance;
+      var $input = $(this);
+      var tag_name = this.tagName.toLowerCase();
+      var settings_element = {
+        'placeholder' : $input.children('option[value=""]').text() || $input.attr('placeholder'),
+        'options'     : {},
+        'optgroups'   : {},
+        'items'       : []
+      };
+
+      if (tag_name === 'select') {
+        init_select($input, settings_element);
+      } else {
+        init_textbox($input, settings_element);
+      }
+
+      instance = new Selectize($input, $.extend(true, {}, defaults, settings_element, settings_user));
+    });
+  };
+
+  $.fn.selectize.defaults = Selectize.defaults;
+
+
+  Selectize.define('drag_drop', function(options) {
+    if (!$.fn.sortable) throw new Error('The "drag_drop" plugin requires jQuery UI "sortable".');
+    if (this.settings.mode !== 'multi') return;
+    var self = this;
+
+    self.lock = (function() {
+      var original = self.lock;
+      return function() {
+        var sortable = self.$control.data('sortable');
+        if (sortable) sortable.disable();
+        return original.apply(self, arguments);
+      };
+    })();
+
+    self.unlock = (function() {
+      var original = self.unlock;
+      return function() {
+        var sortable = self.$control.data('sortable');
+        if (sortable) sortable.enable();
+        return original.apply(self, arguments);
+      };
+    })();
+
+    self.setup = (function() {
+      var original = self.setup;
+      return function() {
+        original.apply(this, arguments);
+
+        var $control = self.$control.sortable({
+          items: '[data-value]',
+          forcePlaceholderSize: true,
+          disabled: self.isLocked,
+          start: function(e, ui) {
+            ui.placeholder.css('width', ui.helper.css('width'));
+            $control.css({overflow: 'visible'});
+          },
+          stop: function() {
+            $control.css({overflow: 'hidden'});
+            var active = self.$activeItems ? self.$activeItems.slice() : null;
+            var values = [];
+            $control.children('[data-value]').each(function() {
+              values.push($(this).attr('data-value'));
+            });
+            self.setValue(values);
+            self.setActiveItem(active);
+          }
+        });
+      };
+    })();
+
+  });
+
+  Selectize.define('dropdown_header', function(options) {
+    var self = this;
+
+    options = $.extend({
+      title         : 'Untitled',
+      headerClass   : 'selectize-dropdown-header',
+      titleRowClass : 'selectize-dropdown-header-title',
+      labelClass    : 'selectize-dropdown-header-label',
+      closeClass    : 'selectize-dropdown-header-close',
+
+      html: function(data) {
+        return (
+          '<div class="' + data.headerClass + '">' +
+            '<div class="' + data.titleRowClass + '">' +
+              '<span class="' + data.labelClass + '">' + data.title + '</span>' +
+              '<a href="javascript:void(0)" class="' + data.closeClass + '">&times;</a>' +
+            '</div>' +
+          '</div>'
+        );
+      }
+    }, options);
+
+    self.setup = (function() {
+      var original = self.setup;
+      return function() {
+        original.apply(self, arguments);
+        self.$dropdown_header = $(options.html(options));
+        self.$dropdown.prepend(self.$dropdown_header);
+      };
+    })();
+
+  });
+
+  Selectize.define('optgroup_columns', function(options) {
+    var self = this;
+
+    options = $.extend({
+      equalizeWidth  : true,
+      equalizeHeight : true
+    }, options);
+
+    this.getAdjacentOption = function($option, direction) {
+      var $options = $option.closest('[data-group]').find('[data-selectable]');
+      var index    = $options.index($option) + direction;
+
+      return index >= 0 && index < $options.length ? $options.eq(index) : $();
+    };
+
+    this.onKeyDown = (function() {
+      var original = self.onKeyDown;
+      return function(e) {
+        var index, $option, $options, $optgroup;
+
+        if (this.isOpen && (e.keyCode === KEY_LEFT || e.keyCode === KEY_RIGHT)) {
+          self.ignoreHover = true;
+          $optgroup = this.$activeOption.closest('[data-group]');
+          index = $optgroup.find('[data-selectable]').index(this.$activeOption);
+
+          if(e.keyCode === KEY_LEFT) {
+            $optgroup = $optgroup.prev('[data-group]');
+          } else {
+            $optgroup = $optgroup.next('[data-group]');
+          }
+
+          $options = $optgroup.find('[data-selectable]');
+          $option  = $options.eq(Math.min($options.length - 1, index));
+          if ($option.length) {
+            this.setActiveOption($option);
+          }
+          return;
+        }
+
+        return original.apply(this, arguments);
+      };
+    })();
+
+    var getScrollbarWidth = function() {
+      var div;
+      var width = getScrollbarWidth.width;
+      var doc = document;
+
+      if (typeof width === 'undefined') {
+        div = doc.createElement('div');
+        div.innerHTML = '<div style="width:50px;height:50px;position:absolute;left:-50px;top:-50px;overflow:auto;"><div style="width:1px;height:100px;"></div></div>';
+        div = div.firstChild;
+        doc.body.appendChild(div);
+        width = getScrollbarWidth.width = div.offsetWidth - div.clientWidth;
+        doc.body.removeChild(div);
+      }
+      return width;
+    };
+
+    var equalizeSizes = function() {
+      var i, n, height_max, width, width_last, width_parent, $optgroups;
+
+      $optgroups = $('[data-group]', self.$dropdown_content);
+      n = $optgroups.length;
+      if (!n || !self.$dropdown_content.width()) return;
+
+      if (options.equalizeHeight) {
+        height_max = 0;
+        for (i = 0; i < n; i++) {
+          height_max = Math.max(height_max, $optgroups.eq(i).height());
+        }
+        $optgroups.css({height: height_max});
+      }
+
+      if (options.equalizeWidth) {
+        width_parent = self.$dropdown_content.innerWidth() - getScrollbarWidth();
+        width = Math.round(width_parent / n);
+        $optgroups.css({width: width});
+        if (n > 1) {
+          width_last = width_parent - width * (n - 1);
+          $optgroups.eq(n - 1).css({width: width_last});
+        }
+      }
+    };
+
+    if (options.equalizeHeight || options.equalizeWidth) {
+      hook.after(this, 'positionDropdown', equalizeSizes);
+      hook.after(this, 'refreshOptions', equalizeSizes);
+    }
+
+
+  });
+
+  Selectize.define('remove_button', function(options) {
+    if (this.settings.mode === 'single') return;
+
+    options = $.extend({
+      label     : '&times;',
+      title     : 'Remove',
+      className : 'remove',
+      append    : true
+    }, options);
+
+    var self = this;
+    var html = '<a href="javascript:void(0)" class="' + options.className + '" tabindex="-1" title="' + escape_html(options.title) + '">' + options.label + '</a>';
+
+    /**
+     * Appends an element as a child (with raw HTML).
+     *
+     * @param {string} html_container
+     * @param {string} html_element
+     * @return {string}
+     */
+    var append = function(html_container, html_element) {
+      var pos = html_container.search(/(<\/[^>]+>\s*)$/);
+      return html_container.substring(0, pos) + html_element + html_container.substring(pos);
+    };
+
+    this.setup = (function() {
+      var original = self.setup;
+      return function() {
+        // override the item rendering method to add the button to each
+        if (options.append) {
+          var render_item = self.settings.render.item;
+          self.settings.render.item = function(data) {
+            return append(render_item.apply(this, arguments), html);
+          };
+        }
+
+        original.apply(this, arguments);
+
+        // add event listener
+        this.$control.on('click', '.' + options.className, function(e) {
+          e.preventDefault();
+          if (self.isLocked) return;
+
+          var $item = $(e.currentTarget).parent();
+          self.setActiveItem($item);
+          if (self.deleteSelection()) {
+            self.setCaret(self.items.length);
+          }
+        });
+
+      };
+    })();
+
+  });
+
+  Selectize.define('restore_on_backspace', function(options) {
+    var self = this;
+
+    options.text = options.text || function(option) {
+      return option[this.settings.labelField];
+    };
+
+    this.onKeyDown = (function(e) {
+      var original = self.onKeyDown;
+      return function(e) {
+        var index, option;
+        if (e.keyCode === KEY_BACKSPACE && this.$control_input.val() === '' && !this.$activeItems.length) {
+          index = this.caretPos - 1;
+          if (index >= 0 && index < this.items.length) {
+            option = this.options[this.items[index]];
+            if (this.deleteSelection(e)) {
+              this.setTextboxValue(options.text.apply(this, [option]));
+              this.refreshOptions(true);
+            }
+            e.preventDefault();
+            return;
+          }
+        }
+        return original.apply(this, arguments);
+      };
+    })();
+  });
+
+  return Selectize;
+}));

+ 114 - 0
app/assets/javascripts/blazer/stupidtable.js

@@ -0,0 +1,114 @@
+// Stupid jQuery table plugin.
+
+// Call on a table
+// sortFns: Sort functions for your datatypes.
+(function($) {
+
+  $.fn.stupidtable = function(sortFns) {
+    return this.each(function() {
+      var $table = $(this);
+      sortFns = sortFns || {};
+
+      // Merge sort functions with some default sort functions.
+      sortFns = $.extend({}, $.fn.stupidtable.default_sort_fns, sortFns);
+
+
+      // ==================================================== //
+      //                  Begin execution!                    //
+      // ==================================================== //
+
+      // Do sorting when THs are clicked
+      $table.on("click.stupidtable", "th", function() {
+        var $this = $(this);
+        var th_index = 0;
+        var dir = $.fn.stupidtable.dir;
+
+        $table.find("th").slice(0, $this.index()).each(function() {
+          var cols = $(this).attr("colspan") || 1;
+          th_index += parseInt(cols,10);
+        });
+
+        // Determine (and/or reverse) sorting direction, default `asc`
+        var sort_dir = $this.data("sort-default") || dir.ASC;
+        if ($this.data("sort-dir"))
+           sort_dir = $this.data("sort-dir") === dir.ASC ? dir.DESC : dir.ASC;
+
+        // Choose appropriate sorting function.
+        var type = $this.data("sort") || null;
+
+        // Prevent sorting if no type defined
+        if (type === null) {
+          return;
+        }
+
+        // Trigger `beforetablesort` event that calling scripts can hook into;
+        // pass parameters for sorted column index and sorting direction
+        $table.trigger("beforetablesort", {column: th_index, direction: sort_dir});
+        // More reliable method of forcing a redraw
+        $table.css("display");
+
+        // Run sorting asynchronously on a timout to force browser redraw after
+        // `beforetablesort` callback. Also avoids locking up the browser too much.
+        setTimeout(function() {
+          // Gather the elements for this column
+          var column = [];
+          var sortMethod = sortFns[type];
+          var trs = $table.children("tbody").children("tr");
+
+          // Extract the data for the column that needs to be sorted and pair it up
+          // with the TR itself into a tuple
+          trs.each(function(index,tr) {
+            var $e = $(tr).children().eq(th_index);
+            var sort_val = $e.data("sort-value");
+            var order_by = typeof(sort_val) !== "undefined" ? sort_val : $e.text();
+            column.push([order_by, tr]);
+          });
+
+          // Sort by the data-order-by value
+          column.sort(function(a, b) { return sortMethod(a[0], b[0]); });
+          if (sort_dir != dir.ASC)
+            column.reverse();
+
+          // Replace the content of tbody with the sorted rows. Strangely (and
+          // conveniently!) enough, .append accomplishes this for us.
+          trs = $.map(column, function(kv) { return kv[1]; });
+          $table.children("tbody").append(trs);
+
+          // Reset siblings
+          $table.find("th").data("sort-dir", null).removeClass("sorting-desc sorting-asc");
+          $this.data("sort-dir", sort_dir).addClass("sorting-"+sort_dir);
+
+          // Trigger `aftertablesort` event. Similar to `beforetablesort`
+          $table.trigger("aftertablesort", {column: th_index, direction: sort_dir});
+          // More reliable method of forcing a redraw
+          $table.css("display");
+        }, 10);
+      });
+    });
+  };
+
+  // Enum containing sorting directions
+  $.fn.stupidtable.dir = {ASC: "asc", DESC: "desc"};
+
+  $.fn.stupidtable.default_sort_fns = {
+    "int": function(a, b) {
+      return parseInt(a.replace(/,/g, ''), 10) - parseInt(b.replace(/,/g, ''), 10);
+    },
+    "float": function(a, b) {
+      return parseFloat(a) - parseFloat(b);
+    },
+    "string": function(a, b) {
+      if (a < b) return -1;
+      if (a > b) return +1;
+      return 0;
+    },
+    "string-ins": function(a, b) {
+      a = a.toLowerCase();
+      b = b.toLowerCase();
+      if (a < b) return -1;
+      if (a > b) return +1;
+      return 0;
+    }
+  };
+
+})(jQuery);

+ 66 - 0
app/assets/stylesheets/blazer/application.css

@@ -0,0 +1,66 @@
+/*
+ *= require ./bootstrap
+ *= require ./selectize.default
+ *= require ./github
+ *= require ./daterangepicker-bs3
+ *= require_self
+ */
+
+pre {
+  border: none;
+  border-radius: 0;
+}
+
+body {
+  padding-top: 20px;
+  padding-bottom: 20px;
+}
+
+#results th {
+  cursor: pointer;
+}
+
+#results thead {
+  background-color: #fff;
+}
+
+input.search {
+  border: none;
+  box-shadow: none;
+}
+
+input.search:focus {
+  outline: none;
+  box-shadow: none;
+}
+
+.button_to {
+  display: inline-block;
+}
+
+.button_to input:focus {
+  outline: none;
+}
+
+.form-inline .selectize-control, .text-right .selectize-control {
+  display: inline-block;
+  vertical-align: middle;
+  margin-left: 5px;
+  margin-right: 20px;
+  text-align: left;
+}
+
+#editor-container {
+  background-color: #141414;
+  padding-top: 10px;
+  padding-bottom: 10px;
+}
+
+#editor {
+  display: none;
+  height: 100px;
+}
+
+.ace_print-margin-layer {
+  display: none;
+}

+ 6203 - 0
app/assets/stylesheets/blazer/bootstrap.css

@@ -0,0 +1,6203 @@
+/*!
+ * Bootstrap v3.2.0 (http://getbootstrap.com)
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+
+/*! normalize.css v3.0.1 | MIT License | git.io/normalize */
+html {
+  font-family: sans-serif;
+  -webkit-text-size-adjust: 100%;
+      -ms-text-size-adjust: 100%;
+}
+body {
+  margin: 0;
+}
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+main,
+nav,
+section,
+summary {
+  display: block;
+}
+audio,
+canvas,
+progress,
+video {
+  display: inline-block;
+  vertical-align: baseline;
+}
+audio:not([controls]) {
+  display: none;
+  height: 0;
+}
+[hidden],
+template {
+  display: none;
+}
+a {
+  background: transparent;
+}
+a:active,
+a:hover {
+  outline: 0;
+}
+abbr[title] {
+  border-bottom: 1px dotted;
+}
+b,
+strong {
+  font-weight: bold;
+}
+dfn {
+  font-style: italic;
+}
+h1 {
+  margin: .67em 0;
+  font-size: 2em;
+}
+mark {
+  color: #000;
+  background: #ff0;
+}
+small {
+  font-size: 80%;
+}
+sub,
+sup {
+  position: relative;
+  font-size: 75%;
+  line-height: 0;
+  vertical-align: baseline;
+}
+sup {
+  top: -.5em;
+}
+sub {
+  bottom: -.25em;
+}
+img {
+  border: 0;
+}
+svg:not(:root) {
+  overflow: hidden;
+}
+figure {
+  margin: 1em 40px;
+}
+hr {
+  height: 0;
+  -webkit-box-sizing: content-box;
+     -moz-box-sizing: content-box;
+          box-sizing: content-box;
+}
+pre {
+  overflow: auto;
+}
+code,
+kbd,
+pre,
+samp {
+  font-family: monospace, monospace;
+  font-size: 1em;
+}
+button,
+input,
+optgroup,
+select,
+textarea {
+  margin: 0;
+  font: inherit;
+  color: inherit;
+}
+button {
+  overflow: visible;
+}
+button,
+select {
+  text-transform: none;
+}
+button,
+html input[type="button"],
+input[type="reset"],
+input[type="submit"] {
+  -webkit-appearance: button;
+  cursor: pointer;
+}
+button[disabled],
+html input[disabled] {
+  cursor: default;
+}
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+  padding: 0;
+  border: 0;
+}
+input {
+  line-height: normal;
+}
+input[type="checkbox"],
+input[type="radio"] {
+  -webkit-box-sizing: border-box;
+     -moz-box-sizing: border-box;
+          box-sizing: border-box;
+  padding: 0;
+}
+input[type="number"]::-webkit-inner-spin-button,
+input[type="number"]::-webkit-outer-spin-button {
+  height: auto;
+}
+input[type="search"] {
+  -webkit-box-sizing: content-box;
+     -moz-box-sizing: content-box;
+          box-sizing: content-box;
+  -webkit-appearance: textfield;
+}
+input[type="search"]::-webkit-search-cancel-button,
+input[type="search"]::-webkit-search-decoration {
+  -webkit-appearance: none;
+}
+fieldset {
+  padding: .35em .625em .75em;
+  margin: 0 2px;
+  border: 1px solid #c0c0c0;
+}
+legend {
+  padding: 0;
+  border: 0;
+}
+textarea {
+  overflow: auto;
+}
+optgroup {
+  font-weight: bold;
+}
+table {
+  border-spacing: 0;
+  border-collapse: collapse;
+}
+td,
+th {
+  padding: 0;
+}
+@media print {
+  * {
+    color: #000 !important;
+    text-shadow: none !important;
+    background: transparent !important;
+    -webkit-box-shadow: none !important;
+            box-shadow: none !important;
+  }
+  a,
+  a:visited {
+    text-decoration: underline;
+  }
+  a[href]:after {
+    content: " (" attr(href) ")";
+  }
+  abbr[title]:after {
+    content: " (" attr(title) ")";
+  }
+  a[href^="javascript:"]:after,
+  a[href^="#"]:after {
+    content: "";
+  }
+  pre,
+  blockquote {
+    border: 1px solid #999;
+
+    page-break-inside: avoid;
+  }
+  thead {
+    display: table-header-group;
+  }
+  tr,
+  img {
+    page-break-inside: avoid;
+  }
+  img {
+    max-width: 100% !important;
+  }
+  p,
+  h2,
+  h3 {
+    orphans: 3;
+    widows: 3;
+  }
+  h2,
+  h3 {
+    page-break-after: avoid;
+  }
+  select {
+    background: #fff !important;
+  }
+  .navbar {
+    display: none;
+  }
+  .table td,
+  .table th {
+    background-color: #fff !important;
+  }
+  .btn > .caret,
+  .dropup > .btn > .caret {
+    border-top-color: #000 !important;
+  }
+  .label {
+    border: 1px solid #000;
+  }
+  .table {
+    border-collapse: collapse !important;
+  }
+  .table-bordered th,
+  .table-bordered td {
+    border: 1px solid #ddd !important;
+  }
+}
+@font-face {
+  font-family: 'Glyphicons Halflings';
+
+  src: url('../fonts/glyphicons-halflings-regular.eot');
+  src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
+}
+.glyphicon {
+  position: relative;
+  top: 1px;
+  display: inline-block;
+  font-family: 'Glyphicons Halflings';
+  font-style: normal;
+  font-weight: normal;
+  line-height: 1;
+
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+.glyphicon-asterisk:before {
+  content: "\2a";
+}
+.glyphicon-plus:before {
+  content: "\2b";
+}
+.glyphicon-euro:before {
+  content: "\20ac";
+}
+.glyphicon-minus:before {
+  content: "\2212";
+}
+.glyphicon-cloud:before {
+  content: "\2601";
+}
+.glyphicon-envelope:before {
+  content: "\2709";
+}
+.glyphicon-pencil:before {
+  content: "\270f";
+}
+.glyphicon-glass:before {
+  content: "\e001";
+}
+.glyphicon-music:before {
+  content: "\e002";
+}
+.glyphicon-search:before {
+  content: "\e003";
+}
+.glyphicon-heart:before {
+  content: "\e005";
+}
+.glyphicon-star:before {
+  content: "\e006";
+}
+.glyphicon-star-empty:before {
+  content: "\e007";
+}
+.glyphicon-user:before {
+  content: "\e008";
+}
+.glyphicon-film:before {
+  content: "\e009";
+}
+.glyphicon-th-large:before {
+  content: "\e010";
+}
+.glyphicon-th:before {
+  content: "\e011";
+}
+.glyphicon-th-list:before {
+  content: "\e012";
+}
+.glyphicon-ok:before {
+  content: "\e013";
+}
+.glyphicon-remove:before {
+  content: "\e014";
+}
+.glyphicon-zoom-in:before {
+  content: "\e015";
+}
+.glyphicon-zoom-out:before {
+  content: "\e016";
+}
+.glyphicon-off:before {
+  content: "\e017";
+}
+.glyphicon-signal:before {
+  content: "\e018";
+}
+.glyphicon-cog:before {
+  content: "\e019";
+}
+.glyphicon-trash:before {
+  content: "\e020";
+}
+.glyphicon-home:before {
+  content: "\e021";
+}
+.glyphicon-file:before {
+  content: "\e022";
+}
+.glyphicon-time:before {
+  content: "\e023";
+}
+.glyphicon-road:before {
+  content: "\e024";
+}
+.glyphicon-download-alt:before {
+  content: "\e025";
+}
+.glyphicon-download:before {
+  content: "\e026";
+}
+.glyphicon-upload:before {
+  content: "\e027";
+}
+.glyphicon-inbox:before {
+  content: "\e028";
+}
+.glyphicon-play-circle:before {
+  content: "\e029";
+}
+.glyphicon-repeat:before {
+  content: "\e030";
+}
+.glyphicon-refresh:before {
+  content: "\e031";
+}
+.glyphicon-list-alt:before {
+  content: "\e032";
+}
+.glyphicon-lock:before {
+  content: "\e033";
+}
+.glyphicon-flag:before {
+  content: "\e034";
+}
+.glyphicon-headphones:before {
+  content: "\e035";
+}
+.glyphicon-volume-off:before {
+  content: "\e036";
+}
+.glyphicon-volume-down:before {
+  content: "\e037";
+}
+.glyphicon-volume-up:before {
+  content: "\e038";
+}
+.glyphicon-qrcode:before {
+  content: "\e039";
+}
+.glyphicon-barcode:before {
+  content: "\e040";
+}
+.glyphicon-tag:before {
+  content: "\e041";
+}
+.glyphicon-tags:before {
+  content: "\e042";
+}
+.glyphicon-book:before {
+  content: "\e043";
+}
+.glyphicon-bookmark:before {
+  content: "\e044";
+}
+.glyphicon-print:before {
+  content: "\e045";
+}
+.glyphicon-camera:before {
+  content: "\e046";
+}
+.glyphicon-font:before {
+  content: "\e047";
+}
+.glyphicon-bold:before {
+  content: "\e048";
+}
+.glyphicon-italic:before {
+  content: "\e049";
+}
+.glyphicon-text-height:before {
+  content: "\e050";
+}
+.glyphicon-text-width:before {
+  content: "\e051";
+}
+.glyphicon-align-left:before {
+  content: "\e052";
+}
+.glyphicon-align-center:before {
+  content: "\e053";
+}
+.glyphicon-align-right:before {
+  content: "\e054";
+}
+.glyphicon-align-justify:before {
+  content: "\e055";
+}
+.glyphicon-list:before {
+  content: "\e056";
+}
+.glyphicon-indent-left:before {
+  content: "\e057";
+}
+.glyphicon-indent-right:before {
+  content: "\e058";
+}
+.glyphicon-facetime-video:before {
+  content: "\e059";
+}
+.glyphicon-picture:before {
+  content: "\e060";
+}
+.glyphicon-map-marker:before {
+  content: "\e062";
+}
+.glyphicon-adjust:before {
+  content: "\e063";
+}
+.glyphicon-tint:before {
+  content: "\e064";
+}
+.glyphicon-edit:before {
+  content: "\e065";
+}
+.glyphicon-share:before {
+  content: "\e066";
+}
+.glyphicon-check:before {
+  content: "\e067";
+}
+.glyphicon-move:before {
+  content: "\e068";
+}
+.glyphicon-step-backward:before {
+  content: "\e069";
+}
+.glyphicon-fast-backward:before {
+  content: "\e070";
+}
+.glyphicon-backward:before {
+  content: "\e071";
+}
+.glyphicon-play:before {
+  content: "\e072";
+}
+.glyphicon-pause:before {
+  content: "\e073";
+}
+.glyphicon-stop:before {
+  content: "\e074";
+}
+.glyphicon-forward:before {
+  content: "\e075";
+}
+.glyphicon-fast-forward:before {
+  content: "\e076";
+}
+.glyphicon-step-forward:before {
+  content: "\e077";
+}
+.glyphicon-eject:before {
+  content: "\e078";
+}
+.glyphicon-chevron-left:before {
+  content: "\e079";
+}
+.glyphicon-chevron-right:before {
+  content: "\e080";
+}
+.glyphicon-plus-sign:before {
+  content: "\e081";
+}
+.glyphicon-minus-sign:before {
+  content: "\e082";
+}
+.glyphicon-remove-sign:before {
+  content: "\e083";
+}
+.glyphicon-ok-sign:before {
+  content: "\e084";
+}
+.glyphicon-question-sign:before {
+  content: "\e085";
+}
+.glyphicon-info-sign:before {
+  content: "\e086";
+}
+.glyphicon-screenshot:before {
+  content: "\e087";
+}
+.glyphicon-remove-circle:before {
+  content: "\e088";
+}
+.glyphicon-ok-circle:before {
+  content: "\e089";
+}
+.glyphicon-ban-circle:before {
+  content: "\e090";
+}
+.glyphicon-arrow-left:before {
+  content: "\e091";
+}
+.glyphicon-arrow-right:before {
+  content: "\e092";
+}
+.glyphicon-arrow-up:before {
+  content: "\e093";
+}
+.glyphicon-arrow-down:before {
+  content: "\e094";
+}
+.glyphicon-share-alt:before {
+  content: "\e095";
+}
+.glyphicon-resize-full:before {
+  content: "\e096";
+}
+.glyphicon-resize-small:before {
+  content: "\e097";
+}
+.glyphicon-exclamation-sign:before {
+  content: "\e101";
+}
+.glyphicon-gift:before {
+  content: "\e102";
+}
+.glyphicon-leaf:before {
+  content: "\e103";
+}
+.glyphicon-fire:before {
+  content: "\e104";
+}
+.glyphicon-eye-open:before {
+  content: "\e105";
+}
+.glyphicon-eye-close:before {
+  content: "\e106";
+}
+.glyphicon-warning-sign:before {
+  content: "\e107";
+}
+.glyphicon-plane:before {
+  content: "\e108";
+}
+.glyphicon-calendar:before {
+  content: "\e109";
+}
+.glyphicon-random:before {
+  content: "\e110";
+}
+.glyphicon-comment:before {
+  content: "\e111";
+}
+.glyphicon-magnet:before {
+  content: "\e112";
+}
+.glyphicon-chevron-up:before {
+  content: "\e113";
+}
+.glyphicon-chevron-down:before {
+  content: "\e114";
+}
+.glyphicon-retweet:before {
+  content: "\e115";
+}
+.glyphicon-shopping-cart:before {
+  content: "\e116";
+}
+.glyphicon-folder-close:before {
+  content: "\e117";
+}
+.glyphicon-folder-open:before {
+  content: "\e118";
+}
+.glyphicon-resize-vertical:before {
+  content: "\e119";
+}
+.glyphicon-resize-horizontal:before {
+  content: "\e120";
+}
+.glyphicon-hdd:before {
+  content: "\e121";
+}
+.glyphicon-bullhorn:before {
+  content: "\e122";
+}
+.glyphicon-bell:before {
+  content: "\e123";
+}
+.glyphicon-certificate:before {
+  content: "\e124";
+}
+.glyphicon-thumbs-up:before {
+  content: "\e125";
+}
+.glyphicon-thumbs-down:before {
+  content: "\e126";
+}
+.glyphicon-hand-right:before {
+  content: "\e127";
+}
+.glyphicon-hand-left:before {
+  content: "\e128";
+}
+.glyphicon-hand-up:before {
+  content: "\e129";
+}
+.glyphicon-hand-down:before {
+  content: "\e130";
+}
+.glyphicon-circle-arrow-right:before {
+  content: "\e131";
+}
+.glyphicon-circle-arrow-left:before {
+  content: "\e132";
+}
+.glyphicon-circle-arrow-up:before {
+  content: "\e133";
+}
+.glyphicon-circle-arrow-down:before {
+  content: "\e134";
+}
+.glyphicon-globe:before {
+  content: "\e135";
+}
+.glyphicon-wrench:before {
+  content: "\e136";
+}
+.glyphicon-tasks:before {
+  content: "\e137";
+}
+.glyphicon-filter:before {
+  content: "\e138";
+}
+.glyphicon-briefcase:before {
+  content: "\e139";
+}
+.glyphicon-fullscreen:before {
+  content: "\e140";
+}
+.glyphicon-dashboard:before {
+  content: "\e141";
+}
+.glyphicon-paperclip:before {
+  content: "\e142";
+}
+.glyphicon-heart-empty:before {
+  content: "\e143";
+}
+.glyphicon-link:before {
+  content: "\e144";
+}
+.glyphicon-phone:before {
+  content: "\e145";
+}
+.glyphicon-pushpin:before {
+  content: "\e146";
+}
+.glyphicon-usd:before {
+  content: "\e148";
+}
+.glyphicon-gbp:before {
+  content: "\e149";
+}
+.glyphicon-sort:before {
+  content: "\e150";
+}
+.glyphicon-sort-by-alphabet:before {
+  content: "\e151";
+}
+.glyphicon-sort-by-alphabet-alt:before {
+  content: "\e152";
+}
+.glyphicon-sort-by-order:before {
+  content: "\e153";
+}
+.glyphicon-sort-by-order-alt:before {
+  content: "\e154";
+}
+.glyphicon-sort-by-attributes:before {
+  content: "\e155";
+}
+.glyphicon-sort-by-attributes-alt:before {
+  content: "\e156";
+}
+.glyphicon-unchecked:before {
+  content: "\e157";
+}
+.glyphicon-expand:before {
+  content: "\e158";
+}
+.glyphicon-collapse-down:before {
+  content: "\e159";
+}
+.glyphicon-collapse-up:before {
+  content: "\e160";
+}
+.glyphicon-log-in:before {
+  content: "\e161";
+}
+.glyphicon-flash:before {
+  content: "\e162";
+}
+.glyphicon-log-out:before {
+  content: "\e163";
+}
+.glyphicon-new-window:before {
+  content: "\e164";
+}
+.glyphicon-record:before {
+  content: "\e165";
+}
+.glyphicon-save:before {
+  content: "\e166";
+}
+.glyphicon-open:before {
+  content: "\e167";
+}
+.glyphicon-saved:before {
+  content: "\e168";
+}
+.glyphicon-import:before {
+  content: "\e169";
+}
+.glyphicon-export:before {
+  content: "\e170";
+}
+.glyphicon-send:before {
+  content: "\e171";
+}
+.glyphicon-floppy-disk:before {
+  content: "\e172";
+}
+.glyphicon-floppy-saved:before {
+  content: "\e173";
+}
+.glyphicon-floppy-remove:before {
+  content: "\e174";
+}
+.glyphicon-floppy-save:before {
+  content: "\e175";
+}
+.glyphicon-floppy-open:before {
+  content: "\e176";
+}
+.glyphicon-credit-card:before {
+  content: "\e177";
+}
+.glyphicon-transfer:before {
+  content: "\e178";
+}
+.glyphicon-cutlery:before {
+  content: "\e179";
+}
+.glyphicon-header:before {
+  content: "\e180";
+}
+.glyphicon-compressed:before {
+  content: "\e181";
+}
+.glyphicon-earphone:before {
+  content: "\e182";
+}
+.glyphicon-phone-alt:before {
+  content: "\e183";
+}
+.glyphicon-tower:before {
+  content: "\e184";
+}
+.glyphicon-stats:before {
+  content: "\e185";
+}
+.glyphicon-sd-video:before {
+  content: "\e186";
+}
+.glyphicon-hd-video:before {
+  content: "\e187";
+}
+.glyphicon-subtitles:before {
+  content: "\e188";
+}
+.glyphicon-sound-stereo:before {
+  content: "\e189";
+}
+.glyphicon-sound-dolby:before {
+  content: "\e190";
+}
+.glyphicon-sound-5-1:before {
+  content: "\e191";
+}
+.glyphicon-sound-6-1:before {
+  content: "\e192";
+}
+.glyphicon-sound-7-1:before {
+  content: "\e193";
+}
+.glyphicon-copyright-mark:before {
+  content: "\e194";
+}
+.glyphicon-registration-mark:before {
+  content: "\e195";
+}
+.glyphicon-cloud-download:before {
+  content: "\e197";
+}
+.glyphicon-cloud-upload:before {
+  content: "\e198";
+}
+.glyphicon-tree-conifer:before {
+  content: "\e199";
+}
+.glyphicon-tree-deciduous:before {
+  content: "\e200";
+}
+* {
+  -webkit-box-sizing: border-box;
+     -moz-box-sizing: border-box;
+          box-sizing: border-box;
+}
+*:before,
+*:after {
+  -webkit-box-sizing: border-box;
+     -moz-box-sizing: border-box;
+          box-sizing: border-box;
+}
+html {
+  font-size: 10px;
+
+  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+body {
+  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+  font-size: 14px;
+  line-height: 1.42857143;
+  color: #333;
+  background-color: #fff;
+}
+input,
+button,
+select,
+textarea {
+  font-family: inherit;
+  font-size: inherit;
+  line-height: inherit;
+}
+a {
+  color: #428bca;
+  text-decoration: none;
+}
+a:hover,
+a:focus {
+  color: #2a6496;
+  text-decoration: underline;
+}
+a:focus {
+  outline: thin dotted;
+  outline: 5px auto -webkit-focus-ring-color;
+  outline-offset: -2px;
+}
+figure {
+  margin: 0;
+}
+img {
+  vertical-align: middle;
+}
+.img-responsive,
+.thumbnail > img,
+.thumbnail a > img,
+.carousel-inner > .item > img,
+.carousel-inner > .item > a > img {
+  display: block;
+  width: 100% \9;
+  max-width: 100%;
+  height: auto;
+}
+.img-rounded {
+  border-radius: 6px;
+}
+.img-thumbnail {
+  display: inline-block;
+  width: 100% \9;
+  max-width: 100%;
+  height: auto;
+  padding: 4px;
+  line-height: 1.42857143;
+  background-color: #fff;
+  border: 1px solid #ddd;
+  border-radius: 4px;
+  -webkit-transition: all .2s ease-in-out;
+       -o-transition: all .2s ease-in-out;
+          transition: all .2s ease-in-out;
+}
+.img-circle {
+  border-radius: 50%;
+}
+hr {
+  margin-top: 20px;
+  margin-bottom: 20px;
+  border: 0;
+  border-top: 1px solid #eee;
+}
+.sr-only {
+  position: absolute;
+  width: 1px;
+  height: 1px;
+  padding: 0;
+  margin: -1px;
+  overflow: hidden;
+  clip: rect(0, 0, 0, 0);
+  border: 0;
+}
+.sr-only-focusable:active,
+.sr-only-focusable:focus {
+  position: static;
+  width: auto;
+  height: auto;
+  margin: 0;
+  overflow: visible;
+  clip: auto;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+.h1,
+.h2,
+.h3,
+.h4,
+.h5,
+.h6 {
+  font-family: inherit;
+  font-weight: 500;
+  line-height: 1.1;
+  color: inherit;
+}
+h1 small,
+h2 small,
+h3 small,
+h4 small,
+h5 small,
+h6 small,
+.h1 small,
+.h2 small,
+.h3 small,
+.h4 small,
+.h5 small,
+.h6 small,
+h1 .small,
+h2 .small,
+h3 .small,
+h4 .small,
+h5 .small,
+h6 .small,
+.h1 .small,
+.h2 .small,
+.h3 .small,
+.h4 .small,
+.h5 .small,
+.h6 .small {
+  font-weight: normal;
+  line-height: 1;
+  color: #777;
+}
+h1,
+.h1,
+h2,
+.h2,
+h3,
+.h3 {
+  margin-top: 20px;
+  margin-bottom: 10px;
+}
+h1 small,
+.h1 small,
+h2 small,
+.h2 small,
+h3 small,
+.h3 small,
+h1 .small,
+.h1 .small,
+h2 .small,
+.h2 .small,
+h3 .small,
+.h3 .small {
+  font-size: 65%;
+}
+h4,
+.h4,
+h5,
+.h5,
+h6,
+.h6 {
+  margin-top: 10px;
+  margin-bottom: 10px;
+}
+h4 small,
+.h4 small,
+h5 small,
+.h5 small,
+h6 small,
+.h6 small,
+h4 .small,
+.h4 .small,
+h5 .small,
+.h5 .small,
+h6 .small,
+.h6 .small {
+  font-size: 75%;
+}
+h1,
+.h1 {
+  font-size: 36px;
+}
+h2,
+.h2 {
+  font-size: 30px;
+}
+h3,
+.h3 {
+  font-size: 24px;
+}
+h4,
+.h4 {
+  font-size: 18px;
+}
+h5,
+.h5 {
+  font-size: 14px;
+}
+h6,
+.h6 {
+  font-size: 12px;
+}
+p {
+  margin: 0 0 10px;
+}
+.lead {
+  margin-bottom: 20px;
+  font-size: 16px;
+  font-weight: 300;
+  line-height: 1.4;
+}
+@media (min-width: 768px) {
+  .lead {
+    font-size: 21px;
+  }
+}
+small,
+.small {
+  font-size: 85%;
+}
+cite {
+  font-style: normal;
+}
+mark,
+.mark {
+  padding: .2em;
+  background-color: #fcf8e3;
+}
+.text-left {
+  text-align: left;
+}
+.text-right {
+  text-align: right;
+}
+.text-center {
+  text-align: center;
+}
+.text-justify {
+  text-align: justify;
+}
+.text-nowrap {
+  white-space: nowrap;
+}
+.text-lowercase {
+  text-transform: lowercase;
+}
+.text-uppercase {
+  text-transform: uppercase;
+}
+.text-capitalize {
+  text-transform: capitalize;
+}
+.text-muted {
+  color: #777;
+}
+.text-primary {
+  color: #428bca;
+}
+a.text-primary:hover {
+  color: #3071a9;
+}
+.text-success {
+  color: #3c763d;
+}
+a.text-success:hover {
+  color: #2b542c;
+}
+.text-info {
+  color: #31708f;
+}
+a.text-info:hover {
+  color: #245269;
+}
+.text-warning {
+  color: #8a6d3b;
+}
+a.text-warning:hover {
+  color: #66512c;
+}
+.text-danger {
+  color: #a94442;
+}
+a.text-danger:hover {
+  color: #843534;
+}
+.bg-primary {
+  color: #fff;
+  background-color: #428bca;
+}
+a.bg-primary:hover {
+  background-color: #3071a9;
+}
+.bg-success {
+  background-color: #dff0d8;
+}
+a.bg-success:hover {
+  background-color: #c1e2b3;
+}
+.bg-info {
+  background-color: #d9edf7;
+}
+a.bg-info:hover {
+  background-color: #afd9ee;
+}
+.bg-warning {
+  background-color: #fcf8e3;
+}
+a.bg-warning:hover {
+  background-color: #f7ecb5;
+}
+.bg-danger {
+  background-color: #f2dede;
+}
+a.bg-danger:hover {
+  background-color: #e4b9b9;
+}
+.page-header {
+  padding-bottom: 9px;
+  margin: 40px 0 20px;
+  border-bottom: 1px solid #eee;
+}
+ul,
+ol {
+  margin-top: 0;
+  margin-bottom: 10px;
+}
+ul ul,
+ol ul,
+ul ol,
+ol ol {
+  margin-bottom: 0;
+}
+.list-unstyled {
+  padding-left: 0;
+  list-style: none;
+}
+.list-inline {
+  padding-left: 0;
+  margin-left: -5px;
+  list-style: none;
+}
+.list-inline > li {
+  display: inline-block;
+  padding-right: 5px;
+  padding-left: 5px;
+}
+dl {
+  margin-top: 0;
+  margin-bottom: 20px;
+}
+dt,
+dd {
+  line-height: 1.42857143;
+}
+dt {
+  font-weight: bold;
+}
+dd {
+  margin-left: 0;
+}
+@media (min-width: 768px) {
+  .dl-horizontal dt {
+    float: left;
+    width: 160px;
+    overflow: hidden;
+    clear: left;
+    text-align: right;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+  .dl-horizontal dd {
+    margin-left: 180px;
+  }
+}
+abbr[title],
+abbr[data-original-title] {
+  cursor: help;
+  border-bottom: 1px dotted #777;
+}
+.initialism {
+  font-size: 90%;
+  text-transform: uppercase;
+}
+blockquote {
+  padding: 10px 20px;
+  margin: 0 0 20px;
+  font-size: 17.5px;
+  border-left: 5px solid #eee;
+}
+blockquote p:last-child,
+blockquote ul:last-child,
+blockquote ol:last-child {
+  margin-bottom: 0;
+}
+blockquote footer,
+blockquote small,
+blockquote .small {
+  display: block;
+  font-size: 80%;
+  line-height: 1.42857143;
+  color: #777;
+}
+blockquote footer:before,
+blockquote small:before,
+blockquote .small:before {
+  content: '\2014 \00A0';
+}
+.blockquote-reverse,
+blockquote.pull-right {
+  padding-right: 15px;
+  padding-left: 0;
+  text-align: right;
+  border-right: 5px solid #eee;
+  border-left: 0;
+}
+.blockquote-reverse footer:before,
+blockquote.pull-right footer:before,
+.blockquote-reverse small:before,
+blockquote.pull-right small:before,
+.blockquote-reverse .small:before,
+blockquote.pull-right .small:before {
+  content: '';
+}
+.blockquote-reverse footer:after,
+blockquote.pull-right footer:after,
+.blockquote-reverse small:after,
+blockquote.pull-right small:after,
+.blockquote-reverse .small:after,
+blockquote.pull-right .small:after {
+  content: '\00A0 \2014';
+}
+blockquote:before,
+blockquote:after {
+  content: "";
+}
+address {
+  margin-bottom: 20px;
+  font-style: normal;
+  line-height: 1.42857143;
+}
+code,
+kbd,
+pre,
+samp {
+  font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
+}
+code {
+  padding: 2px 4px;
+  font-size: 90%;
+  color: #c7254e;
+  background-color: #f9f2f4;
+  border-radius: 4px;
+}
+kbd {
+  padding: 2px 4px;
+  font-size: 90%;
+  color: #fff;
+  background-color: #333;
+  border-radius: 3px;
+  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25);
+          box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25);
+}
+kbd kbd {
+  padding: 0;
+  font-size: 100%;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+pre {
+  display: block;
+  padding: 9.5px;
+  margin: 0 0 10px;
+  font-size: 13px;
+  line-height: 1.42857143;
+  color: #333;
+  word-break: break-all;
+  word-wrap: break-word;
+  background-color: #f5f5f5;
+  border: 1px solid #ccc;
+  border-radius: 4px;
+}
+pre code {
+  padding: 0;
+  font-size: inherit;
+  color: inherit;
+  white-space: pre-wrap;
+  background-color: transparent;
+  border-radius: 0;
+}
+.pre-scrollable {
+  max-height: 340px;
+  overflow-y: scroll;
+}
+.container {
+  padding-right: 15px;
+  padding-left: 15px;
+  margin-right: auto;
+  margin-left: auto;
+}
+@media (min-width: 768px) {
+  .container {
+    width: 750px;
+  }
+}
+@media (min-width: 992px) {
+  .container {
+    width: 970px;
+  }
+}
+@media (min-width: 1200px) {
+  .container {
+    width: 1170px;
+  }
+}
+.container-fluid {
+  padding-right: 15px;
+  padding-left: 15px;
+  margin-right: auto;
+  margin-left: auto;
+}
+.row {
+  margin-right: -15px;
+  margin-left: -15px;
+}
+.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {
+  position: relative;
+  min-height: 1px;
+  padding-right: 15px;
+  padding-left: 15px;
+}
+.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {
+  float: left;
+}
+.col-xs-12 {
+  width: 100%;
+}
+.col-xs-11 {
+  width: 91.66666667%;
+}
+.col-xs-10 {
+  width: 83.33333333%;
+}
+.col-xs-9 {
+  width: 75%;
+}
+.col-xs-8 {
+  width: 66.66666667%;
+}
+.col-xs-7 {
+  width: 58.33333333%;
+}
+.col-xs-6 {
+  width: 50%;
+}
+.col-xs-5 {
+  width: 41.66666667%;
+}
+.col-xs-4 {
+  width: 33.33333333%;
+}
+.col-xs-3 {
+  width: 25%;
+}
+.col-xs-2 {
+  width: 16.66666667%;
+}
+.col-xs-1 {
+  width: 8.33333333%;
+}
+.col-xs-pull-12 {
+  right: 100%;
+}
+.col-xs-pull-11 {
+  right: 91.66666667%;
+}
+.col-xs-pull-10 {
+  right: 83.33333333%;
+}
+.col-xs-pull-9 {
+  right: 75%;
+}
+.col-xs-pull-8 {
+  right: 66.66666667%;
+}
+.col-xs-pull-7 {
+  right: 58.33333333%;
+}
+.col-xs-pull-6 {
+  right: 50%;
+}
+.col-xs-pull-5 {
+  right: 41.66666667%;
+}
+.col-xs-pull-4 {
+  right: 33.33333333%;
+}
+.col-xs-pull-3 {
+  right: 25%;
+}
+.col-xs-pull-2 {
+  right: 16.66666667%;
+}
+.col-xs-pull-1 {
+  right: 8.33333333%;
+}
+.col-xs-pull-0 {
+  right: auto;
+}
+.col-xs-push-12 {
+  left: 100%;
+}
+.col-xs-push-11 {
+  left: 91.66666667%;
+}
+.col-xs-push-10 {
+  left: 83.33333333%;
+}
+.col-xs-push-9 {
+  left: 75%;
+}
+.col-xs-push-8 {
+  left: 66.66666667%;
+}
+.col-xs-push-7 {
+  left: 58.33333333%;
+}
+.col-xs-push-6 {
+  left: 50%;
+}
+.col-xs-push-5 {
+  left: 41.66666667%;
+}
+.col-xs-push-4 {
+  left: 33.33333333%;
+}
+.col-xs-push-3 {
+  left: 25%;
+}
+.col-xs-push-2 {
+  left: 16.66666667%;
+}
+.col-xs-push-1 {
+  left: 8.33333333%;
+}
+.col-xs-push-0 {
+  left: auto;
+}
+.col-xs-offset-12 {
+  margin-left: 100%;
+}
+.col-xs-offset-11 {
+  margin-left: 91.66666667%;
+}
+.col-xs-offset-10 {
+  margin-left: 83.33333333%;
+}
+.col-xs-offset-9 {
+  margin-left: 75%;
+}
+.col-xs-offset-8 {
+  margin-left: 66.66666667%;
+}
+.col-xs-offset-7 {
+  margin-left: 58.33333333%;
+}
+.col-xs-offset-6 {
+  margin-left: 50%;
+}
+.col-xs-offset-5 {
+  margin-left: 41.66666667%;
+}
+.col-xs-offset-4 {
+  margin-left: 33.33333333%;
+}
+.col-xs-offset-3 {
+  margin-left: 25%;
+}
+.col-xs-offset-2 {
+  margin-left: 16.66666667%;
+}
+.col-xs-offset-1 {
+  margin-left: 8.33333333%;
+}
+.col-xs-offset-0 {
+  margin-left: 0;
+}
+@media (min-width: 768px) {
+  .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {
+    float: left;
+  }
+  .col-sm-12 {
+    width: 100%;
+  }
+  .col-sm-11 {
+    width: 91.66666667%;
+  }
+  .col-sm-10 {
+    width: 83.33333333%;
+  }
+  .col-sm-9 {
+    width: 75%;
+  }
+  .col-sm-8 {
+    width: 66.66666667%;
+  }
+  .col-sm-7 {
+    width: 58.33333333%;
+  }
+  .col-sm-6 {
+    width: 50%;
+  }
+  .col-sm-5 {
+    width: 41.66666667%;
+  }
+  .col-sm-4 {
+    width: 33.33333333%;
+  }
+  .col-sm-3 {
+    width: 25%;
+  }
+  .col-sm-2 {
+    width: 16.66666667%;
+  }
+  .col-sm-1 {
+    width: 8.33333333%;
+  }
+  .col-sm-pull-12 {
+    right: 100%;
+  }
+  .col-sm-pull-11 {
+    right: 91.66666667%;
+  }
+  .col-sm-pull-10 {
+    right: 83.33333333%;
+  }
+  .col-sm-pull-9 {
+    right: 75%;
+  }
+  .col-sm-pull-8 {
+    right: 66.66666667%;
+  }
+  .col-sm-pull-7 {
+    right: 58.33333333%;
+  }
+  .col-sm-pull-6 {
+    right: 50%;
+  }
+  .col-sm-pull-5 {
+    right: 41.66666667%;
+  }
+  .col-sm-pull-4 {
+    right: 33.33333333%;
+  }
+  .col-sm-pull-3 {
+    right: 25%;
+  }
+  .col-sm-pull-2 {
+    right: 16.66666667%;
+  }
+  .col-sm-pull-1 {
+    right: 8.33333333%;
+  }
+  .col-sm-pull-0 {
+    right: auto;
+  }
+  .col-sm-push-12 {
+    left: 100%;
+  }
+  .col-sm-push-11 {
+    left: 91.66666667%;
+  }
+  .col-sm-push-10 {
+    left: 83.33333333%;
+  }
+  .col-sm-push-9 {
+    left: 75%;
+  }
+  .col-sm-push-8 {
+    left: 66.66666667%;
+  }
+  .col-sm-push-7 {
+    left: 58.33333333%;
+  }
+  .col-sm-push-6 {
+    left: 50%;
+  }
+  .col-sm-push-5 {
+    left: 41.66666667%;
+  }
+  .col-sm-push-4 {
+    left: 33.33333333%;
+  }
+  .col-sm-push-3 {
+    left: 25%;
+  }
+  .col-sm-push-2 {
+    left: 16.66666667%;
+  }
+  .col-sm-push-1 {
+    left: 8.33333333%;
+  }
+  .col-sm-push-0 {
+    left: auto;
+  }
+  .col-sm-offset-12 {
+    margin-left: 100%;
+  }
+  .col-sm-offset-11 {
+    margin-left: 91.66666667%;
+  }
+  .col-sm-offset-10 {
+    margin-left: 83.33333333%;
+  }
+  .col-sm-offset-9 {
+    margin-left: 75%;
+  }
+  .col-sm-offset-8 {
+    margin-left: 66.66666667%;
+  }
+  .col-sm-offset-7 {
+    margin-left: 58.33333333%;
+  }
+  .col-sm-offset-6 {
+    margin-left: 50%;
+  }
+  .col-sm-offset-5 {
+    margin-left: 41.66666667%;
+  }
+  .col-sm-offset-4 {
+    margin-left: 33.33333333%;
+  }
+  .col-sm-offset-3 {
+    margin-left: 25%;
+  }
+  .col-sm-offset-2 {
+    margin-left: 16.66666667%;
+  }
+  .col-sm-offset-1 {
+    margin-left: 8.33333333%;
+  }
+  .col-sm-offset-0 {
+    margin-left: 0;
+  }
+}
+@media (min-width: 992px) {
+  .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {
+    float: left;
+  }
+  .col-md-12 {
+    width: 100%;
+  }
+  .col-md-11 {
+    width: 91.66666667%;
+  }
+  .col-md-10 {
+    width: 83.33333333%;
+  }
+  .col-md-9 {
+    width: 75%;
+  }
+  .col-md-8 {
+    width: 66.66666667%;
+  }
+  .col-md-7 {
+    width: 58.33333333%;
+  }
+  .col-md-6 {
+    width: 50%;
+  }
+  .col-md-5 {
+    width: 41.66666667%;
+  }
+  .col-md-4 {
+    width: 33.33333333%;
+  }
+  .col-md-3 {
+    width: 25%;
+  }
+  .col-md-2 {
+    width: 16.66666667%;
+  }
+  .col-md-1 {
+    width: 8.33333333%;
+  }
+  .col-md-pull-12 {
+    right: 100%;
+  }
+  .col-md-pull-11 {
+    right: 91.66666667%;
+  }
+  .col-md-pull-10 {
+    right: 83.33333333%;
+  }
+  .col-md-pull-9 {
+    right: 75%;
+  }
+  .col-md-pull-8 {
+    right: 66.66666667%;
+  }
+  .col-md-pull-7 {
+    right: 58.33333333%;
+  }
+  .col-md-pull-6 {
+    right: 50%;
+  }
+  .col-md-pull-5 {
+    right: 41.66666667%;
+  }
+  .col-md-pull-4 {
+    right: 33.33333333%;
+  }
+  .col-md-pull-3 {
+    right: 25%;
+  }
+  .col-md-pull-2 {
+    right: 16.66666667%;
+  }
+  .col-md-pull-1 {
+    right: 8.33333333%;
+  }
+  .col-md-pull-0 {
+    right: auto;
+  }
+  .col-md-push-12 {
+    left: 100%;
+  }
+  .col-md-push-11 {
+    left: 91.66666667%;
+  }
+  .col-md-push-10 {
+    left: 83.33333333%;
+  }
+  .col-md-push-9 {
+    left: 75%;
+  }
+  .col-md-push-8 {
+    left: 66.66666667%;
+  }
+  .col-md-push-7 {
+    left: 58.33333333%;
+  }
+  .col-md-push-6 {
+    left: 50%;
+  }
+  .col-md-push-5 {
+    left: 41.66666667%;
+  }
+  .col-md-push-4 {
+    left: 33.33333333%;
+  }
+  .col-md-push-3 {
+    left: 25%;
+  }
+  .col-md-push-2 {
+    left: 16.66666667%;
+  }
+  .col-md-push-1 {
+    left: 8.33333333%;
+  }
+  .col-md-push-0 {
+    left: auto;
+  }
+  .col-md-offset-12 {
+    margin-left: 100%;
+  }
+  .col-md-offset-11 {
+    margin-left: 91.66666667%;
+  }
+  .col-md-offset-10 {
+    margin-left: 83.33333333%;
+  }
+  .col-md-offset-9 {
+    margin-left: 75%;
+  }
+  .col-md-offset-8 {
+    margin-left: 66.66666667%;
+  }
+  .col-md-offset-7 {
+    margin-left: 58.33333333%;
+  }
+  .col-md-offset-6 {
+    margin-left: 50%;
+  }
+  .col-md-offset-5 {
+    margin-left: 41.66666667%;
+  }
+  .col-md-offset-4 {
+    margin-left: 33.33333333%;
+  }
+  .col-md-offset-3 {
+    margin-left: 25%;
+  }
+  .col-md-offset-2 {
+    margin-left: 16.66666667%;
+  }
+  .col-md-offset-1 {
+    margin-left: 8.33333333%;
+  }
+  .col-md-offset-0 {
+    margin-left: 0;
+  }
+}
+@media (min-width: 1200px) {
+  .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {
+    float: left;
+  }
+  .col-lg-12 {
+    width: 100%;
+  }
+  .col-lg-11 {
+    width: 91.66666667%;
+  }
+  .col-lg-10 {
+    width: 83.33333333%;
+  }
+  .col-lg-9 {
+    width: 75%;
+  }
+  .col-lg-8 {
+    width: 66.66666667%;
+  }
+  .col-lg-7 {
+    width: 58.33333333%;
+  }
+  .col-lg-6 {
+    width: 50%;
+  }
+  .col-lg-5 {
+    width: 41.66666667%;
+  }
+  .col-lg-4 {
+    width: 33.33333333%;
+  }
+  .col-lg-3 {
+    width: 25%;
+  }
+  .col-lg-2 {
+    width: 16.66666667%;
+  }
+  .col-lg-1 {
+    width: 8.33333333%;
+  }
+  .col-lg-pull-12 {
+    right: 100%;
+  }
+  .col-lg-pull-11 {
+    right: 91.66666667%;
+  }
+  .col-lg-pull-10 {
+    right: 83.33333333%;
+  }
+  .col-lg-pull-9 {
+    right: 75%;
+  }
+  .col-lg-pull-8 {
+    right: 66.66666667%;
+  }
+  .col-lg-pull-7 {
+    right: 58.33333333%;
+  }
+  .col-lg-pull-6 {
+    right: 50%;
+  }
+  .col-lg-pull-5 {
+    right: 41.66666667%;
+  }
+  .col-lg-pull-4 {
+    right: 33.33333333%;
+  }
+  .col-lg-pull-3 {
+    right: 25%;
+  }
+  .col-lg-pull-2 {
+    right: 16.66666667%;
+  }
+  .col-lg-pull-1 {
+    right: 8.33333333%;
+  }
+  .col-lg-pull-0 {
+    right: auto;
+  }
+  .col-lg-push-12 {
+    left: 100%;
+  }
+  .col-lg-push-11 {
+    left: 91.66666667%;
+  }
+  .col-lg-push-10 {
+    left: 83.33333333%;
+  }
+  .col-lg-push-9 {
+    left: 75%;
+  }
+  .col-lg-push-8 {
+    left: 66.66666667%;
+  }
+  .col-lg-push-7 {
+    left: 58.33333333%;
+  }
+  .col-lg-push-6 {
+    left: 50%;
+  }
+  .col-lg-push-5 {
+    left: 41.66666667%;
+  }
+  .col-lg-push-4 {
+    left: 33.33333333%;
+  }
+  .col-lg-push-3 {
+    left: 25%;
+  }
+  .col-lg-push-2 {
+    left: 16.66666667%;
+  }
+  .col-lg-push-1 {
+    left: 8.33333333%;
+  }
+  .col-lg-push-0 {
+    left: auto;
+  }
+  .col-lg-offset-12 {
+    margin-left: 100%;
+  }
+  .col-lg-offset-11 {
+    margin-left: 91.66666667%;
+  }
+  .col-lg-offset-10 {
+    margin-left: 83.33333333%;
+  }
+  .col-lg-offset-9 {
+    margin-left: 75%;
+  }
+  .col-lg-offset-8 {
+    margin-left: 66.66666667%;
+  }
+  .col-lg-offset-7 {
+    margin-left: 58.33333333%;
+  }
+  .col-lg-offset-6 {
+    margin-left: 50%;
+  }
+  .col-lg-offset-5 {
+    margin-left: 41.66666667%;
+  }
+  .col-lg-offset-4 {
+    margin-left: 33.33333333%;
+  }
+  .col-lg-offset-3 {
+    margin-left: 25%;
+  }
+  .col-lg-offset-2 {
+    margin-left: 16.66666667%;
+  }
+  .col-lg-offset-1 {
+    margin-left: 8.33333333%;
+  }
+  .col-lg-offset-0 {
+    margin-left: 0;
+  }
+}
+table {
+  background-color: transparent;
+}
+th {
+  text-align: left;
+}
+.table {
+  width: 100%;
+  max-width: 100%;
+  margin-bottom: 20px;
+}
+.table > thead > tr > th,
+.table > tbody > tr > th,
+.table > tfoot > tr > th,
+.table > thead > tr > td,
+.table > tbody > tr > td,
+.table > tfoot > tr > td {
+  padding: 8px;
+  line-height: 1.42857143;
+  vertical-align: top;
+  border-top: 1px solid #ddd;
+}
+.table > thead > tr > th {
+  vertical-align: bottom;
+  border-bottom: 2px solid #ddd;
+}
+.table > caption + thead > tr:first-child > th,
+.table > colgroup + thead > tr:first-child > th,
+.table > thead:first-child > tr:first-child > th,
+.table > caption + thead > tr:first-child > td,
+.table > colgroup + thead > tr:first-child > td,
+.table > thead:first-child > tr:first-child > td {
+  border-top: 0;
+}
+.table > tbody + tbody {
+  border-top: 2px solid #ddd;
+}
+.table .table {
+  background-color: #fff;
+}
+.table-condensed > thead > tr > th,
+.table-condensed > tbody > tr > th,
+.table-condensed > tfoot > tr > th,
+.table-condensed > thead > tr > td,
+.table-condensed > tbody > tr > td,
+.table-condensed > tfoot > tr > td {
+  padding: 5px;
+}
+.table-bordered {
+  border: 1px solid #ddd;
+}
+.table-bordered > thead > tr > th,
+.table-bordered > tbody > tr > th,
+.table-bordered > tfoot > tr > th,
+.table-bordered > thead > tr > td,
+.table-bordered > tbody > tr > td,
+.table-bordered > tfoot > tr > td {
+  border: 1px solid #ddd;
+}
+.table-bordered > thead > tr > th,
+.table-bordered > thead > tr > td {
+  border-bottom-width: 2px;
+}
+.table-striped > tbody > tr:nth-child(odd) > td,
+.table-striped > tbody > tr:nth-child(odd) > th {
+  background-color: #f9f9f9;
+}
+.table-hover > tbody > tr:hover > td,
+.table-hover > tbody > tr:hover > th {
+  background-color: #f5f5f5;
+}
+table col[class*="col-"] {
+  position: static;
+  display: table-column;
+  float: none;
+}
+table td[class*="col-"],
+table th[class*="col-"] {
+  position: static;
+  display: table-cell;
+  float: none;
+}
+.table > thead > tr > td.active,
+.table > tbody > tr > td.active,
+.table > tfoot > tr > td.active,
+.table > thead > tr > th.active,
+.table > tbody > tr > th.active,
+.table > tfoot > tr > th.active,
+.table > thead > tr.active > td,
+.table > tbody > tr.active > td,
+.table > tfoot > tr.active > td,
+.table > thead > tr.active > th,
+.table > tbody > tr.active > th,
+.table > tfoot > tr.active > th {
+  background-color: #f5f5f5;
+}
+.table-hover > tbody > tr > td.active:hover,
+.table-hover > tbody > tr > th.active:hover,
+.table-hover > tbody > tr.active:hover > td,
+.table-hover > tbody > tr:hover > .active,
+.table-hover > tbody > tr.active:hover > th {
+  background-color: #e8e8e8;
+}
+.table > thead > tr > td.success,
+.table > tbody > tr > td.success,
+.table > tfoot > tr > td.success,
+.table > thead > tr > th.success,
+.table > tbody > tr > th.success,
+.table > tfoot > tr > th.success,
+.table > thead > tr.success > td,
+.table > tbody > tr.success > td,
+.table > tfoot > tr.success > td,
+.table > thead > tr.success > th,
+.table > tbody > tr.success > th,
+.table > tfoot > tr.success > th {
+  background-color: #dff0d8;
+}
+.table-hover > tbody > tr > td.success:hover,
+.table-hover > tbody > tr > th.success:hover,
+.table-hover > tbody > tr.success:hover > td,
+.table-hover > tbody > tr:hover > .success,
+.table-hover > tbody > tr.success:hover > th {
+  background-color: #d0e9c6;
+}
+.table > thead > tr > td.info,
+.table > tbody > tr > td.info,
+.table > tfoot > tr > td.info,
+.table > thead > tr > th.info,
+.table > tbody > tr > th.info,
+.table > tfoot > tr > th.info,
+.table > thead > tr.info > td,
+.table > tbody > tr.info > td,
+.table > tfoot > tr.info > td,
+.table > thead > tr.info > th,
+.table > tbody > tr.info > th,
+.table > tfoot > tr.info > th {
+  background-color: #d9edf7;
+}
+.table-hover > tbody > tr > td.info:hover,
+.table-hover > tbody > tr > th.info:hover,
+.table-hover > tbody > tr.info:hover > td,
+.table-hover > tbody > tr:hover > .info,
+.table-hover > tbody > tr.info:hover > th {
+  background-color: #c4e3f3;
+}
+.table > thead > tr > td.warning,
+.table > tbody > tr > td.warning,
+.table > tfoot > tr > td.warning,
+.table > thead > tr > th.warning,
+.table > tbody > tr > th.warning,
+.table > tfoot > tr > th.warning,
+.table > thead > tr.warning > td,
+.table > tbody > tr.warning > td,
+.table > tfoot > tr.warning > td,
+.table > thead > tr.warning > th,
+.table > tbody > tr.warning > th,
+.table > tfoot > tr.warning > th {
+  background-color: #fcf8e3;
+}
+.table-hover > tbody > tr > td.warning:hover,
+.table-hover > tbody > tr > th.warning:hover,
+.table-hover > tbody > tr.warning:hover > td,
+.table-hover > tbody > tr:hover > .warning,
+.table-hover > tbody > tr.warning:hover > th {
+  background-color: #faf2cc;
+}
+.table > thead > tr > td.danger,
+.table > tbody > tr > td.danger,
+.table > tfoot > tr > td.danger,
+.table > thead > tr > th.danger,
+.table > tbody > tr > th.danger,
+.table > tfoot > tr > th.danger,
+.table > thead > tr.danger > td,
+.table > tbody > tr.danger > td,
+.table > tfoot > tr.danger > td,
+.table > thead > tr.danger > th,
+.table > tbody > tr.danger > th,
+.table > tfoot > tr.danger > th {
+  background-color: #f2dede;
+}
+.table-hover > tbody > tr > td.danger:hover,
+.table-hover > tbody > tr > th.danger:hover,
+.table-hover > tbody > tr.danger:hover > td,
+.table-hover > tbody > tr:hover > .danger,
+.table-hover > tbody > tr.danger:hover > th {
+  background-color: #ebcccc;
+}
+@media screen and (max-width: 767px) {
+  .table-responsive {
+    width: 100%;
+    margin-bottom: 15px;
+    overflow-x: auto;
+    overflow-y: hidden;
+    -webkit-overflow-scrolling: touch;
+    -ms-overflow-style: -ms-autohiding-scrollbar;
+    border: 1px solid #ddd;
+  }
+  .table-responsive > .table {
+    margin-bottom: 0;
+  }
+  .table-responsive > .table > thead > tr > th,
+  .table-responsive > .table > tbody > tr > th,
+  .table-responsive > .table > tfoot > tr > th,
+  .table-responsive > .table > thead > tr > td,
+  .table-responsive > .table > tbody > tr > td,
+  .table-responsive > .table > tfoot > tr > td {
+    white-space: nowrap;
+  }
+  .table-responsive > .table-bordered {
+    border: 0;
+  }
+  .table-responsive > .table-bordered > thead > tr > th:first-child,
+  .table-responsive > .table-bordered > tbody > tr > th:first-child,
+  .table-responsive > .table-bordered > tfoot > tr > th:first-child,
+  .table-responsive > .table-bordered > thead > tr > td:first-child,
+  .table-responsive > .table-bordered > tbody > tr > td:first-child,
+  .table-responsive > .table-bordered > tfoot > tr > td:first-child {
+    border-left: 0;
+  }
+  .table-responsive > .table-bordered > thead > tr > th:last-child,
+  .table-responsive > .table-bordered > tbody > tr > th:last-child,
+  .table-responsive > .table-bordered > tfoot > tr > th:last-child,
+  .table-responsive > .table-bordered > thead > tr > td:last-child,
+  .table-responsive > .table-bordered > tbody > tr > td:last-child,
+  .table-responsive > .table-bordered > tfoot > tr > td:last-child {
+    border-right: 0;
+  }
+  .table-responsive > .table-bordered > tbody > tr:last-child > th,
+  .table-responsive > .table-bordered > tfoot > tr:last-child > th,
+  .table-responsive > .table-bordered > tbody > tr:last-child > td,
+  .table-responsive > .table-bordered > tfoot > tr:last-child > td {
+    border-bottom: 0;
+  }
+}
+fieldset {
+  min-width: 0;
+  padding: 0;
+  margin: 0;
+  border: 0;
+}
+legend {
+  display: block;
+  width: 100%;
+  padding: 0;
+  margin-bottom: 20px;
+  font-size: 21px;
+  line-height: inherit;
+  color: #333;
+  border: 0;
+  border-bottom: 1px solid #e5e5e5;
+}
+label {
+  display: inline-block;
+  max-width: 100%;
+  margin-bottom: 5px;
+  font-weight: bold;
+}
+input[type="search"] {
+  -webkit-box-sizing: border-box;
+     -moz-box-sizing: border-box;
+          box-sizing: border-box;
+}
+input[type="radio"],
+input[type="checkbox"] {
+  margin: 4px 0 0;
+  margin-top: 1px \9;
+  line-height: normal;
+}
+input[type="file"] {
+  display: block;
+}
+input[type="range"] {
+  display: block;
+  width: 100%;
+}
+select[multiple],
+select[size] {
+  height: auto;
+}
+input[type="file"]:focus,
+input[type="radio"]:focus,
+input[type="checkbox"]:focus {
+  outline: thin dotted;
+  outline: 5px auto -webkit-focus-ring-color;
+  outline-offset: -2px;
+}
+output {
+  display: block;
+  padding-top: 7px;
+  font-size: 14px;
+  line-height: 1.42857143;
+  color: #555;
+}
+.form-control {
+  display: block;
+  width: 100%;
+  height: 34px;
+  padding: 6px 12px;
+  font-size: 14px;
+  line-height: 1.42857143;
+  color: #555;
+  background-color: #fff;
+  background-image: none;
+  border: 1px solid #ccc;
+  border-radius: 4px;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+  -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;
+       -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
+          transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
+}
+.form-control:focus {
+  border-color: #66afe9;
+  outline: 0;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
+          box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
+}
+.form-control::-moz-placeholder {
+  color: #777;
+  opacity: 1;
+}
+.form-control:-ms-input-placeholder {
+  color: #777;
+}
+.form-control::-webkit-input-placeholder {
+  color: #777;
+}
+.form-control[disabled],
+.form-control[readonly],
+fieldset[disabled] .form-control {
+  cursor: not-allowed;
+  background-color: #eee;
+  opacity: 1;
+}
+textarea.form-control {
+  height: auto;
+}
+input[type="search"] {
+  -webkit-appearance: none;
+}
+input[type="date"],
+input[type="time"],
+input[type="datetime-local"],
+input[type="month"] {
+  line-height: 34px;
+  line-height: 1.42857143 \0;
+}
+input[type="date"].input-sm,
+input[type="time"].input-sm,
+input[type="datetime-local"].input-sm,
+input[type="month"].input-sm {
+  line-height: 30px;
+}
+input[type="date"].input-lg,
+input[type="time"].input-lg,
+input[type="datetime-local"].input-lg,
+input[type="month"].input-lg {
+  line-height: 46px;
+}
+.form-group {
+  margin-bottom: 15px;
+}
+.radio,
+.checkbox {
+  position: relative;
+  display: block;
+  min-height: 20px;
+  margin-top: 10px;
+  margin-bottom: 10px;
+}
+.radio label,
+.checkbox label {
+  padding-left: 20px;
+  margin-bottom: 0;
+  font-weight: normal;
+  cursor: pointer;
+}
+.radio input[type="radio"],
+.radio-inline input[type="radio"],
+.checkbox input[type="checkbox"],
+.checkbox-inline input[type="checkbox"] {
+  position: absolute;
+  margin-top: 4px \9;
+  margin-left: -20px;
+}
+.radio + .radio,
+.checkbox + .checkbox {
+  margin-top: -5px;
+}
+.radio-inline,
+.checkbox-inline {
+  display: inline-block;
+  padding-left: 20px;
+  margin-bottom: 0;
+  font-weight: normal;
+  vertical-align: middle;
+  cursor: pointer;
+}
+.radio-inline + .radio-inline,
+.checkbox-inline + .checkbox-inline {
+  margin-top: 0;
+  margin-left: 10px;
+}
+input[type="radio"][disabled],
+input[type="checkbox"][disabled],
+input[type="radio"].disabled,
+input[type="checkbox"].disabled,
+fieldset[disabled] input[type="radio"],
+fieldset[disabled] input[type="checkbox"] {
+  cursor: not-allowed;
+}
+.radio-inline.disabled,
+.checkbox-inline.disabled,
+fieldset[disabled] .radio-inline,
+fieldset[disabled] .checkbox-inline {
+  cursor: not-allowed;
+}
+.radio.disabled label,
+.checkbox.disabled label,
+fieldset[disabled] .radio label,
+fieldset[disabled] .checkbox label {
+  cursor: not-allowed;
+}
+.form-control-static {
+  padding-top: 7px;
+  padding-bottom: 7px;
+  margin-bottom: 0;
+}
+.form-control-static.input-lg,
+.form-control-static.input-sm {
+  padding-right: 0;
+  padding-left: 0;
+}
+.input-sm,
+.form-horizontal .form-group-sm .form-control {
+  height: 30px;
+  padding: 5px 10px;
+  font-size: 12px;
+  line-height: 1.5;
+  border-radius: 3px;
+}
+select.input-sm {
+  height: 30px;
+  line-height: 30px;
+}
+textarea.input-sm,
+select[multiple].input-sm {
+  height: auto;
+}
+.input-lg,
+.form-horizontal .form-group-lg .form-control {
+  height: 46px;
+  padding: 10px 16px;
+  font-size: 18px;
+  line-height: 1.33;
+  border-radius: 6px;
+}
+select.input-lg {
+  height: 46px;
+  line-height: 46px;
+}
+textarea.input-lg,
+select[multiple].input-lg {
+  height: auto;
+}
+.has-feedback {
+  position: relative;
+}
+.has-feedback .form-control {
+  padding-right: 42.5px;
+}
+.form-control-feedback {
+  position: absolute;
+  top: 25px;
+  right: 0;
+  z-index: 2;
+  display: block;
+  width: 34px;
+  height: 34px;
+  line-height: 34px;
+  text-align: center;
+}
+.input-lg + .form-control-feedback {
+  width: 46px;
+  height: 46px;
+  line-height: 46px;
+}
+.input-sm + .form-control-feedback {
+  width: 30px;
+  height: 30px;
+  line-height: 30px;
+}
+.has-success .help-block,
+.has-success .control-label,
+.has-success .radio,
+.has-success .checkbox,
+.has-success .radio-inline,
+.has-success .checkbox-inline {
+  color: #3c763d;
+}
+.has-success .form-control {
+  border-color: #3c763d;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+}
+.has-success .form-control:focus {
+  border-color: #2b542c;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168;
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168;
+}
+.has-success .input-group-addon {
+  color: #3c763d;
+  background-color: #dff0d8;
+  border-color: #3c763d;
+}
+.has-success .form-control-feedback {
+  color: #3c763d;
+}
+.has-warning .help-block,
+.has-warning .control-label,
+.has-warning .radio,
+.has-warning .checkbox,
+.has-warning .radio-inline,
+.has-warning .checkbox-inline {
+  color: #8a6d3b;
+}
+.has-warning .form-control {
+  border-color: #8a6d3b;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+}
+.has-warning .form-control:focus {
+  border-color: #66512c;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b;
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b;
+}
+.has-warning .input-group-addon {
+  color: #8a6d3b;
+  background-color: #fcf8e3;
+  border-color: #8a6d3b;
+}
+.has-warning .form-control-feedback {
+  color: #8a6d3b;
+}
+.has-error .help-block,
+.has-error .control-label,
+.has-error .radio,
+.has-error .checkbox,
+.has-error .radio-inline,
+.has-error .checkbox-inline {
+  color: #a94442;
+}
+.has-error .form-control {
+  border-color: #a94442;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+}
+.has-error .form-control:focus {
+  border-color: #843534;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483;
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483;
+}
+.has-error .input-group-addon {
+  color: #a94442;
+  background-color: #f2dede;
+  border-color: #a94442;
+}
+.has-error .form-control-feedback {
+  color: #a94442;
+}
+.has-feedback label.sr-only ~ .form-control-feedback {
+  top: 0;
+}
+.help-block {
+  display: block;
+  margin-top: 5px;
+  margin-bottom: 10px;
+  color: #737373;
+}
+@media (min-width: 768px) {
+  .form-inline .form-group {
+    display: inline-block;
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  .form-inline .form-control {
+    display: inline-block;
+    width: auto;
+    vertical-align: middle;
+  }
+  .form-inline .input-group {
+    display: inline-table;
+    vertical-align: middle;
+  }
+  .form-inline .input-group .input-group-addon,
+  .form-inline .input-group .input-group-btn,
+  .form-inline .input-group .form-control {
+    width: auto;
+  }
+  .form-inline .input-group > .form-control {
+    width: 100%;
+  }
+  .form-inline .control-label {
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  .form-inline .radio,
+  .form-inline .checkbox {
+    display: inline-block;
+    margin-top: 0;
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  .form-inline .radio label,
+  .form-inline .checkbox label {
+    padding-left: 0;
+  }
+  .form-inline .radio input[type="radio"],
+  .form-inline .checkbox input[type="checkbox"] {
+    position: relative;
+    margin-left: 0;
+  }
+  .form-inline .has-feedback .form-control-feedback {
+    top: 0;
+  }
+}
+.form-horizontal .radio,
+.form-horizontal .checkbox,
+.form-horizontal .radio-inline,
+.form-horizontal .checkbox-inline {
+  padding-top: 7px;
+  margin-top: 0;
+  margin-bottom: 0;
+}
+.form-horizontal .radio,
+.form-horizontal .checkbox {
+  min-height: 27px;
+}
+.form-horizontal .form-group {
+  margin-right: -15px;
+  margin-left: -15px;
+}
+@media (min-width: 768px) {
+  .form-horizontal .control-label {
+    padding-top: 7px;
+    margin-bottom: 0;
+    text-align: right;
+  }
+}
+.form-horizontal .has-feedback .form-control-feedback {
+  top: 0;
+  right: 15px;
+}
+@media (min-width: 768px) {
+  .form-horizontal .form-group-lg .control-label {
+    padding-top: 14.3px;
+  }
+}
+@media (min-width: 768px) {
+  .form-horizontal .form-group-sm .control-label {
+    padding-top: 6px;
+  }
+}
+.btn {
+  display: inline-block;
+  padding: 6px 12px;
+  margin-bottom: 0;
+  font-size: 14px;
+  font-weight: normal;
+  line-height: 1.42857143;
+  text-align: center;
+  white-space: nowrap;
+  vertical-align: middle;
+  cursor: pointer;
+  -webkit-user-select: none;
+     -moz-user-select: none;
+      -ms-user-select: none;
+          user-select: none;
+  background-image: none;
+  border: 1px solid transparent;
+  border-radius: 4px;
+}
+.btn:focus,
+.btn:active:focus,
+.btn.active:focus {
+  outline: thin dotted;
+  outline: 5px auto -webkit-focus-ring-color;
+  outline-offset: -2px;
+}
+.btn:hover,
+.btn:focus {
+  color: #333;
+  text-decoration: none;
+}
+.btn:active,
+.btn.active {
+  background-image: none;
+  outline: 0;
+  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+          box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+}
+.btn.disabled,
+.btn[disabled],
+fieldset[disabled] .btn {
+  pointer-events: none;
+  cursor: not-allowed;
+  filter: alpha(opacity=65);
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  opacity: .65;
+}
+.btn-default {
+  color: #333;
+  background-color: #fff;
+  border-color: #ccc;
+}
+.btn-default:hover,
+.btn-default:focus,
+.btn-default:active,
+.btn-default.active,
+.open > .dropdown-toggle.btn-default {
+  color: #333;
+  background-color: #e6e6e6;
+  border-color: #adadad;
+}
+.btn-default:active,
+.btn-default.active,
+.open > .dropdown-toggle.btn-default {
+  background-image: none;
+}
+.btn-default.disabled,
+.btn-default[disabled],
+fieldset[disabled] .btn-default,
+.btn-default.disabled:hover,
+.btn-default[disabled]:hover,
+fieldset[disabled] .btn-default:hover,
+.btn-default.disabled:focus,
+.btn-default[disabled]:focus,
+fieldset[disabled] .btn-default:focus,
+.btn-default.disabled:active,
+.btn-default[disabled]:active,
+fieldset[disabled] .btn-default:active,
+.btn-default.disabled.active,
+.btn-default[disabled].active,
+fieldset[disabled] .btn-default.active {
+  background-color: #fff;
+  border-color: #ccc;
+}
+.btn-default .badge {
+  color: #fff;
+  background-color: #333;
+}
+.btn-primary {
+  color: #fff;
+  background-color: #428bca;
+  border-color: #357ebd;
+}
+.btn-primary:hover,
+.btn-primary:focus,
+.btn-primary:active,
+.btn-primary.active,
+.open > .dropdown-toggle.btn-primary {
+  color: #fff;
+  background-color: #3071a9;
+  border-color: #285e8e;
+}
+.btn-primary:active,
+.btn-primary.active,
+.open > .dropdown-toggle.btn-primary {
+  background-image: none;
+}
+.btn-primary.disabled,
+.btn-primary[disabled],
+fieldset[disabled] .btn-primary,
+.btn-primary.disabled:hover,
+.btn-primary[disabled]:hover,
+fieldset[disabled] .btn-primary:hover,
+.btn-primary.disabled:focus,
+.btn-primary[disabled]:focus,
+fieldset[disabled] .btn-primary:focus,
+.btn-primary.disabled:active,
+.btn-primary[disabled]:active,
+fieldset[disabled] .btn-primary:active,
+.btn-primary.disabled.active,
+.btn-primary[disabled].active,
+fieldset[disabled] .btn-primary.active {
+  background-color: #428bca;
+  border-color: #357ebd;
+}
+.btn-primary .badge {
+  color: #428bca;
+  background-color: #fff;
+}
+.btn-success {
+  color: #fff;
+  background-color: #5cb85c;
+  border-color: #4cae4c;
+}
+.btn-success:hover,
+.btn-success:focus,
+.btn-success:active,
+.btn-success.active,
+.open > .dropdown-toggle.btn-success {
+  color: #fff;
+  background-color: #449d44;
+  border-color: #398439;
+}
+.btn-success:active,
+.btn-success.active,
+.open > .dropdown-toggle.btn-success {
+  background-image: none;
+}
+.btn-success.disabled,
+.btn-success[disabled],
+fieldset[disabled] .btn-success,
+.btn-success.disabled:hover,
+.btn-success[disabled]:hover,
+fieldset[disabled] .btn-success:hover,
+.btn-success.disabled:focus,
+.btn-success[disabled]:focus,
+fieldset[disabled] .btn-success:focus,
+.btn-success.disabled:active,
+.btn-success[disabled]:active,
+fieldset[disabled] .btn-success:active,
+.btn-success.disabled.active,
+.btn-success[disabled].active,
+fieldset[disabled] .btn-success.active {
+  background-color: #5cb85c;
+  border-color: #4cae4c;
+}
+.btn-success .badge {
+  color: #5cb85c;
+  background-color: #fff;
+}
+.btn-info {
+  color: #fff;
+  background-color: #5bc0de;
+  border-color: #46b8da;
+}
+.btn-info:hover,
+.btn-info:focus,
+.btn-info:active,
+.btn-info.active,
+.open > .dropdown-toggle.btn-info {
+  color: #fff;
+  background-color: #31b0d5;
+  border-color: #269abc;
+}
+.btn-info:active,
+.btn-info.active,
+.open > .dropdown-toggle.btn-info {
+  background-image: none;
+}
+.btn-info.disabled,
+.btn-info[disabled],
+fieldset[disabled] .btn-info,
+.btn-info.disabled:hover,
+.btn-info[disabled]:hover,
+fieldset[disabled] .btn-info:hover,
+.btn-info.disabled:focus,
+.btn-info[disabled]:focus,
+fieldset[disabled] .btn-info:focus,
+.btn-info.disabled:active,
+.btn-info[disabled]:active,
+fieldset[disabled] .btn-info:active,
+.btn-info.disabled.active,
+.btn-info[disabled].active,
+fieldset[disabled] .btn-info.active {
+  background-color: #5bc0de;
+  border-color: #46b8da;
+}
+.btn-info .badge {
+  color: #5bc0de;
+  background-color: #fff;
+}
+.btn-warning {
+  color: #fff;
+  background-color: #f0ad4e;
+  border-color: #eea236;
+}
+.btn-warning:hover,
+.btn-warning:focus,
+.btn-warning:active,
+.btn-warning.active,
+.open > .dropdown-toggle.btn-warning {
+  color: #fff;
+  background-color: #ec971f;
+  border-color: #d58512;
+}
+.btn-warning:active,
+.btn-warning.active,
+.open > .dropdown-toggle.btn-warning {
+  background-image: none;
+}
+.btn-warning.disabled,
+.btn-warning[disabled],
+fieldset[disabled] .btn-warning,
+.btn-warning.disabled:hover,
+.btn-warning[disabled]:hover,
+fieldset[disabled] .btn-warning:hover,
+.btn-warning.disabled:focus,
+.btn-warning[disabled]:focus,
+fieldset[disabled] .btn-warning:focus,
+.btn-warning.disabled:active,
+.btn-warning[disabled]:active,
+fieldset[disabled] .btn-warning:active,
+.btn-warning.disabled.active,
+.btn-warning[disabled].active,
+fieldset[disabled] .btn-warning.active {
+  background-color: #f0ad4e;
+  border-color: #eea236;
+}
+.btn-warning .badge {
+  color: #f0ad4e;
+  background-color: #fff;
+}
+.btn-danger {
+  color: #fff;
+  background-color: #d9534f;
+  border-color: #d43f3a;
+}
+.btn-danger:hover,
+.btn-danger:focus,
+.btn-danger:active,
+.btn-danger.active,
+.open > .dropdown-toggle.btn-danger {
+  color: #fff;
+  background-color: #c9302c;
+  border-color: #ac2925;
+}
+.btn-danger:active,
+.btn-danger.active,
+.open > .dropdown-toggle.btn-danger {
+  background-image: none;
+}
+.btn-danger.disabled,
+.btn-danger[disabled],
+fieldset[disabled] .btn-danger,
+.btn-danger.disabled:hover,
+.btn-danger[disabled]:hover,
+fieldset[disabled] .btn-danger:hover,
+.btn-danger.disabled:focus,
+.btn-danger[disabled]:focus,
+fieldset[disabled] .btn-danger:focus,
+.btn-danger.disabled:active,
+.btn-danger[disabled]:active,
+fieldset[disabled] .btn-danger:active,
+.btn-danger.disabled.active,
+.btn-danger[disabled].active,
+fieldset[disabled] .btn-danger.active {
+  background-color: #d9534f;
+  border-color: #d43f3a;
+}
+.btn-danger .badge {
+  color: #d9534f;
+  background-color: #fff;
+}
+.btn-link {
+  font-weight: normal;
+  color: #428bca;
+  cursor: pointer;
+  border-radius: 0;
+}
+.btn-link,
+.btn-link:active,
+.btn-link[disabled],
+fieldset[disabled] .btn-link {
+  background-color: transparent;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+.btn-link,
+.btn-link:hover,
+.btn-link:focus,
+.btn-link:active {
+  border-color: transparent;
+}
+.btn-link:hover,
+.btn-link:focus {
+  color: #2a6496;
+  text-decoration: underline;
+  background-color: transparent;
+}
+.btn-link[disabled]:hover,
+fieldset[disabled] .btn-link:hover,
+.btn-link[disabled]:focus,
+fieldset[disabled] .btn-link:focus {
+  color: #777;
+  text-decoration: none;
+}
+.btn-lg,
+.btn-group-lg > .btn {
+  padding: 10px 16px;
+  font-size: 18px;
+  line-height: 1.33;
+  border-radius: 6px;
+}
+.btn-sm,
+.btn-group-sm > .btn {
+  padding: 5px 10px;
+  font-size: 12px;
+  line-height: 1.5;
+  border-radius: 3px;
+}
+.btn-xs,
+.btn-group-xs > .btn {
+  padding: 1px 5px;
+  font-size: 12px;
+  line-height: 1.5;
+  border-radius: 3px;
+}
+.btn-block {
+  display: block;
+  width: 100%;
+}
+.btn-block + .btn-block {
+  margin-top: 5px;
+}
+input[type="submit"].btn-block,
+input[type="reset"].btn-block,
+input[type="button"].btn-block {
+  width: 100%;
+}
+.fade {
+  opacity: 0;
+  -webkit-transition: opacity .15s linear;
+       -o-transition: opacity .15s linear;
+          transition: opacity .15s linear;
+}
+.fade.in {
+  opacity: 1;
+}
+.collapse {
+  display: none;
+}
+.collapse.in {
+  display: block;
+}
+tr.collapse.in {
+  display: table-row;
+}
+tbody.collapse.in {
+  display: table-row-group;
+}
+.collapsing {
+  position: relative;
+  height: 0;
+  overflow: hidden;
+  -webkit-transition: height .35s ease;
+       -o-transition: height .35s ease;
+          transition: height .35s ease;
+}
+.caret {
+  display: inline-block;
+  width: 0;
+  height: 0;
+  margin-left: 2px;
+  vertical-align: middle;
+  border-top: 4px solid;
+  border-right: 4px solid transparent;
+  border-left: 4px solid transparent;
+}
+.dropdown {
+  position: relative;
+}
+.dropdown-toggle:focus {
+  outline: 0;
+}
+.dropdown-menu {
+  position: absolute;
+  top: 100%;
+  left: 0;
+  z-index: 1000;
+  display: none;
+  float: left;
+  min-width: 160px;
+  padding: 5px 0;
+  margin: 2px 0 0;
+  font-size: 14px;
+  text-align: left;
+  list-style: none;
+  background-color: #fff;
+  -webkit-background-clip: padding-box;
+          background-clip: padding-box;
+  border: 1px solid #ccc;
+  border: 1px solid rgba(0, 0, 0, .15);
+  border-radius: 4px;
+  -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
+          box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
+}
+.dropdown-menu.pull-right {
+  right: 0;
+  left: auto;
+}
+.dropdown-menu .divider {
+  height: 1px;
+  margin: 9px 0;
+  overflow: hidden;
+  background-color: #e5e5e5;
+}
+.dropdown-menu > li > a {
+  display: block;
+  padding: 3px 20px;
+  clear: both;
+  font-weight: normal;
+  line-height: 1.42857143;
+  color: #333;
+  white-space: nowrap;
+}
+.dropdown-menu > li > a:hover,
+.dropdown-menu > li > a:focus {
+  color: #262626;
+  text-decoration: none;
+  background-color: #f5f5f5;
+}
+.dropdown-menu > .active > a,
+.dropdown-menu > .active > a:hover,
+.dropdown-menu > .active > a:focus {
+  color: #fff;
+  text-decoration: none;
+  background-color: #428bca;
+  outline: 0;
+}
+.dropdown-menu > .disabled > a,
+.dropdown-menu > .disabled > a:hover,
+.dropdown-menu > .disabled > a:focus {
+  color: #777;
+}
+.dropdown-menu > .disabled > a:hover,
+.dropdown-menu > .disabled > a:focus {
+  text-decoration: none;
+  cursor: not-allowed;
+  background-color: transparent;
+  background-image: none;
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+}
+.open > .dropdown-menu {
+  display: block;
+}
+.open > a {
+  outline: 0;
+}
+.dropdown-menu-right {
+  right: 0;
+  left: auto;
+}
+.dropdown-menu-left {
+  right: auto;
+  left: 0;
+}
+.dropdown-header {
+  display: block;
+  padding: 3px 20px;
+  font-size: 12px;
+  line-height: 1.42857143;
+  color: #777;
+  white-space: nowrap;
+}
+.dropdown-backdrop {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: 990;
+}
+.pull-right > .dropdown-menu {
+  right: 0;
+  left: auto;
+}
+.dropup .caret,
+.navbar-fixed-bottom .dropdown .caret {
+  content: "";
+  border-top: 0;
+  border-bottom: 4px solid;
+}
+.dropup .dropdown-menu,
+.navbar-fixed-bottom .dropdown .dropdown-menu {
+  top: auto;
+  bottom: 100%;
+  margin-bottom: 1px;
+}
+@media (min-width: 768px) {
+  .navbar-right .dropdown-menu {
+    right: 0;
+    left: auto;
+  }
+  .navbar-right .dropdown-menu-left {
+    right: auto;
+    left: 0;
+  }
+}
+.btn-group,
+.btn-group-vertical {
+  position: relative;
+  display: inline-block;
+  vertical-align: middle;
+}
+.btn-group > .btn,
+.btn-group-vertical > .btn {
+  position: relative;
+  float: left;
+}
+.btn-group > .btn:hover,
+.btn-group-vertical > .btn:hover,
+.btn-group > .btn:focus,
+.btn-group-vertical > .btn:focus,
+.btn-group > .btn:active,
+.btn-group-vertical > .btn:active,
+.btn-group > .btn.active,
+.btn-group-vertical > .btn.active {
+  z-index: 2;
+}
+.btn-group > .btn:focus,
+.btn-group-vertical > .btn:focus {
+  outline: 0;
+}
+.btn-group .btn + .btn,
+.btn-group .btn + .btn-group,
+.btn-group .btn-group + .btn,
+.btn-group .btn-group + .btn-group {
+  margin-left: -1px;
+}
+.btn-toolbar {
+  margin-left: -5px;
+}
+.btn-toolbar .btn-group,
+.btn-toolbar .input-group {
+  float: left;
+}
+.btn-toolbar > .btn,
+.btn-toolbar > .btn-group,
+.btn-toolbar > .input-group {
+  margin-left: 5px;
+}
+.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {
+  border-radius: 0;
+}
+.btn-group > .btn:first-child {
+  margin-left: 0;
+}
+.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
+}
+.btn-group > .btn:last-child:not(:first-child),
+.btn-group > .dropdown-toggle:not(:first-child) {
+  border-top-left-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.btn-group > .btn-group {
+  float: left;
+}
+.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {
+  border-radius: 0;
+}
+.btn-group > .btn-group:first-child > .btn:last-child,
+.btn-group > .btn-group:first-child > .dropdown-toggle {
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
+}
+.btn-group > .btn-group:last-child > .btn:first-child {
+  border-top-left-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.btn-group .dropdown-toggle:active,
+.btn-group.open .dropdown-toggle {
+  outline: 0;
+}
+.btn-group > .btn + .dropdown-toggle {
+  padding-right: 8px;
+  padding-left: 8px;
+}
+.btn-group > .btn-lg + .dropdown-toggle {
+  padding-right: 12px;
+  padding-left: 12px;
+}
+.btn-group.open .dropdown-toggle {
+  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+          box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+}
+.btn-group.open .dropdown-toggle.btn-link {
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+.btn .caret {
+  margin-left: 0;
+}
+.btn-lg .caret {
+  border-width: 5px 5px 0;
+  border-bottom-width: 0;
+}
+.dropup .btn-lg .caret {
+  border-width: 0 5px 5px;
+}
+.btn-group-vertical > .btn,
+.btn-group-vertical > .btn-group,
+.btn-group-vertical > .btn-group > .btn {
+  display: block;
+  float: none;
+  width: 100%;
+  max-width: 100%;
+}
+.btn-group-vertical > .btn-group > .btn {
+  float: none;
+}
+.btn-group-vertical > .btn + .btn,
+.btn-group-vertical > .btn + .btn-group,
+.btn-group-vertical > .btn-group + .btn,
+.btn-group-vertical > .btn-group + .btn-group {
+  margin-top: -1px;
+  margin-left: 0;
+}
+.btn-group-vertical > .btn:not(:first-child):not(:last-child) {
+  border-radius: 0;
+}
+.btn-group-vertical > .btn:first-child:not(:last-child) {
+  border-top-right-radius: 4px;
+  border-bottom-right-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.btn-group-vertical > .btn:last-child:not(:first-child) {
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+  border-bottom-left-radius: 4px;
+}
+.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {
+  border-radius: 0;
+}
+.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,
+.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {
+  border-bottom-right-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+}
+.btn-group-justified {
+  display: table;
+  width: 100%;
+  table-layout: fixed;
+  border-collapse: separate;
+}
+.btn-group-justified > .btn,
+.btn-group-justified > .btn-group {
+  display: table-cell;
+  float: none;
+  width: 1%;
+}
+.btn-group-justified > .btn-group .btn {
+  width: 100%;
+}
+.btn-group-justified > .btn-group .dropdown-menu {
+  left: auto;
+}
+[data-toggle="buttons"] > .btn > input[type="radio"],
+[data-toggle="buttons"] > .btn > input[type="checkbox"] {
+  position: absolute;
+  z-index: -1;
+  filter: alpha(opacity=0);
+  opacity: 0;
+}
+.input-group {
+  position: relative;
+  display: table;
+  border-collapse: separate;
+}
+.input-group[class*="col-"] {
+  float: none;
+  padding-right: 0;
+  padding-left: 0;
+}
+.input-group .form-control {
+  position: relative;
+  z-index: 2;
+  float: left;
+  width: 100%;
+  margin-bottom: 0;
+}
+.input-group-lg > .form-control,
+.input-group-lg > .input-group-addon,
+.input-group-lg > .input-group-btn > .btn {
+  height: 46px;
+  padding: 10px 16px;
+  font-size: 18px;
+  line-height: 1.33;
+  border-radius: 6px;
+}
+select.input-group-lg > .form-control,
+select.input-group-lg > .input-group-addon,
+select.input-group-lg > .input-group-btn > .btn {
+  height: 46px;
+  line-height: 46px;
+}
+textarea.input-group-lg > .form-control,
+textarea.input-group-lg > .input-group-addon,
+textarea.input-group-lg > .input-group-btn > .btn,
+select[multiple].input-group-lg > .form-control,
+select[multiple].input-group-lg > .input-group-addon,
+select[multiple].input-group-lg > .input-group-btn > .btn {
+  height: auto;
+}
+.input-group-sm > .form-control,
+.input-group-sm > .input-group-addon,
+.input-group-sm > .input-group-btn > .btn {
+  height: 30px;
+  padding: 5px 10px;
+  font-size: 12px;
+  line-height: 1.5;
+  border-radius: 3px;
+}
+select.input-group-sm > .form-control,
+select.input-group-sm > .input-group-addon,
+select.input-group-sm > .input-group-btn > .btn {
+  height: 30px;
+  line-height: 30px;
+}
+textarea.input-group-sm > .form-control,
+textarea.input-group-sm > .input-group-addon,
+textarea.input-group-sm > .input-group-btn > .btn,
+select[multiple].input-group-sm > .form-control,
+select[multiple].input-group-sm > .input-group-addon,
+select[multiple].input-group-sm > .input-group-btn > .btn {
+  height: auto;
+}
+.input-group-addon,
+.input-group-btn,
+.input-group .form-control {
+  display: table-cell;
+}
+.input-group-addon:not(:first-child):not(:last-child),
+.input-group-btn:not(:first-child):not(:last-child),
+.input-group .form-control:not(:first-child):not(:last-child) {
+  border-radius: 0;
+}
+.input-group-addon,
+.input-group-btn {
+  width: 1%;
+  white-space: nowrap;
+  vertical-align: middle;
+}
+.input-group-addon {
+  padding: 6px 12px;
+  font-size: 14px;
+  font-weight: normal;
+  line-height: 1;
+  color: #555;
+  text-align: center;
+  background-color: #eee;
+  border: 1px solid #ccc;
+  border-radius: 4px;
+}
+.input-group-addon.input-sm {
+  padding: 5px 10px;
+  font-size: 12px;
+  border-radius: 3px;
+}
+.input-group-addon.input-lg {
+  padding: 10px 16px;
+  font-size: 18px;
+  border-radius: 6px;
+}
+.input-group-addon input[type="radio"],
+.input-group-addon input[type="checkbox"] {
+  margin-top: 0;
+}
+.input-group .form-control:first-child,
+.input-group-addon:first-child,
+.input-group-btn:first-child > .btn,
+.input-group-btn:first-child > .btn-group > .btn,
+.input-group-btn:first-child > .dropdown-toggle,
+.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),
+.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
+}
+.input-group-addon:first-child {
+  border-right: 0;
+}
+.input-group .form-control:last-child,
+.input-group-addon:last-child,
+.input-group-btn:last-child > .btn,
+.input-group-btn:last-child > .btn-group > .btn,
+.input-group-btn:last-child > .dropdown-toggle,
+.input-group-btn:first-child > .btn:not(:first-child),
+.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {
+  border-top-left-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.input-group-addon:last-child {
+  border-left: 0;
+}
+.input-group-btn {
+  position: relative;
+  font-size: 0;
+  white-space: nowrap;
+}
+.input-group-btn > .btn {
+  position: relative;
+}
+.input-group-btn > .btn + .btn {
+  margin-left: -1px;
+}
+.input-group-btn > .btn:hover,
+.input-group-btn > .btn:focus,
+.input-group-btn > .btn:active {
+  z-index: 2;
+}
+.input-group-btn:first-child > .btn,
+.input-group-btn:first-child > .btn-group {
+  margin-right: -1px;
+}
+.input-group-btn:last-child > .btn,
+.input-group-btn:last-child > .btn-group {
+  margin-left: -1px;
+}
+.nav {
+  padding-left: 0;
+  margin-bottom: 0;
+  list-style: none;
+}
+.nav > li {
+  position: relative;
+  display: block;
+}
+.nav > li > a {
+  position: relative;
+  display: block;
+  padding: 10px 15px;
+}
+.nav > li > a:hover,
+.nav > li > a:focus {
+  text-decoration: none;
+  background-color: #eee;
+}
+.nav > li.disabled > a {
+  color: #777;
+}
+.nav > li.disabled > a:hover,
+.nav > li.disabled > a:focus {
+  color: #777;
+  text-decoration: none;
+  cursor: not-allowed;
+  background-color: transparent;
+}
+.nav .open > a,
+.nav .open > a:hover,
+.nav .open > a:focus {
+  background-color: #eee;
+  border-color: #428bca;
+}
+.nav .nav-divider {
+  height: 1px;
+  margin: 9px 0;
+  overflow: hidden;
+  background-color: #e5e5e5;
+}
+.nav > li > a > img {
+  max-width: none;
+}
+.nav-tabs {
+  border-bottom: 1px solid #ddd;
+}
+.nav-tabs > li {
+  float: left;
+  margin-bottom: -1px;
+}
+.nav-tabs > li > a {
+  margin-right: 2px;
+  line-height: 1.42857143;
+  border: 1px solid transparent;
+  border-radius: 4px 4px 0 0;
+}
+.nav-tabs > li > a:hover {
+  border-color: #eee #eee #ddd;
+}
+.nav-tabs > li.active > a,
+.nav-tabs > li.active > a:hover,
+.nav-tabs > li.active > a:focus {
+  color: #555;
+  cursor: default;
+  background-color: #fff;
+  border: 1px solid #ddd;
+  border-bottom-color: transparent;
+}
+.nav-tabs.nav-justified {
+  width: 100%;
+  border-bottom: 0;
+}
+.nav-tabs.nav-justified > li {
+  float: none;
+}
+.nav-tabs.nav-justified > li > a {
+  margin-bottom: 5px;
+  text-align: center;
+}
+.nav-tabs.nav-justified > .dropdown .dropdown-menu {
+  top: auto;
+  left: auto;
+}
+@media (min-width: 768px) {
+  .nav-tabs.nav-justified > li {
+    display: table-cell;
+    width: 1%;
+  }
+  .nav-tabs.nav-justified > li > a {
+    margin-bottom: 0;
+  }
+}
+.nav-tabs.nav-justified > li > a {
+  margin-right: 0;
+  border-radius: 4px;
+}
+.nav-tabs.nav-justified > .active > a,
+.nav-tabs.nav-justified > .active > a:hover,
+.nav-tabs.nav-justified > .active > a:focus {
+  border: 1px solid #ddd;
+}
+@media (min-width: 768px) {
+  .nav-tabs.nav-justified > li > a {
+    border-bottom: 1px solid #ddd;
+    border-radius: 4px 4px 0 0;
+  }
+  .nav-tabs.nav-justified > .active > a,
+  .nav-tabs.nav-justified > .active > a:hover,
+  .nav-tabs.nav-justified > .active > a:focus {
+    border-bottom-color: #fff;
+  }
+}
+.nav-pills > li {
+  float: left;
+}
+.nav-pills > li > a {
+  border-radius: 4px;
+}
+.nav-pills > li + li {
+  margin-left: 2px;
+}
+.nav-pills > li.active > a,
+.nav-pills > li.active > a:hover,
+.nav-pills > li.active > a:focus {
+  color: #fff;
+  background-color: #428bca;
+}
+.nav-stacked > li {
+  float: none;
+}
+.nav-stacked > li + li {
+  margin-top: 2px;
+  margin-left: 0;
+}
+.nav-justified {
+  width: 100%;
+}
+.nav-justified > li {
+  float: none;
+}
+.nav-justified > li > a {
+  margin-bottom: 5px;
+  text-align: center;
+}
+.nav-justified > .dropdown .dropdown-menu {
+  top: auto;
+  left: auto;
+}
+@media (min-width: 768px) {
+  .nav-justified > li {
+    display: table-cell;
+    width: 1%;
+  }
+  .nav-justified > li > a {
+    margin-bottom: 0;
+  }
+}
+.nav-tabs-justified {
+  border-bottom: 0;
+}
+.nav-tabs-justified > li > a {
+  margin-right: 0;
+  border-radius: 4px;
+}
+.nav-tabs-justified > .active > a,
+.nav-tabs-justified > .active > a:hover,
+.nav-tabs-justified > .active > a:focus {
+  border: 1px solid #ddd;
+}
+@media (min-width: 768px) {
+  .nav-tabs-justified > li > a {
+    border-bottom: 1px solid #ddd;
+    border-radius: 4px 4px 0 0;
+  }
+  .nav-tabs-justified > .active > a,
+  .nav-tabs-justified > .active > a:hover,
+  .nav-tabs-justified > .active > a:focus {
+    border-bottom-color: #fff;
+  }
+}
+.tab-content > .tab-pane {
+  display: none;
+}
+.tab-content > .active {
+  display: block;
+}
+.nav-tabs .dropdown-menu {
+  margin-top: -1px;
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+}
+.navbar {
+  position: relative;
+  min-height: 50px;
+  margin-bottom: 20px;
+  border: 1px solid transparent;
+}
+@media (min-width: 768px) {
+  .navbar {
+    border-radius: 4px;
+  }
+}
+@media (min-width: 768px) {
+  .navbar-header {
+    float: left;
+  }
+}
+.navbar-collapse {
+  padding-right: 15px;
+  padding-left: 15px;
+  overflow-x: visible;
+  -webkit-overflow-scrolling: touch;
+  border-top: 1px solid transparent;
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1);
+          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1);
+}
+.navbar-collapse.in {
+  overflow-y: auto;
+}
+@media (min-width: 768px) {
+  .navbar-collapse {
+    width: auto;
+    border-top: 0;
+    -webkit-box-shadow: none;
+            box-shadow: none;
+  }
+  .navbar-collapse.collapse {
+    display: block !important;
+    height: auto !important;
+    padding-bottom: 0;
+    overflow: visible !important;
+  }
+  .navbar-collapse.in {
+    overflow-y: visible;
+  }
+  .navbar-fixed-top .navbar-collapse,
+  .navbar-static-top .navbar-collapse,
+  .navbar-fixed-bottom .navbar-collapse {
+    padding-right: 0;
+    padding-left: 0;
+  }
+}
+.navbar-fixed-top .navbar-collapse,
+.navbar-fixed-bottom .navbar-collapse {
+  max-height: 340px;
+}
+@media (max-width: 480px) and (orientation: landscape) {
+  .navbar-fixed-top .navbar-collapse,
+  .navbar-fixed-bottom .navbar-collapse {
+    max-height: 200px;
+  }
+}
+.container > .navbar-header,
+.container-fluid > .navbar-header,
+.container > .navbar-collapse,
+.container-fluid > .navbar-collapse {
+  margin-right: -15px;
+  margin-left: -15px;
+}
+@media (min-width: 768px) {
+  .container > .navbar-header,
+  .container-fluid > .navbar-header,
+  .container > .navbar-collapse,
+  .container-fluid > .navbar-collapse {
+    margin-right: 0;
+    margin-left: 0;
+  }
+}
+.navbar-static-top {
+  z-index: 1000;
+  border-width: 0 0 1px;
+}
+@media (min-width: 768px) {
+  .navbar-static-top {
+    border-radius: 0;
+  }
+}
+.navbar-fixed-top,
+.navbar-fixed-bottom {
+  position: fixed;
+  right: 0;
+  left: 0;
+  z-index: 1030;
+  -webkit-transform: translate3d(0, 0, 0);
+       -o-transform: translate3d(0, 0, 0);
+          transform: translate3d(0, 0, 0);
+}
+@media (min-width: 768px) {
+  .navbar-fixed-top,
+  .navbar-fixed-bottom {
+    border-radius: 0;
+  }
+}
+.navbar-fixed-top {
+  top: 0;
+  border-width: 0 0 1px;
+}
+.navbar-fixed-bottom {
+  bottom: 0;
+  margin-bottom: 0;
+  border-width: 1px 0 0;
+}
+.navbar-brand {
+  float: left;
+  height: 50px;
+  padding: 15px 15px;
+  font-size: 18px;
+  line-height: 20px;
+}
+.navbar-brand:hover,
+.navbar-brand:focus {
+  text-decoration: none;
+}
+@media (min-width: 768px) {
+  .navbar > .container .navbar-brand,
+  .navbar > .container-fluid .navbar-brand {
+    margin-left: -15px;
+  }
+}
+.navbar-toggle {
+  position: relative;
+  float: right;
+  padding: 9px 10px;
+  margin-top: 8px;
+  margin-right: 15px;
+  margin-bottom: 8px;
+  background-color: transparent;
+  background-image: none;
+  border: 1px solid transparent;
+  border-radius: 4px;
+}
+.navbar-toggle:focus {
+  outline: 0;
+}
+.navbar-toggle .icon-bar {
+  display: block;
+  width: 22px;
+  height: 2px;
+  border-radius: 1px;
+}
+.navbar-toggle .icon-bar + .icon-bar {
+  margin-top: 4px;
+}
+@media (min-width: 768px) {
+  .navbar-toggle {
+    display: none;
+  }
+}
+.navbar-nav {
+  margin: 7.5px -15px;
+}
+.navbar-nav > li > a {
+  padding-top: 10px;
+  padding-bottom: 10px;
+  line-height: 20px;
+}
+@media (max-width: 767px) {
+  .navbar-nav .open .dropdown-menu {
+    position: static;
+    float: none;
+    width: auto;
+    margin-top: 0;
+    background-color: transparent;
+    border: 0;
+    -webkit-box-shadow: none;
+            box-shadow: none;
+  }
+  .navbar-nav .open .dropdown-menu > li > a,
+  .navbar-nav .open .dropdown-menu .dropdown-header {
+    padding: 5px 15px 5px 25px;
+  }
+  .navbar-nav .open .dropdown-menu > li > a {
+    line-height: 20px;
+  }
+  .navbar-nav .open .dropdown-menu > li > a:hover,
+  .navbar-nav .open .dropdown-menu > li > a:focus {
+    background-image: none;
+  }
+}
+@media (min-width: 768px) {
+  .navbar-nav {
+    float: left;
+    margin: 0;
+  }
+  .navbar-nav > li {
+    float: left;
+  }
+  .navbar-nav > li > a {
+    padding-top: 15px;
+    padding-bottom: 15px;
+  }
+  .navbar-nav.navbar-right:last-child {
+    margin-right: -15px;
+  }
+}
+@media (min-width: 768px) {
+  .navbar-left {
+    float: left !important;
+  }
+  .navbar-right {
+    float: right !important;
+  }
+}
+.navbar-form {
+  padding: 10px 15px;
+  margin-top: 8px;
+  margin-right: -15px;
+  margin-bottom: 8px;
+  margin-left: -15px;
+  border-top: 1px solid transparent;
+  border-bottom: 1px solid transparent;
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1);
+          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1);
+}
+@media (min-width: 768px) {
+  .navbar-form .form-group {
+    display: inline-block;
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  .navbar-form .form-control {
+    display: inline-block;
+    width: auto;
+    vertical-align: middle;
+  }
+  .navbar-form .input-group {
+    display: inline-table;
+    vertical-align: middle;
+  }
+  .navbar-form .input-group .input-group-addon,
+  .navbar-form .input-group .input-group-btn,
+  .navbar-form .input-group .form-control {
+    width: auto;
+  }
+  .navbar-form .input-group > .form-control {
+    width: 100%;
+  }
+  .navbar-form .control-label {
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  .navbar-form .radio,
+  .navbar-form .checkbox {
+    display: inline-block;
+    margin-top: 0;
+    margin-bottom: 0;
+    vertical-align: middle;
+  }
+  .navbar-form .radio label,
+  .navbar-form .checkbox label {
+    padding-left: 0;
+  }
+  .navbar-form .radio input[type="radio"],
+  .navbar-form .checkbox input[type="checkbox"] {
+    position: relative;
+    margin-left: 0;
+  }
+  .navbar-form .has-feedback .form-control-feedback {
+    top: 0;
+  }
+}
+@media (max-width: 767px) {
+  .navbar-form .form-group {
+    margin-bottom: 5px;
+  }
+}
+@media (min-width: 768px) {
+  .navbar-form {
+    width: auto;
+    padding-top: 0;
+    padding-bottom: 0;
+    margin-right: 0;
+    margin-left: 0;
+    border: 0;
+    -webkit-box-shadow: none;
+            box-shadow: none;
+  }
+  .navbar-form.navbar-right:last-child {
+    margin-right: -15px;
+  }
+}
+.navbar-nav > li > .dropdown-menu {
+  margin-top: 0;
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+}
+.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {
+  border-bottom-right-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.navbar-btn {
+  margin-top: 8px;
+  margin-bottom: 8px;
+}
+.navbar-btn.btn-sm {
+  margin-top: 10px;
+  margin-bottom: 10px;
+}
+.navbar-btn.btn-xs {
+  margin-top: 14px;
+  margin-bottom: 14px;
+}
+.navbar-text {
+  margin-top: 15px;
+  margin-bottom: 15px;
+}
+@media (min-width: 768px) {
+  .navbar-text {
+    float: left;
+    margin-right: 15px;
+    margin-left: 15px;
+  }
+  .navbar-text.navbar-right:last-child {
+    margin-right: 0;
+  }
+}
+.navbar-default {
+  background-color: #f8f8f8;
+  border-color: #e7e7e7;
+}
+.navbar-default .navbar-brand {
+  color: #777;
+}
+.navbar-default .navbar-brand:hover,
+.navbar-default .navbar-brand:focus {
+  color: #5e5e5e;
+  background-color: transparent;
+}
+.navbar-default .navbar-text {
+  color: #777;
+}
+.navbar-default .navbar-nav > li > a {
+  color: #777;
+}
+.navbar-default .navbar-nav > li > a:hover,
+.navbar-default .navbar-nav > li > a:focus {
+  color: #333;
+  background-color: transparent;
+}
+.navbar-default .navbar-nav > .active > a,
+.navbar-default .navbar-nav > .active > a:hover,
+.navbar-default .navbar-nav > .active > a:focus {
+  color: #555;
+  background-color: #e7e7e7;
+}
+.navbar-default .navbar-nav > .disabled > a,
+.navbar-default .navbar-nav > .disabled > a:hover,
+.navbar-default .navbar-nav > .disabled > a:focus {
+  color: #ccc;
+  background-color: transparent;
+}
+.navbar-default .navbar-toggle {
+  border-color: #ddd;
+}
+.navbar-default .navbar-toggle:hover,
+.navbar-default .navbar-toggle:focus {
+  background-color: #ddd;
+}
+.navbar-default .navbar-toggle .icon-bar {
+  background-color: #888;
+}
+.navbar-default .navbar-collapse,
+.navbar-default .navbar-form {
+  border-color: #e7e7e7;
+}
+.navbar-default .navbar-nav > .open > a,
+.navbar-default .navbar-nav > .open > a:hover,
+.navbar-default .navbar-nav > .open > a:focus {
+  color: #555;
+  background-color: #e7e7e7;
+}
+@media (max-width: 767px) {
+  .navbar-default .navbar-nav .open .dropdown-menu > li > a {
+    color: #777;
+  }
+  .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,
+  .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {
+    color: #333;
+    background-color: transparent;
+  }
+  .navbar-default .navbar-nav .open .dropdown-menu > .active > a,
+  .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,
+  .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {
+    color: #555;
+    background-color: #e7e7e7;
+  }
+  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,
+  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,
+  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {
+    color: #ccc;
+    background-color: transparent;
+  }
+}
+.navbar-default .navbar-link {
+  color: #777;
+}
+.navbar-default .navbar-link:hover {
+  color: #333;
+}
+.navbar-default .btn-link {
+  color: #777;
+}
+.navbar-default .btn-link:hover,
+.navbar-default .btn-link:focus {
+  color: #333;
+}
+.navbar-default .btn-link[disabled]:hover,
+fieldset[disabled] .navbar-default .btn-link:hover,
+.navbar-default .btn-link[disabled]:focus,
+fieldset[disabled] .navbar-default .btn-link:focus {
+  color: #ccc;
+}
+.navbar-inverse {
+  background-color: #222;
+  border-color: #080808;
+}
+.navbar-inverse .navbar-brand {
+  color: #777;
+}
+.navbar-inverse .navbar-brand:hover,
+.navbar-inverse .navbar-brand:focus {
+  color: #fff;
+  background-color: transparent;
+}
+.navbar-inverse .navbar-text {
+  color: #777;
+}
+.navbar-inverse .navbar-nav > li > a {
+  color: #777;
+}
+.navbar-inverse .navbar-nav > li > a:hover,
+.navbar-inverse .navbar-nav > li > a:focus {
+  color: #fff;
+  background-color: transparent;
+}
+.navbar-inverse .navbar-nav > .active > a,
+.navbar-inverse .navbar-nav > .active > a:hover,
+.navbar-inverse .navbar-nav > .active > a:focus {
+  color: #fff;
+  background-color: #080808;
+}
+.navbar-inverse .navbar-nav > .disabled > a,
+.navbar-inverse .navbar-nav > .disabled > a:hover,
+.navbar-inverse .navbar-nav > .disabled > a:focus {
+  color: #444;
+  background-color: transparent;
+}
+.navbar-inverse .navbar-toggle {
+  border-color: #333;
+}
+.navbar-inverse .navbar-toggle:hover,
+.navbar-inverse .navbar-toggle:focus {
+  background-color: #333;
+}
+.navbar-inverse .navbar-toggle .icon-bar {
+  background-color: #fff;
+}
+.navbar-inverse .navbar-collapse,
+.navbar-inverse .navbar-form {
+  border-color: #101010;
+}
+.navbar-inverse .navbar-nav > .open > a,
+.navbar-inverse .navbar-nav > .open > a:hover,
+.navbar-inverse .navbar-nav > .open > a:focus {
+  color: #fff;
+  background-color: #080808;
+}
+@media (max-width: 767px) {
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {
+    border-color: #080808;
+  }
+  .navbar-inverse .navbar-nav .open .dropdown-menu .divider {
+    background-color: #080808;
+  }
+  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {
+    color: #777;
+  }
+  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,
+  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {
+    color: #fff;
+    background-color: transparent;
+  }
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {
+    color: #fff;
+    background-color: #080808;
+  }
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {
+    color: #444;
+    background-color: transparent;
+  }
+}
+.navbar-inverse .navbar-link {
+  color: #777;
+}
+.navbar-inverse .navbar-link:hover {
+  color: #fff;
+}
+.navbar-inverse .btn-link {
+  color: #777;
+}
+.navbar-inverse .btn-link:hover,
+.navbar-inverse .btn-link:focus {
+  color: #fff;
+}
+.navbar-inverse .btn-link[disabled]:hover,
+fieldset[disabled] .navbar-inverse .btn-link:hover,
+.navbar-inverse .btn-link[disabled]:focus,
+fieldset[disabled] .navbar-inverse .btn-link:focus {
+  color: #444;
+}
+.breadcrumb {
+  padding: 8px 15px;
+  margin-bottom: 20px;
+  list-style: none;
+  background-color: #f5f5f5;
+  border-radius: 4px;
+}
+.breadcrumb > li {
+  display: inline-block;
+}
+.breadcrumb > li + li:before {
+  padding: 0 5px;
+  color: #ccc;
+  content: "/\00a0";
+}
+.breadcrumb > .active {
+  color: #777;
+}
+.pagination {
+  display: inline-block;
+  padding-left: 0;
+  margin: 20px 0;
+  border-radius: 4px;
+}
+.pagination > li {
+  display: inline;
+}
+.pagination > li > a,
+.pagination > li > span {
+  position: relative;
+  float: left;
+  padding: 6px 12px;
+  margin-left: -1px;
+  line-height: 1.42857143;
+  color: #428bca;
+  text-decoration: none;
+  background-color: #fff;
+  border: 1px solid #ddd;
+}
+.pagination > li:first-child > a,
+.pagination > li:first-child > span {
+  margin-left: 0;
+  border-top-left-radius: 4px;
+  border-bottom-left-radius: 4px;
+}
+.pagination > li:last-child > a,
+.pagination > li:last-child > span {
+  border-top-right-radius: 4px;
+  border-bottom-right-radius: 4px;
+}
+.pagination > li > a:hover,
+.pagination > li > span:hover,
+.pagination > li > a:focus,
+.pagination > li > span:focus {
+  color: #2a6496;
+  background-color: #eee;
+  border-color: #ddd;
+}
+.pagination > .active > a,
+.pagination > .active > span,
+.pagination > .active > a:hover,
+.pagination > .active > span:hover,
+.pagination > .active > a:focus,
+.pagination > .active > span:focus {
+  z-index: 2;
+  color: #fff;
+  cursor: default;
+  background-color: #428bca;
+  border-color: #428bca;
+}
+.pagination > .disabled > span,
+.pagination > .disabled > span:hover,
+.pagination > .disabled > span:focus,
+.pagination > .disabled > a,
+.pagination > .disabled > a:hover,
+.pagination > .disabled > a:focus {
+  color: #777;
+  cursor: not-allowed;
+  background-color: #fff;
+  border-color: #ddd;
+}
+.pagination-lg > li > a,
+.pagination-lg > li > span {
+  padding: 10px 16px;
+  font-size: 18px;
+}
+.pagination-lg > li:first-child > a,
+.pagination-lg > li:first-child > span {
+  border-top-left-radius: 6px;
+  border-bottom-left-radius: 6px;
+}
+.pagination-lg > li:last-child > a,
+.pagination-lg > li:last-child > span {
+  border-top-right-radius: 6px;
+  border-bottom-right-radius: 6px;
+}
+.pagination-sm > li > a,
+.pagination-sm > li > span {
+  padding: 5px 10px;
+  font-size: 12px;
+}
+.pagination-sm > li:first-child > a,
+.pagination-sm > li:first-child > span {
+  border-top-left-radius: 3px;
+  border-bottom-left-radius: 3px;
+}
+.pagination-sm > li:last-child > a,
+.pagination-sm > li:last-child > span {
+  border-top-right-radius: 3px;
+  border-bottom-right-radius: 3px;
+}
+.pager {
+  padding-left: 0;
+  margin: 20px 0;
+  text-align: center;
+  list-style: none;
+}
+.pager li {
+  display: inline;
+}
+.pager li > a,
+.pager li > span {
+  display: inline-block;
+  padding: 5px 14px;
+  background-color: #fff;
+  border: 1px solid #ddd;
+  border-radius: 15px;
+}
+.pager li > a:hover,
+.pager li > a:focus {
+  text-decoration: none;
+  background-color: #eee;
+}
+.pager .next > a,
+.pager .next > span {
+  float: right;
+}
+.pager .previous > a,
+.pager .previous > span {
+  float: left;
+}
+.pager .disabled > a,
+.pager .disabled > a:hover,
+.pager .disabled > a:focus,
+.pager .disabled > span {
+  color: #777;
+  cursor: not-allowed;
+  background-color: #fff;
+}
+.label {
+  display: inline;
+  padding: .2em .6em .3em;
+  font-size: 75%;
+  font-weight: bold;
+  line-height: 1;
+  color: #fff;
+  text-align: center;
+  white-space: nowrap;
+  vertical-align: baseline;
+  border-radius: .25em;
+}
+a.label:hover,
+a.label:focus {
+  color: #fff;
+  text-decoration: none;
+  cursor: pointer;
+}
+.label:empty {
+  display: none;
+}
+.btn .label {
+  position: relative;
+  top: -1px;
+}
+.label-default {
+  background-color: #777;
+}
+.label-default[href]:hover,
+.label-default[href]:focus {
+  background-color: #5e5e5e;
+}
+.label-primary {
+  background-color: #428bca;
+}
+.label-primary[href]:hover,
+.label-primary[href]:focus {
+  background-color: #3071a9;
+}
+.label-success {
+  background-color: #5cb85c;
+}
+.label-success[href]:hover,
+.label-success[href]:focus {
+  background-color: #449d44;
+}
+.label-info {
+  background-color: #5bc0de;
+}
+.label-info[href]:hover,
+.label-info[href]:focus {
+  background-color: #31b0d5;
+}
+.label-warning {
+  background-color: #f0ad4e;
+}
+.label-warning[href]:hover,
+.label-warning[href]:focus {
+  background-color: #ec971f;
+}
+.label-danger {
+  background-color: #d9534f;
+}
+.label-danger[href]:hover,
+.label-danger[href]:focus {
+  background-color: #c9302c;
+}
+.badge {
+  display: inline-block;
+  min-width: 10px;
+  padding: 3px 7px;
+  font-size: 12px;
+  font-weight: bold;
+  line-height: 1;
+  color: #fff;
+  text-align: center;
+  white-space: nowrap;
+  vertical-align: baseline;
+  background-color: #777;
+  border-radius: 10px;
+}
+.badge:empty {
+  display: none;
+}
+.btn .badge {
+  position: relative;
+  top: -1px;
+}
+.btn-xs .badge {
+  top: 0;
+  padding: 1px 5px;
+}
+a.badge:hover,
+a.badge:focus {
+  color: #fff;
+  text-decoration: none;
+  cursor: pointer;
+}
+a.list-group-item.active > .badge,
+.nav-pills > .active > a > .badge {
+  color: #428bca;
+  background-color: #fff;
+}
+.nav-pills > li > a > .badge {
+  margin-left: 3px;
+}
+.jumbotron {
+  padding: 30px;
+  margin-bottom: 30px;
+  color: inherit;
+  background-color: #eee;
+}
+.jumbotron h1,
+.jumbotron .h1 {
+  color: inherit;
+}
+.jumbotron p {
+  margin-bottom: 15px;
+  font-size: 21px;
+  font-weight: 200;
+}
+.jumbotron > hr {
+  border-top-color: #d5d5d5;
+}
+.container .jumbotron {
+  border-radius: 6px;
+}
+.jumbotron .container {
+  max-width: 100%;
+}
+@media screen and (min-width: 768px) {
+  .jumbotron {
+    padding-top: 48px;
+    padding-bottom: 48px;
+  }
+  .container .jumbotron {
+    padding-right: 60px;
+    padding-left: 60px;
+  }
+  .jumbotron h1,
+  .jumbotron .h1 {
+    font-size: 63px;
+  }
+}
+.thumbnail {
+  display: block;
+  padding: 4px;
+  margin-bottom: 20px;
+  line-height: 1.42857143;
+  background-color: #fff;
+  border: 1px solid #ddd;
+  border-radius: 4px;
+  -webkit-transition: all .2s ease-in-out;
+       -o-transition: all .2s ease-in-out;
+          transition: all .2s ease-in-out;
+}
+.thumbnail > img,
+.thumbnail a > img {
+  margin-right: auto;
+  margin-left: auto;
+}
+a.thumbnail:hover,
+a.thumbnail:focus,
+a.thumbnail.active {
+  border-color: #428bca;
+}
+.thumbnail .caption {
+  padding: 9px;
+  color: #333;
+}
+.alert {
+  padding: 15px;
+  margin-bottom: 20px;
+  border: 1px solid transparent;
+  border-radius: 4px;
+}
+.alert h4 {
+  margin-top: 0;
+  color: inherit;
+}
+.alert .alert-link {
+  font-weight: bold;
+}
+.alert > p,
+.alert > ul {
+  margin-bottom: 0;
+}
+.alert > p + p {
+  margin-top: 5px;
+}
+.alert-dismissable,
+.alert-dismissible {
+  padding-right: 35px;
+}
+.alert-dismissable .close,
+.alert-dismissible .close {
+  position: relative;
+  top: -2px;
+  right: -21px;
+  color: inherit;
+}
+.alert-success {
+  color: #3c763d;
+  background-color: #dff0d8;
+  border-color: #d6e9c6;
+}
+.alert-success hr {
+  border-top-color: #c9e2b3;
+}
+.alert-success .alert-link {
+  color: #2b542c;
+}
+.alert-info {
+  color: #31708f;
+  background-color: #d9edf7;
+  border-color: #bce8f1;
+}
+.alert-info hr {
+  border-top-color: #a6e1ec;
+}
+.alert-info .alert-link {
+  color: #245269;
+}
+.alert-warning {
+  color: #8a6d3b;
+  background-color: #fcf8e3;
+  border-color: #faebcc;
+}
+.alert-warning hr {
+  border-top-color: #f7e1b5;
+}
+.alert-warning .alert-link {
+  color: #66512c;
+}
+.alert-danger {
+  color: #a94442;
+  background-color: #f2dede;
+  border-color: #ebccd1;
+}
+.alert-danger hr {
+  border-top-color: #e4b9c0;
+}
+.alert-danger .alert-link {
+  color: #843534;
+}
+@-webkit-keyframes progress-bar-stripes {
+  from {
+    background-position: 40px 0;
+  }
+  to {
+    background-position: 0 0;
+  }
+}
+@-o-keyframes progress-bar-stripes {
+  from {
+    background-position: 40px 0;
+  }
+  to {
+    background-position: 0 0;
+  }
+}
+@keyframes progress-bar-stripes {
+  from {
+    background-position: 40px 0;
+  }
+  to {
+    background-position: 0 0;
+  }
+}
+.progress {
+  height: 20px;
+  margin-bottom: 20px;
+  overflow: hidden;
+  background-color: #f5f5f5;
+  border-radius: 4px;
+  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);
+          box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);
+}
+.progress-bar {
+  float: left;
+  width: 0;
+  height: 100%;
+  font-size: 12px;
+  line-height: 20px;
+  color: #fff;
+  text-align: center;
+  background-color: #428bca;
+  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15);
+          box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15);
+  -webkit-transition: width .6s ease;
+       -o-transition: width .6s ease;
+          transition: width .6s ease;
+}
+.progress-striped .progress-bar,
+.progress-bar-striped {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  -webkit-background-size: 40px 40px;
+          background-size: 40px 40px;
+}
+.progress.active .progress-bar,
+.progress-bar.active {
+  -webkit-animation: progress-bar-stripes 2s linear infinite;
+       -o-animation: progress-bar-stripes 2s linear infinite;
+          animation: progress-bar-stripes 2s linear infinite;
+}
+.progress-bar[aria-valuenow="1"],
+.progress-bar[aria-valuenow="2"] {
+  min-width: 30px;
+}
+.progress-bar[aria-valuenow="0"] {
+  min-width: 30px;
+  color: #777;
+  background-color: transparent;
+  background-image: none;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+.progress-bar-success {
+  background-color: #5cb85c;
+}
+.progress-striped .progress-bar-success {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+}
+.progress-bar-info {
+  background-color: #5bc0de;
+}
+.progress-striped .progress-bar-info {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+}
+.progress-bar-warning {
+  background-color: #f0ad4e;
+}
+.progress-striped .progress-bar-warning {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+}
+.progress-bar-danger {
+  background-color: #d9534f;
+}
+.progress-striped .progress-bar-danger {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+}
+.media,
+.media-body {
+  overflow: hidden;
+  zoom: 1;
+}
+.media,
+.media .media {
+  margin-top: 15px;
+}
+.media:first-child {
+  margin-top: 0;
+}
+.media-object {
+  display: block;
+}
+.media-heading {
+  margin: 0 0 5px;
+}
+.media > .pull-left {
+  margin-right: 10px;
+}
+.media > .pull-right {
+  margin-left: 10px;
+}
+.media-list {
+  padding-left: 0;
+  list-style: none;
+}
+.list-group {
+  padding-left: 0;
+  margin-bottom: 20px;
+}
+.list-group-item {
+  position: relative;
+  display: block;
+  padding: 10px 15px;
+  margin-bottom: -1px;
+  background-color: #fff;
+  border: 1px solid #ddd;
+}
+.list-group-item:first-child {
+  border-top-left-radius: 4px;
+  border-top-right-radius: 4px;
+}
+.list-group-item:last-child {
+  margin-bottom: 0;
+  border-bottom-right-radius: 4px;
+  border-bottom-left-radius: 4px;
+}
+.list-group-item > .badge {
+  float: right;
+}
+.list-group-item > .badge + .badge {
+  margin-right: 5px;
+}
+a.list-group-item {
+  color: #555;
+}
+a.list-group-item .list-group-item-heading {
+  color: #333;
+}
+a.list-group-item:hover,
+a.list-group-item:focus {
+  color: #555;
+  text-decoration: none;
+  background-color: #f5f5f5;
+}
+.list-group-item.disabled,
+.list-group-item.disabled:hover,
+.list-group-item.disabled:focus {
+  color: #777;
+  background-color: #eee;
+}
+.list-group-item.disabled .list-group-item-heading,
+.list-group-item.disabled:hover .list-group-item-heading,
+.list-group-item.disabled:focus .list-group-item-heading {
+  color: inherit;
+}
+.list-group-item.disabled .list-group-item-text,
+.list-group-item.disabled:hover .list-group-item-text,
+.list-group-item.disabled:focus .list-group-item-text {
+  color: #777;
+}
+.list-group-item.active,
+.list-group-item.active:hover,
+.list-group-item.active:focus {
+  z-index: 2;
+  color: #fff;
+  background-color: #428bca;
+  border-color: #428bca;
+}
+.list-group-item.active .list-group-item-heading,
+.list-group-item.active:hover .list-group-item-heading,
+.list-group-item.active:focus .list-group-item-heading,
+.list-group-item.active .list-group-item-heading > small,
+.list-group-item.active:hover .list-group-item-heading > small,
+.list-group-item.active:focus .list-group-item-heading > small,
+.list-group-item.active .list-group-item-heading > .small,
+.list-group-item.active:hover .list-group-item-heading > .small,
+.list-group-item.active:focus .list-group-item-heading > .small {
+  color: inherit;
+}
+.list-group-item.active .list-group-item-text,
+.list-group-item.active:hover .list-group-item-text,
+.list-group-item.active:focus .list-group-item-text {
+  color: #e1edf7;
+}
+.list-group-item-success {
+  color: #3c763d;
+  background-color: #dff0d8;
+}
+a.list-group-item-success {
+  color: #3c763d;
+}
+a.list-group-item-success .list-group-item-heading {
+  color: inherit;
+}
+a.list-group-item-success:hover,
+a.list-group-item-success:focus {
+  color: #3c763d;
+  background-color: #d0e9c6;
+}
+a.list-group-item-success.active,
+a.list-group-item-success.active:hover,
+a.list-group-item-success.active:focus {
+  color: #fff;
+  background-color: #3c763d;
+  border-color: #3c763d;
+}
+.list-group-item-info {
+  color: #31708f;
+  background-color: #d9edf7;
+}
+a.list-group-item-info {
+  color: #31708f;
+}
+a.list-group-item-info .list-group-item-heading {
+  color: inherit;
+}
+a.list-group-item-info:hover,
+a.list-group-item-info:focus {
+  color: #31708f;
+  background-color: #c4e3f3;
+}
+a.list-group-item-info.active,
+a.list-group-item-info.active:hover,
+a.list-group-item-info.active:focus {
+  color: #fff;
+  background-color: #31708f;
+  border-color: #31708f;
+}
+.list-group-item-warning {
+  color: #8a6d3b;
+  background-color: #fcf8e3;
+}
+a.list-group-item-warning {
+  color: #8a6d3b;
+}
+a.list-group-item-warning .list-group-item-heading {
+  color: inherit;
+}
+a.list-group-item-warning:hover,
+a.list-group-item-warning:focus {
+  color: #8a6d3b;
+  background-color: #faf2cc;
+}
+a.list-group-item-warning.active,
+a.list-group-item-warning.active:hover,
+a.list-group-item-warning.active:focus {
+  color: #fff;
+  background-color: #8a6d3b;
+  border-color: #8a6d3b;
+}
+.list-group-item-danger {
+  color: #a94442;
+  background-color: #f2dede;
+}
+a.list-group-item-danger {
+  color: #a94442;
+}
+a.list-group-item-danger .list-group-item-heading {
+  color: inherit;
+}
+a.list-group-item-danger:hover,
+a.list-group-item-danger:focus {
+  color: #a94442;
+  background-color: #ebcccc;
+}
+a.list-group-item-danger.active,
+a.list-group-item-danger.active:hover,
+a.list-group-item-danger.active:focus {
+  color: #fff;
+  background-color: #a94442;
+  border-color: #a94442;
+}
+.list-group-item-heading {
+  margin-top: 0;
+  margin-bottom: 5px;
+}
+.list-group-item-text {
+  margin-bottom: 0;
+  line-height: 1.3;
+}
+.panel {
+  margin-bottom: 20px;
+  background-color: #fff;
+  border: 1px solid transparent;
+  border-radius: 4px;
+  -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05);
+          box-shadow: 0 1px 1px rgba(0, 0, 0, .05);
+}
+.panel-body {
+  padding: 15px;
+}
+.panel-heading {
+  padding: 10px 15px;
+  border-bottom: 1px solid transparent;
+  border-top-left-radius: 3px;
+  border-top-right-radius: 3px;
+}
+.panel-heading > .dropdown .dropdown-toggle {
+  color: inherit;
+}
+.panel-title {
+  margin-top: 0;
+  margin-bottom: 0;
+  font-size: 16px;
+  color: inherit;
+}
+.panel-title > a {
+  color: inherit;
+}
+.panel-footer {
+  padding: 10px 15px;
+  background-color: #f5f5f5;
+  border-top: 1px solid #ddd;
+  border-bottom-right-radius: 3px;
+  border-bottom-left-radius: 3px;
+}
+.panel > .list-group {
+  margin-bottom: 0;
+}
+.panel > .list-group .list-group-item {
+  border-width: 1px 0;
+  border-radius: 0;
+}
+.panel > .list-group:first-child .list-group-item:first-child {
+  border-top: 0;
+  border-top-left-radius: 3px;
+  border-top-right-radius: 3px;
+}
+.panel > .list-group:last-child .list-group-item:last-child {
+  border-bottom: 0;
+  border-bottom-right-radius: 3px;
+  border-bottom-left-radius: 3px;
+}
+.panel-heading + .list-group .list-group-item:first-child {
+  border-top-width: 0;
+}
+.list-group + .panel-footer {
+  border-top-width: 0;
+}
+.panel > .table,
+.panel > .table-responsive > .table,
+.panel > .panel-collapse > .table {
+  margin-bottom: 0;
+}
+.panel > .table:first-child,
+.panel > .table-responsive:first-child > .table:first-child {
+  border-top-left-radius: 3px;
+  border-top-right-radius: 3px;
+}
+.panel > .table:first-child > thead:first-child > tr:first-child td:first-child,
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child,
+.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child,
+.panel > .table:first-child > thead:first-child > tr:first-child th:first-child,
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child,
+.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {
+  border-top-left-radius: 3px;
+}
+.panel > .table:first-child > thead:first-child > tr:first-child td:last-child,
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child,
+.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child,
+.panel > .table:first-child > thead:first-child > tr:first-child th:last-child,
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child,
+.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {
+  border-top-right-radius: 3px;
+}
+.panel > .table:last-child,
+.panel > .table-responsive:last-child > .table:last-child {
+  border-bottom-right-radius: 3px;
+  border-bottom-left-radius: 3px;
+}
+.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child,
+.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {
+  border-bottom-left-radius: 3px;
+}
+.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child,
+.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child {
+  border-bottom-right-radius: 3px;
+}
+.panel > .panel-body + .table,
+.panel > .panel-body + .table-responsive {
+  border-top: 1px solid #ddd;
+}
+.panel > .table > tbody:first-child > tr:first-child th,
+.panel > .table > tbody:first-child > tr:first-child td {
+  border-top: 0;
+}
+.panel > .table-bordered,
+.panel > .table-responsive > .table-bordered {
+  border: 0;
+}
+.panel > .table-bordered > thead > tr > th:first-child,
+.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,
+.panel > .table-bordered > tbody > tr > th:first-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,
+.panel > .table-bordered > tfoot > tr > th:first-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,
+.panel > .table-bordered > thead > tr > td:first-child,
+.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,
+.panel > .table-bordered > tbody > tr > td:first-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,
+.panel > .table-bordered > tfoot > tr > td:first-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {
+  border-left: 0;
+}
+.panel > .table-bordered > thead > tr > th:last-child,
+.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,
+.panel > .table-bordered > tbody > tr > th:last-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,
+.panel > .table-bordered > tfoot > tr > th:last-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,
+.panel > .table-bordered > thead > tr > td:last-child,
+.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,
+.panel > .table-bordered > tbody > tr > td:last-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,
+.panel > .table-bordered > tfoot > tr > td:last-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {
+  border-right: 0;
+}
+.panel > .table-bordered > thead > tr:first-child > td,
+.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,
+.panel > .table-bordered > tbody > tr:first-child > td,
+.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,
+.panel > .table-bordered > thead > tr:first-child > th,
+.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,
+.panel > .table-bordered > tbody > tr:first-child > th,
+.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {
+  border-bottom: 0;
+}
+.panel > .table-bordered > tbody > tr:last-child > td,
+.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,
+.panel > .table-bordered > tfoot > tr:last-child > td,
+.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,
+.panel > .table-bordered > tbody > tr:last-child > th,
+.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,
+.panel > .table-bordered > tfoot > tr:last-child > th,
+.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th {
+  border-bottom: 0;
+}
+.panel > .table-responsive {
+  margin-bottom: 0;
+  border: 0;
+}
+.panel-group {
+  margin-bottom: 20px;
+}
+.panel-group .panel {
+  margin-bottom: 0;
+  border-radius: 4px;
+}
+.panel-group .panel + .panel {
+  margin-top: 5px;
+}
+.panel-group .panel-heading {
+  border-bottom: 0;
+}
+.panel-group .panel-heading + .panel-collapse > .panel-body {
+  border-top: 1px solid #ddd;
+}
+.panel-group .panel-footer {
+  border-top: 0;
+}
+.panel-group .panel-footer + .panel-collapse .panel-body {
+  border-bottom: 1px solid #ddd;
+}
+.panel-default {
+  border-color: #ddd;
+}
+.panel-default > .panel-heading {
+  color: #333;
+  background-color: #f5f5f5;
+  border-color: #ddd;
+}
+.panel-default > .panel-heading + .panel-collapse > .panel-body {
+  border-top-color: #ddd;
+}
+.panel-default > .panel-heading .badge {
+  color: #f5f5f5;
+  background-color: #333;
+}
+.panel-default > .panel-footer + .panel-collapse > .panel-body {
+  border-bottom-color: #ddd;
+}
+.panel-primary {
+  border-color: #428bca;
+}
+.panel-primary > .panel-heading {
+  color: #fff;
+  background-color: #428bca;
+  border-color: #428bca;
+}
+.panel-primary > .panel-heading + .panel-collapse > .panel-body {
+  border-top-color: #428bca;
+}
+.panel-primary > .panel-heading .badge {
+  color: #428bca;
+  background-color: #fff;
+}
+.panel-primary > .panel-footer + .panel-collapse > .panel-body {
+  border-bottom-color: #428bca;
+}
+.panel-success {
+  border-color: #d6e9c6;
+}
+.panel-success > .panel-heading {
+  color: #3c763d;
+  background-color: #dff0d8;
+  border-color: #d6e9c6;
+}
+.panel-success > .panel-heading + .panel-collapse > .panel-body {
+  border-top-color: #d6e9c6;
+}
+.panel-success > .panel-heading .badge {
+  color: #dff0d8;
+  background-color: #3c763d;
+}
+.panel-success > .panel-footer + .panel-collapse > .panel-body {
+  border-bottom-color: #d6e9c6;
+}
+.panel-info {
+  border-color: #bce8f1;
+}
+.panel-info > .panel-heading {
+  color: #31708f;
+  background-color: #d9edf7;
+  border-color: #bce8f1;
+}
+.panel-info > .panel-heading + .panel-collapse > .panel-body {
+  border-top-color: #bce8f1;
+}
+.panel-info > .panel-heading .badge {
+  color: #d9edf7;
+  background-color: #31708f;
+}
+.panel-info > .panel-footer + .panel-collapse > .panel-body {
+  border-bottom-color: #bce8f1;
+}
+.panel-warning {
+  border-color: #faebcc;
+}
+.panel-warning > .panel-heading {
+  color: #8a6d3b;
+  background-color: #fcf8e3;
+  border-color: #faebcc;
+}
+.panel-warning > .panel-heading + .panel-collapse > .panel-body {
+  border-top-color: #faebcc;
+}
+.panel-warning > .panel-heading .badge {
+  color: #fcf8e3;
+  background-color: #8a6d3b;
+}
+.panel-warning > .panel-footer + .panel-collapse > .panel-body {
+  border-bottom-color: #faebcc;
+}
+.panel-danger {
+  border-color: #ebccd1;
+}
+.panel-danger > .panel-heading {
+  color: #a94442;
+  background-color: #f2dede;
+  border-color: #ebccd1;
+}
+.panel-danger > .panel-heading + .panel-collapse > .panel-body {
+  border-top-color: #ebccd1;
+}
+.panel-danger > .panel-heading .badge {
+  color: #f2dede;
+  background-color: #a94442;
+}
+.panel-danger > .panel-footer + .panel-collapse > .panel-body {
+  border-bottom-color: #ebccd1;
+}
+.embed-responsive {
+  position: relative;
+  display: block;
+  height: 0;
+  padding: 0;
+  overflow: hidden;
+}
+.embed-responsive .embed-responsive-item,
+.embed-responsive iframe,
+.embed-responsive embed,
+.embed-responsive object {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  border: 0;
+}
+.embed-responsive.embed-responsive-16by9 {
+  padding-bottom: 56.25%;
+}
+.embed-responsive.embed-responsive-4by3 {
+  padding-bottom: 75%;
+}
+.well {
+  min-height: 20px;
+  padding: 19px;
+  margin-bottom: 20px;
+  background-color: #f5f5f5;
+  border: 1px solid #e3e3e3;
+  border-radius: 4px;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);
+}
+.well blockquote {
+  border-color: #ddd;
+  border-color: rgba(0, 0, 0, .15);
+}
+.well-lg {
+  padding: 24px;
+  border-radius: 6px;
+}
+.well-sm {
+  padding: 9px;
+  border-radius: 3px;
+}
+.close {
+  float: right;
+  font-size: 21px;
+  font-weight: bold;
+  line-height: 1;
+  color: #000;
+  text-shadow: 0 1px 0 #fff;
+  filter: alpha(opacity=20);
+  opacity: .2;
+}
+.close:hover,
+.close:focus {
+  color: #000;
+  text-decoration: none;
+  cursor: pointer;
+  filter: alpha(opacity=50);
+  opacity: .5;
+}
+button.close {
+  -webkit-appearance: none;
+  padding: 0;
+  cursor: pointer;
+  background: transparent;
+  border: 0;
+}
+.modal-open {
+  overflow: hidden;
+}
+.modal {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: 1050;
+  display: none;
+  overflow: hidden;
+  -webkit-overflow-scrolling: touch;
+  outline: 0;
+}
+.modal.fade .modal-dialog {
+  -webkit-transition: -webkit-transform .3s ease-out;
+       -o-transition:      -o-transform .3s ease-out;
+          transition:         transform .3s ease-out;
+  -webkit-transform: translate3d(0, -25%, 0);
+       -o-transform: translate3d(0, -25%, 0);
+          transform: translate3d(0, -25%, 0);
+}
+.modal.in .modal-dialog {
+  -webkit-transform: translate3d(0, 0, 0);
+       -o-transform: translate3d(0, 0, 0);
+          transform: translate3d(0, 0, 0);
+}
+.modal-open .modal {
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+.modal-dialog {
+  position: relative;
+  width: auto;
+  margin: 10px;
+}
+.modal-content {
+  position: relative;
+  background-color: #fff;
+  -webkit-background-clip: padding-box;
+          background-clip: padding-box;
+  border: 1px solid #999;
+  border: 1px solid rgba(0, 0, 0, .2);
+  border-radius: 6px;
+  outline: 0;
+  -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5);
+          box-shadow: 0 3px 9px rgba(0, 0, 0, .5);
+}
+.modal-backdrop {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: 1040;
+  background-color: #000;
+}
+.modal-backdrop.fade {
+  filter: alpha(opacity=0);
+  opacity: 0;
+}
+.modal-backdrop.in {
+  filter: alpha(opacity=50);
+  opacity: .5;
+}
+.modal-header {
+  min-height: 16.42857143px;
+  padding: 15px;
+  border-bottom: 1px solid #e5e5e5;
+}
+.modal-header .close {
+  margin-top: -2px;
+}
+.modal-title {
+  margin: 0;
+  line-height: 1.42857143;
+}
+.modal-body {
+  position: relative;
+  padding: 15px;
+}
+.modal-footer {
+  padding: 15px;
+  text-align: right;
+  border-top: 1px solid #e5e5e5;
+}
+.modal-footer .btn + .btn {
+  margin-bottom: 0;
+  margin-left: 5px;
+}
+.modal-footer .btn-group .btn + .btn {
+  margin-left: -1px;
+}
+.modal-footer .btn-block + .btn-block {
+  margin-left: 0;
+}
+.modal-scrollbar-measure {
+  position: absolute;
+  top: -9999px;
+  width: 50px;
+  height: 50px;
+  overflow: scroll;
+}
+@media (min-width: 768px) {
+  .modal-dialog {
+    width: 600px;
+    margin: 30px auto;
+  }
+  .modal-content {
+    -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5);
+            box-shadow: 0 5px 15px rgba(0, 0, 0, .5);
+  }
+  .modal-sm {
+    width: 300px;
+  }
+}
+@media (min-width: 992px) {
+  .modal-lg {
+    width: 900px;
+  }
+}
+.tooltip {
+  position: absolute;
+  z-index: 1070;
+  display: block;
+  font-size: 12px;
+  line-height: 1.4;
+  visibility: visible;
+  filter: alpha(opacity=0);
+  opacity: 0;
+}
+.tooltip.in {
+  filter: alpha(opacity=90);
+  opacity: .9;
+}
+.tooltip.top {
+  padding: 5px 0;
+  margin-top: -3px;
+}
+.tooltip.right {
+  padding: 0 5px;
+  margin-left: 3px;
+}
+.tooltip.bottom {
+  padding: 5px 0;
+  margin-top: 3px;
+}
+.tooltip.left {
+  padding: 0 5px;
+  margin-left: -3px;
+}
+.tooltip-inner {
+  max-width: 200px;
+  padding: 3px 8px;
+  color: #fff;
+  text-align: center;
+  text-decoration: none;
+  background-color: #000;
+  border-radius: 4px;
+}
+.tooltip-arrow {
+  position: absolute;
+  width: 0;
+  height: 0;
+  border-color: transparent;
+  border-style: solid;
+}
+.tooltip.top .tooltip-arrow {
+  bottom: 0;
+  left: 50%;
+  margin-left: -5px;
+  border-width: 5px 5px 0;
+  border-top-color: #000;
+}
+.tooltip.top-left .tooltip-arrow {
+  bottom: 0;
+  left: 5px;
+  border-width: 5px 5px 0;
+  border-top-color: #000;
+}
+.tooltip.top-right .tooltip-arrow {
+  right: 5px;
+  bottom: 0;
+  border-width: 5px 5px 0;
+  border-top-color: #000;
+}
+.tooltip.right .tooltip-arrow {
+  top: 50%;
+  left: 0;
+  margin-top: -5px;
+  border-width: 5px 5px 5px 0;
+  border-right-color: #000;
+}
+.tooltip.left .tooltip-arrow {
+  top: 50%;
+  right: 0;
+  margin-top: -5px;
+  border-width: 5px 0 5px 5px;
+  border-left-color: #000;
+}
+.tooltip.bottom .tooltip-arrow {
+  top: 0;
+  left: 50%;
+  margin-left: -5px;
+  border-width: 0 5px 5px;
+  border-bottom-color: #000;
+}
+.tooltip.bottom-left .tooltip-arrow {
+  top: 0;
+  left: 5px;
+  border-width: 0 5px 5px;
+  border-bottom-color: #000;
+}
+.tooltip.bottom-right .tooltip-arrow {
+  top: 0;
+  right: 5px;
+  border-width: 0 5px 5px;
+  border-bottom-color: #000;
+}
+.popover {
+  position: absolute;
+  top: 0;
+  left: 0;
+  z-index: 1060;
+  display: none;
+  max-width: 276px;
+  padding: 1px;
+  text-align: left;
+  white-space: normal;
+  background-color: #fff;
+  -webkit-background-clip: padding-box;
+          background-clip: padding-box;
+  border: 1px solid #ccc;
+  border: 1px solid rgba(0, 0, 0, .2);
+  border-radius: 6px;
+  -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
+          box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
+}
+.popover.top {
+  margin-top: -10px;
+}
+.popover.right {
+  margin-left: 10px;
+}
+.popover.bottom {
+  margin-top: 10px;
+}
+.popover.left {
+  margin-left: -10px;
+}
+.popover-title {
+  padding: 8px 14px;
+  margin: 0;
+  font-size: 14px;
+  font-weight: normal;
+  line-height: 18px;
+  background-color: #f7f7f7;
+  border-bottom: 1px solid #ebebeb;
+  border-radius: 5px 5px 0 0;
+}
+.popover-content {
+  padding: 9px 14px;
+}
+.popover > .arrow,
+.popover > .arrow:after {
+  position: absolute;
+  display: block;
+  width: 0;
+  height: 0;
+  border-color: transparent;
+  border-style: solid;
+}
+.popover > .arrow {
+  border-width: 11px;
+}
+.popover > .arrow:after {
+  content: "";
+  border-width: 10px;
+}
+.popover.top > .arrow {
+  bottom: -11px;
+  left: 50%;
+  margin-left: -11px;
+  border-top-color: #999;
+  border-top-color: rgba(0, 0, 0, .25);
+  border-bottom-width: 0;
+}
+.popover.top > .arrow:after {
+  bottom: 1px;
+  margin-left: -10px;
+  content: " ";
+  border-top-color: #fff;
+  border-bottom-width: 0;
+}
+.popover.right > .arrow {
+  top: 50%;
+  left: -11px;
+  margin-top: -11px;
+  border-right-color: #999;
+  border-right-color: rgba(0, 0, 0, .25);
+  border-left-width: 0;
+}
+.popover.right > .arrow:after {
+  bottom: -10px;
+  left: 1px;
+  content: " ";
+  border-right-color: #fff;
+  border-left-width: 0;
+}
+.popover.bottom > .arrow {
+  top: -11px;
+  left: 50%;
+  margin-left: -11px;
+  border-top-width: 0;
+  border-bottom-color: #999;
+  border-bottom-color: rgba(0, 0, 0, .25);
+}
+.popover.bottom > .arrow:after {
+  top: 1px;
+  margin-left: -10px;
+  content: " ";
+  border-top-width: 0;
+  border-bottom-color: #fff;
+}
+.popover.left > .arrow {
+  top: 50%;
+  right: -11px;
+  margin-top: -11px;
+  border-right-width: 0;
+  border-left-color: #999;
+  border-left-color: rgba(0, 0, 0, .25);
+}
+.popover.left > .arrow:after {
+  right: 1px;
+  bottom: -10px;
+  content: " ";
+  border-right-width: 0;
+  border-left-color: #fff;
+}
+.carousel {
+  position: relative;
+}
+.carousel-inner {
+  position: relative;
+  width: 100%;
+  overflow: hidden;
+}
+.carousel-inner > .item {
+  position: relative;
+  display: none;
+  -webkit-transition: .6s ease-in-out left;
+       -o-transition: .6s ease-in-out left;
+          transition: .6s ease-in-out left;
+}
+.carousel-inner > .item > img,
+.carousel-inner > .item > a > img {
+  line-height: 1;
+}
+.carousel-inner > .active,
+.carousel-inner > .next,
+.carousel-inner > .prev {
+  display: block;
+}
+.carousel-inner > .active {
+  left: 0;
+}
+.carousel-inner > .next,
+.carousel-inner > .prev {
+  position: absolute;
+  top: 0;
+  width: 100%;
+}
+.carousel-inner > .next {
+  left: 100%;
+}
+.carousel-inner > .prev {
+  left: -100%;
+}
+.carousel-inner > .next.left,
+.carousel-inner > .prev.right {
+  left: 0;
+}
+.carousel-inner > .active.left {
+  left: -100%;
+}
+.carousel-inner > .active.right {
+  left: 100%;
+}
+.carousel-control {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  width: 15%;
+  font-size: 20px;
+  color: #fff;
+  text-align: center;
+  text-shadow: 0 1px 2px rgba(0, 0, 0, .6);
+  filter: alpha(opacity=50);
+  opacity: .5;
+}
+.carousel-control.left {
+  background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%);
+  background-image:      -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%);
+  background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001)));
+  background-image:         linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);
+  background-repeat: repeat-x;
+}
+.carousel-control.right {
+  right: 0;
+  left: auto;
+  background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%);
+  background-image:      -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%);
+  background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5)));
+  background-image:         linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);
+  background-repeat: repeat-x;
+}
+.carousel-control:hover,
+.carousel-control:focus {
+  color: #fff;
+  text-decoration: none;
+  filter: alpha(opacity=90);
+  outline: 0;
+  opacity: .9;
+}
+.carousel-control .icon-prev,
+.carousel-control .icon-next,
+.carousel-control .glyphicon-chevron-left,
+.carousel-control .glyphicon-chevron-right {
+  position: absolute;
+  top: 50%;
+  z-index: 5;
+  display: inline-block;
+}
+.carousel-control .icon-prev,
+.carousel-control .glyphicon-chevron-left {
+  left: 50%;
+  margin-left: -10px;
+}
+.carousel-control .icon-next,
+.carousel-control .glyphicon-chevron-right {
+  right: 50%;
+  margin-right: -10px;
+}
+.carousel-control .icon-prev,
+.carousel-control .icon-next {
+  width: 20px;
+  height: 20px;
+  margin-top: -10px;
+  font-family: serif;
+}
+.carousel-control .icon-prev:before {
+  content: '\2039';
+}
+.carousel-control .icon-next:before {
+  content: '\203a';
+}
+.carousel-indicators {
+  position: absolute;
+  bottom: 10px;
+  left: 50%;
+  z-index: 15;
+  width: 60%;
+  padding-left: 0;
+  margin-left: -30%;
+  text-align: center;
+  list-style: none;
+}
+.carousel-indicators li {
+  display: inline-block;
+  width: 10px;
+  height: 10px;
+  margin: 1px;
+  text-indent: -999px;
+  cursor: pointer;
+  background-color: #000 \9;
+  background-color: rgba(0, 0, 0, 0);
+  border: 1px solid #fff;
+  border-radius: 10px;
+}
+.carousel-indicators .active {
+  width: 12px;
+  height: 12px;
+  margin: 0;
+  background-color: #fff;
+}
+.carousel-caption {
+  position: absolute;
+  right: 15%;
+  bottom: 20px;
+  left: 15%;
+  z-index: 10;
+  padding-top: 20px;
+  padding-bottom: 20px;
+  color: #fff;
+  text-align: center;
+  text-shadow: 0 1px 2px rgba(0, 0, 0, .6);
+}
+.carousel-caption .btn {
+  text-shadow: none;
+}
+@media screen and (min-width: 768px) {
+  .carousel-control .glyphicon-chevron-left,
+  .carousel-control .glyphicon-chevron-right,
+  .carousel-control .icon-prev,
+  .carousel-control .icon-next {
+    width: 30px;
+    height: 30px;
+    margin-top: -15px;
+    font-size: 30px;
+  }
+  .carousel-control .glyphicon-chevron-left,
+  .carousel-control .icon-prev {
+    margin-left: -15px;
+  }
+  .carousel-control .glyphicon-chevron-right,
+  .carousel-control .icon-next {
+    margin-right: -15px;
+  }
+  .carousel-caption {
+    right: 20%;
+    left: 20%;
+    padding-bottom: 30px;
+  }
+  .carousel-indicators {
+    bottom: 20px;
+  }
+}
+.clearfix:before,
+.clearfix:after,
+.dl-horizontal dd:before,
+.dl-horizontal dd:after,
+.container:before,
+.container:after,
+.container-fluid:before,
+.container-fluid:after,
+.row:before,
+.row:after,
+.form-horizontal .form-group:before,
+.form-horizontal .form-group:after,
+.btn-toolbar:before,
+.btn-toolbar:after,
+.btn-group-vertical > .btn-group:before,
+.btn-group-vertical > .btn-group:after,
+.nav:before,
+.nav:after,
+.navbar:before,
+.navbar:after,
+.navbar-header:before,
+.navbar-header:after,
+.navbar-collapse:before,
+.navbar-collapse:after,
+.pager:before,
+.pager:after,
+.panel-body:before,
+.panel-body:after,
+.modal-footer:before,
+.modal-footer:after {
+  display: table;
+  content: " ";
+}
+.clearfix:after,
+.dl-horizontal dd:after,
+.container:after,
+.container-fluid:after,
+.row:after,
+.form-horizontal .form-group:after,
+.btn-toolbar:after,
+.btn-group-vertical > .btn-group:after,
+.nav:after,
+.navbar:after,
+.navbar-header:after,
+.navbar-collapse:after,
+.pager:after,
+.panel-body:after,
+.modal-footer:after {
+  clear: both;
+}
+.center-block {
+  display: block;
+  margin-right: auto;
+  margin-left: auto;
+}
+.pull-right {
+  float: right !important;
+}
+.pull-left {
+  float: left !important;
+}
+.hide {
+  display: none !important;
+}
+.show {
+  display: block !important;
+}
+.invisible {
+  visibility: hidden;
+}
+.text-hide {
+  font: 0/0 a;
+  color: transparent;
+  text-shadow: none;
+  background-color: transparent;
+  border: 0;
+}
+.hidden {
+  display: none !important;
+  visibility: hidden !important;
+}
+.affix {
+  position: fixed;
+  -webkit-transform: translate3d(0, 0, 0);
+       -o-transform: translate3d(0, 0, 0);
+          transform: translate3d(0, 0, 0);
+}
+@-ms-viewport {
+  width: device-width;
+}
+.visible-xs,
+.visible-sm,
+.visible-md,
+.visible-lg {
+  display: none !important;
+}
+.visible-xs-block,
+.visible-xs-inline,
+.visible-xs-inline-block,
+.visible-sm-block,
+.visible-sm-inline,
+.visible-sm-inline-block,
+.visible-md-block,
+.visible-md-inline,
+.visible-md-inline-block,
+.visible-lg-block,
+.visible-lg-inline,
+.visible-lg-inline-block {
+  display: none !important;
+}
+@media (max-width: 767px) {
+  .visible-xs {
+    display: block !important;
+  }
+  table.visible-xs {
+    display: table;
+  }
+  tr.visible-xs {
+    display: table-row !important;
+  }
+  th.visible-xs,
+  td.visible-xs {
+    display: table-cell !important;
+  }
+}
+@media (max-width: 767px) {
+  .visible-xs-block {
+    display: block !important;
+  }
+}
+@media (max-width: 767px) {
+  .visible-xs-inline {
+    display: inline !important;
+  }
+}
+@media (max-width: 767px) {
+  .visible-xs-inline-block {
+    display: inline-block !important;
+  }
+}
+@media (min-width: 768px) and (max-width: 991px) {
+  .visible-sm {
+    display: block !important;
+  }
+  table.visible-sm {
+    display: table;
+  }
+  tr.visible-sm {
+    display: table-row !important;
+  }
+  th.visible-sm,
+  td.visible-sm {
+    display: table-cell !important;
+  }
+}
+@media (min-width: 768px) and (max-width: 991px) {
+  .visible-sm-block {
+    display: block !important;
+  }
+}
+@media (min-width: 768px) and (max-width: 991px) {
+  .visible-sm-inline {
+    display: inline !important;
+  }
+}
+@media (min-width: 768px) and (max-width: 991px) {
+  .visible-sm-inline-block {
+    display: inline-block !important;
+  }
+}
+@media (min-width: 992px) and (max-width: 1199px) {
+  .visible-md {
+    display: block !important;
+  }
+  table.visible-md {
+    display: table;
+  }
+  tr.visible-md {
+    display: table-row !important;
+  }
+  th.visible-md,
+  td.visible-md {
+    display: table-cell !important;
+  }
+}
+@media (min-width: 992px) and (max-width: 1199px) {
+  .visible-md-block {
+    display: block !important;
+  }
+}
+@media (min-width: 992px) and (max-width: 1199px) {
+  .visible-md-inline {
+    display: inline !important;
+  }
+}
+@media (min-width: 992px) and (max-width: 1199px) {
+  .visible-md-inline-block {
+    display: inline-block !important;
+  }
+}
+@media (min-width: 1200px) {
+  .visible-lg {
+    display: block !important;
+  }
+  table.visible-lg {
+    display: table;
+  }
+  tr.visible-lg {
+    display: table-row !important;
+  }
+  th.visible-lg,
+  td.visible-lg {
+    display: table-cell !important;
+  }
+}
+@media (min-width: 1200px) {
+  .visible-lg-block {
+    display: block !important;
+  }
+}
+@media (min-width: 1200px) {
+  .visible-lg-inline {
+    display: inline !important;
+  }
+}
+@media (min-width: 1200px) {
+  .visible-lg-inline-block {
+    display: inline-block !important;
+  }
+}
+@media (max-width: 767px) {
+  .hidden-xs {
+    display: none !important;
+  }
+}
+@media (min-width: 768px) and (max-width: 991px) {
+  .hidden-sm {
+    display: none !important;
+  }
+}
+@media (min-width: 992px) and (max-width: 1199px) {
+  .hidden-md {
+    display: none !important;
+  }
+}
+@media (min-width: 1200px) {
+  .hidden-lg {
+    display: none !important;
+  }
+}
+.visible-print {
+  display: none !important;
+}
+@media print {
+  .visible-print {
+    display: block !important;
+  }
+  table.visible-print {
+    display: table;
+  }
+  tr.visible-print {
+    display: table-row !important;
+  }
+  th.visible-print,
+  td.visible-print {
+    display: table-cell !important;
+  }
+}
+.visible-print-block {
+  display: none !important;
+}
+@media print {
+  .visible-print-block {
+    display: block !important;
+  }
+}
+.visible-print-inline {
+  display: none !important;
+}
+@media print {
+  .visible-print-inline {
+    display: inline !important;
+  }
+}
+.visible-print-inline-block {
+  display: none !important;
+}
+@media print {
+  .visible-print-inline-block {
+    display: inline-block !important;
+  }
+}
+@media print {
+  .hidden-print {
+    display: none !important;
+  }
+}
+/*# sourceMappingURL=bootstrap.css.map */

File diff suppressed because it is too large
+ 0 - 0
app/assets/stylesheets/blazer/bootstrap.css.map


+ 267 - 0
app/assets/stylesheets/blazer/daterangepicker-bs3.css

@@ -0,0 +1,267 @@
+/*!
+ * Stylesheet for the Date Range Picker, for use with Bootstrap 3.x
+ *
+ * Copyright 2013 Dan Grossman ( http://www.dangrossman.info )
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Built for http://www.improvely.com
+ */
+
+ .daterangepicker.dropdown-menu {
+  max-width: none;
+  z-index: 3000;
+}
+
+.daterangepicker.opensleft .ranges, .daterangepicker.opensleft .calendar {
+  float: left;
+  margin: 4px;
+}
+
+.daterangepicker.opensright .ranges, .daterangepicker.opensright .calendar {
+  float: right;
+  margin: 4px;
+}
+
+.daterangepicker .ranges {
+  width: 160px;
+  text-align: left;
+}
+
+.daterangepicker .ranges .range_inputs>div {
+  float: left;
+}
+
+.daterangepicker .ranges .range_inputs>div:nth-child(2) {
+  padding-left: 11px;
+}
+
+.daterangepicker .calendar {
+  display: none;
+  max-width: 270px;
+}
+
+.daterangepicker.show-calendar .calendar {
+    display: block;
+}
+
+.daterangepicker .calendar.single .calendar-date {
+  border: none;
+}
+
+.daterangepicker .calendar th, .daterangepicker .calendar td {
+  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+  white-space: nowrap;
+  text-align: center;
+  min-width: 32px;
+}
+
+.daterangepicker .daterangepicker_start_input label,
+.daterangepicker .daterangepicker_end_input label {
+  color: #333;
+  display: block;
+  font-size: 11px;
+  font-weight: normal;
+  height: 20px;
+  line-height: 20px;
+  margin-bottom: 2px;
+  text-shadow: #fff 1px 1px 0px;
+  text-transform: uppercase;
+  width: 74px;
+}
+
+.daterangepicker .ranges input {
+  font-size: 11px;
+}
+
+.daterangepicker .ranges .input-mini {
+  background-color: #eee;
+  border: 1px solid #ccc;
+  border-radius: 4px;
+  color: #555;
+  display: block;
+  font-size: 11px;
+  height: 30px;
+  line-height: 30px;
+  vertical-align: middle;
+  margin: 0 0 10px 0;
+  padding: 0 6px;
+  width: 74px;
+}
+
+.daterangepicker .ranges ul {
+  list-style: none;
+  margin: 0;
+  padding: 0;
+}
+
+.daterangepicker .ranges li {
+  font-size: 13px;
+  background: #f5f5f5;
+  border: 1px solid #f5f5f5;
+  color: #08c;
+  padding: 3px 12px;
+  margin-bottom: 8px;
+  -webkit-border-radius: 5px;
+  -moz-border-radius: 5px;
+  border-radius: 5px;
+  cursor: pointer;
+}
+
+.daterangepicker .ranges li.active, .daterangepicker .ranges li:hover {
+  background: #08c;
+  border: 1px solid #08c;
+  color: #fff;
+}
+
+.daterangepicker .calendar-date {
+  border: 1px solid #ddd;
+  padding: 4px;
+  border-radius: 4px;
+  background: #fff;
+}
+
+.daterangepicker .calendar-time {
+  text-align: center;
+  margin: 8px auto 0 auto;
+  line-height: 30px;
+}
+
+.daterangepicker {
+  position: absolute;
+  background: #fff;
+  top: 100px;
+  left: 20px;
+  padding: 4px;
+  margin-top: 1px;
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+  border-radius: 4px;
+}
+
+.daterangepicker.opensleft:before {
+  position: absolute;
+  top: -7px;
+  right: 9px;
+  display: inline-block;
+  border-right: 7px solid transparent;
+  border-bottom: 7px solid #ccc;
+  border-left: 7px solid transparent;
+  border-bottom-color: rgba(0, 0, 0, 0.2);
+  content: '';
+}
+
+.daterangepicker.opensleft:after {
+  position: absolute;
+  top: -6px;
+  right: 10px;
+  display: inline-block;
+  border-right: 6px solid transparent;
+  border-bottom: 6px solid #fff;
+  border-left: 6px solid transparent;
+  content: '';
+}
+
+.daterangepicker.opensright:before {
+  position: absolute;
+  top: -7px;
+  left: 9px;
+  display: inline-block;
+  border-right: 7px solid transparent;
+  border-bottom: 7px solid #ccc;
+  border-left: 7px solid transparent;
+  border-bottom-color: rgba(0, 0, 0, 0.2);
+  content: '';
+}
+
+.daterangepicker.opensright:after {
+  position: absolute;
+  top: -6px;
+  left: 10px;
+  display: inline-block;
+  border-right: 6px solid transparent;
+  border-bottom: 6px solid #fff;
+  border-left: 6px solid transparent;
+  content: '';
+}
+
+.daterangepicker table {
+  width: 100%;
+  margin: 0;
+}
+
+.daterangepicker td, .daterangepicker th {
+  text-align: center;
+  width: 20px;
+  height: 20px;
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+  border-radius: 4px;
+  cursor: pointer;
+  white-space: nowrap;
+}
+
+.daterangepicker td.off {
+  color: #999;
+}
+
+.daterangepicker td.disabled {
+  color: #999;
+}
+
+.daterangepicker td.available:hover, .daterangepicker th.available:hover {
+  background: #eee;
+}
+
+.daterangepicker td.in-range {
+  background: #ebf4f8;
+  -webkit-border-radius: 0;
+  -moz-border-radius: 0;
+  border-radius: 0;
+}
+
+.daterangepicker td.active, .daterangepicker td.active:hover {
+  background-color: #357ebd;
+  border-color: #3071a9;
+  color: #fff;
+}
+
+.daterangepicker td.week, .daterangepicker th.week {
+  font-size: 80%;
+  color: #ccc;
+}
+
+.daterangepicker select.monthselect, .daterangepicker select.yearselect {
+  font-size: 12px;
+  padding: 1px;
+  height: auto;
+  margin: 0;
+  cursor: default;
+}
+
+.daterangepicker select.monthselect {
+  margin-right: 2%;
+  width: 56%;
+}
+
+.daterangepicker select.yearselect {
+  width: 40%;
+}
+
+.daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.ampmselect {
+  width: 50px;
+  margin-bottom: 0;
+}
+
+.daterangepicker_start_input {
+  float: left;
+}
+
+.daterangepicker_end_input {
+  float: left;
+  padding-left: 11px
+}
+
+.daterangepicker th.month {
+  width: auto;
+}

+ 126 - 0
app/assets/stylesheets/blazer/github.css

@@ -0,0 +1,126 @@
+/*
+
+github.com style (c) Vasily Polovnyov <vast@whiteants.net>
+
+*/
+
+.hljs {
+  display: block; padding: 0.5em;
+  color: #333;
+  background: #f8f8f8
+}
+
+.hljs-comment,
+.hljs-template_comment,
+.diff .hljs-header,
+.hljs-javadoc {
+  color: #998;
+  font-style: italic
+}
+
+.hljs-keyword,
+.css .rule .hljs-keyword,
+.hljs-winutils,
+.javascript .hljs-title,
+.nginx .hljs-title,
+.hljs-subst,
+.hljs-request,
+.hljs-status {
+  color: #333;
+  font-weight: bold
+}
+
+.hljs-number,
+.hljs-hexcolor,
+.ruby .hljs-constant {
+  color: #099;
+}
+
+.hljs-string,
+.hljs-tag .hljs-value,
+.hljs-phpdoc,
+.tex .hljs-formula {
+  color: #d14
+}
+
+.hljs-title,
+.hljs-id,
+.coffeescript .hljs-params,
+.scss .hljs-preprocessor {
+  color: #900;
+  font-weight: bold
+}
+
+.javascript .hljs-title,
+.lisp .hljs-title,
+.clojure .hljs-title,
+.hljs-subst {
+  font-weight: normal
+}
+
+.hljs-class .hljs-title,
+.haskell .hljs-type,
+.vhdl .hljs-literal,
+.tex .hljs-command {
+  color: #458;
+  font-weight: bold
+}
+
+.hljs-tag,
+.hljs-tag .hljs-title,
+.hljs-rules .hljs-property,
+.django .hljs-tag .hljs-keyword {
+  color: #000080;
+  font-weight: normal
+}
+
+.hljs-attribute,
+.lisp .hljs-body {
+  color: #008080
+}
+
+.hljs-variable,
+.hljs-regexp {
+  color: #009926;
+  font-weight: bold
+}
+
+.hljs-symbol,
+.ruby .hljs-symbol .hljs-string,
+.lisp .hljs-keyword,
+.tex .hljs-special,
+.hljs-prompt {
+  color: #990073
+}
+
+.hljs-built_in,
+.lisp .hljs-title,
+.clojure .hljs-built_in {
+  color: #0086b3
+}
+
+.hljs-preprocessor,
+.hljs-pragma,
+.hljs-pi,
+.hljs-doctype,
+.hljs-shebang,
+.hljs-cdata {
+  color: #999;
+  font-weight: bold
+}
+
+.hljs-deletion {
+  background: #fdd
+}
+
+.hljs-addition {
+  background: #dfd
+}
+
+.diff .hljs-change {
+  background: #0086b3
+}
+
+.hljs-chunk {
+  color: #aaa
+}

+ 386 - 0
app/assets/stylesheets/blazer/selectize.default.css

@@ -0,0 +1,386 @@
+/**
+ * selectize.default.css (v0.10.1) - Default Theme
+ * Copyright (c) 2013 Brian Reavis & contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
+ * file except in compliance with the License. You may obtain a copy of the License at:
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
+ * ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ *
+ * @author Brian Reavis <brian@thirdroute.com>
+ */
+.selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder {
+  visibility: visible !important;
+  background: #f2f2f2 !important;
+  background: rgba(0, 0, 0, 0.06) !important;
+  border: 0 none !important;
+  -webkit-box-shadow: inset 0 0 12px 4px #ffffff;
+  box-shadow: inset 0 0 12px 4px #ffffff;
+}
+.selectize-control.plugin-drag_drop .ui-sortable-placeholder::after {
+  content: '!';
+  visibility: hidden;
+}
+.selectize-control.plugin-drag_drop .ui-sortable-helper {
+  -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
+  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
+}
+.selectize-dropdown-header {
+  position: relative;
+  padding: 5px 8px;
+  border-bottom: 1px solid #d0d0d0;
+  background: #f8f8f8;
+  -webkit-border-radius: 3px 3px 0 0;
+  -moz-border-radius: 3px 3px 0 0;
+  border-radius: 3px 3px 0 0;
+}
+.selectize-dropdown-header-close {
+  position: absolute;
+  right: 8px;
+  top: 50%;
+  color: #303030;
+  opacity: 0.4;
+  margin-top: -12px;
+  line-height: 20px;
+  font-size: 20px !important;
+}
+.selectize-dropdown-header-close:hover {
+  color: #000000;
+}
+.selectize-dropdown.plugin-optgroup_columns .optgroup {
+  border-right: 1px solid #f2f2f2;
+  border-top: 0 none;
+  float: left;
+  -webkit-box-sizing: border-box;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+}
+.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child {
+  border-right: 0 none;
+}
+.selectize-dropdown.plugin-optgroup_columns .optgroup:before {
+  display: none;
+}
+.selectize-dropdown.plugin-optgroup_columns .optgroup-header {
+  border-top: 0 none;
+}
+.selectize-control.plugin-remove_button [data-value] {
+  position: relative;
+  padding-right: 24px !important;
+}
+.selectize-control.plugin-remove_button [data-value] .remove {
+  z-index: 1;
+  /* fixes ie bug (see #392) */
+  position: absolute;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  width: 17px;
+  text-align: center;
+  font-weight: bold;
+  font-size: 12px;
+  color: inherit;
+  text-decoration: none;
+  vertical-align: middle;
+  display: inline-block;
+  padding: 2px 0 0 0;
+  border-left: 1px solid #0073bb;
+  -webkit-border-radius: 0 2px 2px 0;
+  -moz-border-radius: 0 2px 2px 0;
+  border-radius: 0 2px 2px 0;
+  -webkit-box-sizing: border-box;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+}
+.selectize-control.plugin-remove_button [data-value] .remove:hover {
+  background: rgba(0, 0, 0, 0.05);
+}
+.selectize-control.plugin-remove_button [data-value].active .remove {
+  border-left-color: #00578d;
+}
+.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover {
+  background: none;
+}
+.selectize-control.plugin-remove_button .disabled [data-value] .remove {
+  border-left-color: #aaaaaa;
+}
+.selectize-control {
+  position: relative;
+}
+.selectize-dropdown,
+.selectize-input,
+.selectize-input input {
+  color: #303030;
+  font-family: inherit;
+  font-size: 13px;
+  line-height: 18px;
+  -webkit-font-smoothing: inherit;
+}
+.selectize-input,
+.selectize-control.single .selectize-input.input-active {
+  background: #ffffff;
+  cursor: text;
+  display: inline-block;
+}
+.selectize-input {
+  border: 1px solid #d0d0d0;
+  padding: 8px 8px;
+  display: inline-block;
+  width: 100%;
+  overflow: hidden;
+  position: relative;
+  z-index: 1;
+  -webkit-box-sizing: border-box;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
+  -webkit-border-radius: 3px;
+  -moz-border-radius: 3px;
+  border-radius: 3px;
+}
+.selectize-control.multi .selectize-input.has-items {
+  padding: 5px 8px 2px;
+}
+.selectize-input.full {
+  background-color: #ffffff;
+}
+.selectize-input.disabled,
+.selectize-input.disabled * {
+  cursor: default !important;
+}
+.selectize-input.focus {
+  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
+  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
+}
+.selectize-input.dropdown-active {
+  -webkit-border-radius: 3px 3px 0 0;
+  -moz-border-radius: 3px 3px 0 0;
+  border-radius: 3px 3px 0 0;
+}
+.selectize-input > * {
+  vertical-align: baseline;
+  display: -moz-inline-stack;
+  display: inline-block;
+  zoom: 1;
+  *display: inline;
+}
+.selectize-control.multi .selectize-input > div {
+  cursor: pointer;
+  margin: 0 3px 3px 0;
+  padding: 2px 6px;
+  background: #1da7ee;
+  color: #ffffff;
+  border: 1px solid #0073bb;
+}
+.selectize-control.multi .selectize-input > div.active {
+  background: #92c836;
+  color: #ffffff;
+  border: 1px solid #00578d;
+}
+.selectize-control.multi .selectize-input.disabled > div,
+.selectize-control.multi .selectize-input.disabled > div.active {
+  color: #ffffff;
+  background: #d2d2d2;
+  border: 1px solid #aaaaaa;
+}
+.selectize-input > input {
+  padding: 0 !important;
+  min-height: 0 !important;
+  max-height: none !important;
+  max-width: 100% !important;
+  margin: 0 1px !important;
+  text-indent: 0 !important;
+  border: 0 none !important;
+  background: none !important;
+  line-height: inherit !important;
+  -webkit-user-select: auto !important;
+  -webkit-box-shadow: none !important;
+  box-shadow: none !important;
+}
+.selectize-input > input::-ms-clear {
+  display: none;
+}
+.selectize-input > input:focus {
+  outline: none !important;
+}
+.selectize-input::after {
+  content: ' ';
+  display: block;
+  clear: left;
+}
+.selectize-input.dropdown-active::before {
+  content: ' ';
+  display: block;
+  position: absolute;
+  background: #f0f0f0;
+  height: 1px;
+  bottom: 0;
+  left: 0;
+  right: 0;
+}
+.selectize-dropdown {
+  position: absolute;
+  z-index: 10;
+  border: 1px solid #d0d0d0;
+  background: #ffffff;
+  margin: -1px 0 0 0;
+  border-top: 0 none;
+  -webkit-box-sizing: border-box;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+  -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+  -webkit-border-radius: 0 0 3px 3px;
+  -moz-border-radius: 0 0 3px 3px;
+  border-radius: 0 0 3px 3px;
+}
+.selectize-dropdown [data-selectable] {
+  cursor: pointer;
+  overflow: hidden;
+}
+.selectize-dropdown [data-selectable] .highlight {
+  background: rgba(125, 168, 208, 0.2);
+  -webkit-border-radius: 1px;
+  -moz-border-radius: 1px;
+  border-radius: 1px;
+}
+.selectize-dropdown [data-selectable],
+.selectize-dropdown .optgroup-header {
+  padding: 5px 8px;
+}
+.selectize-dropdown .optgroup:first-child .optgroup-header {
+  border-top: 0 none;
+}
+.selectize-dropdown .optgroup-header {
+  color: #303030;
+  background: #ffffff;
+  cursor: default;
+}
+.selectize-dropdown .active {
+  background-color: #f5fafd;
+  color: #495c68;
+}
+.selectize-dropdown .active.create {
+  color: #495c68;
+}
+.selectize-dropdown .create {
+  color: rgba(48, 48, 48, 0.5);
+}
+.selectize-dropdown-content {
+  overflow-y: auto;
+  overflow-x: hidden;
+  max-height: 200px;
+}
+.selectize-control.single .selectize-input,
+.selectize-control.single .selectize-input input {
+  cursor: pointer;
+}
+.selectize-control.single .selectize-input.input-active,
+.selectize-control.single .selectize-input.input-active input {
+  cursor: text;
+}
+.selectize-control.single .selectize-input:after {
+  content: ' ';
+  display: block;
+  position: absolute;
+  top: 50%;
+  right: 15px;
+  margin-top: -3px;
+  width: 0;
+  height: 0;
+  border-style: solid;
+  border-width: 5px 5px 0 5px;
+  border-color: #808080 transparent transparent transparent;
+}
+.selectize-control.single .selectize-input.dropdown-active:after {
+  margin-top: -4px;
+  border-width: 0 5px 5px 5px;
+  border-color: transparent transparent #808080 transparent;
+}
+.selectize-control.rtl.single .selectize-input:after {
+  left: 15px;
+  right: auto;
+}
+.selectize-control.rtl .selectize-input > input {
+  margin: 0 4px 0 -2px !important;
+}
+.selectize-control .selectize-input.disabled {
+  opacity: 0.5;
+  background-color: #fafafa;
+}
+.selectize-control.multi .selectize-input.has-items {
+  padding-left: 5px;
+  padding-right: 5px;
+}
+.selectize-control.multi .selectize-input.disabled [data-value] {
+  color: #999;
+  text-shadow: none;
+  background: none;
+  -webkit-box-shadow: none;
+  box-shadow: none;
+}
+.selectize-control.multi .selectize-input.disabled [data-value],
+.selectize-control.multi .selectize-input.disabled [data-value] .remove {
+  border-color: #e6e6e6;
+}
+.selectize-control.multi .selectize-input.disabled [data-value] .remove {
+  background: none;
+}
+.selectize-control.multi .selectize-input [data-value] {
+  text-shadow: 0 1px 0 rgba(0, 51, 83, 0.3);
+  -webkit-border-radius: 3px;
+  -moz-border-radius: 3px;
+  border-radius: 3px;
+  background-color: #1b9dec;
+  background-image: -moz-linear-gradient(top, #1da7ee, #178ee9);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#1da7ee), to(#178ee9));
+  background-image: -webkit-linear-gradient(top, #1da7ee, #178ee9);
+  background-image: -o-linear-gradient(top, #1da7ee, #178ee9);
+  background-image: linear-gradient(to bottom, #1da7ee, #178ee9);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff1da7ee', endColorstr='#ff178ee9', GradientType=0);
+  -webkit-box-shadow: 0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03);
+  box-shadow: 0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03);
+}
+.selectize-control.multi .selectize-input [data-value].active {
+  background-color: #0085d4;
+  background-image: -moz-linear-gradient(top, #008fd8, #0075cf);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#008fd8), to(#0075cf));
+  background-image: -webkit-linear-gradient(top, #008fd8, #0075cf);
+  background-image: -o-linear-gradient(top, #008fd8, #0075cf);
+  background-image: linear-gradient(to bottom, #008fd8, #0075cf);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff008fd8', endColorstr='#ff0075cf', GradientType=0);
+}
+.selectize-control.single .selectize-input {
+  -webkit-box-shadow: 0 1px 0 rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.8);
+  box-shadow: 0 1px 0 rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.8);
+  background-color: #f9f9f9;
+  background-image: -moz-linear-gradient(top, #fefefe, #f2f2f2);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fefefe), to(#f2f2f2));
+  background-image: -webkit-linear-gradient(top, #fefefe, #f2f2f2);
+  background-image: -o-linear-gradient(top, #fefefe, #f2f2f2);
+  background-image: linear-gradient(to bottom, #fefefe, #f2f2f2);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffefefe', endColorstr='#fff2f2f2', GradientType=0);
+}
+.selectize-control.single .selectize-input,
+.selectize-dropdown.single {
+  border-color: #b8b8b8;
+}
+.selectize-dropdown .optgroup-header {
+  padding-top: 7px;
+  font-weight: bold;
+  font-size: 0.85em;
+}
+.selectize-dropdown .optgroup {
+  border-top: 1px solid #f0f0f0;
+}
+.selectize-dropdown .optgroup:first-child {
+  border-top: 0 none;
+}

+ 216 - 0
app/controllers/blazer/queries_controller.rb

@@ -0,0 +1,216 @@
+module Blazer
+  class QueriesController < ApplicationController
+    # skip all filters
+    skip_filter *_process_action_callbacks.map(&:filter)
+
+    protect_from_forgery with: :exception
+
+    if ENV["BLAZER_PASSWORD"]
+      http_basic_authenticate_with name: ENV["BLAZER_USERNAME"], password: ENV["BLAZER_PASSWORD"]
+    end
+
+    layout "blazer/application"
+
+    before_action :ensure_database_url
+    before_action :set_query, only: [:show, :edit, :update, :destroy]
+
+    def index
+      @queries = Blazer::Query.order(:name)
+    end
+
+    def new
+      @query = Blazer::Query.new(statement: params[:statement])
+    end
+
+    def create
+      @query = Blazer::Query.new(query_params)
+      @query.creator = current_user if respond_to?(:current_user)
+
+      if @query.save
+        redirect_to @query
+      else
+        render :new
+      end
+    end
+
+    def show
+      @statement = @query.statement
+      process_vars(@statement)
+
+      @smart_vars = {}
+      @sql_errors = []
+      @bind_vars.each do |var|
+        query = smart_variables[var]
+        if query
+          rows, error = run_statement(query)
+          @smart_vars[var] = rows.map{|v| v.values.reverse }
+          @sql_errors << error if error
+        end
+      end
+    end
+
+    def edit
+    end
+
+    def run
+      @statement = params[:statement]
+      process_vars(@statement)
+
+      if @success
+        # audit
+        if Blazer.audit
+          audit = Blazer::Audit.new(statement: @statement)
+          audit.user = current_user if respond_to?(:current_user)
+          audit.save!
+        end
+
+        @rows, @error = run_statement(@statement)
+
+        @columns = {}
+        if @rows.any?
+          @rows.first.each do |key, value|
+            @columns[key] =
+              case value
+              when Integer
+                "int"
+              when Float
+                "float"
+              else
+                "string-ins"
+              end
+          end
+        end
+
+        @query = Query.find_by(id: params[:query_id]) if params[:query_id]
+        @filename = @query.name.parameterize if @query
+
+        @min_width_types = (@rows.first || {}).select{|k, v| v.is_a?(Time) or v.is_a?(String) or smart_columns[k] }.keys
+
+        @boom = {}
+        @columns.keys.each do |key|
+          query = smart_columns[key]
+          if query
+            values = @rows.map{|r| r[key] }.compact.uniq
+            rows, error = run_statement(ActiveRecord::Base.send(:sanitize_sql_array, [query.sub("{value}", "(?)"), values]))
+            @boom[key] = Hash[ rows.map(&:values) ]
+          end
+        end
+
+        @linked_columns = linked_columns
+      end
+
+      respond_to do |format|
+        format.html do
+          render layout: false
+        end
+        format.csv do
+          send_data csv_data(@rows), type: 'text/csv; charset=utf-8; header=present', disposition: "attachment; filename=\"#{@query ? @query.name.parameterize : "query"}.csv\""
+        end
+      end
+    end
+
+    def update
+      if @query.update(query_params)
+        redirect_to query_path(@query)
+      else
+        render :edit
+      end
+    end
+
+    def destroy
+      @query.destroy
+      redirect_to root_url
+    end
+
+    private
+
+    def ensure_database_url
+      render text: "BLAZER_DATABASE_URL required" if !ENV["BLAZER_DATABASE_URL"]
+    end
+
+    def set_query
+      @query = Blazer::Query.find(params[:id].to_s.split("-").first)
+    end
+
+    def query_params
+      params.require(:query).permit(:name, :description, :statement)
+    end
+
+    def csv_data(rows)
+      CSV.generate do |csv|
+        if rows.any?
+          csv << rows.first.keys
+        end
+        rows.each do |row|
+          csv << row.values
+        end
+      end
+    end
+
+    def run_statement(statement)
+      rows = []
+      error = nil
+      begin
+        Blazer::Connection.transaction do
+          result = Blazer::Connection.connection.select_all(statement)
+          result.each do |untyped_row|
+            row = {}
+            untyped_row.each do |k, v|
+              row[k] = result.column_types[k].type_cast(v)
+            end
+            rows << row
+          end
+          raise ActiveRecord::Rollback
+        end
+      rescue ActiveRecord::StatementInvalid => e
+        error = e.message.sub(/.+ERROR: /, "")
+      end
+      [rows, error]
+    end
+
+    def extract_vars(statement)
+      statement.scan(/\{.*?\}/).map{|v| v[1...-1] }.uniq
+    end
+
+    def process_vars(statement)
+      @bind_vars = extract_vars(statement)
+      @success = @bind_vars.all?{|v| params[v] }
+
+      if @success
+        @bind_vars.each do |var|
+          value = params[var].presence
+          value = value.to_i if value.to_i.to_s == value
+          if var.end_with?("_at")
+            value = Blazer.time_zone.parse(value) rescue nil
+          end
+          statement.gsub!("{#{var}}", ActiveRecord::Base.connection.quote(value))
+        end
+      end
+    end
+
+    def settings
+      YAML.load(File.read(Rails.root.join("config", "blazer.yml")))
+    end
+
+    def linked_columns
+      settings["linked_columns"] || {}
+    end
+
+    def smart_columns
+      settings["smart_columns"] || {}
+    end
+
+    def smart_variables
+      settings["smart_variables"] || {}
+    end
+
+    def tables
+      default_schema = Blazer::Connection.connection.adapter_name == "PostgreSQL" ? "public" : Blazer::Connection.connection_config[:database]
+      schema = Blazer::Connection.connection_config[:schema] || default_schema
+      rows, error = run_statement(Blazer::Connection.send(:sanitize_sql_array, ["SELECT table_name, column_name, ordinal_position, data_type FROM information_schema.columns WHERE table_schema = ?", schema]))
+      Hash[ rows.group_by{|r| r["table_name"] }.map{|t, f| [t, f.sort_by{|f| f["ordinal_position"] }.map{|f| f.slice("column_name", "data_type") }] }.sort_by{|t, f| t } ]
+    end
+    helper_method :tables
+
+  end
+end

+ 21 - 0
app/helpers/blazer/queries_helper.rb

@@ -0,0 +1,21 @@
+module Blazer
+  module QueriesHelper
+
+    def title(title = nil)
+      if title
+        content_for(:title) { title }
+      else
+        content_for?(:title) ? content_for(:title) : nil
+      end
+    end
+
+    def format_value(key, value)
+      if value.is_a?(Integer) and !key.to_s.end_with?("id")
+        number_with_delimiter(value)
+      else
+        value
+      end
+    end
+
+  end
+end

+ 5 - 0
app/models/blazer/audit.rb

@@ -0,0 +1,5 @@
+module Blazer
+  class Audit < ActiveRecord::Base
+
+  end
+end

+ 5 - 0
app/models/blazer/connection.rb

@@ -0,0 +1,5 @@
+module Blazer
+  class Connection < ActiveRecord::Base
+    establish_connection ENV["BLAZER_DATABASE_URL"] if ENV["BLAZER_DATABASE_URL"]
+  end
+end

+ 13 - 0
app/models/blazer/query.rb

@@ -0,0 +1,13 @@
+module Blazer
+  class Query < ActiveRecord::Base
+    belongs_to :creator, class_name: "User"
+
+    validates :name, presence: true
+    validates :statement, presence: true
+
+    def to_param
+      [id, name.remove("'").parameterize].join("-")
+    end
+
+  end
+end

+ 84 - 0
app/views/blazer/queries/_form.html.erb

@@ -0,0 +1,84 @@
+<% if @query.errors.any? %>
+  <div class="alert alert-danger"><%= @query.errors.full_messages.first %></div>
+<% end %>
+
+<%= form_for @query, html: {class: "the_form", autocomplete: "off"} do |f| %>
+  <div class="row">
+    <div class="col-xs-8">
+      <div class= "form-group">
+        <%= f.hidden_field :statement %>
+        <div id="editor-container">
+          <div id="editor"><%= @query.statement %></div>
+        </div>
+      </div>
+      <div class="form-group text-right">
+        <div class="pull-left" style="margin-top: 6px;">
+          <%= link_to "Back", :back %>
+          <span class="text-muted" style="margin-left: 20px;"> Use {start_time} and {end_time} for time ranges</span>
+        </div>
+        <%= select_tag :table_names, options_for_select([["Preview table", nil]] + tables.keys), style: "margin-right: 20px; width: 240px;" %>
+        <%= link_to "Run", "#", class: "btn btn-info", id: "run", style: "vertical-align: top;" %>
+      </div>
+    </div>
+    <div class="col-xs-4">
+      <div class="form-group">
+        <%= f.text_field :name, placeholder: "Name", class: "form-control" %>
+      </div>
+      <div class="form-group">
+        <%= f.text_area :description, placeholder: "Optional description", style: "height: 70px;", class: "form-control" %>
+      </div>
+      <div class="text-right">
+        <% if @query.persisted? %>
+          <%= link_to "Delete", query_path(@query), method: :delete, "data-confirm" => "Are you sure?", class: "btn btn-danger" %>
+        <% end %>
+        <%= f.submit "Save", class: "btn btn-success" %>
+      </div>
+    </div>
+  </div>
+<% end %>
+
+<div id="results"></div>
+
+<script>
+  var editor = ace.edit("editor");
+  editor.setTheme("ace/theme/twilight");
+  editor.getSession().setMode("ace/mode/sql");
+  editor.setOptions({
+    enableBasicAutocompletion: false,
+    enableSnippets: false,
+    enableLiveAutocompletion: false,
+    highlightActiveLine: false,
+    fontSize: 12
+  });
+  editor.renderer.setShowGutter(false);
+  editor.renderer.setPrintMarginColumn(false);
+  editor.renderer.setPadding(10);
+  editor.getSession().setUseWrapMode(true);
+  $("#editor").show();
+
+  $("#run").click( function (e) {
+    e.preventDefault();
+    $("#results").html('<p class="text-muted">Loading...</p>');
+    $.post("<%= run_queries_path %>", {statement: editor.getValue()}, function (data) {
+      $("#results").html(data);
+    });
+  });
+
+  if ($("#query_statement").val() != "") {
+    $("#run").click();
+  }
+
+  $("#table_names").selectize({
+  }).change( function () {
+    var val = $(this).val();
+    if (val.length > 0) {
+      editor.setValue("SELECT * FROM " + val + " LIMIT 10");
+      $("#run").click();
+    }
+  });
+
+  $("form.the_form").on("submit", function() {
+    $("#query_statement").val(editor.getValue());
+    return true;
+  });
+</script>

+ 1 - 0
app/views/blazer/queries/edit.html.erb

@@ -0,0 +1 @@
+<%= render partial: "form" %>

+ 47 - 0
app/views/blazer/queries/index.html.erb

@@ -0,0 +1,47 @@
+<div id="queries">
+  <div id="header" style="margin-bottom: 20px;">
+    <div class="pull-right">
+      <%= link_to "New Query", new_query_path, class: "btn btn-info" %>
+    </div>
+    <input type="text" placeholder="Start typing a query or person" style="width: 300px; display: inline-block;" autofocus=true class="search form-control" />
+  </div>
+
+  <table class="table">
+    <thead>
+      <tr>
+        <th>Query</th>
+        <th style="width: 20%; text-align: right;">Mastermind</th>
+      </tr>
+    </thead>
+    <tbody class="list">
+      <% @queries.each do |query| %>
+        <tr>
+          <td class="query">
+            <%= link_to query.name, query %>
+            <% if query.created_at > 2.days.ago %>
+              <small style="font-weight: bold; color: #5cb85c;">NEW</small>
+            <% end %>
+          </td>
+          <td class="creator text-right text-muted">
+            <% if (creator = query.creator) and creator.respond_to?(:name) %>
+              <% name = creator.name %>
+              <% if respond_to?(:current_user) and creator == current_user %>
+                you
+                <div class="hide">me <%= name %></div>
+              <% else %>
+                <%= name %>
+              <% end %>
+            <% end %>
+          </td>
+        </tr>
+      <% end %>
+    </tbody>
+  </table>
+</div>
+
+<script>
+  var options = {
+    valueNames: ['query', 'creator']
+  };
+  var userList = new List('queries', options);
+</script>

+ 1 - 0
app/views/blazer/queries/new.html.erb

@@ -0,0 +1 @@
+<%= render partial: "form" %>

+ 50 - 0
app/views/blazer/queries/run.html.erb

@@ -0,0 +1,50 @@
+<% if @error %>
+  <div class="alert alert-danger"><%= @error %></div>
+<% elsif !@success %>
+  <div class="alert alert-info">Can’t run queries with variables yet</div>
+<% else %>
+  <p class="text-muted"><%= pluralize(@rows.size, "row") %></p>
+  <% if @rows.any? %>
+    <% header_width = 100 / @rows.first.keys.size.to_f %>
+    <table class="table">
+      <thead>
+        <tr>
+          <% @columns.each do |key, type| %>
+            <th style="width: <%= header_width %>%;" data-sort="<%= type %>">
+              <div style="min-width: <%= @min_width_types.include?(key) ? 180 : 60 %>px;">
+                <%= key %>
+              </div>
+            </th>
+          <% end %>
+        </tr>
+      </thead>
+      <tbody>
+        <% @rows.each do |row| %>
+          <tr>
+            <% row.each do |k, v| %>
+              <td>
+                <% if v.is_a?(Time) %>
+                  <% v = v.in_time_zone(Blazer.time_zone) %>
+                <% end %>
+
+                <% unless v.nil? %>
+                  <% if v == "" %>
+                    <div class="text-muted">empty string</div>
+                  <% elsif @linked_columns[k] %>
+                    <%= link_to format_value(k, v), @linked_columns[k].gsub("{value}", u(v.to_s)), target: "_blank" %>
+                  <% else %>
+                    <%= format_value(k, v) %>
+                  <% end %>
+
+                  <% if v2 = (@boom[k] || {})[v] %>
+                    <div class="text-muted"><%= v2 %></div>
+                  <% end %>
+                <% end %>
+              </td>
+            <% end %>
+          </tr>
+        <% end %>
+      </tbody>
+    </table>
+  <% end %>
+<% end %>

+ 138 - 0
app/views/blazer/queries/show.html.erb

@@ -0,0 +1,138 @@
+<% title @query.name %>
+
+<div style="position: fixed; top: 0; left: 0; right: 0; background-color: whitesmoke; height: 60px; z-index: 1001;">
+  <div class="container">
+    <div class="row" style="padding-top: 13px;">
+      <div class="col-sm-9">
+        <h3 style="margin: 0; line-height: 34px;">
+          <%= @query.name %>
+        </h3>
+      </div>
+      <div class="col-sm-3 text-right">
+        <%= link_to "Home", root_path, class: "btn btn-primary" %>
+        <%= link_to "Edit", edit_query_path(@query), class: "btn btn-default" %>
+
+        <% if !@error and @success %>
+          <%= button_to "Download", run_queries_path(statement: @query.statement, query_id: @query.id, format: "csv"), class: "btn btn-info" %>
+        <% end %>
+      </div>
+    </div>
+  </div>
+</div>
+
+<div style="margin-bottom: 60px;"></div>
+
+<% if @sql_errors.any? %>
+  <div class="alert alert-danger">
+    <ul>
+      <% @sql_errors.each do |message| %>
+        <li><%= message %></li>
+      <% end %>
+    </ul>
+  </div>
+<% end %>
+
+<% if @query.description.present? %>
+  <p><%= @query.description %></p>
+<% end %>
+
+<% if @bind_vars.any? %>
+  <form id="bind" method="get" action="<%= url_for(params) %>" class="form-inline" style="margin-bottom: 10px;">
+    <% date_vars = ["start_time", "end_time"] %>
+    <% if (date_vars - @bind_vars).empty? %>
+      <% @bind_vars = @bind_vars - date_vars %>
+    <% else %>
+      <% date_vars = nil %>
+    <% end %>
+
+    <% @bind_vars.each_with_index do |var, i| %>
+      <%= label_tag var, var %>
+      <% if (data = @smart_vars[var]) %>
+        <%= select_tag var, options_for_select([[nil, nil]] + data, selected: params[var]), style: "margin-right: 20px; width: 200px;" %>
+        <script>
+          $("#<%= var %>").selectize({
+            create: true
+          });
+        </script>
+      <% else %>
+        <%= text_field_tag var, params[var], style: "width: 120px; margin-right: 20px;", autofocus: i == 0 && !var.end_with?("_at") && !params[var], class: "form-control" %>
+        <% if var.end_with?("_at") %>
+          <script>
+            $("#<%= var %>").datepicker({format: "yyyy-mm-dd", autoclose: true, todayBtn: "linked"})
+          </script>
+        <% end %>
+      <% end %>
+    <% end %>
+
+    <% if date_vars %>
+      <% date_vars.each do |var| %>
+        <%= hidden_field_tag var, params[var] %>
+      <% end %>
+
+      <%= label_tag nil, date_vars.join(" & ") %>
+      <div class="selectize-control single" style="width: 300px;">
+        <div id="reportrange" class="selectize-input" style="display: inline-block;">
+          <span>Select a time range</span>
+        </div>
+      </div>
+
+      <script>
+        $('#reportrange').daterangepicker(
+          {
+            ranges: {
+             'Today': [moment(), moment()],
+             'Last 7 Days': [moment().subtract('days', 6), moment()],
+             'Last 30 Days': [moment().subtract('days', 29), moment()]
+            },
+            startDate: moment().subtract('days', 29),
+            endDate: moment(),
+            opens: "left"
+          },
+          function(start, end) {
+            $("#start_time").val(start.toISOString());
+            $("#end_time").val(end.endOf("day").toISOString());
+            $("#start_time").closest("form").submit();
+          }
+        ).on('apply.daterangepicker', function(ev, picker) {
+          $("#start_time").val(picker.startDate.toISOString());
+          $("#end_time").val(picker.endDate.endOf("day").toISOString());
+          $('#reportrange span').html(picker.startDate.format('MMMM D, YYYY') + ' - ' + picker.endDate.format('MMMM D, YYYY'));
+        })
+
+        if ($("#start_time").val().length > 0) {
+          var picker = $("#reportrange").data('daterangepicker');
+          picker.setStartDate(moment($("#start_time").val()));
+          picker.setEndDate(moment($("#end_time").val()));
+          $("#reportrange").trigger('apply.daterangepicker', picker)
+        }
+      </script>
+    <% end %>
+
+    <input type="submit" class="btn btn-success" value="Run" style="vertical-align: top;" />
+  </form>
+<% end %>
+
+<pre><code><%= @query.statement %></code></pre>
+
+<% if @success %>
+  <div id="results">
+    <p class="text-muted">Loading...</p>
+  </div>
+
+  <script>
+    $.post("<%= run_queries_path %>", <%= json_escape({statement: @query.statement, query_id: @query.id}.to_json).html_safe %>, function (data) {
+      $("#results").html(data);
+      $("#results table").stupidtable().stickyTableHeaders({fixedOffset: 60});
+    });
+
+    $(".form-inline input, .form-inline select").change( function () {
+      if ($(this).val() != "") {
+        $(this).closest("form").submit();
+      }
+    });
+  </script>
+<% end %>
+
+<script>
+  hljs.initHighlightingOnLoad();
+</script>

+ 17 - 0
app/views/layouts/blazer/application.html.erb

@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title><%= title ? title : "Blazer" %></title>
+
+    <meta charset="utf-8" />
+
+    <%= stylesheet_link_tag "blazer/application" %>
+    <%= javascript_include_tag "blazer/application" %>
+    <%= csrf_meta_tags %>
+  </head>
+  <body>
+    <div class="container">
+      <%= yield %>
+    </div>
+  </body>
+</html>

+ 25 - 0
blazer.gemspec

@@ -0,0 +1,25 @@
+# coding: utf-8
+lib = File.expand_path('../lib', __FILE__)
+$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
+require 'blazer/version'
+
+Gem::Specification.new do |spec|
+  spec.name          = "blazer"
+  spec.version       = Blazer::VERSION
+  spec.authors       = ["Andrew Kane"]
+  spec.email         = ["andrew@chartkick.com"]
+  spec.summary       = %q{Create and share SQL queries instantly}
+  spec.description   = %q{Create and share SQL queries instantly}
+  spec.homepage      = "https://github.com/ankane/blazer"
+  spec.license       = "MIT"
+
+  spec.files         = `git ls-files -z`.split("\x0")
+  spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
+  spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
+  spec.require_paths = ["lib"]
+
+  spec.add_dependency "rails"
+
+  spec.add_development_dependency "bundler", "~> 1.7"
+  spec.add_development_dependency "rake", "~> 10.0"
+end

+ 6 - 0
config/routes.rb

@@ -0,0 +1,6 @@
+Blazer::Engine.routes.draw do
+  resources :queries, except: [:index] do
+    post :run, on: :collection # err on the side of caution
+  end
+  root to: "queries#index"
+end

+ 16 - 0
lib/blazer.rb

@@ -0,0 +1,16 @@
+require "csv"
+require "blazer/version"
+require "blazer/engine"
+
+module Blazer
+  class << self
+    attr_accessor :audit
+    attr_reader :time_zone
+  end
+  self.audit = true
+
+  def self.time_zone=(time_zone)
+    @time_zone = time_zone.is_a?(ActiveSupport::TimeZone) ? time_zone : ActiveSupport::TimeZone[time_zone.to_s]
+  end
+  self.time_zone = Time.zone
+end

+ 11 - 0
lib/blazer/engine.rb

@@ -0,0 +1,11 @@
+module Blazer
+  class Engine < ::Rails::Engine
+    isolate_namespace Blazer
+
+    initializer "precompile" do |app|
+      # use a proc instead of a string
+      app.config.assets.precompile << Proc.new{|path| path =~ /\Ablazer\/application\.(js|css)\z/ }
+    end
+
+  end
+end

+ 3 - 0
lib/blazer/version.rb

@@ -0,0 +1,3 @@
+module Blazer
+  VERSION = "0.0.1"
+end

+ 33 - 0
lib/generators/blazer/install_generator.rb

@@ -0,0 +1,33 @@
+# taken from https://github.com/collectiveidea/audited/blob/master/lib/generators/audited/install_generator.rb
+require "rails/generators"
+require "rails/generators/migration"
+require "active_record"
+require "rails/generators/active_record"
+
+module Blazer
+  module Generators
+    class InstallGenerator < Rails::Generators::Base
+      include Rails::Generators::Migration
+
+      source_root File.expand_path("../templates", __FILE__)
+
+      # Implement the required interface for Rails::Generators::Migration.
+      def self.next_migration_number(dirname) #:nodoc:
+        next_migration_number = current_migration_number(dirname) + 1
+        if ActiveRecord::Base.timestamped_migrations
+          [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max
+        else
+          "%.3d" % next_migration_number
+        end
+      end
+
+      def copy_migration
+        migration_template "install.rb", "db/migrate/install_blazer.rb"
+      end
+
+      def copy_config
+        template "config.yml", "config/blazer.yml"
+      end
+    end
+  end
+end

+ 8 - 0
lib/generators/blazer/templates/config.yml

@@ -0,0 +1,8 @@
+linked_columns:
+  # user_id: "/admin/users/{value}"
+
+smart_columns:
+  # user_id: "SELECT id, name FROM users WHERE id IN {value}"
+
+smart_variables:
+  # zone_id: "SELECT id, name FROM zones ORDER BY name ASC"

+ 17 - 0
lib/generators/blazer/templates/install.rb

@@ -0,0 +1,17 @@
+class <%= migration_class_name %> < ActiveRecord::Migration
+  def change
+    create_table :blazer_queries do |t|
+      t.references :creator
+      t.string :name
+      t.text :description
+      t.text :statement
+      t.timestamps
+    end
+
+    create_table :blazer_audits do |t|
+      t.references :user
+      t.text :statement
+      t.timestamp :created_at
+    end
+  end
+end

Some files were not shown because too many files changed in this diff