mirror of
https://github.com/twbs/bootstrap.git
synced 2024-11-30 12:24:19 +01:00
Merge branch 'master' of github.com:twbs/bootstrap
This commit is contained in:
commit
ec261a83a5
@ -6,9 +6,9 @@ before_install:
|
|||||||
install:
|
install:
|
||||||
- if [ "$TWBS_TEST" = validate-html ]; then time gem install jekyll; fi
|
- if [ "$TWBS_TEST" = validate-html ]; then time gem install jekyll; fi
|
||||||
- time npm install -g grunt-cli
|
- time npm install -g grunt-cli
|
||||||
- time ./test-infra/node_modules_cache.py download || time npm install
|
- time ./test-infra/node_modules_cache.py download package.json ./node_modules || time npm install
|
||||||
after_script:
|
after_script:
|
||||||
- if [ "$TWBS_TEST" = core ]; then time ./test-infra/node_modules_cache.py upload; fi
|
- if [ "$TWBS_TEST" = core ]; then time ./test-infra/node_modules_cache.py upload package.json ./node_modules; fi
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- SAUCE_USERNAME: bootstrap
|
- SAUCE_USERNAME: bootstrap
|
||||||
|
@ -193,6 +193,8 @@ module.exports = function (grunt) {
|
|||||||
|
|
||||||
validation: {
|
validation: {
|
||||||
options: {
|
options: {
|
||||||
|
charset: 'utf-8',
|
||||||
|
doctype: 'HTML5',
|
||||||
reset: true,
|
reset: true,
|
||||||
relaxerror: [
|
relaxerror: [
|
||||||
'Bad value X-UA-Compatible for attribute http-equiv on element meta.',
|
'Bad value X-UA-Compatible for attribute http-equiv on element meta.',
|
||||||
|
@ -1552,7 +1552,7 @@ base_url: "../"
|
|||||||
|
|
||||||
|
|
||||||
<h2 id="input-groups-basic">Basic example</h2>
|
<h2 id="input-groups-basic">Basic example</h2>
|
||||||
<p>Place one add-on or button on either side of an input. You may also place one on both sides of an input. <strong class="text-danger">We do not support mutiple add-ons on a single side.</strong></p>
|
<p>Place one add-on or button on either side of an input. You may also place one on both sides of an input. <strong class="text-danger">We do not support multiple add-ons on a single side.</strong></p>
|
||||||
<form class="bs-example bs-example-form" role="form">
|
<form class="bs-example bs-example-form" role="form">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-addon">@</span>
|
<span class="input-group-addon">@</span>
|
||||||
|
66
css.html
66
css.html
@ -1965,31 +1965,31 @@ For example, <code><section></code> should be wrapped as inline.
|
|||||||
<div class="bs-example">
|
<div class="bs-example">
|
||||||
<form role="form">
|
<form role="form">
|
||||||
<div class="form-group has-success">
|
<div class="form-group has-success">
|
||||||
<label class="control-label" for="inputSuccess">Input with success</label>
|
<label class="control-label" for="inputSuccess1">Input with success</label>
|
||||||
<input type="text" class="form-control" id="inputSuccess">
|
<input type="text" class="form-control" id="inputSuccess1">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group has-warning">
|
<div class="form-group has-warning">
|
||||||
<label class="control-label" for="inputWarning">Input with warning</label>
|
<label class="control-label" for="inputWarning1">Input with warning</label>
|
||||||
<input type="text" class="form-control" id="inputWarning">
|
<input type="text" class="form-control" id="inputWarning1">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group has-error">
|
<div class="form-group has-error">
|
||||||
<label class="control-label" for="inputError">Input with error</label>
|
<label class="control-label" for="inputError1">Input with error</label>
|
||||||
<input type="text" class="form-control" id="inputError">
|
<input type="text" class="form-control" id="inputError1">
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div><!-- /.bs-example -->
|
</div><!-- /.bs-example -->
|
||||||
{% highlight html %}
|
{% highlight html %}
|
||||||
<div class="form-group has-success">
|
<div class="form-group has-success">
|
||||||
<label class="control-label" for="inputSuccess">Input with success</label>
|
<label class="control-label" for="inputSuccess1">Input with success</label>
|
||||||
<input type="text" class="form-control" id="inputSuccess">
|
<input type="text" class="form-control" id="inputSuccess1">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group has-warning">
|
<div class="form-group has-warning">
|
||||||
<label class="control-label" for="inputWarning">Input with warning</label>
|
<label class="control-label" for="inputWarning1">Input with warning</label>
|
||||||
<input type="text" class="form-control" id="inputWarning">
|
<input type="text" class="form-control" id="inputWarning1">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group has-error">
|
<div class="form-group has-error">
|
||||||
<label class="control-label" for="inputError">Input with error</label>
|
<label class="control-label" for="inputError1">Input with error</label>
|
||||||
<input type="text" class="form-control" id="inputError">
|
<input type="text" class="form-control" id="inputError1">
|
||||||
</div>
|
</div>
|
||||||
{% endhighlight %}
|
{% endhighlight %}
|
||||||
|
|
||||||
@ -1998,36 +1998,36 @@ For example, <code><section></code> should be wrapped as inline.
|
|||||||
<div class="bs-example">
|
<div class="bs-example">
|
||||||
<form role="form">
|
<form role="form">
|
||||||
<div class="form-group has-success has-feedback">
|
<div class="form-group has-success has-feedback">
|
||||||
<label class="control-label" for="inputSuccess">Input with success</label>
|
<label class="control-label" for="inputSuccess2">Input with success</label>
|
||||||
<input type="text" class="form-control" id="inputSuccess">
|
<input type="text" class="form-control" id="inputSuccess2">
|
||||||
<span class="glyphicon glyphicon-ok form-control-feedback"></span>
|
<span class="glyphicon glyphicon-ok form-control-feedback"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group has-warning has-feedback">
|
<div class="form-group has-warning has-feedback">
|
||||||
<label class="control-label" for="inputWarning">Input with warning</label>
|
<label class="control-label" for="inputWarning2">Input with warning</label>
|
||||||
<input type="text" class="form-control" id="inputWarning">
|
<input type="text" class="form-control" id="inputWarning2">
|
||||||
<span class="glyphicon glyphicon-warning-sign form-control-feedback"></span>
|
<span class="glyphicon glyphicon-warning-sign form-control-feedback"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group has-error has-feedback">
|
<div class="form-group has-error has-feedback">
|
||||||
<label class="control-label" for="inputError">Input with error</label>
|
<label class="control-label" for="inputError2">Input with error</label>
|
||||||
<input type="text" class="form-control" id="inputError">
|
<input type="text" class="form-control" id="inputError2">
|
||||||
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
|
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{% highlight html %}
|
{% highlight html %}
|
||||||
<div class="form-group has-success has-feedback">
|
<div class="form-group has-success has-feedback">
|
||||||
<label class="control-label" for="inputSuccess">Input with success</label>
|
<label class="control-label" for="inputSuccess2">Input with success</label>
|
||||||
<input type="text" class="form-control" id="inputSuccess">
|
<input type="text" class="form-control" id="inputSuccess2">
|
||||||
<span class="glyphicon glyphicon-ok form-control-feedback"></span>
|
<span class="glyphicon glyphicon-ok form-control-feedback"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group has-warning has-feedback">
|
<div class="form-group has-warning has-feedback">
|
||||||
<label class="control-label" for="inputWarning">Input with warning</label>
|
<label class="control-label" for="inputWarning2">Input with warning</label>
|
||||||
<input type="text" class="form-control" id="inputWarning">
|
<input type="text" class="form-control" id="inputWarning2">
|
||||||
<span class="glyphicon glyphicon-warning-sign form-control-feedback"></span>
|
<span class="glyphicon glyphicon-warning-sign form-control-feedback"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group has-error has-feedback">
|
<div class="form-group has-error has-feedback">
|
||||||
<label class="control-label" for="inputError">Input with error</label>
|
<label class="control-label" for="inputError2">Input with error</label>
|
||||||
<input type="text" class="form-control" id="inputError">
|
<input type="text" class="form-control" id="inputError2">
|
||||||
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
|
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
|
||||||
</div>
|
</div>
|
||||||
{% endhighlight %}
|
{% endhighlight %}
|
||||||
@ -2036,9 +2036,9 @@ For example, <code><section></code> should be wrapped as inline.
|
|||||||
<div class="bs-example">
|
<div class="bs-example">
|
||||||
<form class="form-horizontal" role="form">
|
<form class="form-horizontal" role="form">
|
||||||
<div class="form-group has-success has-feedback">
|
<div class="form-group has-success has-feedback">
|
||||||
<label class="control-label col-sm-3" for="inputSuccess">Input with success</label>
|
<label class="control-label col-sm-3" for="inputSuccess3">Input with success</label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input type="text" class="form-control" id="inputSuccess">
|
<input type="text" class="form-control" id="inputSuccess3">
|
||||||
<span class="glyphicon glyphicon-ok form-control-feedback"></span>
|
<span class="glyphicon glyphicon-ok form-control-feedback"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -2047,9 +2047,9 @@ For example, <code><section></code> should be wrapped as inline.
|
|||||||
{% highlight html %}
|
{% highlight html %}
|
||||||
<form class="form-horizontal" role="form">
|
<form class="form-horizontal" role="form">
|
||||||
<div class="form-group has-success has-feedback">
|
<div class="form-group has-success has-feedback">
|
||||||
<label class="control-label col-sm-3" for="inputSuccess">Input with success</label>
|
<label class="control-label col-sm-3" for="inputSuccess3">Input with success</label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input type="text" class="form-control" id="inputSuccess">
|
<input type="text" class="form-control" id="inputSuccess3">
|
||||||
<span class="glyphicon glyphicon-ok form-control-feedback"></span>
|
<span class="glyphicon glyphicon-ok form-control-feedback"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -2059,8 +2059,8 @@ For example, <code><section></code> should be wrapped as inline.
|
|||||||
<div class="bs-example">
|
<div class="bs-example">
|
||||||
<form class="form-inline" role="form">
|
<form class="form-inline" role="form">
|
||||||
<div class="form-group has-success has-feedback">
|
<div class="form-group has-success has-feedback">
|
||||||
<label class="control-label" for="inputSuccess">Input with success</label>
|
<label class="control-label" for="inputSuccess4">Input with success</label>
|
||||||
<input type="text" class="form-control" id="inputSuccess">
|
<input type="text" class="form-control" id="inputSuccess4">
|
||||||
<span class="glyphicon glyphicon-ok form-control-feedback"></span>
|
<span class="glyphicon glyphicon-ok form-control-feedback"></span>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@ -2068,8 +2068,8 @@ For example, <code><section></code> should be wrapped as inline.
|
|||||||
{% highlight html %}
|
{% highlight html %}
|
||||||
<form class="form-inline" role="form">
|
<form class="form-inline" role="form">
|
||||||
<div class="form-group has-success has-feedback">
|
<div class="form-group has-success has-feedback">
|
||||||
<label class="control-label" for="inputSuccess">Input with success</label>
|
<label class="control-label" for="inputSuccess4">Input with success</label>
|
||||||
<input type="text" class="form-control" id="inputSuccess">
|
<input type="text" class="form-control" id="inputSuccess4">
|
||||||
<span class="glyphicon glyphicon-ok form-control-feedback"></span>
|
<span class="glyphicon glyphicon-ok form-control-feedback"></span>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@ -3006,7 +3006,7 @@ a {
|
|||||||
{% endhighlight %}
|
{% endhighlight %}
|
||||||
|
|
||||||
<h3 id="less-mixins-box-shadow">Box (Drop) shadows</h3>
|
<h3 id="less-mixins-box-shadow">Box (Drop) shadows</h3>
|
||||||
<p>If your target audience is using the latest and greatest browsers and devices, be sure to just use the <code>box-shadow</code> property on it's own. If you need support for older Android (pre-v4) and iOS devices (pre-iOS 5), use of the mixin to pick up the required <code>-webkit</code> prefix.</p>
|
<p>If your target audience is using the latest and greatest browsers and devices, be sure to just use the <code>box-shadow</code> property on its own. If you need support for older Android (pre-v4) and iOS devices (pre-iOS 5), use the mixin to pick up the required <code>-webkit</code> prefix.</p>
|
||||||
<p>Be sure to use <code>rgba()</code> colors in your box shadows so they blend as seamlessly as possible with backgrounds.</p>
|
<p>Be sure to use <code>rgba()</code> colors in your box shadows so they blend as seamlessly as possible with backgrounds.</p>
|
||||||
{% highlight css %}
|
{% highlight css %}
|
||||||
.box-shadow(@shadow: 0 1px 3px rgba(0,0,0,.25)) {
|
.box-shadow(@shadow: 0 1px 3px rgba(0,0,0,.25)) {
|
||||||
|
@ -884,8 +884,8 @@ if (navigator.userAgent.match(/IEMobile\/10\.0/)) {
|
|||||||
{% highlight html %}
|
{% highlight html %}
|
||||||
<script>
|
<script>
|
||||||
var nua = navigator.userAgent;
|
var nua = navigator.userAgent;
|
||||||
var is_android = ((nua.indexOf('Mozilla/5.0') > -1 && nua.indexOf('Android ') > -1 && nua.indexOf('AppleWebKit') > -1) && !(nua.indexOf('Chrome') > -1));
|
var isAndroid = (nua.indexOf('Mozilla/5.0') > -1 && nua.indexOf('Android ') > -1 && nua.indexOf('AppleWebKit') > -1 && nua.indexOf('Chrome') === -1);
|
||||||
if(is_android) {
|
if (isAndroid) {
|
||||||
$('select.form-control').removeClass('form-control').css('width', '100%');
|
$('select.form-control').removeClass('form-control').css('width', '100%');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -245,7 +245,7 @@ $('#myModal').on('show.bs.modal', function (e) {
|
|||||||
<!-- Large modal -->
|
<!-- Large modal -->
|
||||||
<button class="btn btn-primary" data-toggle="modal" data-target=".bs-modal-lg">Large modal</button>
|
<button class="btn btn-primary" data-toggle="modal" data-target=".bs-modal-lg">Large modal</button>
|
||||||
|
|
||||||
<div class="modal fade bs-modal-lg" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
<div class="modal fade bs-modal-lg" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-lg">
|
<div class="modal-dialog modal-lg">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
...
|
...
|
||||||
@ -256,7 +256,7 @@ $('#myModal').on('show.bs.modal', function (e) {
|
|||||||
<!-- Small modal -->
|
<!-- Small modal -->
|
||||||
<button class="btn btn-primary" data-toggle="modal" data-target=".bs-modal-sm">Small modal</button>
|
<button class="btn btn-primary" data-toggle="modal" data-target=".bs-modal-sm">Small modal</button>
|
||||||
|
|
||||||
<div class="modal fade bs-modal-sm" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
<div class="modal fade bs-modal-sm" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-sm">
|
<div class="modal-dialog modal-sm">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
...
|
...
|
||||||
@ -266,13 +266,13 @@ $('#myModal').on('show.bs.modal', function (e) {
|
|||||||
{% endhighlight %}
|
{% endhighlight %}
|
||||||
|
|
||||||
<!-- Modal content for the above example -->
|
<!-- Modal content for the above example -->
|
||||||
<div class="modal fade bs-modal-lg" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
<div class="modal fade bs-modal-lg" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-lg">
|
<div class="modal-dialog modal-lg">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
|
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
<h4 class="modal-title" id="myModalLabel">Large modal</h4>
|
<h4 class="modal-title" id="myLargeModalLabel">Large modal</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
...
|
...
|
||||||
@ -280,13 +280,13 @@ $('#myModal').on('show.bs.modal', function (e) {
|
|||||||
</div><!-- /.modal-content -->
|
</div><!-- /.modal-content -->
|
||||||
</div><!-- /.modal-dialog -->
|
</div><!-- /.modal-dialog -->
|
||||||
</div><!-- /.modal -->
|
</div><!-- /.modal -->
|
||||||
<div class="modal fade bs-modal-sm" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
<div class="modal fade bs-modal-sm" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-sm">
|
<div class="modal-dialog modal-sm">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
|
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
<h4 class="modal-title" id="myModalLabel">Small modal</h4>
|
<h4 class="modal-title" id="mySmallModalLabel">Small modal</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
...
|
...
|
||||||
@ -2053,7 +2053,7 @@ $('#myCarousel').on('slide.bs.carousel', function () {
|
|||||||
<p>The affix plugin toggles between three classes, each representing a particular state: <code>.affix</code>, <code>.affix-top</code>, and <code>.affix-bottom</code>. You must provide the styles for these classes yourself (independent of this plugin) to handle the actual positions.</p>
|
<p>The affix plugin toggles between three classes, each representing a particular state: <code>.affix</code>, <code>.affix-top</code>, and <code>.affix-bottom</code>. You must provide the styles for these classes yourself (independent of this plugin) to handle the actual positions.</p>
|
||||||
<p>Here's how the affix plugin works:</p>
|
<p>Here's how the affix plugin works:</p>
|
||||||
<ol>
|
<ol>
|
||||||
<li>To start, the plugin adds <code>.affix-top</code> to indicate the element is in it's top-most position. At this point no CSS positioning is required.</li>
|
<li>To start, the plugin adds <code>.affix-top</code> to indicate the element is in its top-most position. At this point no CSS positioning is required.</li>
|
||||||
<li>Scrolling past the element you want affixed should trigger the actual affixing. This is where <code>.affix</code> replaces <code>.affix-top</code> and sets <code>position: fixed;</code> (provided by Bootstrap's code CSS).</li>
|
<li>Scrolling past the element you want affixed should trigger the actual affixing. This is where <code>.affix</code> replaces <code>.affix-top</code> and sets <code>position: fixed;</code> (provided by Bootstrap's code CSS).</li>
|
||||||
<li>If a bottom offset is defined, scrolling past that should replace <code>.affix</code> with <code>.affix-bottom</code>. Since offsets are optional, setting one requires you to set the appropriate CSS. In this case, add <code>position: absolute;</code> when necessary. The plugin uses the data attribute or JavaScript option to determine where to position the element from there.</li>
|
<li>If a bottom offset is defined, scrolling past that should replace <code>.affix</code> with <code>.affix-bottom</code>. Since offsets are optional, setting one requires you to set the appropriate CSS. In this case, add <code>position: absolute;</code> when necessary. The plugin uses the data attribute or JavaScript option to determine where to position the element from there.</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
@ -265,7 +265,7 @@ input[type="checkbox"],
|
|||||||
// Feedback icon (requires .glyphicon classes)
|
// Feedback icon (requires .glyphicon classes)
|
||||||
.form-control-feedback {
|
.form-control-feedback {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: (@line-height-computed + 5); // Height of the `label` and it's margin
|
top: (@line-height-computed + 5); // Height of the `label` and its margin
|
||||||
right: 0;
|
right: 0;
|
||||||
display: block;
|
display: block;
|
||||||
width: @input-height-base;
|
width: @input-height-base;
|
||||||
|
@ -212,7 +212,7 @@
|
|||||||
|
|
||||||
// Navbar nav links
|
// Navbar nav links
|
||||||
//
|
//
|
||||||
// Builds on top of the `.nav` components with it's own modifier class to make
|
// Builds on top of the `.nav` components with its own modifier class to make
|
||||||
// the nav the full height of the horizontal nav (above 768px).
|
// the nav the full height of the horizontal nav (above 768px).
|
||||||
|
|
||||||
.navbar-nav {
|
.navbar-nav {
|
||||||
|
@ -91,7 +91,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Active state, and it's :hover to override normal :hover
|
// Active state, and its :hover to override normal :hover
|
||||||
&.active > a {
|
&.active > a {
|
||||||
&,
|
&,
|
||||||
&:hover,
|
&:hover,
|
||||||
|
@ -95,15 +95,17 @@
|
|||||||
border: 0;
|
border: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
> .table-striped > tbody > tr:last-child,
|
> .table-striped,
|
||||||
> .table-responsive > .table-striped > tbody > tr:last-child {
|
> .table-responsive > .table-striped {
|
||||||
td:first-child,
|
> tbody > tr:last-child {
|
||||||
th:first-child {
|
td:first-child,
|
||||||
border-bottom-left-radius: (@panel-border-radius - 1);
|
th:first-child {
|
||||||
}
|
border-bottom-left-radius: (@panel-border-radius - 1);
|
||||||
td:last-child,
|
}
|
||||||
th:last-child {
|
td:last-child,
|
||||||
border-bottom-left-radius: (@panel-border-radius - 1);
|
th:last-child {
|
||||||
|
border-bottom-left-radius: (@panel-border-radius - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,7 +122,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Within heading, strip any `h*` tag of it's default margins for spacing.
|
// Within heading, strip any `h*` tag of its default margins for spacing.
|
||||||
.panel-title {
|
.panel-title {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
@ -126,7 +126,7 @@ cite { font-style: normal; }
|
|||||||
// For now we'll leave these alongside the text classes until v4 when we can
|
// For now we'll leave these alongside the text classes until v4 when we can
|
||||||
// safely shift things around (per SemVer rules).
|
// safely shift things around (per SemVer rules).
|
||||||
.bg-primary {
|
.bg-primary {
|
||||||
// Given the contrast here, this is the only class to have it's color inverted
|
// Given the contrast here, this is the only class to have its color inverted
|
||||||
// automatically.
|
// automatically.
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: @brand-primary;
|
background-color: @brand-primary;
|
||||||
|
@ -3,7 +3,7 @@ from __future__ import absolute_import, unicode_literals, print_function, divisi
|
|||||||
|
|
||||||
from sys import argv
|
from sys import argv
|
||||||
from os import environ, stat, remove as _delete_file
|
from os import environ, stat, remove as _delete_file
|
||||||
from os.path import isfile
|
from os.path import isfile, dirname, basename, abspath
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
from subprocess import check_call as run
|
from subprocess import check_call as run
|
||||||
|
|
||||||
@ -12,7 +12,6 @@ from boto.s3.key import Key
|
|||||||
from boto.exception import S3ResponseError
|
from boto.exception import S3ResponseError
|
||||||
|
|
||||||
|
|
||||||
NODE_MODULES_TARBALL = 'node_modules.tar.gz'
|
|
||||||
NEED_TO_UPLOAD_MARKER = '.need-to-upload'
|
NEED_TO_UPLOAD_MARKER = '.need-to-upload'
|
||||||
BYTES_PER_MB = 1024 * 1024
|
BYTES_PER_MB = 1024 * 1024
|
||||||
try:
|
try:
|
||||||
@ -25,7 +24,9 @@ def _sha256_of_file(filename):
|
|||||||
hasher = sha256()
|
hasher = sha256()
|
||||||
with open(filename, 'rb') as input_file:
|
with open(filename, 'rb') as input_file:
|
||||||
hasher.update(input_file.read())
|
hasher.update(input_file.read())
|
||||||
return hasher.hexdigest()
|
file_hash = hasher.hexdigest()
|
||||||
|
print('sha256({}) = {}'.format(filename, file_hash))
|
||||||
|
return file_hash
|
||||||
|
|
||||||
|
|
||||||
def _delete_file_quietly(filename):
|
def _delete_file_quietly(filename):
|
||||||
@ -35,52 +36,71 @@ def _delete_file_quietly(filename):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def _tarball_size():
|
def _tarball_size(directory):
|
||||||
kib = stat(NODE_MODULES_TARBALL).st_size // BYTES_PER_MB
|
kib = stat(_tarball_filename_for(directory)).st_size // BYTES_PER_MB
|
||||||
return "{} MiB".format(kib)
|
return "{} MiB".format(kib)
|
||||||
|
|
||||||
|
|
||||||
|
def _tarball_filename_for(directory):
|
||||||
|
return abspath('./{}.tar.gz'.format(basename(directory)))
|
||||||
|
|
||||||
|
|
||||||
|
def _create_tarball(directory):
|
||||||
|
print("Creating tarball of {}...".format(directory))
|
||||||
|
run(['tar', '-czf', _tarball_filename_for(directory), '-C', dirname(directory), basename(directory)])
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_tarball(directory):
|
||||||
|
print("Extracting tarball of {}...".format(directory))
|
||||||
|
run(['tar', '-xzf', _tarball_filename_for(directory), '-C', dirname(directory)])
|
||||||
|
|
||||||
|
|
||||||
|
def download(directory):
|
||||||
|
_delete_file_quietly(NEED_TO_UPLOAD_MARKER)
|
||||||
|
try:
|
||||||
|
print("Downloading {} tarball from S3...".format(basename(directory)))
|
||||||
|
key.get_contents_to_filename(_tarball_filename_for(directory))
|
||||||
|
except S3ResponseError as err:
|
||||||
|
open(NEED_TO_UPLOAD_MARKER, 'a').close()
|
||||||
|
print(err)
|
||||||
|
raise SystemExit("Cached {} download failed!".format(basename(directory)))
|
||||||
|
print("Downloaded {}.".format(_tarball_size(directory)))
|
||||||
|
_extract_tarball(directory)
|
||||||
|
print("{} successfully installed from cache.".format(directory))
|
||||||
|
|
||||||
|
|
||||||
|
def upload(directory):
|
||||||
|
_create_tarball(directory)
|
||||||
|
print("Uploading {} tarball to S3... ({})".format(basename(directory), _tarball_size(directory)))
|
||||||
|
key.set_contents_from_filename(_tarball_filename_for(directory))
|
||||||
|
print("{} cache successfully updated.".format(directory))
|
||||||
|
_delete_file_quietly(NEED_TO_UPLOAD_MARKER)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# Uses environment variables:
|
# Uses environment variables:
|
||||||
# AWS_ACCESS_KEY_ID - AWS Access Key ID
|
# AWS_ACCESS_KEY_ID - AWS Access Key ID
|
||||||
# AWS_SECRET_ACCESS_KEY - AWS Secret Access Key
|
# AWS_SECRET_ACCESS_KEY - AWS Secret Access Key
|
||||||
argv.pop(0)
|
argv.pop(0)
|
||||||
if len(argv) != 1:
|
if len(argv) != 3:
|
||||||
raise SystemExit("USAGE: node_modules_cache.py <download | upload>")
|
raise SystemExit("USAGE: node_modules_cache.py <download | upload> <dependencies file> <directory>")
|
||||||
mode = argv.pop()
|
mode, dependencies_file, directory = argv
|
||||||
|
|
||||||
conn = S3Connection()
|
conn = S3Connection()
|
||||||
bucket = conn.lookup(BUCKET_NAME)
|
bucket = conn.lookup(BUCKET_NAME)
|
||||||
if bucket is None:
|
if bucket is None:
|
||||||
raise SystemExit("Could not access bucket!")
|
raise SystemExit("Could not access bucket!")
|
||||||
|
|
||||||
package_json_hash = _sha256_of_file('package.json')
|
dependencies_file_hash = _sha256_of_file(dependencies_file)
|
||||||
print('sha256(package.json) = ' + package_json_hash)
|
|
||||||
|
|
||||||
key = Key(bucket, package_json_hash)
|
key = Key(bucket, dependencies_file_hash)
|
||||||
key.storage_class = 'REDUCED_REDUNDANCY'
|
key.storage_class = 'REDUCED_REDUNDANCY'
|
||||||
|
|
||||||
if mode == 'download':
|
if mode == 'download':
|
||||||
_delete_file_quietly(NEED_TO_UPLOAD_MARKER)
|
download(directory)
|
||||||
try:
|
|
||||||
print("Downloading tarball from S3...")
|
|
||||||
key.get_contents_to_filename(NODE_MODULES_TARBALL)
|
|
||||||
except S3ResponseError as err:
|
|
||||||
open(NEED_TO_UPLOAD_MARKER, 'a').close()
|
|
||||||
print(err)
|
|
||||||
raise SystemExit("Cached node_modules download failed!")
|
|
||||||
print("Downloaded {}.".format(_tarball_size()))
|
|
||||||
print("Extracting tarball...")
|
|
||||||
run(['tar', 'xzf', NODE_MODULES_TARBALL])
|
|
||||||
print("node_modules successfully installed from cache.")
|
|
||||||
elif mode == 'upload':
|
elif mode == 'upload':
|
||||||
if isfile(NEED_TO_UPLOAD_MARKER):
|
if isfile(NEED_TO_UPLOAD_MARKER): # FIXME
|
||||||
print("Creating tarball...")
|
upload(directory)
|
||||||
run(['tar', 'czf', NODE_MODULES_TARBALL, 'node_modules'])
|
|
||||||
print("Uploading tarball to S3... ({})".format(_tarball_size()))
|
|
||||||
key.set_contents_from_filename(NODE_MODULES_TARBALL)
|
|
||||||
print("node_modules cache successfully updated.")
|
|
||||||
_delete_file_quietly(NEED_TO_UPLOAD_MARKER)
|
|
||||||
else:
|
else:
|
||||||
print("No need to upload anything.")
|
print("No need to upload anything.")
|
||||||
else:
|
else:
|
||||||
|
Loading…
Reference in New Issue
Block a user