1
0
Fork 0
mirror of https://github.com/mperham/sidekiq.git synced 2022-11-09 13:52:34 -05:00
This commit is contained in:
Niels Kristian 2012-09-07 14:48:55 +02:00
commit 07930b8a2b
45 changed files with 6765 additions and 3607 deletions

View file

@ -1,8 +1,20 @@
HEAD
2.3.0
-----------
- Upgrade Celluloid to 0.12
- Upgrade Twitter Bootstrap to 2.1.0
- Rescue more Exceptions
- Change Job ID to be Hex, rather than Base64, for HTTP safety
- Use `Airbrake#notify_or_ignore`
2.2.1
-----------
- Add support for custom tabs to Sidekiq::Web [#346]
- Change capistrano recipe to run 'quiet' before deploy:update\_code so
it is run upon both 'deploy' and 'deploy:migrations'. [#352]
- Rescue Exception rather than StandardError to catch and log any sort
of Processor death.
2.2.0
-----------

View file

@ -1,9 +1,8 @@
source 'http://rubygems.org'
gemspec
gem 'celluloid'
gem 'celluloid', "~> 0.12.0"
gem 'slim'
gem 'sprockets'
gem 'sass'
gem 'rails', '3.2.8'
gem 'sqlite3'

View file

@ -7,8 +7,8 @@ Sidekiq
Simple, efficient message processing for Ruby.
Sidekiq uses threads to handle many messages at the same time in the
same process. It integrates tightly with Rails 3 to make background
message processing dead simple.
same process. It does not require Rails but will integrate tightly with
Rails 3 to make background message processing dead simple.
Sidekiq is compatible with Resque. It uses the exact same
message format as Resque so it can integrate into an existing Resque processing farm.

View file

@ -19,8 +19,8 @@ class SinatraWorker
end
get '/' do
@failed = $redis.get('stat:failed')
@processed = $redis.get('stat:processed')
@failed = Sidekiq::Stats.failed
@processed = Sidekiq::Stats.processed
@messages = $redis.lrange('sinkiq-example-messages', 0, -1)
erb :index
end

View file

@ -4,6 +4,7 @@ require 'sidekiq/client'
require 'sidekiq/worker'
require 'sidekiq/redis_connection'
require 'sidekiq/util'
require 'sidekiq/stats'
require 'sidekiq/extensions/class_methods'
require 'sidekiq/extensions/action_mailer'
@ -13,6 +14,7 @@ require 'sidekiq/rails' if defined?(::Rails::Engine)
require 'multi_json'
module Sidekiq
LICENSE = 'See LICENSE and the LGPL-3.0 for licensing details.'
DEFAULTS = {
:queues => [],

View file

@ -5,24 +5,25 @@ Capistrano::Configuration.instance.load do
after "deploy:restart", "sidekiq:restart"
_cset(:sidekiq_timeout) { 10 }
_cset(:sidekiq_role) { :app }
_cset(:sidekiq_role) { :app }
_cset(:sidekiq_pid) { "#{current_path}/tmp/pids/sidekiq.pid" }
namespace :sidekiq do
desc "Quiet sidekiq (stop accepting new work)"
task :quiet, :roles => lambda { fetch(:sidekiq_role) }, :on_no_matching_servers => :continue do
run "if [ -d #{current_path} ] && [ -f #{current_path}/tmp/pids/sidekiq.pid ]; then cd #{current_path} && #{fetch(:bundle_cmd, "bundle")} exec sidekiqctl quiet #{current_path}/tmp/pids/sidekiq.pid ; fi"
run "if [ -d #{current_path} ] && [ -f #{fetch :sidekiq_pid} ]; then cd #{current_path} && #{fetch(:bundle_cmd, "bundle")} exec sidekiqctl quiet #{fetch :sidekiq_pid} ; fi"
end
desc "Stop sidekiq"
task :stop, :roles => lambda { fetch(:sidekiq_role) }, :on_no_matching_servers => :continue do
run "if [ -d #{current_path} ] && [ -f #{current_path}/tmp/pids/sidekiq.pid ]; then cd #{current_path} && #{fetch(:bundle_cmd, "bundle")} exec sidekiqctl stop #{current_path}/tmp/pids/sidekiq.pid #{fetch :sidekiq_timeout} ; fi"
run "if [ -d #{current_path} ] && [ -f #{fetch :sidekiq_pid} ]; then cd #{current_path} && #{fetch(:bundle_cmd, "bundle")} exec sidekiqctl stop #{fetch :sidekiq_pid} #{fetch :sidekiq_timeout} ; fi"
end
desc "Start sidekiq"
task :start, :roles => lambda { fetch(:sidekiq_role) }, :on_no_matching_servers => :continue do
rails_env = fetch(:rails_env, "production")
run "cd #{current_path} ; nohup #{fetch(:bundle_cmd, "bundle")} exec sidekiq -e #{rails_env} -C #{current_path}/config/sidekiq.yml -P #{current_path}/tmp/pids/sidekiq.pid >> #{current_path}/log/sidekiq.log 2>&1 &", :pty => false
run "cd #{current_path} ; nohup #{fetch(:bundle_cmd, "bundle")} exec sidekiq -e #{rails_env} -C #{current_path}/config/sidekiq.yml -P #{fetch :sidekiq_pid} >> #{current_path}/log/sidekiq.log 2>&1 &", :pty => false
end
desc "Restart sidekiq"

View file

@ -66,6 +66,10 @@ module Sidekiq
end
def run
logger.info "Booting Sidekiq #{Sidekiq::VERSION} with Redis at #{redis {|x| x.client.id}}"
logger.info "Running in #{RUBY_DESCRIPTION}"
logger.info Sidekiq::LICENSE
@manager = Sidekiq::Manager.new(options)
poller = Sidekiq::Scheduled::Poller.new
begin

View file

@ -1,3 +1,5 @@
require 'securerandom'
require 'sidekiq/middleware/chain'
module Sidekiq
@ -44,7 +46,7 @@ module Sidekiq
item = worker_class.get_sidekiq_options.merge(item)
item['retry'] = !!item['retry']
queue = item['queue']
item['jid'] = SecureRandom.base64
item['jid'] = SecureRandom.hex(12)
pushed = false
Sidekiq.client_middleware.invoke(worker_class, item, queue) do

View file

@ -13,7 +13,7 @@ module Sidekiq
private
def send_to_airbrake(msg, ex)
::Airbrake.notify(ex, :parameters => msg)
::Airbrake.notify_or_ignore(ex, :parameters => msg)
end
def send_to_exceptional(msg, ex)

View file

@ -18,8 +18,6 @@ module Sidekiq
trap_exit :processor_died
def initialize(options={})
logger.info "Booting sidekiq #{Sidekiq::VERSION} with Redis at #{redis {|x| x.client.id}}"
logger.info "Running in #{RUBY_DESCRIPTION}"
logger.debug { options.inspect }
@count = options[:concurrency] || 25
@done_callback = nil
@ -129,6 +127,7 @@ module Sidekiq
# processor is an actor proxy and we can't call any methods
# that would go to the actor (since it's busy). Instead
# we'll use the object_id to track the worker's data here.
processor.terminate if processor.alive?
msg, queue = @in_progress[processor.object_id]
conn.lpush("queue:#{queue}", msg)
end

View file

@ -10,7 +10,7 @@ module Sidekiq
logger.info { "start" }
yield
logger.info { "done: #{elapsed(start)} sec" }
rescue
rescue Exception
logger.info { "fail: #{elapsed(start)} sec" }
raise
end

View file

@ -40,8 +40,8 @@ module Sidekiq
def call(worker, msg, queue)
yield
rescue => e
raise unless msg['retry']
rescue Exception => e
raise e unless msg['retry']
msg['queue'] = queue
msg['error_message'] = e.message
@ -72,7 +72,7 @@ module Sidekiq
# Goodbye dear message, you (re)tried your best I'm sure.
logger.debug { "Dropping message after hitting the retry maximum: #{msg}" }
end
raise
raise e
end
end

View file

@ -15,6 +15,8 @@ module Sidekiq
include Util
include Celluloid
task_class Celluloid::TaskThread
def self.default_middleware
Middleware::Chain.new do |m|
m.add Middleware::Server::Logging
@ -29,24 +31,19 @@ module Sidekiq
end
def process(msgstr, queue)
# Defer worker execution to Celluloid's thread pool since all actor
# invocations are run within a Fiber, which dramatically limits
# our stack size.
defer do
begin
msg = Sidekiq.load_json(msgstr)
klass = constantize(msg['class'])
worker = klass.new
begin
msg = Sidekiq.load_json(msgstr)
klass = constantize(msg['class'])
worker = klass.new
stats(worker, msg, queue) do
Sidekiq.server_middleware.invoke(worker, msg, queue) do
worker.perform(*cloned(msg['args']))
end
stats(worker, msg, queue) do
Sidekiq.server_middleware.invoke(worker, msg, queue) do
worker.perform(*cloned(msg['args']))
end
rescue => ex
handle_exception(ex, msg || { :message => msgstr })
raise
end
rescue Exception => ex
handle_exception(ex, msg || { :message => msgstr })
raise
end
@boss.processor_done!(current_actor)
end

36
lib/sidekiq/stats.rb Normal file
View file

@ -0,0 +1,36 @@
module Sidekiq
module_function
def info
results = {}
processed, failed, queues = Sidekiq.redis { |conn|
conn.multi do
conn.get('stat:processed')
conn.get('stat:failed')
conn.smembers('queues')
end
}
results[:queues_with_sizes] = Sidekiq.redis do |conn|
queues.inject({}) { |memo, q|
memo[q] = conn.llen("queue:#{q}")
memo
}.sort_by { |_, size| size }
end
results[:processed] = (processed || 0).to_i
results[:failed] = (failed || 0).to_i
results[:backlog] = results[:queues_with_sizes].
map {|_, size| size }.
inject(0) {|memo, val| memo + val }
results
end
def size(*queues)
return info[:backlog] if queues.empty?
Sidekiq.redis { |conn|
conn.multi {
queues.map { |q| conn.llen("queue:#{q}") }
}
}.inject(0) { |memo, count| memo += count }
end
end

View file

@ -22,7 +22,7 @@ module Sidekiq
def watchdog(last_words)
yield
rescue => ex
rescue Exception => ex
handle_exception(ex, { :context => last_words })
end

View file

@ -1,3 +1,3 @@
module Sidekiq
VERSION = "2.2.0"
VERSION = "2.3.0"
end

View file

@ -60,12 +60,16 @@ module Sidekiq
end
end
def info
@info ||= Sidekiq.info
end
def processed
Sidekiq.redis { |conn| conn.get('stat:processed') } || 0
info[:processed]
end
def failed
Sidekiq.redis { |conn| conn.get('stat:failed') } || 0
info[:failed]
end
def zcard(name)
@ -73,15 +77,11 @@ module Sidekiq
end
def queues
@queues ||= Sidekiq.redis do |conn|
conn.smembers('queues').map do |q|
[q, conn.llen("queue:#{q}") || 0]
end.sort { |x,y| x[1] <=> y[1] }
end
@queues ||= Sidekiq.info[:queues_with_sizes]
end
def backlog
queues.map {|name, size| size }.inject(0) {|memo, val| memo + val }
info[:backlog]
end
def retries_with_score(score)
@ -111,6 +111,23 @@ module Sidekiq
def display_args(args, count=100)
args.map { |arg| a = arg.inspect; a.size > count ? "#{a[0..count]}..." : a }.join(", ")
end
def tabs
self.class.tabs
end
def number_with_delimiter(number)
begin
Float(number)
rescue ArgumentError, TypeError
return number
end
options = {:delimiter => ',', :separator => '.'}
parts = number.to_s.to_str.split('.')
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
parts.join(options[:separator])
end
end
get "/" do
@ -223,6 +240,10 @@ module Sidekiq
end
end
def self.tabs
@tabs ||= ["Queues", "Retries", "Scheduled"]
end
end
end

View file

@ -17,7 +17,7 @@ Gem::Specification.new do |gem|
gem.add_dependency 'redis', '~> 3'
gem.add_dependency 'redis-namespace'
gem.add_dependency 'connection_pool', '~> 0.9.2'
gem.add_dependency 'celluloid', '~> 0.11.1'
gem.add_dependency 'celluloid', '~> 0.12.0'
gem.add_dependency 'multi_json', '~> 1'
gem.add_development_dependency 'minitest', '~> 3'
gem.add_development_dependency 'sinatra'
@ -25,4 +25,5 @@ Gem::Specification.new do |gem|
gem.add_development_dependency 'rake'
gem.add_development_dependency 'actionmailer', '~> 3'
gem.add_development_dependency 'activerecord', '~> 3'
gem.add_development_dependency 'pry'
end

View file

@ -4,6 +4,8 @@ if ENV.has_key?("SIMPLECOV")
SimpleCov.start
end
require 'pry'
require 'minitest/unit'
require 'minitest/pride'
require 'minitest/autorun'

View file

@ -49,7 +49,7 @@ class TestExceptionHandler < MiniTest::Unit::TestCase
end
it "notifies Airbrake" do
::Airbrake.expect(:notify,nil,[TEST_EXCEPTION,:parameters => { :a => 1 }])
::Airbrake.expect(:notify_or_ignore,nil,[TEST_EXCEPTION,:parameters => { :a => 1 }])
Component.new.invoke_exception(:a => 1)
::Airbrake.verify
end

View file

@ -11,6 +11,7 @@ class TestStats < MiniTest::Unit::TestCase
class DumbWorker
include Sidekiq::Worker
sidekiq_options :queue => 'dumbq'
def perform(arg)
raise 'bang' if arg == nil
@ -31,15 +32,15 @@ class TestStats < MiniTest::Unit::TestCase
boss.expect(:processor_done!, nil, [processor])
boss.expect(:processor_done!, nil, [processor])
assert_equal 0, conn.get('stat:failed').to_i
assert_equal 0, conn.get('stat:processed').to_i
assert_equal 0, Sidekiq.info[:failed]
assert_equal 0, Sidekiq.info[:processed]
processor.process(msg, 'xyzzy')
processor.process(msg, 'xyzzy')
processor.process(msg, 'xyzzy')
assert_equal 0, conn.get('stat:failed').to_i
assert_equal 3, conn.get('stat:processed').to_i
assert_equal 0, Sidekiq.info[:failed]
assert_equal 3, Sidekiq.info[:processed]
end
end
@ -49,8 +50,8 @@ class TestStats < MiniTest::Unit::TestCase
@redis.with do |conn|
assert_equal [], conn.smembers('workers')
assert_equal 0, conn.get('stat:failed').to_i
assert_equal 0, conn.get('stat:processed').to_i
assert_equal 0, Sidekiq.info[:failed]
assert_equal 0, Sidekiq.info[:processed]
processor = Sidekiq::Processor.new(boss)
@ -59,8 +60,46 @@ class TestStats < MiniTest::Unit::TestCase
processor.process(msg, 'xyzzy')
end
assert_equal 1, conn.get('stat:failed').to_i
assert_equal 1, conn.get('stat:processed').to_i
assert_equal 1, Sidekiq.info[:failed]
assert_equal 1, Sidekiq.info[:processed]
end
end
describe "info counts" do
before do
@redis.with do |conn|
conn.rpush 'queue:foo', '{}'
conn.sadd 'queues', 'foo'
conn.rpush 'queue:bar', '{}'
conn.rpush 'queue:bar', '{}'
conn.sadd 'queues', 'bar'
conn.rpush 'queue:baz', '{}'
conn.sadd 'queues', 'baz'
end
end
describe "queues_with_sizes" do
it "returns queue names and corresponding job counts" do
assert_equal [["foo", 1], ["baz", 1], ["bar", 2]], Sidekiq.info[:queues_with_sizes]
end
end
describe "backlog" do
it "returns count of all jobs yet to be processed" do
assert_equal 4, Sidekiq.info[:backlog]
end
end
describe "size" do
it "returns size of queues" do
assert_equal 0, Sidekiq.size("foox")
assert_equal 1, Sidekiq.size(:foo)
assert_equal 1, Sidekiq.size("foo")
assert_equal 3, Sidekiq.size("foo", "bar")
assert_equal 4, Sidekiq.size
end
end
end

View file

@ -148,6 +148,15 @@ class TestWeb < MiniTest::Unit::TestCase
assert_match /#{msg['args'][2]}/, last_response.body
end
it 'can show user defined tab' do
Sidekiq::Web.tabs << 'Custom Tab'
get '/'
assert_match 'Custom Tab', last_response.body
Sidekiq::Web.tabs.delete 'Custom Tab'
end
def add_scheduled
msg = { 'class' => 'HardWorker',
'args' => ['bob', 1, Time.now.to_f],

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -39,6 +39,7 @@ $(function() {
var responseHtml = $(data);
$('.hero-unit').replaceWith(responseHtml.find('.hero-unit'));
$('.workers').replaceWith(responseHtml.find('.workers'));
$('time').timeago();
});
var currentTime = new Date();
$('.poll-status').text('Last polled at: ' + currentTime.getHours() + ':' + pad(currentTime.getMinutes()) + ':' + pad(currentTime.getSeconds()));
@ -49,3 +50,9 @@ $(function() {
pollLink.data('polling', !pollLink.data('polling'));
})
});
$(function() {
$('[data-confirm]').click(function() {
return confirm($(this).attr('data-confirm'));
});
});

File diff suppressed because it is too large Load diff

View file

@ -1,91 +0,0 @@
/* ==========================================================
* bootstrap-alert.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#alerts
* ==========================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ========================================================== */
!function( $ ){
"use strict"
/* ALERT CLASS DEFINITION
* ====================== */
var dismiss = '[data-dismiss="alert"]'
, Alert = function ( el ) {
$(el).on('click', dismiss, this.close)
}
Alert.prototype = {
constructor: Alert
, close: function ( e ) {
var $this = $(this)
, selector = $this.attr('data-target')
, $parent
if (!selector) {
selector = $this.attr('href')
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
}
$parent = $(selector)
$parent.trigger('close')
e && e.preventDefault()
$parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent())
$parent.removeClass('in')
function removeElement() {
$parent.remove()
$parent.trigger('closed')
}
$.support.transition && $parent.hasClass('fade') ?
$parent.on($.support.transition.end, removeElement) :
removeElement()
}
}
/* ALERT PLUGIN DEFINITION
* ======================= */
$.fn.alert = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('alert')
if (!data) $this.data('alert', (data = new Alert(this)))
if (typeof option == 'string') data[option].call($this)
})
}
$.fn.alert.Constructor = Alert
/* ALERT DATA-API
* ============== */
$(function () {
$('body').on('click.alert.data-api', dismiss, Alert.prototype.close)
})
}( window.jQuery )

View file

@ -1,98 +0,0 @@
/* ============================================================
* bootstrap-button.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#buttons
* ============================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ============================================================ */
!function( $ ){
"use strict"
/* BUTTON PUBLIC CLASS DEFINITION
* ============================== */
var Button = function ( element, options ) {
this.$element = $(element)
this.options = $.extend({}, $.fn.button.defaults, options)
}
Button.prototype = {
constructor: Button
, setState: function ( state ) {
var d = 'disabled'
, $el = this.$element
, data = $el.data()
, val = $el.is('input') ? 'val' : 'html'
state = state + 'Text'
data.resetText || $el.data('resetText', $el[val]())
$el[val](data[state] || this.options[state])
// push to event loop to allow forms to submit
setTimeout(function () {
state == 'loadingText' ?
$el.addClass(d).attr(d, d) :
$el.removeClass(d).removeAttr(d)
}, 0)
}
, toggle: function () {
var $parent = this.$element.parent('[data-toggle="buttons-radio"]')
$parent && $parent
.find('.active')
.removeClass('active')
this.$element.toggleClass('active')
}
}
/* BUTTON PLUGIN DEFINITION
* ======================== */
$.fn.button = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('button')
, options = typeof option == 'object' && option
if (!data) $this.data('button', (data = new Button(this, options)))
if (option == 'toggle') data.toggle()
else if (option) data.setState(option)
})
}
$.fn.button.defaults = {
loadingText: 'loading...'
}
$.fn.button.Constructor = Button
/* BUTTON DATA-API
* =============== */
$(function () {
$('body').on('click.button.data-api', '[data-toggle^=button]', function ( e ) {
$(e.target).button('toggle')
})
})
}( window.jQuery )

View file

@ -1,154 +0,0 @@
/* ==========================================================
* bootstrap-carousel.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#carousel
* ==========================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ========================================================== */
!function( $ ){
"use strict"
/* CAROUSEL CLASS DEFINITION
* ========================= */
var Carousel = function (element, options) {
this.$element = $(element)
this.options = $.extend({}, $.fn.carousel.defaults, options)
this.options.slide && this.slide(this.options.slide)
}
Carousel.prototype = {
cycle: function () {
this.interval = setInterval($.proxy(this.next, this), this.options.interval)
return this
}
, to: function (pos) {
var $active = this.$element.find('.active')
, children = $active.parent().children()
, activePos = children.index($active)
, that = this
if (pos > (children.length - 1) || pos < 0) return
if (this.sliding) {
return this.$element.one('slid', function () {
that.to(pos)
})
}
if (activePos == pos) {
return this.pause().cycle()
}
return this.slide(pos > activePos ? 'next' : 'prev', $(children[pos]))
}
, pause: function () {
clearInterval(this.interval)
return this
}
, next: function () {
if (this.sliding) return
return this.slide('next')
}
, prev: function () {
if (this.sliding) return
return this.slide('prev')
}
, slide: function (type, next) {
var $active = this.$element.find('.active')
, $next = next || $active[type]()
, isCycling = this.interval
, direction = type == 'next' ? 'left' : 'right'
, fallback = type == 'next' ? 'first' : 'last'
, that = this
this.sliding = true
isCycling && this.pause()
$next = $next.length ? $next : this.$element.find('.item')[fallback]()
if (!$.support.transition && this.$element.hasClass('slide')) {
this.$element.trigger('slide')
$active.removeClass('active')
$next.addClass('active')
this.sliding = false
this.$element.trigger('slid')
} else {
$next.addClass(type)
$next[0].offsetWidth // force reflow
$active.addClass(direction)
$next.addClass(direction)
this.$element.trigger('slide')
this.$element.one($.support.transition.end, function () {
$next.removeClass([type, direction].join(' ')).addClass('active')
$active.removeClass(['active', direction].join(' '))
that.sliding = false
setTimeout(function () { that.$element.trigger('slid') }, 0)
})
}
isCycling && this.cycle()
return this
}
}
/* CAROUSEL PLUGIN DEFINITION
* ========================== */
$.fn.carousel = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('carousel')
, options = typeof option == 'object' && option
if (!data) $this.data('carousel', (data = new Carousel(this, options)))
if (typeof option == 'number') data.to(option)
else if (typeof option == 'string' || (option = options.slide)) data[option]()
else data.cycle()
})
}
$.fn.carousel.defaults = {
interval: 5000
}
$.fn.carousel.Constructor = Carousel
/* CAROUSEL DATA-API
* ================= */
$(function () {
$('body').on('click.carousel.data-api', '[data-slide]', function ( e ) {
var $this = $(this), href
, $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
, options = !$target.data('modal') && $.extend({}, $target.data(), $this.data())
$target.carousel(options)
e.preventDefault()
})
})
}( window.jQuery )

View file

@ -1,136 +0,0 @@
/* =============================================================
* bootstrap-collapse.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#collapse
* =============================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ============================================================ */
!function( $ ){
"use strict"
var Collapse = function ( element, options ) {
this.$element = $(element)
this.options = $.extend({}, $.fn.collapse.defaults, options)
if (this.options["parent"]) {
this.$parent = $(this.options["parent"])
}
this.options.toggle && this.toggle()
}
Collapse.prototype = {
constructor: Collapse
, dimension: function () {
var hasWidth = this.$element.hasClass('width')
return hasWidth ? 'width' : 'height'
}
, show: function () {
var dimension = this.dimension()
, scroll = $.camelCase(['scroll', dimension].join('-'))
, actives = this.$parent && this.$parent.find('.in')
, hasData
if (actives && actives.length) {
hasData = actives.data('collapse')
actives.collapse('hide')
hasData || actives.data('collapse', null)
}
this.$element[dimension](0)
this.transition('addClass', 'show', 'shown')
this.$element[dimension](this.$element[0][scroll])
}
, hide: function () {
var dimension = this.dimension()
this.reset(this.$element[dimension]())
this.transition('removeClass', 'hide', 'hidden')
this.$element[dimension](0)
}
, reset: function ( size ) {
var dimension = this.dimension()
this.$element
.removeClass('collapse')
[dimension](size || 'auto')
[0].offsetWidth
this.$element.addClass('collapse')
}
, transition: function ( method, startEvent, completeEvent ) {
var that = this
, complete = function () {
if (startEvent == 'show') that.reset()
that.$element.trigger(completeEvent)
}
this.$element
.trigger(startEvent)
[method]('in')
$.support.transition && this.$element.hasClass('collapse') ?
this.$element.one($.support.transition.end, complete) :
complete()
}
, toggle: function () {
this[this.$element.hasClass('in') ? 'hide' : 'show']()
}
}
/* COLLAPSIBLE PLUGIN DEFINITION
* ============================== */
$.fn.collapse = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('collapse')
, options = typeof option == 'object' && option
if (!data) $this.data('collapse', (data = new Collapse(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.collapse.defaults = {
toggle: true
}
$.fn.collapse.Constructor = Collapse
/* COLLAPSIBLE DATA-API
* ==================== */
$(function () {
$('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) {
var $this = $(this), href
, target = $this.attr('data-target')
|| e.preventDefault()
|| (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
, option = $(target).data('collapse') ? 'toggle' : $this.data()
$(target).collapse(option)
})
})
}( window.jQuery )

View file

@ -1,92 +0,0 @@
/* ============================================================
* bootstrap-dropdown.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#dropdowns
* ============================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ============================================================ */
!function( $ ){
"use strict"
/* DROPDOWN CLASS DEFINITION
* ========================= */
var toggle = '[data-toggle="dropdown"]'
, Dropdown = function ( element ) {
var $el = $(element).on('click.dropdown.data-api', this.toggle)
$('html').on('click.dropdown.data-api', function () {
$el.parent().removeClass('open')
})
}
Dropdown.prototype = {
constructor: Dropdown
, toggle: function ( e ) {
var $this = $(this)
, selector = $this.attr('data-target')
, $parent
, isActive
if (!selector) {
selector = $this.attr('href')
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
}
$parent = $(selector)
$parent.length || ($parent = $this.parent())
isActive = $parent.hasClass('open')
clearMenus()
!isActive && $parent.toggleClass('open')
return false
}
}
function clearMenus() {
$(toggle).parent().removeClass('open')
}
/* DROPDOWN PLUGIN DEFINITION
* ========================== */
$.fn.dropdown = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('dropdown')
if (!data) $this.data('dropdown', (data = new Dropdown(this)))
if (typeof option == 'string') data[option].call($this)
})
}
$.fn.dropdown.Constructor = Dropdown
/* APPLY TO STANDARD DROPDOWN ELEMENTS
* =================================== */
$(function () {
$('html').on('click.dropdown.data-api', clearMenus)
$('body').on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle)
})
}( window.jQuery )

View file

@ -1,210 +0,0 @@
/* =========================================================
* bootstrap-modal.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#modals
* =========================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ========================================================= */
!function( $ ){
"use strict"
/* MODAL CLASS DEFINITION
* ====================== */
var Modal = function ( content, options ) {
this.options = options
this.$element = $(content)
.delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
}
Modal.prototype = {
constructor: Modal
, toggle: function () {
return this[!this.isShown ? 'show' : 'hide']()
}
, show: function () {
var that = this
if (this.isShown) return
$('body').addClass('modal-open')
this.isShown = true
this.$element.trigger('show')
escape.call(this)
backdrop.call(this, function () {
var transition = $.support.transition && that.$element.hasClass('fade')
!that.$element.parent().length && that.$element.appendTo(document.body) //don't move modals dom position
that.$element
.show()
if (transition) {
that.$element[0].offsetWidth // force reflow
}
that.$element.addClass('in')
transition ?
that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) :
that.$element.trigger('shown')
})
}
, hide: function ( e ) {
e && e.preventDefault()
if (!this.isShown) return
var that = this
this.isShown = false
$('body').removeClass('modal-open')
escape.call(this)
this.$element
.trigger('hide')
.removeClass('in')
$.support.transition && this.$element.hasClass('fade') ?
hideWithTransition.call(this) :
hideModal.call(this)
}
}
/* MODAL PRIVATE METHODS
* ===================== */
function hideWithTransition() {
var that = this
, timeout = setTimeout(function () {
that.$element.off($.support.transition.end)
hideModal.call(that)
}, 500)
this.$element.one($.support.transition.end, function () {
clearTimeout(timeout)
hideModal.call(that)
})
}
function hideModal( that ) {
this.$element
.hide()
.trigger('hidden')
backdrop.call(this)
}
function backdrop( callback ) {
var that = this
, animate = this.$element.hasClass('fade') ? 'fade' : ''
if (this.isShown && this.options.backdrop) {
var doAnimate = $.support.transition && animate
this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
.appendTo(document.body)
if (this.options.backdrop != 'static') {
this.$backdrop.click($.proxy(this.hide, this))
}
if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
this.$backdrop.addClass('in')
doAnimate ?
this.$backdrop.one($.support.transition.end, callback) :
callback()
} else if (!this.isShown && this.$backdrop) {
this.$backdrop.removeClass('in')
$.support.transition && this.$element.hasClass('fade')?
this.$backdrop.one($.support.transition.end, $.proxy(removeBackdrop, this)) :
removeBackdrop.call(this)
} else if (callback) {
callback()
}
}
function removeBackdrop() {
this.$backdrop.remove()
this.$backdrop = null
}
function escape() {
var that = this
if (this.isShown && this.options.keyboard) {
$(document).on('keyup.dismiss.modal', function ( e ) {
e.which == 27 && that.hide()
})
} else if (!this.isShown) {
$(document).off('keyup.dismiss.modal')
}
}
/* MODAL PLUGIN DEFINITION
* ======================= */
$.fn.modal = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('modal')
, options = $.extend({}, $.fn.modal.defaults, typeof option == 'object' && option)
if (!data) $this.data('modal', (data = new Modal(this, options)))
if (typeof option == 'string') data[option]()
else if (options.show) data.show()
})
}
$.fn.modal.defaults = {
backdrop: true
, keyboard: true
, show: true
}
$.fn.modal.Constructor = Modal
/* MODAL DATA-API
* ============== */
$(function () {
$('body').on('click.modal.data-api', '[data-toggle="modal"]', function ( e ) {
var $this = $(this), href
, $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
, option = $target.data('modal') ? 'toggle' : $.extend({}, $target.data(), $this.data())
e.preventDefault()
$target.modal(option)
})
})
}( window.jQuery )

View file

@ -1,95 +0,0 @@
/* ===========================================================
* bootstrap-popover.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#popovers
* ===========================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* =========================================================== */
!function( $ ) {
"use strict"
var Popover = function ( element, options ) {
this.init('popover', element, options)
}
/* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js
========================================== */
Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, {
constructor: Popover
, setContent: function () {
var $tip = this.tip()
, title = this.getTitle()
, content = this.getContent()
$tip.find('.popover-title')[ $.type(title) == 'object' ? 'append' : 'html' ](title)
$tip.find('.popover-content > *')[ $.type(content) == 'object' ? 'append' : 'html' ](content)
$tip.removeClass('fade top bottom left right in')
}
, hasContent: function () {
return this.getTitle() || this.getContent()
}
, getContent: function () {
var content
, $e = this.$element
, o = this.options
content = $e.attr('data-content')
|| (typeof o.content == 'function' ? o.content.call($e[0]) : o.content)
content = content.toString().replace(/(^\s*|\s*$)/, "")
return content
}
, tip: function() {
if (!this.$tip) {
this.$tip = $(this.options.template)
}
return this.$tip
}
})
/* POPOVER PLUGIN DEFINITION
* ======================= */
$.fn.popover = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('popover')
, options = typeof option == 'object' && option
if (!data) $this.data('popover', (data = new Popover(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.popover.Constructor = Popover
$.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, {
placement: 'right'
, content: ''
, template: '<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'
})
}( window.jQuery )

View file

@ -1,125 +0,0 @@
/* =============================================================
* bootstrap-scrollspy.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#scrollspy
* =============================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ============================================================== */
!function ( $ ) {
"use strict"
/* SCROLLSPY CLASS DEFINITION
* ========================== */
function ScrollSpy( element, options) {
var process = $.proxy(this.process, this)
, $element = $(element).is('body') ? $(window) : $(element)
, href
this.options = $.extend({}, $.fn.scrollspy.defaults, options)
this.$scrollElement = $element.on('scroll.scroll.data-api', process)
this.selector = (this.options.target
|| ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
|| '') + ' .nav li > a'
this.$body = $('body').on('click.scroll.data-api', this.selector, process)
this.refresh()
this.process()
}
ScrollSpy.prototype = {
constructor: ScrollSpy
, refresh: function () {
this.targets = this.$body
.find(this.selector)
.map(function () {
var href = $(this).attr('href')
return /^#\w/.test(href) && $(href).length ? href : null
})
this.offsets = $.map(this.targets, function (id) {
return $(id).position().top
})
}
, process: function () {
var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
, offsets = this.offsets
, targets = this.targets
, activeTarget = this.activeTarget
, i
for (i = offsets.length; i--;) {
activeTarget != targets[i]
&& scrollTop >= offsets[i]
&& (!offsets[i + 1] || scrollTop <= offsets[i + 1])
&& this.activate( targets[i] )
}
}
, activate: function (target) {
var active
this.activeTarget = target
this.$body
.find(this.selector).parent('.active')
.removeClass('active')
active = this.$body
.find(this.selector + '[href="' + target + '"]')
.parent('li')
.addClass('active')
if ( active.parent('.dropdown-menu') ) {
active.closest('li.dropdown').addClass('active')
}
}
}
/* SCROLLSPY PLUGIN DEFINITION
* =========================== */
$.fn.scrollspy = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('scrollspy')
, options = typeof option == 'object' && option
if (!data) $this.data('scrollspy', (data = new ScrollSpy(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.scrollspy.Constructor = ScrollSpy
$.fn.scrollspy.defaults = {
offset: 10
}
/* SCROLLSPY DATA-API
* ================== */
$(function () {
$('[data-spy="scroll"]').each(function () {
var $spy = $(this)
$spy.scrollspy($spy.data())
})
})
}( window.jQuery )

View file

@ -1,130 +0,0 @@
/* ========================================================
* bootstrap-tab.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#tabs
* ========================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ======================================================== */
!function( $ ){
"use strict"
/* TAB CLASS DEFINITION
* ==================== */
var Tab = function ( element ) {
this.element = $(element)
}
Tab.prototype = {
constructor: Tab
, show: function () {
var $this = this.element
, $ul = $this.closest('ul:not(.dropdown-menu)')
, selector = $this.attr('data-target')
, previous
, $target
if (!selector) {
selector = $this.attr('href')
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
}
if ( $this.parent('li').hasClass('active') ) return
previous = $ul.find('.active a').last()[0]
$this.trigger({
type: 'show'
, relatedTarget: previous
})
$target = $(selector)
this.activate($this.parent('li'), $ul)
this.activate($target, $target.parent(), function () {
$this.trigger({
type: 'shown'
, relatedTarget: previous
})
})
}
, activate: function ( element, container, callback) {
var $active = container.find('> .active')
, transition = callback
&& $.support.transition
&& $active.hasClass('fade')
function next() {
$active
.removeClass('active')
.find('> .dropdown-menu > .active')
.removeClass('active')
element.addClass('active')
if (transition) {
element[0].offsetWidth // reflow for transition
element.addClass('in')
} else {
element.removeClass('fade')
}
if ( element.parent('.dropdown-menu') ) {
element.closest('li.dropdown').addClass('active')
}
callback && callback()
}
transition ?
$active.one($.support.transition.end, next) :
next()
$active.removeClass('in')
}
}
/* TAB PLUGIN DEFINITION
* ===================== */
$.fn.tab = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('tab')
if (!data) $this.data('tab', (data = new Tab(this)))
if (typeof option == 'string') data[option]()
})
}
$.fn.tab.Constructor = Tab
/* TAB DATA-API
* ============ */
$(function () {
$('body').on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
e.preventDefault()
$(this).tab('show')
})
})
}( window.jQuery )

View file

@ -1,270 +0,0 @@
/* ===========================================================
* bootstrap-tooltip.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#tooltips
* Inspired by the original jQuery.tipsy by Jason Frame
* ===========================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ========================================================== */
!function( $ ) {
"use strict"
/* TOOLTIP PUBLIC CLASS DEFINITION
* =============================== */
var Tooltip = function ( element, options ) {
this.init('tooltip', element, options)
}
Tooltip.prototype = {
constructor: Tooltip
, init: function ( type, element, options ) {
var eventIn
, eventOut
this.type = type
this.$element = $(element)
this.options = this.getOptions(options)
this.enabled = true
if (this.options.trigger != 'manual') {
eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus'
eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur'
this.$element.on(eventIn, this.options.selector, $.proxy(this.enter, this))
this.$element.on(eventOut, this.options.selector, $.proxy(this.leave, this))
}
this.options.selector ?
(this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
this.fixTitle()
}
, getOptions: function ( options ) {
options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data())
if (options.delay && typeof options.delay == 'number') {
options.delay = {
show: options.delay
, hide: options.delay
}
}
return options
}
, enter: function ( e ) {
var self = $(e.currentTarget)[this.type](this._options).data(this.type)
if (!self.options.delay || !self.options.delay.show) {
self.show()
} else {
self.hoverState = 'in'
setTimeout(function() {
if (self.hoverState == 'in') {
self.show()
}
}, self.options.delay.show)
}
}
, leave: function ( e ) {
var self = $(e.currentTarget)[this.type](this._options).data(this.type)
if (!self.options.delay || !self.options.delay.hide) {
self.hide()
} else {
self.hoverState = 'out'
setTimeout(function() {
if (self.hoverState == 'out') {
self.hide()
}
}, self.options.delay.hide)
}
}
, show: function () {
var $tip
, inside
, pos
, actualWidth
, actualHeight
, placement
, tp
if (this.hasContent() && this.enabled) {
$tip = this.tip()
this.setContent()
if (this.options.animation) {
$tip.addClass('fade')
}
placement = typeof this.options.placement == 'function' ?
this.options.placement.call(this, $tip[0], this.$element[0]) :
this.options.placement
inside = /in/.test(placement)
$tip
.remove()
.css({ top: 0, left: 0, display: 'block' })
.appendTo(inside ? this.$element : document.body)
pos = this.getPosition(inside)
actualWidth = $tip[0].offsetWidth
actualHeight = $tip[0].offsetHeight
switch (inside ? placement.split(' ')[1] : placement) {
case 'bottom':
tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
break
case 'top':
tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
break
case 'left':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
break
case 'right':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
break
}
$tip
.css(tp)
.addClass(placement)
.addClass('in')
}
}
, setContent: function () {
var $tip = this.tip()
$tip.find('.tooltip-inner').html(this.getTitle())
$tip.removeClass('fade in top bottom left right')
}
, hide: function () {
var that = this
, $tip = this.tip()
$tip.removeClass('in')
function removeWithAnimation() {
var timeout = setTimeout(function () {
$tip.off($.support.transition.end).remove()
}, 500)
$tip.one($.support.transition.end, function () {
clearTimeout(timeout)
$tip.remove()
})
}
$.support.transition && this.$tip.hasClass('fade') ?
removeWithAnimation() :
$tip.remove()
}
, fixTitle: function () {
var $e = this.$element
if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
$e.attr('data-original-title', $e.attr('title') || '').removeAttr('title')
}
}
, hasContent: function () {
return this.getTitle()
}
, getPosition: function (inside) {
return $.extend({}, (inside ? {top: 0, left: 0} : this.$element.offset()), {
width: this.$element[0].offsetWidth
, height: this.$element[0].offsetHeight
})
}
, getTitle: function () {
var title
, $e = this.$element
, o = this.options
title = $e.attr('data-original-title')
|| (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
title = title.toString().replace(/(^\s*|\s*$)/, "")
return title
}
, tip: function () {
return this.$tip = this.$tip || $(this.options.template)
}
, validate: function () {
if (!this.$element[0].parentNode) {
this.hide()
this.$element = null
this.options = null
}
}
, enable: function () {
this.enabled = true
}
, disable: function () {
this.enabled = false
}
, toggleEnabled: function () {
this.enabled = !this.enabled
}
, toggle: function () {
this[this.tip().hasClass('in') ? 'hide' : 'show']()
}
}
/* TOOLTIP PLUGIN DEFINITION
* ========================= */
$.fn.tooltip = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('tooltip')
, options = typeof option == 'object' && option
if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.tooltip.Constructor = Tooltip
$.fn.tooltip.defaults = {
animation: true
, delay: 0
, selector: false
, placement: 'top'
, trigger: 'hover'
, title: ''
, template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
}
}( window.jQuery )

View file

@ -1,51 +0,0 @@
/* ===================================================
* bootstrap-transition.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#transitions
* ===================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ========================================================== */
!function( $ ) {
$(function () {
"use strict"
/* CSS TRANSITION SUPPORT (https://gist.github.com/373874)
* ======================================================= */
$.support.transition = (function () {
var thisBody = document.body || document.documentElement
, thisStyle = thisBody.style
, support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined
return support && {
end: (function () {
var transitionEnd = "TransitionEnd"
if ( $.browser.webkit ) {
transitionEnd = "webkitTransitionEnd"
} else if ( $.browser.mozilla ) {
transitionEnd = "transitionend"
} else if ( $.browser.opera ) {
transitionEnd = "oTransitionEnd"
}
return transitionEnd
}())
}
})()
})
}( window.jQuery )

View file

@ -1,271 +0,0 @@
/* =============================================================
* bootstrap-typeahead.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#typeahead
* =============================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ============================================================ */
!function( $ ){
"use strict"
var Typeahead = function ( element, options ) {
this.$element = $(element)
this.options = $.extend({}, $.fn.typeahead.defaults, options)
this.matcher = this.options.matcher || this.matcher
this.sorter = this.options.sorter || this.sorter
this.highlighter = this.options.highlighter || this.highlighter
this.$menu = $(this.options.menu).appendTo('body')
this.source = this.options.source
this.shown = false
this.listen()
}
Typeahead.prototype = {
constructor: Typeahead
, select: function () {
var val = this.$menu.find('.active').attr('data-value')
this.$element.val(val)
return this.hide()
}
, show: function () {
var pos = $.extend({}, this.$element.offset(), {
height: this.$element[0].offsetHeight
})
this.$menu.css({
top: pos.top + pos.height
, left: pos.left
})
this.$menu.show()
this.shown = true
return this
}
, hide: function () {
this.$menu.hide()
this.shown = false
return this
}
, lookup: function (event) {
var that = this
, items
, q
this.query = this.$element.val()
if (!this.query) {
return this.shown ? this.hide() : this
}
items = $.grep(this.source, function (item) {
if (that.matcher(item)) return item
})
items = this.sorter(items)
if (!items.length) {
return this.shown ? this.hide() : this
}
return this.render(items.slice(0, this.options.items)).show()
}
, matcher: function (item) {
return ~item.toLowerCase().indexOf(this.query.toLowerCase())
}
, sorter: function (items) {
var beginswith = []
, caseSensitive = []
, caseInsensitive = []
, item
while (item = items.shift()) {
if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
else if (~item.indexOf(this.query)) caseSensitive.push(item)
else caseInsensitive.push(item)
}
return beginswith.concat(caseSensitive, caseInsensitive)
}
, highlighter: function (item) {
return item.replace(new RegExp('(' + this.query + ')', 'ig'), function ($1, match) {
return '<strong>' + match + '</strong>'
})
}
, render: function (items) {
var that = this
items = $(items).map(function (i, item) {
i = $(that.options.item).attr('data-value', item)
i.find('a').html(that.highlighter(item))
return i[0]
})
items.first().addClass('active')
this.$menu.html(items)
return this
}
, next: function (event) {
var active = this.$menu.find('.active').removeClass('active')
, next = active.next()
if (!next.length) {
next = $(this.$menu.find('li')[0])
}
next.addClass('active')
}
, prev: function (event) {
var active = this.$menu.find('.active').removeClass('active')
, prev = active.prev()
if (!prev.length) {
prev = this.$menu.find('li').last()
}
prev.addClass('active')
}
, listen: function () {
this.$element
.on('blur', $.proxy(this.blur, this))
.on('keypress', $.proxy(this.keypress, this))
.on('keyup', $.proxy(this.keyup, this))
if ($.browser.webkit || $.browser.msie) {
this.$element.on('keydown', $.proxy(this.keypress, this))
}
this.$menu
.on('click', $.proxy(this.click, this))
.on('mouseenter', 'li', $.proxy(this.mouseenter, this))
}
, keyup: function (e) {
e.stopPropagation()
e.preventDefault()
switch(e.keyCode) {
case 40: // down arrow
case 38: // up arrow
break
case 9: // tab
case 13: // enter
if (!this.shown) return
this.select()
break
case 27: // escape
this.hide()
break
default:
this.lookup()
}
}
, keypress: function (e) {
e.stopPropagation()
if (!this.shown) return
switch(e.keyCode) {
case 9: // tab
case 13: // enter
case 27: // escape
e.preventDefault()
break
case 38: // up arrow
e.preventDefault()
this.prev()
break
case 40: // down arrow
e.preventDefault()
this.next()
break
}
}
, blur: function (e) {
var that = this
e.stopPropagation()
e.preventDefault()
setTimeout(function () { that.hide() }, 150)
}
, click: function (e) {
e.stopPropagation()
e.preventDefault()
this.select()
}
, mouseenter: function (e) {
this.$menu.find('.active').removeClass('active')
$(e.currentTarget).addClass('active')
}
}
/* TYPEAHEAD PLUGIN DEFINITION
* =========================== */
$.fn.typeahead = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('typeahead')
, options = typeof option == 'object' && option
if (!data) $this.data('typeahead', (data = new Typeahead(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.typeahead.defaults = {
source: []
, items: 8
, menu: '<ul class="typeahead dropdown-menu"></ul>'
, item: '<li><a href="#"></a></li>'
}
$.fn.typeahead.Constructor = Typeahead
/* TYPEAHEAD DATA-API
* ================== */
$(function () {
$('body').on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
var $this = $(this)
if ($this.data('typeahead')) return
e.preventDefault()
$this.typeahead($this.data())
})
})
}( window.jQuery )

View file

@ -22,5 +22,9 @@ code {
}
.poll-status {
padding-left: 10px;
padding-right: 10px;
}
.navbar a.brand {
color: white;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,8 @@
.hero-unit
h1 Sidekiq is #{current_status}
p Processed: #{processed}
p Failed: #{failed}
p Busy Workers: #{workers.size}
p Scheduled: #{zcard('schedule')}
p Retries Pending: #{zcard('retry')}
p Queue Backlog: #{backlog}
p Processed: #{number_with_delimiter(processed)}
p Failed: #{number_with_delimiter(failed)}
p Busy Workers: #{number_with_delimiter(workers.size)}
p Scheduled: #{number_with_delimiter(zcard('schedule'))}
p Retries Pending: #{number_with_delimiter(zcard('retry'))}
p Queue Backlog: #{number_with_delimiter(backlog)}

View file

@ -1,10 +1,11 @@
== slim :_summary
.poll
a*{name: 'poll'} href='#{{root_path}}poll' Live Poll
span class="poll-status"
.actions
form.pull-left action="#{root_path}reset" method="post"
button.btn.btn-small type="submit" title="If you kill -9 Sidekiq, this table can fill up with old data." Clear worker list
.pull-right
span.poll-status
a.btn.btn-small name='poll' href='#{{root_path}}poll' Live Poll
== slim :_workers
form action="#{root_path}reset" method="post"
button.btn type="submit" title="If you kill -9 Sidekiq, this table can fill up with old data." Clear worker list

View file

@ -4,7 +4,7 @@ html
link href='#{{root_path}}assets/application.css' media='screen' rel='stylesheet' type='text/css'
title Sidekiq
body
.navbar.navbar-fixed-top
.navbar.navbar-fixed-top.navbar-inverse
.navbar-inner
.container
a.btn.btn-navbar data-toggle="collapse" data-target=".nav-collapse"
@ -17,12 +17,10 @@ html
ul.nav
li
a href='#{{root_path}}' Home
li
a href='#{{root_path}}queues' Queues
li
a href='#{{root_path}}retries' Retries
li
a href='#{{root_path}}scheduled' Scheduled
- tabs.each do |tab|
li
a href='#{{root_path}}#{{tab.downcase}}': #{tab}
ul.nav.pull-right
li
a Redis: #{location}

View file

@ -10,10 +10,10 @@ h1 Queues
tr
td
a href="#{root_path}queues/#{queue}" #{queue}
td= size
td= number_with_delimiter(size)
td
form action="#{root_path}queues/#{queue}" method="post"
input.btn.btn-danger type="submit" name="delete" value="Delete"
input.btn.btn-danger type="submit" name="delete" value="Delete" data-confirm="Are you sure you want to delete the #{queue} queue?"
- else
p No queues found.
a href="#{root_path}" Back