├── .babelrc
├── .eslintrc
├── .gitignore
├── .gitmodules
├── LICENSE
├── README.md
├── css
├── application.sass
├── assets
│ ├── .gitkeep
│ └── logo.svg
├── container-html.sass
├── container-index.sass
├── dashboard.sass
├── side.sass
├── theme.sass
└── vendor.sass
├── index.html
├── index.js
├── js
├── component
│ ├── Application.jsx
│ ├── CssContainer.jsx
│ ├── FrameworksContainer.jsx
│ ├── HtmlContainer.jsx
│ ├── IndexContainer.jsx
│ ├── JsContainer.jsx
│ ├── PreviewModal.jsx
│ ├── Side.jsx
│ └── VendorsTab.jsx
├── configurator
│ ├── blobify.js
│ ├── entrify.js
│ ├── framework.js
│ ├── htmlify.js
│ ├── index.js
│ ├── npmdeps.js
│ ├── template.js
│ ├── tpl
│ │ ├── angular1.js
│ │ ├── vendoredcss.js
│ │ └── vendoredjs.js
│ ├── util.js
│ └── webpack.js
├── index.jsx
└── reducer
│ ├── actions.js
│ ├── config.js
│ ├── css.js
│ ├── fw.js
│ ├── html.js
│ ├── index.js
│ ├── initialState.js
│ ├── js.js
│ ├── persist.js
│ └── tabs.js
├── lib
├── FileSaver
│ └── index.js
└── prism
│ ├── prism.css
│ └── prism.js
├── media
└── screen.png
├── package.json
├── webpack.config.bundle.js
├── webpack.config.dist.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets" : ["es2015", "react"]
3 | }
4 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "commonjs": true,
5 | "es6": true
6 | },
7 | "extends": "eslint:recommended",
8 | "parserOptions": {
9 | "ecmaFeatures": {
10 | "experimentalObjectRestSpread": true,
11 | "jsx": true
12 | },
13 | "sourceType": "module"
14 | },
15 | "plugins": [
16 | "react"
17 | ],
18 | "rules": {
19 | "react/jsx-uses-react": 1,
20 | "react/jsx-uses-vars": 1,
21 | "no-console" : 0,
22 | "indent": [
23 | "error",
24 | 2
25 | ],
26 | "linebreak-style": [
27 | "error",
28 | "unix"
29 | ],
30 | "quotes": [
31 | "error",
32 | "single"
33 | ],
34 | "semi": [
35 | "error",
36 | "never"
37 | ]
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | #lib/react-materialize
3 | # Logs
4 | logs
5 | *.log
6 | npm-debug.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 |
13 | # Directory for instrumented libs generated by jscoverage/JSCover
14 | lib-cov
15 |
16 | # Coverage directory used by tools like istanbul
17 | coverage
18 |
19 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
20 | .grunt
21 |
22 | # node-waf configuration
23 | .lock-wscript
24 |
25 | # Compiled binary addons (http://nodejs.org/api/addons.html)
26 | build/Release
27 |
28 | # Dependency directory
29 | node_modules
30 |
31 | # Optional npm cache directory
32 | .npm
33 |
34 | # Optional REPL history
35 | .node_repl_history
36 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "lib/react-materialize"]
2 | path = lib/react-materialize
3 | url = https://github.com/15lyfromsaturn/react-materialize.git
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Svetlana Linuxenko
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Unstuck-Webpack ([Online](http://linuxenko.github.io/unstuck-webpack))
2 |
3 | [](http://linuxenko.github.io/unstuck-webpack)
4 |
5 |
6 | Webpack based projects configurator
7 |
8 | * Predefined templates support for:
9 | * Angular1
10 | * Angular2
11 | * React
12 | * Vuejs
13 | * Nodejs
14 | * HTML preprocessors
15 | * CSS preprocessors and options
16 | * General project build settings
17 | * Many more
18 |
19 |
20 | ### Usage
21 |
22 | [Configure](http://linuxenko.github.io/unstuck-webpack) and save project files (.zip).
23 | Extract them and run `npm install`.
24 |
25 | ### TODO
26 |
27 | - [ ] Versioning support.
28 | - [ ] Notification about linter configuration
29 |
--------------------------------------------------------------------------------
/css/application.sass:
--------------------------------------------------------------------------------
1 |
2 | $sideBg : #6a1b9a
3 | $sideActiveColor: #fff
4 | $activeColor: #6a1b9a
5 | $titleColor: darken(#6a1b9a, 2%)
6 | $inactiveColor: #ddd
7 |
8 | @import './theme.sass'
9 |
10 |
11 | html,body
12 | height: 100%
13 |
14 | .application
15 | height: 100%
16 | background: #fff url('assets/logo.svg') center top no-repeat
17 | background-size: cover
18 |
19 | .uw_application
20 | display: flex
21 | height: 100%
22 |
23 | &--version
24 | position: fixed
25 | top: 5px
26 | right: 5px
27 | font-size: .7rem
28 | color: $sideBg
29 |
30 | &--container
31 | margin: auto
32 | box-shadow: 0px 2px 5px #444
33 | background-color: #fff
34 | &--subcontainer
35 | padding: 20px 20px 0px 20px
36 | .page-title
37 | font-weight: bold
38 | font-size: 22px
39 | text-align: right
40 | padding-bottom: 10px
41 | color: $titleColor
42 |
43 | .tab-content
44 | margin-top: 30px
45 |
46 |
47 | .mb-10
48 | margin-bottom: 10px
49 |
50 | @import './side.sass'
51 | @import './container-index.sass'
52 | @import './container-html.sass'
53 | @import './dashboard.sass'
54 |
--------------------------------------------------------------------------------
/css/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linuxenko/unstuck-webpack/1aa09b20edc400d470ac04ddb1b6467e7e9e21d6/css/assets/.gitkeep
--------------------------------------------------------------------------------
/css/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
134 |
--------------------------------------------------------------------------------
/css/container-html.sass:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linuxenko/unstuck-webpack/1aa09b20edc400d470ac04ddb1b6467e7e9e21d6/css/container-html.sass
--------------------------------------------------------------------------------
/css/container-index.sass:
--------------------------------------------------------------------------------
1 | .uw_application--index
2 |
--------------------------------------------------------------------------------
/css/dashboard.sass:
--------------------------------------------------------------------------------
1 | .uw_application--dashboard
2 | .template-card
3 | background: $sideActiveColor
4 | box-shadow: 0px 1px 1px #ddd
5 | display: block
6 |
7 | input:checked
8 | & + label:before, & + label:after
9 | border: none
10 | input + label
11 | margin-top: 18px
12 | &:after, &:before
13 | border: 1px solid $sideBg
14 | width: 22px
15 | height: 22px
16 | background-color: $sideActiveColor
17 |
18 | .col + label
19 | font-size: 26px
20 | cursor: pointer
21 | display: block
22 | padding: 14px
23 | color: $sideBg
24 | i
25 | margin-left: 5px
26 | &.active
27 | background: lighten($sideBg, 10%)
28 | .col + label
29 | color: $sideActiveColor
30 |
--------------------------------------------------------------------------------
/css/side.sass:
--------------------------------------------------------------------------------
1 |
2 | .uw_application
3 | &--container
4 | & > .row, & > .row > .col
5 | margin: 0px
6 | padding: 0px
7 |
8 |
9 | &--side
10 | height: 100%
11 | background: radial-gradient($sideBg, darken($sideBg, 5%)) !important
12 | box-shadow: 0px 1px 1px #444
13 | .logo
14 | text-align: left
15 | color : $sideActiveColor
16 | font-size: 22px
17 | font-weight: bold
18 | padding: 20px 0px 30px 20px
19 | .collection
20 | border: none
21 | padding: 0px
22 | margin: 0px
23 | li.collection-item
24 | border: none
25 | background: transparent !important
26 | padding: 0px
27 | cursor: default
28 | & > a
29 | overflow: hidden
30 | height: 114px
31 | display: block
32 | padding: 20px
33 | &.disabled
34 | cursor: default
35 | .control
36 | div
37 | color: darken($sideActiveColor, 50%)
38 | border-bottom: 1px solid #888
39 | span
40 | font-size: 22px
41 |
42 | i
43 | font-size: 22px
44 | margin-right: 5px
45 | .description
46 | padding-top: 5px
47 | color: darken($sideActiveColor, 50%)
48 | .enabled
49 | display: block
50 | .control
51 | div
52 | font-weight: bold
53 | color: $sideActiveColor
54 |
55 | .description
56 | color: darken($sideActiveColor, 5%)
57 | li.collection-item.active
58 | background: darken($sideBg, 10%) !important
59 | .control
60 | border-bottom: 1px dashed $sideActiveColor
61 |
--------------------------------------------------------------------------------
/css/theme.sass:
--------------------------------------------------------------------------------
1 | [type="radio"]:checked + label:after, [type="radio"].with-gap:checked + label:before, [type="radio"].with-gap:checked + label:after
2 | border: 2px solid $activeColor
3 |
4 | [type="radio"]:checked + label:after, [type="radio"].with-gap:checked + label:after
5 | background-color: lighten($activeColor, 5%)
6 |
7 | ul.tabs
8 | overflow: hidden
9 | .indicator
10 | background-color: $activeColor
11 | .tab
12 | a
13 | color: $activeColor
14 | &:hover
15 | color: lighten($activeColor, 10%)
16 | &.disabled a
17 | color: $inactiveColor
18 |
19 |
20 | .switch label input[type=checkbox]:checked + .lever
21 | background-color: lighten($activeColor, 40%)
22 | &:after
23 | background-color: $activeColor
24 |
25 | .uw_application--side
26 | .switch label input[type=checkbox]:checked + .lever
27 | background-color: lighten($sideActiveColor, 40%)
28 | &:after
29 | background-color: $sideActiveColor
30 |
31 | [type="checkbox"]:checked + label:before
32 | border-right-color: $activeColor
33 | border-bottom-color: $activeColor
34 |
35 | input:not([type]), input[type=text], input[type=password], input[type=email], input[type=url], input[type=time], input[type=date], input[type=datetime], input[type=datetime-local], input[type=tel], input[type=number], input[type=search], textarea.materialize-textarea
36 | border-bottom-color: $activeColor
37 |
38 | .input-field input[type=text]:focus
39 | border-bottom: 1px solid $activeColor
40 | box-shadow: 0 1px 0 0 $activeColor
41 |
42 | .input-field input[type=text]:focus + label
43 | color: $activeColor
44 |
45 | .select-wrapper
46 | .caret
47 | color: $activeColor !important
48 | input.select-dropdown
49 | border-bottom-color: $activeColor
50 |
51 | .dropdown-content li > a, .dropdown-content li
52 | background-color: $sideActiveColor
53 | & > span
54 | color: $sideBg
55 |
56 | .dropdown-content li:hover
57 | background-color: $sideBg
58 | span
59 | color: $sideActiveColor
60 |
61 | .btn:hover, .btn-large:hover
62 | background-color: $activeColor
63 | color: $sideActiveColor
64 |
65 | pre[class*="language-"]
66 | border-radius: 0px
67 | border: none
68 | box-shadow: none
69 |
--------------------------------------------------------------------------------
/css/vendor.sass:
--------------------------------------------------------------------------------
1 | @import "~font-awesome/css/font-awesome.css"
2 | @import "~devicons/css/devicons.css"
3 | @import "~materialize-css/dist/css/materialize.css"
4 | @import "~prism/prism.css"
5 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Unstuck Webpack
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import './index.html'
2 | import './css/vendor.sass'
3 | import './css/application.sass'
4 |
5 | window.$ = window.jQuery = require('jquery')
6 | window.$.velocity = require('velocity-animate/velocity.js')
7 |
8 | import 'materialize-css/dist/js/materialize.js'
9 | import 'prism/prism.js'
10 | import './js/index.jsx'
11 |
--------------------------------------------------------------------------------
/js/component/Application.jsx:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import {connect} from 'react-redux'
3 | import {Row, Col, Button, Modal} from 'react-materialize'
4 |
5 | import PreviewModal from 'component/PreviewModal.jsx'
6 | import Side from 'component/Side.jsx'
7 |
8 | import store from 'reducer/index'
9 |
10 | import {BlobifyConfigurator} from 'configurator'
11 |
12 | import PackageJSON from 'json!../../package.json'
13 |
14 |
15 | class Application extends Component {
16 | zipConfig() {
17 | BlobifyConfigurator(this.props.state)
18 | }
19 |
20 | render() {
21 | return (
22 |
23 |
24 |
25 |
26 |
27 | {this.props.children}
28 |
29 |
39 |
40 |
41 |
v{PackageJSON.version}
42 |
43 | )
44 | }
45 | }
46 |
47 | export default connect(state => ({state}))(Application)
48 |
--------------------------------------------------------------------------------
/js/component/CssContainer.jsx:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import {Tabs,Tab,Row, Col, Input} from 'react-materialize'
3 | import {connect} from 'react-redux'
4 | import Actions from 'reducer/actions'
5 |
6 | class CssContainer extends Component {
7 |
8 |
9 | selectTranspiller(transpiller, e) {
10 | this.props.dispatch(Actions.CSS_SELECT_TRANSPILLER(transpiller, e.target.checked))
11 | }
12 |
13 | toggleAutoprefix(e) {
14 | this.props.dispatch(Actions.CSS_TOGGLE_AUTOPREFIX(e.target.checked))
15 | }
16 |
17 | tabSelect(tab) {
18 | this.props.dispatch(Actions.TAB_DEFAULT('css', tab))
19 | }
20 |
21 | render() {
22 | return (
23 |
68 | )
69 | }
70 | }
71 |
72 | export default connect(state => ({state}))(CssContainer)
73 |
--------------------------------------------------------------------------------
/js/component/FrameworksContainer.jsx:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import {connect} from 'react-redux'
3 | import {Row, Col, Input, Tabs, Tab} from 'react-materialize'
4 | import Actions from 'reducer/actions'
5 |
6 | class FrameworksContainer extends Component {
7 |
8 | toggleFramework(fw, e) {
9 | if (e.target.checked === true) {
10 | //this.props.dispatch(Actions.FW_ADD_FRAMEWORK(fw))
11 | this.props.dispatch(Actions.ADD_FW(fw))
12 | } else {
13 | this.props.dispatch(Actions.FW_REMOVE_FRAMEWORK(fw))
14 | this.props.dispatch(Actions.FW_REMOVE_FRAMEWORK(fw + '-js'))
15 | }
16 | }
17 |
18 | isChecked(fw) {
19 | return this.props.state.fw.indexOf(fw) !== -1 ? 'checked' : null
20 | }
21 |
22 | toggleFwOption(fw, e) {
23 | if (fw !== e.target.value) {
24 | this.props.dispatch(Actions.FW_REMOVE_FRAMEWORK(fw))
25 | } else {
26 | //this.props.dispatch(Actions.FW_ADD_FRAMEWORK(fw))
27 | this.props.dispatch(Actions.ADD_FW(fw))
28 | }
29 | }
30 |
31 | tabSelect(tab) {
32 | this.props.dispatch(Actions.TAB_DEFAULT('fw', tab))
33 | }
34 |
35 | render() {
36 | return (
37 |
159 | )
160 | }
161 | }
162 |
163 | export default connect(state => ({state}))(FrameworksContainer)
164 |
--------------------------------------------------------------------------------
/js/component/HtmlContainer.jsx:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import {Row, Col, Input, Tabs, Tab} from 'react-materialize'
3 |
4 | import {connect} from 'react-redux'
5 | import Actions from 'reducer/actions'
6 |
7 |
8 | class HtmlContainer extends Component {
9 | toggleTemplate(type, e) {
10 | this.props.dispatch(Actions.TOGGLE_HTML_TEMPLATE(type, e.target.checked))
11 | }
12 |
13 | setTarget(type, e) {
14 | this.props.dispatch(Actions.SET_HTML_TEMPLATE_TARGET(type, e.target.value))
15 | }
16 |
17 | render() {
18 | let enabledItems = {
19 | html : this.props.state.html.templates.html.enabled,
20 | jade : this.props.state.html.templates.jade.enabled,
21 | markdown : this.props.state.html.templates.markdown.enabled,
22 | handlebars : this.props.state.html.templates.handlebars.enabled
23 | }
24 |
25 | return (
26 |
85 | )
86 | }
87 | }
88 |
89 |
90 | export default connect(state => ({state}))(HtmlContainer)
91 |
--------------------------------------------------------------------------------
/js/component/IndexContainer.jsx:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import {connect} from 'react-redux'
3 | import {Tabs, Tab, Row, Col, Input} from 'react-materialize'
4 | import VendorsTab from 'component/VendorsTab.jsx'
5 | import Actions from 'reducer/actions'
6 |
7 | class IndexContainer extends Component {
8 | tabSelect(tab) {
9 | this.props.dispatch(Actions.TAB_DEFAULT('dd', tab))
10 | }
11 |
12 | selectTemplate(template) {
13 | this.props.dispatch(Actions.SET_TEMPLATE(template))
14 | }
15 |
16 | setTarget(plugin, option, e) {
17 | this.props.dispatch(Actions.CONFIG_GENERAL_TARGET(plugin, option, e.target.value))
18 | }
19 |
20 | togglePlugin(plugin, e) {
21 | this.props.dispatch(Actions.CONFIG_TOGGLE_PLUGIN(plugin, e.target.checked))
22 | }
23 |
24 | render() {
25 | return (
26 |
161 | )
162 | }
163 | }
164 |
165 | export default connect(state => ({state}))(IndexContainer)
166 |
--------------------------------------------------------------------------------
/js/component/JsContainer.jsx:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import {connect} from 'react-redux'
3 | import {Tabs, Tab, Row, Col, Input} from 'react-materialize'
4 | import Actions from 'reducer/actions'
5 |
6 | class JsContainer extends Component {
7 |
8 | tabSelect(tab) {
9 | this.props.dispatch(Actions.TAB_DEFAULT('js', tab))
10 | }
11 |
12 | selectTranspiller(transpiller, e) {
13 | this.props.dispatch(Actions.JS_SELECT_TRANSPILLER(transpiller, e.target.checked))
14 | }
15 |
16 | toggleLinter(e) {
17 | this.props.dispatch(Actions.JS_TOGGLE_LINTER(e.target.checked))
18 | }
19 |
20 | selectSourceMap(sourceMap) {
21 | this.props.dispatch(Actions.JS_SELECT_SOURCEMAP(sourceMap))
22 | }
23 |
24 | render() {
25 | return (
26 |
109 | )
110 | }
111 | }
112 |
113 | export default connect(state => ({state}))(JsContainer)
114 |
--------------------------------------------------------------------------------
/js/component/PreviewModal.jsx:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import {connect} from 'react-redux'
3 |
4 | import Prism from 'prism/prism'
5 | import $ from 'jquery'
6 |
7 | import {NpmConfigurator, WebpackConfigurator,
8 | EntrifyConfigurator, HtmlifyConfigurator} from 'configurator'
9 | import {TS} from 'configurator/util'
10 |
11 | class PreviewModal extends Component {
12 | componentDidMount() {
13 | setTimeout(() => {
14 | Prism.highlightAll($('.prism'))
15 | }, 5)
16 | }
17 |
18 | render() {
19 | return (
20 |
21 |
Files containing project settings.
22 |
index.js
23 |
24 |
25 | {EntrifyConfigurator(this.props.state).idx}
26 |
27 |
28 | {this.props.state.css.enabled === true ?
29 |
30 |
{this.props.state.config.cssdir}/vendors.{TS(this.props.state.css.transpiller)}
31 |
32 |
33 | {EntrifyConfigurator(this.props.state).vcss}
34 |
35 |
36 |
37 | : '' }
38 | {this.props.state.config.html.enabled === true ?
39 |
40 |
index.html
41 |
42 |
43 | {HtmlifyConfigurator(this.props.state)}
44 |
45 |
46 |
47 | : '' }
48 |
package.json
49 |
50 |
51 | {JSON.stringify(NpmConfigurator(this.props.state), null, 2)}
52 |
53 |
54 |
webpack.config.js
55 |
56 |
57 | {WebpackConfigurator(this.props.state)}
58 |
59 |
60 |
61 | )
62 | }
63 | }
64 |
65 | export default connect(state => ({state}))(PreviewModal)
66 |
--------------------------------------------------------------------------------
/js/component/Side.jsx:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import {connect} from 'react-redux'
3 | import {hashHistory} from 'react-router'
4 | import {Collection, CollectionItem, Input, Row, Col} from 'react-materialize'
5 |
6 | import Actions from 'reducer/actions'
7 |
8 | class Side extends Component {
9 |
10 | switchChange(type,e) {
11 | switch(type) {
12 | case 'html':
13 | this.props.dispatch(Actions.TOGGLE_HTML(e.target.checked))
14 | break
15 | case 'js':
16 | this.props.dispatch(Actions.TOGGLE_JS(e.target.checked))
17 | break
18 | case 'css':
19 | this.props.dispatch(Actions.TOGGLE_CSS(e.target.checked))
20 | break
21 | }
22 | }
23 |
24 | fitSidebar() {
25 | setTimeout(() => {
26 | try {
27 | let height = document.getElementById('uw-application-container').offsetHeight
28 | this.containerEl.style.minHeight = height + 'px'
29 | } catch(e) { console.log(e)}
30 | }, 5)
31 | }
32 |
33 | componentDidUpdate() {
34 | this.fitSidebar()
35 | }
36 |
37 | componentDidMount() {
38 | this.fitSidebar()
39 | }
40 |
41 | render() {
42 | let enabledItems = {
43 | html : this.props.state.html.enabled,
44 | js : this.props.state.js.enabled,
45 | css : this.props.state.css.enabled
46 | }
47 |
48 | let activeItem = this.props.route.replace(/^\//,'')
49 |
50 | if (enabledItems.hasOwnProperty(activeItem) && enabledItems[activeItem] === false) {
51 | setTimeout( () => hashHistory.push('/'), 1)
52 | }
53 |
54 | return (
55 |
158 | )
159 | }
160 | }
161 |
162 | export default connect(state => ({state}))(Side)
163 |
--------------------------------------------------------------------------------
/js/component/VendorsTab.jsx:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import {connect} from 'react-redux'
3 | import Actions from 'reducer/actions'
4 | import {Row, Col, Input} from 'react-materialize'
5 |
6 | class VendorsTab extends Component {
7 | cssVendor() {
8 | if (this.props.state.css.enabled === false) {
9 | return []
10 | }
11 | let trp = this.props.state.css.transpiller
12 |
13 | if (trp.less === true) {
14 | return ['vendors.less']
15 | }
16 |
17 | if (trp.sass === true) {
18 | return ['vendors.sass']
19 | }
20 |
21 | if (trp.styl) {
22 | return ['vendors.styl']
23 | }
24 |
25 | return []
26 | }
27 |
28 | jsVendor() {
29 | if (this.props.state.js.enabled === false) {
30 | return []
31 | }
32 |
33 | let trp = this.props.state.js.transpiller
34 |
35 | if (trp.react === true) {
36 | return ['react', 'react-dom']
37 | }
38 |
39 | return []
40 | }
41 |
42 | isChecked(vendor) {
43 | return this.props.state.config.vendors.indexOf(vendor) !== -1 ? 'checked' : null
44 | }
45 |
46 | toggleVendor(vendor, e) {
47 | this.props.dispatch(Actions.TOGGLE_VENDOR(vendor, e.target.checked))
48 | }
49 |
50 | render() {
51 | let vendours = [...this.cssVendor.call(this),...this.jsVendor.call(this)].map(v => {
52 | return (
53 |
54 |
59 |
60 | )
61 | })
62 |
63 | return (
64 |
65 | {vendours}
66 |
67 | )
68 | }
69 | }
70 |
71 |
72 | export default connect(state => ({state}))(VendorsTab)
73 |
--------------------------------------------------------------------------------
/js/configurator/blobify.js:
--------------------------------------------------------------------------------
1 | import JSZIP from 'jszip'
2 | import {saveAs} from 'FileSaver/index'
3 | import {NpmConfigurator, WebpackConfigurator,
4 | EntrifyConfigurator, HtmlifyConfigurator} from 'configurator'
5 | import {TS} from './util'
6 |
7 |
8 | export default function BlobifyConfigurator(state) {
9 | let zip = new JSZIP()
10 | let entries = EntrifyConfigurator(state)
11 | let html = HtmlifyConfigurator(state)
12 |
13 | zip.file('package.json', JSON.stringify(NpmConfigurator(state), null, 2))
14 | zip.file('webpack.config.js', WebpackConfigurator(state))
15 |
16 | if (state.js.enabled === true) {
17 | let babelrc = {
18 | presets: []
19 | }
20 | if (state.js.transpiller.babel === true && state.js.transpiller.react === false) {
21 | babelrc.presets = ['es2015']
22 | }
23 | if (state.js.transpiller.react === true) {
24 | babelrc.presets = ['es2015', 'react']
25 | }
26 |
27 | zip.file('.babelrc', JSON.stringify(babelrc, null, 2))
28 | }
29 |
30 | if (state.config.html.enabled === true) {
31 | zip.file('index.html', html)
32 | }
33 |
34 | /* EntryPoint */
35 | if (entries.idx !== null) {
36 | zip.file('index.js', entries.idx)
37 | }
38 |
39 | /* Application example */
40 | let appFolder = zip
41 | let cssFolder = zip
42 | if (state.config.jsdir.length > 0 && state.config.jsdir !== '/') {
43 | appFolder = zip.folder(state.config.jsdir)
44 | }
45 |
46 | if (state.config.cssdir.length > 0 && state.config.cssdir !== '/') {
47 | cssFolder = zip.folder(state.config.cssdir)
48 | }
49 |
50 | if (entries.vcss !== null) {
51 | cssFolder.file('vendors.' + TS(state.css.transpiller), entries.vcss)
52 | }
53 |
54 | if (entries.css !== null) {
55 | cssFolder.file('application.' + TS(state.css.transpiller), '/* generated by unstuck-webpack */')
56 | }
57 |
58 | if (entries.app !== null) {
59 | appFolder.file('index.js', entries.app)
60 | }
61 |
62 | zip.generateAsync({type:'blob'}).then(function(content) {
63 | saveAs(content, 'config.zip')
64 | })
65 | }
66 |
--------------------------------------------------------------------------------
/js/configurator/entrify.js:
--------------------------------------------------------------------------------
1 | import VendoredJS from './tpl/vendoredjs'
2 | import VendoredCSS from './tpl/vendoredcss'
3 | import Angular1 from './tpl/angular1'
4 |
5 |
6 | const nodeApp = `
7 | console.log(\`hello world from \${__dirname}\`)
8 | `
9 |
10 | export default function EntrifyConfigurator(state) {
11 | let entries = { app : null,
12 | idx : VendoredJS(state),
13 | vcss: state.css.enabled === true ? VendoredCSS(state) : null ,
14 | css : ''}
15 |
16 | if (state.config.template === 'node') {
17 | entries.app = nodeApp
18 | return entries
19 | }
20 |
21 | if (state.config.template === 'angular1') {
22 | entries.app = Angular1(state)
23 | return entries
24 | }
25 |
26 |
27 |
28 | return entries
29 | }
30 |
--------------------------------------------------------------------------------
/js/configurator/framework.js:
--------------------------------------------------------------------------------
1 | import Actions from 'reducer/actions'
2 | import store from 'reducer'
3 |
4 | const hasFw = fw => store.getState().fw.indexOf(fw) !== -1
5 |
6 | export default function FrameworkConfigurator(dispatch, fw) {
7 |
8 | if (hasFw('bootstrap') && hasFw('bootstrap-js')) {
9 | dispatch(Actions.FW_ADD_FRAMEWORK('jquery'))
10 | }
11 |
12 | if (hasFw('materialize') && hasFw('materialize-js')) {
13 | dispatch(Actions.FW_ADD_FRAMEWORK('jquery'))
14 | }
15 |
16 | if (hasFw('foundation') && hasFw('foundation-js')) {
17 | dispatch(Actions.FW_ADD_FRAMEWORK('jquery'))
18 | }
19 |
20 | dispatch(Actions.FW_ADD_FRAMEWORK(fw))
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/js/configurator/htmlify.js:
--------------------------------------------------------------------------------
1 | const template = function(options) {
2 | return `
3 |
4 |
5 | ${options.isCssVendor ?
6 | '' : ''}
7 |
8 |
9 |
10 |
11 | ${options.isJsVendor ?
12 | '' : '' }
13 |
14 |
15 | `
16 | }
17 |
18 | export default function HtmlifyConfigurator(state) {
19 | let options = {
20 | isJsVendor : state.config.chunks.enabled,
21 | isCssVendor : state.config.extract.enabled
22 | }
23 |
24 |
25 | return template(options)
26 | }
27 |
--------------------------------------------------------------------------------
/js/configurator/index.js:
--------------------------------------------------------------------------------
1 | export const TemplateConfigurator = require('configurator/template').default
2 | export const FrameworkConfigurator = require('configurator/framework').default
3 | export const NpmConfigurator = require('configurator/npmdeps').default
4 | export const WebpackConfigurator = require('configurator/webpack').default
5 | export const BlobifyConfigurator = require('configurator/blobify').default
6 | export const EntrifyConfigurator = require('configurator/entrify').default
7 | export const HtmlifyConfigurator = require('configurator/htmlify').default
8 |
--------------------------------------------------------------------------------
/js/configurator/npmdeps.js:
--------------------------------------------------------------------------------
1 | let Conf = {
2 | devDependencies : {
3 | webpack : 'latest'
4 | }
5 | }
6 |
7 | let hasDep = (conf, dep) => conf.devDependencies.hasOwnProperty(dep)
8 |
9 | let insertDep = (conf, dep) => {
10 | if (hasDep(conf, dep)) {
11 | return conf
12 | }
13 | conf.devDependencies[dep] = 'latest'
14 | return conf
15 | }
16 |
17 | let frameworksMap = function(fw) {
18 | switch(fw) {
19 | case 'angular1':
20 | return 'angular'
21 | case 'bootstrap-js':
22 | return 'bootstrap'
23 | case 'materialize':
24 | case 'materialize-js':
25 | return 'materialize-css'
26 | case 'foundation':
27 | case 'foundation-js':
28 | return 'foundation-sites'
29 | }
30 | return fw
31 | }
32 |
33 | export default function(state) {
34 | let conf = {devDependencies : Object.assign({}, Conf.devDependencies)}
35 |
36 | /* state.config dependencies */
37 |
38 | if (state.config.extract.enabled === true) {
39 | conf = insertDep(conf, 'extract-text-webpack-plugin')
40 | }
41 |
42 | if (state.config.assets.enabled === true) {
43 | conf = insertDep(conf, 'url-loader')
44 | conf = insertDep(conf, 'file-loader')
45 | conf = insertDep(conf, 'resolve-url')
46 | }
47 |
48 | /* state.js dependencies */
49 | if (state.js.enabled === true) {
50 | if (state.js.linter === true) {
51 | conf = insertDep(conf, 'eslint')
52 | conf = insertDep(conf, 'eslint-loader')
53 |
54 | if (state.js.transpiller.react === true) {
55 | conf = insertDep(conf, 'eslint-plugin-react')
56 | }
57 | }
58 |
59 | if (state.js.transpiller.babel === true || state.js.transpiller.react === true || state.js.transpiller.vue === true) {
60 | conf = insertDep(conf, 'babel')
61 | conf = insertDep(conf, 'babel-loader')
62 | conf = insertDep(conf, 'babel-preset-es2015')
63 | if (state.js.transpiller.react === true) {
64 | conf = insertDep(conf, 'babel-preset-react')
65 | conf = insertDep(conf, 'react')
66 | conf = insertDep(conf, 'react-dom')
67 | }
68 |
69 | if (state.js.transpiller.vue === true) {
70 | conf = insertDep(conf, 'vue')
71 | conf = insertDep(conf, 'vue-loader')
72 | conf = insertDep(conf, 'vue-style-loader')
73 | conf = insertDep(conf, 'vue-html-loader')
74 | }
75 | }
76 | }
77 |
78 | /* state.fw dependencies */
79 | state.fw.forEach((fw) => {
80 | conf = insertDep(conf, frameworksMap(fw))
81 | })
82 |
83 | /* state.html dependencies */
84 | if (state.html.enabled === true) {
85 | conf = insertDep(conf, 'url-loader')
86 | conf = insertDep(conf, 'file-loader')
87 | conf = insertDep(conf, 'resolve-url')
88 |
89 | if (state.html.templates.jade.enabled === true) {
90 | conf = insertDep(conf, 'jade-loader')
91 | }
92 |
93 | if (state.html.templates.markdown.enabled === true) {
94 | conf = insertDep(conf, 'markdown-loader')
95 | }
96 |
97 | if (state.html.templates.handlebars.enabled === true) {
98 | conf = insertDep(conf, 'handlebars-loader')
99 | }
100 | }
101 |
102 | /* state.css dependencies */
103 | if (state.css.enabled === true) {
104 | conf = insertDep(conf, 'css-loader')
105 | conf = insertDep(conf, 'style-loader')
106 |
107 | if (state.css.autoprefix === true) {
108 | conf = insertDep(conf, 'precss')
109 | conf = insertDep(conf, 'postcss-loader')
110 | conf = insertDep(conf, 'autoprefixer')
111 | }
112 |
113 | if (state.css.transpiller.less === true) {
114 | conf = insertDep(conf, 'less')
115 | conf = insertDep(conf, 'less-loader')
116 | }
117 |
118 | if (state.css.transpiller.sass === true) {
119 | conf = insertDep(conf, 'node-sass')
120 | conf = insertDep(conf, 'sass-loader')
121 | }
122 |
123 | if (state.css.transpiller.styl === true) {
124 | conf = insertDep(conf, 'stylus')
125 | conf = insertDep(conf, 'stylus-loader')
126 | }
127 | }
128 |
129 | return conf
130 | }
131 |
--------------------------------------------------------------------------------
/js/configurator/template.js:
--------------------------------------------------------------------------------
1 | import Actions from 'reducer/actions'
2 |
3 | export default function TemplateConfigurator(dispatch, template) {
4 | dispatch(Actions.RESET())
5 | dispatch(Actions.CONFIG_TEMPLATE(template))
6 |
7 | if (template === 'node') {
8 | dispatch(Actions.TOGGLE_JS(true))
9 | dispatch(Actions.JS_SELECT_TRANSPILLER('babel', true))
10 | dispatch(Actions.CONFIG_TOGGLE_PLUGIN('html', false))
11 | dispatch(Actions.CONFIG_TOGGLE_PLUGIN('chunks', false))
12 | dispatch(Actions.CONFIG_TOGGLE_PLUGIN('extract', false))
13 | dispatch(Actions.CONFIG_TOGGLE_PLUGIN('assets', false))
14 | }
15 |
16 | if (template === 'angular1') {
17 | dispatch(Actions.TOGGLE_JS(true))
18 | dispatch(Actions.TOGGLE_CSS(true))
19 | dispatch(Actions.JS_SELECT_TRANSPILLER('babel', true))
20 | dispatch(Actions.CSS_SELECT_TRANSPILLER('less', true))
21 | dispatch(Actions.FW_ADD_FRAMEWORK('jquery'))
22 | dispatch(Actions.FW_ADD_FRAMEWORK('angular1'))
23 | }
24 |
25 | if (template === 'angular2') {
26 | dispatch(Actions.TOGGLE_JS(true))
27 | dispatch(Actions.JS_SELECT_TRANSPILLER('babel', true))
28 | dispatch(Actions.TOGGLE_CSS(true))
29 | dispatch(Actions.CSS_SELECT_TRANSPILLER('sass', true))
30 | }
31 |
32 | if (template === 'react') {
33 | dispatch(Actions.TOGGLE_JS(true))
34 | dispatch(Actions.JS_SELECT_TRANSPILLER('react', true))
35 | dispatch(Actions.TOGGLE_CSS(true))
36 | dispatch(Actions.CSS_SELECT_TRANSPILLER('sass', true))
37 | }
38 |
39 | if (template === 'ember') {
40 | dispatch(Actions.TOGGLE_JS(true))
41 | dispatch(Actions.JS_SELECT_TRANSPILLER('babel', true))
42 | dispatch(Actions.TOGGLE_CSS(true))
43 | dispatch(Actions.CSS_SELECT_TRANSPILLER('sass', true))
44 | dispatch(Actions.FW_ADD_FRAMEWORK('jquery'))
45 | dispatch(Actions.FW_ADD_FRAMEWORK('ember'))
46 | dispatch(Actions.FW_ADD_FRAMEWORK('ember-data'))
47 | }
48 |
49 | if (template === 'vue') {
50 | dispatch(Actions.TOGGLE_JS(true))
51 | dispatch(Actions.JS_SELECT_TRANSPILLER('vue', true))
52 | dispatch(Actions.TOGGLE_CSS(true))
53 | dispatch(Actions.CSS_SELECT_TRANSPILLER('styl', true))
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/js/configurator/tpl/angular1.js:
--------------------------------------------------------------------------------
1 |
2 | export default function Angular1() {
3 |
4 | return ''
5 | }
6 |
--------------------------------------------------------------------------------
/js/configurator/tpl/vendoredcss.js:
--------------------------------------------------------------------------------
1 | const hasFw = (state, fw) => state.fw.indexOf(fw) !== -1
2 |
3 | export default function VendoredCSS(state) {
4 | let out = ''
5 |
6 | if (hasFw(state, 'bootstrap')) {
7 | out += '\n@import "~bootstrap/dist/css/bootstrap.css";'
8 | }
9 |
10 | if (hasFw(state, 'materialize')) {
11 | out += '\n@import "~materialize-css/dist/css/materialize.css";'
12 | }
13 |
14 | if (hasFw(state, 'foundation')) {
15 | out += '\n@import "~foundation-sites/dist/foundation.css";'
16 | }
17 |
18 | if (hasFw(state, 'font-awesome')) {
19 | out += '\n@import "~font-awesome/css/font-awesome.css";'
20 | }
21 |
22 | if (hasFw(state, 'normalize')) {
23 | out += '\n@import "~normalize-css/normalize.css";'
24 | }
25 |
26 | if (hasFw(state, 'animate.css')) {
27 | out += '\n@import "~animate.css/animate.css";'
28 | }
29 |
30 | if (hasFw(state, 'github-markdown-css')) {
31 | out += '\n@import "~github-markdown-css/github-markdown.css";'
32 | }
33 |
34 | if (state.css.transpiller.less === true) {
35 | return out.replace(/^\n/,'')
36 | }
37 |
38 | return out.replace(/\;/gi, '').replace(/^\n/,'')
39 | }
40 |
--------------------------------------------------------------------------------
/js/configurator/tpl/vendoredjs.js:
--------------------------------------------------------------------------------
1 | const hasFw = (state, fw) => state.fw.indexOf(fw) !== -1
2 |
3 | let cssTranspiller = ts => {
4 | if (ts.less === true) {
5 | return 'less'
6 | }
7 | if (ts.sass === true) {
8 | return 'sass'
9 | }
10 | if (ts.styl === true) {
11 | return 'styl'
12 | }
13 | return 'css'
14 | }
15 |
16 | export default function VendoredJS(state) {
17 | let out = ''
18 |
19 | if (state.css.enabled === true && state.config.cssdir.length > 0 && state.config.cssdir !== '/') {
20 | out += `\nimport \'./${state.config.cssdir}/vendors.${cssTranspiller(state.css.transpiller)}\'`
21 | out += `\nimport \'./${state.config.cssdir}/application.${cssTranspiller(state.css.transpiller)}\'`
22 | }
23 |
24 | if (hasFw(state, 'jquery')) {
25 | out += '\nwindow.$ = window.jQuery = require(\'jquery\')'
26 | }
27 |
28 | if (hasFw(state, 'materialize') && hasFw(state, 'materialize-js')) {
29 | out += '\nimport \'materialize-css/dist/js/materialize.js\''
30 | }
31 |
32 | if (hasFw(state, 'bootstrap') && hasFw(state, 'bootstrap-js')) {
33 | out += '\nimport \'bootstrap/dist/js/bootstrap.js\''
34 | }
35 |
36 | if (hasFw(state, 'foundation') && hasFw(state, 'foundation-js')) {
37 | out += '\nimport \'foundation-sites/dist/foundation.js\''
38 | }
39 |
40 | if (state.config.html.enabled === true) {
41 | out += '\nimport \'./index.html\''
42 | }
43 |
44 | out += `\nimport \'./${state.config.jsdir}/index.js\'`
45 |
46 | return out.replace(/^\n/,'')
47 | }
48 |
--------------------------------------------------------------------------------
/js/configurator/util.js:
--------------------------------------------------------------------------------
1 | export const TS = function(ts) {
2 | if (ts.less === true) {
3 | return 'less'
4 | }
5 | if (ts.sass === true) {
6 | return 'sass'
7 | }
8 | if (ts.styl === true) {
9 | return 'styl'
10 | }
11 | return 'css'
12 | }
13 |
14 |
15 | export default null
16 |
--------------------------------------------------------------------------------
/js/configurator/webpack.js:
--------------------------------------------------------------------------------
1 | let configWeb = function(names) {
2 | return `
3 | /* global __dirname */
4 | /* generated by unstuck-webpack */
5 |
6 | var path = require('path');
7 | var webpack = require('webpack');${names.plugins.extract ?
8 | `\nvar ExtractTextPlugin = require("extract-text-webpack-plugin");` : ''}
9 | var dir_js = path.resolve(__dirname, '${names.jsdir}');${names.cssdir ?
10 | `\nvar dir_css = path.resolve(__dirname, '${names.cssdir}');` : ''}
11 | var dir_build = path.resolve(__dirname, '${names.distdir}');
12 |
13 | module.exports = {
14 | entry: {
15 | app : path.resolve(__dirname, 'index.js'),
16 | vendor : ${names.vendors}
17 | },
18 | output: {
19 | path: dir_build,
20 | filename: 'bundle.js'
21 | },
22 | resolve: {
23 | modulesDirectories: ['node_modules', dir_js],
24 | },
25 | devServer: {
26 | contentBase: dir_build,
27 | },${names.isAutoprefix ? `
28 | postcss: function () {
29 | return [require('autoprefixer')];
30 | },` : ''} ${names.devtool ? `
31 | devtool: '${names.devtool}',` : '' }
32 | stats: {
33 | colors: true,
34 | chunkModules: false
35 | },
36 | plugins: [${names.plugins.extract ? `
37 | new ExtractTextPlugin("[name].css", { allChunks: true }),` : ''
38 | } ${names.plugins.chunks ? `
39 | new webpack.optimize.CommonsChunkPlugin("vendor", "[name].js"),` : ''}
40 | new webpack.NoErrorsPlugin()
41 | ],
42 |
43 | module: {
44 | loaders: [${names.loaders}
45 | ]${names.isLinter ? `,
46 | preLoaders: [
47 | {
48 | test: /\\.js($|\\?)|\\.jsx($|\\?)/,
49 | loaders: ['eslint'],
50 | exclude : [/node_modules/]
51 | }
52 | ]` : ''}
53 | }
54 | }
55 | `
56 | }
57 |
58 | let configNode = function(names) {
59 | return `
60 | /* global __dirname */
61 | /* generated by unstuck-webpack */
62 |
63 | var path = require('path');
64 | var webpack = require('webpack');${names.plugins.extract ?
65 | `\nvar ExtractTextPlugin = require("extract-text-webpack-plugin");` : ''}
66 | var dir_js = path.resolve(__dirname, '${names.jsdir}');${names.cssdir ?
67 | `\nvar dir_css = path.resolve(__dirname, '${names.cssdir}');` : ''}
68 | var dir_build = path.resolve(__dirname, '${names.distdir}');
69 | var fs = require('fs');
70 |
71 | var nodeModules = {};
72 | fs.readdirSync('node_modules')
73 | .filter(function(x) {
74 | return ['.bin'].indexOf(x) === -1;
75 | })
76 | .forEach(function(mod) {
77 | nodeModules[mod] = 'commonjs ' + mod;
78 | });
79 |
80 | module.exports = {
81 | entry: {
82 | app : path.resolve(dir_js, 'index.js')
83 | },
84 | target : 'node',
85 | output: {
86 | path: dir_build,
87 | filename: 'bundle.js'
88 | },
89 | externals: nodeModules,
90 | resolve: {
91 | modulesDirectories: ['node_modules', dir_js],
92 | },
93 | devServer: {
94 | contentBase: dir_build,
95 | },${names.isAutoprefix ? `
96 | postcss: function () {
97 | return [require('autoprefixer')];
98 | },` : ''} ${names.devtool ? `
99 | devtool: '${names.devtool}',` : '' }
100 | stats: {
101 | colors: true,
102 | chunkModules: false
103 | },
104 | plugins: [${names.plugins.extract ? `
105 | new ExtractTextPlugin("[name].css", { allChunks: true }),` : ''
106 | } ${names.plugins.chunks ? `
107 | new webpack.optimize.CommonsChunkPlugin("vendor", "[name].js"),` : ''}
108 | new webpack.NoErrorsPlugin()
109 | ],
110 |
111 | module: {
112 | loaders: [${names.loaders}
113 | ]${names.isLinter ? `,
114 | preLoaders: [
115 | {
116 | test: /\\.js($|\\?)|\\.jsx($|\\?)/,
117 | loaders: ['eslint'],
118 | exclude : [/node_modules/]
119 | }
120 | ]` : ''}
121 | }
122 | }
123 | `
124 | }
125 |
126 | let createLoader = function(loader, options) {
127 | if (loader === 'react') {
128 | return `
129 | {
130 | loader: 'babel-loader',
131 | test: /\\.js($|\\?)|\\.jsx($|\\?)/,
132 | exclude: /node_modules/,
133 | presets : ['es2015', 'react']
134 | }`
135 | }
136 |
137 | if (loader === 'babel') {
138 | return `
139 | {
140 | loader: 'babel-loader',
141 | test: /\\.js$/,
142 | exclude: /node_modules/,
143 | presets : ['es2015']
144 | }`
145 | }
146 |
147 | if (loader === 'vue') {
148 | return `
149 | {
150 | loader: 'vue-loader',
151 | test: /\\.vue$/,
152 | exclude: /node_modules/
153 | }`
154 | }
155 |
156 | if (loader === 'assets') {
157 | return `
158 | {
159 | loader: 'file?name=${options.dir}[name].[ext]',
160 | test: /\\.png($|\\?)|\\.woff($|\\?)|\\.woff2($|\\?)|\\.ttf($|\\?)|\\.eot($|\\?)|\\.svg($|\\?)/
161 | }`
162 | }
163 |
164 | if (loader === 'html') {
165 | return `
166 | {
167 | loader: 'file?name=${options.dir}[name].html',
168 | test: /\\.html$/
169 | }`
170 | }
171 |
172 | if (loader === 'jade') {
173 | return `
174 | {
175 | loader: 'file?name=${options.dir}[name].html!jade-loader',
176 | test: /\\.jade$/
177 | }`
178 | }
179 |
180 | if (loader === 'markdown') {
181 | return `
182 | {
183 | loader: 'file?name=${options.dir}[name].html!markdown-loader',
184 | test: /\\.md$/
185 | }`
186 | }
187 |
188 | if (loader === 'handlebars') {
189 | return `
190 | {
191 | loader: 'handlebars-loader',
192 | test: /\\.handlebars$/
193 | }`
194 | }
195 |
196 | if (loader === 'less' && options.extract === false) {
197 | return `
198 | {
199 | loader: 'style-loader!css-loader?sourceMap!${options.prefix ? 'postcss-loader!' : ''}less-loader?sourceMap',
200 | test: /\\.less$/
201 | }`
202 | }
203 |
204 | if (loader === 'less' && options.extract === true) {
205 | return `
206 | {
207 | loader: ExtractTextPlugin.extract('style-loader', 'css-loader?sourceMap!${options.prefix ? 'postcss-loader!' : ''}less-loader?sourceMap'),
208 | test: /\\.less$/
209 | }`
210 | }
211 |
212 | if (loader === 'sass' && options.extract === false) {
213 | return `
214 | {
215 | loader: 'style-loader!css-loader?sourceMap!${options.prefix ? 'postcss-loader!' : ''}sass-loader?sourceMap',
216 | test: /\\.sass$/
217 | }`
218 | }
219 |
220 | if (loader === 'sass' && options.extract === true) {
221 | return `
222 | {
223 | loader: ExtractTextPlugin.extract('style-loader', 'css-loader?sourceMap!${options.prefix ? 'postcss-loader!' : ''}sass-loader?sourceMap'),
224 | test: /\\.sass$/
225 | }`
226 | }
227 |
228 | if (loader === 'styl' && options.extract === false) {
229 | return `
230 | {
231 | loader: 'style-loader!css-loader?sourceMap!${options.prefix ? 'postcss-loader!' : ''}stylus-loader?sourceMap',
232 | test: /\\.styl$/
233 | }`
234 | }
235 |
236 | if (loader === 'styl' && options.extract === true) {
237 | return `
238 | {
239 | loader: ExtractTextPlugin.extract('style-loader', 'css-loader?sourceMap!${options.prefix ? 'postcss-loader!' : ''}stylus-loader?sourceMap'),
240 | test: /\\.styl$/
241 | }`
242 | }
243 | }
244 |
245 |
246 | export default function WebpackConfigurator(state) {
247 | let options = {
248 | jsdir : state.config.jsdir,
249 | cssdir : state.css.enabled ? state.config.cssdir : false,
250 | vendors : JSON.stringify(state.config.vendors),
251 | isAutoprefix : state.css.enabled && state.css.autoprefix,
252 | isLinter : state.js.enabled && state.js.linter,
253 | devtool : state.js.enabled && state.js.sourceMap !== 'none' ? state.js.sourceMap : false,
254 | isNode : state.config.template === 'node',
255 | distdir : 'dist',
256 | plugins : {},
257 | loaders : []
258 | }
259 |
260 | if (state.config.extract.enabled === true) {
261 | options.plugins.extract = true
262 | }
263 |
264 | if (state.config.chunks.enabled === true) {
265 | options.plugins.chunks = true
266 | }
267 |
268 | if (state.config.assets.enabled === true) {
269 | options.loaders.push(createLoader('assets', {dir : state.config.assets.dir}))
270 | }
271 |
272 | /* js */
273 | if (state.js.enabled === true) {
274 | if (state.js.transpiller.babel === true && state.js.transpiller.react === false) {
275 | options.loaders.push(createLoader('babel'))
276 | }
277 |
278 | if (state.js.transpiller.react === true) {
279 | options.loaders.push(createLoader('react'))
280 | }
281 |
282 | if (state.js.transpiller.vue === true) {
283 | options.loaders.push(createLoader('vue'))
284 | }
285 | }
286 |
287 | /* html */
288 | if (state.html.enabled === true) {
289 | if (state.html.templates.html.enabled === true) {
290 | options.loaders.push(createLoader('html', {dir : state.html.templates.html.target}))
291 | }
292 |
293 | if (state.html.templates.jade.enabled === true) {
294 | options.loaders.push(createLoader('jade', {dir : state.html.templates.jade.target}))
295 | }
296 |
297 | if (state.html.templates.markdown.enabled === true) {
298 | options.loaders.push(createLoader('markdown', {dir : state.html.templates.markdown.target}))
299 | }
300 |
301 | if (state.html.templates.handlebars.enabled === true) {
302 | options.loaders.push(createLoader('handlebars'))
303 | }
304 | }
305 |
306 | /* css */
307 | if (state.css.enabled === true) {
308 | if (state.css.transpiller.less === true) {
309 | options.loaders.push(createLoader('less',
310 | {extract : options.plugins.extract, prefix : options.isAutoprefix}))
311 | }
312 |
313 | if (state.css.transpiller.sass === true) {
314 | options.loaders.push(createLoader('sass',
315 | {extract : options.plugins.extract, prefix : options.isAutoprefix}))
316 | }
317 |
318 | if (state.css.transpiller.styl === true) {
319 | options.loaders.push(createLoader('styl',
320 | {extract : options.plugins.extract, prefix : options.isAutoprefix}))
321 | }
322 | }
323 |
324 | return (options.isNode ? configNode(options) : configWeb(options)).replace(/^\n/,'')
325 | }
326 |
--------------------------------------------------------------------------------
/js/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import {Provider} from 'react-redux'
4 | import { Router, Route, hashHistory, IndexRoute } from 'react-router'
5 |
6 | import store from 'reducer/index'
7 |
8 | import Application from 'component/Application.jsx'
9 |
10 | import IndexContainer from 'component/IndexContainer.jsx'
11 | import HtmlContainer from 'component/HtmlContainer.jsx'
12 | import JsContainer from 'component/JsContainer.jsx'
13 | import CssContainer from 'component/CssContainer.jsx'
14 | import FrameworksContainer from 'component/FrameworksContainer.jsx'
15 |
16 | ReactDOM.render(
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | ,document.getElementById('application'))
29 |
--------------------------------------------------------------------------------
/js/reducer/actions.js:
--------------------------------------------------------------------------------
1 | import {TemplateConfigurator, FrameworkConfigurator} from 'configurator/index'
2 |
3 | export default class Actions {
4 |
5 | static SET_TEMPLATE(template = null) {
6 | return dispatch => TemplateConfigurator(dispatch, template)
7 | }
8 |
9 | static ADD_FW(fw = null) {
10 | return dispatch => FrameworkConfigurator(dispatch, fw)
11 | }
12 |
13 |
14 | static RESET() {
15 | return {
16 | type : 'RESET',
17 | payload : null
18 | }
19 | }
20 |
21 | static TOGGLE_VENDOR(vendor = null, checked = false) {
22 | return {
23 | type : 'TOGGLE_VENDOR',
24 | payload : {vendor, checked}
25 | }
26 | }
27 |
28 | static TOGGLE_HTML(checked = false) {
29 | return {
30 | type : 'TOGGLE_HTML',
31 | payload : {checked}
32 | }
33 | }
34 |
35 | static TOGGLE_HTML_TEMPLATE(template = null, checked = false) {
36 | return {
37 | type : 'TOGGLE_HTML_TEMPLATE',
38 | payload : {template, checked}
39 | }
40 | }
41 |
42 | static SET_HTML_TEMPLATE_TARGET(template = null, target = '/') {
43 | return {
44 | type : 'SET_HTML_TEMPLATE_TARGET',
45 | payload : {template, target}
46 | }
47 | }
48 |
49 | static FW_ADD_FRAMEWORK(fw = null) {
50 | return {
51 | type : 'FW_ADD_FRAMEWORK',
52 | payload : {fw}
53 | }
54 | }
55 |
56 | static FW_REMOVE_FRAMEWORK(fw = null) {
57 | return {
58 | type : 'FW_REMOVE_FRAMEWORK',
59 | payload : {fw}
60 | }
61 | }
62 |
63 | static TOGGLE_JS(checked = false) {
64 | return {
65 | type : 'TOGGLE_JS',
66 | payload : {checked}
67 | }
68 | }
69 |
70 | static TOGGLE_CSS(checked = false) {
71 | return {
72 | type : 'TOGGLE_CSS',
73 | payload : {checked}
74 | }
75 | }
76 |
77 | static TAB_DEFAULT(page, tab) {
78 | return {
79 | type : 'TAB_DEFAULT',
80 | payload : {page , tab}
81 | }
82 | }
83 |
84 | static CONFIG_TEMPLATE(template) {
85 | return {
86 | type : 'CONFIG_TEMPLATE',
87 | payload : {template}
88 | }
89 | }
90 |
91 | static CONFIG_GENERAL_TARGET(plugin = null, option, target) {
92 | return {
93 | type : 'CONFIG_GENERAL_TARGET',
94 | payload : {plugin, option, target}
95 | }
96 | }
97 |
98 | static CONFIG_TOGGLE_PLUGIN(plugin, checked = false) {
99 | return {
100 | type : 'CONFIG_TOGGLE_PLUGIN',
101 | payload : {plugin, checked}
102 | }
103 | }
104 |
105 | static JS_TOGGLE_LINTER(checked = false) {
106 | return {
107 | type : 'JS_TOGGLE_LINTER',
108 | payload : {checked}
109 | }
110 | }
111 |
112 | static JS_SELECT_TRANSPILLER(transpiller = 'none', checked = false) {
113 | return {
114 | type : 'JS_SELECT_TRANSPILLER',
115 | payload : {transpiller, checked}
116 | }
117 | }
118 |
119 | static JS_SELECT_SOURCEMAP(sourcemap = 'none') {
120 | return {
121 | type : 'JS_SELECT_SOURCEMAP',
122 | payload : {sourcemap}
123 | }
124 | }
125 |
126 | static CSS_SELECT_TRANSPILLER(transpiller = 'none', checked = false) {
127 | return {
128 | type : 'CSS_SELECT_TRANSPILLER',
129 | payload : {transpiller, checked}
130 | }
131 | }
132 |
133 | static CSS_TOGGLE_AUTOPREFIX(checked = false) {
134 | return {
135 | type : 'CSS_TOGGLE_AUTOPREFIX',
136 | payload : {checked}
137 | }
138 | }
139 |
140 | }
141 |
--------------------------------------------------------------------------------
/js/reducer/config.js:
--------------------------------------------------------------------------------
1 | import initialState from 'reducer/initialState'
2 |
3 | export default function(state = {}, action) {
4 |
5 | if (action.type === 'RESET') {
6 | let init = Object.assign({}, initialState.config)
7 | init.html = Object.assign({}, initialState.config.html)
8 | init.chunks = Object.assign({}, initialState.config.chunks)
9 | init.extract = Object.assign({}, initialState.config.extract)
10 | init.assets = Object.assign({}, initialState.config.assets)
11 | init.vendors = []
12 |
13 | return init
14 | }
15 |
16 | if (action.type === 'TOGGLE_VENDOR') {
17 | if (action.payload.checked === true) {
18 | if (state.vendors.indexOf(action.payload.vendor) !== -1) {
19 | return Object.assign({}, state)
20 | }
21 | state.vendors.push(action.payload.vendor)
22 | } else {
23 | let idx = state.vendors.indexOf(action.payload.vendor)
24 | if (idx > -1) {
25 | state.vendors = state.vendors.slice(0, idx).concat(state.vendors.slice(idx + 1))
26 | }
27 | }
28 | return Object.assign({}, state)
29 | }
30 |
31 | if (action.type === 'CONFIG_TEMPLATE') {
32 | state.template = action.payload.template
33 | return Object.assign({}, state)
34 | }
35 |
36 | if (action.type === 'CONFIG_GENERAL_TARGET') {
37 | if (action.payload.plugin !== null) {
38 | state[action.payload.plugin][action.payload.option] = action.payload.target
39 | } else {
40 | state[action.payload.option] = action.payload.target
41 | }
42 | return Object.assign({}, state)
43 | }
44 |
45 | if (action.type === 'CONFIG_TOGGLE_PLUGIN') {
46 | state[action.payload.plugin].enabled = action.payload.checked
47 | return Object.assign({}, state)
48 | }
49 |
50 | return state
51 | }
52 |
--------------------------------------------------------------------------------
/js/reducer/css.js:
--------------------------------------------------------------------------------
1 | import initialState from 'reducer/initialState'
2 |
3 | export default function(state = {}, action) {
4 |
5 | if (action.type === 'RESET') {
6 | let init = Object.assign({}, initialState.css)
7 | init.transpiller = Object.assign({}, initialState.css.transpiller)
8 | return init
9 | }
10 |
11 | if (action.type === 'TOGGLE_CSS') {
12 | state.enabled = action.payload.checked
13 | return Object.assign({}, state)
14 | }
15 |
16 | if (action.type === 'CSS_SELECT_TRANSPILLER') {
17 | state.transpiller[action.payload.transpiller] = action.payload.checked
18 | return Object.assign({}, state)
19 | }
20 |
21 | if (action.type === 'CSS_TOGGLE_AUTOPREFIX') {
22 | state.autoprefix = action.payload.checked
23 | return Object.assign({}, state)
24 | }
25 |
26 | return state
27 | }
28 |
--------------------------------------------------------------------------------
/js/reducer/fw.js:
--------------------------------------------------------------------------------
1 | import initialState from 'reducer/initialState'
2 |
3 | export default function(state = [], action) {
4 |
5 | if (action.type === 'RESET') {
6 | return initialState.fw.slice()
7 | }
8 |
9 | if (action.type === 'FW_ADD_FRAMEWORK') {
10 | if (state.indexOf(action.payload.fw) === -1) {
11 | return [action.payload.fw,...state]
12 | }
13 | return [...state]
14 | }
15 |
16 | if (action.type === 'FW_REMOVE_FRAMEWORK') {
17 | let idx = state.indexOf(action.payload.fw)
18 | if (idx > -1) {
19 | return state.slice(0, idx).concat(state.slice(idx + 1))
20 | }
21 | return [...state]
22 | }
23 |
24 | return state
25 | }
26 |
--------------------------------------------------------------------------------
/js/reducer/html.js:
--------------------------------------------------------------------------------
1 | import initialState from 'reducer/initialState'
2 |
3 | export default function(state = {}, action) {
4 |
5 | if (action.type === 'RESET') {
6 | return Object.assign({}, initialState.html)
7 | }
8 |
9 | if (action.type === 'TOGGLE_HTML') {
10 | state.enabled = action.payload.checked
11 | return Object.assign({}, state)
12 | }
13 |
14 | if (action.type === 'TOGGLE_HTML_TEMPLATE') {
15 | state.templates[action.payload.template].enabled = action.payload.checked
16 | return Object.assign({}, state)
17 | }
18 |
19 | if (action.type === 'SET_HTML_TEMPLATE_TARGET') {
20 | state.templates[action.payload.template].target = action.payload.target
21 | return Object.assign({}, state)
22 | }
23 |
24 | return state
25 | }
26 |
--------------------------------------------------------------------------------
/js/reducer/index.js:
--------------------------------------------------------------------------------
1 | import {createStore, combineReducers, applyMiddleware, compose} from 'redux'
2 | import thunk from 'redux-thunk'
3 | import PersistLocal from 'reducer/persist'
4 |
5 | import html from 'reducer/html'
6 | import js from 'reducer/js'
7 | import css from 'reducer/css'
8 | import fw from 'reducer/fw'
9 | import tabs from 'reducer/tabs'
10 | import config from 'reducer/config'
11 |
12 | import defaultState from 'reducer/initialState'
13 | const {read, write} = PersistLocal('unstuck-webpack')
14 | const localState = read()
15 | const initialState = localState === null ? Object.assign({}, defaultState) : localState
16 |
17 | const store = createStore(
18 | combineReducers({
19 | html,
20 | js,
21 | css,
22 | fw,
23 | tabs,
24 | config
25 | }),
26 | initialState,
27 | compose(
28 | applyMiddleware(write),
29 | applyMiddleware(thunk),
30 | window.devToolsExtension ? window.devToolsExtension() : f => f
31 | )
32 | )
33 |
34 | export default store
35 |
--------------------------------------------------------------------------------
/js/reducer/initialState.js:
--------------------------------------------------------------------------------
1 | export default {
2 | html: {
3 | enabled : true,
4 | templates : {
5 | html : {
6 | enabled : true,
7 | target : '/'
8 | },
9 | jade : {
10 | enabled : false,
11 | target : '/'
12 | },
13 | markdown : {
14 | enabled : false,
15 | target : '/'
16 | },
17 | handlebars : {
18 | enabled : false
19 | }
20 | }
21 | },
22 | js : {
23 | enabled : false,
24 | sourceMap : 'none',
25 | transpiller : {babel : false, react : false, vue : false},
26 | linter : false
27 | },
28 | css : {
29 | enabled : false,
30 | autoprefix : false,
31 | transpiller : { less : false, sass : false, styl : false}
32 | },
33 | fw : [],
34 | tabs : {},
35 | config : {
36 | template : 'custom',
37 | jsdir : 'app',
38 | cssdir : 'css',
39 | chunks : { enabled : true },
40 | extract : { enabled : true },
41 | html : { enabled : true },
42 | assets : {enabled : true, dir : 'assets'},
43 | vendors : []
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/js/reducer/js.js:
--------------------------------------------------------------------------------
1 | import initialState from 'reducer/initialState'
2 |
3 | export default function(state = {}, action) {
4 |
5 | if (action.type === 'RESET') {
6 | let init = Object.assign({}, initialState.js)
7 | init.transpiller = Object.assign({}, initialState.js.transpiller)
8 | return init
9 | }
10 |
11 | if (action.type === 'TOGGLE_JS') {
12 | state.enabled = action.payload.checked
13 | return Object.assign({}, state)
14 | }
15 |
16 | if (action.type === 'JS_TOGGLE_LINTER') {
17 | state.linter = action.payload.checked
18 | return Object.assign({}, state)
19 | }
20 |
21 | if (action.type === 'JS_SELECT_TRANSPILLER') {
22 | state.transpiller[action.payload.transpiller] = action.payload.checked
23 | return Object.assign({}, state)
24 | }
25 |
26 | if (action.type === 'JS_SELECT_SOURCEMAP') {
27 | state.sourceMap = action.payload.sourcemap
28 | return Object.assign({}, state)
29 | }
30 |
31 | return state
32 | }
33 |
--------------------------------------------------------------------------------
/js/reducer/persist.js:
--------------------------------------------------------------------------------
1 | export default key => ({
2 | write: store => next => action => {
3 | let result = next(action)
4 | window.localStorage.setItem(key, JSON.stringify(store.getState()))
5 | return result
6 | },
7 | read: ()=> JSON.parse(window.localStorage.getItem(key))
8 | })
9 |
--------------------------------------------------------------------------------
/js/reducer/tabs.js:
--------------------------------------------------------------------------------
1 | export default function(state = {}, action) {
2 |
3 | if (action.type === 'TAB_DEFAULT') {
4 | state[action.payload.page] = action.payload.tab
5 | return Object.assign({}, state)
6 | }
7 |
8 | return state
9 | }
10 |
--------------------------------------------------------------------------------
/lib/FileSaver/index.js:
--------------------------------------------------------------------------------
1 | /* FileSaver.js
2 | * A saveAs() FileSaver implementation.
3 | * 1.1.20160328
4 | *
5 | * By Eli Grey, http://eligrey.com
6 | * License: MIT
7 | * See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
8 | */
9 |
10 | /*global self */
11 | /*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
12 |
13 | /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
14 |
15 | var saveAs = saveAs || (function(view) {
16 | "use strict";
17 | // IE <10 is explicitly unsupported
18 | if (typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
19 | return;
20 | }
21 | var
22 | doc = view.document
23 | // only get URL when necessary in case Blob.js hasn't overridden it yet
24 | , get_URL = function() {
25 | return view.URL || view.webkitURL || view;
26 | }
27 | , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
28 | , can_use_save_link = "download" in save_link
29 | , click = function(node) {
30 | var event = new MouseEvent("click");
31 | node.dispatchEvent(event);
32 | }
33 | , is_safari = /Version\/[\d\.]+.*Safari/.test(navigator.userAgent)
34 | , webkit_req_fs = view.webkitRequestFileSystem
35 | , req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem
36 | , throw_outside = function(ex) {
37 | (view.setImmediate || view.setTimeout)(function() {
38 | throw ex;
39 | }, 0);
40 | }
41 | , force_saveable_type = "application/octet-stream"
42 | , fs_min_size = 0
43 | // the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
44 | , arbitrary_revoke_timeout = 1000 * 40 // in ms
45 | , revoke = function(file) {
46 | var revoker = function() {
47 | if (typeof file === "string") { // file is an object URL
48 | get_URL().revokeObjectURL(file);
49 | } else { // file is a File
50 | file.remove();
51 | }
52 | };
53 | /* // Take note W3C:
54 | var
55 | uri = typeof file === "string" ? file : file.toURL()
56 | , revoker = function(evt) {
57 | // idealy DownloadFinishedEvent.data would be the URL requested
58 | if (evt.data === uri) {
59 | if (typeof file === "string") { // file is an object URL
60 | get_URL().revokeObjectURL(file);
61 | } else { // file is a File
62 | file.remove();
63 | }
64 | }
65 | }
66 | ;
67 | view.addEventListener("downloadfinished", revoker);
68 | */
69 | setTimeout(revoker, arbitrary_revoke_timeout);
70 | }
71 | , dispatch = function(filesaver, event_types, event) {
72 | event_types = [].concat(event_types);
73 | var i = event_types.length;
74 | while (i--) {
75 | var listener = filesaver["on" + event_types[i]];
76 | if (typeof listener === "function") {
77 | try {
78 | listener.call(filesaver, event || filesaver);
79 | } catch (ex) {
80 | throw_outside(ex);
81 | }
82 | }
83 | }
84 | }
85 | , auto_bom = function(blob) {
86 | // prepend BOM for UTF-8 XML and text/* types (including HTML)
87 | if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
88 | return new Blob(["\ufeff", blob], {type: blob.type});
89 | }
90 | return blob;
91 | }
92 | , FileSaver = function(blob, name, no_auto_bom) {
93 | if (!no_auto_bom) {
94 | blob = auto_bom(blob);
95 | }
96 | // First try a.download, then web filesystem, then object URLs
97 | var
98 | filesaver = this
99 | , type = blob.type
100 | , blob_changed = false
101 | , object_url
102 | , target_view
103 | , dispatch_all = function() {
104 | dispatch(filesaver, "writestart progress write writeend".split(" "));
105 | }
106 | // on any filesys errors revert to saving with object URLs
107 | , fs_error = function() {
108 | if (target_view && is_safari && typeof FileReader !== "undefined") {
109 | // Safari doesn't allow downloading of blob urls
110 | var reader = new FileReader();
111 | reader.onloadend = function() {
112 | var base64Data = reader.result;
113 | target_view.location.href = "data:attachment/file" + base64Data.slice(base64Data.search(/[,;]/));
114 | filesaver.readyState = filesaver.DONE;
115 | dispatch_all();
116 | };
117 | reader.readAsDataURL(blob);
118 | filesaver.readyState = filesaver.INIT;
119 | return;
120 | }
121 | // don't create more object URLs than needed
122 | if (blob_changed || !object_url) {
123 | object_url = get_URL().createObjectURL(blob);
124 | }
125 | if (target_view) {
126 | target_view.location.href = object_url;
127 | } else {
128 | var new_tab = view.open(object_url, "_blank");
129 | if (new_tab === undefined && is_safari) {
130 | //Apple do not allow window.open, see http://bit.ly/1kZffRI
131 | view.location.href = object_url
132 | }
133 | }
134 | filesaver.readyState = filesaver.DONE;
135 | dispatch_all();
136 | revoke(object_url);
137 | }
138 | , abortable = function(func) {
139 | return function() {
140 | if (filesaver.readyState !== filesaver.DONE) {
141 | return func.apply(this, arguments);
142 | }
143 | };
144 | }
145 | , create_if_not_found = {create: true, exclusive: false}
146 | , slice
147 | ;
148 | filesaver.readyState = filesaver.INIT;
149 | if (!name) {
150 | name = "download";
151 | }
152 | if (can_use_save_link) {
153 | object_url = get_URL().createObjectURL(blob);
154 | setTimeout(function() {
155 | save_link.href = object_url;
156 | save_link.download = name;
157 | click(save_link);
158 | dispatch_all();
159 | revoke(object_url);
160 | filesaver.readyState = filesaver.DONE;
161 | });
162 | return;
163 | }
164 | // Object and web filesystem URLs have a problem saving in Google Chrome when
165 | // viewed in a tab, so I force save with application/octet-stream
166 | // http://code.google.com/p/chromium/issues/detail?id=91158
167 | // Update: Google errantly closed 91158, I submitted it again:
168 | // https://code.google.com/p/chromium/issues/detail?id=389642
169 | if (view.chrome && type && type !== force_saveable_type) {
170 | slice = blob.slice || blob.webkitSlice;
171 | blob = slice.call(blob, 0, blob.size, force_saveable_type);
172 | blob_changed = true;
173 | }
174 | // Since I can't be sure that the guessed media type will trigger a download
175 | // in WebKit, I append .download to the filename.
176 | // https://bugs.webkit.org/show_bug.cgi?id=65440
177 | if (webkit_req_fs && name !== "download") {
178 | name += ".download";
179 | }
180 | if (type === force_saveable_type || webkit_req_fs) {
181 | target_view = view;
182 | }
183 | if (!req_fs) {
184 | fs_error();
185 | return;
186 | }
187 | fs_min_size += blob.size;
188 | req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) {
189 | fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) {
190 | var save = function() {
191 | dir.getFile(name, create_if_not_found, abortable(function(file) {
192 | file.createWriter(abortable(function(writer) {
193 | writer.onwriteend = function(event) {
194 | target_view.location.href = file.toURL();
195 | filesaver.readyState = filesaver.DONE;
196 | dispatch(filesaver, "writeend", event);
197 | revoke(file);
198 | };
199 | writer.onerror = function() {
200 | var error = writer.error;
201 | if (error.code !== error.ABORT_ERR) {
202 | fs_error();
203 | }
204 | };
205 | "writestart progress write abort".split(" ").forEach(function(event) {
206 | writer["on" + event] = filesaver["on" + event];
207 | });
208 | writer.write(blob);
209 | filesaver.abort = function() {
210 | writer.abort();
211 | filesaver.readyState = filesaver.DONE;
212 | };
213 | filesaver.readyState = filesaver.WRITING;
214 | }), fs_error);
215 | }), fs_error);
216 | };
217 | dir.getFile(name, {create: false}, abortable(function(file) {
218 | // delete file if it already exists
219 | file.remove();
220 | save();
221 | }), abortable(function(ex) {
222 | if (ex.code === ex.NOT_FOUND_ERR) {
223 | save();
224 | } else {
225 | fs_error();
226 | }
227 | }));
228 | }), fs_error);
229 | }), fs_error);
230 | }
231 | , FS_proto = FileSaver.prototype
232 | , saveAs = function(blob, name, no_auto_bom) {
233 | return new FileSaver(blob, name, no_auto_bom);
234 | }
235 | ;
236 | // IE 10+ (native saveAs)
237 | if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
238 | return function(blob, name, no_auto_bom) {
239 | if (!no_auto_bom) {
240 | blob = auto_bom(blob);
241 | }
242 | return navigator.msSaveOrOpenBlob(blob, name || "download");
243 | };
244 | }
245 |
246 | FS_proto.abort = function() {
247 | var filesaver = this;
248 | filesaver.readyState = filesaver.DONE;
249 | dispatch(filesaver, "abort");
250 | };
251 | FS_proto.readyState = FS_proto.INIT = 0;
252 | FS_proto.WRITING = 1;
253 | FS_proto.DONE = 2;
254 |
255 | FS_proto.error =
256 | FS_proto.onwritestart =
257 | FS_proto.onprogress =
258 | FS_proto.onwrite =
259 | FS_proto.onabort =
260 | FS_proto.onerror =
261 | FS_proto.onwriteend =
262 | null;
263 |
264 | return saveAs;
265 | }(
266 | typeof self !== "undefined" && self
267 | || typeof window !== "undefined" && window
268 | || this.content
269 | ));
270 | // `self` is undefined in Firefox for Android content script context
271 | // while `this` is nsIContentFrameMessageManager
272 | // with an attribute `content` that corresponds to the window
273 |
274 | if (typeof module !== "undefined" && module.exports) {
275 | module.exports.saveAs = saveAs;
276 | } else if ((typeof define !== "undefined" && define !== null) && (define.amd !== null)) {
277 | define([], function() {
278 | return saveAs;
279 | });
280 | }
281 |
--------------------------------------------------------------------------------
/lib/prism/prism.css:
--------------------------------------------------------------------------------
1 | /* http://prismjs.com/download.html?themes=prism-twilight&languages=clike+javascript+json */
2 | /**
3 | * prism.js Twilight theme
4 | * Based (more or less) on the Twilight theme originally of Textmate fame.
5 | * @author Remy Bach
6 | */
7 | code[class*="language-"],
8 | pre[class*="language-"] {
9 | color: white;
10 | background: none;
11 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
12 | text-align: left;
13 | text-shadow: 0 -.1em .2em black;
14 | white-space: pre;
15 | word-spacing: normal;
16 | word-break: normal;
17 | word-wrap: normal;
18 | line-height: 1.5;
19 |
20 | -moz-tab-size: 4;
21 | -o-tab-size: 4;
22 | tab-size: 4;
23 |
24 | -webkit-hyphens: none;
25 | -moz-hyphens: none;
26 | -ms-hyphens: none;
27 | hyphens: none;
28 | }
29 |
30 | pre[class*="language-"],
31 | :not(pre) > code[class*="language-"] {
32 | background: hsl(0, 0%, 8%); /* #141414 */
33 | }
34 |
35 | /* Code blocks */
36 | pre[class*="language-"] {
37 | border-radius: .5em;
38 | border: .3em solid hsl(0, 0%, 33%); /* #282A2B */
39 | box-shadow: 1px 1px .5em black inset;
40 | margin: .5em 0;
41 | overflow: auto;
42 | padding: 1em;
43 | }
44 |
45 | pre[class*="language-"]::-moz-selection {
46 | /* Firefox */
47 | background: hsl(200, 4%, 16%); /* #282A2B */
48 | }
49 |
50 | pre[class*="language-"]::selection {
51 | /* Safari */
52 | background: hsl(200, 4%, 16%); /* #282A2B */
53 | }
54 |
55 | /* Text Selection colour */
56 | pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
57 | code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
58 | text-shadow: none;
59 | background: hsla(0, 0%, 93%, 0.15); /* #EDEDED */
60 | }
61 |
62 | pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
63 | code[class*="language-"]::selection, code[class*="language-"] ::selection {
64 | text-shadow: none;
65 | background: hsla(0, 0%, 93%, 0.15); /* #EDEDED */
66 | }
67 |
68 | /* Inline code */
69 | :not(pre) > code[class*="language-"] {
70 | border-radius: .3em;
71 | border: .13em solid hsl(0, 0%, 33%); /* #545454 */
72 | box-shadow: 1px 1px .3em -.1em black inset;
73 | padding: .15em .2em .05em;
74 | white-space: normal;
75 | }
76 |
77 | .token.comment,
78 | .token.prolog,
79 | .token.doctype,
80 | .token.cdata {
81 | color: hsl(0, 0%, 47%); /* #777777 */
82 | }
83 |
84 | .token.punctuation {
85 | opacity: .7;
86 | }
87 |
88 | .namespace {
89 | opacity: .7;
90 | }
91 |
92 | .token.tag,
93 | .token.boolean,
94 | .token.number,
95 | .token.deleted {
96 | color: hsl(14, 58%, 55%); /* #CF6A4C */
97 | }
98 |
99 | .token.keyword,
100 | .token.property,
101 | .token.selector,
102 | .token.constant,
103 | .token.symbol,
104 | .token.builtin {
105 | color: hsl(53, 89%, 79%); /* #F9EE98 */
106 | }
107 |
108 | .token.attr-name,
109 | .token.attr-value,
110 | .token.string,
111 | .token.char,
112 | .token.operator,
113 | .token.entity,
114 | .token.url,
115 | .language-css .token.string,
116 | .style .token.string,
117 | .token.variable,
118 | .token.inserted {
119 | color: hsl(76, 21%, 52%); /* #8F9D6A */
120 | }
121 |
122 | .token.atrule {
123 | color: hsl(218, 22%, 55%); /* #7587A6 */
124 | }
125 |
126 | .token.regex,
127 | .token.important {
128 | color: hsl(42, 75%, 65%); /* #E9C062 */
129 | }
130 |
131 | .token.important,
132 | .token.bold {
133 | font-weight: bold;
134 | }
135 | .token.italic {
136 | font-style: italic;
137 | }
138 |
139 | .token.entity {
140 | cursor: help;
141 | }
142 |
143 | pre[data-line] {
144 | padding: 1em 0 1em 3em;
145 | position: relative;
146 | }
147 |
148 | /* Markup */
149 | .language-markup .token.tag,
150 | .language-markup .token.attr-name,
151 | .language-markup .token.punctuation {
152 | color: hsl(33, 33%, 52%); /* #AC885B */
153 | }
154 |
155 | /* Make the tokens sit above the line highlight so the colours don't look faded. */
156 | .token {
157 | position: relative;
158 | z-index: 1;
159 | }
160 |
161 | .line-highlight {
162 | background: -moz-linear-gradient(to right, hsla(0, 0%, 33%, .1) 70%, hsla(0, 0%, 33%, 0)); /* #545454 */
163 | background: -o-linear-gradient(to right, hsla(0, 0%, 33%, .1) 70%, hsla(0, 0%, 33%, 0)); /* #545454 */
164 | background: -webkit-linear-gradient(to right, hsla(0, 0%, 33%, .1) 70%, hsla(0, 0%, 33%, 0)); /* #545454 */
165 | background: hsla(0, 0%, 33%, 0.25); /* #545454 */
166 | background: linear-gradient(to right, hsla(0, 0%, 33%, .1) 70%, hsla(0, 0%, 33%, 0)); /* #545454 */
167 | border-bottom: 1px dashed hsl(0, 0%, 33%); /* #545454 */
168 | border-top: 1px dashed hsl(0, 0%, 33%); /* #545454 */
169 | left: 0;
170 | line-height: inherit;
171 | margin-top: 0.75em; /* Same as .prism’s padding-top */
172 | padding: inherit 0;
173 | pointer-events: none;
174 | position: absolute;
175 | right: 0;
176 | white-space: pre;
177 | z-index: 0;
178 | }
179 |
180 | .line-highlight:before,
181 | .line-highlight[data-end]:after {
182 | background-color: hsl(215, 15%, 59%); /* #8794A6 */
183 | border-radius: 999px;
184 | box-shadow: 0 1px white;
185 | color: hsl(24, 20%, 95%); /* #F5F2F0 */
186 | content: attr(data-start);
187 | font: bold 65%/1.5 sans-serif;
188 | left: .6em;
189 | min-width: 1em;
190 | padding: 0 .5em;
191 | position: absolute;
192 | text-align: center;
193 | text-shadow: none;
194 | top: .4em;
195 | vertical-align: .3em;
196 | }
197 |
198 | .line-highlight[data-end]:after {
199 | bottom: .4em;
200 | content: attr(data-end);
201 | top: auto;
202 | }
203 |
204 |
--------------------------------------------------------------------------------
/lib/prism/prism.js:
--------------------------------------------------------------------------------
1 | /* http://prismjs.com/download.html?themes=prism-twilight&languages=clike+javascript+json */
2 | var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(\w+)\b/i,t=0,n=_self.Prism={util:{encode:function(e){return e instanceof a?new a(e.type,n.util.encode(e.content),e.alias):"Array"===n.util.type(e)?e.map(n.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(m instanceof a)){u.lastIndex=0;var y=u.exec(m),v=1;if(!y&&h&&p!=r.length-1){var b=r[p+1].matchedStr||r[p+1],k=m+b;if(p=m.length)continue;var _=y.index+y[0].length,P=m.length+b.length;if(v=3,P>=_){if(r[p+1].greedy)continue;v=2,k=k.slice(0,P)}m=k}if(y){g&&(f=y[1].length);var w=y.index+f,y=y[0].slice(f),_=w+y.length,S=m.slice(0,w),O=m.slice(_),j=[p,v];S&&j.push(S);var A=new a(i,c?n.tokenize(y,c):y,d,y,h);j.push(A),O&&j.push(O),Array.prototype.splice.apply(r,j)}}}}}return r},hooks:{all:{},add:function(e,t){var a=n.hooks.all;a[e]=a[e]||[],a[e].push(t)},run:function(e,t){var a=n.hooks.all[e];if(a&&a.length)for(var r,l=0;r=a[l++];)r(t)}}},a=n.Token=function(e,t,n,a,r){this.type=e,this.content=t,this.alias=n,this.matchedStr=a||null,this.greedy=!!r};if(a.stringify=function(e,t,r){if("string"==typeof e)return e;if("Array"===n.util.type(e))return e.map(function(n){return a.stringify(n,t,e)}).join("");var l={type:e.type,content:a.stringify(e.content,t,r),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:r};if("comment"==l.type&&(l.attributes.spellcheck="true"),e.alias){var i="Array"===n.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(l.classes,i)}n.hooks.run("wrap",l);var o="";for(var s in l.attributes)o+=(o?" ":"")+s+'="'+(l.attributes[s]||"")+'"';return"<"+l.tag+' class="'+l.classes.join(" ")+'" '+o+">"+l.content+""+l.tag+">"},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var t=JSON.parse(e.data),a=t.language,r=t.code,l=t.immediateClose;_self.postMessage(n.highlight(r,n.languages[a],a)),l&&_self.close()},!1),_self.Prism):_self.Prism;var r=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return r&&(n.filename=r.src,document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",n.highlightAll)),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism);
3 | Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:{pattern:/(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,"boolean":/\b(true|false)\b/,"function":/[a-z0-9_]+(?=\()/i,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/};
4 | Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/,number:/\b-?(0x[\dA-Fa-f]+|0b[01]+|0o[0-7]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|Infinity)\b/,"function":/[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*(?=\()/i}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^\/])\/(?!\/)(\[.+?]|\\.|[^\/\\\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0,greedy:!0}}),Prism.languages.insertBefore("javascript","class-name",{"template-string":{pattern:/`(?:\\\\|\\?[^\\])*?`/,greedy:!0,inside:{interpolation:{pattern:/\$\{[^}]+\}/,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/(