From 6803ff70f9203595a04c5adb20d7556cd52c5923 Mon Sep 17 00:00:00 2001
From: Guillaume Gautreau <guillaume+github@ghusse.com>
Date: Sat, 26 Jan 2013 13:50:26 +0100
Subject: [PATCH 1/7] Tooltip positionning on special cases

---
 js/bootstrap-tooltip.js | 65 ++++++++++++++++++++++++++++++++++++++---
 less/tooltip.less       |  9 +++---
 2 files changed, 65 insertions(+), 9 deletions(-)

diff --git a/js/bootstrap-tooltip.js b/js/bootstrap-tooltip.js
index c23d8267a8e7..0d8a20f56834 100644
--- a/js/bootstrap-tooltip.js
+++ b/js/bootstrap-tooltip.js
@@ -152,15 +152,68 @@
             break
         }
 
-        $tip
-          .offset(tp)
-          .addClass(placement)
-          .addClass('in')
+        this.applyPlacement(tp, placement);
 
         this.$element.trigger('shown')
       }
     }
 
+  , applyPlacement: function(offset, placement){
+    var $tip
+      , width
+      , height
+      , actualWidth
+      , actualHeight
+      , delta
+      , replace = false;
+
+    $tip = this.tip();
+
+    width = $tip[0].offsetWidth;
+    height = $tip[0].offsetHeight;
+
+    $tip
+          .offset(offset)
+          .addClass(placement)
+          .addClass('in');
+
+    actualWidth = $tip[0].offsetWidth;
+    actualHeight = $tip[0].offsetHeight;
+
+    if (placement == "top" && actualHeight != actualWidth){
+      offset.top = offset.top + height - actualHeight;
+      replace = true;
+    }
+
+    if (placement == "bottom" || placement == "top"){
+      delta = 0;
+
+      if (offset.left < 0){
+        delta = -offset.left * 2;
+        offset.left = 0;
+        $tip.offset(offset);
+        actualWidth = $tip[0].offsetWidth;
+        actualHeight = $tip[0].offsetHeight;
+      }
+
+      this.replaceArrow(delta - width + actualWidth, actualWidth, "left");
+    }else{
+      this.replaceArrow(actualHeight - height, actualHeight, "top");
+    }
+
+    if (replace) $tip.offset(offset);
+  }
+
+  , replaceArrow: function(delta, dimension, position){
+    var $arrow = this.arrow();
+
+    if (delta !== 0){
+      $arrow.css(position, 50 * (1 - delta / dimension) + "%");
+    }else{
+      $arrow.css(position, "");
+    }
+  }
+
   , setContent: function () {
       var $tip = this.tip()
         , title = this.getTitle()
@@ -233,6 +286,10 @@
       return this.$tip = this.$tip || $(this.options.template)
     }
 
+  , arrow: function(){
+    return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow");
+  }
+
   , validate: function () {
       if (!this.$element[0].parentNode) {
         this.hide()
diff --git a/less/tooltip.less b/less/tooltip.less
index 59b02cd4314c..d5a2bfaba642 100644
--- a/less/tooltip.less
+++ b/less/tooltip.less
@@ -9,15 +9,14 @@
   z-index: @zindexTooltip;
   display: block;
   visibility: visible;
-  padding: 5px;
   font-size: 11px;
   line-height: 1.4;
   .opacity(0);
   &.in     { .opacity(80); }
-  &.top    { margin-top:  -3px; }
-  &.right  { margin-left:  3px; }
-  &.bottom { margin-top:   3px; }
-  &.left   { margin-left: -3px; }
+  &.top    { margin-top:  -3px; padding: 5px 0;}
+  &.right  { margin-left:  3px; padding: 0 5px;}
+  &.bottom { margin-top:   3px; padding: 5px 0;}
+  &.left   { margin-left: -3px; padding: 0 5px;}
 }
 
 // Wrapper for the tooltip content

From 929598784f3a2e4f706d5dcd5a41cea207dfd65e Mon Sep 17 00:00:00 2001
From: Guillaume Gautreau <guillaume+github@ghusse.com>
Date: Sat, 26 Jan 2013 14:06:58 +0100
Subject: [PATCH 2/7] Unit test for replacing tooltip inside window

---
 js/bootstrap-tooltip.js            |  2 +-
 js/tests/unit/bootstrap-tooltip.js | 18 ++++++++++++++++++
 2 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/js/bootstrap-tooltip.js b/js/bootstrap-tooltip.js
index 0d8a20f56834..49c3c6be4294 100644
--- a/js/bootstrap-tooltip.js
+++ b/js/bootstrap-tooltip.js
@@ -190,7 +190,7 @@
 
       if (offset.left < 0){
         delta = -offset.left * 2;
-        offset.left = 0;
+        offset.left = .1;
         $tip.offset(offset);
         actualWidth = $tip[0].offsetWidth;
         actualHeight = $tip[0].offsetHeight;
diff --git a/js/tests/unit/bootstrap-tooltip.js b/js/tests/unit/bootstrap-tooltip.js
index ef21bd96b477..94f40f339f60 100644
--- a/js/tests/unit/bootstrap-tooltip.js
+++ b/js/tests/unit/bootstrap-tooltip.js
@@ -251,4 +251,22 @@ $(function () {
         ok(!$("#qunit-fixture > .tooltip").length, 'not found in parent')
         tooltip.tooltip('hide')
       })
+
+      test("should place tooltip inside window", function(){
+        $("#qunit-fixture").show();
+        var tooltip = $("<a href='#' title='Very very very very very very very very long tooltip'></a>")
+          .css({position: "absolute", top:0, left: 0})
+          .appendTo("#qunit-fixture")
+          .tooltip({placement: "top"})
+          .tooltip("show");
+
+        stop();
+
+        setTimeout(function(){
+          ok($(".tooltip").offset().left >= 0);
+
+          start();
+          $("#qunit-fixture").hide();
+        }, 200)
+      });
 })

From e5be883bb9a5a9e4eb90df7f3d2e24237a3e010a Mon Sep 17 00:00:00 2001
From: Guillaume Gautreau <guillaume+github@ghusse.com>
Date: Sat, 26 Jan 2013 14:31:33 +0100
Subject: [PATCH 3/7] lint + wrong comparison

---
 js/bootstrap-tooltip.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/js/bootstrap-tooltip.js b/js/bootstrap-tooltip.js
index 49c3c6be4294..8b0357744247 100644
--- a/js/bootstrap-tooltip.js
+++ b/js/bootstrap-tooltip.js
@@ -180,7 +180,7 @@
     actualWidth = $tip[0].offsetWidth;
     actualHeight = $tip[0].offsetHeight;
 
-    if (placement == "top" && actualHeight != actualWidth){
+    if (placement == "top" && actualHeight != height){
       offset.top = offset.top + height - actualHeight;
       replace = true;
     }
@@ -190,7 +190,7 @@
 
       if (offset.left < 0){
         delta = -offset.left * 2;
-        offset.left = .1;
+        offset.left = 0.1;
         $tip.offset(offset);
         actualWidth = $tip[0].offsetWidth;
         actualHeight = $tip[0].offsetHeight;

From db9ec13ce4088fc13a7ef7b07662317ea2c435c8 Mon Sep 17 00:00:00 2001
From: Guillaume Gautreau <guillaume+github@ghusse.com>
Date: Sat, 26 Jan 2013 21:39:05 +0100
Subject: [PATCH 4/7] Test for replacing tooltip when resized

---
 js/tests/index.html                 |  3 +++
 js/tests/unit/bootstrap-tooltip.css | 13 +++++++++++
 js/tests/unit/bootstrap-tooltip.js  | 34 ++++++++++++++++++++++++-----
 3 files changed, 44 insertions(+), 6 deletions(-)
 create mode 100644 js/tests/unit/bootstrap-tooltip.css

diff --git a/js/tests/index.html b/js/tests/index.html
index 976ca16872b6..e9ad01d67a69 100644
--- a/js/tests/index.html
+++ b/js/tests/index.html
@@ -29,6 +29,9 @@
   <script src="../../js/bootstrap-typeahead.js"></script>
   <script src="../../js/bootstrap-affix.js"></script>
 
+  <!-- Needed for testing -->
+  <link rel="stylesheet" type="text/css" href="unit/bootstrap-tooltip.css" />
+
   <!-- unit tests -->
   <script src="unit/bootstrap-transition.js"></script>
   <script src="unit/bootstrap-alert.js"></script>
diff --git a/js/tests/unit/bootstrap-tooltip.css b/js/tests/unit/bootstrap-tooltip.css
new file mode 100644
index 000000000000..8614e60d7d01
--- /dev/null
+++ b/js/tests/unit/bootstrap-tooltip.css
@@ -0,0 +1,13 @@
+
+
+.tooltip{
+	position: absolute;
+}
+
+.tooltip-inner{
+	max-width: 200px;
+}
+
+.tooltip.top .tooltip-arrow{
+	position: absolute;
+}
\ No newline at end of file
diff --git a/js/tests/unit/bootstrap-tooltip.js b/js/tests/unit/bootstrap-tooltip.js
index 94f40f339f60..49c034e2ff93 100644
--- a/js/tests/unit/bootstrap-tooltip.js
+++ b/js/tests/unit/bootstrap-tooltip.js
@@ -253,11 +253,12 @@ $(function () {
       })
 
       test("should place tooltip inside window", function(){
-        $("#qunit-fixture").show();
-        var tooltip = $("<a href='#' title='Very very very very very very very very long tooltip'></a>")
+        var container = $("<div />").appendTo("body")
+            .css({position: "absolute", width: 200, height: 200, bottom: 0, left: 0})
+          , tooltip = $("<a href='#' title='Very very very very very very very very long tooltip'>Hover me</a>")
           .css({position: "absolute", top:0, left: 0})
-          .appendTo("#qunit-fixture")
-          .tooltip({placement: "top"})
+          .appendTo(container)
+          .tooltip({placement: "top", animate: false})
           .tooltip("show");
 
         stop();
@@ -266,7 +267,28 @@ $(function () {
           ok($(".tooltip").offset().left >= 0);
 
           start();
-          $("#qunit-fixture").hide();
-        }, 200)
+          container.remove();
+        }, 100)
       });
+
+      test("should place tooltip on top of element", function(){
+        var container = $("<div />").appendTo("body")
+              .css({position: "absolute", bottom: 0, left: 0, textAlign: "right", width: 300, height: 300})
+            , p = $("<p style='margin-top:200px' />").appendTo(container)
+            , tooltiped = $("<a href='#' title='very very very very very very very long tooltip'>Hover me</a>")
+              .css({marginTop: 200})
+              .appendTo(p)
+              .tooltip({placement: "top", animate: false})
+              .tooltip("show");
+
+        stop();
+
+        setTimeout(function(){
+          var tooltip = container.find(".tooltip");
+
+          start();
+          ok(tooltip.offset().top + tooltip.outerHeight() <= tooltiped.offset().top);
+          container.remove();
+        }, 100)
+      })
 })

From d9a0abaa06b4e94ffffc921dcb1119b3bb83f522 Mon Sep 17 00:00:00 2001
From: Guillaume Gautreau <guillaume+github@ghusse.com>
Date: Sat, 26 Jan 2013 21:39:49 +0100
Subject: [PATCH 5/7] works with fixed test

---
 js/bootstrap-tooltip.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/js/bootstrap-tooltip.js b/js/bootstrap-tooltip.js
index 8b0357744247..c3708744e54f 100644
--- a/js/bootstrap-tooltip.js
+++ b/js/bootstrap-tooltip.js
@@ -190,7 +190,7 @@
 
       if (offset.left < 0){
         delta = -offset.left * 2;
-        offset.left = 0.1;
+        offset.left = 0;
         $tip.offset(offset);
         actualWidth = $tip[0].offsetWidth;
         actualHeight = $tip[0].offsetHeight;

From 5b0f956a60d8bd41084a167f83c25e276de11705 Mon Sep 17 00:00:00 2001
From: Guillaume Gautreau <guillaume+github@ghusse.com>
Date: Sat, 26 Jan 2013 21:45:21 +0100
Subject: [PATCH 6/7] Arrow replacement

---
 js/tests/unit/bootstrap-tooltip.js | 23 ++++++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/js/tests/unit/bootstrap-tooltip.js b/js/tests/unit/bootstrap-tooltip.js
index 49c034e2ff93..666c417ee1cf 100644
--- a/js/tests/unit/bootstrap-tooltip.js
+++ b/js/tests/unit/bootstrap-tooltip.js
@@ -290,5 +290,26 @@ $(function () {
           ok(tooltip.offset().top + tooltip.outerHeight() <= tooltiped.offset().top);
           container.remove();
         }, 100)
-      })
+      });
+
+      test("arrow should point to element", function(){
+        var container = $("<div />").appendTo("body")
+            .css({position: "absolute", bottom: 0, left: 0, textAlign: "right", width: 300, height: 300})
+          , p = $("<p style='margin-top:200px' />").appendTo(container)
+          , tooltiped = $("<a href='#' title='very very very very very very very long tooltip'>Hover me</a>")
+            .css({marginTop: 200})
+            .appendTo(p)
+            .tooltip({placement: "top", animate: false})
+            .tooltip("show");
+
+        stop();
+
+        setTimeout(function(){
+          var arrow = container.find(".tooltip-arrow");
+
+          start();
+          ok(Math.abs(arrow.offset().left - tooltiped.offset().left - tooltiped.outerWidth()/2) <= 1);
+          container.remove();
+        }, 100);
+      });
 })

From ee71fb492ffc664627436431c5f7b71d2107a526 Mon Sep 17 00:00:00 2001
From: Guillaume Gautreau <guillaume+github@ghusse.com>
Date: Sun, 27 Jan 2013 10:56:01 +0100
Subject: [PATCH 7/7] No semicolumns

---
 js/bootstrap-tooltip.js            | 46 +++++++++++++++---------------
 js/tests/unit/bootstrap-tooltip.js | 42 +++++++++++++--------------
 2 files changed, 44 insertions(+), 44 deletions(-)

diff --git a/js/bootstrap-tooltip.js b/js/bootstrap-tooltip.js
index c3708744e54f..718f9643ee1a 100644
--- a/js/bootstrap-tooltip.js
+++ b/js/bootstrap-tooltip.js
@@ -152,7 +152,7 @@
             break
         }
 
-        this.applyPlacement(tp, placement);
+        this.applyPlacement(tp, placement)
 
         this.$element.trigger('shown')
       }
@@ -165,52 +165,52 @@
       , actualWidth
       , actualHeight
       , delta
-      , replace = false;
+      , replace = false
 
-    $tip = this.tip();
+    $tip = this.tip()
 
-    width = $tip[0].offsetWidth;
-    height = $tip[0].offsetHeight;
+    width = $tip[0].offsetWidth
+    height = $tip[0].offsetHeight
 
     $tip
           .offset(offset)
           .addClass(placement)
-          .addClass('in');
+          .addClass('in')
 
-    actualWidth = $tip[0].offsetWidth;
-    actualHeight = $tip[0].offsetHeight;
+    actualWidth = $tip[0].offsetWidth
+    actualHeight = $tip[0].offsetHeight
 
     if (placement == "top" && actualHeight != height){
-      offset.top = offset.top + height - actualHeight;
-      replace = true;
+      offset.top = offset.top + height - actualHeight
+      replace = true
     }
 
     if (placement == "bottom" || placement == "top"){
-      delta = 0;
+      delta = 0
 
       if (offset.left < 0){
-        delta = -offset.left * 2;
-        offset.left = 0;
-        $tip.offset(offset);
-        actualWidth = $tip[0].offsetWidth;
-        actualHeight = $tip[0].offsetHeight;
+        delta = -offset.left * 2
+        offset.left = 0
+        $tip.offset(offset)
+        actualWidth = $tip[0].offsetWidth
+        actualHeight = $tip[0].offsetHeight
       }
 
-      this.replaceArrow(delta - width + actualWidth, actualWidth, "left");
+      this.replaceArrow(delta - width + actualWidth, actualWidth, "left")
     }else{
-      this.replaceArrow(actualHeight - height, actualHeight, "top");
+      this.replaceArrow(actualHeight - height, actualHeight, "top")
     }
 
-    if (replace) $tip.offset(offset);
+    if (replace) $tip.offset(offset)
   }
 
   , replaceArrow: function(delta, dimension, position){
-    var $arrow = this.arrow();
+    var $arrow = this.arrow()
 
     if (delta !== 0){
-      $arrow.css(position, 50 * (1 - delta / dimension) + "%");
+      $arrow.css(position, 50 * (1 - delta / dimension) + "%")
     }else{
-      $arrow.css(position, "");
+      $arrow.css(position, "")
     }
   }
 
@@ -287,7 +287,7 @@
     }
 
   , arrow: function(){
-    return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow");
+    return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow")
   }
 
   , validate: function () {
diff --git a/js/tests/unit/bootstrap-tooltip.js b/js/tests/unit/bootstrap-tooltip.js
index 666c417ee1cf..dc4c19bcfd08 100644
--- a/js/tests/unit/bootstrap-tooltip.js
+++ b/js/tests/unit/bootstrap-tooltip.js
@@ -259,17 +259,17 @@ $(function () {
           .css({position: "absolute", top:0, left: 0})
           .appendTo(container)
           .tooltip({placement: "top", animate: false})
-          .tooltip("show");
+          .tooltip("show")
 
-        stop();
+        stop()
 
         setTimeout(function(){
-          ok($(".tooltip").offset().left >= 0);
+          ok($(".tooltip").offset().left >= 0)
 
-          start();
-          container.remove();
+          start()
+          container.remove()
         }, 100)
-      });
+      })
 
       test("should place tooltip on top of element", function(){
         var container = $("<div />").appendTo("body")
@@ -279,18 +279,18 @@ $(function () {
               .css({marginTop: 200})
               .appendTo(p)
               .tooltip({placement: "top", animate: false})
-              .tooltip("show");
+              .tooltip("show")
 
-        stop();
+        stop()
 
         setTimeout(function(){
-          var tooltip = container.find(".tooltip");
+          var tooltip = container.find(".tooltip")
 
-          start();
-          ok(tooltip.offset().top + tooltip.outerHeight() <= tooltiped.offset().top);
-          container.remove();
+          start()
+          ok(tooltip.offset().top + tooltip.outerHeight() <= tooltiped.offset().top)
+          container.remove()
         }, 100)
-      });
+      })
 
       test("arrow should point to element", function(){
         var container = $("<div />").appendTo("body")
@@ -300,16 +300,16 @@ $(function () {
             .css({marginTop: 200})
             .appendTo(p)
             .tooltip({placement: "top", animate: false})
-            .tooltip("show");
+            .tooltip("show")
 
-        stop();
+        stop()
 
         setTimeout(function(){
-          var arrow = container.find(".tooltip-arrow");
+          var arrow = container.find(".tooltip-arrow")
 
-          start();
-          ok(Math.abs(arrow.offset().left - tooltiped.offset().left - tooltiped.outerWidth()/2) <= 1);
-          container.remove();
-        }, 100);
-      });
+          start()
+          ok(Math.abs(arrow.offset().left - tooltiped.offset().left - tooltiped.outerWidth()/2) <= 1)
+          container.remove()
+        }, 100)
+      })
 })