From 8aa5181126d9480eef502403aa98e469a3597826 Mon Sep 17 00:00:00 2001 From: Chris Rebert Date: Mon, 23 Sep 2013 13:00:43 -0700 Subject: move /assets/ to /docs-assets/ ; fixes #10715 --- docs-assets/js/customizer.js | 290 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) create mode 100644 docs-assets/js/customizer.js (limited to 'docs-assets/js/customizer.js') diff --git a/docs-assets/js/customizer.js b/docs-assets/js/customizer.js new file mode 100644 index 000000000..5abfe4228 --- /dev/null +++ b/docs-assets/js/customizer.js @@ -0,0 +1,290 @@ +window.onload = function () { // wait for load in a dumb way because B-0 + var cw = '/*!\n * Bootstrap v3.0.0\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 ? '
' + err.extract.join('\n') + '
' : '') + '\ +
\ +
').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(configData) { + var data = { + "description": "Bootstrap Customizer Config", + "public": true, + "files": { + "config.json": { + "content": JSON.stringify(configData, null, 2) + } + } + } + $.ajax({ + url: 'https://api.github.com/gists', + type: 'POST', + dataType: 'json', + data: JSON.stringify(data) + }) + .success(function(result) { + history.replaceState(false, document.title, window.location.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, 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]) + } + } + + 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 + } + } + + function generateCSS() { + var $checked = $('#less-section input:checked') + + if (!$checked.length) return false + + var result = {} + var vars = {} + var css = '' + + $('#less-variables-section input') + .each(function () { + $(this).val() && (vars[ $(this).prev().text() ] = $(this).val()) + }) + + css += __less['variables.less'] + if (vars) css += generateCustomCSS(vars) + css += __less['mixins.less'] + css += __less['normalize.less'] + css += __less['scaffolding.less'] + css += $checked + .map(function () { return __less[this.value] }) + .toArray() + .join('\n') + + 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) { + e.preventDefault() + + $compileBtn.attr('disabled', 'disabled') + + generateZip(generateCSS(), generateJavascript(), generateFonts(), function (blob) { + $compileBtn.removeAttr('disabled') + saveAs(blob, "bootstrap.zip") + createGist(getCustomizerData()) + }) + }) + + // 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() +} -- cgit v1.2.3 From 3663e3700540e08e9168d4066dc17b97554d9732 Mon Sep 17 00:00:00 2001 From: Ross Allen Date: Mon, 23 Sep 2013 23:29:45 -0700 Subject: Use bootstrap.less file order in Customizer Appending stylesheets by iterating the `__less` Hash creates an order that is not the same as 'bootstrap.less'. Some stylesheets like 'component-animations.less' and 'modals.less' have selectors with matching specificity, and so file order decides which style wins. This causes issue #10030 by putting `.fade.in` after `.modal-backdrop.in` and gives the backdrop a higher opacity than intended. This change uses the Less ordering in 'bootstrap.less' to generate the final stylesheets in the Customizer to make sure customized file ordering matches the distribution file order. Fixes #10030 --- docs-assets/js/customizer.js | 48 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 11 deletions(-) (limited to 'docs-assets/js/customizer.js') diff --git a/docs-assets/js/customizer.js b/docs-assets/js/customizer.js index 5abfe4228..08c8bfb60 100644 --- a/docs-assets/js/customizer.js +++ b/docs-assets/js/customizer.js @@ -155,10 +155,32 @@ window.onload = function () { // wait for load in a dumb way because B-0 } } + // 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 $checked = $('#less-section input:checked') + 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 (!$checked.length) return false + if (!oneChecked) return false var result = {} var vars = {} @@ -169,15 +191,19 @@ window.onload = function () { // wait for load in a dumb way because B-0 $(this).val() && (vars[ $(this).prev().text() ] = $(this).val()) }) - css += __less['variables.less'] - if (vars) css += generateCustomCSS(vars) - css += __less['mixins.less'] - css += __less['normalize.less'] - css += __less['scaffolding.less'] - css += $checked - .map(function () { return __less[this.value] }) - .toArray() - .join('\n') + $.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 -- cgit v1.2.3 From 58a47b6bfd274e7018316c0ea77ce0ca40a7e53f Mon Sep 17 00:00:00 2001 From: "Stuart P. Bentley" Date: Sat, 14 Sep 2013 04:26:42 -0400 Subject: Customizer: include config.json in bootstrap.zip merges #10633; fixes part of #9951 --- docs-assets/js/customizer.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'docs-assets/js/customizer.js') diff --git a/docs-assets/js/customizer.js b/docs-assets/js/customizer.js index 5abfe4228..cc772d222 100644 --- a/docs-assets/js/customizer.js +++ b/docs-assets/js/customizer.js @@ -31,13 +31,13 @@ window.onload = function () { // wait for load in a dumb way because B-0 return match && decodeURIComponent(match[1].replace(/\+/g, " ")); } - function createGist(configData) { + function createGist(configJson) { var data = { "description": "Bootstrap Customizer Config", "public": true, "files": { "config.json": { - "content": JSON.stringify(configData, null, 2) + "content": configJson } } } @@ -107,7 +107,7 @@ window.onload = function () { // wait for load in a dumb way because B-0 }) } - function generateZip(css, js, fonts, complete) { + 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() @@ -133,6 +133,10 @@ window.onload = function () { // wait for load in a dumb way because B-0 } } + if (config) { + zip.file('config.json', config) + } + var content = zip.generate({type:"blob"}) complete(content) @@ -262,14 +266,17 @@ window.onload = function () { // wait for load in a dumb way because B-0 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(), function (blob) { + generateZip(generateCSS(), generateJavascript(), generateFonts(), configJson, function (blob) { $compileBtn.removeAttr('disabled') saveAs(blob, "bootstrap.zip") - createGist(getCustomizerData()) + createGist(configJson) }) }) -- cgit v1.2.3 From aad70834fc2546d5e877898098707930a7d7a4c3 Mon Sep 17 00:00:00 2001 From: herom Date: Wed, 21 Aug 2013 14:16:45 +0430 Subject: Fixes #9925: convert font data to base64, fixing 0xefbfbd (Unicode Replacement Character) chars in customizer fonts. Merges #9982 --- docs-assets/js/customizer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs-assets/js/customizer.js') diff --git a/docs-assets/js/customizer.js b/docs-assets/js/customizer.js index cc772d222..e59418916 100644 --- a/docs-assets/js/customizer.js +++ b/docs-assets/js/customizer.js @@ -129,7 +129,7 @@ window.onload = function () { // wait for load in a dumb way because B-0 if (fonts) { var fontsFolder = zip.folder('fonts') for (var fileName in fonts) { - fontsFolder.file(fileName, fonts[fileName]) + fontsFolder.file(fileName, fonts[fileName], {base64: true}) } } -- cgit v1.2.3 From c88cd1cd95dc4bef63697124f795fe7e60308a99 Mon Sep 17 00:00:00 2001 From: Chris Rebert Date: Thu, 17 Oct 2013 11:33:04 -0700 Subject: add explicit copyright header to docs CSS & JS files; fixes #11112 --- docs-assets/js/customizer.js | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'docs-assets/js/customizer.js') diff --git a/docs-assets/js/customizer.js b/docs-assets/js/customizer.js index 799707784..29479e5c6 100644 --- a/docs-assets/js/customizer.js +++ b/docs-assets/js/customizer.js @@ -1,3 +1,11 @@ +/*! + * Copyright 2013 Twitter, Inc. + * This work is licensed under the Creative Commons Attribution 3.0 Unported License. + * You should have received a copy of this license along with this work. + * If not, visit 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.0\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' -- cgit v1.2.3 From 1f33dc8033b2b8245df98a79ebe8800c6c0b992e Mon Sep 17 00:00:00 2001 From: Mark Otto Date: Thu, 17 Oct 2013 14:11:40 -0700 Subject: even shorter banner; remove space between slash and period --- docs-assets/js/customizer.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'docs-assets/js/customizer.js') diff --git a/docs-assets/js/customizer.js b/docs-assets/js/customizer.js index 29479e5c6..1b358b914 100644 --- a/docs-assets/js/customizer.js +++ b/docs-assets/js/customizer.js @@ -1,8 +1,8 @@ /*! * Copyright 2013 Twitter, Inc. - * This work is licensed under the Creative Commons Attribution 3.0 Unported License. - * You should have received a copy of this license along with this work. - * If not, visit http://creativecommons.org/licenses/by/3.0/ . + * + * Licensed under the Creative Commons Attribution 3.0 Unported License. For + * details, see http://creativecommons.org/licenses/by/3.0/. */ -- cgit v1.2.3