diff --git a/.travis.yml b/.travis.yml index f36af0aa4fabb52ac87dbf05f11e21a65e801569..47e28bf232a89899dbd2049c7c15f78ad70d352f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,9 +6,9 @@ before_install: install: - if [ "$TWBS_TEST" = validate-html ]; then time gem install jekyll; fi - 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: - - 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: global: - SAUCE_USERNAME: bootstrap diff --git a/Gruntfile.js b/Gruntfile.js index c4c3ced58f2c7348dd4a39ec69259a1adf18d45e..d350ce3ad6583bd0acd279c3b3fdebcee5b7fae3 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -193,6 +193,8 @@ module.exports = function (grunt) { validation: { options: { + charset: 'utf-8', + doctype: 'HTML5', reset: true, relaxerror: [ 'Bad value X-UA-Compatible for attribute http-equiv on element meta.', diff --git a/components.html b/components.html index 4afb2cd005307c84d1ba5dc1be19f6368fec6b83..8e61cf5265fcd9d9ed14697b22ca71c458f48b74 100644 --- a/components.html +++ b/components.html @@ -1552,7 +1552,7 @@ base_url: "../" <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"> <div class="input-group"> <span class="input-group-addon">@</span> diff --git a/css.html b/css.html index 4220b998cc1698433daf3f22cb9859084f63fab0..cd233ef2ab23fa2ee7da6b0de87a098233ac015d 100644 --- a/css.html +++ b/css.html @@ -1965,31 +1965,31 @@ For example, <code><section></code> should be wrapped as inline. <div class="bs-example"> <form role="form"> <div class="form-group has-success"> - <label class="control-label" for="inputSuccess">Input with success</label> - <input type="text" class="form-control" id="inputSuccess"> + <label class="control-label" for="inputSuccess1">Input with success</label> + <input type="text" class="form-control" id="inputSuccess1"> </div> <div class="form-group has-warning"> - <label class="control-label" for="inputWarning">Input with warning</label> - <input type="text" class="form-control" id="inputWarning"> + <label class="control-label" for="inputWarning1">Input with warning</label> + <input type="text" class="form-control" id="inputWarning1"> </div> <div class="form-group has-error"> - <label class="control-label" for="inputError">Input with error</label> - <input type="text" class="form-control" id="inputError"> + <label class="control-label" for="inputError1">Input with error</label> + <input type="text" class="form-control" id="inputError1"> </div> </form> </div><!-- /.bs-example --> {% highlight html %} <div class="form-group has-success"> - <label class="control-label" for="inputSuccess">Input with success</label> - <input type="text" class="form-control" id="inputSuccess"> + <label class="control-label" for="inputSuccess1">Input with success</label> + <input type="text" class="form-control" id="inputSuccess1"> </div> <div class="form-group has-warning"> - <label class="control-label" for="inputWarning">Input with warning</label> - <input type="text" class="form-control" id="inputWarning"> + <label class="control-label" for="inputWarning1">Input with warning</label> + <input type="text" class="form-control" id="inputWarning1"> </div> <div class="form-group has-error"> - <label class="control-label" for="inputError">Input with error</label> - <input type="text" class="form-control" id="inputError"> + <label class="control-label" for="inputError1">Input with error</label> + <input type="text" class="form-control" id="inputError1"> </div> {% endhighlight %} @@ -1998,36 +1998,36 @@ For example, <code><section></code> should be wrapped as inline. <div class="bs-example"> <form role="form"> <div class="form-group has-success has-feedback"> - <label class="control-label" for="inputSuccess">Input with success</label> - <input type="text" class="form-control" id="inputSuccess"> + <label class="control-label" for="inputSuccess2">Input with success</label> + <input type="text" class="form-control" id="inputSuccess2"> <span class="glyphicon glyphicon-ok form-control-feedback"></span> </div> <div class="form-group has-warning has-feedback"> - <label class="control-label" for="inputWarning">Input with warning</label> - <input type="text" class="form-control" id="inputWarning"> + <label class="control-label" for="inputWarning2">Input with warning</label> + <input type="text" class="form-control" id="inputWarning2"> <span class="glyphicon glyphicon-warning-sign form-control-feedback"></span> </div> <div class="form-group has-error has-feedback"> - <label class="control-label" for="inputError">Input with error</label> - <input type="text" class="form-control" id="inputError"> + <label class="control-label" for="inputError2">Input with error</label> + <input type="text" class="form-control" id="inputError2"> <span class="glyphicon glyphicon-remove form-control-feedback"></span> </div> </form> </div> {% highlight html %} <div class="form-group has-success has-feedback"> - <label class="control-label" for="inputSuccess">Input with success</label> - <input type="text" class="form-control" id="inputSuccess"> + <label class="control-label" for="inputSuccess2">Input with success</label> + <input type="text" class="form-control" id="inputSuccess2"> <span class="glyphicon glyphicon-ok form-control-feedback"></span> </div> <div class="form-group has-warning has-feedback"> - <label class="control-label" for="inputWarning">Input with warning</label> - <input type="text" class="form-control" id="inputWarning"> + <label class="control-label" for="inputWarning2">Input with warning</label> + <input type="text" class="form-control" id="inputWarning2"> <span class="glyphicon glyphicon-warning-sign form-control-feedback"></span> </div> <div class="form-group has-error has-feedback"> - <label class="control-label" for="inputError">Input with error</label> - <input type="text" class="form-control" id="inputError"> + <label class="control-label" for="inputError2">Input with error</label> + <input type="text" class="form-control" id="inputError2"> <span class="glyphicon glyphicon-remove form-control-feedback"></span> </div> {% endhighlight %} @@ -2036,9 +2036,9 @@ For example, <code><section></code> should be wrapped as inline. <div class="bs-example"> <form class="form-horizontal" role="form"> <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"> - <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> </div> </div> @@ -2047,9 +2047,9 @@ For example, <code><section></code> should be wrapped as inline. {% highlight html %} <form class="form-horizontal" role="form"> <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"> - <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> </div> </div> @@ -2059,8 +2059,8 @@ For example, <code><section></code> should be wrapped as inline. <div class="bs-example"> <form class="form-inline" role="form"> <div class="form-group has-success has-feedback"> - <label class="control-label" for="inputSuccess">Input with success</label> - <input type="text" class="form-control" id="inputSuccess"> + <label class="control-label" for="inputSuccess4">Input with success</label> + <input type="text" class="form-control" id="inputSuccess4"> <span class="glyphicon glyphicon-ok form-control-feedback"></span> </div> </form> @@ -2068,8 +2068,8 @@ For example, <code><section></code> should be wrapped as inline. {% highlight html %} <form class="form-inline" role="form"> <div class="form-group has-success has-feedback"> - <label class="control-label" for="inputSuccess">Input with success</label> - <input type="text" class="form-control" id="inputSuccess"> + <label class="control-label" for="inputSuccess4">Input with success</label> + <input type="text" class="form-control" id="inputSuccess4"> <span class="glyphicon glyphicon-ok form-control-feedback"></span> </div> </form> @@ -3006,7 +3006,7 @@ a { {% endhighlight %} <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> {% highlight css %} .box-shadow(@shadow: 0 1px 3px rgba(0,0,0,.25)) { diff --git a/getting-started.html b/getting-started.html index c43abe197999e049d715456c891c8cf859531cdb..286058ba4f676e5e14fd611e1acc5d96e24c0fca 100644 --- a/getting-started.html +++ b/getting-started.html @@ -884,8 +884,8 @@ if (navigator.userAgent.match(/IEMobile\/10\.0/)) { {% highlight html %} <script> 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)); -if(is_android) { +var isAndroid = (nua.indexOf('Mozilla/5.0') > -1 && nua.indexOf('Android ') > -1 && nua.indexOf('AppleWebKit') > -1 && nua.indexOf('Chrome') === -1); +if (isAndroid) { $('select.form-control').removeClass('form-control').css('width', '100%'); } </script> diff --git a/javascript.html b/javascript.html index d83358a3974a68eadfc2478e70160c95f4879d70..c330a47921f2cf05147efabdfae444b93b168aee 100644 --- a/javascript.html +++ b/javascript.html @@ -245,7 +245,7 @@ $('#myModal').on('show.bs.modal', function (e) { <!-- Large modal --> <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-content"> ... @@ -256,7 +256,7 @@ $('#myModal').on('show.bs.modal', function (e) { <!-- Small modal --> <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-content"> ... @@ -266,13 +266,13 @@ $('#myModal').on('show.bs.modal', function (e) { {% endhighlight %} <!-- 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-content"> <div class="modal-header"> <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 class="modal-body"> ... @@ -280,13 +280,13 @@ $('#myModal').on('show.bs.modal', function (e) { </div><!-- /.modal-content --> </div><!-- /.modal-dialog --> </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-content"> <div class="modal-header"> <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 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>Here's how the affix plugin works:</p> <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>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> diff --git a/less/forms.less b/less/forms.less index 530257499fb7ca2bb809aeed9e1f230f0ce07cf9..aefa5a462c858a6642d0d138eb611e8e2205e9d3 100644 --- a/less/forms.less +++ b/less/forms.less @@ -265,7 +265,7 @@ input[type="checkbox"], // Feedback icon (requires .glyphicon classes) .form-control-feedback { 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; display: block; width: @input-height-base; diff --git a/less/navbar.less b/less/navbar.less index d96f85e30635c086c9d8a972c4ce932c32a46488..621772fbbfb464bd870ca9554eb7931285c81816 100644 --- a/less/navbar.less +++ b/less/navbar.less @@ -212,7 +212,7 @@ // 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). .navbar-nav { diff --git a/less/navs.less b/less/navs.less index e4ac1445952df8bbdd3d14adbc63a97917e2fc38..9e729b39fe5e31b062c5e9644ae2fb58d0b829ec 100644 --- a/less/navs.less +++ b/less/navs.less @@ -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 { &, &:hover, diff --git a/less/panels.less b/less/panels.less index 1b72cebd160666ef56d996adbbd7645d4efa272c..ef878686721ea42e7fa004b785d3d68cc4386dc7 100644 --- a/less/panels.less +++ b/less/panels.less @@ -95,15 +95,17 @@ border: 0; margin-bottom: 0; } - > .table-striped > tbody > tr:last-child, - > .table-responsive > .table-striped > tbody > tr:last-child { - td:first-child, - th:first-child { - border-bottom-left-radius: (@panel-border-radius - 1); - } - td:last-child, - th:last-child { - border-bottom-left-radius: (@panel-border-radius - 1); + > .table-striped, + > .table-responsive > .table-striped { + > tbody > tr:last-child { + td:first-child, + th:first-child { + border-bottom-left-radius: (@panel-border-radius - 1); + } + td:last-child, + 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 { margin-top: 0; margin-bottom: 0; diff --git a/less/type.less b/less/type.less index 9d032b2681c18ce8484935a08ca24d47a6f669dd..5373975d2fa53c10b11ba6af1af5dd2760513adf 100644 --- a/less/type.less +++ b/less/type.less @@ -126,7 +126,7 @@ cite { font-style: normal; } // For now we'll leave these alongside the text classes until v4 when we can // safely shift things around (per SemVer rules). .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. color: #fff; background-color: @brand-primary; diff --git a/test-infra/node_modules_cache.py b/test-infra/node_modules_cache.py index 9d9272fcc1620d4ea9c5c8f848bb430fcb40a6ea..6acddb38a765044ae273bca0014b780975b1ac52 100755 --- a/test-infra/node_modules_cache.py +++ b/test-infra/node_modules_cache.py @@ -3,7 +3,7 @@ from __future__ import absolute_import, unicode_literals, print_function, divisi from sys import argv 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 subprocess import check_call as run @@ -12,7 +12,6 @@ from boto.s3.key import Key from boto.exception import S3ResponseError -NODE_MODULES_TARBALL = 'node_modules.tar.gz' NEED_TO_UPLOAD_MARKER = '.need-to-upload' BYTES_PER_MB = 1024 * 1024 try: @@ -25,7 +24,9 @@ def _sha256_of_file(filename): hasher = sha256() with open(filename, 'rb') as input_file: 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): @@ -35,52 +36,71 @@ def _delete_file_quietly(filename): pass -def _tarball_size(): - kib = stat(NODE_MODULES_TARBALL).st_size // BYTES_PER_MB +def _tarball_size(directory): + kib = stat(_tarball_filename_for(directory)).st_size // BYTES_PER_MB 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__': # Uses environment variables: # AWS_ACCESS_KEY_ID - AWS Access Key ID # AWS_SECRET_ACCESS_KEY - AWS Secret Access Key argv.pop(0) - if len(argv) != 1: - raise SystemExit("USAGE: node_modules_cache.py <download | upload>") - mode = argv.pop() + if len(argv) != 3: + raise SystemExit("USAGE: node_modules_cache.py <download | upload> <dependencies file> <directory>") + mode, dependencies_file, directory = argv conn = S3Connection() bucket = conn.lookup(BUCKET_NAME) if bucket is None: raise SystemExit("Could not access bucket!") - package_json_hash = _sha256_of_file('package.json') - print('sha256(package.json) = ' + package_json_hash) + dependencies_file_hash = _sha256_of_file(dependencies_file) - key = Key(bucket, package_json_hash) + key = Key(bucket, dependencies_file_hash) key.storage_class = 'REDUCED_REDUNDANCY' if mode == 'download': - _delete_file_quietly(NEED_TO_UPLOAD_MARKER) - 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.") + download(directory) elif mode == 'upload': - if isfile(NEED_TO_UPLOAD_MARKER): - print("Creating tarball...") - 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) + if isfile(NEED_TO_UPLOAD_MARKER): # FIXME + upload(directory) else: print("No need to upload anything.") else: