1
0
Fork 0
mirror of https://github.com/twbs/bootstrap.git synced 2022-11-09 12:25:43 -05:00

Merge pull request #14 from twbs/nuke_customizer

Nuke the customizer
This commit is contained in:
Mark Otto 2014-12-11 12:21:05 -08:00
commit e0a9a7722f
14 changed files with 3 additions and 18522 deletions

View file

@ -18,14 +18,7 @@ module.exports = function (grunt) {
var fs = require('fs'); var fs = require('fs');
var path = require('path'); var path = require('path');
var npmShrinkwrap = require('npm-shrinkwrap'); var npmShrinkwrap = require('npm-shrinkwrap');
var BsLessdocParser = require('./grunt/bs-lessdoc-parser.js');
var getLessVarsData = function () {
var filePath = path.join(__dirname, 'scss/_variables.scss');
var fileContent = fs.readFileSync(filePath, { encoding: 'utf8' });
var parser = new BsLessdocParser(fileContent);
return { sections: parser.parseFile() };
};
// var generateRawFiles = require('./grunt/bs-raw-files-generator.js');
var generateCommonJSModule = require('./grunt/bs-commonjs-generator.js'); var generateCommonJSModule = require('./grunt/bs-commonjs-generator.js');
var configBridge = grunt.file.readJSON('./grunt/configBridge.json', { encoding: 'utf8' }); var configBridge = grunt.file.readJSON('./grunt/configBridge.json', { encoding: 'utf8' });
@ -131,10 +124,6 @@ module.exports = function (grunt) {
src: '<%= concat.bootstrap.dest %>', src: '<%= concat.bootstrap.dest %>',
dest: 'dist/js/<%= pkg.name %>.min.js' dest: 'dist/js/<%= pkg.name %>.min.js'
}, },
customize: {
src: configBridge.paths.customizerJs,
dest: 'docs/assets/js/customize.min.js'
},
docsJs: { docsJs: {
src: configBridge.paths.docsJs, src: configBridge.paths.docsJs,
dest: 'docs/assets/js/docs.min.js' dest: 'docs/assets/js/docs.min.js'
@ -273,21 +262,6 @@ module.exports = function (grunt) {
} }
}, },
jade: {
options: {
pretty: true,
data: getLessVarsData
},
customizerVars: {
src: 'docs/_jade/customizer-variables.jade',
dest: 'docs/_includes/customizer-variables.html'
},
customizerNav: {
src: 'docs/_jade/customizer-nav.jade',
dest: 'docs/_includes/nav/customize.html'
}
},
validation: { validation: {
options: { options: {
charset: 'utf-8', charset: 'utf-8',
@ -417,14 +391,6 @@ module.exports = function (grunt) {
// This can be overzealous, so its changes should always be manually reviewed! // This can be overzealous, so its changes should always be manually reviewed!
grunt.registerTask('change-version-number', 'sed'); grunt.registerTask('change-version-number', 'sed');
// task for building customizer
// grunt.registerTask('build-customizer', ['build-customizer-html', 'build-raw-files']);
// grunt.registerTask('build-customizer-html', 'jade');
// grunt.registerTask('build-raw-files', 'Add scripts/less files to customizer.', function () {
// var banner = grunt.template.process('<%= banner %>');
// generateRawFiles(grunt, banner);
// });
grunt.registerTask('commonjs', 'Generate CommonJS entrypoint module in dist dir.', function () { grunt.registerTask('commonjs', 'Generate CommonJS entrypoint module in dist dir.', function () {
var srcFiles = grunt.config.get('concat.bootstrap.src'); var srcFiles = grunt.config.get('concat.bootstrap.src');
var destFilepath = 'dist/js/npm.js'; var destFilepath = 'dist/js/npm.js';
@ -433,7 +399,7 @@ module.exports = function (grunt) {
// Docs task. // Docs task.
grunt.registerTask('docs-css', ['autoprefixer:docs', 'autoprefixer:examples', 'csscomb:docs', 'csscomb:examples', 'cssmin:docs']); grunt.registerTask('docs-css', ['autoprefixer:docs', 'autoprefixer:examples', 'csscomb:docs', 'csscomb:examples', 'cssmin:docs']);
grunt.registerTask('docs-js', ['uglify:docsJs', 'uglify:customize']); grunt.registerTask('docs-js', ['uglify:docsJs']);
grunt.registerTask('lint-docs-js', ['jshint:assets', 'jscs:assets']); grunt.registerTask('lint-docs-js', ['jshint:assets', 'jscs:assets']);
grunt.registerTask('docs', ['docs-css', 'docs-js', 'lint-docs-js', 'clean:docs', 'copy:docs']); grunt.registerTask('docs', ['docs-css', 'docs-js', 'lint-docs-js', 'clean:docs', 'copy:docs']);

View file

@ -47,17 +47,6 @@
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% if page.slug == "customize" %}
<script>var __configBridge = {{ site.data.configBridge.config | jsonify }}</script>
{% if site.github %}
<script src="../assets/js/customize.min.js"></script>
{% else %}
{% for file in site.data.configBridge.paths.customizerJs %}
<script src="{{ file }}"></script>
{% endfor %}
{% endif %}
{% endif %}
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug --> <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
<script src="../assets/js/ie10-viewport-bug-workaround.js"></script> <script src="../assets/js/ie10-viewport-bug-workaround.js"></script>

View file

@ -1,45 +0,0 @@
<!-- NOTE: DO NOT EDIT THE FOLLOWING SECTION DIRECTLY! It is autogenerated via the `build-customizer-html` Grunt task using the customizer-nav.jade template.-->
<li><a href="#import-drop-target">Import</a></li>
<li><a href="#less">Less components</a></li>
<li><a href="#plugins">jQuery plugins</a></li>
<li><a href="#less-variables">Less variables</a>
<ul class="nav">
<li><a href="#colors">Colors</a></li>
<li><a href="#scaffolding">Scaffolding</a></li>
<li><a href="#global-settings">Global settings</a></li>
<li><a href="#typography">Typography</a></li>
<li><a href="#components">Components</a></li>
<li><a href="#tables">Tables</a></li>
<li><a href="#buttons">Buttons</a></li>
<li><a href="#forms">Forms</a></li>
<li><a href="#dropdowns">Dropdowns</a></li>
<li><a href="#media-queries-breakpoints">Media queries breakpoints</a></li>
<li><a href="#grid-system">Grid system</a></li>
<li><a href="#container-sizes">Container sizes</a></li>
<li><a href="#navbar">Navbar</a></li>
<li><a href="#navs">Navs</a></li>
<li><a href="#tabs">Tabs</a></li>
<li><a href="#pills">Pills</a></li>
<li><a href="#pagination">Pagination</a></li>
<li><a href="#pager">Pager</a></li>
<li><a href="#jumbotron">Jumbotron</a></li>
<li><a href="#form-states-and-alerts">Form states and alerts</a></li>
<li><a href="#tooltips">Tooltips</a></li>
<li><a href="#popovers">Popovers</a></li>
<li><a href="#labels">Labels</a></li>
<li><a href="#modals">Modals</a></li>
<li><a href="#alerts">Alerts</a></li>
<li><a href="#progress-bars">Progress bars</a></li>
<li><a href="#list-group">List group</a></li>
<li><a href="#thumbnails">Thumbnails</a></li>
<li><a href="#badges">Badges</a></li>
<li><a href="#breadcrumbs">Breadcrumbs</a></li>
<li><a href="#carousel">Carousel</a></li>
<li><a href="#close">Close</a></li>
<li><a href="#code">Code</a></li>
<li><a href="#type">Type</a></li>
</ul>
</li>
<li><a href="#download">Download</a></li>
<!-- NOTE: DO NOT EDIT THE PRECEDING SECTION DIRECTLY! It is autogenerated via the `build-customizer-html` Grunt task using the customizer-nav.jade template.-->

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,512 +0,0 @@
/*!
* Bootstrap Customizer (http://getbootstrap.com/customize/)
* Copyright 2011-2014 Twitter, Inc.
*
* Licensed under the Creative Commons Attribution 3.0 Unported License. For
* details, see http://creativecommons.org/licenses/by/3.0/.
*/
/* global JSZip, less, autoprefixer, saveAs, UglifyJS, __configBridge, __js, __less, __fonts */
window.onload = function () { // wait for load in a dumb way because B-0
'use strict';
var cw = '/*!\n' +
' * Bootstrap v3.3.0 (http://getbootstrap.com)\n' +
' * Copyright 2011-' + new Date().getFullYear() + ' Twitter, Inc.\n' +
' * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n' +
' */\n\n'
var supportsFile = (window.File && window.FileReader && window.FileList && window.Blob)
var importDropTarget = $('#import-drop-target')
function showError(msg, err) {
$('<div id="bsCustomizerAlert" class="bs-customizer-alert">' +
'<div class="container">' +
'<a href="#bsCustomizerAlert" data-dismiss="alert" class="close pull-right">&times;</a>' +
'<p class="bs-customizer-alert-text"><span class="glyphicon glyphicon-warning-sign"></span>' + msg + '</p>' +
(err.extract ? '<pre class="bs-customizer-alert-extract">' + err.extract.join('\n') + '</pre>' : '') +
'</div>' +
'</div>').appendTo('body').alert()
throw err
}
function showSuccess(msg) {
$('<div class="bs-callout bs-callout-info">' +
'<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + msg +
'</div>').insertAfter('.bs-customize-download')
}
function showCallout(msg, showUpTop) {
var callout = $('<div class="bs-callout bs-callout-danger">' +
'<h4>Attention!</h4>' +
'<p>' + msg + '</p>' +
'</div>')
if (showUpTop) {
callout.appendTo('.bs-docs-container')
} else {
callout.insertAfter('.bs-customize-download')
}
}
function showAlert(type, msg, insertAfter) {
$('<div class="alert alert-' + type + '">' + msg + '<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button></div>')
.insertAfter(insertAfter)
}
function getQueryParam(key) {
key = key.replace(/[*+?^$.\[\]{}()|\\\/]/g, '\\$&') // escape RegEx meta chars
var match = location.search.match(new RegExp('[?&]' + key + '=([^&]+)(&|$)'))
return match && decodeURIComponent(match[1].replace(/\+/g, ' '))
}
function createGist(configJson, callback) {
var data = {
description: 'Bootstrap Customizer Config',
'public': true,
files: {
'config.json': {
content: configJson
}
}
}
$.ajax({
url: 'https://api.github.com/gists',
type: 'POST',
contentType: 'application/json; charset=UTF-8',
dataType: 'json',
data: JSON.stringify(data)
})
.success(function (result) {
var gistUrl = result.html_url;
var origin = window.location.protocol + '//' + window.location.host
var customizerUrl = origin + window.location.pathname + '?id=' + result.id
showSuccess('<strong>Success!</strong> Your configuration has been saved to <a href="' + gistUrl + '">' + gistUrl + '</a> ' +
'and can be revisited here at <a href="' + customizerUrl + '">' + customizerUrl + '</a> for further customization.')
history.replaceState(false, document.title, customizerUrl)
callback(gistUrl, customizerUrl)
})
.error(function (err) {
try {
showError('<strong>Ruh roh!</strong> Could not save gist file, configuration not saved.', err)
} catch (sameErr) {
// deliberately ignore the error
}
callback('<none>', '<none>')
})
}
function getCustomizerData() {
var vars = {}
$('#less-variables-section input')
.each(function () {
$(this).val() && (vars[$(this).prev().text()] = $(this).val())
})
var data = {
vars: vars,
css: $('#less-section input:checked') .map(function () { return this.value }).toArray(),
js: $('#plugin-section input:checked').map(function () { return this.value }).toArray()
}
if ($.isEmptyObject(data.vars) && !data.css.length && !data.js.length) return
return data
}
function updateCustomizerFromJson(data) {
if (data.js) {
$('#plugin-section input').each(function () {
$(this).prop('checked', ~$.inArray(this.value, data.js))
})
}
if (data.css) {
$('#less-section input').each(function () {
$(this).prop('checked', ~$.inArray(this.value, data.css))
})
}
if (data.vars) {
for (var i in data.vars) {
$('input[data-var="' + i + '"]').val(data.vars[i])
}
}
}
function parseUrl() {
var id = getQueryParam('id')
if (!id) return
$.ajax({
url: 'https://api.github.com/gists/' + id,
type: 'GET',
dataType: 'json'
})
.success(function (result) {
var data = JSON.parse(result.files['config.json'].content)
updateCustomizerFromJson(data)
})
.error(function (err) {
showError('Error fetching bootstrap config file', err)
})
}
function generateZip(css, js, fonts, config, complete) {
if (!css && !js) return showError('<strong>Ruh roh!</strong> No Bootstrap files selected.', new Error('no Bootstrap'))
var zip = new JSZip()
if (css) {
var cssFolder = zip.folder('css')
for (var fileName in css) {
cssFolder.file(fileName, css[fileName])
}
}
if (js) {
var jsFolder = zip.folder('js')
for (var jsFileName in js) {
jsFolder.file(jsFileName, js[jsFileName])
}
}
if (fonts) {
var fontsFolder = zip.folder('fonts')
for (var fontsFileName in fonts) {
fontsFolder.file(fontsFileName, fonts[fontsFileName], { base64: true })
}
}
if (config) {
zip.file('config.json', config)
}
var content = zip.generate({ type: 'blob' })
complete(content)
}
function generateCustomLess(vars) {
var result = ''
for (var key in vars) {
result += key + ': ' + vars[key] + ';\n'
}
return result + '\n\n'
}
function generateFonts() {
var glyphicons = $('#less-section [value="glyphicons.less"]:checked')
if (glyphicons.length) {
return __fonts
}
}
// Returns an Array of @import'd filenames in the order
// in which they appear in the file.
function includedLessFilenames(lessFilename) {
var IMPORT_REGEX = /^@import \"(.*?)\";$/
var lessLines = __less[lessFilename].split('\n')
var imports = []
$.each(lessLines, function (index, lessLine) {
var match = IMPORT_REGEX.exec(lessLine)
if (match) {
var importee = match[1]
var transitiveImports = includedLessFilenames(importee)
$.each(transitiveImports, function (index, transitiveImportee) {
if ($.inArray(transitiveImportee, imports) === -1) {
imports.push(transitiveImportee)
}
})
imports.push(importee)
}
})
return imports
}
function generateLESS(lessFilename, lessFileIncludes, vars) {
var lessSource = __less[lessFilename]
var lessFilenames = includedLessFilenames(lessFilename)
$.each(lessFilenames, function (index, filename) {
var fileInclude = lessFileIncludes[filename]
// Files not explicitly unchecked are compiled into the final stylesheet.
// Core stylesheets like 'normalize.less' are not included in the form
// since disabling them would wreck everything, and so their 'fileInclude'
// will be 'undefined'.
if (fileInclude || (fileInclude == null)) lessSource += __less[filename]
// Custom variables are added after Bootstrap variables so the custom
// ones take precedence.
if (('variables.less' === filename) && vars) lessSource += generateCustomLess(vars)
})
lessSource = lessSource.replace(/@import[^\n]*/gi, '') // strip any imports
return lessSource
}
function compileLESS(lessSource, baseFilename, intoResult) {
var promise = $.Deferred()
var parser = new less.Parser({
paths: ['variables.less', 'mixins.less'],
optimization: 0,
filename: baseFilename + '.css'
})
parser.parse(lessSource, function (err, tree) {
if (err) {
return promise.reject(err)
}
intoResult[baseFilename + '.css'] = cw + tree.toCSS()
intoResult[baseFilename + '.min.css'] = cw + tree.toCSS({ compress: true })
promise.resolve()
})
return promise.promise()
}
function generateCSS(preamble) {
var promise = $.Deferred()
var oneChecked = false
var lessFileIncludes = {}
$('#less-section input').each(function () {
var $this = $(this)
var checked = $this.is(':checked')
lessFileIncludes[$this.val()] = checked
oneChecked = oneChecked || checked
})
if (!oneChecked) return false
var result = {}
var vars = {}
$('#less-variables-section input')
.each(function () {
$(this).val() && (vars[$(this).prev().text()] = $(this).val())
})
var bsLessSource = preamble + generateLESS('bootstrap.less', lessFileIncludes, vars)
var themeLessSource = preamble + generateLESS('theme.less', lessFileIncludes, vars)
var prefixer = autoprefixer({ browsers: __configBridge.autoprefixerBrowsers })
$.when(
compileLESS(bsLessSource, 'bootstrap', result),
compileLESS(themeLessSource, 'bootstrap-theme', result)
).done(function () {
for (var key in result) {
result[key] = prefixer.process(result[key]).css
}
promise.resolve(result)
}).fail(function (err) {
showError('<strong>Ruh roh!</strong> Could not parse less files.', err)
promise.reject()
})
return promise.promise()
}
function uglify(js) {
var ast = UglifyJS.parse(js)
ast.figure_out_scope()
var compressor = UglifyJS.Compressor()
var compressedAst = ast.transform(compressor)
compressedAst.figure_out_scope()
compressedAst.compute_char_frequency()
compressedAst.mangle_names()
var stream = UglifyJS.OutputStream()
compressedAst.print(stream)
return stream.toString()
}
function generateJS(preamble) {
var $checked = $('#plugin-section input:checked')
var jqueryCheck = __configBridge.jqueryCheck.join('\n')
var jqueryVersionCheck = __configBridge.jqueryVersionCheck.join('\n')
if (!$checked.length) return false
var js = $checked
.map(function () { return __js[this.value] })
.toArray()
.join('\n')
preamble = cw + preamble
js = jqueryCheck + jqueryVersionCheck + js
return {
'bootstrap.js': preamble + js,
'bootstrap.min.js': preamble + uglify(js)
}
}
function removeImportAlerts() {
importDropTarget.nextAll('.alert').remove()
}
function handleConfigFileSelect(e) {
e.stopPropagation()
e.preventDefault()
var file = (e.originalEvent.hasOwnProperty('dataTransfer')) ? e.originalEvent.dataTransfer.files[0] : e.originalEvent.target.files[0]
var reader = new FileReader()
reader.onload = function (e) {
var text = e.target.result
try {
var json = JSON.parse(text)
if (!$.isPlainObject(json)) {
throw new Error('JSON data from config file is not an object.')
}
updateCustomizerFromJson(json)
showAlert('success', '<strong>Woohoo!</strong> Your configuration was successfully uploaded. Tweak your settings, then hit Download.', importDropTarget)
} catch (err) {
return showAlert('danger', '<strong>Shucks.</strong> We can only read valid <code>.json</code> files. Please try again.', importDropTarget)
}
}
reader.readAsText(file, 'utf-8')
}
function handleConfigDragOver(e) {
e.stopPropagation()
e.preventDefault()
e.originalEvent.dataTransfer.dropEffect = 'copy'
removeImportAlerts()
}
if (supportsFile) {
importDropTarget
.on('dragover', handleConfigDragOver)
.on('drop', handleConfigFileSelect)
}
$('#import-file-select').on('change', handleConfigFileSelect)
$('#import-manual-trigger').on('click', removeImportAlerts)
var inputsComponent = $('#less-section input')
var inputsPlugin = $('#plugin-section input')
var inputsVariables = $('#less-variables-section input')
$('#less-section .toggle').on('click', function (e) {
e.preventDefault()
inputsComponent.prop('checked', !inputsComponent.is(':checked'))
})
$('#plugin-section .toggle').on('click', function (e) {
e.preventDefault()
inputsPlugin.prop('checked', !inputsPlugin.is(':checked'))
})
$('#less-variables-section .toggle').on('click', function (e) {
e.preventDefault()
inputsVariables.val('')
})
$('[data-dependencies]').on('click', function () {
if (!$(this).is(':checked')) return
var dependencies = this.getAttribute('data-dependencies')
if (!dependencies) return
dependencies = dependencies.split(',')
for (var i = 0; i < dependencies.length; i++) {
var dependency = $('[value="' + dependencies[i] + '"]')
dependency && dependency.prop('checked', true)
}
})
$('[data-dependents]').on('click', function () {
if ($(this).is(':checked')) return
var dependents = this.getAttribute('data-dependents')
if (!dependents) return
dependents = dependents.split(',')
for (var i = 0; i < dependents.length; i++) {
var dependent = $('[value="' + dependents[i] + '"]')
dependent && dependent.prop('checked', false)
}
})
var $compileBtn = $('#btn-compile')
$compileBtn.on('click', function (e) {
var configData = getCustomizerData()
var configJson = JSON.stringify(configData, null, 2)
e.preventDefault()
$compileBtn.attr('disabled', 'disabled')
createGist(configJson, function (gistUrl, customizerUrl) {
configData.customizerUrl = customizerUrl
configJson = JSON.stringify(configData, null, 2)
var preamble = '/*!\n' +
' * Generated using the Bootstrap Customizer (' + customizerUrl + ')\n' +
' * Config saved to config.json and ' + gistUrl + '\n' +
' */\n'
$.when(
generateCSS(preamble),
generateJS(preamble),
generateFonts()
).done(function (css, js, fonts) {
generateZip(css, js, fonts, configJson, function (blob) {
$compileBtn.removeAttr('disabled')
setTimeout(function () { saveAs(blob, 'bootstrap.zip') }, 0)
})
})
})
});
// browser support alert
(function () {
function failback() {
$('.bs-docs-section, .bs-docs-sidebar').css('display', 'none')
showCallout('Looks like your current browser doesn\'t support the Bootstrap Customizer. Please take a second ' +
'to <a href="http://browsehappy.com/">upgrade to a more modern browser</a> (other than Safari).', true)
}
/**
* Based on:
* Blob Feature Check v1.1.0
* https://github.com/ssorallen/blob-feature-check/
* License: Public domain (http://unlicense.org)
*/
var url = window.webkitURL || window.URL // Safari 6 uses "webkitURL".
var svg = new Blob(
['<svg xmlns=\'http://www.w3.org/2000/svg\'></svg>'],
{ type: 'image/svg+xml;charset=utf-8' }
)
var objectUrl = url.createObjectURL(svg);
if (/^blob:/.exec(objectUrl) === null || !supportsFile) {
// `URL.createObjectURL` created a URL that started with something other
// than "blob:", which means it has been polyfilled and is not supported by
// this browser.
failback()
} else {
$('<img>')
.on('load', function () {
$compileBtn.prop('disabled', false)
})
.on('error', failback)
.attr('src', objectUrl)
}
})();
parseUrl()
}

View file

@ -1,197 +0,0 @@
/* Blob.js
* A Blob implementation.
* 2014-07-24
*
* By Eli Grey, http://eligrey.com
* By Devin Samarin, https://github.com/dsamarin
* License: X11/MIT
* See https://github.com/eligrey/Blob.js/blob/master/LICENSE.md
*/
/*global self, unescape */
/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
plusplus: true */
/*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */
(function (view) {
"use strict";
view.URL = view.URL || view.webkitURL;
if (view.Blob && view.URL) {
try {
new Blob;
return;
} catch (e) {}
}
// Internally we use a BlobBuilder implementation to base Blob off of
// in order to support older browsers that only have BlobBuilder
var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) {
var
get_class = function(object) {
return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1];
}
, FakeBlobBuilder = function BlobBuilder() {
this.data = [];
}
, FakeBlob = function Blob(data, type, encoding) {
this.data = data;
this.size = data.length;
this.type = type;
this.encoding = encoding;
}
, FBB_proto = FakeBlobBuilder.prototype
, FB_proto = FakeBlob.prototype
, FileReaderSync = view.FileReaderSync
, FileException = function(type) {
this.code = this[this.name = type];
}
, file_ex_codes = (
"NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR "
+ "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR"
).split(" ")
, file_ex_code = file_ex_codes.length
, real_URL = view.URL || view.webkitURL || view
, real_create_object_URL = real_URL.createObjectURL
, real_revoke_object_URL = real_URL.revokeObjectURL
, URL = real_URL
, btoa = view.btoa
, atob = view.atob
, ArrayBuffer = view.ArrayBuffer
, Uint8Array = view.Uint8Array
, origin = /^[\w-]+:\/*\[?[\w\.:-]+\]?(?::[0-9]+)?/
;
FakeBlob.fake = FB_proto.fake = true;
while (file_ex_code--) {
FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1;
}
// Polyfill URL
if (!real_URL.createObjectURL) {
URL = view.URL = function(uri) {
var
uri_info = document.createElementNS("http://www.w3.org/1999/xhtml", "a")
, uri_origin
;
uri_info.href = uri;
if (!("origin" in uri_info)) {
if (uri_info.protocol.toLowerCase() === "data:") {
uri_info.origin = null;
} else {
uri_origin = uri.match(origin);
uri_info.origin = uri_origin && uri_origin[1];
}
}
return uri_info;
};
}
URL.createObjectURL = function(blob) {
var
type = blob.type
, data_URI_header
;
if (type === null) {
type = "application/octet-stream";
}
if (blob instanceof FakeBlob) {
data_URI_header = "data:" + type;
if (blob.encoding === "base64") {
return data_URI_header + ";base64," + blob.data;
} else if (blob.encoding === "URI") {
return data_URI_header + "," + decodeURIComponent(blob.data);
} if (btoa) {
return data_URI_header + ";base64," + btoa(blob.data);
} else {
return data_URI_header + "," + encodeURIComponent(blob.data);
}
} else if (real_create_object_URL) {
return real_create_object_URL.call(real_URL, blob);
}
};
URL.revokeObjectURL = function(object_URL) {
if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) {
real_revoke_object_URL.call(real_URL, object_URL);
}
};
FBB_proto.append = function(data/*, endings*/) {
var bb = this.data;
// decode data to a binary string
if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) {
var
str = ""
, buf = new Uint8Array(data)
, i = 0
, buf_len = buf.length
;
for (; i < buf_len; i++) {
str += String.fromCharCode(buf[i]);
}
bb.push(str);
} else if (get_class(data) === "Blob" || get_class(data) === "File") {
if (FileReaderSync) {
var fr = new FileReaderSync;
bb.push(fr.readAsBinaryString(data));
} else {
// async FileReader won't work as BlobBuilder is sync
throw new FileException("NOT_READABLE_ERR");
}
} else if (data instanceof FakeBlob) {
if (data.encoding === "base64" && atob) {
bb.push(atob(data.data));
} else if (data.encoding === "URI") {
bb.push(decodeURIComponent(data.data));
} else if (data.encoding === "raw") {
bb.push(data.data);
}
} else {
if (typeof data !== "string") {
data += ""; // convert unsupported types to strings
}
// decode UTF-16 to binary string
bb.push(unescape(encodeURIComponent(data)));
}
};
FBB_proto.getBlob = function(type) {
if (!arguments.length) {
type = null;
}
return new FakeBlob(this.data.join(""), type, "raw");
};
FBB_proto.toString = function() {
return "[object BlobBuilder]";
};
FB_proto.slice = function(start, end, type) {
var args = arguments.length;
if (args < 3) {
type = null;
}
return new FakeBlob(
this.data.slice(start, args > 1 ? end : this.data.length)
, type
, this.encoding
);
};
FB_proto.toString = function() {
return "[object Blob]";
};
FB_proto.close = function() {
this.size = 0;
delete this.data;
};
return FakeBlobBuilder;
}(view));
view.Blob = function(blobParts, options) {
var type = options ? (options.type || "") : "";
var builder = new BlobBuilder();
if (blobParts) {
for (var i = 0, len = blobParts.length; i < len; i++) {
builder.append(blobParts[i]);
}
}
return builder.getBlob(type);
};
}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));

View file

@ -1,243 +0,0 @@
/* FileSaver.js
* A saveAs() FileSaver implementation.
* 2014-08-29
*
* By Eli Grey, http://eligrey.com
* License: X11/MIT
* See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
*/
/*global self */
/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
var saveAs = saveAs
// IE 10+ (native saveAs)
|| (typeof navigator !== "undefined" &&
navigator.msSaveOrOpenBlob && navigator.msSaveOrOpenBlob.bind(navigator))
// Everyone else
|| (function(view) {
"use strict";
// IE <10 is explicitly unsupported
if (typeof navigator !== "undefined" &&
/MSIE [1-9]\./.test(navigator.userAgent)) {
return;
}
var
doc = view.document
// only get URL when necessary in case Blob.js hasn't overridden it yet
, get_URL = function() {
return view.URL || view.webkitURL || view;
}
, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
, can_use_save_link = "download" in save_link
, click = function(node) {
var event = doc.createEvent("MouseEvents");
event.initMouseEvent(
"click", true, false, view, 0, 0, 0, 0, 0
, false, false, false, false, 0, null
);
node.dispatchEvent(event);
}
, webkit_req_fs = view.webkitRequestFileSystem
, req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem
, throw_outside = function(ex) {
(view.setImmediate || view.setTimeout)(function() {
throw ex;
}, 0);
}
, force_saveable_type = "application/octet-stream"
, fs_min_size = 0
// See https://code.google.com/p/chromium/issues/detail?id=375297#c7 for
// the reasoning behind the timeout and revocation flow
, arbitrary_revoke_timeout = 10
, revoke = function(file) {
var revoker = function() {
if (typeof file === "string") { // file is an object URL
get_URL().revokeObjectURL(file);
} else { // file is a File
file.remove();
}
};
if (view.chrome) {
revoker();
} else {
setTimeout(revoker, arbitrary_revoke_timeout);
}
}
, dispatch = function(filesaver, event_types, event) {
event_types = [].concat(event_types);
var i = event_types.length;
while (i--) {
var listener = filesaver["on" + event_types[i]];
if (typeof listener === "function") {
try {
listener.call(filesaver, event || filesaver);
} catch (ex) {
throw_outside(ex);
}
}
}
}
, FileSaver = function(blob, name) {
// First try a.download, then web filesystem, then object URLs
var
filesaver = this
, type = blob.type
, blob_changed = false
, object_url
, target_view
, dispatch_all = function() {
dispatch(filesaver, "writestart progress write writeend".split(" "));
}
// on any filesys errors revert to saving with object URLs
, fs_error = function() {
// don't create more object URLs than needed
if (blob_changed || !object_url) {
object_url = get_URL().createObjectURL(blob);
}
if (target_view) {
target_view.location.href = object_url;
} else {
var new_tab = view.open(object_url, "_blank");
if (new_tab == undefined && typeof safari !== "undefined") {
//Apple do not allow window.open, see http://bit.ly/1kZffRI
view.location.href = object_url
}
}
filesaver.readyState = filesaver.DONE;
dispatch_all();
revoke(object_url);
}
, abortable = function(func) {
return function() {
if (filesaver.readyState !== filesaver.DONE) {
return func.apply(this, arguments);
}
};
}
, create_if_not_found = {create: true, exclusive: false}
, slice
;
filesaver.readyState = filesaver.INIT;
if (!name) {
name = "download";
}
if (can_use_save_link) {
object_url = get_URL().createObjectURL(blob);
save_link.href = object_url;
save_link.download = name;
click(save_link);
filesaver.readyState = filesaver.DONE;
dispatch_all();
revoke(object_url);
return;
}
// Object and web filesystem URLs have a problem saving in Google Chrome when
// viewed in a tab, so I force save with application/octet-stream
// http://code.google.com/p/chromium/issues/detail?id=91158
// Update: Google errantly closed 91158, I submitted it again:
// https://code.google.com/p/chromium/issues/detail?id=389642
if (view.chrome && type && type !== force_saveable_type) {
slice = blob.slice || blob.webkitSlice;
blob = slice.call(blob, 0, blob.size, force_saveable_type);
blob_changed = true;
}
// Since I can't be sure that the guessed media type will trigger a download
// in WebKit, I append .download to the filename.
// https://bugs.webkit.org/show_bug.cgi?id=65440
if (webkit_req_fs && name !== "download") {
name += ".download";
}
if (type === force_saveable_type || webkit_req_fs) {
target_view = view;
}
if (!req_fs) {
fs_error();
return;
}
fs_min_size += blob.size;
req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) {
fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) {
var save = function() {
dir.getFile(name, create_if_not_found, abortable(function(file) {
file.createWriter(abortable(function(writer) {
writer.onwriteend = function(event) {
target_view.location.href = file.toURL();
filesaver.readyState = filesaver.DONE;
dispatch(filesaver, "writeend", event);
revoke(file);
};
writer.onerror = function() {
var error = writer.error;
if (error.code !== error.ABORT_ERR) {
fs_error();
}
};
"writestart progress write abort".split(" ").forEach(function(event) {
writer["on" + event] = filesaver["on" + event];
});
writer.write(blob);
filesaver.abort = function() {
writer.abort();
filesaver.readyState = filesaver.DONE;
};
filesaver.readyState = filesaver.WRITING;
}), fs_error);
}), fs_error);
};
dir.getFile(name, {create: false}, abortable(function(file) {
// delete file if it already exists
file.remove();
save();
}), abortable(function(ex) {
if (ex.code === ex.NOT_FOUND_ERR) {
save();
} else {
fs_error();
}
}));
}), fs_error);
}), fs_error);
}
, FS_proto = FileSaver.prototype
, saveAs = function(blob, name) {
return new FileSaver(blob, name);
}
;
FS_proto.abort = function() {
var filesaver = this;
filesaver.readyState = filesaver.DONE;
dispatch(filesaver, "abort");
};
FS_proto.readyState = FS_proto.INIT = 0;
FS_proto.WRITING = 1;
FS_proto.DONE = 2;
FS_proto.error =
FS_proto.onwritestart =
FS_proto.onprogress =
FS_proto.onwrite =
FS_proto.onabort =
FS_proto.onerror =
FS_proto.onwriteend =
null;
return saveAs;
}(
typeof self !== "undefined" && self
|| typeof window !== "undefined" && window
|| this.content
));
// `self` is undefined in Firefox for Android content script context
// while `this` is nsIContentFrameMessageManager
// with an attribute `content` that corresponds to the window
if (typeof module !== "undefined" && module !== null) {
module.exports = saveAs;
} else if ((typeof define !== "undefined" && define !== null) && (define.amd != null)) {
define([], function() {
return saveAs;
});
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,377 +0,0 @@
---
layout: default
title: Customize and download
slug: customize
lead: Customize Bootstrap's components, Less variables, and jQuery plugins to get your very own version.
---
<!-- less.js isn't IE8-compatible and throws an exception during initialization, so our Blob compatibility check and error messaging code never get called in that case.
So we use a conditional comment instead to inform folks about the lack of IE8 support.
-->
<!--[if lt IE 9]>
<style>
.bs-customizer,
.bs-customizer-import,
.bs-docs-sidebar {
display: none;
}
</style>
<div class="alert alert-danger">
<strong>The Bootstrap Customizer does not support IE9 and below.</strong><br>
Please take a second to <a href="http://browsehappy.com/">upgrade to a more modern browser</a>.
</div>
<![endif]-->
<!-- Customizer form -->
<div class="bs-docs-section bs-customizer-import">
<div id="import-drop-target" class="bs-dropzone">
<h2></h2>
<p class="lead">Have an existing configuration? Upload your <code>config.json</code> to import it.</p>
<p>Drag and drop here, or <label id="import-manual-trigger" class="btn-link">manually upload<input type="file" id="import-file-select" class="hidden"></label>.</p>
<hr>
<p><strong>Don't have one?</strong> That's okay—just start customizing the fields below.</p>
</div>
</div><!-- /import -->
<form class="bs-customizer">
<div class="bs-docs-section" id="less-section">
<button class="btn btn-secondary toggle" type="button">Toggle all</button>
<h1 id="less" class="page-header">Less files</h1>
<p class="lead">Choose which Less files to compile into your custom build of Bootstrap. Not sure which files to use? Read through the <a href="../css/">CSS</a> and <a href="../components/">Components</a> pages in the docs.</p>
<div class="row">
<div class="col-xs-6 col-sm-4">
<h3>Common CSS</h3>
<div class="checkbox">
<label>
<input type="checkbox" checked value="print.less">
Print media styles
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="type.less">
Typography
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="code.less">
Code
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="grid.less">
Grid system
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="tables.less">
Tables
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="forms.less" data-dependents="navbar.less,input-groups.less">
Forms
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="buttons.less" data-dependents="button-groups.less">
Buttons
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="responsive-utilities.less">
Responsive utilities
</label>
</div>
</div><!-- .col-xs-6 .col-sm-4 -->
<div class="col-xs-6 col-sm-4">
<h3>Components</h3>
<div class="checkbox">
<label>
<input type="checkbox" checked value="glyphicons.less">
Glyphicons
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="button-groups.less" data-dependencies="buttons.less">
Button groups
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="input-groups.less" data-dependencies="forms.less">
Input groups
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="navs.less" data-dependents="navbar.less">
Navs
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="navbar.less" data-dependencies="forms.less,navs.less">
Navbar
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="breadcrumbs.less">
Breadcrumbs
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="pagination.less">
Pagination
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="pager.less">
Pager
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="labels.less">
Labels
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="badges.less">
Badges
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="jumbotron.less">
Jumbotron
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="thumbnails.less">
Thumbnails
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="alerts.less">
Alerts
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="progress-bars.less">
Progress bars
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="media.less">
Media items
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="list-group.less">
List groups
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="panels.less">
Panels
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="responsive-embed.less">
Responsive embed
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="wells.less">
Wells
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="close.less">
Close icon
</label>
</div>
</div><!-- .col-xs-6 .col-sm-4 -->
<div class="col-xs-6 col-sm-4">
<h3>JavaScript components</h3>
<div class="checkbox">
<label>
<input type="checkbox" checked value="component-animations.less">
Component animations (for JS)
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="dropdowns.less">
Dropdowns
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="tooltip.less">
Tooltips
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="popovers.less">
Popovers
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="modals.less">
Modals
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="carousel.less">
Carousel
</label>
</div>
</div><!-- .col-xs-6 .col-sm-4 -->
</div><!-- /.row -->
</div>
<div class="bs-docs-section" id="plugin-section">
<button class="btn btn-secondary toggle" type="button">Toggle all</button>
<h1 id="plugins" class="page-header">jQuery plugins</h1>
<p class="lead">Choose which jQuery plugins should be included in your custom JavaScript files. Unsure what to include? Read the <a href="../javascript/">JavaScript</a> page in the docs.</p>
<div class="row">
<div class="col-lg-6">
<h4>Linked to components</h4>
<div class="checkbox">
<label>
<input type="checkbox" checked value="alert.js">
Alert dismissal
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="button.js">
Advanced buttons
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="carousel.js">
Carousel functionality
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="dropdown.js">
Dropdowns
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="modal.js">
Modals
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="tooltip.js">
Tooltips
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="popover.js" data-dependencies="tooltip.js">
Popovers <small>(requires Tooltips)</small>
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="tab.js">
Togglable tabs
</label>
</div>
</div>
<div class="col-lg-6">
<h4>Magic</h4>
<div class="checkbox">
<label>
<input type="checkbox" checked value="affix.js">
Affix
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="collapse.js">
Collapse
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="scrollspy.js">
Scrollspy
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" checked value="transition.js">
Transitions <small>(required for any kind of animation)</small>
</label>
</div>
</div>
</div>
<div class="bs-callout bs-callout-info">
<h4>Produces two files</h4>
<p>All checked plugins will be compiled into a readable <code>bootstrap.js</code> and a minified <code>bootstrap.min.js</code>. We recommend you use the minified version in production.</p>
</div>
<div class="bs-callout bs-callout-danger">
<h4>jQuery required</h4>
<p>All plugins require the latest version of <a href="http://jquery.com/" target="_blank">jQuery</a> to be included.</p>
</div>
</div>
<div class="bs-docs-section" id="less-variables-section">
<button class="btn btn-secondary toggle" type="button">Reset to defaults</button>
<h1 id="less-variables" class="page-header">Less variables</h1>
<p class="lead">Customize Less variables to define colors, sizes and more inside your custom CSS stylesheets.</p>
{% include customizer-variables.html %}
</div>
<div class="bs-docs-section">
<h1 id="download" class="page-header">Download</h1>
<p class="lead">Hooray! Your custom version of Bootstrap is now ready to be compiled. Just click the button below to finish the process.</p>
<div class="bs-customize-download">
<button type="submit" id="btn-compile" disabled class="btn btn-block btn-lg btn-outline" onclick="ga('send', 'event', 'Customize', 'Download', 'Customize and Download');">Compile and Download</button>
</div>
</div><!-- /download -->
</form>

View file

@ -1,15 +1,5 @@
{ {
"paths": { "paths": {
"customizerJs": [
"../assets/js/vendor/autoprefixer.js",
"../assets/js/vendor/less.min.js",
"../assets/js/vendor/jszip.min.js",
"../assets/js/vendor/uglify.min.js",
"../assets/js/vendor/Blob.js",
"../assets/js/vendor/FileSaver.js",
"../assets/js/raw-files.min.js",
"../assets/js/src/customizer.js"
],
"docsJs": [ "docsJs": [
"../assets/js/vendor/holder.js", "../assets/js/vendor/holder.js",
"../assets/js/vendor/ZeroClipboard.min.js", "../assets/js/vendor/ZeroClipboard.min.js",