problem_child 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.bowerrc +3 -0
- data/.gitignore +10 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/Procfile +1 -0
- data/README.md +53 -0
- data/Rakefile +8 -0
- data/bower.json +13 -0
- data/config.ru +3 -0
- data/lib/problem_child.rb +126 -0
- data/lib/problem_child/public/vendor/bootstrap/.bower.json +47 -0
- data/lib/problem_child/public/vendor/bootstrap/Gruntfile.js +472 -0
- data/lib/problem_child/public/vendor/bootstrap/LICENSE +21 -0
- data/lib/problem_child/public/vendor/bootstrap/README.md +129 -0
- data/lib/problem_child/public/vendor/bootstrap/bower.json +38 -0
- data/lib/problem_child/public/vendor/bootstrap/dist/css/bootstrap-theme.css +470 -0
- data/lib/problem_child/public/vendor/bootstrap/dist/css/bootstrap-theme.css.map +1 -0
- data/lib/problem_child/public/vendor/bootstrap/dist/css/bootstrap-theme.min.css +5 -0
- data/lib/problem_child/public/vendor/bootstrap/dist/css/bootstrap.css +6332 -0
- data/lib/problem_child/public/vendor/bootstrap/dist/css/bootstrap.css.map +1 -0
- data/lib/problem_child/public/vendor/bootstrap/dist/css/bootstrap.min.css +5 -0
- data/lib/problem_child/public/vendor/bootstrap/dist/fonts/glyphicons-halflings-regular.eot +0 -0
- data/lib/problem_child/public/vendor/bootstrap/dist/fonts/glyphicons-halflings-regular.svg +229 -0
- data/lib/problem_child/public/vendor/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/lib/problem_child/public/vendor/bootstrap/dist/fonts/glyphicons-halflings-regular.woff +0 -0
- data/lib/problem_child/public/vendor/bootstrap/dist/js/bootstrap.js +2320 -0
- data/lib/problem_child/public/vendor/bootstrap/dist/js/bootstrap.min.js +7 -0
- data/lib/problem_child/public/vendor/bootstrap/dist/js/npm.js +13 -0
- data/lib/problem_child/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.eot +0 -0
- data/lib/problem_child/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.svg +229 -0
- data/lib/problem_child/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/lib/problem_child/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff +0 -0
- data/lib/problem_child/public/vendor/bootstrap/js/.jscsrc +34 -0
- data/lib/problem_child/public/vendor/bootstrap/js/.jshintrc +15 -0
- data/lib/problem_child/public/vendor/bootstrap/js/affix.js +162 -0
- data/lib/problem_child/public/vendor/bootstrap/js/alert.js +94 -0
- data/lib/problem_child/public/vendor/bootstrap/js/button.js +116 -0
- data/lib/problem_child/public/vendor/bootstrap/js/carousel.js +240 -0
- data/lib/problem_child/public/vendor/bootstrap/js/collapse.js +211 -0
- data/lib/problem_child/public/vendor/bootstrap/js/dropdown.js +161 -0
- data/lib/problem_child/public/vendor/bootstrap/js/modal.js +324 -0
- data/lib/problem_child/public/vendor/bootstrap/js/popover.js +119 -0
- data/lib/problem_child/public/vendor/bootstrap/js/scrollspy.js +175 -0
- data/lib/problem_child/public/vendor/bootstrap/js/tab.js +153 -0
- data/lib/problem_child/public/vendor/bootstrap/js/tooltip.js +478 -0
- data/lib/problem_child/public/vendor/bootstrap/js/transition.js +59 -0
- data/lib/problem_child/public/vendor/bootstrap/package.json +82 -0
- data/lib/problem_child/public/vendor/jquery/.bower.json +37 -0
- data/lib/problem_child/public/vendor/jquery/MIT-LICENSE.txt +21 -0
- data/lib/problem_child/public/vendor/jquery/bower.json +27 -0
- data/lib/problem_child/public/vendor/jquery/dist/jquery.js +9205 -0
- data/lib/problem_child/public/vendor/jquery/dist/jquery.min.js +5 -0
- data/lib/problem_child/public/vendor/jquery/dist/jquery.min.map +1 -0
- data/lib/problem_child/version.rb +3 -0
- data/lib/problem_child/views/form.erb +22 -0
- data/lib/problem_child/views/layout.erb +27 -0
- data/problem_child.gemspec +32 -0
- data/script/bootstrap +1 -0
- data/script/console +1 -0
- data/script/release +38 -0
- data/script/server +1 -0
- 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
data/.gitignore
ADDED
data/Gemfile
ADDED
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
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,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
|
+
};
|