Repository: nprapps/lunchbox Branch: master Commit: 5678043c0387 Files: 120 Total size: 1.2 MB Directory structure: gitextract_pbzm31xv/ ├── .gitignore ├── LICENSE ├── README.md ├── app.py ├── app_config.py ├── fabfile/ │ ├── __init__.py │ ├── flat.py │ ├── render.py │ └── utils.py ├── less/ │ ├── app.less │ ├── factlist.less │ ├── lib/ │ │ ├── bootstrap/ │ │ │ ├── alerts.less │ │ │ ├── badges.less │ │ │ ├── bootstrap.less │ │ │ ├── breadcrumbs.less │ │ │ ├── button-groups.less │ │ │ ├── buttons.less │ │ │ ├── carousel.less │ │ │ ├── close.less │ │ │ ├── code.less │ │ │ ├── component-animations.less │ │ │ ├── dropdowns.less │ │ │ ├── forms.less │ │ │ ├── glyphicons.less │ │ │ ├── grid.less │ │ │ ├── input-groups.less │ │ │ ├── jumbotron.less │ │ │ ├── labels.less │ │ │ ├── list-group.less │ │ │ ├── media.less │ │ │ ├── mixins.less │ │ │ ├── modals.less │ │ │ ├── navbar.less │ │ │ ├── navs.less │ │ │ ├── normalize.less │ │ │ ├── pager.less │ │ │ ├── pagination.less │ │ │ ├── panels.less │ │ │ ├── popovers.less │ │ │ ├── print.less │ │ │ ├── progress-bars.less │ │ │ ├── responsive-utilities.less │ │ │ ├── scaffolding.less │ │ │ ├── tables.less │ │ │ ├── theme.less │ │ │ ├── thumbnails.less │ │ │ ├── tooltip.less │ │ │ ├── type.less │ │ │ ├── utilities.less │ │ │ ├── variables.less │ │ │ └── wells.less │ │ ├── font-awesome/ │ │ │ ├── bootstrap.less │ │ │ ├── bordered-pulled.less │ │ │ ├── core.less │ │ │ ├── extras.less │ │ │ ├── fixed-width.less │ │ │ ├── font-awesome-ie7.less │ │ │ ├── font-awesome.less │ │ │ ├── icons.less │ │ │ ├── larger.less │ │ │ ├── list.less │ │ │ ├── mixins.less │ │ │ ├── path.less │ │ │ ├── rotated-flipped.less │ │ │ ├── spinning.less │ │ │ ├── stacked.less │ │ │ └── variables.less │ │ └── prefixer.less │ ├── medium.less │ ├── quotable.less │ ├── variables.less │ └── waterbug.less ├── package.json ├── packager-config.json ├── render_utils.py ├── requirements.txt ├── static.py ├── templates/ │ ├── _base.html │ ├── _css_header.css │ ├── _fonts.html │ ├── _js_header.js │ ├── _meta.html │ ├── factlist.html │ ├── index.html │ ├── oauth/ │ │ ├── _oauth_base.html │ │ ├── authenticate.html │ │ ├── oauth.html │ │ └── warning.html │ ├── quotable.html │ └── waterbug.html ├── tests/ │ ├── __init__.py │ └── test_app.py └── www/ ├── css/ │ └── .placeholder ├── font/ │ └── FontAwesome.otf ├── img/ │ └── icon-lunchbox.icns ├── js/ │ ├── app.js │ ├── factlist.js │ ├── lib/ │ │ ├── bootstrap.js │ │ ├── fileinput.js │ │ ├── html2canvas.js │ │ ├── jquery.js │ │ ├── modernizr.js │ │ ├── moment.js │ │ ├── underscore.js │ │ └── webfont.js │ ├── quotable.js │ ├── waterbug-config.js │ └── waterbug.js ├── main.js ├── package.json └── test/ ├── SpecRunner.html ├── fixtures/ │ └── example.json ├── lib/ │ ├── jasmine-1.3.1/ │ │ ├── MIT.LICENSE │ │ ├── jasmine-html.js │ │ ├── jasmine.css │ │ └── jasmine.js │ ├── jasmine-jquery.js │ └── sinon-1.5.2.js └── spec/ ├── _tests.js └── app.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.py[co] *.sw[op] # Packages *.egg *.egg-info dist build eggs parts bin var sdist develop-eggs .installed.cfg node_modules # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox #Translations *.mo .DS_store # Rendered files www/*.html www/css/*.min.*.css www/css/*.min.css www/css/*.less.css www/js/*.min.*.js www/js/*.min.js www/js/templates.js www/js/app_config.js www/js/copy.js www/test/test.html www/comments/index.html confs/rendered/* www/factlist www/quotable www/waterbug # Local data data/gdoc*.csv data/copy.xls data/copy.xlsx www/assets/* !www/assets/assetsignore www/live-data/* # built apps electron ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2015 NPR Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ Lunchbox ============= * [What is this?](#what-is-this) * [Assumptions?](#assumptions) * [What's in here?](#what-is-in-here) * [Quick start](#quick-start) * [Configuration](#configuration) * [Deploy the desktop app](#deploy-the-desktop-app) * [About](#about) What is this? ------------- **Lunchbox** is a suite of tools to create images intended for social media sharing. It includes: * *Quotable*: Converts quoted text into a branded image. * *Factlist*: Produces a branded image with a list of items. * *Waterbug*: Creates a watermarked image with attribution. Assumptions ------------- **Lunchbox** is a customizable toolset deployable as a web app. The following instructions are meant for developers setting up and customizing the app for their organization. For end-users of the tools, see [usage guidelines](http://blog.apps.npr.org/lunchbox). The following things are assumed to be true in this documentation. * You are running OSX. * You are using Python 2.7. (Probably the version that came OSX.) * You have [virtualenv](https://pypi.python.org/pypi/virtualenv) and [virtualenvwrapper](https://pypi.python.org/pypi/virtualenvwrapper) installed and working. What's in here? ------------- * ``fabfile`` -- [Fabric](http://docs.fabfile.org/en/latest/) commands for automating setup and deployment. * ``less`` -- Application styles and [Bootstrap](http://getbootstrap.com/css/) less files. * ``templates`` -- HTML ([Jinja2](http://jinja.pocoo.org/docs/)) templates, to be compiled locally. * ``www`` -- App assets and rendered files. * ``Lunchbox Setup.exe`` -- Lunchbox Demo installer for Windows. * ``Lunchbox.dmg`` -- Lunchbox Demo installer for OSX. * ``app.py`` -- A [Flask](http://flask.pocoo.org/) app for rendering the project locally. * ``app_config.py`` -- Configuration variables for the Flask app. * ``package.json`` -- Node dependencies and scripts for building [Electron](https://github.com/atom/electron) app. * ``packager-config.json`` -- Configuration for create installers with [Electron](https://github.com/atom/electron). * ``render_utils.py`` -- Helper functions for baking out Flask app. * ``requirements.txt`` -- Python requirements. * ``static.py`` -- Routes for static files in Flask app. Quick Start ------------- Clone or fork this repo (NPR users: Use the `npr` branch), then do the following: Change to the project directory you just cloned: ``` cd lunchbox ``` Create a new virtualenv to get an isolated Python environment: | with virtualenvwrapper | with Anaconda | |------------------------|---------------| | ```mkvirtualenv lunchbox``` | ```conda create --name lunchbox python=2.7``` | Then, activate your virtual environment. | with virtualenvwrapper | with Anaconda | |------------------------|---------------| | ```workon lunchbox``` | ```conda activate lunchbox``` | Next, install Python dependencies: ``` pip install -r requirements.txt ``` Install the Node.js dependencies (most importantly, Less): ``` npm install ``` Then run the app: ``` fab app ``` Visit [localhost:8000](http://127.0.0.1:8000/) in your browser to see the app. Configuration ------------- You can skip configuration if you just want to [deploy Lunchbox](#deploy-the-desktop-app) and start using it with the application's default branding (or you can [download the Demo](http://blog.apps.npr.org/lunchbox/) ). Configuration options allow you to tailor the app to match your organization's branding and theme. ### Assets If you are customizing the branding of the apps, you will probably want to use your organization's web fonts and logos. For fonts, we provide a Jinja template at `templates/_fonts.html` using Typekit's [webfontloader](https://github.com/typekit/webfontloader) for loading fonts from Google, Typekit, or custom stylesheets. Then, the fonts will be available in the CSS and JavaScript in all of the apps. For your organization's logos, you can provide SVGs or PNGs. Make sure that there is no whitespace around the logo so that the padding performs properly. You can place them anywhere in the `www` folder as long as you link them correctly when you [define your global variables](#define-your-global-variables), but we recommend `www/img`. For Waterbug, you will want to have a white version and a black version of your logo so that you can choose the appropriate version for light and dark photos. ### Define global variables There are two places where variables are defined, one place for Quotable and Factlist and one place for Waterbug. #### Quotable/Factlist For Quotable and Factlist, all configuration takes places in `less/variables.less`. You can define font families, establish the default background color/text color and define the logo used on the images. Importantly, if you use a custom logo, you will also need to explicitly define the width and height of the logo in both square crop and 16:9 crop scenarios. The variables at the top of the file will do this: ``` @logo-path: url('../path/to/logo.svg'); @logo-sq-width: 145px; @logo-sq-height: 48px; @logo-16x9-width: 121px; @logo-16x9-height: 40px; ``` Additionally, you can fine-tune various aspects of Quotable and Factlist using the app-specific variables also listed in the file. The defaults should work well out of the box, but your organization's logo or font may require tweaks. #### Waterbug Waterbug has a different configuration system because it cannot be controlled through CSS. To customize Waterbug, go to `www/js/waterbug-config.js` and customize the variables at the top of the file. In this file, you can define the logos used and the sizes with which they render by editing the `logos` object. ``` var logos = { 'name-of-logo': { whitePath: '../path/to/logo-white.svg', // path to white logo blackPath: '../path/to/logo-black.svg', // path to black logo w: 200, // width of logo h: 67, // height of logo display: 'Name of logo' // how the button toggle will appear in the UI }, 'name-of-second-logo': { whitePath: '../path/to/second-logo-white.svg', blackPath: '../path/to/second-logo-black.svg', w: 150, h: 51, display: 'Name of second logo' } }; ``` If you have more than one logo, the UI will automatically add toggle buttons so that you can switch between logos on the fly. Additionally, You can change every property of the font rendering (font face, size, shadow, etc.) as well as the padding around all of the elements (`elementPadding`) in the image and the export width of the image (`canvasWidth`). You will want to configure the copyright options for Waterbug based on the photo providers your news organization can use. This is defined in an large object that contains an object for each copyright option. The boolean values control the behavior of the form: ``` // copyright options var orgName = 'Your News Organization'; var freelanceString = 'for ' + orgName; var copyrightOptions = { 'internal': { showPhotographer: true, // show the photographer input box showSource: false, // show the source input box photographerRequired: false, // require a photographer sourceRequired: false, // require a source source: orgName, // How the source should appear on the image, e.g. 'NPR' display: orgName, // How the option will appear in the dropdown menu }, 'freelance': { showPhotographer: true, showSource: false, photographerRequired: true, sourceRequired: false, source: freelanceString, display: 'Freelance' }, 'ap': { showPhotographer: true, showSource: false, photographerRequired: false, sourceRequired: false, source: 'AP', display: 'AP' }, 'getty': { showPhotographer: true, showSource: false, photographerRequired: false, sourceRequired: false, source: 'Getty Images', display: 'Getty' }, 'thirdParty': { showPhotographer: true, showSource: true, photographerRequired: false, sourceRequired: true, source: '', display: 'Third Party/Courtesy' } } ``` The app will automatically add all of your copyright options to the dropdown menu. Also, it will perform form validation based on the boolean values above. Finally, you can configure the application defaults. Ensure that the logo and image paths point to existing files: ``` // app load defaults var currentCrop = 'twitter'; // default crop size var currentLogo = 'lunchbox'; // default logo slug var currentLogoColor = 'white'; // default logo color var currentTextColor = 'white'; // default text color var defaultImage = '../img/test-kitten.jpg'; // path to image to load as test image var defaultLogo = logos[currentLogo]['whitePath'] // path to default logo ``` At the bottom of the form, you will notice a Sharing Guidelines section. To edit that section, you can just update the list in `templates/waterbug.html`. ### Multiple Themes For Quotable and Factlist, you can provide up to three themes in addition to the default theme if your news organization requires different branding for different accounts (think [NPR](http://twitter.com/npr) vs. [NPR Music](http://twitter.com/nprmusic)). In `less/variables.less`, you can define themes at the bottom of the file. For each theme, you can change the background color, text color, and logo: ``` @theme2-bg-color: #41474E; @theme2-text-color: #dbe0e6; @theme2-logo-path: url('../img/icon-socializr-white.svg'); @theme2-sq-logo-width: 145px; @theme2-sq-logo-height: 48px; @theme2-16x9-logo-width: 121px; @theme2-16x9-logo-height: 40px; ``` In the form UI, you can change the display of the theme selection buttons in each app's HTML template (`templates/quotable.html`, `templates/factlist.html`). Be sure not to change the ID attribute of the button, as these IDs control the JavaScript that adds and removes classes on the image. Deployment =============== We support two separate deployment options: Amazon S3 and any fileserver that you can SSH into. Deploy to Amazon S3 ------------------- For Amazon S3, ensure that you've installed the AWS command-line interface (if you're using brew, you can use `brew install awscli`), and set up a new S3 bucket. Store your AWS Access Key ID and Secret Access Key as environment variables by running the following in Terminal: ``` export AWS_ACCESS_KEY_ID="YOURACCESSKEYID" export AWS_SECRET_ACCESS_KEY="YOURSECRETACCESSKEY" ``` Then, in `app_config.py`, change your staging and production S3 targets: ``` PRODUCTION_S3_BUCKET = 'your.bucket.org' STAGING_S3_BUCKET = 'stage-your.bucket.org' ``` Note: The placeholder is the name of your bucket and not its url. For a simple S3 bucket with no custom DNS named `lunchbox-s3`, you would use `lunchbox-s3` instead of `s3.amazonaws.com/lunchbox-s3`, for instance. With these variables set, you can run `fab [production/staging] master deploy` to deploy Lunchbox to your S3 bucket. Deploy to other file server --------------------------- For other file servers, you can change the following app_config variables: ``` FILE_SERVER_USER = 'ubuntu' # set this to the user you use to SSH onto the server FILE_SERVER = 'your.fileserver.org' # set this to either the hostname or IP address of your file server FILE_SERVER_PATH = '~/www' # set this to the path that your server serves files to the web from ``` Then, you can run `fab fileserver master deploy`. This will `rsync` the rendered files to `FILE_SERVER_PATH/lunchbox`. Known Issues ------------- - Firefox compatibility with SVG: Firefox is not capable of rendering SVG logos with Quotable or Factlist. About ------------- Lunchbox consolidates [NPR](https://github.com/nprapps/)’s [Quotable](https://github.com/nprapps/quotable), [Factlist](https://github.com/nprapps/factlist) and [Waterbug](https://github.com/nprapps/waterbug), apps into a suite of tools for the newsroom. It was worked on during the [OpenNews](http://opennews.org) Portland Code Convening on July 23-24, 2015. Additional contributors: - [Jason Emory Parker](https://github.com/postandcourier) - [Ben Chartoff](https://github.com/bchartoff) - [Chris Barna](https://github.com/ctbarna) - [David Ryan](https://github.com/dryanmedia) - [Davis Shaver](https://github.com/davisshaver) ================================================ FILE: app.py ================================================ #!/usr/bin/env python """ Example application views. Note that `render_template` is wrapped with `make_response` in all application routes. While not necessary for most Flask apps, it is required in the App Template for static publishing. """ from datetime import datetime import app_config import json import static from flask import Flask, make_response, render_template from render_utils import make_context, smarty_filter, urlencode_filter from werkzeug.debug import DebuggedApplication app = Flask(__name__) app.debug = app_config.DEBUG app.add_template_filter(smarty_filter, name='smarty') app.add_template_filter(urlencode_filter, name='urlencode') @app.route('/') @app.route('/index.html') def index(): """ Example view demonstrating rendering a simple HTML page. """ context = make_context() context['name'] = 'Lunchbox' context['id'] = 'home' context['now'] = datetime.now().strftime('%B %-d, %Y') return make_response(render_template('index.html', **context)) @app.route('/factlist/index.html') def factlist(): context = make_context() context['name'] = 'Factlist' context['id'] = context['name'] return make_response(render_template('factlist.html', **context)) @app.route('/quotable/index.html') def quotable(): context = make_context() context['name'] = 'Quotable' context['id'] = context['name'] return make_response(render_template('quotable.html', **context)) @app.route('/waterbug/index.html') def waterbug(): context = make_context() context['name'] = 'Waterbug' context['id'] = context['name'] return make_response(render_template('waterbug.html', **context)) app.register_blueprint(static.static) # Enable Werkzeug debug pages if app_config.DEBUG: wsgi_app = DebuggedApplication(app, evalex=False) else: wsgi_app = app # Catch attempts to run the app directly if __name__ == '__main__': print 'This command has been removed! Please run "fab app" instead!' ================================================ FILE: app_config.py ================================================ #!/usr/bin/env python """ Project-wide application configuration. DO NOT STORE SECRETS, PASSWORDS, ETC. IN THIS FILE. They will be exposed to users. Use environment variables instead. See get_secrets() below for a fast way to access them. """ import os """ NAMES """ # Project name to be used in urls # Use dashes, not underscores! PROJECT_SLUG = 'lunchbox-test' # Project name to be used in file paths PROJECT_FILENAME = 'lunchbox' # The name of the repository containing the source REPOSITORY_NAME = 'lunchbox' GITHUB_USERNAME = 'nprapps' REPOSITORY_URL = 'git@github.com:%s/%s.git' % (GITHUB_USERNAME, REPOSITORY_NAME) REPOSITORY_ALT_URL = None # 'git@bitbucket.org:nprapps/%s.git' % REPOSITORY_NAME' DEV_CONTACT = 'EDIT THIS IN APP_CONFIG.PY' """ DEPLOYMENT """ PRODUCTION_S3_BUCKET = 'apps.npr.org' STAGING_S3_BUCKET = 'stage-apps.npr.org' DEFAULT_MAX_AGE = 20 FILE_SERVER_USER = 'ubuntu' FILE_SERVER = 'tools.apps.npr.org' FILE_SERVER_PATH = '~/www' # These variables will be set at runtime. See configure_targets() below S3_BUCKET = None S3_BASE_URL = None S3_DEPLOY_URL = None DEBUG = True """ Utilities """ def get_secrets(): """ A method for accessing our secrets. """ secrets_dict = {} for k,v in os.environ.items(): if k.startswith(PROJECT_SLUG): k = k[len(PROJECT_SLUG) + 1:] secrets_dict[k] = v return secrets_dict def configure_targets(deployment_target): """ Configure deployment targets. Abstracted so this can be overriden for rendering before deployment. """ global S3_BUCKET global S3_BASE_URL global S3_DEPLOY_URL global DEBUG global DEPLOYMENT_TARGET global ASSETS_MAX_AGE if deployment_target == 'electron': S3_BUCKET = None S3_BASE_URL = None S3_DEPLOY_URL = None DEBUG = False ASSETS_MAX_AGE = 0 if deployment_target == 'fileserver': S3_BUCKET = None S3_BASE_URL = None S3_DEPLOY_URL = None DEBUG = False ASSETS_MAX_AGE = 0 if deployment_target == 'production': S3_BUCKET = PRODUCTION_S3_BUCKET S3_BASE_URL = 'http://%s/%s' % (S3_BUCKET, PROJECT_SLUG) S3_DEPLOY_URL = 's3://%s/%s' % (S3_BUCKET, PROJECT_SLUG) DEBUG = False ASSETS_MAX_AGE = 86400 elif deployment_target == 'staging': S3_BUCKET = STAGING_S3_BUCKET S3_BASE_URL = 'http://%s/%s' % (S3_BUCKET, PROJECT_SLUG) S3_DEPLOY_URL = 's3://%s/%s' % (S3_BUCKET, PROJECT_SLUG) DEBUG = True ASSETS_MAX_AGE = 20 else: S3_BUCKET = None S3_BASE_URL = 'http://127.0.0.1:8000' S3_DEPLOY_URL = None DEBUG = True ASSETS_MAX_AGE = 20 DEPLOYMENT_TARGET = deployment_target """ Run automated configuration """ DEPLOYMENT_TARGET = os.environ.get('DEPLOYMENT_TARGET', None) configure_targets(DEPLOYMENT_TARGET) ================================================ FILE: fabfile/__init__.py ================================================ #!/usr/bin/env python from datetime import datetime import json import os from boto.s3.key import Key from fabric.api import local, require, settings, task from fabric.state import env from termcolor import colored import app_config # Other fabfiles import flat import render import utils # Bootstrap can only be run once, then it's disabled if app_config.PROJECT_SLUG == '$NEW_PROJECT_SLUG': import bootstrap """ Base configuration """ env.forward_agent = True env.hosts = [] env.settings = None """ Environments Changing environment requires a full-stack test. An environment points to both a server and an S3 bucket. """ @task def electron(): """ Run as though building electron app. """ env.settings = 'electron' app_config.configure_targets(env.settings) @task def fileserver(): """ Run as though building electron app. """ env.settings = 'fileserver' app_config.configure_targets(env.settings) @task def production(): """ Run as though on production. """ env.settings = 'production' app_config.configure_targets(env.settings) @task def staging(): """ Run as though on staging. """ env.settings = 'staging' app_config.configure_targets(env.settings) """ Branches Changing branches requires deploying that branch to a host. """ @task def stable(): """ Work on stable branch. """ env.branch = 'stable' @task def master(): """ Work on development branch. """ env.branch = 'master' @task def branch(branch_name): """ Work on any specified branch. """ env.branch = branch_name """ Running the app """ @task def app(port='8000'): """ Serve app.py. """ if env.settings: local("DEPLOYMENT_TARGET=%s bash -c 'gunicorn -b 0.0.0.0:%s --timeout 3600 --debug --reload app:wsgi_app'" % (env.settings, port)) else: local('gunicorn -b 0.0.0.0:%s --timeout 3600 --debug --reload app:wsgi_app' % port) @task def public_app(port='8001'): """ Serve public_app.py. """ if env.settings: local("DEPLOYMENT_TARGET=%s bash -c 'gunicorn -b 0.0.0.0:%s --timeout 3600 --debug --reload public_app:wsgi_app'" % (env.settings, port)) else: local('gunicorn -b 0.0.0.0:%s --timeout 3600 --debug --reload public_app:wsgi_app' % port) @task def tests(): """ Run Python unit tests. """ local('nosetests') """ Deployment Changes to deployment requires a full-stack test. Deployment has two primary functions: Pushing flat files to S3 and deploying code to a remote server if required. """ @task def deploy(remote='origin', reload=False): """ Deploy the latest app to S3 and, if configured, to our servers. """ require('settings', provided_by=[production, staging, electron]) render.render_all() if env.settings == 'electron': if not os.path.exists('electron'): os.makedirs('electron') local('npm run-script pack') if env.settings == 'fileserver': local('rsync -vr www/ %s@%s:%s/%s' % ( app_config.FILE_SERVER_USER, app_config.FILE_SERVER, app_config.FILE_SERVER_PATH, app_config.PROJECT_SLUG )) if env.settings == 'production' or env.settings == 'staging': flat.deploy_folder( app_config.S3_BUCKET, 'www', app_config.PROJECT_SLUG, headers={ 'Cache-Control': 'max-age=%i' % app_config.DEFAULT_MAX_AGE }, ignore=['www/img/*', 'www/live-data/*'] ) flat.deploy_folder( app_config.S3_BUCKET, 'www/img', '%s/img' % app_config.PROJECT_SLUG, headers={ 'Cache-Control': 'max-age=%i' % app_config.ASSETS_MAX_AGE } ) """ Destruction Changes to destruction require setup/deploy to a test host in order to test. Destruction should remove all files related to the project from both a remote host and S3. """ @task def shiva_the_destroyer(): """ Deletes the app from s3 """ require('settings', provided_by=[production, staging]) utils.confirm( colored("You are about to destroy everything deployed to %s for this project.\nDo you know what you're doing?')" % app_config.DEPLOYMENT_TARGET, "red") ) with settings(warn_only=True): flat.delete_folder(app_config.S3_BUCKET, app_config.PROJECT_SLUG) if app_config.DEPLOY_TO_SERVERS: servers.delete_project() if app_config.DEPLOY_CRONTAB: servers.uninstall_crontab() if app_config.DEPLOY_SERVICES: servers.nuke_confs() ================================================ FILE: fabfile/flat.py ================================================ #!/usr/bin/env python import copy from cStringIO import StringIO from fnmatch import fnmatch import gzip import hashlib import mimetypes import os from boto.s3.key import Key import app_config import utils GZIP_FILE_TYPES = ['.html', '.js', '.json', '.css', '.xml'] class FakeTime: def time(self): return 1261130520.0 # Hack to override gzip's time implementation # See: http://stackoverflow.com/questions/264224/setting-the-gzip-timestamp-from-python gzip.time = FakeTime() def deploy_file(bucket, src, dst, headers={}): """ Deploy a single file to S3, if the local version is different. """ k = bucket.get_key(dst) s3_md5 = None if k: s3_md5 = k.etag.strip('"') else: k = Key(bucket) k.key = dst file_headers = copy.copy(headers) if 'Content-Type' not in headers: file_headers['Content-Type'] = mimetypes.guess_type(src)[0] # Gzip file if os.path.splitext(src)[1].lower() in GZIP_FILE_TYPES: file_headers['Content-Encoding'] = 'gzip' with open(src, 'rb') as f_in: contents = f_in.read() output = StringIO() f_out = gzip.GzipFile(filename=dst, mode='wb', fileobj=output) f_out.write(contents) f_out.close() local_md5 = hashlib.md5() local_md5.update(output.getvalue()) local_md5 = local_md5.hexdigest() if local_md5 == s3_md5: print 'Skipping %s (has not changed)' % src else: print 'Uploading %s --> %s (gzipped)' % (src, dst) k.set_contents_from_string(output.getvalue(), file_headers, policy='public-read') # Non-gzip file else: with open(src, 'rb') as f: local_md5 = hashlib.md5() local_md5.update(f.read()) local_md5 = local_md5.hexdigest() if local_md5 == s3_md5: print 'Skipping %s (has not changed)' % src else: print 'Uploading %s --> %s' % (src, dst) k.set_contents_from_filename(src, file_headers, policy='public-read') def deploy_folder(bucket_name, src, dst, headers={}, ignore=[]): """ Deploy a folder to S3, checking each file to see if it has changed. """ to_deploy = [] for local_path, subdirs, filenames in os.walk(src, topdown=True): rel_path = os.path.relpath(local_path, src) for name in filenames: if name.startswith('.'): continue src_path = os.path.join(local_path, name) skip = False for pattern in ignore: if fnmatch(src_path, pattern): skip = True break if skip: continue if rel_path == '.': dst_path = os.path.join(dst, name) else: dst_path = os.path.join(dst, rel_path, name) to_deploy.append((src_path, dst_path)) bucket = utils.get_bucket(bucket_name) for src, dst in to_deploy: deploy_file(bucket, src, dst, headers) def delete_folder(bucket_name, dst): """ Delete a folder from S3. """ bucket = utils.get_bucket(bucket_name) for key in bucket.list(prefix='%s/' % dst): print 'Deleting %s' % (key.key) key.delete() ================================================ FILE: fabfile/render.py ================================================ #!/usr/bin/env python """ Commands for rendering various parts of the app stack. """ from glob import glob import os from fabric.api import local, task import app def _fake_context(path): """ Create a fact request context for a given path. """ return app.app.test_request_context(path=path) def _view_from_name(name): """ Determine what module a view resides in, then get a reference to it. """ bits = name.split('.') # Determine which module the view resides in if len(bits) > 1: module, name = bits else: module = 'app' return globals()[module].__dict__[name] @task def less(): """ Render LESS files to CSS. """ for path in glob('less/*.less'): filename = os.path.split(path)[-1] name = os.path.splitext(filename)[0] out_path = 'www/css/%s.less.css' % name try: local('node_modules/less/bin/lessc %s %s' % (path, out_path)) except: print 'It looks like "lessc" isn\'t installed. Try running: "npm install"' raise @task def app_config_js(): """ Render app_config.js to file. """ from static import _app_config_js with _fake_context('/js/app_config.js'): response = _app_config_js() with open('www/js/app_config.js', 'w') as f: f.write(response.data) @task(default=True) def render_all(): """ Render HTML templates and compile assets. """ from flask import g less() app_config_js() compiled_includes = {} # Loop over all views in the app for rule in app.app.url_map.iter_rules(): rule_string = rule.rule name = rule.endpoint # Skip utility views if name == 'static' or name.startswith('_'): print 'Skipping %s' % name continue # Convert trailing slashes to index.html files if rule_string.endswith('/'): filename = 'www' + rule_string + 'index.html' elif rule_string.endswith('.html'): filename = 'www' + rule_string else: print 'Skipping %s' % name continue # Create the output path dirname = os.path.dirname(filename) if not (os.path.exists(dirname)): os.makedirs(dirname) print 'Rendering %s' % (filename) # Render views, reusing compiled assets with _fake_context(rule_string): g.compile_includes = True g.compiled_includes = compiled_includes view = _view_from_name(name) content = view().data compiled_includes = g.compiled_includes # Write rendered view # NB: Flask response object has utf-8 encoded the data with open(filename, 'w') as f: f.write(content) ================================================ FILE: fabfile/utils.py ================================================ #!/usr/bin/env python import boto from boto.s3.connection import OrdinaryCallingFormat """ Utilities used by multiple commands. """ from fabric.api import prompt def confirm(message): """ Verify a users intentions. """ answer = prompt(message, default="Not at all") if answer.lower() not in ('y', 'yes', 'buzz off', 'screw you'): exit() def get_bucket(bucket_name): """ Established a connection and gets s3 bucket """ if '.' in bucket_name: s3 = boto.connect_s3(calling_format=OrdinaryCallingFormat()) else: s3 = boto.connect_s3() return s3.get_bucket(bucket_name) ================================================ FILE: less/app.less ================================================ @import "less/lib/bootstrap/bootstrap"; @import "less/lib/font-awesome/font-awesome"; @import "less/medium"; @import "less/variables"; body { font-family: @font-family !important; font-size: @base-font-size !important; } canvas { display: none; background: @musicDark; } header { background: @gray-darker; margin: 0; padding: 25px @grid-gutter-width / 2; color: @gray-lighter; .home & { @media screen and (min-width: 700px){ padding-top: 44px; padding-bottom: 44px; } .logo-wrapper { width: 100%; text-align: center; img { height: 150px; width: auto; } } } a { color: @gray-lighter; &:hover { color: @gray; text-decoration: none; } } .nav-links { margin: 0 0 11px 0; padding: 0; @media screen and (min-width: 700px){ margin: 0 auto; padding: 0 @grid-gutter-width / 2; float: right; } a { line-height: 32px; margin-right: @grid-gutter-width / 2; font-size: 16px; text-transform: lowercase; -webkit-font-smoothing: antialiased; opacity: 0.7; &.active { font-weight: bold; } &:last-child { margin-right: 0; } &:hover { color: #eee; opacity: 0.5; } @media screen and (min-width: 700px){ text-align: right; } } } h1 { display: inline-block; text-transform: uppercase; font-weight: normal; line-height: 32px; font-size: @font-size-h3; letter-spacing: 2px; margin: 0; -webkit-font-smoothing: antialiased; font-family: @font-family; .home & { display: block; text-align: center; } span.help { font-size: 14px; margin-left: 11px; opacity: 0.7; text-transform: none; } @media screen and (min-width: 700px){ font-size: @font-size-h1; } } } .home #content { padding-top: 33px; padding-bottom: 33px; .poster-wrapper { text-align: center; margin-bottom: 15px; a:hover { text-decoration: none; img { opacity: 0.7; } } img { display: block; margin: 0 auto 22px auto; } h3 { letter-spacing: 1px; margin: 0 0 11px 0; font-size: 24px; font-weight: 600; text-transform: uppercase; -webkit-font-smoothing: antialiased; } p { color: #666; font-size: 14px; margin: 0; i { font-style: italic; color: #999; display: block; font-size: 13px; margin-top: 5px; } } } } .content { display: table; width: 100%; max-width: 1200px; min-width: 640px; margin: 0 auto; .poster-wrapper, .container { display: table-cell; vertical-align: top; } .poster-wrapper { width: 640px; } @media screen and (max-width: @screen-sm-max){ display: block; .poster-wrapper, .container { display: block; } } @media screen and (max-width: @screen-xs-max){ .controls-wrapper { font-size: 250%; .btn { font-size: 100%; height: 88px; min-width: 88px; } } #fontsize { display: block; width: 90%; } } .touch & { input[type="range"] { background: #fff; width: 100%; height: 88px; -webkit-appearance: none; border-radius: 8px; -moz-border-radius: 8px; -wekkit-border-radius: 8px; border: 1px solid #ddd; } input[type="range"]::-webkit-slider-thumb { -webkit-appearance:none !important; width:88px; height:88px; -webkit-appearance: none; border-radius: 8px; -moz-border-radius: 8px; -wekkit-border-radius: 8px; border:none; background: #aaa; } @media screen and (min-width: @screen-sm) { input[type="range"]{ height: 44px; } input[type="range"]::-webkit-slider-thumb { width:44px; height:44px; } } } } .controls-wrapper { padding-top: @line-height; small { font-size: 100%; opacity: 0.5; } .btn-group { -webkit-font-smoothing: antialiased; } .btn-group, .btn-group + .btn { margin-right: @padding / 2; } .btn-group-vertical .btn { text-align: left; } .form-group, .btn-group { margin-bottom: @line-height / 2; } .btn-group, .btn-group-vertical, .input-group .btn { -webkit-font-smoothing: antialiased; } .input-group .btn:focus { outline: none; } .btn-group-sm > .btn { font-size: 13px; padding: 5px; } .btn:active, .btn.active { outline: 0; background-image: none; -webkit-box-shadow: none; box-shadow: none; } input[type="range"] { width: 100%; } label { display: block; font-weight: normal; letter-spacing: 0.1rem; color: #000; &.btn { color: #fff; } .tooltip { text-transform: none; letter-spacing: 0; } } .help { color: @gray-light; } } .warning { font-size: 12px; font-style: italic; margin-top: 6px; } ================================================ FILE: less/factlist.less ================================================ @import "less/lib/bootstrap/bootstrap"; @import "less/lib/font-awesome/font-awesome"; @import "less/medium"; @import "less/variables"; // Global variables @img-root: "../assets"; @icon-sprite: "data:image/png;base64,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"; html { min-width: 680px; } body { background: @gray-lighter; min-width: 680px; } canvas { display: none; background: @musicDark; } .content { display: table; width: 100%; max-width: 1200px; min-width: 640px; margin: 0 auto; .poster-wrapper, .container { display: table-cell; vertical-align: top; } .poster-wrapper { width: 640px; } @media screen and (max-width: @screen-sm-max){ display: block; .poster-wrapper, .container { display: block; } } @media screen and (max-width: @screen-xs-max){ .controls-wrapper { font-size: 250%; .btn { font-size: 100%; height: 88px; min-width: 88px; } } #fontsize { display: block; width: 90%; } } .touch & { input[type="range"]{ background: #fff; width: 100%; height: 88px; -webkit-appearance: none; border-radius: 8px; -moz-border-radius: 8px; -wekkit-border-radius: 8px; border: 1px solid #ddd; } input[type="range"]::-webkit-slider-thumb{ -webkit-appearance:none !important; width:88px; height:88px; -webkit-appearance: none; border-radius: 8px; -moz-border-radius: 8px; -wekkit-border-radius: 8px; border:none; background: #aaa; } @media screen and (min-width: @screen-sm){ input[type="range"]{ height: 44px; } input[type="range"]::-webkit-slider-thumb { width:44px; height:44px; } } } } .controls-wrapper { padding-top: @line-height; small { font-size: 100%; opacity: 0.5; } .btn-group, .btn-group + .btn { margin-right: @padding / 2; } .btn-group-vertical .btn { text-align: left; } .form-group, .btn-group { margin-bottom: @line-height / 2; } .btn-group, .btn-group-vertical, .input-group .btn { -webkit-font-smoothing: antialiased; } .input-group .btn:focus { outline: none; } .btn-group-sm > .btn { font-size: 13px; padding: 5px; } .btn:active, .btn.active { outline: 0; background-image: none; -webkit-box-shadow: none; box-shadow: none; } label { display: block; font-weight: normal; font-family: @condensed-font-family; letter-spacing: 0.1rem; .tooltip { text-transform: none; letter-spacing: 0; } } .help { color: @gray-light; } } .poster { height: 640px; width: 640px; background: @bg-color; margin: @line-height; position: relative; padding: @padding * 2; font-size: @base-font-size * 2; line-height: 1 !important; overflow: hidden; color: @text-color; &.poster-theme1 { background: @bg-color; color: @text-color; .logo-wrapper { background-image: @logo-path; background-repeat: no-repeat; background-size: @logo-sq-width @logo-sq-height; width: @logo-sq-width + @padding; height: @logo-sq-height + @padding; .sixteen-by-nine& { background-size: @logo-16x9-width @logo-16x9-height; width: @logo-16x9-width + @padding; height: @logo-16x9-height + @padding; } } } &.poster-theme2 { background: @theme2-bg-color; color: @theme2-text-color; .logo-wrapper { background: @theme2-bg-color; color: @theme2-text-color; background-image: @theme2-logo-path; background-repeat: no-repeat; background-size: @theme2-sq-logo-width @theme2-sq-logo-height; width: @theme2-sq-logo-width + @padding; height: @theme2-sq-logo-height + @padding; .sixteen-by-nine& { background-size: @theme2-16x9-logo-width @theme2-16x9-logo-height; width: @theme2-16x9-logo-width + @padding; height: @theme2-16x9-logo-height + @padding; } } } &.poster-theme3 { background: @theme3-bg-color; color: @theme3-text-color; .logo-wrapper { background: @theme3-bg-color; color: @theme3-text-color; background-image: @theme3-logo-path; background-repeat: no-repeat; background-size: @theme3-sq-logo-width @theme3-sq-logo-height; width: @theme3-sq-logo-width + @padding; height: @theme3-sq-logo-height + @padding; .sixteen-by-nine& { background-size: @theme3-16x9-logo-width @theme3-16x9-logo-height; width: @theme3-16x9-logo-width + @padding; height: @theme3-16x9-logo-height + @padding; } } } &.poster-theme4 { background: @theme4-bg-color; color: @theme4-text-color; .logo-wrapper { background: @theme4-bg-color; color: @theme4-text-color; background-image: @theme4-logo-path; background-repeat: no-repeat; background-size: @theme4-sq-logo-width @theme4-sq-logo-height; width: @theme4-sq-logo-width + @padding; height: @theme4-sq-logo-height + @padding; .sixteen-by-nine& { background-size: @theme4-16x9-logo-width @theme4-16x9-logo-height; width: @theme4-16x9-logo-width + @padding; height: @theme4-16x9-logo-height + @padding; } } } &.sixteen-by-nine { height: 360px; padding: @padding; .quote& { .left-quote { top: @padding; left: @padding; } } blockquote { margin-bottom: 20px; } .logo-wrapper { padding: @padding; img { width: 150px; } .poster-music& { padding-bottom: @padding - 10; } } } h2 { text-transform: uppercase; letter-spacing: 0.05em; font-size: @base-font-size; font-family: @condensed-font-family; margin-top: 0; margin-bottom: 1em; overflow: hidden; } .kicker { white-space: nowrap; } blockquote { padding: 0 0 0 1em; margin: 0 0 @line-height * 2; border-left: none; font-size: inherit; line-height: 100%; font-weight: normal; display: block; overflow: visible; &:focus { border: none; outline: none; } p { font-size: inherit; font-weight: inherit; margin: 0; line-height: 110%; text-indent: @list-text-indent; margin-left: @list-margin-left; margin-bottom: @list-margin-bottom; span { line-height: inherit !important; } &:before { content: @bullet-symbol; margin-right: @bullet-margin; } } &:last-child { color: red; } } p.timestamp { font-size: @base-font-size; color: @timestamp-color; text-transform: uppercase; letter-spacing: 0.1em; font-weight: normal; line-height: 1.3; display: block; margin-top: 0; z-index: 101; position: relative; &:after { content: @timestamp-timezone; } } .logo-wrapper { position: absolute; width: 100%; z-index: 100; bottom: 0; right: 0; text-align: right; padding: @padding @padding * 2 @padding * 2; margin: 0; font-size: 46px; background: @bg-color; background-image: @logo-path; background-repeat: no-repeat; background-size: @logo-sq-width @logo-sq-height; width: @logo-sq-width + @padding; height: @logo-sq-height + @padding; .sixteen-by-nine& { background-size: @logo-16x9-width @logo-16x9-height; width: @logo-16x9-width + @padding; height: @logo-16x9-height + @padding; } .logo { .hide; } } } .warning { font-size: 12px; font-style: italic; margin-top: 6px; } ================================================ FILE: less/lib/bootstrap/alerts.less ================================================ // // Alerts // -------------------------------------------------- // Base styles // ------------------------- .alert { padding: @alert-padding; margin-bottom: @line-height-computed; border: 1px solid transparent; border-radius: @alert-border-radius; // Headings for larger alerts h4 { margin-top: 0; // Specified for the h4 to prevent conflicts of changing @headings-color color: inherit; } // Provide class for links that match alerts .alert-link { font-weight: @alert-link-font-weight; } // Improve alignment and spacing of inner content > p, > ul { margin-bottom: 0; } > p + p { margin-top: 5px; } } // Dismissable alerts // // Expand the right padding and account for the close button's positioning. .alert-dismissable { padding-right: (@alert-padding + 20); // Adjust close link position .close { position: relative; top: -2px; right: -21px; color: inherit; } } // Alternate styles // // Generate contextual modifier classes for colorizing the alert. .alert-success { .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text); } .alert-info { .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text); } .alert-warning { .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text); } .alert-danger { .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text); } ================================================ FILE: less/lib/bootstrap/badges.less ================================================ // // Badges // -------------------------------------------------- // Base classes .badge { display: inline-block; min-width: 10px; padding: 3px 7px; font-size: @font-size-small; font-weight: @badge-font-weight; color: @badge-color; line-height: @badge-line-height; vertical-align: baseline; white-space: nowrap; text-align: center; background-color: @badge-bg; border-radius: @badge-border-radius; // Empty badges collapse automatically (not available in IE8) &:empty { display: none; } // Quick fix for badges in buttons .btn & { position: relative; top: -1px; } } // Hover state, but only for links a.badge { &:hover, &:focus { color: @badge-link-hover-color; text-decoration: none; cursor: pointer; } } // Account for counters in navs a.list-group-item.active > .badge, .nav-pills > .active > a > .badge { color: @badge-active-color; background-color: @badge-active-bg; } .nav-pills > li > a > .badge { margin-left: 3px; } ================================================ FILE: less/lib/bootstrap/bootstrap.less ================================================ // Core variables and mixins @import "variables.less"; @import "mixins.less"; // Reset @import "normalize.less"; @import "print.less"; // Core CSS @import "scaffolding.less"; @import "type.less"; @import "code.less"; @import "grid.less"; @import "tables.less"; @import "forms.less"; @import "buttons.less"; // Components @import "component-animations.less"; @import "glyphicons.less"; @import "dropdowns.less"; @import "button-groups.less"; @import "input-groups.less"; @import "navs.less"; @import "navbar.less"; @import "breadcrumbs.less"; @import "pagination.less"; @import "pager.less"; @import "labels.less"; @import "badges.less"; @import "jumbotron.less"; @import "thumbnails.less"; @import "alerts.less"; @import "progress-bars.less"; @import "media.less"; @import "list-group.less"; @import "panels.less"; @import "wells.less"; @import "close.less"; // Components w/ JavaScript @import "modals.less"; @import "tooltip.less"; @import "popovers.less"; @import "carousel.less"; // Utility classes @import "utilities.less"; @import "responsive-utilities.less"; ================================================ FILE: less/lib/bootstrap/breadcrumbs.less ================================================ // // Breadcrumbs // -------------------------------------------------- .breadcrumb { padding: 8px 15px; margin-bottom: @line-height-computed; list-style: none; background-color: @breadcrumb-bg; border-radius: @border-radius-base; > li { display: inline-block; + li:before { content: "@{breadcrumb-separator}\00a0"; // Unicode space added since inline-block means non-collapsing white-space padding: 0 5px; color: @breadcrumb-color; } } > .active { color: @breadcrumb-active-color; } } ================================================ FILE: less/lib/bootstrap/button-groups.less ================================================ // // Button groups // -------------------------------------------------- // Make the div behave like a button .btn-group, .btn-group-vertical { position: relative; display: inline-block; vertical-align: middle; // match .btn alignment given font-size hack above > .btn { position: relative; float: left; // Bring the "active" button to the front &:hover, &:focus, &:active, &.active { z-index: 2; } &:focus { // Remove focus outline when dropdown JS adds it after closing the menu outline: none; } } } // Prevent double borders when buttons are next to each other .btn-group { .btn + .btn, .btn + .btn-group, .btn-group + .btn, .btn-group + .btn-group { margin-left: -1px; } } // Optional: Group multiple button groups together for a toolbar .btn-toolbar { .clearfix(); .btn-group { float: left; } // Space out series of button groups > .btn, > .btn-group { + .btn, + .btn-group { margin-left: 5px; } } } .btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { border-radius: 0; } // Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match .btn-group > .btn:first-child { margin-left: 0; &:not(:last-child):not(.dropdown-toggle) { .border-right-radius(0); } } // Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it .btn-group > .btn:last-child:not(:first-child), .btn-group > .dropdown-toggle:not(:first-child) { .border-left-radius(0); } // Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group) .btn-group > .btn-group { float: left; } .btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { border-radius: 0; } .btn-group > .btn-group:first-child { > .btn:last-child, > .dropdown-toggle { .border-right-radius(0); } } .btn-group > .btn-group:last-child > .btn:first-child { .border-left-radius(0); } // On active and open, don't show outline .btn-group .dropdown-toggle:active, .btn-group.open .dropdown-toggle { outline: 0; } // Sizing // // Remix the default button sizing classes into new ones for easier manipulation. .btn-group-xs > .btn { .btn-xs(); } .btn-group-sm > .btn { .btn-sm(); } .btn-group-lg > .btn { .btn-lg(); } // Split button dropdowns // ---------------------- // Give the line between buttons some depth .btn-group > .btn + .dropdown-toggle { padding-left: 8px; padding-right: 8px; } .btn-group > .btn-lg + .dropdown-toggle { padding-left: 12px; padding-right: 12px; } // The clickable button for toggling the menu // Remove the gradient and set the same inset shadow as the :active state .btn-group.open .dropdown-toggle { .box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); // Show no shadow for `.btn-link` since it has no other button styles. &.btn-link { .box-shadow(none); } } // Reposition the caret .btn .caret { margin-left: 0; } // Carets in other button sizes .btn-lg .caret { border-width: @caret-width-large @caret-width-large 0; border-bottom-width: 0; } // Upside down carets for .dropup .dropup .btn-lg .caret { border-width: 0 @caret-width-large @caret-width-large; } // Vertical button groups // ---------------------- .btn-group-vertical { > .btn, > .btn-group, > .btn-group > .btn { display: block; float: none; width: 100%; max-width: 100%; } // Clear floats so dropdown menus can be properly placed > .btn-group { .clearfix(); > .btn { float: none; } } > .btn + .btn, > .btn + .btn-group, > .btn-group + .btn, > .btn-group + .btn-group { margin-top: -1px; margin-left: 0; } } .btn-group-vertical > .btn { &:not(:first-child):not(:last-child) { border-radius: 0; } &:first-child:not(:last-child) { border-top-right-radius: @border-radius-base; .border-bottom-radius(0); } &:last-child:not(:first-child) { border-bottom-left-radius: @border-radius-base; .border-top-radius(0); } } .btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { border-radius: 0; } .btn-group-vertical > .btn-group:first-child { > .btn:last-child, > .dropdown-toggle { .border-bottom-radius(0); } } .btn-group-vertical > .btn-group:last-child > .btn:first-child { .border-top-radius(0); } // Justified button groups // ---------------------- .btn-group-justified { display: table; width: 100%; table-layout: fixed; border-collapse: separate; > .btn, > .btn-group { float: none; display: table-cell; width: 1%; } > .btn-group .btn { width: 100%; } } // Checkbox and radio options [data-toggle="buttons"] > .btn > input[type="radio"], [data-toggle="buttons"] > .btn > input[type="checkbox"] { display: none; } ================================================ FILE: less/lib/bootstrap/buttons.less ================================================ // // Buttons // -------------------------------------------------- // Base styles // -------------------------------------------------- .btn { display: inline-block; margin-bottom: 0; // For input.btn font-weight: @btn-font-weight; text-align: center; vertical-align: middle; cursor: pointer; background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 border: 1px solid transparent; white-space: nowrap; .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @border-radius-base); .user-select(none); &:focus { .tab-focus(); } &:hover, &:focus { color: @btn-default-color; text-decoration: none; } &:active, &.active { outline: 0; background-image: none; .box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); } &.disabled, &[disabled], fieldset[disabled] & { cursor: not-allowed; pointer-events: none; // Future-proof disabling of clicks .opacity(.65); .box-shadow(none); } } // Alternate buttons // -------------------------------------------------- .btn-default { .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border); } .btn-primary { .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border); } // Warning appears as orange .btn-warning { .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border); } // Danger and error appear as red .btn-danger { .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border); } // Success appears as green .btn-success { .button-variant(@btn-success-color; @btn-success-bg; @btn-success-border); } // Info appears as blue-green .btn-info { .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border); } // Link buttons // ------------------------- // Make a button look and behave like a link .btn-link { color: @link-color; font-weight: normal; cursor: pointer; border-radius: 0; &, &:active, &[disabled], fieldset[disabled] & { background-color: transparent; .box-shadow(none); } &, &:hover, &:focus, &:active { border-color: transparent; } &:hover, &:focus { color: @link-hover-color; text-decoration: underline; background-color: transparent; } &[disabled], fieldset[disabled] & { &:hover, &:focus { color: @btn-link-disabled-color; text-decoration: none; } } } // Button Sizes // -------------------------------------------------- .btn-lg { // line-height: ensure even-numbered height of button next to large input .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large); } .btn-sm { // line-height: ensure proper height of button next to small input .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small); } .btn-xs { .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @border-radius-small); } // Block button // -------------------------------------------------- .btn-block { display: block; width: 100%; padding-left: 0; padding-right: 0; } // Vertically space out multiple block buttons .btn-block + .btn-block { margin-top: 5px; } // Specificity overrides input[type="submit"], input[type="reset"], input[type="button"] { &.btn-block { width: 100%; } } ================================================ FILE: less/lib/bootstrap/carousel.less ================================================ // // Carousel // -------------------------------------------------- // Wrapper for the slide container and indicators .carousel { position: relative; } .carousel-inner { position: relative; overflow: hidden; width: 100%; > .item { display: none; position: relative; .transition(.6s ease-in-out left); // Account for jankitude on images > img, > a > img { .img-responsive(); line-height: 1; } } > .active, > .next, > .prev { display: block; } > .active { left: 0; } > .next, > .prev { position: absolute; top: 0; width: 100%; } > .next { left: 100%; } > .prev { left: -100%; } > .next.left, > .prev.right { left: 0; } > .active.left { left: -100%; } > .active.right { left: 100%; } } // Left/right controls for nav // --------------------------- .carousel-control { position: absolute; top: 0; left: 0; bottom: 0; width: @carousel-control-width; .opacity(@carousel-control-opacity); font-size: @carousel-control-font-size; color: @carousel-control-color; text-align: center; text-shadow: @carousel-text-shadow; // We can't have this transition here because WebKit cancels the carousel // animation if you trip this while in the middle of another animation. // Set gradients for backgrounds &.left { #gradient > .horizontal(@start-color: rgba(0,0,0,.5); @end-color: rgba(0,0,0,.0001)); } &.right { left: auto; right: 0; #gradient > .horizontal(@start-color: rgba(0,0,0,.0001); @end-color: rgba(0,0,0,.5)); } // Hover/focus state &:hover, &:focus { outline: none; color: @carousel-control-color; text-decoration: none; .opacity(.9); } // Toggles .icon-prev, .icon-next, .glyphicon-chevron-left, .glyphicon-chevron-right { position: absolute; top: 50%; z-index: 5; display: inline-block; } .icon-prev, .glyphicon-chevron-left { left: 50%; } .icon-next, .glyphicon-chevron-right { right: 50%; } .icon-prev, .icon-next { width: 20px; height: 20px; margin-top: -10px; margin-left: -10px; font-family: serif; } .icon-prev { &:before { content: '\2039';// SINGLE LEFT-POINTING ANGLE QUOTATION MARK (U+2039) } } .icon-next { &:before { content: '\203a';// SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (U+203A) } } } // Optional indicator pips // // Add an unordered list with the following class and add a list item for each // slide your carousel holds. .carousel-indicators { position: absolute; bottom: 10px; left: 50%; z-index: 15; width: 60%; margin-left: -30%; padding-left: 0; list-style: none; text-align: center; li { display: inline-block; width: 10px; height: 10px; margin: 1px; text-indent: -999px; border: 1px solid @carousel-indicator-border-color; border-radius: 10px; cursor: pointer; // IE8-9 hack for event handling // // Internet Explorer 8-9 does not support clicks on elements without a set // `background-color`. We cannot use `filter` since that's not viewed as a // background color by the browser. Thus, a hack is needed. // // For IE8, we set solid black as it doesn't support `rgba()`. For IE9, we // set alpha transparency for the best results possible. background-color: #000 \9; // IE8 background-color: rgba(0,0,0,0); // IE9 } .active { margin: 0; width: 12px; height: 12px; background-color: @carousel-indicator-active-bg; } } // Optional captions // ----------------------------- // Hidden by default for smaller viewports .carousel-caption { position: absolute; left: 15%; right: 15%; bottom: 20px; z-index: 10; padding-top: 20px; padding-bottom: 20px; color: @carousel-caption-color; text-align: center; text-shadow: @carousel-text-shadow; & .btn { text-shadow: none; // No shadow for button elements in carousel-caption } } // Scale up controls for tablets and up @media screen and (min-width: @screen-sm-min) { // Scale up the controls a smidge .carousel-control { .glyphicons-chevron-left, .glyphicons-chevron-right, .icon-prev, .icon-next { width: 30px; height: 30px; margin-top: -15px; margin-left: -15px; font-size: 30px; } } // Show and left align the captions .carousel-caption { left: 20%; right: 20%; padding-bottom: 30px; } // Move up the indicators .carousel-indicators { bottom: 20px; } } ================================================ FILE: less/lib/bootstrap/close.less ================================================ // // Close icons // -------------------------------------------------- .close { float: right; font-size: (@font-size-base * 1.5); font-weight: @close-font-weight; line-height: 1; color: @close-color; text-shadow: @close-text-shadow; .opacity(.2); &:hover, &:focus { color: @close-color; text-decoration: none; cursor: pointer; .opacity(.5); } // Additional properties for button version // iOS requires the button element instead of an anchor tag. // If you want the anchor version, it requires `href="#"`. button& { padding: 0; cursor: pointer; background: transparent; border: 0; -webkit-appearance: none; } } ================================================ FILE: less/lib/bootstrap/code.less ================================================ // // Code (inline and block) // -------------------------------------------------- // Inline and block code styles code, kbd, pre, samp { font-family: @font-family-monospace; } // Inline code code { padding: 2px 4px; font-size: 90%; color: @code-color; background-color: @code-bg; white-space: nowrap; border-radius: @border-radius-base; } // Blocks of code pre { display: block; padding: ((@line-height-computed - 1) / 2); margin: 0 0 (@line-height-computed / 2); font-size: (@font-size-base - 1); // 14px to 13px line-height: @line-height-base; word-break: break-all; word-wrap: break-word; color: @pre-color; background-color: @pre-bg; border: 1px solid @pre-border-color; border-radius: @border-radius-base; // Account for some code outputs that place code tags in pre tags code { padding: 0; font-size: inherit; color: inherit; white-space: pre-wrap; background-color: transparent; border-radius: 0; } } // Enable scrollable blocks of code .pre-scrollable { max-height: @pre-scrollable-max-height; overflow-y: scroll; } ================================================ FILE: less/lib/bootstrap/component-animations.less ================================================ // // Component animations // -------------------------------------------------- // Heads up! // // We don't use the `.opacity()` mixin here since it causes a bug with text // fields in IE7-8. Source: https://github.com/twitter/bootstrap/pull/3552. .fade { opacity: 0; .transition(opacity .15s linear); &.in { opacity: 1; } } .collapse { display: none; &.in { display: block; } } .collapsing { position: relative; height: 0; overflow: hidden; .transition(height .35s ease); } ================================================ FILE: less/lib/bootstrap/dropdowns.less ================================================ // // Dropdown menus // -------------------------------------------------- // Dropdown arrow/caret .caret { display: inline-block; width: 0; height: 0; margin-left: 2px; vertical-align: middle; border-top: @caret-width-base solid; border-right: @caret-width-base solid transparent; border-left: @caret-width-base solid transparent; } // The dropdown wrapper (div) .dropdown { position: relative; } // Prevent the focus on the dropdown toggle when closing dropdowns .dropdown-toggle:focus { outline: 0; } // The dropdown menu (ul) .dropdown-menu { position: absolute; top: 100%; left: 0; z-index: @zindex-dropdown; display: none; // none by default, but block on "open" of the menu float: left; min-width: 160px; padding: 5px 0; margin: 2px 0 0; // override default ul list-style: none; font-size: @font-size-base; background-color: @dropdown-bg; border: 1px solid @dropdown-fallback-border; // IE8 fallback border: 1px solid @dropdown-border; border-radius: @border-radius-base; .box-shadow(0 6px 12px rgba(0,0,0,.175)); background-clip: padding-box; // Aligns the dropdown menu to right &.pull-right { right: 0; left: auto; } // Dividers (basically an hr) within the dropdown .divider { .nav-divider(@dropdown-divider-bg); } // Links within the dropdown menu > li > a { display: block; padding: 3px 20px; clear: both; font-weight: normal; line-height: @line-height-base; color: @dropdown-link-color; white-space: nowrap; // prevent links from randomly breaking onto new lines } } // Hover/Focus state .dropdown-menu > li > a { &:hover, &:focus { text-decoration: none; color: @dropdown-link-hover-color; background-color: @dropdown-link-hover-bg; } } // Active state .dropdown-menu > .active > a { &, &:hover, &:focus { color: @dropdown-link-active-color; text-decoration: none; outline: 0; background-color: @dropdown-link-active-bg; } } // Disabled state // // Gray out text and ensure the hover/focus state remains gray .dropdown-menu > .disabled > a { &, &:hover, &:focus { color: @dropdown-link-disabled-color; } } // Nuke hover/focus effects .dropdown-menu > .disabled > a { &:hover, &:focus { text-decoration: none; background-color: transparent; background-image: none; // Remove CSS gradient .reset-filter(); cursor: not-allowed; } } // Open state for the dropdown .open { // Show the menu > .dropdown-menu { display: block; } // Remove the outline when :focus is triggered > a { outline: 0; } } // Dropdown section headers .dropdown-header { display: block; padding: 3px 20px; font-size: @font-size-small; line-height: @line-height-base; color: @dropdown-header-color; } // Backdrop to catch body clicks on mobile, etc. .dropdown-backdrop { position: fixed; left: 0; right: 0; bottom: 0; top: 0; z-index: @zindex-dropdown - 10; } // Right aligned dropdowns .pull-right > .dropdown-menu { right: 0; left: auto; } // Allow for dropdowns to go bottom up (aka, dropup-menu) // // Just add .dropup after the standard .dropdown class and you're set, bro. // TODO: abstract this so that the navbar fixed styles are not placed here? .dropup, .navbar-fixed-bottom .dropdown { // Reverse the caret .caret { border-top: 0; border-bottom: @caret-width-base solid; content: ""; } // Different positioning for bottom up menu .dropdown-menu { top: auto; bottom: 100%; margin-bottom: 1px; } } // Component alignment // // Reiterate per navbar.less and the modified component alignment there. @media (min-width: @grid-float-breakpoint) { .navbar-right { .dropdown-menu { .pull-right > .dropdown-menu(); } } } ================================================ FILE: less/lib/bootstrap/forms.less ================================================ // // Forms // -------------------------------------------------- // Normalize non-controls // // Restyle and baseline non-control form elements. fieldset { padding: 0; margin: 0; border: 0; } legend { display: block; width: 100%; padding: 0; margin-bottom: @line-height-computed; font-size: (@font-size-base * 1.5); line-height: inherit; color: @legend-color; border: 0; border-bottom: 1px solid @legend-border-color; } label { display: inline-block; margin-bottom: 5px; font-weight: bold; } // Normalize form controls // Override content-box in Normalize (* isn't specific enough) input[type="search"] { .box-sizing(border-box); } // Position radios and checkboxes better input[type="radio"], input[type="checkbox"] { margin: 4px 0 0; margin-top: 1px \9; /* IE8-9 */ line-height: normal; } // Set the height of select and file controls to match text inputs input[type="file"] { display: block; } // Make multiple select elements height not fixed select[multiple], select[size] { height: auto; } // Fix optgroup Firefox bug per https://github.com/twbs/bootstrap/issues/7611 select optgroup { font-size: inherit; font-style: inherit; font-family: inherit; } // Focus for select, file, radio, and checkbox input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus { .tab-focus(); } // Fix for Chrome number input // Setting certain font-sizes causes the `I` bar to appear on hover of the bottom increment button. // See https://github.com/twbs/bootstrap/issues/8350 for more. input[type="number"] { &::-webkit-outer-spin-button, &::-webkit-inner-spin-button { height: auto; } } // Adjust output element output { display: block; padding-top: (@padding-base-vertical + 1); font-size: @font-size-base; line-height: @line-height-base; color: @input-color; vertical-align: middle; } // Common form controls // // Shared size and type resets for form controls. Apply `.form-control` to any // of the following form controls: // // select // textarea // input[type="text"] // input[type="password"] // input[type="datetime"] // input[type="datetime-local"] // input[type="date"] // input[type="month"] // input[type="time"] // input[type="week"] // input[type="number"] // input[type="email"] // input[type="url"] // input[type="search"] // input[type="tel"] // input[type="color"] .form-control { display: block; width: 100%; height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border) padding: @padding-base-vertical @padding-base-horizontal; font-size: @font-size-base; line-height: @line-height-base; color: @input-color; vertical-align: middle; background-color: @input-bg; background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 border: 1px solid @input-border; border-radius: @input-border-radius; .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); .transition(~"border-color ease-in-out .15s, box-shadow ease-in-out .15s"); // Customize the `:focus` state to imitate native WebKit styles. .form-control-focus(); // Placeholder // // Placeholder text gets special styles because when browsers invalidate entire // lines if it doesn't understand a selector/ .placeholder(); // Disabled and read-only inputs // Note: HTML5 says that controls under a fieldset > legend:first-child won't // be disabled if the fieldset is disabled. Due to implementation difficulty, // we don't honor that edge case; we style them as disabled anyway. &[disabled], &[readonly], fieldset[disabled] & { cursor: not-allowed; background-color: @input-bg-disabled; } // Reset height for `textarea`s textarea& { height: auto; } } // Form groups // // Designed to help with the organization and spacing of vertical forms. For // horizontal forms, use the predefined grid classes. .form-group { margin-bottom: 15px; } // Checkboxes and radios // // Indent the labels to position radios/checkboxes as hanging controls. .radio, .checkbox { display: block; min-height: @line-height-computed; // clear the floating input if there is no label text margin-top: 10px; margin-bottom: 10px; padding-left: 20px; vertical-align: middle; label { display: inline; margin-bottom: 0; font-weight: normal; cursor: pointer; } } .radio input[type="radio"], .radio-inline input[type="radio"], .checkbox input[type="checkbox"], .checkbox-inline input[type="checkbox"] { float: left; margin-left: -20px; } .radio + .radio, .checkbox + .checkbox { margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing } // Radios and checkboxes on same line .radio-inline, .checkbox-inline { display: inline-block; padding-left: 20px; margin-bottom: 0; vertical-align: middle; font-weight: normal; cursor: pointer; } .radio-inline + .radio-inline, .checkbox-inline + .checkbox-inline { margin-top: 0; margin-left: 10px; // space out consecutive inline controls } // Apply same disabled cursor tweak as for inputs // // Note: Neither radios nor checkboxes can be readonly. input[type="radio"], input[type="checkbox"], .radio, .radio-inline, .checkbox, .checkbox-inline { &[disabled], fieldset[disabled] & { cursor: not-allowed; } } // Form control sizing .input-sm { .input-size(@input-height-small; @padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small); } .input-lg { .input-size(@input-height-large; @padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large); } // Form control feedback states // // Apply contextual and semantic states to individual form controls. // Warning .has-warning { .form-control-validation(@state-warning-text; @state-warning-text; @state-warning-bg); } // Error .has-error { .form-control-validation(@state-danger-text; @state-danger-text; @state-danger-bg); } // Success .has-success { .form-control-validation(@state-success-text; @state-success-text; @state-success-bg); } // Static form control text // // Apply class to a `p` element to make any string of text align with labels in // a horizontal form layout. .form-control-static { margin-bottom: 0; // Remove default margin from `p` } // Help text // // Apply to any element you wish to create light text for placement immediately // below a form control. Use for general help, formatting, or instructional text. .help-block { display: block; // account for any element using help-block margin-top: 5px; margin-bottom: 10px; color: lighten(@text-color, 25%); // lighten the text some for contrast } // Inline forms // // Make forms appear inline(-block) by adding the `.form-inline` class. Inline // forms begin stacked on extra small (mobile) devices and then go inline when // viewports reach <768px. // // Requires wrapping inputs and labels with `.form-group` for proper display of // default HTML form controls and our custom form controls (e.g., input groups). // // Heads up! This is mixin-ed into `.navbar-form` in navbars.less. .form-inline { // Kick in the inline @media (min-width: @screen-sm) { // Inline-block all the things for "inline" .form-group { display: inline-block; margin-bottom: 0; vertical-align: middle; } // In navbar-form, allow folks to *not* use `.form-group` .form-control { display: inline-block; } // Override `width: 100%;` when not within a `.form-group` select.form-control { width: auto; } // Remove default margin on radios/checkboxes that were used for stacking, and // then undo the floating of radios and checkboxes to match (which also avoids // a bug in WebKit: https://github.com/twbs/bootstrap/issues/1969). .radio, .checkbox { display: inline-block; margin-top: 0; margin-bottom: 0; padding-left: 0; } .radio input[type="radio"], .checkbox input[type="checkbox"] { float: none; margin-left: 0; } } } // Horizontal forms // // Horizontal forms are built on grid classes and allow you to create forms with // labels on the left and inputs on the right. .form-horizontal { // Consistent vertical alignment of labels, radios, and checkboxes .control-label, .radio, .checkbox, .radio-inline, .checkbox-inline { margin-top: 0; margin-bottom: 0; padding-top: (@padding-base-vertical + 1); // Default padding plus a border } // Account for padding we're adding to ensure the alignment and of help text // and other content below items .radio, .checkbox { min-height: @line-height-computed + (@padding-base-vertical + 1); } // Make form groups behave like rows .form-group { .make-row(); } .form-control-static { padding-top: (@padding-base-vertical + 1); } // Only right align form labels here when the columns stop stacking @media (min-width: @screen-sm-min) { .control-label { text-align: right; } } } ================================================ FILE: less/lib/bootstrap/glyphicons.less ================================================ // // Glyphicons for Bootstrap // // Since icons are fonts, they can be placed anywhere text is placed and are // thus automatically sized to match the surrounding child. To use, create an // inline element with the appropriate classes, like so: // // Star // Import the fonts @font-face { font-family: 'Glyphicons Halflings'; src: ~"url('@{icon-font-path}@{icon-font-name}.eot')"; src: ~"url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype')", ~"url('@{icon-font-path}@{icon-font-name}.woff') format('woff')", ~"url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype')", ~"url('@{icon-font-path}@{icon-font-name}.svg#glyphicons-halflingsregular') format('svg')"; } // Catchall baseclass .glyphicon { position: relative; top: 1px; display: inline-block; font-family: 'Glyphicons Halflings'; font-style: normal; font-weight: normal; line-height: 1; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; &:empty { width: 1em; } } // Individual icons .glyphicon-asterisk { &:before { content: "\2a"; } } .glyphicon-plus { &:before { content: "\2b"; } } .glyphicon-euro { &:before { content: "\20ac"; } } .glyphicon-minus { &:before { content: "\2212"; } } .glyphicon-cloud { &:before { content: "\2601"; } } .glyphicon-envelope { &:before { content: "\2709"; } } .glyphicon-pencil { &:before { content: "\270f"; } } .glyphicon-glass { &:before { content: "\e001"; } } .glyphicon-music { &:before { content: "\e002"; } } .glyphicon-search { &:before { content: "\e003"; } } .glyphicon-heart { &:before { content: "\e005"; } } .glyphicon-star { &:before { content: "\e006"; } } .glyphicon-star-empty { &:before { content: "\e007"; } } .glyphicon-user { &:before { content: "\e008"; } } .glyphicon-film { &:before { content: "\e009"; } } .glyphicon-th-large { &:before { content: "\e010"; } } .glyphicon-th { &:before { content: "\e011"; } } .glyphicon-th-list { &:before { content: "\e012"; } } .glyphicon-ok { &:before { content: "\e013"; } } .glyphicon-remove { &:before { content: "\e014"; } } .glyphicon-zoom-in { &:before { content: "\e015"; } } .glyphicon-zoom-out { &:before { content: "\e016"; } } .glyphicon-off { &:before { content: "\e017"; } } .glyphicon-signal { &:before { content: "\e018"; } } .glyphicon-cog { &:before { content: "\e019"; } } .glyphicon-trash { &:before { content: "\e020"; } } .glyphicon-home { &:before { content: "\e021"; } } .glyphicon-file { &:before { content: "\e022"; } } .glyphicon-time { &:before { content: "\e023"; } } .glyphicon-road { &:before { content: "\e024"; } } .glyphicon-download-alt { &:before { content: "\e025"; } } .glyphicon-download { &:before { content: "\e026"; } } .glyphicon-upload { &:before { content: "\e027"; } } .glyphicon-inbox { &:before { content: "\e028"; } } .glyphicon-play-circle { &:before { content: "\e029"; } } .glyphicon-repeat { &:before { content: "\e030"; } } .glyphicon-refresh { &:before { content: "\e031"; } } .glyphicon-list-alt { &:before { content: "\e032"; } } .glyphicon-lock { &:before { content: "\e033"; } } .glyphicon-flag { &:before { content: "\e034"; } } .glyphicon-headphones { &:before { content: "\e035"; } } .glyphicon-volume-off { &:before { content: "\e036"; } } .glyphicon-volume-down { &:before { content: "\e037"; } } .glyphicon-volume-up { &:before { content: "\e038"; } } .glyphicon-qrcode { &:before { content: "\e039"; } } .glyphicon-barcode { &:before { content: "\e040"; } } .glyphicon-tag { &:before { content: "\e041"; } } .glyphicon-tags { &:before { content: "\e042"; } } .glyphicon-book { &:before { content: "\e043"; } } .glyphicon-bookmark { &:before { content: "\e044"; } } .glyphicon-print { &:before { content: "\e045"; } } .glyphicon-camera { &:before { content: "\e046"; } } .glyphicon-font { &:before { content: "\e047"; } } .glyphicon-bold { &:before { content: "\e048"; } } .glyphicon-italic { &:before { content: "\e049"; } } .glyphicon-text-height { &:before { content: "\e050"; } } .glyphicon-text-width { &:before { content: "\e051"; } } .glyphicon-align-left { &:before { content: "\e052"; } } .glyphicon-align-center { &:before { content: "\e053"; } } .glyphicon-align-right { &:before { content: "\e054"; } } .glyphicon-align-justify { &:before { content: "\e055"; } } .glyphicon-list { &:before { content: "\e056"; } } .glyphicon-indent-left { &:before { content: "\e057"; } } .glyphicon-indent-right { &:before { content: "\e058"; } } .glyphicon-facetime-video { &:before { content: "\e059"; } } .glyphicon-picture { &:before { content: "\e060"; } } .glyphicon-map-marker { &:before { content: "\e062"; } } .glyphicon-adjust { &:before { content: "\e063"; } } .glyphicon-tint { &:before { content: "\e064"; } } .glyphicon-edit { &:before { content: "\e065"; } } .glyphicon-share { &:before { content: "\e066"; } } .glyphicon-check { &:before { content: "\e067"; } } .glyphicon-move { &:before { content: "\e068"; } } .glyphicon-step-backward { &:before { content: "\e069"; } } .glyphicon-fast-backward { &:before { content: "\e070"; } } .glyphicon-backward { &:before { content: "\e071"; } } .glyphicon-play { &:before { content: "\e072"; } } .glyphicon-pause { &:before { content: "\e073"; } } .glyphicon-stop { &:before { content: "\e074"; } } .glyphicon-forward { &:before { content: "\e075"; } } .glyphicon-fast-forward { &:before { content: "\e076"; } } .glyphicon-step-forward { &:before { content: "\e077"; } } .glyphicon-eject { &:before { content: "\e078"; } } .glyphicon-chevron-left { &:before { content: "\e079"; } } .glyphicon-chevron-right { &:before { content: "\e080"; } } .glyphicon-plus-sign { &:before { content: "\e081"; } } .glyphicon-minus-sign { &:before { content: "\e082"; } } .glyphicon-remove-sign { &:before { content: "\e083"; } } .glyphicon-ok-sign { &:before { content: "\e084"; } } .glyphicon-question-sign { &:before { content: "\e085"; } } .glyphicon-info-sign { &:before { content: "\e086"; } } .glyphicon-screenshot { &:before { content: "\e087"; } } .glyphicon-remove-circle { &:before { content: "\e088"; } } .glyphicon-ok-circle { &:before { content: "\e089"; } } .glyphicon-ban-circle { &:before { content: "\e090"; } } .glyphicon-arrow-left { &:before { content: "\e091"; } } .glyphicon-arrow-right { &:before { content: "\e092"; } } .glyphicon-arrow-up { &:before { content: "\e093"; } } .glyphicon-arrow-down { &:before { content: "\e094"; } } .glyphicon-share-alt { &:before { content: "\e095"; } } .glyphicon-resize-full { &:before { content: "\e096"; } } .glyphicon-resize-small { &:before { content: "\e097"; } } .glyphicon-exclamation-sign { &:before { content: "\e101"; } } .glyphicon-gift { &:before { content: "\e102"; } } .glyphicon-leaf { &:before { content: "\e103"; } } .glyphicon-fire { &:before { content: "\e104"; } } .glyphicon-eye-open { &:before { content: "\e105"; } } .glyphicon-eye-close { &:before { content: "\e106"; } } .glyphicon-warning-sign { &:before { content: "\e107"; } } .glyphicon-plane { &:before { content: "\e108"; } } .glyphicon-calendar { &:before { content: "\e109"; } } .glyphicon-random { &:before { content: "\e110"; } } .glyphicon-comment { &:before { content: "\e111"; } } .glyphicon-magnet { &:before { content: "\e112"; } } .glyphicon-chevron-up { &:before { content: "\e113"; } } .glyphicon-chevron-down { &:before { content: "\e114"; } } .glyphicon-retweet { &:before { content: "\e115"; } } .glyphicon-shopping-cart { &:before { content: "\e116"; } } .glyphicon-folder-close { &:before { content: "\e117"; } } .glyphicon-folder-open { &:before { content: "\e118"; } } .glyphicon-resize-vertical { &:before { content: "\e119"; } } .glyphicon-resize-horizontal { &:before { content: "\e120"; } } .glyphicon-hdd { &:before { content: "\e121"; } } .glyphicon-bullhorn { &:before { content: "\e122"; } } .glyphicon-bell { &:before { content: "\e123"; } } .glyphicon-certificate { &:before { content: "\e124"; } } .glyphicon-thumbs-up { &:before { content: "\e125"; } } .glyphicon-thumbs-down { &:before { content: "\e126"; } } .glyphicon-hand-right { &:before { content: "\e127"; } } .glyphicon-hand-left { &:before { content: "\e128"; } } .glyphicon-hand-up { &:before { content: "\e129"; } } .glyphicon-hand-down { &:before { content: "\e130"; } } .glyphicon-circle-arrow-right { &:before { content: "\e131"; } } .glyphicon-circle-arrow-left { &:before { content: "\e132"; } } .glyphicon-circle-arrow-up { &:before { content: "\e133"; } } .glyphicon-circle-arrow-down { &:before { content: "\e134"; } } .glyphicon-globe { &:before { content: "\e135"; } } .glyphicon-wrench { &:before { content: "\e136"; } } .glyphicon-tasks { &:before { content: "\e137"; } } .glyphicon-filter { &:before { content: "\e138"; } } .glyphicon-briefcase { &:before { content: "\e139"; } } .glyphicon-fullscreen { &:before { content: "\e140"; } } .glyphicon-dashboard { &:before { content: "\e141"; } } .glyphicon-paperclip { &:before { content: "\e142"; } } .glyphicon-heart-empty { &:before { content: "\e143"; } } .glyphicon-link { &:before { content: "\e144"; } } .glyphicon-phone { &:before { content: "\e145"; } } .glyphicon-pushpin { &:before { content: "\e146"; } } .glyphicon-usd { &:before { content: "\e148"; } } .glyphicon-gbp { &:before { content: "\e149"; } } .glyphicon-sort { &:before { content: "\e150"; } } .glyphicon-sort-by-alphabet { &:before { content: "\e151"; } } .glyphicon-sort-by-alphabet-alt { &:before { content: "\e152"; } } .glyphicon-sort-by-order { &:before { content: "\e153"; } } .glyphicon-sort-by-order-alt { &:before { content: "\e154"; } } .glyphicon-sort-by-attributes { &:before { content: "\e155"; } } .glyphicon-sort-by-attributes-alt { &:before { content: "\e156"; } } .glyphicon-unchecked { &:before { content: "\e157"; } } .glyphicon-expand { &:before { content: "\e158"; } } .glyphicon-collapse-down { &:before { content: "\e159"; } } .glyphicon-collapse-up { &:before { content: "\e160"; } } .glyphicon-log-in { &:before { content: "\e161"; } } .glyphicon-flash { &:before { content: "\e162"; } } .glyphicon-log-out { &:before { content: "\e163"; } } .glyphicon-new-window { &:before { content: "\e164"; } } .glyphicon-record { &:before { content: "\e165"; } } .glyphicon-save { &:before { content: "\e166"; } } .glyphicon-open { &:before { content: "\e167"; } } .glyphicon-saved { &:before { content: "\e168"; } } .glyphicon-import { &:before { content: "\e169"; } } .glyphicon-export { &:before { content: "\e170"; } } .glyphicon-send { &:before { content: "\e171"; } } .glyphicon-floppy-disk { &:before { content: "\e172"; } } .glyphicon-floppy-saved { &:before { content: "\e173"; } } .glyphicon-floppy-remove { &:before { content: "\e174"; } } .glyphicon-floppy-save { &:before { content: "\e175"; } } .glyphicon-floppy-open { &:before { content: "\e176"; } } .glyphicon-credit-card { &:before { content: "\e177"; } } .glyphicon-transfer { &:before { content: "\e178"; } } .glyphicon-cutlery { &:before { content: "\e179"; } } .glyphicon-header { &:before { content: "\e180"; } } .glyphicon-compressed { &:before { content: "\e181"; } } .glyphicon-earphone { &:before { content: "\e182"; } } .glyphicon-phone-alt { &:before { content: "\e183"; } } .glyphicon-tower { &:before { content: "\e184"; } } .glyphicon-stats { &:before { content: "\e185"; } } .glyphicon-sd-video { &:before { content: "\e186"; } } .glyphicon-hd-video { &:before { content: "\e187"; } } .glyphicon-subtitles { &:before { content: "\e188"; } } .glyphicon-sound-stereo { &:before { content: "\e189"; } } .glyphicon-sound-dolby { &:before { content: "\e190"; } } .glyphicon-sound-5-1 { &:before { content: "\e191"; } } .glyphicon-sound-6-1 { &:before { content: "\e192"; } } .glyphicon-sound-7-1 { &:before { content: "\e193"; } } .glyphicon-copyright-mark { &:before { content: "\e194"; } } .glyphicon-registration-mark { &:before { content: "\e195"; } } .glyphicon-cloud-download { &:before { content: "\e197"; } } .glyphicon-cloud-upload { &:before { content: "\e198"; } } .glyphicon-tree-conifer { &:before { content: "\e199"; } } .glyphicon-tree-deciduous { &:before { content: "\e200"; } } ================================================ FILE: less/lib/bootstrap/grid.less ================================================ // // Grid system // -------------------------------------------------- // Set the container width, and override it for fixed navbars in media queries .container { .container-fixed(); @media (min-width: @screen-sm) { width: @container-sm; } @media (min-width: @screen-md) { width: @container-md; } @media (min-width: @screen-lg-min) { width: @container-lg; } } // mobile first defaults .row { .make-row(); } // Common styles for small and large grid columns .make-grid-columns(); // Extra small grid // // Columns, offsets, pushes, and pulls for extra small devices like // smartphones. .make-grid-columns-float(xs); .make-grid(@grid-columns, xs, width); .make-grid(@grid-columns, xs, pull); .make-grid(@grid-columns, xs, push); .make-grid(@grid-columns, xs, offset); // Small grid // // Columns, offsets, pushes, and pulls for the small device range, from phones // to tablets. @media (min-width: @screen-sm-min) { .make-grid-columns-float(sm); .make-grid(@grid-columns, sm, width); .make-grid(@grid-columns, sm, pull); .make-grid(@grid-columns, sm, push); .make-grid(@grid-columns, sm, offset); } // Medium grid // // Columns, offsets, pushes, and pulls for the desktop device range. @media (min-width: @screen-md-min) { .make-grid-columns-float(md); .make-grid(@grid-columns, md, width); .make-grid(@grid-columns, md, pull); .make-grid(@grid-columns, md, push); .make-grid(@grid-columns, md, offset); } // Large grid // // Columns, offsets, pushes, and pulls for the large desktop device range. @media (min-width: @screen-lg-min) { .make-grid-columns-float(lg); .make-grid(@grid-columns, lg, width); .make-grid(@grid-columns, lg, pull); .make-grid(@grid-columns, lg, push); .make-grid(@grid-columns, lg, offset); } ================================================ FILE: less/lib/bootstrap/input-groups.less ================================================ // // Input groups // -------------------------------------------------- // Base styles // ------------------------- .input-group { position: relative; // For dropdowns display: table; border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table // Undo padding and float of grid classes &[class*="col-"] { float: none; padding-left: 0; padding-right: 0; } .form-control { width: 100%; margin-bottom: 0; } } // Sizing options // // Remix the default form control sizing classes into new ones for easier // manipulation. .input-group-lg > .form-control, .input-group-lg > .input-group-addon, .input-group-lg > .input-group-btn > .btn { .input-lg(); } .input-group-sm > .form-control, .input-group-sm > .input-group-addon, .input-group-sm > .input-group-btn > .btn { .input-sm(); } // Display as table-cell // ------------------------- .input-group-addon, .input-group-btn, .input-group .form-control { display: table-cell; &:not(:first-child):not(:last-child) { border-radius: 0; } } // Addon and addon wrapper for buttons .input-group-addon, .input-group-btn { width: 1%; white-space: nowrap; vertical-align: middle; // Match the inputs } // Text input groups // ------------------------- .input-group-addon { padding: @padding-base-vertical @padding-base-horizontal; font-size: @font-size-base; font-weight: normal; line-height: 1; color: @input-color; text-align: center; background-color: @input-group-addon-bg; border: 1px solid @input-group-addon-border-color; border-radius: @border-radius-base; // Sizing &.input-sm { padding: @padding-small-vertical @padding-small-horizontal; font-size: @font-size-small; border-radius: @border-radius-small; } &.input-lg { padding: @padding-large-vertical @padding-large-horizontal; font-size: @font-size-large; border-radius: @border-radius-large; } // Nuke default margins from checkboxes and radios to vertically center within. input[type="radio"], input[type="checkbox"] { margin-top: 0; } } // Reset rounded corners .input-group .form-control:first-child, .input-group-addon:first-child, .input-group-btn:first-child > .btn, .input-group-btn:first-child > .dropdown-toggle, .input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle) { .border-right-radius(0); } .input-group-addon:first-child { border-right: 0; } .input-group .form-control:last-child, .input-group-addon:last-child, .input-group-btn:last-child > .btn, .input-group-btn:last-child > .dropdown-toggle, .input-group-btn:first-child > .btn:not(:first-child) { .border-left-radius(0); } .input-group-addon:last-child { border-left: 0; } // Button input groups // ------------------------- .input-group-btn { position: relative; white-space: nowrap; // Negative margin to only have a 1px border between the two &:first-child > .btn { margin-right: -1px; } &:last-child > .btn { margin-left: -1px; } } .input-group-btn > .btn { position: relative; // Jankily prevent input button groups from wrapping + .btn { margin-left: -4px; } // Bring the "active" button to the front &:hover, &:active { z-index: 2; } } ================================================ FILE: less/lib/bootstrap/jumbotron.less ================================================ // // Jumbotron // -------------------------------------------------- .jumbotron { padding: @jumbotron-padding; margin-bottom: @jumbotron-padding; font-size: @jumbotron-font-size; font-weight: 200; line-height: (@line-height-base * 1.5); color: @jumbotron-color; background-color: @jumbotron-bg; h1, .h1 { line-height: 1; color: @jumbotron-heading-color; } p { line-height: 1.4; } .container & { border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container } .container { max-width: 100%; } @media screen and (min-width: @screen-sm-min) { padding-top: (@jumbotron-padding * 1.6); padding-bottom: (@jumbotron-padding * 1.6); .container & { padding-left: (@jumbotron-padding * 2); padding-right: (@jumbotron-padding * 2); } h1, .h1 { font-size: (@font-size-base * 4.5); } } } ================================================ FILE: less/lib/bootstrap/labels.less ================================================ // // Labels // -------------------------------------------------- .label { display: inline; padding: .2em .6em .3em; font-size: 75%; font-weight: bold; line-height: 1; color: @label-color; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: .25em; // Add hover effects, but only for links &[href] { &:hover, &:focus { color: @label-link-hover-color; text-decoration: none; cursor: pointer; } } // Empty labels collapse automatically (not available in IE8) &:empty { display: none; } // Quick fix for labels in buttons .btn & { position: relative; top: -1px; } } // Colors // Contextual variations (linked labels get darker on :hover) .label-default { .label-variant(@label-default-bg); } .label-primary { .label-variant(@label-primary-bg); } .label-success { .label-variant(@label-success-bg); } .label-info { .label-variant(@label-info-bg); } .label-warning { .label-variant(@label-warning-bg); } .label-danger { .label-variant(@label-danger-bg); } ================================================ FILE: less/lib/bootstrap/list-group.less ================================================ // // List groups // -------------------------------------------------- // Base class // // Easily usable on