diff --git a/src/Umbraco.Web.UI.Client/bower.json b/src/Umbraco.Web.UI.Client/bower.json
index a1def1a7ff..9a6b26aeaf 100644
--- a/src/Umbraco.Web.UI.Client/bower.json
+++ b/src/Umbraco.Web.UI.Client/bower.json
@@ -25,7 +25,6 @@
"jquery-ui": "1.10.3",
"angular-dynamic-locale": "~0.1.27",
"tinymce": "~4.1.10",
- "bootstrap-tabdrop": "~1.0.0",
"codemirror": "~5.3.0"
}
}
diff --git a/src/Umbraco.Web.UI.Client/lib/bootstrap-tabdrop/README.md b/src/Umbraco.Web.UI.Client/lib/bootstrap-tabdrop/README.md
new file mode 100644
index 0000000000..e8d266fb24
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/lib/bootstrap-tabdrop/README.md
@@ -0,0 +1,62 @@
+bootstrap-tabdrop
+=================
+
+*****************************************************************
+NOTE: THIS IS A CUSTOM FIXED VERSION!!!!!!!!!!!!!!!!!!!!!!
+- THE ORIGINAL HAS A MEMORY LEAK, SO WE'VE HAD TO EMBED THIS
+ INTO THE CORE WITH THE FIX
+
+--- UMBRACO CORE TEAM
+*****************************************************************
+
+A dropdown tab tool for @twitter bootstrap forked from Stefan Petre's (of eyecon.ro),
+
+The dropdown tab appears when your tabs do not all fit in the same row.
+
+Original site and examples: http://www.eyecon.ro/bootstrap-tabdrop/
+
+Added functionality: Displays the text of an active tab selected from the dropdown list instead of the text option on the dropdown tab.
+
+
+## Requirements
+
+* [Bootstrap](http://twitter.github.com/bootstrap/) 2.0.4+
+* [jQuery](http://jquery.com/) 1.7.1+
+
+## Example
+
+No additional HTML needed - the script adds it when the dropdown tab is needed.
+
+Using bootstrap-tabdrop.js
+Call the tab drop via javascript on .nav-tabs and .nav-pills:
+```js
+$('.nav-pills, .nav-tabs').tabdrop()
+```
+
+### Options
+
+#### text
+Type: string
+Default: icon
+```html
+
+```
+To change the default value, call
+```javascript
+.tabdrop({text: "your text here"});
+```
+when initalizing the tabdrop. The displayed value will change when a tab is selected from the dropdown list.
+
+### Methods
+
+```js
+.tabdrop(options)
+```
+
+Initializes an tab drop.
+
+```js
+.tabdrop('layout')
+```
+
+Checks if the tabs fit in one single row.
diff --git a/src/Umbraco.Web.UI.Client/lib/bootstrap-tabdrop/bootstrap-tabdrop.js b/src/Umbraco.Web.UI.Client/lib/bootstrap-tabdrop/bootstrap-tabdrop.js
new file mode 100644
index 0000000000..ede04bfc6b
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/lib/bootstrap-tabdrop/bootstrap-tabdrop.js
@@ -0,0 +1,132 @@
+/* =========================================================
+ * bootstrap-tabdrop.js
+ * http://www.eyecon.ro/bootstrap-tabdrop
+ * =========================================================
+ * Copyright 2012 Stefan Petre
+ *
+ * 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.
+ * ========================================================= */
+
+/*****************************************************************
+ * NOTE: THIS IS A CUSTOM FIXED VERSION!!!!!!!!!!!!!!!!!!!!!!
+ * - THE ORIGINAL HAS A MEMORY LEAK, SO WE'VE HAD TO EMBED THIS
+ * INTO THE CORE WITH THE FIX
+ *
+ * --- UMBRACO CORE TEAM
+ *****************************************************************/
+
+!function( $ ) {
+
+ var WinReszier = (function(){
+ var registered = [];
+ var inited = false;
+ var timer;
+ var resize = function(ev) {
+ clearTimeout(timer);
+ timer = setTimeout(notify, 100);
+ };
+ var notify = function() {
+ for(var i=0, cnt=registered.length; i -1) {
+ registered.splice(index, 1);
+ }
+ }
+ };
+ }());
+
+ var TabDrop = function(element, options) {
+ this.element = $(element);
+ this.dropdown = $('' +
+ '' +
+ options.text + ' ' +
+ '').prependTo(this.element);
+ if (this.element.parent().is('.tabs-below')) {
+ this.dropdown.addClass('dropup');
+ }
+ this.resizeCallback = $.proxy(this.layout, this);
+ WinReszier.register(this.resizeCallback);
+ this.layout();
+ };
+
+ TabDrop.prototype = {
+ constructor: TabDrop,
+
+ layout: function() {
+ var collection = [];
+ this.dropdown.removeClass('hide');
+ this.element
+ .append(this.dropdown.find('li'))
+ .find('>li')
+ .not('.tabdrop')
+ .each(function(){
+ if(this.offsetTop > 0) {
+ collection.push(this);
+ }
+ });
+ if (collection.length > 0) {
+ collection = $(collection);
+ this.dropdown
+ .find('ul')
+ .empty()
+ .append(collection);
+ if (this.dropdown.find('.active').length == 1) {
+ this.dropdown.addClass('active');
+ } else {
+ this.dropdown.removeClass('active');
+ }
+ } else {
+ this.dropdown.addClass('hide');
+ }
+ },
+
+ destroy: function() {
+ this.dropdown.html();
+ WinReszier.unregister(this.resizeCallback);
+ }
+ };
+
+ $.fn.tabdrop = function ( option ) {
+ return this.each(function () {
+ var $this = $(this),
+ data = $this.data('tabdrop'),
+ options = typeof option === 'object' && option;
+ if (!data) {
+ $this.data('tabdrop', (data = new TabDrop(this, $.extend({},
+ $.fn.tabdrop.defaults,options))));
+ }
+ if (typeof option == 'string') {
+ data[option]();
+ }
+ });
+ };
+
+ $.fn.tabdrop.defaults = {
+ text: ''
+ };
+
+ $.fn.tabdrop.Constructor = TabDrop;
+
+}(window.jQuery);
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/html/umbheader.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/html/umbheader.directive.js
index 439f27e5ee..8a13a3c714 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/html/umbheader.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/html/umbheader.directive.js
@@ -19,51 +19,13 @@ angular.module("umbraco.directives")
tabs: "="
},
link: function (scope, iElement, iAttrs) {
-
- var maxTabs = 4;
-
- function collectFromDom(activeTab){
- var $panes = $('div.tab-content');
-
- angular.forEach($panes.find('.tab-pane'), function (pane, index) {
- var $this = angular.element(pane);
-
- var id = $this.attr("rel");
- var label = $this.attr("label");
- var tab = {id: id, label: label, active: false};
- if(!activeTab){
- tab.active = true;
- activeTab = tab;
- }
-
- if ($this.attr("rel") === String(activeTab.id)) {
- $this.addClass('active');
- }
- else {
- $this.removeClass('active');
- }
-
- if(label){
- scope.visibleTabs.push(tab);
- }
-
- });
-
- //TODO: We'll need to destroy this I'm assuming!
- iElement.find('.nav-pills, .nav-tabs').tabdrop();
- }
-
+
scope.showTabs = iAttrs.tabs ? true : false;
scope.visibleTabs = [];
- scope.overflownTabs = [];
- $timeout(function () {
- collectFromDom(undefined);
- }, 500);
-
- //when the tabs change, we need to hack the planet a bit and force the first tab content to be active,
- //unfortunately twitter bootstrap tabs is not playing perfectly with angular.
- scope.$watch("tabs", function (newValue, oldValue) {
+ //since tabs are loaded async, we need to put a watch on them to determine
+ // when they are loaded, then we can close the watch
+ var tabWatch = scope.$watch("tabs", function (newValue, oldValue) {
angular.forEach(newValue, function(val, index){
var tab = {id: val.id, label: val.label};
@@ -73,16 +35,25 @@ angular.module("umbraco.directives")
//don't process if we cannot or have already done so
if (!newValue) {return;}
if (!newValue.length || newValue.length === 0){return;}
-
- var activeTab = _.find(newValue, function (item) {
- return item.active;
- });
-
+
//we need to do a timeout here so that the current sync operation can complete
// and update the UI, then this will fire and the UI elements will be available.
$timeout(function () {
- collectFromDom(activeTab);
- }, 500);
+
+ //use bootstrap tabs API to show the first one
+ iElement.find(".nav-tabs a:first").tab('show');
+
+ //enable the tab drop
+ iElement.find('.nav-pills, .nav-tabs').tabdrop();
+
+ //ensure to destroy tabdrop (unbinds window resize listeners)
+ scope.$on('$destroy', function () {
+ iElement.find('.nav-pills, .nav-tabs').tabdrop("destroy");
+ });
+
+ //stop watching now
+ tabWatch();
+ });
});
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/util/delayedMouseLeave.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/util/delayedMouseLeave.directive.js
index 94d5925f2c..28a90f9041 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/util/delayedMouseLeave.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/util/delayedMouseLeave.directive.js
@@ -5,21 +5,31 @@ angular.module("umbraco.directives")
link: function (scope, element, attrs, ctrl) {
var active = false;
var fn = $parse(attrs.delayedMouseleave);
- element.on("mouseleave", function(event) {
- var callback = function() {
- fn(scope, {$event:event});
+
+ function mouseLeave(event) {
+ var callback = function () {
+ fn(scope, { $event: event });
};
active = false;
- $timeout(function(){
- if(active === false){
+ $timeout(function () {
+ if (active === false) {
scope.$apply(callback);
}
}, 650);
- });
+ }
- element.on("mouseenter", function(event, args){
+ function mouseEnter(event, args){
active = true;
+ }
+
+ element.on("mouseleave", mouseLeave);
+ element.on("mouseenter", mouseEnter);
+
+ //unbind!!
+ scope.$on('$destroy', function () {
+ element.off("mouseleave", mouseLeave);
+ element.off("mouseenter", mouseEnter);
});
}
};
diff --git a/src/Umbraco.Web.UI.Client/src/views/directives/umb-header.html b/src/Umbraco.Web.UI.Client/src/views/directives/umb-header.html
index f7798bba09..016055b84a 100644
--- a/src/Umbraco.Web.UI.Client/src/views/directives/umb-header.html
+++ b/src/Umbraco.Web.UI.Client/src/views/directives/umb-header.html
@@ -5,7 +5,7 @@
- -
+
-
{{ tab.label }}
diff --git a/src/Umbraco.Web/UI/JavaScript/JsInitialize.js b/src/Umbraco.Web/UI/JavaScript/JsInitialize.js
index f021d9c841..cec31154d8 100644
--- a/src/Umbraco.Web/UI/JavaScript/JsInitialize.js
+++ b/src/Umbraco.Web/UI/JavaScript/JsInitialize.js
@@ -20,7 +20,7 @@
'lib/jquery-file-upload/jquery.fileupload-angular.js',
'lib/bootstrap/js/bootstrap.2.3.2.min.js',
- 'lib/bootstrap-tabdrop/bootstrap-tabdrop.min.js',
+ 'lib/bootstrap-tabdrop/bootstrap-tabdrop.js',
'lib/umbraco/Extensions.js',
'lib/umbraco/NamespaceManager.js',