problem_child 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.bowerrc +3 -0
  3. data/.gitignore +10 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/Procfile +1 -0
  7. data/README.md +53 -0
  8. data/Rakefile +8 -0
  9. data/bower.json +13 -0
  10. data/config.ru +3 -0
  11. data/lib/problem_child.rb +126 -0
  12. data/lib/problem_child/public/vendor/bootstrap/.bower.json +47 -0
  13. data/lib/problem_child/public/vendor/bootstrap/Gruntfile.js +472 -0
  14. data/lib/problem_child/public/vendor/bootstrap/LICENSE +21 -0
  15. data/lib/problem_child/public/vendor/bootstrap/README.md +129 -0
  16. data/lib/problem_child/public/vendor/bootstrap/bower.json +38 -0
  17. data/lib/problem_child/public/vendor/bootstrap/dist/css/bootstrap-theme.css +470 -0
  18. data/lib/problem_child/public/vendor/bootstrap/dist/css/bootstrap-theme.css.map +1 -0
  19. data/lib/problem_child/public/vendor/bootstrap/dist/css/bootstrap-theme.min.css +5 -0
  20. data/lib/problem_child/public/vendor/bootstrap/dist/css/bootstrap.css +6332 -0
  21. data/lib/problem_child/public/vendor/bootstrap/dist/css/bootstrap.css.map +1 -0
  22. data/lib/problem_child/public/vendor/bootstrap/dist/css/bootstrap.min.css +5 -0
  23. data/lib/problem_child/public/vendor/bootstrap/dist/fonts/glyphicons-halflings-regular.eot +0 -0
  24. data/lib/problem_child/public/vendor/bootstrap/dist/fonts/glyphicons-halflings-regular.svg +229 -0
  25. data/lib/problem_child/public/vendor/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf +0 -0
  26. data/lib/problem_child/public/vendor/bootstrap/dist/fonts/glyphicons-halflings-regular.woff +0 -0
  27. data/lib/problem_child/public/vendor/bootstrap/dist/js/bootstrap.js +2320 -0
  28. data/lib/problem_child/public/vendor/bootstrap/dist/js/bootstrap.min.js +7 -0
  29. data/lib/problem_child/public/vendor/bootstrap/dist/js/npm.js +13 -0
  30. data/lib/problem_child/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.eot +0 -0
  31. data/lib/problem_child/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.svg +229 -0
  32. data/lib/problem_child/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
  33. data/lib/problem_child/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff +0 -0
  34. data/lib/problem_child/public/vendor/bootstrap/js/.jscsrc +34 -0
  35. data/lib/problem_child/public/vendor/bootstrap/js/.jshintrc +15 -0
  36. data/lib/problem_child/public/vendor/bootstrap/js/affix.js +162 -0
  37. data/lib/problem_child/public/vendor/bootstrap/js/alert.js +94 -0
  38. data/lib/problem_child/public/vendor/bootstrap/js/button.js +116 -0
  39. data/lib/problem_child/public/vendor/bootstrap/js/carousel.js +240 -0
  40. data/lib/problem_child/public/vendor/bootstrap/js/collapse.js +211 -0
  41. data/lib/problem_child/public/vendor/bootstrap/js/dropdown.js +161 -0
  42. data/lib/problem_child/public/vendor/bootstrap/js/modal.js +324 -0
  43. data/lib/problem_child/public/vendor/bootstrap/js/popover.js +119 -0
  44. data/lib/problem_child/public/vendor/bootstrap/js/scrollspy.js +175 -0
  45. data/lib/problem_child/public/vendor/bootstrap/js/tab.js +153 -0
  46. data/lib/problem_child/public/vendor/bootstrap/js/tooltip.js +478 -0
  47. data/lib/problem_child/public/vendor/bootstrap/js/transition.js +59 -0
  48. data/lib/problem_child/public/vendor/bootstrap/package.json +82 -0
  49. data/lib/problem_child/public/vendor/jquery/.bower.json +37 -0
  50. data/lib/problem_child/public/vendor/jquery/MIT-LICENSE.txt +21 -0
  51. data/lib/problem_child/public/vendor/jquery/bower.json +27 -0
  52. data/lib/problem_child/public/vendor/jquery/dist/jquery.js +9205 -0
  53. data/lib/problem_child/public/vendor/jquery/dist/jquery.min.js +5 -0
  54. data/lib/problem_child/public/vendor/jquery/dist/jquery.min.map +1 -0
  55. data/lib/problem_child/version.rb +3 -0
  56. data/lib/problem_child/views/form.erb +22 -0
  57. data/lib/problem_child/views/layout.erb +27 -0
  58. data/problem_child.gemspec +32 -0
  59. data/script/bootstrap +1 -0
  60. data/script/console +1 -0
  61. data/script/release +38 -0
  62. data/script/server +1 -0
  63. metadata +260 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 08d5b9350b508efeb191f28f4526a654a8d10985
4
+ data.tar.gz: 29c20c08be11903467679bcb0ba4e1815b5bb6bd
5
+ SHA512:
6
+ metadata.gz: 4411bf367cb7a24ed7487f132a3e4a5e6a2f1d3c8cb9bbb12ff4c2e5df0b3c6549090358ccf34f7a873969baff666756b2a338a4dbe1e149c361c6a40de98306
7
+ data.tar.gz: 6b5afb95ea27827ff55ea88ef0f6cf0e2c0f417ac5768036d00c3d3ad4ea5e98be2c935cd984b3e5bd1ea877ff96878989e8fd97307f501baaaca7d8267c50eb
data/.bowerrc ADDED
@@ -0,0 +1,3 @@
1
+ {
2
+ "directory": "lib/problem_child/public/vendor"
3
+ }
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ .env
5
+
6
+ #bower bloat
7
+ lib/problem_child/public/vendor/bootstrap/grunt
8
+ lib/problem_child/public/vendor/bootstrap/less
9
+ lib/problem_child/public/vendor/bootstrap/test-infra
10
+ lib/problem_child/public/vendor/jquery/src
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in problem_child.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Ben Balter
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Procfile ADDED
@@ -0,0 +1 @@
1
+ web: bundle exec rackup config.ru -p $PORT
data/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # Problem Child
2
+
3
+ Allows authenticated or anonymous users to fill out a standard web form to create GitHub issues.
4
+
5
+ ## Usage
6
+
7
+ 1. Create a `Gemfile` and add `gem "problem_child"`
8
+ 2. Create a `config.ru` file and add the following:
9
+ ```ruby
10
+ require "problem_child"
11
+ run ProblemChild::App
12
+ ```
13
+ 3. Follow the configuration options below
14
+
15
+ ## Configuring
16
+
17
+ You must set the following as environmental variables:
18
+
19
+ * `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET` - Created via [github.com/settings/applications/new](https://github.com/settings/applications/new)
20
+ * `GITHUB_REPO` - the repo to open the issue against in the form of `owner/repo`
21
+
22
+ You must also set **one** of the following:
23
+
24
+ * `GITHUB_TOKEN` - A personal access token for a bot account with the ability to create an issue in the `GITHUB_REPO` if you would like all submissions to be anonymous
25
+ * `GITHUB_ORG_ID` - The GitHub Org ID e.g, `@whitehouse` if you'd like all users to authenticate against a GitHub Org prior to being presented the form
26
+ * `GITHUB_TEAM_ID` - The numeric Team ID (e.g., 1234) if you'd like all users to authenticate against a GitHub Team prior to being presented the form
27
+
28
+ *Pro-tip*: When developing locally, you can add these values to a `.env` file in the project root, and they will be automatically read in on load
29
+
30
+ ## Customizing
31
+
32
+ By default, Problem Child will prompt the user with a simple form that contains only the title and body. If you'd like to customize the form, you must do the following:
33
+
34
+ 1. Create a new folder called `views`
35
+ 2. Create a `layout.erb` and `form.erb`
36
+ 3. Customize both the layout and form as a standard HTML form. Check out [these examples](lib/problem_child/views) to get started.
37
+ 4. Add the following (middle) line to your `config.ru` file:
38
+
39
+ ```ruby
40
+ require "problem_child"
41
+ ProblemChild.views_dir = "/path/to/your/views/directory"
42
+ run ProblemChild::App
43
+ ```
44
+
45
+ *Pro-tip*: You can use any standard HTML form fields, but be sure to name one field `title`, which will become the issue title.
46
+
47
+ ## Contributing
48
+
49
+ 1. Fork it ( https://github.com/[my-github-username]/problem_child/fork )
50
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
51
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
52
+ 4. Push to the branch (`git push origin my-new-feature`)
53
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new(:test) do |test|
5
+ test.libs << 'lib' << 'test'
6
+ test.pattern = 'test/**/test_problem_child*.rb'
7
+ test.verbose = true
8
+ end
data/bower.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "problem_child",
3
+ "version": "0.0.1",
4
+ "authors": [
5
+ "Ben Balter <ben.balter@github.com>"
6
+ ],
7
+ "main": "config.ru",
8
+ "license": "MIT",
9
+ "homepage": "https://github.com/benbalter/problem_child",
10
+ "dependencies": {
11
+ "bootstrap": "~3.3.1"
12
+ }
13
+ }
data/config.ru ADDED
@@ -0,0 +1,3 @@
1
+ require "problem_child"
2
+
3
+ run ProblemChild::App
@@ -0,0 +1,126 @@
1
+ require 'octokit'
2
+ require 'sinatra'
3
+ require 'sinatra_auth_github'
4
+ require 'dotenv'
5
+ require 'json'
6
+ require 'active_support'
7
+ require 'active_support/core_ext/string'
8
+ require "problem_child/version"
9
+
10
+ module ProblemChild
11
+
12
+ def self.root
13
+ File.expand_path "./problem_child", File.dirname(__FILE__)
14
+ end
15
+
16
+ def self.views_dir
17
+ @views_dir ||= File.expand_path "views", ProblemChild.root
18
+ end
19
+
20
+ def self.views_dir=(dir)
21
+ @views_dir = dir
22
+ end
23
+
24
+ class App < Sinatra::Base
25
+
26
+ enable :sessions
27
+
28
+ set :github_options, {
29
+ :scopes => "repo",
30
+ :secret => ENV['GITHUB_CLIENT_SECRET'],
31
+ :client_id => ENV['GITHUB_CLIENT_ID'],
32
+ }
33
+
34
+ register Sinatra::Auth::Github
35
+
36
+ use Rack::Session::Cookie, {
37
+ :http_only => true,
38
+ :secret => SecureRandom.hex
39
+ }
40
+
41
+ configure :production do
42
+ require 'rack-ssl-enforcer'
43
+ use Rack::SslEnforcer
44
+ end
45
+
46
+ set :views, Proc.new { ProblemChild.views_dir }
47
+ set :root, Proc.new { ProblemChild.root }
48
+ set :public_folder, Proc.new { File.expand_path "public", ProblemChild.root }
49
+
50
+ def repo
51
+ ENV["GITHUB_REPO"]
52
+ end
53
+
54
+ def user
55
+ env['warden'].user unless env['warden'].nil?
56
+ end
57
+
58
+ def anonymous_submissions?
59
+ ENV["GITHUB_TOKEN"] && !ENV["GITHUB_TOKEN"].empty?
60
+ end
61
+
62
+ def token
63
+ if anonymous_submissions?
64
+ ENV["GITHUB_TOKEN"]
65
+ elsif !user.nil?
66
+ user.token
67
+ end
68
+ end
69
+
70
+ def client
71
+ @client ||= Octokit::Client.new :access_token => token
72
+ end
73
+
74
+ def render_template(template, locals={})
75
+ halt erb template, :layout => :layout, :locals => locals.merge({ :template => template })
76
+ end
77
+
78
+ def issue_body
79
+ form_data.reject { |key, value| key == "title" }.map { |key,value| "* **#{key.humanize}**: #{value}"}.join("\n")
80
+ end
81
+
82
+ # abstraction to allow cached form data to be used in place of default params
83
+ def form_data
84
+ session["form_data"].nil? ? params : JSON.parse(session["form_data"])
85
+ end
86
+
87
+ def create_issue(data=params)
88
+ client.create_issue(repo, form_data["title"], issue_body)
89
+ end
90
+
91
+ def auth!
92
+ if anonymous_submissions?
93
+ true
94
+ elsif ENV['GITHUB_TEAM_ID']
95
+ github_team_authenticate!(ENV['GITHUB_TEAM_ID'])
96
+ elsif ENV['GITHUB_ORG_ID']
97
+ github_organization_authenticate!(ENV['GITHUB_ORG_ID'])
98
+ else
99
+ raise "Must define GITHUB_TEAM_ID, GITHUB_ORG_ID, OR GITHUB_TOKEN"
100
+ halt 401
101
+ end
102
+ end
103
+
104
+ get "/" do
105
+ if session[:form_data]
106
+ flash = :success if create_issue
107
+ session[:form_data] = nil
108
+ else
109
+ flash = nil
110
+ auth!
111
+ end
112
+ halt erb :form, :layout => :layout, :locals => { :repo => repo, :anonymous => anonymous_submissions?, :flash => flash }
113
+ end
114
+
115
+ post "/" do
116
+ if anonymous_submissions?
117
+ create_issue
118
+ else
119
+ session[:form_data] = params.to_json
120
+ auth!
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+ Dotenv.load unless ProblemChild::App.production?
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "bootstrap",
3
+ "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.",
4
+ "version": "3.3.1",
5
+ "keywords": [
6
+ "css",
7
+ "js",
8
+ "less",
9
+ "mobile-first",
10
+ "responsive",
11
+ "front-end",
12
+ "framework",
13
+ "web"
14
+ ],
15
+ "homepage": "http://getbootstrap.com",
16
+ "main": [
17
+ "less/bootstrap.less",
18
+ "dist/css/bootstrap.css",
19
+ "dist/js/bootstrap.js",
20
+ "dist/fonts/glyphicons-halflings-regular.eot",
21
+ "dist/fonts/glyphicons-halflings-regular.svg",
22
+ "dist/fonts/glyphicons-halflings-regular.ttf",
23
+ "dist/fonts/glyphicons-halflings-regular.woff"
24
+ ],
25
+ "ignore": [
26
+ "/.*",
27
+ "_config.yml",
28
+ "CNAME",
29
+ "composer.json",
30
+ "CONTRIBUTING.md",
31
+ "docs",
32
+ "js/tests",
33
+ "test-infra"
34
+ ],
35
+ "dependencies": {
36
+ "jquery": ">= 1.9.1"
37
+ },
38
+ "_release": "3.3.1",
39
+ "_resolution": {
40
+ "type": "version",
41
+ "tag": "v3.3.1",
42
+ "commit": "9a7e365c2c4360335d25246dac11afb1f577210a"
43
+ },
44
+ "_source": "git://github.com/twbs/bootstrap.git",
45
+ "_target": "~3.3.1",
46
+ "_originalSource": "bootstrap"
47
+ }
@@ -0,0 +1,472 @@
1
+ /*!
2
+ * Bootstrap's Gruntfile
3
+ * http://getbootstrap.com
4
+ * Copyright 2013-2014 Twitter, Inc.
5
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
6
+ */
7
+
8
+ module.exports = function (grunt) {
9
+ 'use strict';
10
+
11
+ // Force use of Unix newlines
12
+ grunt.util.linefeed = '\n';
13
+
14
+ RegExp.quote = function (string) {
15
+ return string.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&');
16
+ };
17
+
18
+ var fs = require('fs');
19
+ var path = require('path');
20
+ var npmShrinkwrap = require('npm-shrinkwrap');
21
+ var BsLessdocParser = require('./grunt/bs-lessdoc-parser.js');
22
+ var getLessVarsData = function () {
23
+ var filePath = path.join(__dirname, 'less/variables.less');
24
+ var fileContent = fs.readFileSync(filePath, { encoding: 'utf8' });
25
+ var parser = new BsLessdocParser(fileContent);
26
+ return { sections: parser.parseFile() };
27
+ };
28
+ var generateRawFiles = require('./grunt/bs-raw-files-generator.js');
29
+ var generateCommonJSModule = require('./grunt/bs-commonjs-generator.js');
30
+ var configBridge = grunt.file.readJSON('./grunt/configBridge.json', { encoding: 'utf8' });
31
+
32
+ Object.keys(configBridge.paths).forEach(function (key) {
33
+ configBridge.paths[key].forEach(function (val, i, arr) {
34
+ arr[i] = path.join('./docs/assets', val);
35
+ });
36
+ });
37
+
38
+ // Project configuration.
39
+ grunt.initConfig({
40
+
41
+ // Metadata.
42
+ pkg: grunt.file.readJSON('package.json'),
43
+ banner: '/*!\n' +
44
+ ' * Bootstrap v<%= pkg.version %> (<%= pkg.homepage %>)\n' +
45
+ ' * Copyright 2011-<%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' +
46
+ ' * Licensed under <%= pkg.license.type %> (<%= pkg.license.url %>)\n' +
47
+ ' */\n',
48
+ jqueryCheck: configBridge.config.jqueryCheck.join('\n'),
49
+ jqueryVersionCheck: configBridge.config.jqueryVersionCheck.join('\n'),
50
+
51
+ // Task configuration.
52
+ clean: {
53
+ dist: 'dist',
54
+ docs: 'docs/dist'
55
+ },
56
+
57
+ jshint: {
58
+ options: {
59
+ jshintrc: 'js/.jshintrc'
60
+ },
61
+ grunt: {
62
+ options: {
63
+ jshintrc: 'grunt/.jshintrc'
64
+ },
65
+ src: ['Gruntfile.js', 'grunt/*.js']
66
+ },
67
+ core: {
68
+ src: 'js/*.js'
69
+ },
70
+ test: {
71
+ options: {
72
+ jshintrc: 'js/tests/unit/.jshintrc'
73
+ },
74
+ src: 'js/tests/unit/*.js'
75
+ },
76
+ assets: {
77
+ src: ['docs/assets/js/src/*.js', 'docs/assets/js/*.js', '!docs/assets/js/*.min.js']
78
+ }
79
+ },
80
+
81
+ jscs: {
82
+ options: {
83
+ config: 'js/.jscsrc'
84
+ },
85
+ grunt: {
86
+ src: '<%= jshint.grunt.src %>'
87
+ },
88
+ core: {
89
+ src: '<%= jshint.core.src %>'
90
+ },
91
+ test: {
92
+ src: '<%= jshint.test.src %>'
93
+ },
94
+ assets: {
95
+ options: {
96
+ requireCamelCaseOrUpperCaseIdentifiers: null
97
+ },
98
+ src: '<%= jshint.assets.src %>'
99
+ }
100
+ },
101
+
102
+ concat: {
103
+ options: {
104
+ banner: '<%= banner %>\n<%= jqueryCheck %>\n<%= jqueryVersionCheck %>',
105
+ stripBanners: false
106
+ },
107
+ bootstrap: {
108
+ src: [
109
+ 'js/transition.js',
110
+ 'js/alert.js',
111
+ 'js/button.js',
112
+ 'js/carousel.js',
113
+ 'js/collapse.js',
114
+ 'js/dropdown.js',
115
+ 'js/modal.js',
116
+ 'js/tooltip.js',
117
+ 'js/popover.js',
118
+ 'js/scrollspy.js',
119
+ 'js/tab.js',
120
+ 'js/affix.js'
121
+ ],
122
+ dest: 'dist/js/<%= pkg.name %>.js'
123
+ }
124
+ },
125
+
126
+ uglify: {
127
+ options: {
128
+ preserveComments: 'some'
129
+ },
130
+ core: {
131
+ src: '<%= concat.bootstrap.dest %>',
132
+ dest: 'dist/js/<%= pkg.name %>.min.js'
133
+ },
134
+ customize: {
135
+ src: configBridge.paths.customizerJs,
136
+ dest: 'docs/assets/js/customize.min.js'
137
+ },
138
+ docsJs: {
139
+ src: configBridge.paths.docsJs,
140
+ dest: 'docs/assets/js/docs.min.js'
141
+ }
142
+ },
143
+
144
+ qunit: {
145
+ options: {
146
+ inject: 'js/tests/unit/phantom.js'
147
+ },
148
+ files: 'js/tests/index.html'
149
+ },
150
+
151
+ less: {
152
+ compileCore: {
153
+ options: {
154
+ strictMath: true,
155
+ sourceMap: true,
156
+ outputSourceFiles: true,
157
+ sourceMapURL: '<%= pkg.name %>.css.map',
158
+ sourceMapFilename: 'dist/css/<%= pkg.name %>.css.map'
159
+ },
160
+ src: 'less/bootstrap.less',
161
+ dest: 'dist/css/<%= pkg.name %>.css'
162
+ },
163
+ compileTheme: {
164
+ options: {
165
+ strictMath: true,
166
+ sourceMap: true,
167
+ outputSourceFiles: true,
168
+ sourceMapURL: '<%= pkg.name %>-theme.css.map',
169
+ sourceMapFilename: 'dist/css/<%= pkg.name %>-theme.css.map'
170
+ },
171
+ src: 'less/theme.less',
172
+ dest: 'dist/css/<%= pkg.name %>-theme.css'
173
+ }
174
+ },
175
+
176
+ autoprefixer: {
177
+ options: {
178
+ browsers: configBridge.config.autoprefixerBrowsers
179
+ },
180
+ core: {
181
+ options: {
182
+ map: true
183
+ },
184
+ src: 'dist/css/<%= pkg.name %>.css'
185
+ },
186
+ theme: {
187
+ options: {
188
+ map: true
189
+ },
190
+ src: 'dist/css/<%= pkg.name %>-theme.css'
191
+ },
192
+ docs: {
193
+ src: 'docs/assets/css/src/docs.css'
194
+ },
195
+ examples: {
196
+ expand: true,
197
+ cwd: 'docs/examples/',
198
+ src: ['**/*.css'],
199
+ dest: 'docs/examples/'
200
+ }
201
+ },
202
+
203
+ csslint: {
204
+ options: {
205
+ csslintrc: 'less/.csslintrc'
206
+ },
207
+ dist: [
208
+ 'dist/css/bootstrap.css',
209
+ 'dist/css/bootstrap-theme.css'
210
+ ],
211
+ examples: [
212
+ 'docs/examples/**/*.css'
213
+ ],
214
+ docs: {
215
+ options: {
216
+ ids: false,
217
+ 'overqualified-elements': false
218
+ },
219
+ src: 'docs/assets/css/src/docs.css'
220
+ }
221
+ },
222
+
223
+ cssmin: {
224
+ options: {
225
+ compatibility: 'ie8',
226
+ keepSpecialComments: '*',
227
+ noAdvanced: true
228
+ },
229
+ minifyCore: {
230
+ src: 'dist/css/<%= pkg.name %>.css',
231
+ dest: 'dist/css/<%= pkg.name %>.min.css'
232
+ },
233
+ minifyTheme: {
234
+ src: 'dist/css/<%= pkg.name %>-theme.css',
235
+ dest: 'dist/css/<%= pkg.name %>-theme.min.css'
236
+ },
237
+ docs: {
238
+ src: [
239
+ 'docs/assets/css/src/docs.css',
240
+ 'docs/assets/css/src/pygments-manni.css'
241
+ ],
242
+ dest: 'docs/assets/css/docs.min.css'
243
+ }
244
+ },
245
+
246
+ usebanner: {
247
+ options: {
248
+ position: 'top',
249
+ banner: '<%= banner %>'
250
+ },
251
+ files: {
252
+ src: 'dist/css/*.css'
253
+ }
254
+ },
255
+
256
+ csscomb: {
257
+ options: {
258
+ config: 'less/.csscomb.json'
259
+ },
260
+ dist: {
261
+ expand: true,
262
+ cwd: 'dist/css/',
263
+ src: ['*.css', '!*.min.css'],
264
+ dest: 'dist/css/'
265
+ },
266
+ examples: {
267
+ expand: true,
268
+ cwd: 'docs/examples/',
269
+ src: '**/*.css',
270
+ dest: 'docs/examples/'
271
+ },
272
+ docs: {
273
+ src: 'docs/assets/css/src/docs.css',
274
+ dest: 'docs/assets/css/src/docs.css'
275
+ }
276
+ },
277
+
278
+ copy: {
279
+ fonts: {
280
+ src: 'fonts/*',
281
+ dest: 'dist/'
282
+ },
283
+ docs: {
284
+ src: 'dist/*/*',
285
+ dest: 'docs/'
286
+ }
287
+ },
288
+
289
+ connect: {
290
+ server: {
291
+ options: {
292
+ port: 3000,
293
+ base: '.'
294
+ }
295
+ }
296
+ },
297
+
298
+ jekyll: {
299
+ docs: {}
300
+ },
301
+
302
+ jade: {
303
+ options: {
304
+ pretty: true,
305
+ data: getLessVarsData
306
+ },
307
+ customizerVars: {
308
+ src: 'docs/_jade/customizer-variables.jade',
309
+ dest: 'docs/_includes/customizer-variables.html'
310
+ },
311
+ customizerNav: {
312
+ src: 'docs/_jade/customizer-nav.jade',
313
+ dest: 'docs/_includes/nav/customize.html'
314
+ }
315
+ },
316
+
317
+ validation: {
318
+ options: {
319
+ charset: 'utf-8',
320
+ doctype: 'HTML5',
321
+ failHard: true,
322
+ reset: true,
323
+ relaxerror: [
324
+ 'Element img is missing required attribute src.',
325
+ 'Attribute autocomplete not allowed on element input at this point.',
326
+ 'Attribute autocomplete not allowed on element button at this point.'
327
+ ]
328
+ },
329
+ files: {
330
+ src: '_gh_pages/**/*.html'
331
+ }
332
+ },
333
+
334
+ watch: {
335
+ src: {
336
+ files: '<%= jshint.core.src %>',
337
+ tasks: ['jshint:src', 'qunit', 'concat']
338
+ },
339
+ test: {
340
+ files: '<%= jshint.test.src %>',
341
+ tasks: ['jshint:test', 'qunit']
342
+ },
343
+ less: {
344
+ files: 'less/**/*.less',
345
+ tasks: 'less'
346
+ }
347
+ },
348
+
349
+ sed: {
350
+ versionNumber: {
351
+ pattern: (function () {
352
+ var old = grunt.option('oldver');
353
+ return old ? RegExp.quote(old) : old;
354
+ })(),
355
+ replacement: grunt.option('newver'),
356
+ recursive: true
357
+ }
358
+ },
359
+
360
+ 'saucelabs-qunit': {
361
+ all: {
362
+ options: {
363
+ build: process.env.TRAVIS_JOB_ID,
364
+ concurrency: 10,
365
+ maxRetries: 3,
366
+ urls: ['http://127.0.0.1:3000/js/tests/index.html'],
367
+ browsers: grunt.file.readYAML('grunt/sauce_browsers.yml')
368
+ }
369
+ }
370
+ },
371
+
372
+ exec: {
373
+ npmUpdate: {
374
+ command: 'npm update'
375
+ }
376
+ }
377
+ });
378
+
379
+
380
+ // These plugins provide necessary tasks.
381
+ require('load-grunt-tasks')(grunt, { scope: 'devDependencies' });
382
+ require('time-grunt')(grunt);
383
+
384
+ // Docs HTML validation task
385
+ grunt.registerTask('validate-html', ['jekyll', 'validation']);
386
+
387
+ var runSubset = function (subset) {
388
+ return !process.env.TWBS_TEST || process.env.TWBS_TEST === subset;
389
+ };
390
+ var isUndefOrNonZero = function (val) {
391
+ return val === undefined || val !== '0';
392
+ };
393
+
394
+ // Test task.
395
+ var testSubtasks = [];
396
+ // Skip core tests if running a different subset of the test suite
397
+ if (runSubset('core')) {
398
+ testSubtasks = testSubtasks.concat(['dist-css', 'dist-js', 'csslint:dist', 'test-js', 'docs']);
399
+ }
400
+ // Skip HTML validation if running a different subset of the test suite
401
+ if (runSubset('validate-html') &&
402
+ // Skip HTML5 validator on Travis when [skip validator] is in the commit message
403
+ isUndefOrNonZero(process.env.TWBS_DO_VALIDATOR)) {
404
+ testSubtasks.push('validate-html');
405
+ }
406
+ // Only run Sauce Labs tests if there's a Sauce access key
407
+ if (typeof process.env.SAUCE_ACCESS_KEY !== 'undefined' &&
408
+ // Skip Sauce if running a different subset of the test suite
409
+ runSubset('sauce-js-unit') &&
410
+ // Skip Sauce on Travis when [skip sauce] is in the commit message
411
+ isUndefOrNonZero(process.env.TWBS_DO_SAUCE)) {
412
+ testSubtasks.push('connect');
413
+ testSubtasks.push('saucelabs-qunit');
414
+ }
415
+ grunt.registerTask('test', testSubtasks);
416
+ grunt.registerTask('test-js', ['jshint:core', 'jshint:test', 'jshint:grunt', 'jscs:core', 'jscs:test', 'jscs:grunt', 'qunit']);
417
+
418
+ // JS distribution task.
419
+ grunt.registerTask('dist-js', ['concat', 'uglify:core', 'commonjs']);
420
+
421
+ // CSS distribution task.
422
+ grunt.registerTask('less-compile', ['less:compileCore', 'less:compileTheme']);
423
+ grunt.registerTask('dist-css', ['less-compile', 'autoprefixer:core', 'autoprefixer:theme', 'usebanner', 'csscomb:dist', 'cssmin:minifyCore', 'cssmin:minifyTheme']);
424
+
425
+ // Full distribution task.
426
+ grunt.registerTask('dist', ['clean:dist', 'dist-css', 'copy:fonts', 'dist-js']);
427
+
428
+ // Default task.
429
+ grunt.registerTask('default', ['clean:dist', 'copy:fonts', 'test']);
430
+
431
+ // Version numbering task.
432
+ // grunt change-version-number --oldver=A.B.C --newver=X.Y.Z
433
+ // This can be overzealous, so its changes should always be manually reviewed!
434
+ grunt.registerTask('change-version-number', 'sed');
435
+
436
+ // task for building customizer
437
+ grunt.registerTask('build-customizer', ['build-customizer-html', 'build-raw-files']);
438
+ grunt.registerTask('build-customizer-html', 'jade');
439
+ grunt.registerTask('build-raw-files', 'Add scripts/less files to customizer.', function () {
440
+ var banner = grunt.template.process('<%= banner %>');
441
+ generateRawFiles(grunt, banner);
442
+ });
443
+
444
+ grunt.registerTask('commonjs', 'Generate CommonJS entrypoint module in dist dir.', function () {
445
+ var srcFiles = grunt.config.get('concat.bootstrap.src');
446
+ var destFilepath = 'dist/js/npm.js';
447
+ generateCommonJSModule(grunt, srcFiles, destFilepath);
448
+ });
449
+
450
+ // Docs task.
451
+ grunt.registerTask('docs-css', ['autoprefixer:docs', 'autoprefixer:examples', 'csscomb:docs', 'csscomb:examples', 'cssmin:docs']);
452
+ grunt.registerTask('lint-docs-css', ['csslint:docs', 'csslint:examples']);
453
+ grunt.registerTask('docs-js', ['uglify:docsJs', 'uglify:customize']);
454
+ grunt.registerTask('lint-docs-js', ['jshint:assets', 'jscs:assets']);
455
+ grunt.registerTask('docs', ['docs-css', 'lint-docs-css', 'docs-js', 'lint-docs-js', 'clean:docs', 'copy:docs', 'build-customizer']);
456
+
457
+ // Task for updating the cached npm packages used by the Travis build (which are controlled by test-infra/npm-shrinkwrap.json).
458
+ // This task should be run and the updated file should be committed whenever Bootstrap's dependencies change.
459
+ grunt.registerTask('update-shrinkwrap', ['exec:npmUpdate', '_update-shrinkwrap']);
460
+ grunt.registerTask('_update-shrinkwrap', function () {
461
+ var done = this.async();
462
+ npmShrinkwrap({ dev: true, dirname: __dirname }, function (err) {
463
+ if (err) {
464
+ grunt.fail.warn(err);
465
+ }
466
+ var dest = 'test-infra/npm-shrinkwrap.json';
467
+ fs.renameSync('npm-shrinkwrap.json', dest);
468
+ grunt.log.writeln('File ' + dest.cyan + ' updated.');
469
+ done();
470
+ });
471
+ });
472
+ };