diff --git a/public/app/partials/signup_invited.html b/public/app/partials/signup_invited.html
new file mode 100644
index 00000000000..8fe4569c3a3
--- /dev/null
+++ b/public/app/partials/signup_invited.html
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+

+
+
+
+
+ Hello {{greeting}}.
+
+
+
+ {{invitedBy}} has invited you to join Grafana and the organization {{contextSrv.user.orgName}}Please complete the following to accept your invitation and continue:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/app/routes/all.js b/public/app/routes/all.js
index 026eccc1d45..0adca374452 100644
--- a/public/app/routes/all.js
+++ b/public/app/routes/all.js
@@ -97,6 +97,10 @@ define([
templateUrl: 'app/partials/login.html',
controller : 'LoginCtrl',
})
+ .when('/invite/:code', {
+ templateUrl: 'app/partials/signup_invited.html',
+ controller : 'InvitedCtrl',
+ })
.when('/user/password/send-reset-email', {
templateUrl: 'app/partials/reset_password.html',
controller : 'ResetPasswordCtrl',
diff --git a/public/app/services/alertSrv.js b/public/app/services/alertSrv.js
index 4a8ef273523..88885f4bd78 100644
--- a/public/app/services/alertSrv.js
+++ b/public/app/services/alertSrv.js
@@ -71,7 +71,7 @@ function (angular, _) {
var confirmModal = $modal({
template: './app/partials/confirm_modal.html',
persist: false,
- modalClass: 'confirm-modal',
+ modalClass: 'modal-no-header confirm-modal',
show: false,
scope: scope,
keyboard: false
diff --git a/public/app/services/utilSrv.js b/public/app/services/utilSrv.js
index 7fca0c0334e..12aa518e24c 100644
--- a/public/app/services/utilSrv.js
+++ b/public/app/services/utilSrv.js
@@ -14,6 +14,7 @@ function (angular) {
this.showModal = function(e, options) {
var modal = $modal({
+ modalClass: options.modalClass,
template: options.src,
persist: false,
show: false,
diff --git a/public/css/less/bootswatch.dark.less b/public/css/less/bootswatch.dark.less
index bb216b38c2d..4d83d04199c 100644
--- a/public/css/less/bootswatch.dark.less
+++ b/public/css/less/bootswatch.dark.less
@@ -220,6 +220,7 @@ div.subnav {
li > a:hover,
li.active > a,
+ li.active > a:focus,
li.active > a:hover {
border-color: transparent;
background-color: transparent;
@@ -547,8 +548,6 @@ a:hover {
}
.modal {
- .border-radius(1px);
- border-top: solid 1px lighten(@grayDark, 5%);
background-color: @grafanaPanelBackground;
}
diff --git a/public/css/less/bootswatch.light.less b/public/css/less/bootswatch.light.less
index a369be152c7..fd3c00cabd3 100644
--- a/public/css/less/bootswatch.light.less
+++ b/public/css/less/bootswatch.light.less
@@ -159,6 +159,7 @@ div.subnav {
li > a:hover,
li.active > a,
+ li.active > a:focus,
li.active > a:hover {
border-color: transparent;
background-color: transparent;
@@ -531,19 +532,6 @@ a.thumbnail {
.border-radius(0);
}
-.modal {
- .border-radius(0);
- background-color: @bodyBackground;
- &-header {
- border-bottom: none;
- }
-
- &-footer {
- border-top: none;
- background-color: transparent;
- }
-}
-
.popover {
.border-radius(0);
diff --git a/public/css/less/gfbox.less b/public/css/less/gfbox.less
index 90bb3ccb36a..eb386f740d7 100644
--- a/public/css/less/gfbox.less
+++ b/public/css/less/gfbox.less
@@ -67,12 +67,6 @@
position: relative;
border: @grafanaPanelBorder;
padding: 20px 20px 60px 49px;
-
- h2 {
- color: @textColor;
- font-weight: normal;
- font-size: 22px;
- }
}
.page {
diff --git a/public/css/less/grafana.less b/public/css/less/grafana.less
index 4f7eeeeb18d..3062ece1391 100644
--- a/public/css/less/grafana.less
+++ b/public/css/less/grafana.less
@@ -1,3 +1,4 @@
+@import "type.less";
@import "login.less";
@import "submenu.less";
@import "graph.less";
@@ -15,6 +16,7 @@
@import "admin.less";
@import "validation.less";
@import "fonts.less";
+@import "tabs.less";
.row-control-inner {
padding:0px;
@@ -257,18 +259,28 @@
td:first-child { text-align: right; }
}
-.confirm-modal {
+.modal-no-header {
border: 1px solid @grafanaTargetFuncBackground;
- max-width: 500px;
- background-color: @grafanaPanelBackground;
text-align: center;
+ h3 {
+ margin-top: 30px;
+ }
+
.modal-close {
float: right;
font-size: 140%;
padding: 10px;
}
+ .modal-tagline {
+ font-size: 16px;
+ }
+}
+
+.confirm-modal {
+ max-width: 500px;
+
.confirm-modal-icon {
padding-top: 41px;
font-size: 280%;
@@ -282,10 +294,6 @@
margin-bottom: 15px;
}
- .confirm-modal-text {
- font-size: 16px;
- }
-
.confirm-modal-buttons {
margin-top: 35px;
margin-bottom: 35px;
@@ -354,5 +362,27 @@
color: @orange;
}
+.highlight-word {
+ color: @orange;
+}
+.body-copy-emphasis {
+ color: @headingsColor;
+}
+.signup-page-container {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ height: 100%;
+ width: 100%;
+ background-image: url(/img/background_tease.jpg);
+
+ .signup-logo-container {
+ width: 150px;
+ margin: 0 auto;
+ padding: 80px 0;
+ }
+}
diff --git a/public/css/less/login.less b/public/css/less/login.less
index 7fa677c6a78..5de9ebc1430 100644
--- a/public/css/less/login.less
+++ b/public/css/less/login.less
@@ -93,4 +93,46 @@
}
}
+.login-page-background {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ height: 100%;
+ width: 100%;
+ background-image: url(/img/background_tease.jpg);
+ opacity: 0.05;
+ z-index: -1;
+}
+.invite-box {
+ text-align: center;
+ border: 1px solid @grafanaTargetFuncBackground;
+ background-color: @grafanaPanelBackground;
+ position: fixed;
+ max-width: 800px;
+ left: 0;
+ right: 0;
+ margin-left: auto;
+ margin-right: auto;
+ top: 20%;
+
+ .tight-form {
+ text-align: left;
+ }
+
+ h3 {
+ margin-top: 30px;
+ }
+
+ .modal-close {
+ float: right;
+ font-size: 140%;
+ padding: 10px;
+ }
+
+ .modal-tagline {
+ font-size: 16px;
+ }
+}
diff --git a/public/css/less/tables_lists.less b/public/css/less/tables_lists.less
index 8582ec18f99..f0c7275ffe1 100644
--- a/public/css/less/tables_lists.less
+++ b/public/css/less/tables_lists.less
@@ -33,23 +33,11 @@
white-space: nowrap;
}
-.grafana-options-list {
- list-style: none;
- margin: 0;
- max-width: 450px;
-
- li:nth-child(odd) {
- background-color: @grafanaListAccent;
- }
-
- li {
- float: left;
- margin: 2px;
- padding: 5px 10px;
- border: 1px solid @grafanaListBorderBottom;
- border: 1px solid @grafanaListBorderBottom;
- }
- li:first-child {
- border: 1px solid @grafanaListBorderBottom;
- }
+.grafana-list-item {
+ display: block;
+ padding: 1px 10px;
+ line-height: 34px;
+ background-color: @grafanaTargetBackground;
+ margin-bottom: 4px;
+ cursor: pointer;
}
diff --git a/public/css/less/tabs.less b/public/css/less/tabs.less
new file mode 100644
index 00000000000..973f817af06
--- /dev/null
+++ b/public/css/less/tabs.less
@@ -0,0 +1,33 @@
+
+.nav-tabs-alt {
+ border-bottom: @grafanaTriggerBorder;
+ padding-left: 10px;
+
+ & > li > a {
+ color: darken(@linkColor, 20%);
+ }
+
+ li > a:hover {
+ border-bottom: none;
+ }
+
+ li.active > a,
+ li.active > a:focus,
+ li.active > a:hover {
+ .border-radius(3px);
+ border: @grafanaTriggerBorder;
+ background-color: transparent;
+ border-bottom: 1px solid @grafanaPanelBackground;
+ color: @linkColor;
+ }
+
+ li.disabled > a {
+ color: @textColor;
+ }
+
+ .open .dropdown-toggle {
+ background-color: #060606;
+ border-color: transparent;
+ }
+}
+
diff --git a/public/css/less/tightform.less b/public/css/less/tightform.less
index 71457a62141..a85e852bcbc 100644
--- a/public/css/less/tightform.less
+++ b/public/css/less/tightform.less
@@ -165,7 +165,7 @@ select.tight-form-input {
margin: 0px;
border-radius: 0;
height: 36px;
- padding: 8px 3px;
+ padding: 9px 3px;
&.last {
border-right: none;
}
diff --git a/public/css/less/type.less b/public/css/less/type.less
new file mode 100644
index 00000000000..1d833792484
--- /dev/null
+++ b/public/css/less/type.less
@@ -0,0 +1,247 @@
+//
+// Typography
+// --------------------------------------------------
+
+
+// Body text
+// -------------------------
+
+p {
+ margin: 0 0 @baseLineHeight / 2;
+}
+.lead {
+ margin-bottom: @baseLineHeight;
+ font-size: @baseFontSize * 1.5;
+ font-weight: 200;
+ line-height: @baseLineHeight * 1.5;
+}
+
+
+// Emphasis & misc
+// -------------------------
+
+// Ex: 14px base font * 85% = about 12px
+small { font-size: 85%; }
+
+strong { font-weight: 500; }
+em { font-style: italic; color: @headingsColor; }
+cite { font-style: normal; }
+
+// Utility classes
+.muted { color: @grayLight; }
+a.muted:hover,
+a.muted:focus { color: darken(@grayLight, 10%); }
+
+.text-warning { color: @warningText; }
+a.text-warning:hover,
+a.text-warning:focus { color: darken(@warningText, 10%); }
+
+.text-error { color: @errorText; }
+a.text-error:hover,
+a.text-error:focus { color: darken(@errorText, 10%); }
+
+.text-info { color: @infoText; }
+a.text-info:hover,
+a.text-info:focus { color: darken(@infoText, 10%); }
+
+.text-success { color: @successText; }
+a.text-success:hover,
+a.text-success:focus { color: darken(@successText, 10%); }
+
+.text-left { text-align: left; }
+.text-right { text-align: right; }
+.text-center { text-align: center; }
+
+
+// Headings
+// -------------------------
+
+h1, h2, h3, h4, h5, h6 {
+ margin: (@baseLineHeight / 2) 0;
+ font-family: @headingsFontFamily;
+ font-weight: @headingsFontWeight;
+ line-height: @baseLineHeight;
+ color: @headingsColor;
+ text-rendering: optimizelegibility; // Fix the character spacing for headings
+ small {
+ font-weight: normal;
+ line-height: 1;
+ color: @grayLight;
+ }
+}
+
+h1,
+h2,
+h3 { line-height: @baseLineHeight * 2; }
+
+h1 { font-size: @baseFontSize * 2.00; } // ~38px
+h2 { font-size: @baseFontSize * 1.75; } // ~32px
+h3 { font-size: @baseFontSize * 1.50; } // ~24px
+h4 { font-size: @baseFontSize * 1.25; } // ~18px
+h5 { font-size: @baseFontSize; }
+h6 { font-size: @baseFontSize * 0.85; } // ~12px
+
+h1 small { font-size: @baseFontSize * 1.75; } // ~24px
+h2 small { font-size: @baseFontSize * 1.25; } // ~18px
+h3 small { font-size: @baseFontSize; }
+h4 small { font-size: @baseFontSize; }
+
+
+// Page header
+// -------------------------
+
+.page-header {
+ padding-bottom: (@baseLineHeight / 2) - 1;
+ margin: @baseLineHeight 0 (@baseLineHeight * 1.5);
+ border-bottom: 1px solid @grayLighter;
+}
+
+
+
+// Lists
+// --------------------------------------------------
+
+// Unordered and Ordered lists
+ul, ol {
+ padding: 0;
+ margin: 0 0 @baseLineHeight / 2 25px;
+}
+ul ul,
+ul ol,
+ol ol,
+ol ul {
+ margin-bottom: 0;
+}
+li {
+ line-height: @baseLineHeight;
+}
+
+// Remove default list styles
+ul.unstyled,
+ol.unstyled {
+ margin-left: 0;
+ list-style: none;
+}
+
+// Single-line list items
+ul.inline,
+ol.inline {
+ margin-left: 0;
+ list-style: none;
+ > li {
+ display: inline-block;
+ .ie7-inline-block();
+ padding-left: 5px;
+ padding-right: 5px;
+ }
+}
+
+// Description Lists
+dl {
+ margin-bottom: @baseLineHeight;
+}
+dt,
+dd {
+ line-height: @baseLineHeight;
+}
+dt {
+ font-weight: bold;
+}
+dd {
+ margin-left: @baseLineHeight / 2;
+}
+// Horizontal layout (like forms)
+.dl-horizontal {
+ .clearfix(); // Ensure dl clears floats if empty dd elements present
+ dt {
+ float: left;
+ width: @horizontalComponentOffset - 20;
+ clear: left;
+ text-align: right;
+ .text-overflow();
+ }
+ dd {
+ margin-left: @horizontalComponentOffset;
+ }
+}
+
+// MISC
+// ----
+
+// Horizontal rules
+hr {
+ margin: @baseLineHeight 0;
+ border: 0;
+ border-top: 1px solid @hrBorder;
+ border-bottom: 1px solid @white;
+}
+
+// Abbreviations and acronyms
+abbr[title],
+// Added data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257
+abbr[data-original-title] {
+ cursor: help;
+ border-bottom: 1px dotted @grayLight;
+}
+abbr.initialism {
+ font-size: 90%;
+ text-transform: uppercase;
+}
+
+// Blockquotes
+blockquote {
+ padding: 0 0 0 15px;
+ margin: 0 0 @baseLineHeight;
+ border-left: 5px solid @grayLighter;
+ p {
+ margin-bottom: 0;
+ font-size: @baseFontSize * 1.25;
+ font-weight: 300;
+ line-height: 1.25;
+ }
+ small {
+ display: block;
+ line-height: @baseLineHeight;
+ color: @grayLight;
+ &:before {
+ content: '\2014 \00A0';
+ }
+ }
+
+ // Float right with text-align: right
+ &.pull-right {
+ float: right;
+ padding-right: 15px;
+ padding-left: 0;
+ border-right: 5px solid @grayLighter;
+ border-left: 0;
+ p,
+ small {
+ text-align: right;
+ }
+ small {
+ &:before {
+ content: '';
+ }
+ &:after {
+ content: '\00A0 \2014';
+ }
+ }
+ }
+}
+
+// Quotes
+q:before,
+q:after,
+blockquote:before,
+blockquote:after {
+ content: "";
+}
+
+// Addresses
+address {
+ display: block;
+ margin-bottom: @baseLineHeight;
+ font-style: normal;
+ line-height: @baseLineHeight;
+}
diff --git a/public/css/less/variables.dark.less b/public/css/less/variables.dark.less
index bc49b66cd74..9614d61ba74 100644
--- a/public/css/less/variables.dark.less
+++ b/public/css/less/variables.dark.less
@@ -69,8 +69,8 @@
@altFontFamily: @serifFontFamily;
@headingsFontFamily: inherit; // empty to use BS default, @baseFontFamily
-@headingsFontWeight: bold; // instead of browser default, bold
-@headingsColor: @textColor; // empty to use BS default, @textColor
+@headingsFontWeight: normal; // instead of browser default, bold
+@headingsColor: darken(@white,11%); // empty to use BS default, @textColor
@inputText: @black;
@@ -98,6 +98,8 @@
@grafanaListHighlight: #333;
@grafanaListMainLinkColor: @textColor;
+@pageContainerBorderColor: @grayDark;
+
// Scrollbars
@scrollbarBackground: #3a3a3a;
@scrollbarBackground2: #3a3a3a;
diff --git a/public/css/less/variables.light.less b/public/css/less/variables.light.less
index 140e0be3341..5025dfa63ea 100644
--- a/public/css/less/variables.light.less
+++ b/public/css/less/variables.light.less
@@ -33,9 +33,9 @@
// grafana Variables
// -------------------------
-@grafanaPanelBackground: @white;
-@grafanaPanelBorder: solid 1px #ddd;
-@grafanaTriggerBorder: solid 1px @grayLighter;
+@grafanaPanelBackground: @grayLighter;
+@grafanaPanelBorder: solid 1px #ddd;
+@grafanaTriggerBorder: solid 1px @grayLight;
// Submenu
@submenuBackground: rgb(218, 217, 217);
@@ -58,16 +58,14 @@
// Scaffolding
// -------------------------
-@bodyBackground: #EAEAEA;
+@bodyBackground: #EFEFEF;
@textColor: @gray;
-
// Links
// -------------------------
@linkColor: @gray;
@linkColorDisabled: lighten(@linkColor,30%);
-@linkColorHover: @grayDarker;
-
+@linkColorHover: darken(@linkColor, 20%);
// Typography
// -------------------------
@@ -76,14 +74,14 @@
@monoFontFamily: Menlo, Monaco, Consolas, "Courier New", monospace;
@baseFontSize: 14px;
-@baseFontWeight: 400;
+@baseFontWeight: 400;
@baseFontFamily: @sansFontFamily;
@baseLineHeight: 20px;
@altFontFamily: @serifFontFamily;
@headingsFontFamily: inherit; // empty to use BS default, @baseFontFamily
-@headingsFontWeight: bold; // instead of browser default, bold
-@headingsColor: @grayDarker; // empty to use BS default, @textColor
+@headingsFontWeight: normal; // instead of browser default, bold
+@headingsColor: @textColor; // empty to use BS default, @textColor
// Component sizing
@@ -111,6 +109,7 @@
@grafanaListHighlightContrast: #ddd;
@grafanaListMainLinkColor: @textColor;
+@pageContainerBorderColor: darken(@grafanaTargetBackground, 5%);
// Tables
diff --git a/public/emails/README.md b/public/emails/README.md
new file mode 100644
index 00000000000..18ae81728e3
--- /dev/null
+++ b/public/emails/README.md
@@ -0,0 +1,2 @@
+
+html files in this folder are generated from templates and build system in repo_root/emails
diff --git a/public/emails/invited_to_org.html b/public/emails/invited_to_org.html
new file mode 100644
index 00000000000..fb61438b441
--- /dev/null
+++ b/public/emails/invited_to_org.html
@@ -0,0 +1,198 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{{Subject .Subject "{{.InvitedBy}} has added you to the Grafana organization {{.OrgName}}"}}
+
+
+
+
+
+
+
+
+ You have been added to the Grafana organization {{.OrgName}}
+ |
+ |
+
+
+
+ |
+
+
+
+
+
+
+
+
+ |
+ You can switch organization in the left side menu, in the dropdown below your username.
+ |
+ |
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+
diff --git a/public/emails/new_user_invite.html b/public/emails/new_user_invite.html
new file mode 100644
index 00000000000..aeadbb98d7b
--- /dev/null
+++ b/public/emails/new_user_invite.html
@@ -0,0 +1,204 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{{Subject .Subject "{{.InvitedBy}} has invited you to join Grafana"}}
+
+
+
+
+
+
+
+
+ You're invited to sign up to Grafana and join organization {{.OrgName}}
+ |
+ |
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+
diff --git a/public/emails/reset_password.html b/public/emails/reset_password.html
index 56d2ecbd3af..3aed7f76c69 100644
--- a/public/emails/reset_password.html
+++ b/public/emails/reset_password.html
@@ -1,71 +1,203 @@
-{{Subject .Subject "Reset your Grafana password - {{.Name}}"}}
-
-
-
-
-
-
-
-
-
+
diff --git a/public/emails/welcome_on_signup.html b/public/emails/welcome_on_signup.html
index 4b7ae73ef30..ed9df33f680 100644
--- a/public/emails/welcome_on_signup.html
+++ b/public/emails/welcome_on_signup.html
@@ -1,69 +1,197 @@
-{{Subject .Subject "Welcome to Grafana"}}
+
+
+
+
+
-
-
-
-
-
-
-
{{.CheckType}} {{ .State }} for {{.Endpoint}}"}}
-
-
+
+
+
+
-
-
-
+
+
+
+ {{Subject .Subject "Welcome to Grafana"}}
+
+
+
+
+
+
+
+
+ Hi {{.Name}}
+ |
+ |
+
+
+
+ |
+
+
+
+
+
+
+
+
+ |
+
+ If you are new to Grafana please read the Getting Started guide.
+
+ |
+ |
+
+
+ |
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+
diff --git a/public/img/background_tease.jpg b/public/img/background_tease.jpg
new file mode 100644
index 00000000000..043ad7f8976
Binary files /dev/null and b/public/img/background_tease.jpg differ
diff --git a/public/test/test-main.js b/public/test/test-main.js
index e6e18512216..946cc6aad77 100644
--- a/public/test/test-main.js
+++ b/public/test/test-main.js
@@ -22,6 +22,7 @@ require.config({
'angular-sanitize': '../vendor/angular-sanitize/angular-sanitize',
angularMocks: '../vendor/angular-mocks/angular-mocks',
'angular-dragdrop': '../vendor/angular-native-dragdrop/draganddrop',
+ 'angular-ui': '../vendor/angular-ui/angular-bootstrap',
'angular-strap': '../vendor/angular-other/angular-strap',
timepicker: '../vendor/angular-other/timepicker',
datepicker: '../vendor/angular-other/datepicker',
@@ -83,6 +84,7 @@ require.config({
'angular-route': ['angular'],
'angular-sanitize': ['angular'],
+ 'angular-ui': ['angular'],
'angular-dragdrop': ['jquery', 'angular'],
'angular-mocks': ['angular'],
'angular-strap': ['angular', 'bootstrap','timepicker', 'datepicker'],
diff --git a/public/vendor/angular-ui/angular-bootstrap.js b/public/vendor/angular-ui/angular-bootstrap.js
new file mode 100644
index 00000000000..f90955af4e7
--- /dev/null
+++ b/public/vendor/angular-ui/angular-bootstrap.js
@@ -0,0 +1,6 @@
+define([
+ 'angular',
+ '../vendor/angular-ui/tabs',
+], function() {
+});
+
diff --git a/public/vendor/angular-ui/tabs.js b/public/vendor/angular-ui/tabs.js
new file mode 100644
index 00000000000..4d87ac0bab1
--- /dev/null
+++ b/public/vendor/angular-ui/tabs.js
@@ -0,0 +1,293 @@
+
+/**
+ * @ngdoc overview
+ * @name ui.bootstrap.tabs
+ *
+ * @description
+ * AngularJS version of the tabs directive.
+ */
+
+angular.module('ui.bootstrap.tabs', [])
+
+.controller('TabsetController', ['$scope', function TabsetCtrl($scope) {
+ var ctrl = this,
+ tabs = ctrl.tabs = $scope.tabs = [];
+
+ ctrl.select = function(selectedTab) {
+ angular.forEach(tabs, function(tab) {
+ if (tab.active && tab !== selectedTab) {
+ tab.active = false;
+ tab.onDeselect();
+ }
+ });
+ selectedTab.active = true;
+ selectedTab.onSelect();
+ };
+
+ ctrl.addTab = function addTab(tab) {
+ tabs.push(tab);
+ // we can't run the select function on the first tab
+ // since that would select it twice
+ if (tabs.length === 1 && tab.active !== false) {
+ tab.active = true;
+ } else if (tab.active) {
+ ctrl.select(tab);
+ }
+ else {
+ tab.active = false;
+ }
+ };
+
+ ctrl.removeTab = function removeTab(tab) {
+ var index = tabs.indexOf(tab);
+ //Select a new tab if the tab to be removed is selected and not destroyed
+ if (tab.active && tabs.length > 1 && !destroyed) {
+ //If this is the last tab, select the previous tab. else, the next tab.
+ var newActiveIndex = index == tabs.length - 1 ? index - 1 : index + 1;
+ ctrl.select(tabs[newActiveIndex]);
+ }
+ tabs.splice(index, 1);
+ };
+
+ var destroyed;
+ $scope.$on('$destroy', function() {
+ destroyed = true;
+ });
+}])
+
+/**
+ * @ngdoc directive
+ * @name ui.bootstrap.tabs.directive:tabset
+ * @restrict EA
+ *
+ * @description
+ * Tabset is the outer container for the tabs directive
+ *
+ * @param {boolean=} vertical Whether or not to use vertical styling for the tabs.
+ * @param {boolean=} justified Whether or not to use justified styling for the tabs.
+ *
+ * @example
+
+
+
+ First Content!
+ Second Content!
+
+
+
+ First Vertical Content!
+ Second Vertical Content!
+
+
+ First Justified Content!
+ Second Justified Content!
+
+
+
+ */
+.directive('tabset', function() {
+ return {
+ restrict: 'EA',
+ transclude: true,
+ replace: true,
+ scope: {
+ type: '@'
+ },
+ controller: 'TabsetController',
+ templateUrl: 'app/partials/bootstrap/tabset.html',
+ link: function(scope, element, attrs) {
+ scope.vertical = angular.isDefined(attrs.vertical) ? scope.$parent.$eval(attrs.vertical) : false;
+ scope.justified = angular.isDefined(attrs.justified) ? scope.$parent.$eval(attrs.justified) : false;
+ }
+ };
+})
+
+/**
+ * @ngdoc directive
+ * @name ui.bootstrap.tabs.directive:tab
+ * @restrict EA
+ *
+ * @param {string=} heading The visible heading, or title, of the tab. Set HTML headings with {@link ui.bootstrap.tabs.directive:tabHeading tabHeading}.
+ * @param {string=} select An expression to evaluate when the tab is selected.
+ * @param {boolean=} active A binding, telling whether or not this tab is selected.
+ * @param {boolean=} disabled A binding, telling whether or not this tab is disabled.
+ *
+ * @description
+ * Creates a tab with a heading and content. Must be placed within a {@link ui.bootstrap.tabs.directive:tabset tabset}.
+ *
+ * @example
+
+
+
+
+
+
+
+ First Tab
+
+ Alert me!
+ Second Tab, with alert callback and html heading!
+
+
+ {{item.content}}
+
+
+
+
+
+ function TabsDemoCtrl($scope) {
+ $scope.items = [
+ { title:"Dynamic Title 1", content:"Dynamic Item 0" },
+ { title:"Dynamic Title 2", content:"Dynamic Item 1", disabled: true }
+ ];
+
+ $scope.alertMe = function() {
+ setTimeout(function() {
+ alert("You've selected the alert tab!");
+ });
+ };
+ };
+
+
+ */
+
+/**
+ * @ngdoc directive
+ * @name ui.bootstrap.tabs.directive:tabHeading
+ * @restrict EA
+ *
+ * @description
+ * Creates an HTML heading for a {@link ui.bootstrap.tabs.directive:tab tab}. Must be placed as a child of a tab element.
+ *
+ * @example
+
+
+
+
+ HTML in my titles?!
+ And some content, too!
+
+
+ Icon heading?!?
+ That's right.
+
+
+
+
+ */
+.directive('tab', ['$parse', '$log', function($parse, $log) {
+ return {
+ require: '^tabset',
+ restrict: 'EA',
+ replace: true,
+ templateUrl: 'app/partials/bootstrap/tab.html',
+ transclude: true,
+ scope: {
+ active: '=?',
+ heading: '@',
+ onSelect: '&select', //This callback is called in contentHeadingTransclude
+ //once it inserts the tab's content into the dom
+ onDeselect: '&deselect'
+ },
+ controller: function() {
+ //Empty controller so other directives can require being 'under' a tab
+ },
+ compile: function(elm, attrs, transclude) {
+ return function postLink(scope, elm, attrs, tabsetCtrl) {
+ scope.$watch('active', function(active) {
+ if (active) {
+ tabsetCtrl.select(scope);
+ }
+ });
+
+ scope.disabled = false;
+ if ( attrs.disable ) {
+ scope.$parent.$watch($parse(attrs.disable), function(value) {
+ scope.disabled = !! value;
+ });
+ }
+
+ // Deprecation support of "disabled" parameter
+ // fix(tab): IE9 disabled attr renders grey text on enabled tab #2677
+ // This code is duplicated from the lines above to make it easy to remove once
+ // the feature has been completely deprecated
+ if ( attrs.disabled ) {
+ $log.warn('Use of "disabled" attribute has been deprecated, please use "disable"');
+ scope.$parent.$watch($parse(attrs.disabled), function(value) {
+ scope.disabled = !! value;
+ });
+ }
+
+ scope.select = function() {
+ if ( !scope.disabled ) {
+ scope.active = true;
+ }
+ };
+
+ tabsetCtrl.addTab(scope);
+ scope.$on('$destroy', function() {
+ tabsetCtrl.removeTab(scope);
+ });
+
+ //We need to transclude later, once the content container is ready.
+ //when this link happens, we're inside a tab heading.
+ scope.$transcludeFn = transclude;
+ };
+ }
+ };
+}])
+
+.directive('tabHeadingTransclude', [function() {
+ return {
+ restrict: 'A',
+ require: '^tab',
+ link: function(scope, elm, attrs, tabCtrl) {
+ scope.$watch('headingElement', function updateHeadingElement(heading) {
+ if (heading) {
+ elm.html('');
+ elm.append(heading);
+ }
+ });
+ }
+ };
+}])
+
+.directive('tabContentTransclude', function() {
+ return {
+ restrict: 'A',
+ require: '^tabset',
+ link: function(scope, elm, attrs) {
+ var tab = scope.$eval(attrs.tabContentTransclude);
+
+ //Now our tab is ready to be transcluded: both the tab heading area
+ //and the tab content area are loaded. Transclude 'em both.
+ tab.$transcludeFn(tab.$parent, function(contents) {
+ angular.forEach(contents, function(node) {
+ if (isTabHeading(node)) {
+ //Let tabHeadingTransclude know.
+ tab.headingElement = node;
+ } else {
+ elm.append(node);
+ }
+ });
+ });
+ }
+ };
+ function isTabHeading(node) {
+ return node.tagName && (
+ node.hasAttribute('tab-heading') ||
+ node.hasAttribute('data-tab-heading') ||
+ node.tagName.toLowerCase() === 'tab-heading' ||
+ node.tagName.toLowerCase() === 'data-tab-heading'
+ );
+ }
+})
+
+;
diff --git a/public/vendor/bootstrap/less/bootstrap.less b/public/vendor/bootstrap/less/bootstrap.less
index a22c4b24756..758dbde9848 100644
--- a/public/vendor/bootstrap/less/bootstrap.less
+++ b/public/vendor/bootstrap/less/bootstrap.less
@@ -22,7 +22,6 @@
// Base CSS
@import "type.less";
-@import "code.less";
@import "forms.less";
@import "tables.less";
diff --git a/public/vendor/bootstrap/less/type.less b/public/vendor/bootstrap/less/type.less
index 6a472db4979..8b137891791 100644
--- a/public/vendor/bootstrap/less/type.less
+++ b/public/vendor/bootstrap/less/type.less
@@ -1,247 +1 @@
-//
-// Typography
-// --------------------------------------------------
-
-// Body text
-// -------------------------
-
-p {
- margin: 0 0 @baseLineHeight / 2;
-}
-.lead {
- margin-bottom: @baseLineHeight;
- font-size: @baseFontSize * 1.5;
- font-weight: 200;
- line-height: @baseLineHeight * 1.5;
-}
-
-
-// Emphasis & misc
-// -------------------------
-
-// Ex: 14px base font * 85% = about 12px
-small { font-size: 85%; }
-
-strong { font-weight: bold; }
-em { font-style: italic; }
-cite { font-style: normal; }
-
-// Utility classes
-.muted { color: @grayLight; }
-a.muted:hover,
-a.muted:focus { color: darken(@grayLight, 10%); }
-
-.text-warning { color: @warningText; }
-a.text-warning:hover,
-a.text-warning:focus { color: darken(@warningText, 10%); }
-
-.text-error { color: @errorText; }
-a.text-error:hover,
-a.text-error:focus { color: darken(@errorText, 10%); }
-
-.text-info { color: @infoText; }
-a.text-info:hover,
-a.text-info:focus { color: darken(@infoText, 10%); }
-
-.text-success { color: @successText; }
-a.text-success:hover,
-a.text-success:focus { color: darken(@successText, 10%); }
-
-.text-left { text-align: left; }
-.text-right { text-align: right; }
-.text-center { text-align: center; }
-
-
-// Headings
-// -------------------------
-
-h1, h2, h3, h4, h5, h6 {
- margin: (@baseLineHeight / 2) 0;
- font-family: @headingsFontFamily;
- font-weight: @headingsFontWeight;
- line-height: @baseLineHeight;
- color: @headingsColor;
- text-rendering: optimizelegibility; // Fix the character spacing for headings
- small {
- font-weight: normal;
- line-height: 1;
- color: @grayLight;
- }
-}
-
-h1,
-h2,
-h3 { line-height: @baseLineHeight * 2; }
-
-h1 { font-size: @baseFontSize * 2.75; } // ~38px
-h2 { font-size: @baseFontSize * 2.25; } // ~32px
-h3 { font-size: @baseFontSize * 1.75; } // ~24px
-h4 { font-size: @baseFontSize * 1.25; } // ~18px
-h5 { font-size: @baseFontSize; }
-h6 { font-size: @baseFontSize * 0.85; } // ~12px
-
-h1 small { font-size: @baseFontSize * 1.75; } // ~24px
-h2 small { font-size: @baseFontSize * 1.25; } // ~18px
-h3 small { font-size: @baseFontSize; }
-h4 small { font-size: @baseFontSize; }
-
-
-// Page header
-// -------------------------
-
-.page-header {
- padding-bottom: (@baseLineHeight / 2) - 1;
- margin: @baseLineHeight 0 (@baseLineHeight * 1.5);
- border-bottom: 1px solid @grayLighter;
-}
-
-
-
-// Lists
-// --------------------------------------------------
-
-// Unordered and Ordered lists
-ul, ol {
- padding: 0;
- margin: 0 0 @baseLineHeight / 2 25px;
-}
-ul ul,
-ul ol,
-ol ol,
-ol ul {
- margin-bottom: 0;
-}
-li {
- line-height: @baseLineHeight;
-}
-
-// Remove default list styles
-ul.unstyled,
-ol.unstyled {
- margin-left: 0;
- list-style: none;
-}
-
-// Single-line list items
-ul.inline,
-ol.inline {
- margin-left: 0;
- list-style: none;
- > li {
- display: inline-block;
- .ie7-inline-block();
- padding-left: 5px;
- padding-right: 5px;
- }
-}
-
-// Description Lists
-dl {
- margin-bottom: @baseLineHeight;
-}
-dt,
-dd {
- line-height: @baseLineHeight;
-}
-dt {
- font-weight: bold;
-}
-dd {
- margin-left: @baseLineHeight / 2;
-}
-// Horizontal layout (like forms)
-.dl-horizontal {
- .clearfix(); // Ensure dl clears floats if empty dd elements present
- dt {
- float: left;
- width: @horizontalComponentOffset - 20;
- clear: left;
- text-align: right;
- .text-overflow();
- }
- dd {
- margin-left: @horizontalComponentOffset;
- }
-}
-
-// MISC
-// ----
-
-// Horizontal rules
-hr {
- margin: @baseLineHeight 0;
- border: 0;
- border-top: 1px solid @hrBorder;
- border-bottom: 1px solid @white;
-}
-
-// Abbreviations and acronyms
-abbr[title],
-// Added data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257
-abbr[data-original-title] {
- cursor: help;
- border-bottom: 1px dotted @grayLight;
-}
-abbr.initialism {
- font-size: 90%;
- text-transform: uppercase;
-}
-
-// Blockquotes
-blockquote {
- padding: 0 0 0 15px;
- margin: 0 0 @baseLineHeight;
- border-left: 5px solid @grayLighter;
- p {
- margin-bottom: 0;
- font-size: @baseFontSize * 1.25;
- font-weight: 300;
- line-height: 1.25;
- }
- small {
- display: block;
- line-height: @baseLineHeight;
- color: @grayLight;
- &:before {
- content: '\2014 \00A0';
- }
- }
-
- // Float right with text-align: right
- &.pull-right {
- float: right;
- padding-right: 15px;
- padding-left: 0;
- border-right: 5px solid @grayLighter;
- border-left: 0;
- p,
- small {
- text-align: right;
- }
- small {
- &:before {
- content: '';
- }
- &:after {
- content: '\00A0 \2014';
- }
- }
- }
-}
-
-// Quotes
-q:before,
-q:after,
-blockquote:before,
-blockquote:after {
- content: "";
-}
-
-// Addresses
-address {
- display: block;
- margin-bottom: @baseLineHeight;
- font-style: normal;
- line-height: @baseLineHeight;
-}