text: /*!
   * Bootstrap Customizer (http://getbootstrap.com/customize/)
   * Copyright 2011-2016 Twitter, Inc.
   *
   * Licensed under the Creative Commons Attribution 3.0 Unported License. For
   * details, see https://creativecommons.org/licenses/by/3.0/.
   */
  
  /* global JSON, 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.7 (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" aria-label="Close" role="button"><span aria-hidden="true">&times;</span></a>' +
              '<p class="bs-customizer-alert-text"><span class="glyphicon glyphicon-warning-sign" aria-hidden="true"></span><span class="sr-only">Warning:</span>' + msg + '</p>' +
              (err.message ? $('<p></p>').text('Error: ' + err.message)[0].outerHTML : '') +
              (err.extract ? $('<pre class="bs-customizer-alert-extract"></pre>').text(err.extract.join('\n'))[0].outerHTML : '') +
              '</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-label="Close"><span aria-hidden="true">&times;</span></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-label="Close"><span aria-hidden="true">&times;</span></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 null
  
          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 (filename === 'variables.less' && 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 (parseErr, tree) {
              if (parseErr) {
                  return promise.reject(parseErr)
              }
              try {
                  intoResult[baseFilename + '.css'] = cw + tree.toCSS()
                  intoResult[baseFilename + '.min.css'] = cw + tree.toCSS({compress: true})
              } catch (compileErr) {
                  return promise.reject(compileErr)
              }
              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> Problem parsing or compiling 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()
  }
  