couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ryanram...@apache.org
Subject [14/51] [partial] Restructure to simpler jam/erica style.
Date Sat, 11 May 2013 05:48:27 GMT
http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/bootstrap/js/bootstrap-typeahead.js
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/bootstrap/js/bootstrap-typeahead.js b/src/fauxton/jam/bootstrap/js/bootstrap-typeahead.js
new file mode 100644
index 0000000..088e7ce
--- /dev/null
+++ b/src/fauxton/jam/bootstrap/js/bootstrap-typeahead.js
@@ -0,0 +1,323 @@
+/* =============================================================
+ * bootstrap-typeahead.js v2.2.2
+ * 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"; // jshint ;_;
+
+
+ /* TYPEAHEAD PUBLIC CLASS DEFINITION
+  * ================================= */
+
+  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.updater = this.options.updater || this.updater
+    this.source = this.options.source
+    this.$menu = $(this.options.menu)
+    this.shown = false
+    this.listen()
+  }
+
+  Typeahead.prototype = {
+
+    constructor: Typeahead
+
+  , select: function () {
+      var val = this.$menu.find('.active').attr('data-value')
+      this.$element
+        .val(this.updater(val))
+        .change()
+      return this.hide()
+    }
+
+  , updater: function (item) {
+      return item
+    }
+
+  , show: function () {
+      var pos = $.extend({}, this.$element.position(), {
+        height: this.$element[0].offsetHeight
+      })
+
+      this.$menu
+        .insertAfter(this.$element)
+        .css({
+          top: pos.top + pos.height
+        , left: pos.left
+        })
+        .show()
+
+      this.shown = true
+      return this
+    }
+
+  , hide: function () {
+      this.$menu.hide()
+      this.shown = false
+      return this
+    }
+
+  , lookup: function (event) {
+      var items
+
+      this.query = this.$element.val()
+
+      if (!this.query || this.query.length < this.options.minLength) {
+        return this.shown ? this.hide() : this
+      }
+
+      items = $.isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source
+
+      return items ? this.process(items) : this
+    }
+
+  , process: function (items) {
+      var that = this
+
+      items = $.grep(items, function (item) {
+        return that.matcher(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) {
+      var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&')
+      return item.replace(new RegExp('(' + 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 (this.eventSupported('keydown')) {
+        this.$element.on('keydown', $.proxy(this.keydown, this))
+      }
+
+      this.$menu
+        .on('click', $.proxy(this.click, this))
+        .on('mouseenter', 'li', $.proxy(this.mouseenter, this))
+    }
+
+  , eventSupported: function(eventName) {
+      var isSupported = eventName in this.$element
+      if (!isSupported) {
+        this.$element.setAttribute(eventName, 'return;')
+        isSupported = typeof this.$element[eventName] === 'function'
+      }
+      return isSupported
+    }
+
+  , move: function (e) {
+      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
+      }
+
+      e.stopPropagation()
+    }
+
+  , keydown: function (e) {
+      this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40,38,9,13,27])
+      this.move(e)
+    }
+
+  , keypress: function (e) {
+      if (this.suppressKeyPressRepeat) return
+      this.move(e)
+    }
+
+  , keyup: function (e) {
+      switch(e.keyCode) {
+        case 40: // down arrow
+        case 38: // up arrow
+        case 16: // shift
+        case 17: // ctrl
+        case 18: // alt
+          break
+
+        case 9: // tab
+        case 13: // enter
+          if (!this.shown) return
+          this.select()
+          break
+
+        case 27: // escape
+          if (!this.shown) return
+          this.hide()
+          break
+
+        default:
+          this.lookup()
+      }
+
+      e.stopPropagation()
+      e.preventDefault()
+  }
+
+  , blur: function (e) {
+      var that = this
+      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
+   * =========================== */
+
+  var old = $.fn.typeahead
+
+  $.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>'
+  , minLength: 1
+  }
+
+  $.fn.typeahead.Constructor = Typeahead
+
+
+ /* TYPEAHEAD NO CONFLICT
+  * =================== */
+
+  $.fn.typeahead.noConflict = function () {
+    $.fn.typeahead = old
+    return this
+  }
+
+
+ /* TYPEAHEAD DATA-API
+  * ================== */
+
+  $(document).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);

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/bootstrap/js/tests/index.html
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/bootstrap/js/tests/index.html b/src/fauxton/jam/bootstrap/js/tests/index.html
new file mode 100644
index 0000000..976ca16
--- /dev/null
+++ b/src/fauxton/jam/bootstrap/js/tests/index.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bootstrap Plugin Test Suite</title>
+
+  <!-- jquery -->
+  <!--<script src="http://code.jquery.com/jquery-1.7.min.js"></script>-->
+  <script src="vendor/jquery.js"></script>
+
+  <!-- qunit -->
+  <link rel="stylesheet" href="vendor/qunit.css" type="text/css" media="screen" />
+  <script src="vendor/qunit.js"></script>
+
+  <!-- phantomjs logging script-->
+  <script src="unit/bootstrap-phantom.js"></script>
+
+  <!--  plugin sources -->
+  <script src="../../js/bootstrap-transition.js"></script>
+  <script src="../../js/bootstrap-alert.js"></script>
+  <script src="../../js/bootstrap-button.js"></script>
+  <script src="../../js/bootstrap-carousel.js"></script>
+  <script src="../../js/bootstrap-collapse.js"></script>
+  <script src="../../js/bootstrap-dropdown.js"></script>
+  <script src="../../js/bootstrap-modal.js"></script>
+  <script src="../../js/bootstrap-scrollspy.js"></script>
+  <script src="../../js/bootstrap-tab.js"></script>
+  <script src="../../js/bootstrap-tooltip.js"></script>
+  <script src="../../js/bootstrap-popover.js"></script>
+  <script src="../../js/bootstrap-typeahead.js"></script>
+  <script src="../../js/bootstrap-affix.js"></script>
+
+  <!-- unit tests -->
+  <script src="unit/bootstrap-transition.js"></script>
+  <script src="unit/bootstrap-alert.js"></script>
+  <script src="unit/bootstrap-button.js"></script>
+  <script src="unit/bootstrap-carousel.js"></script>
+  <script src="unit/bootstrap-collapse.js"></script>
+  <script src="unit/bootstrap-dropdown.js"></script>
+  <script src="unit/bootstrap-modal.js"></script>
+  <script src="unit/bootstrap-scrollspy.js"></script>
+  <script src="unit/bootstrap-tab.js"></script>
+  <script src="unit/bootstrap-tooltip.js"></script>
+  <script src="unit/bootstrap-popover.js"></script>
+  <script src="unit/bootstrap-typeahead.js"></script>
+  <script src="unit/bootstrap-affix.js"></script>
+</head>
+<body>
+  <div>
+    <h1 id="qunit-header">Bootstrap Plugin Test Suite</h1>
+    <h2 id="qunit-banner"></h2>
+    <h2 id="qunit-userAgent"></h2>
+    <ol id="qunit-tests"></ol>
+    <div id="qunit-fixture"></div>
+  </div>
+</body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/bootstrap/js/tests/phantom.js
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/bootstrap/js/tests/phantom.js b/src/fauxton/jam/bootstrap/js/tests/phantom.js
new file mode 100644
index 0000000..4105bf5
--- /dev/null
+++ b/src/fauxton/jam/bootstrap/js/tests/phantom.js
@@ -0,0 +1,63 @@
+// Simple phantom.js integration script
+// Adapted from Modernizr
+
+function waitFor(testFx, onReady, timeOutMillis) {
+  var maxtimeOutMillis = timeOutMillis ? timeOutMillis :  5001 //< Default Max Timout is 5s
+    , start = new Date().getTime()
+    , condition = false
+    , interval = setInterval(function () {
+        if ((new Date().getTime() - start < maxtimeOutMillis) && !condition) {
+          // If not time-out yet and condition not yet fulfilled
+          condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()) //< defensive code
+        } else {
+          if (!condition) {
+            // If condition still not fulfilled (timeout but condition is 'false')
+            console.log("'waitFor()' timeout")
+            phantom.exit(1)
+          } else {
+            // Condition fulfilled (timeout and/or condition is 'true')
+            typeof(onReady) === "string" ? eval(onReady) : onReady() //< Do what it's supposed to do once the condition is fulfilled
+            clearInterval(interval) //< Stop this interval
+          }
+        }
+    }, 100) //< repeat check every 100ms
+}
+
+
+if (phantom.args.length === 0 || phantom.args.length > 2) {
+  console.log('Usage: phantom.js URL')
+  phantom.exit()
+}
+
+var page = new WebPage()
+
+// Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this")
+page.onConsoleMessage = function(msg) {
+  console.log(msg)
+};
+
+page.open(phantom.args[0], function(status){
+  if (status !== "success") {
+    console.log("Unable to access network")
+    phantom.exit()
+  } else {
+    waitFor(function(){
+      return page.evaluate(function(){
+        var el = document.getElementById('qunit-testresult')
+        if (el && el.innerText.match('completed')) {
+          return true
+        }
+        return false
+      })
+    }, function(){
+      var failedNum = page.evaluate(function(){
+        var el = document.getElementById('qunit-testresult')
+        try {
+          return el.getElementsByClassName('failed')[0].innerHTML
+        } catch (e) { }
+        return 10000
+      });
+      phantom.exit((parseInt(failedNum, 10) > 0) ? 1 : 0)
+    })
+  }
+})
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/bootstrap/js/tests/server.js
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/bootstrap/js/tests/server.js b/src/fauxton/jam/bootstrap/js/tests/server.js
new file mode 100644
index 0000000..7c8445f
--- /dev/null
+++ b/src/fauxton/jam/bootstrap/js/tests/server.js
@@ -0,0 +1,14 @@
+/*
+ * Simple connect server for phantom.js
+ * Adapted from Modernizr
+ */
+
+var connect = require('connect')
+  , http = require('http')
+  , fs   = require('fs')
+  , app = connect()
+      .use(connect.static(__dirname + '/../../'));
+
+http.createServer(app).listen(3000);
+
+fs.writeFileSync(__dirname + '/pid.txt', process.pid, 'utf-8')
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-affix.js
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-affix.js b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-affix.js
new file mode 100644
index 0000000..c978878
--- /dev/null
+++ b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-affix.js
@@ -0,0 +1,25 @@
+$(function () {
+
+    module("bootstrap-affix")
+
+      test("should provide no conflict", function () {
+        var affix = $.fn.affix.noConflict()
+        ok(!$.fn.affix, 'affix was set back to undefined (org value)')
+        $.fn.affix = affix
+      })
+
+      test("should be defined on jquery object", function () {
+        ok($(document.body).affix, 'affix method is defined')
+      })
+
+      test("should return element", function () {
+        ok($(document.body).affix()[0] == document.body, 'document.body returned')
+      })
+
+      test("should exit early if element is not visible", function () {
+        var $affix = $('<div style="display: none"></div>').affix()
+        $affix.data('affix').checkPosition()
+        ok(!$affix.hasClass('affix'), 'affix class was not added')
+      })
+
+})
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-alert.js
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-alert.js b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-alert.js
new file mode 100644
index 0000000..9a6b514
--- /dev/null
+++ b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-alert.js
@@ -0,0 +1,62 @@
+$(function () {
+
+    module("bootstrap-alerts")
+
+      test("should provide no conflict", function () {
+        var alert = $.fn.alert.noConflict()
+        ok(!$.fn.alert, 'alert was set back to undefined (org value)')
+        $.fn.alert = alert
+      })
+
+      test("should be defined on jquery object", function () {
+        ok($(document.body).alert, 'alert method is defined')
+      })
+
+      test("should return element", function () {
+        ok($(document.body).alert()[0] == document.body, 'document.body returned')
+      })
+
+      test("should fade element out on clicking .close", function () {
+        var alertHTML = '<div class="alert-message warning fade in">'
+          + '<a class="close" href="#" data-dismiss="alert">×</a>'
+          + '<p><strong>Holy guacamole!</strong> Best check yo self, you\'re not looking too good.</p>'
+          + '</div>'
+          , alert = $(alertHTML).alert()
+
+        alert.find('.close').click()
+
+        ok(!alert.hasClass('in'), 'remove .in class on .close click')
+      })
+
+      test("should remove element when clicking .close", function () {
+        $.support.transition = false
+
+        var alertHTML = '<div class="alert-message warning fade in">'
+          + '<a class="close" href="#" data-dismiss="alert">×</a>'
+          + '<p><strong>Holy guacamole!</strong> Best check yo self, you\'re not looking too good.</p>'
+          + '</div>'
+          , alert = $(alertHTML).appendTo('#qunit-fixture').alert()
+
+        ok($('#qunit-fixture').find('.alert-message').length, 'element added to dom')
+
+        alert.find('.close').click()
+
+        ok(!$('#qunit-fixture').find('.alert-message').length, 'element removed from dom')
+      })
+
+      test("should not fire closed when close is prevented", function () {
+        $.support.transition = false
+        stop();
+        $('<div class="alert"/>')
+          .bind('close', function (e) {
+            e.preventDefault();
+            ok(true);
+            start();
+          })
+          .bind('closed', function () {
+            ok(false);
+          })
+          .alert('close')
+      })
+
+})
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-button.js
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-button.js b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-button.js
new file mode 100644
index 0000000..e23ff9e
--- /dev/null
+++ b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-button.js
@@ -0,0 +1,102 @@
+$(function () {
+
+    module("bootstrap-buttons")
+
+      test("should provide no conflict", function () {
+        var button = $.fn.button.noConflict()
+        ok(!$.fn.button, 'button was set back to undefined (org value)')
+        $.fn.button = button
+      })
+
+      test("should be defined on jquery object", function () {
+        ok($(document.body).button, 'button method is defined')
+      })
+
+      test("should return element", function () {
+        ok($(document.body).button()[0] == document.body, 'document.body returned')
+      })
+
+      test("should return set state to loading", function () {
+        var btn = $('<button class="btn" data-loading-text="fat">mdo</button>')
+        equals(btn.html(), 'mdo', 'btn text equals mdo')
+        btn.button('loading')
+        equals(btn.html(), 'fat', 'btn text equals fat')
+        stop()
+        setTimeout(function () {
+          ok(btn.attr('disabled'), 'btn is disabled')
+          ok(btn.hasClass('disabled'), 'btn has disabled class')
+          start()
+        }, 0)
+      })
+
+      test("should return reset state", function () {
+        var btn = $('<button class="btn" data-loading-text="fat">mdo</button>')
+        equals(btn.html(), 'mdo', 'btn text equals mdo')
+        btn.button('loading')
+        equals(btn.html(), 'fat', 'btn text equals fat')
+        stop()
+        setTimeout(function () {
+          ok(btn.attr('disabled'), 'btn is disabled')
+          ok(btn.hasClass('disabled'), 'btn has disabled class')
+          start()
+          stop()
+        }, 0)
+        btn.button('reset')
+        equals(btn.html(), 'mdo', 'btn text equals mdo')
+        setTimeout(function () {
+          ok(!btn.attr('disabled'), 'btn is not disabled')
+          ok(!btn.hasClass('disabled'), 'btn does not have disabled class')
+          start()
+        }, 0)
+      })
+
+      test("should toggle active", function () {
+        var btn = $('<button class="btn">mdo</button>')
+        ok(!btn.hasClass('active'), 'btn does not have active class')
+        btn.button('toggle')
+        ok(btn.hasClass('active'), 'btn has class active')
+      })
+
+      test("should toggle active when btn children are clicked", function () {
+        var btn = $('<button class="btn" data-toggle="button">mdo</button>')
+          , inner = $('<i></i>')
+        btn
+          .append(inner)
+          .appendTo($('#qunit-fixture'))
+        ok(!btn.hasClass('active'), 'btn does not have active class')
+        inner.click()
+        ok(btn.hasClass('active'), 'btn has class active')
+      })
+
+      test("should toggle active when btn children are clicked within btn-group", function () {
+        var btngroup = $('<div class="btn-group" data-toggle="buttons-checkbox"></div>')
+          , btn = $('<button class="btn">fat</button>')
+          , inner = $('<i></i>')
+        btngroup
+          .append(btn.append(inner))
+          .appendTo($('#qunit-fixture'))
+        ok(!btn.hasClass('active'), 'btn does not have active class')
+        inner.click()
+        ok(btn.hasClass('active'), 'btn has class active')
+      })
+
+      test("should check for closest matching toggle", function () {
+        var group = $("<div data-toggle='buttons-radio'></div>")
+          , btn1  = $("<button class='btn active'></button>")
+          , btn2  = $("<button class='btn'></button>")
+          , wrap  = $("<div></div>")
+
+        wrap.append(btn1, btn2)
+
+        group
+          .append(wrap)
+          .appendTo($('#qunit-fixture'))
+
+        ok(btn1.hasClass('active'), 'btn1 has active class')
+        ok(!btn2.hasClass('active'), 'btn2 does not have active class')
+        btn2.click()
+        ok(!btn1.hasClass('active'), 'btn1 does not have active class')
+        ok(btn2.hasClass('active'), 'btn2 has active class')
+      })
+
+})
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-carousel.js
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-carousel.js b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-carousel.js
new file mode 100644
index 0000000..13b8f72
--- /dev/null
+++ b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-carousel.js
@@ -0,0 +1,69 @@
+$(function () {
+
+    module("bootstrap-carousel")
+
+      test("should provide no conflict", function () {
+        var carousel = $.fn.carousel.noConflict()
+        ok(!$.fn.carousel, 'carousel was set back to undefined (org value)')
+        $.fn.carousel = carousel
+      })
+
+      test("should be defined on jquery object", function () {
+        ok($(document.body).carousel, 'carousel method is defined')
+      })
+
+      test("should return element", function () {
+        ok($(document.body).carousel()[0] == document.body, 'document.body returned')
+      })
+
+      test("should not fire sliden when slide is prevented", function () {
+        $.support.transition = false
+        stop()
+        $('<div class="carousel"/>')
+          .bind('slide', function (e) {
+            e.preventDefault();
+            ok(true);
+            start();
+          })
+          .bind('slid', function () {
+            ok(false);
+          })
+          .carousel('next')
+      })
+
+      test("should fire slide event with relatedTarget", function () {
+        var template = '<div id="myCarousel" class="carousel slide"><div class="carousel-inner"><div class="item active"><img alt=""><div class="carousel-caption"><h4>{{_i}}First Thumbnail label{{/i}}</h4><p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p></div></div><div class="item"><img alt=""><div class="carousel-caption"><h4>{{_i}}Second Thumbnail label{{/i}}</h4><p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p></div></div><div class="item"><img alt=""><div class="carousel-caption"><h4>{{_i}}Third Thumbnail label{{/i}}</h4><p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p></div></div></div><a class="left carousel-contr
 ol" href="#myCarousel" data-slide="prev">&lsaquo;</a><a class="right carousel-control" href="#myCarousel" data-slide="next">&rsaquo;</a></div>'
+        $.support.transition = false
+        stop()
+        $(template)
+          .on('slide', function (e) {
+            e.preventDefault();
+            ok(e.relatedTarget);
+            ok($(e.relatedTarget).hasClass('item'));
+            start();
+          })
+          .carousel('next')
+      })
+
+      test("should set interval from data attribute", 3,function () {
+        var template = $('<div id="myCarousel" class="carousel slide"> <div class="carousel-inner"> <div class="item active"> <img alt=""> <div class="carousel-caption"> <h4>{{_i}}First Thumbnail label{{/i}}</h4> <p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p> </div> </div> <div class="item"> <img alt=""> <div class="carousel-caption"> <h4>{{_i}}Second Thumbnail label{{/i}}</h4> <p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p> </div> </div> <div class="item"> <img alt=""> <div class="carousel-caption"> <h4>{{_i}}Third Thumbnail label{{/i}}</h4> <p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p> </div> </div> </div> <a 
 class="left carousel-control" href="#myCarousel" data-slide="prev">&lsaquo;</a> <a class="right carousel-control" href="#myCarousel" data-slide="next">&rsaquo;</a> </div>');
+        template.attr("data-interval", 1814);
+
+        template.appendTo("body");
+        $('[data-slide]').first().click();
+        ok($('#myCarousel').data('carousel').options.interval == 1814);
+        $('#myCarousel').remove();
+
+        template.appendTo("body").attr("data-modal", "foobar");
+        $('[data-slide]').first().click();
+        ok($('#myCarousel').data('carousel').options.interval == 1814, "even if there is an data-modal attribute set");
+        $('#myCarousel').remove();
+
+        template.appendTo("body");
+        $('[data-slide]').first().click();
+        $('#myCarousel').attr('data-interval', 1860);
+        $('[data-slide]').first().click();
+        ok($('#myCarousel').data('carousel').options.interval == 1814, "attributes should be read only on intitialization");
+        $('#myCarousel').remove();
+      })
+})
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-collapse.js
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-collapse.js b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-collapse.js
new file mode 100644
index 0000000..4c5916e
--- /dev/null
+++ b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-collapse.js
@@ -0,0 +1,94 @@
+$(function () {
+
+    module("bootstrap-collapse")
+
+      test("should provide no conflict", function () {
+        var collapse = $.fn.collapse.noConflict()
+        ok(!$.fn.collapse, 'collapse was set back to undefined (org value)')
+        $.fn.collapse = collapse
+      })
+
+      test("should be defined on jquery object", function () {
+        ok($(document.body).collapse, 'collapse method is defined')
+      })
+
+      test("should return element", function () {
+        ok($(document.body).collapse()[0] == document.body, 'document.body returned')
+      })
+
+      test("should show a collapsed element", function () {
+        var el = $('<div class="collapse"></div>').collapse('show')
+        ok(el.hasClass('in'), 'has class in')
+        ok(/height/.test(el.attr('style')), 'has height set')
+      })
+
+      test("should hide a collapsed element", function () {
+        var el = $('<div class="collapse"></div>').collapse('hide')
+        ok(!el.hasClass('in'), 'does not have class in')
+        ok(/height/.test(el.attr('style')), 'has height set')
+      })
+
+      test("should not fire shown when show is prevented", function () {
+        $.support.transition = false
+        stop()
+        $('<div class="collapse"/>')
+          .bind('show', function (e) {
+            e.preventDefault();
+            ok(true);
+            start();
+          })
+          .bind('shown', function () {
+            ok(false);
+          })
+          .collapse('show')
+      })
+
+      test("should reset style to auto after finishing opening collapse", function () {
+        $.support.transition = false
+        stop()
+        $('<div class="collapse" style="height: 0px"/>')
+          .bind('show', function () {
+            ok(this.style.height == '0px')
+          })
+          .bind('shown', function () {
+            ok(this.style.height == 'auto')
+            start()
+          })
+          .collapse('show')
+      })
+
+      test("should add active class to target when collapse shown", function () {
+        $.support.transition = false
+        stop()
+
+        var target = $('<a data-toggle="collapse" href="#test1"></a>')
+          .appendTo($('#qunit-fixture'))
+
+        var collapsible = $('<div id="test1"></div>')
+          .appendTo($('#qunit-fixture'))
+          .on('show', function () {
+            ok(!target.hasClass('collapsed'))
+            start()
+          })
+
+        target.click()
+      })
+
+      test("should remove active class to target when collapse hidden", function () {
+        $.support.transition = false
+        stop()
+
+        var target = $('<a data-toggle="collapse" href="#test1"></a>')
+          .appendTo($('#qunit-fixture'))
+
+        var collapsible = $('<div id="test1" class="in"></div>')
+          .appendTo($('#qunit-fixture'))
+          .on('hide', function () {
+            ok(target.hasClass('collapsed'))
+            start()
+          })
+
+        target.click()
+      })
+
+})
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-dropdown.js
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-dropdown.js b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-dropdown.js
new file mode 100644
index 0000000..2f0d2d2
--- /dev/null
+++ b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-dropdown.js
@@ -0,0 +1,151 @@
+$(function () {
+
+    module("bootstrap-dropdowns")
+
+      test("should provide no conflict", function () {
+        var dropdown = $.fn.dropdown.noConflict()
+        ok(!$.fn.dropdown, 'dropdown was set back to undefined (org value)')
+        $.fn.dropdown = dropdown
+      })
+
+      test("should be defined on jquery object", function () {
+        ok($(document.body).dropdown, 'dropdown method is defined')
+      })
+
+      test("should return element", function () {
+        var el = $("<div />")
+        ok(el.dropdown()[0] === el[0], 'same element returned')
+      })
+
+      test("should not open dropdown if target is disabled", function () {
+        var dropdownHTML = '<ul class="tabs">'
+          + '<li class="dropdown">'
+          + '<button disabled href="#" class="btn dropdown-toggle" data-toggle="dropdown">Dropdown</button>'
+          + '<ul class="dropdown-menu">'
+          + '<li><a href="#">Secondary link</a></li>'
+          + '<li><a href="#">Something else here</a></li>'
+          + '<li class="divider"></li>'
+          + '<li><a href="#">Another link</a></li>'
+          + '</ul>'
+          + '</li>'
+          + '</ul>'
+          , dropdown = $(dropdownHTML).find('[data-toggle="dropdown"]').dropdown().click()
+
+        ok(!dropdown.parent('.dropdown').hasClass('open'), 'open class added on click')
+      })
+
+      test("should not open dropdown if target is disabled", function () {
+        var dropdownHTML = '<ul class="tabs">'
+          + '<li class="dropdown">'
+          + '<button href="#" class="btn dropdown-toggle disabled" data-toggle="dropdown">Dropdown</button>'
+          + '<ul class="dropdown-menu">'
+          + '<li><a href="#">Secondary link</a></li>'
+          + '<li><a href="#">Something else here</a></li>'
+          + '<li class="divider"></li>'
+          + '<li><a href="#">Another link</a></li>'
+          + '</ul>'
+          + '</li>'
+          + '</ul>'
+          , dropdown = $(dropdownHTML).find('[data-toggle="dropdown"]').dropdown().click()
+
+        ok(!dropdown.parent('.dropdown').hasClass('open'), 'open class added on click')
+      })
+
+      test("should add class open to menu if clicked", function () {
+        var dropdownHTML = '<ul class="tabs">'
+          + '<li class="dropdown">'
+          + '<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>'
+          + '<ul class="dropdown-menu">'
+          + '<li><a href="#">Secondary link</a></li>'
+          + '<li><a href="#">Something else here</a></li>'
+          + '<li class="divider"></li>'
+          + '<li><a href="#">Another link</a></li>'
+          + '</ul>'
+          + '</li>'
+          + '</ul>'
+          , dropdown = $(dropdownHTML).find('[data-toggle="dropdown"]').dropdown().click()
+
+        ok(dropdown.parent('.dropdown').hasClass('open'), 'open class added on click')
+      })
+
+      test("should test if element has a # before assuming it's a selector", function () {
+        var dropdownHTML = '<ul class="tabs">'
+          + '<li class="dropdown">'
+          + '<a href="/foo/" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>'
+          + '<ul class="dropdown-menu">'
+          + '<li><a href="#">Secondary link</a></li>'
+          + '<li><a href="#">Something else here</a></li>'
+          + '<li class="divider"></li>'
+          + '<li><a href="#">Another link</a></li>'
+          + '</ul>'
+          + '</li>'
+          + '</ul>'
+          , dropdown = $(dropdownHTML).find('[data-toggle="dropdown"]').dropdown().click()
+
+        ok(dropdown.parent('.dropdown').hasClass('open'), 'open class added on click')
+      })
+
+
+      test("should remove open class if body clicked", function () {
+        var dropdownHTML = '<ul class="tabs">'
+          + '<li class="dropdown">'
+          + '<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>'
+          + '<ul class="dropdown-menu">'
+          + '<li><a href="#">Secondary link</a></li>'
+          + '<li><a href="#">Something else here</a></li>'
+          + '<li class="divider"></li>'
+          + '<li><a href="#">Another link</a></li>'
+          + '</ul>'
+          + '</li>'
+          + '</ul>'
+          , dropdown = $(dropdownHTML)
+            .appendTo('#qunit-fixture')
+            .find('[data-toggle="dropdown"]')
+            .dropdown()
+            .click()
+        ok(dropdown.parent('.dropdown').hasClass('open'), 'open class added on click')
+        $('body').click()
+        ok(!dropdown.parent('.dropdown').hasClass('open'), 'open class removed')
+        dropdown.remove()
+      })
+
+      test("should remove open class if body clicked, with multiple drop downs", function () {
+          var dropdownHTML =
+            '<ul class="nav">'
+            + '    <li><a href="#menu1">Menu 1</a></li>'
+            + '    <li class="dropdown" id="testmenu">'
+            + '      <a class="dropdown-toggle" data-toggle="dropdown" href="#testmenu">Test menu <b class="caret"></b></a>'
+            + '      <ul class="dropdown-menu" role="menu">'
+            + '        <li><a href="#sub1">Submenu 1</a></li>'
+            + '      </ul>'
+            + '    </li>'
+            + '</ul>'
+            + '<div class="btn-group">'
+            + '    <button class="btn">Actions</button>'
+            + '    <button class="btn dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></button>'
+            + '    <ul class="dropdown-menu">'
+            + '        <li><a href="#">Action 1</a></li>'
+            + '    </ul>'
+            + '</div>'
+          , dropdowns = $(dropdownHTML).appendTo('#qunit-fixture').find('[data-toggle="dropdown"]')
+          , first = dropdowns.first()
+          , last = dropdowns.last()
+
+        ok(dropdowns.length == 2, "Should be two dropdowns")
+
+        first.click()
+        ok(first.parents('.open').length == 1, 'open class added on click')
+        ok($('#qunit-fixture .open').length == 1, 'only one object is open')
+        $('body').click()
+        ok($("#qunit-fixture .open").length === 0, 'open class removed')
+
+        last.click()
+        ok(last.parent('.open').length == 1, 'open class added on click')
+        ok($('#qunit-fixture .open').length == 1, 'only one object is open')
+        $('body').click()
+        ok($("#qunit-fixture .open").length === 0, 'open class removed')
+
+        $("#qunit-fixture").html("")
+      })
+
+})
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-modal.js
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-modal.js b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-modal.js
new file mode 100644
index 0000000..98aa990
--- /dev/null
+++ b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-modal.js
@@ -0,0 +1,120 @@
+$(function () {
+
+    module("bootstrap-modal")
+
+      test("should provide no conflict", function () {
+        var modal = $.fn.modal.noConflict()
+        ok(!$.fn.modal, 'modal was set back to undefined (org value)')
+        $.fn.modal = modal
+      })
+
+      test("should be defined on jquery object", function () {
+        var div = $("<div id='modal-test'></div>")
+        ok(div.modal, 'modal method is defined')
+      })
+
+      test("should return element", function () {
+        var div = $("<div id='modal-test'></div>")
+        ok(div.modal() == div, 'document.body returned')
+        $('#modal-test').remove()
+      })
+
+      test("should expose defaults var for settings", function () {
+        ok($.fn.modal.defaults, 'default object exposed')
+      })
+
+      test("should insert into dom when show method is called", function () {
+        stop()
+        $.support.transition = false
+        $("<div id='modal-test'></div>")
+          .bind("shown", function () {
+            ok($('#modal-test').length, 'modal insterted into dom')
+            $(this).remove()
+            start()
+          })
+          .modal("show")
+      })
+
+      test("should fire show event", function () {
+        stop()
+        $.support.transition = false
+        $("<div id='modal-test'></div>")
+          .bind("show", function () {
+            ok(true, "show was called")
+          })
+          .bind("shown", function () {
+            $(this).remove()
+            start()
+          })
+          .modal("show")
+      })
+
+      test("should not fire shown when default prevented", function () {
+        stop()
+        $.support.transition = false
+        $("<div id='modal-test'></div>")
+          .bind("show", function (e) {
+            e.preventDefault()
+            ok(true, "show was called")
+            start()
+          })
+          .bind("shown", function () {
+            ok(false, "shown was called")
+          })
+          .modal("show")
+      })
+
+      test("should hide modal when hide is called", function () {
+        stop()
+        $.support.transition = false
+
+        $("<div id='modal-test'></div>")
+          .bind("shown", function () {
+            ok($('#modal-test').is(":visible"), 'modal visible')
+            ok($('#modal-test').length, 'modal insterted into dom')
+            $(this).modal("hide")
+          })
+          .bind("hidden", function() {
+            ok(!$('#modal-test').is(":visible"), 'modal hidden')
+            $('#modal-test').remove()
+            start()
+          })
+          .modal("show")
+      })
+
+      test("should toggle when toggle is called", function () {
+        stop()
+        $.support.transition = false
+        var div = $("<div id='modal-test'></div>")
+        div
+          .bind("shown", function () {
+            ok($('#modal-test').is(":visible"), 'modal visible')
+            ok($('#modal-test').length, 'modal insterted into dom')
+            div.modal("toggle")
+          })
+          .bind("hidden", function() {
+            ok(!$('#modal-test').is(":visible"), 'modal hidden')
+            div.remove()
+            start()
+          })
+          .modal("toggle")
+      })
+
+      test("should remove from dom when click [data-dismiss=modal]", function () {
+        stop()
+        $.support.transition = false
+        var div = $("<div id='modal-test'><span class='close' data-dismiss='modal'></span></div>")
+        div
+          .bind("shown", function () {
+            ok($('#modal-test').is(":visible"), 'modal visible')
+            ok($('#modal-test').length, 'modal insterted into dom')
+            div.find('.close').click()
+          })
+          .bind("hidden", function() {
+            ok(!$('#modal-test').is(":visible"), 'modal hidden')
+            div.remove()
+            start()
+          })
+          .modal("toggle")
+      })
+})
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-phantom.js
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-phantom.js b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-phantom.js
new file mode 100644
index 0000000..a04aeaa
--- /dev/null
+++ b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-phantom.js
@@ -0,0 +1,21 @@
+// Logging setup for phantom integration
+// adapted from Modernizr
+
+QUnit.begin = function () {
+  console.log("Starting test suite")
+  console.log("================================================\n")
+}
+
+QUnit.moduleDone = function (opts) {
+  if (opts.failed === 0) {
+    console.log("\u2714 All tests passed in '" + opts.name + "' module")
+  } else {
+    console.log("\u2716 " + opts.failed + " tests failed in '" + opts.name + "' module")
+  }
+}
+
+QUnit.done = function (opts) {
+  console.log("\n================================================")
+  console.log("Tests completed in " + opts.runtime + " milliseconds")
+  console.log(opts.passed + " tests of " + opts.total + " passed, " + opts.failed + " failed.")
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-popover.js
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-popover.js b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-popover.js
new file mode 100644
index 0000000..20234e1
--- /dev/null
+++ b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-popover.js
@@ -0,0 +1,113 @@
+$(function () {
+
+    module("bootstrap-popover")
+
+      test("should provide no conflict", function () {
+        var popover = $.fn.popover.noConflict()
+        ok(!$.fn.popover, 'popover was set back to undefined (org value)')
+        $.fn.popover = popover
+      })
+
+      test("should be defined on jquery object", function () {
+        var div = $('<div></div>')
+        ok(div.popover, 'popover method is defined')
+      })
+
+      test("should return element", function () {
+        var div = $('<div></div>')
+        ok(div.popover() == div, 'document.body returned')
+      })
+
+      test("should render popover element", function () {
+        $.support.transition = false
+        var popover = $('<a href="#" title="mdo" data-content="http://twitter.com/mdo">@mdo</a>')
+          .appendTo('#qunit-fixture')
+          .popover('show')
+
+        ok($('.popover').length, 'popover was inserted')
+        popover.popover('hide')
+        ok(!$(".popover").length, 'popover removed')
+      })
+
+      test("should store popover instance in popover data object", function () {
+        $.support.transition = false
+        var popover = $('<a href="#" title="mdo" data-content="http://twitter.com/mdo">@mdo</a>')
+          .popover()
+
+        ok(!!popover.data('popover'), 'popover instance exists')
+      })
+
+      test("should get title and content from options", function () {
+        $.support.transition = false
+        var popover = $('<a href="#">@fat</a>')
+          .appendTo('#qunit-fixture')
+          .popover({
+            title: function () {
+              return '@fat'
+            }
+          , content: function () {
+              return 'loves writing tests (╯°□°)╯︵ ┻━┻'
+            }
+          })
+
+        popover.popover('show')
+
+        ok($('.popover').length, 'popover was inserted')
+        equals($('.popover .popover-title').text(), '@fat', 'title correctly inserted')
+        equals($('.popover .popover-content').text(), 'loves writing tests (╯°□°)╯︵ ┻━┻', 'content correctly inserted')
+
+        popover.popover('hide')
+        ok(!$('.popover').length, 'popover was removed')
+        $('#qunit-fixture').empty()
+      })
+
+      test("should get title and content from attributes", function () {
+        $.support.transition = false
+        var popover = $('<a href="#" title="@mdo" data-content="loves data attributes (づ。◕‿‿◕。)づ ︵ ┻━┻" >@mdo</a>')
+          .appendTo('#qunit-fixture')
+          .popover()
+          .popover('show')
+
+        ok($('.popover').length, 'popover was inserted')
+        equals($('.popover .popover-title').text(), '@mdo', 'title correctly inserted')
+        equals($('.popover .popover-content').text(), "loves data attributes (づ。◕‿‿◕。)づ ︵ ┻━┻", 'content correctly inserted')
+
+        popover.popover('hide')
+        ok(!$('.popover').length, 'popover was removed')
+        $('#qunit-fixture').empty()
+      })
+
+      test("should respect custom classes", function() {
+        $.support.transition = false
+        var popover = $('<a href="#">@fat</a>')
+          .appendTo('#qunit-fixture')
+          .popover({
+            title: 'Test'
+          , content: 'Test'
+          , template: '<div class="popover foobar"><div class="arrow"></div><div class="inner"><h3 class="title"></h3><div class="content"><p></p></div></div></div>'
+          })
+
+        popover.popover('show')
+
+        ok($('.popover').length, 'popover was inserted')
+        ok($('.popover').hasClass('foobar'), 'custom class is present')
+
+        popover.popover('hide')
+        ok(!$('.popover').length, 'popover was removed')
+        $('#qunit-fixture').empty()
+      })
+
+      test("should destroy popover", function () {
+        var popover = $('<div/>').popover({trigger: 'hover'}).on('click.foo', function(){})
+        ok(popover.data('popover'), 'popover has data')
+        ok($._data(popover[0], 'events').mouseover && $._data(popover[0], 'events').mouseout, 'popover has hover event')
+        ok($._data(popover[0], 'events').click[0].namespace == 'foo', 'popover has extra click.foo event')
+        popover.popover('show')
+        popover.popover('destroy')
+        ok(!popover.hasClass('in'), 'popover is hidden')
+        ok(!popover.data('popover'), 'popover does not have data')
+        ok($._data(popover[0],'events').click[0].namespace == 'foo', 'popover still has click.foo')
+        ok(!$._data(popover[0], 'events').mouseover && !$._data(popover[0], 'events').mouseout, 'popover does not have any events')
+      })
+
+})
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-scrollspy.js
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-scrollspy.js b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-scrollspy.js
new file mode 100644
index 0000000..32bfa71
--- /dev/null
+++ b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-scrollspy.js
@@ -0,0 +1,37 @@
+$(function () {
+
+    module("bootstrap-scrollspy")
+
+      test("should provide no conflict", function () {
+        var scrollspy = $.fn.scrollspy.noConflict()
+        ok(!$.fn.scrollspy, 'scrollspy was set back to undefined (org value)')
+        $.fn.scrollspy = scrollspy
+      })
+
+      test("should be defined on jquery object", function () {
+        ok($(document.body).scrollspy, 'scrollspy method is defined')
+      })
+
+      test("should return element", function () {
+        ok($(document.body).scrollspy()[0] == document.body, 'document.body returned')
+      })
+
+      test("should switch active class on scroll", function () {
+        var sectionHTML = '<div id="masthead"></div>'
+          , $section = $(sectionHTML).append('#qunit-fixture')
+          , topbarHTML ='<div class="topbar">'
+          + '<div class="topbar-inner">'
+          + '<div class="container">'
+          + '<h3><a href="#">Bootstrap</a></h3>'
+          + '<ul class="nav">'
+          + '<li><a href="#masthead">Overview</a></li>'
+          + '</ul>'
+          + '</div>'
+          + '</div>'
+          + '</div>'
+          , $topbar = $(topbarHTML).scrollspy()
+
+        ok($topbar.find('.active', true))
+      })
+
+})
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-tab.js
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-tab.js b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-tab.js
new file mode 100644
index 0000000..ba5910d
--- /dev/null
+++ b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-tab.js
@@ -0,0 +1,86 @@
+$(function () {
+
+    module("bootstrap-tabs")
+
+      test("should provide no conflict", function () {
+        var tab = $.fn.tab.noConflict()
+        ok(!$.fn.tab, 'tab was set back to undefined (org value)')
+        $.fn.tab = tab
+      })
+
+      test("should be defined on jquery object", function () {
+        ok($(document.body).tab, 'tabs method is defined')
+      })
+
+      test("should return element", function () {
+        ok($(document.body).tab()[0] == document.body, 'document.body returned')
+      })
+
+      test("should activate element by tab id", function () {
+        var tabsHTML =
+            '<ul class="tabs">'
+          + '<li><a href="#home">Home</a></li>'
+          + '<li><a href="#profile">Profile</a></li>'
+          + '</ul>'
+
+        $('<ul><li id="home"></li><li id="profile"></li></ul>').appendTo("#qunit-fixture")
+
+        $(tabsHTML).find('li:last a').tab('show')
+        equals($("#qunit-fixture").find('.active').attr('id'), "profile")
+
+        $(tabsHTML).find('li:first a').tab('show')
+        equals($("#qunit-fixture").find('.active').attr('id'), "home")
+      })
+
+      test("should activate element by tab id", function () {
+        var pillsHTML =
+            '<ul class="pills">'
+          + '<li><a href="#home">Home</a></li>'
+          + '<li><a href="#profile">Profile</a></li>'
+          + '</ul>'
+
+        $('<ul><li id="home"></li><li id="profile"></li></ul>').appendTo("#qunit-fixture")
+
+        $(pillsHTML).find('li:last a').tab('show')
+        equals($("#qunit-fixture").find('.active').attr('id'), "profile")
+
+        $(pillsHTML).find('li:first a').tab('show')
+        equals($("#qunit-fixture").find('.active').attr('id'), "home")
+      })
+
+
+      test("should not fire closed when close is prevented", function () {
+        $.support.transition = false
+        stop();
+        $('<div class="tab"/>')
+          .bind('show', function (e) {
+            e.preventDefault();
+            ok(true);
+            start();
+          })
+          .bind('shown', function () {
+            ok(false);
+          })
+          .tab('show')
+      })
+
+      test("show and shown events should reference correct relatedTarget", function () {
+        var dropHTML =
+            '<ul class="drop">'
+          + '<li class="dropdown"><a data-toggle="dropdown" href="#">1</a>'
+          + '<ul class="dropdown-menu">'
+          + '<li><a href="#1-1" data-toggle="tab">1-1</a></li>'
+          + '<li><a href="#1-2" data-toggle="tab">1-2</a></li>'
+          + '</ul>'
+          + '</li>'
+          + '</ul>'
+
+        $(dropHTML).find('ul>li:first a').tab('show').end()
+          .find('ul>li:last a').on('show', function(event){
+            equals(event.relatedTarget.hash, "#1-1")
+          }).on('shown', function(event){
+            equals(event.relatedTarget.hash, "#1-1")
+          }).tab('show')
+      })
+
+})
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-tooltip.js
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-tooltip.js b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-tooltip.js
new file mode 100644
index 0000000..ba51347
--- /dev/null
+++ b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-tooltip.js
@@ -0,0 +1,159 @@
+$(function () {
+
+    module("bootstrap-tooltip")
+
+      test("should provide no conflict", function () {
+        var tooltip = $.fn.tooltip.noConflict()
+        ok(!$.fn.tooltip, 'tooltip was set back to undefined (org value)')
+        $.fn.tooltip = tooltip
+      })
+
+      test("should be defined on jquery object", function () {
+        var div = $("<div></div>")
+        ok(div.tooltip, 'popover method is defined')
+      })
+
+      test("should return element", function () {
+        var div = $("<div></div>")
+        ok(div.tooltip() == div, 'document.body returned')
+      })
+
+      test("should expose default settings", function () {
+        ok(!!$.fn.tooltip.defaults, 'defaults is defined')
+      })
+
+      test("should remove title attribute", function () {
+        var tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"></a>').tooltip()
+        ok(!tooltip.attr('title'), 'title tag was removed')
+      })
+
+      test("should add data attribute for referencing original title", function () {
+        var tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"></a>').tooltip()
+        equals(tooltip.attr('data-original-title'), 'Another tooltip', 'original title preserved in data attribute')
+      })
+
+      test("should place tooltips relative to placement option", function () {
+        $.support.transition = false
+        var tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"></a>')
+          .appendTo('#qunit-fixture')
+          .tooltip({placement: 'bottom'})
+          .tooltip('show')
+
+        ok($(".tooltip").is('.fade.bottom.in'), 'has correct classes applied')
+        tooltip.tooltip('hide')
+      })
+
+      test("should allow html entities", function () {
+        $.support.transition = false
+        var tooltip = $('<a href="#" rel="tooltip" title="<b>@fat</b>"></a>')
+          .appendTo('#qunit-fixture')
+          .tooltip({html: true})
+          .tooltip('show')
+
+        ok($('.tooltip b').length, 'b tag was inserted')
+        tooltip.tooltip('hide')
+        ok(!$(".tooltip").length, 'tooltip removed')
+      })
+
+      test("should respect custom classes", function () {
+        var tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"></a>')
+          .appendTo('#qunit-fixture')
+          .tooltip({ template: '<div class="tooltip some-class"><div class="tooltip-arrow"/><div class="tooltip-inner"/></div>'})
+          .tooltip('show')
+
+        ok($('.tooltip').hasClass('some-class'), 'custom class is present')
+        tooltip.tooltip('hide')
+        ok(!$(".tooltip").length, 'tooltip removed')
+      })
+
+      test("should not show tooltip if leave event occurs before delay expires", function () {
+        var tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"></a>')
+          .appendTo('#qunit-fixture')
+          .tooltip({ delay: 200 })
+
+        stop()
+
+        tooltip.trigger('mouseenter')
+
+        setTimeout(function () {
+          ok(!$(".tooltip").is('.fade.in'), 'tooltip is not faded in')
+          tooltip.trigger('mouseout')
+          setTimeout(function () {
+            ok(!$(".tooltip").is('.fade.in'), 'tooltip is not faded in')
+            start()
+          }, 200)
+        }, 100)
+      })
+
+      test("should not show tooltip if leave event occurs before delay expires, even if hide delay is 0", function () {
+        var tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"></a>')
+          .appendTo('#qunit-fixture')
+          .tooltip({ delay: { show: 200, hide: 0} })
+
+        stop()
+
+        tooltip.trigger('mouseenter')
+
+        setTimeout(function () {
+          ok(!$(".tooltip").is('.fade.in'), 'tooltip is not faded in')
+          tooltip.trigger('mouseout')
+          setTimeout(function () {
+            ok(!$(".tooltip").is('.fade.in'), 'tooltip is not faded in')
+            start()
+          }, 200)
+        }, 100)
+      })
+
+      test("should not show tooltip if leave event occurs before delay expires", function () {
+        var tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"></a>')
+          .appendTo('#qunit-fixture')
+          .tooltip({ delay: 100 })
+        stop()
+        tooltip.trigger('mouseenter')
+        setTimeout(function () {
+          ok(!$(".tooltip").is('.fade.in'), 'tooltip is not faded in')
+          tooltip.trigger('mouseout')
+          setTimeout(function () {
+            ok(!$(".tooltip").is('.fade.in'), 'tooltip is not faded in')
+            start()
+          }, 100)
+        }, 50)
+      })
+
+      test("should show tooltip if leave event hasn't occured before delay expires", function () {
+        var tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"></a>')
+          .appendTo('#qunit-fixture')
+          .tooltip({ delay: 150 })
+        stop()
+        tooltip.trigger('mouseenter')
+        setTimeout(function () {
+          ok(!$(".tooltip").is('.fade.in'), 'tooltip is not faded in')
+        }, 100)
+        setTimeout(function () {
+          ok($(".tooltip").is('.fade.in'), 'tooltip has faded in')
+          start()
+        }, 200)
+      })
+
+      test("should destroy tooltip", function () {
+        var tooltip = $('<div/>').tooltip().on('click.foo', function(){})
+        ok(tooltip.data('tooltip'), 'tooltip has data')
+        ok($._data(tooltip[0], 'events').mouseover && $._data(tooltip[0], 'events').mouseout, 'tooltip has hover event')
+        ok($._data(tooltip[0], 'events').click[0].namespace == 'foo', 'tooltip has extra click.foo event')
+        tooltip.tooltip('show')
+        tooltip.tooltip('destroy')
+        ok(!tooltip.hasClass('in'), 'tooltip is hidden')
+        ok(!$._data(tooltip[0], 'tooltip'), 'tooltip does not have data')
+        ok($._data(tooltip[0], 'events').click[0].namespace == 'foo', 'tooltip still has click.foo')
+        ok(!$._data(tooltip[0], 'events').mouseover && !$._data(tooltip[0], 'events').mouseout, 'tooltip does not have any events')
+      })
+
+      test("should show tooltip with delegate selector on click", function () {
+        var div = $('<div><a href="#" rel="tooltip" title="Another tooltip"></a></div>')
+        var tooltip = div.appendTo('#qunit-fixture')
+                         .tooltip({ selector: 'a[rel=tooltip]',
+                                    trigger: 'click' })
+        div.find('a').trigger('click')
+        ok($(".tooltip").is('.fade.in'), 'tooltip is faded in')
+      })
+})
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-transition.js
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-transition.js b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-transition.js
new file mode 100644
index 0000000..086773f
--- /dev/null
+++ b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-transition.js
@@ -0,0 +1,13 @@
+$(function () {
+
+    module("bootstrap-transition")
+
+      test("should be defined on jquery support object", function () {
+        ok($.support.transition !== undefined, 'transition object is defined')
+      })
+
+      test("should provide an end object", function () {
+        ok($.support.transition ? $.support.transition.end : true, 'end string is defined')
+      })
+
+})
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-typeahead.js
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-typeahead.js b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-typeahead.js
new file mode 100644
index 0000000..4bdbce9
--- /dev/null
+++ b/src/fauxton/jam/bootstrap/js/tests/unit/bootstrap-typeahead.js
@@ -0,0 +1,231 @@
+$(function () {
+
+    module("bootstrap-typeahead")
+
+      test("should provide no conflict", function () {
+        var typeahead = $.fn.typeahead.noConflict()
+        ok(!$.fn.typeahead, 'typeahead was set back to undefined (org value)')
+        $.fn.typeahead = typeahead
+      })
+
+      test("should be defined on jquery object", function () {
+        ok($(document.body).typeahead, 'alert method is defined')
+      })
+
+      test("should return element", function () {
+        ok($(document.body).typeahead()[0] == document.body, 'document.body returned')
+      })
+
+      test("should listen to an input", function () {
+        var $input = $('<input />')
+        $input.typeahead()
+        ok($._data($input[0], 'events').blur, 'has a blur event')
+        ok($._data($input[0], 'events').keypress, 'has a keypress event')
+        ok($._data($input[0], 'events').keyup, 'has a keyup event')
+      })
+
+      test("should create a menu", function () {
+        var $input = $('<input />')
+        ok($input.typeahead().data('typeahead').$menu, 'has a menu')
+      })
+
+      test("should listen to the menu", function () {
+        var $input = $('<input />')
+          , $menu = $input.typeahead().data('typeahead').$menu
+
+        ok($._data($menu[0], 'events').mouseover, 'has a mouseover(pseudo: mouseenter)')
+        ok($._data($menu[0], 'events').click, 'has a click')
+      })
+
+      test("should show menu when query entered", function () {
+        var $input = $('<input />')
+            .appendTo('body')
+            .typeahead({
+              source: ['aa', 'ab', 'ac']
+            })
+          , typeahead = $input.data('typeahead')
+
+        $input.val('a')
+        typeahead.lookup()
+
+        ok(typeahead.$menu.is(":visible"), 'typeahead is visible')
+        equals(typeahead.$menu.find('li').length, 3, 'has 3 items in menu')
+        equals(typeahead.$menu.find('.active').length, 1, 'one item is active')
+
+        $input.remove()
+        typeahead.$menu.remove()
+      })
+
+      test("should accept data source via synchronous function", function () {
+        var $input = $('<input />').typeahead({
+              source: function () {
+                return ['aa', 'ab', 'ac']
+              }
+            }).appendTo('body')
+          , typeahead = $input.data('typeahead')
+
+        $input.val('a')
+        typeahead.lookup()
+
+        ok(typeahead.$menu.is(":visible"), 'typeahead is visible')
+        equals(typeahead.$menu.find('li').length, 3, 'has 3 items in menu')
+        equals(typeahead.$menu.find('.active').length, 1, 'one item is active')
+
+        $input.remove()
+        typeahead.$menu.remove()
+      })
+
+      test("should accept data source via asynchronous function", function () {
+        var $input = $('<input />').typeahead({
+              source: function (query, process) {
+                process(['aa', 'ab', 'ac'])
+              }
+            }).appendTo('body')
+          , typeahead = $input.data('typeahead')
+
+        $input.val('a')
+        typeahead.lookup()
+
+        ok(typeahead.$menu.is(":visible"), 'typeahead is visible')
+        equals(typeahead.$menu.find('li').length, 3, 'has 3 items in menu')
+        equals(typeahead.$menu.find('.active').length, 1, 'one item is active')
+
+        $input.remove()
+        typeahead.$menu.remove()
+      })
+
+      test("should not explode when regex chars are entered", function () {
+        var $input = $('<input />').typeahead({
+              source: ['aa', 'ab', 'ac', 'mdo*', 'fat+']
+            }).appendTo('body')
+          , typeahead = $input.data('typeahead')
+
+        $input.val('+')
+        typeahead.lookup()
+
+        ok(typeahead.$menu.is(":visible"), 'typeahead is visible')
+        equals(typeahead.$menu.find('li').length, 1, 'has 1 item in menu')
+        equals(typeahead.$menu.find('.active').length, 1, 'one item is active')
+
+        $input.remove()
+        typeahead.$menu.remove()
+      })
+
+      test("should hide menu when query entered", function () {
+        stop()
+        var $input = $('<input />').typeahead({
+              source: ['aa', 'ab', 'ac']
+            }).appendTo('body')
+          , typeahead = $input.data('typeahead')
+
+        $input.val('a')
+        typeahead.lookup()
+
+        ok(typeahead.$menu.is(":visible"), 'typeahead is visible')
+        equals(typeahead.$menu.find('li').length, 3, 'has 3 items in menu')
+        equals(typeahead.$menu.find('.active').length, 1, 'one item is active')
+
+        $input.blur()
+
+        setTimeout(function () {
+          ok(!typeahead.$menu.is(":visible"), "typeahead is no longer visible")
+          start()
+        }, 200)
+
+        $input.remove()
+        typeahead.$menu.remove()
+      })
+
+      test("should set next item when down arrow is pressed", function () {
+        var $input = $('<input />').typeahead({
+              source: ['aa', 'ab', 'ac']
+            }).appendTo('body')
+          , typeahead = $input.data('typeahead')
+
+        $input.val('a')
+        typeahead.lookup()
+
+        ok(typeahead.$menu.is(":visible"), 'typeahead is visible')
+        equals(typeahead.$menu.find('li').length, 3, 'has 3 items in menu')
+        equals(typeahead.$menu.find('.active').length, 1, 'one item is active')
+        ok(typeahead.$menu.find('li').first().hasClass('active'), "first item is active")
+
+        // simulate entire key pressing event
+        $input.trigger({
+          type: 'keydown'
+        , keyCode: 40
+        })
+        .trigger({
+          type: 'keypress'
+        , keyCode: 40
+        })
+        .trigger({
+          type: 'keyup'
+        , keyCode: 40
+        })
+
+        ok(typeahead.$menu.find('li').first().next().hasClass('active'), "second item is active")
+
+        $input.trigger({
+          type: 'keydown'
+        , keyCode: 38
+        })
+        .trigger({
+          type: 'keypress'
+        , keyCode: 38
+        })
+        .trigger({
+          type: 'keyup'
+        , keyCode: 38
+        })
+
+        ok(typeahead.$menu.find('li').first().hasClass('active'), "first item is active")
+
+        $input.remove()
+        typeahead.$menu.remove()
+      })
+
+
+      test("should set input value to selected item", function () {
+        var $input = $('<input />').typeahead({
+              source: ['aa', 'ab', 'ac']
+            }).appendTo('body')
+          , typeahead = $input.data('typeahead')
+          , changed = false
+
+        $input.val('a')
+        typeahead.lookup()
+
+        $input.change(function() { changed = true });
+
+        $(typeahead.$menu.find('li')[2]).mouseover().click()
+
+        equals($input.val(), 'ac', 'input value was correctly set')
+        ok(!typeahead.$menu.is(':visible'), 'the menu was hidden')
+        ok(changed, 'a change event was fired')
+
+        $input.remove()
+        typeahead.$menu.remove()
+      })
+
+      test("should start querying when minLength is met", function () {
+        var $input = $('<input />').typeahead({
+              source: ['aaaa', 'aaab', 'aaac'],
+              minLength: 3
+            }).appendTo('body')
+          , typeahead = $input.data('typeahead')
+
+        $input.val('aa')
+        typeahead.lookup()
+
+        equals(typeahead.$menu.find('li').length, 0, 'has 0 items in menu')
+
+        $input.val('aaa')
+        typeahead.lookup()
+
+        equals(typeahead.$menu.find('li').length, 3, 'has 3 items in menu')
+
+        $input.remove()
+        typeahead.$menu.remove()
+      })
+})


Mime
View raw message