/*!
* Copyright 2013 Twitter, Inc.
*
* Licensed under the Creative Commons Attribution 3.0 Unported License. For
* details, see http://creativecommons.org/licenses/by/3.0/.
*/
window.onload = function () { // wait for load in a dumb way because B-0
var cw = '/*!\n * Bootstrap v3.0.2\n *\n * Copyright 2013 Twitter, Inc\n * Licensed under the Apache License v2.0\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Designed and built with all the love in the world @twitter by @mdo and @fat.\n */\n\n'
function showError(msg, err) {
$('
\
\
×\
' + msg + '
' +
(err.extract ? '' : '') + '\
\
').appendTo('body').alert()
throw err
}
function showCallout(msg, showUpTop) {
var callout = $('\
Attention!
\
' + msg + '
\
')
if (showUpTop) {
callout.appendTo('.bs-docs-container')
} else {
callout.insertAfter('.bs-customize-download')
}
}
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) {
var data = {
"description": "Bootstrap Customizer Config",
"public": true,
"files": {
"config.json": {
"content": configJson
}
}
}
$.ajax({
url: 'https://api.github.com/gists',
type: 'POST',
dataType: 'json',
data: JSON.stringify(data)
})
.success(function(result) {
var origin = window.location.protocol + "//" + window.location.host
history.replaceState(false, document.title, origin + window.location.pathname + '?id=' + result.id)
})
.error(function(err) {
showError('Ruh roh! Could not save gist file, configuration not saved.', err)
})
}
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 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)
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])
}
}
})
.error(function(err) {
showError('Error fetching bootstrap config file', err)
})
}
function generateZip(css, js, fonts, config, complete) {
if (!css && !js) return showError('Ruh roh! 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 fileName in js) {
jsFolder.file(fileName, js[fileName])
}
}
if (fonts) {
var fontsFolder = zip.folder('fonts')
for (var fileName in fonts) {
fontsFolder.file(fileName, fonts[fileName], {base64: true})
}
}
if (config) {
zip.file('config.json', config)
}
var content = zip.generate({type:"blob"})
complete(content)
}
function generateCustomCSS(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 from 'bootstrap.less' in the order
// in which they appear in the file.
function bootstrapLessFilenames() {
var IMPORT_REGEX = /^@import \"(.*?)\";$/
var bootstrapLessLines = __less['bootstrap.less'].split('\n')
for (var i = 0, imports = []; i < bootstrapLessLines.length; i++) {
var match = IMPORT_REGEX.exec(bootstrapLessLines[i])
if (match) imports.push(match[1])
}
return imports
}
function generateCSS() {
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 = {}
var css = ''
$('#less-variables-section input')
.each(function () {
$(this).val() && (vars[ $(this).prev().text() ] = $(this).val())
})
$.each(bootstrapLessFilenames(), 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)) css += __less[filename]
// Custom variables are added after Bootstrap variables so the custom
// ones take precedence.
if (('variables.less' === filename) && vars) css += generateCustomCSS(vars)
})
css = css.replace(/@import[^\n]*/gi, '') //strip any imports
try {
var parser = new less.Parser({
paths: ['variables.less', 'mixins.less']
, optimization: 0
, filename: 'bootstrap.css'
}).parse(css, function (err, tree) {
if (err) {
return showError('Ruh roh! Could not parse less files.', err)
}
result = {
'bootstrap.css' : cw + tree.toCSS(),
'bootstrap.min.css' : cw + tree.toCSS({ compress: true })
}
})
} catch (err) {
return showError('Ruh roh! Could not parse less files.', err)
}
return result
}
function generateJavascript() {
var $checked = $('#plugin-section input:checked')
if (!$checked.length) return false
var js = $checked
.map(function () { return __js[this.value] })
.toArray()
.join('\n')
return {
'bootstrap.js': js,
'bootstrap.min.js': cw + uglify(js)
}
}
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')
var $downloadBtn = $('#btn-download')
$compileBtn.on('click', function (e) {
var configData = getCustomizerData()
var configJson = JSON.stringify(configData, null, 2)
e.preventDefault()
$compileBtn.attr('disabled', 'disabled')
generateZip(generateCSS(), generateJavascript(), generateFonts(), configJson, function (blob) {
$compileBtn.removeAttr('disabled')
saveAs(blob, "bootstrap.zip")
createGist(configJson)
})
})
// browser support alerts
if (!window.URL && navigator.userAgent.toLowerCase().indexOf('safari') != -1) {
showCallout("Looks like you're using safari, which sadly doesn't have the best support\
for HTML5 blobs. Because of this your file will be downloaded with the name \"untitled\"
.\
However, if you check your downloads folder, just rename this \"untitled\"
file\
to \"bootstrap.zip\"
and you should be good to go!")
} else if (!window.URL && !window.webkitURL) {
$('.bs-docs-section, .bs-sidebar').css('display', 'none')
showCallout("Looks like your current browser doesn't support the Bootstrap Customizer. Please take a second\
to upgrade to a more modern browser.", true)
}
parseUrl()
}