├── .eslintignore
├── .eslintrc
├── .gitattributes
├── .gitignore
├── .prettierrc
├── .travis.yml
├── LICENSE
├── README.md
├── babel.config.js
├── gulpfile.js
├── package-lock.json
├── package.json
├── src
├── assets
│ ├── fabricator
│ │ ├── scripts
│ │ │ ├── fabricator.js
│ │ │ └── prism.js
│ │ └── styles
│ │ │ ├── fabricator.scss
│ │ │ └── partials
│ │ │ ├── _code.scss
│ │ │ ├── _color-chips.scss
│ │ │ ├── _controls.scss
│ │ │ ├── _item.scss
│ │ │ ├── _layout.scss
│ │ │ ├── _menu.scss
│ │ │ └── _variables.scss
│ └── toolkit
│ │ ├── scripts
│ │ └── toolkit.js
│ │ └── styles
│ │ └── toolkit.scss
├── data
│ └── toolkit.yml
├── docs
│ └── javascript.md
├── favicon.ico
├── materials
│ ├── components
│ │ ├── button.html
│ │ ├── checkbox.html
│ │ ├── lists
│ │ │ ├── ordered.html
│ │ │ └── unordered.html
│ │ ├── paragraph.html
│ │ ├── radio.html
│ │ ├── select.html
│ │ ├── table.html
│ │ ├── text-input.html
│ │ └── textarea.html
│ └── structures
│ │ └── form.html
└── views
│ ├── components.html
│ ├── docs.html
│ ├── index.html
│ ├── layouts
│ ├── default.html
│ └── includes
│ │ ├── f-control-bar.html
│ │ ├── f-controls.html
│ │ ├── f-icons.html
│ │ ├── f-item-content.html
│ │ ├── f-item-controls.html
│ │ ├── f-item.html
│ │ └── f-menu.html
│ ├── pages.html
│ ├── pages
│ └── home.html
│ └── structures.html
└── webpack.config.js
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .idea
4 | src/assets/fabricator
5 | src/docs
6 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["airbnb-base", "plugin:prettier/recommended"],
3 | "rules": {
4 | "arrow-body-style": 0
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .sass-cache
4 | .idea
5 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "es5"
4 | }
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 |
4 | node_js:
5 | - "8.14"
6 |
7 | notifications:
8 | webhooks:
9 | on_success: change
10 | on_failure: always
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | Copyright (c) 2016 Luke Askew
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5 |
6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7 |
8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | []()
2 | [](https://travis-ci.org/fbrctr/fabricator)
3 | [](https://david-dm.org/fbrctr/fabricator#info=devDependencies)
4 | [](https://gitter.im/fbrctr/fabricator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
5 |
6 |
7 |
8 |
9 |
10 | # Fabricator
11 |
12 | > _fabricate_ - to make by assembling parts or sections.
13 |
14 | Fabricator is a tool for building website UI toolkits - _think ["Tiny Bootstraps, for Every Client"](http://daverupert.com/2013/04/responsive-deliverables/#tiny-bootstraps-for-every-client)_
15 |
16 | ## Quick Start
17 |
18 | ```shell
19 | $ curl -L https://github.com/fbrctr/fabricator/archive/master.tar.gz | tar zx --strip 1
20 | $ npm start
21 | ```
22 |
23 | ## Documentation
24 |
25 | #### [Read the docs →](http://fbrctr.github.io/docs)
26 |
27 | ## Demo
28 |
29 | #### [Default Fabricator Instance →](http://fbrctr.github.io/demo)
30 |
31 | ## Credits
32 |
33 | Created by [Luke Askew](http://twitter.com/lukeaskew).
34 |
35 | Logo by [Abby Putinski](https://abbyputinski.com/)
36 |
37 | ## License
38 |
39 | [The MIT License (MIT)](http://opensource.org/licenses/mit-license.php)
40 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function babelConfig(api) {
2 | api.cache.using(() => process.env.NODE_ENV === 'development');
3 |
4 | return {
5 | presets: [
6 | [
7 | '@babel/preset-env',
8 | {
9 | modules: false,
10 | useBuiltIns: 'usage',
11 | },
12 | ],
13 | ],
14 | };
15 | };
16 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | const fabAssemble = require('fabricator-assemble');
2 | const browserSync = require('browser-sync');
3 | const csso = require('gulp-csso');
4 | const del = require('del');
5 | const gulp = require('gulp');
6 | const argv = require('minimist')(process.argv.slice(2));
7 | const log = require('fancy-log');
8 | const gulpif = require('gulp-if');
9 | const imagemin = require('gulp-imagemin');
10 | const prefix = require('gulp-autoprefixer');
11 | const rename = require('gulp-rename');
12 | const sass = require('gulp-sass');
13 | const sourcemaps = require('gulp-sourcemaps');
14 | const webpack = require('webpack');
15 | sass.compiler = require('node-sass');
16 |
17 | let server = false;
18 | function reload(done) {
19 | if (server) server.reload();
20 | done();
21 | }
22 |
23 | // configuration
24 | const config = {
25 | dev: !!argv.dev,
26 | styles: {
27 | browsers: [
28 | 'ie 11',
29 | 'edge >= 16',
30 | 'chrome >= 70',
31 | 'firefox >= 63',
32 | 'safari >= 11',
33 | 'iOS >= 12',
34 | 'ChromeAndroid >= 70',
35 | ],
36 | fabricator: {
37 | src: 'src/assets/fabricator/styles/fabricator.scss',
38 | dest: 'dist/assets/fabricator/styles',
39 | watch: 'src/assets/fabricator/styles/**/*.scss',
40 | },
41 | toolkit: {
42 | src: 'src/assets/toolkit/styles/toolkit.scss',
43 | dest: 'dist/assets/toolkit/styles',
44 | watch: 'src/assets/toolkit/styles/**/*.scss',
45 | },
46 | },
47 | scripts: {
48 | fabricator: {
49 | src: './src/assets/fabricator/scripts/fabricator.js',
50 | dest: 'dist/assets/fabricator/scripts',
51 | watch: 'src/assets/fabricator/scripts/**/*',
52 | },
53 | toolkit: {
54 | src: './src/assets/toolkit/scripts/toolkit.js',
55 | dest: 'dist/assets/toolkit/scripts',
56 | watch: 'src/assets/toolkit/scripts/**/*',
57 | },
58 | },
59 | images: {
60 | toolkit: {
61 | src: ['src/assets/toolkit/images/**/*', 'src/favicon.ico'],
62 | dest: 'dist/assets/toolkit/images',
63 | watch: 'src/assets/toolkit/images/**/*',
64 | },
65 | },
66 | templates: {
67 | watch: 'src/**/*.{html,md,json,yml}',
68 | },
69 | dest: 'dist',
70 | };
71 |
72 | // clean
73 | const clean = () => del([config.dest]);
74 |
75 | // styles
76 | function stylesFabricator() {
77 | return gulp
78 | .src(config.styles.fabricator.src)
79 | .pipe(sourcemaps.init())
80 | .pipe(sass().on('error', sass.logError))
81 | .pipe(prefix(config.styles.browsers))
82 | .pipe(gulpif(!config.dev, csso()))
83 | .pipe(rename('f.css'))
84 | .pipe(sourcemaps.write())
85 | .pipe(gulp.dest(config.styles.fabricator.dest));
86 | }
87 |
88 | function stylesToolkit() {
89 | return gulp
90 | .src(config.styles.toolkit.src)
91 | .pipe(gulpif(config.dev, sourcemaps.init()))
92 | .pipe(
93 | sass({
94 | includePaths: './node_modules',
95 | }).on('error', sass.logError)
96 | )
97 | .pipe(prefix(config.styles.browsers))
98 | .pipe(gulpif(!config.dev, csso()))
99 | .pipe(gulpif(config.dev, sourcemaps.write()))
100 | .pipe(gulp.dest(config.styles.toolkit.dest));
101 | }
102 |
103 | const styles = gulp.parallel(stylesFabricator, stylesToolkit);
104 |
105 | // scripts
106 | const webpackConfig = require('./webpack.config')(config);
107 |
108 | function scripts(done) {
109 | webpack(webpackConfig, (err, stats) => {
110 | if (err) {
111 | log.error(err());
112 | }
113 | const result = stats.toJson();
114 | if (result.errors.length) {
115 | result.errors.forEach(error => {
116 | log.error(error);
117 | });
118 | }
119 | done();
120 | });
121 | }
122 |
123 | // images
124 | function imgFavicon() {
125 | return gulp.src('src/favicon.ico').pipe(gulp.dest(config.dest));
126 | }
127 |
128 | function imgMinification() {
129 | return gulp
130 | .src(config.images.toolkit.src)
131 | .pipe(imagemin())
132 | .pipe(gulp.dest(config.images.toolkit.dest));
133 | }
134 | const images = gulp.series(imgFavicon, imgMinification);
135 |
136 | // assembly
137 | function assembler(done) {
138 | fabAssemble({
139 | logErrors: config.dev,
140 | dest: config.dest,
141 | helpers: {
142 | // {{ default description "string of content used if description var is undefined" }}
143 | default: function defaultFn(...args) {
144 | return args.find(value => !!value);
145 | },
146 | // {{ concat str1 "string 2" }}
147 | concat: function concat(...args) {
148 | return args.slice(0, args.length - 1).join('');
149 | },
150 | // {{> (dynamicPartial name) }} ---- name = 'nameOfComponent'
151 | dynamicPartial: function dynamicPartial(name) {
152 | return name;
153 | },
154 | eq: function eq(v1, v2) {
155 | return v1 === v2;
156 | },
157 | ne: function ne(v1, v2) {
158 | return v1 !== v2;
159 | },
160 | and: function and(v1, v2) {
161 | return v1 && v2;
162 | },
163 | or: function or(v1, v2) {
164 | return v1 || v2;
165 | },
166 | not: function not(v1) {
167 | return !v1;
168 | },
169 | gte: function gte(a, b) {
170 | return +a >= +b;
171 | },
172 | lte: function lte(a, b) {
173 | return +a <= +b;
174 | },
175 | plus: function plus(a, b) {
176 | return +a + +b;
177 | },
178 | minus: function minus(a, b) {
179 | return +a - +b;
180 | },
181 | divide: function divide(a, b) {
182 | return +a / +b;
183 | },
184 | multiply: function multiply(a, b) {
185 | return +a * +b;
186 | },
187 | abs: function abs(a) {
188 | return Math.abs(a);
189 | },
190 | mod: function mod(a, b) {
191 | return +a % +b;
192 | },
193 | },
194 | });
195 | done();
196 | }
197 |
198 | // server
199 | function serve(done) {
200 | server = browserSync.create();
201 | server.init({
202 | server: {
203 | baseDir: config.dest,
204 | },
205 | notify: false,
206 | logPrefix: 'FABRICATOR',
207 | });
208 | done();
209 | }
210 |
211 | function watch() {
212 | gulp.watch(
213 | config.templates.watch,
214 | { interval: 500 },
215 | gulp.series(assembler, reload)
216 | );
217 | gulp.watch(
218 | [config.scripts.fabricator.watch, config.scripts.toolkit.watch],
219 | { interval: 500 },
220 | gulp.series(scripts, reload)
221 | );
222 | gulp.watch(
223 | config.images.toolkit.watch,
224 | { interval: 500 },
225 | gulp.series(images, reload)
226 | );
227 | gulp.watch(
228 | [config.styles.fabricator.watch, config.styles.toolkit.watch],
229 | { interval: 500 },
230 | gulp.series(styles, reload)
231 | );
232 | }
233 |
234 | // default build task
235 | let tasks = [clean, styles, scripts, images, assembler];
236 | if (config.dev) tasks = tasks.concat([serve, watch]);
237 | gulp.task('default', gulp.series(tasks));
238 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fabricator",
3 | "version": "2.0.2",
4 | "description": "A tool for building website UI toolkits",
5 | "license": "MIT",
6 | "homepage": "http://fbrctr.github.io/",
7 | "scripts": {
8 | "prestart": "npm install",
9 | "build": "npm run prestart && gulp",
10 | "start": "gulp --dev",
11 | "test": "eslint --fix . && npm run build",
12 | "lint": "eslint ."
13 | },
14 | "browserslist": [
15 | "ie 11",
16 | "edge >= 16",
17 | "chrome >= 70",
18 | "firefox >= 63",
19 | "safari >= 11",
20 | "iOS >= 12",
21 | "ChromeAndroid >= 70"
22 | ],
23 | "keywords": [
24 | "toolkit",
25 | "styleguide",
26 | "framework",
27 | "scaffold",
28 | "patterns",
29 | "library"
30 | ],
31 | "author": "Luke Askew",
32 | "repository": {
33 | "type": "git",
34 | "url": "https://github.com/fbrctr/fabricator.git"
35 | },
36 | "engines": {
37 | "node": ">=8.14",
38 | "npm": ">=6.4.1"
39 | },
40 | "devDependencies": {
41 | "@babel/core": "^7.1.6",
42 | "@babel/preset-env": "^7.1.6",
43 | "babel-loader": "^8.0.4",
44 | "browser-sync": "^2.26.5",
45 | "del": "^3.0.0",
46 | "eslint": "^5.9.0",
47 | "eslint-config-airbnb-base": "^13.1.0",
48 | "eslint-config-prettier": "^3.3.0",
49 | "eslint-plugin-import": "^2.14.0",
50 | "eslint-plugin-prettier": "^3.0.0",
51 | "fabricator-assemble": "^1.4.0",
52 | "fancy-log": "^1.3.2",
53 | "gulp": "^4.0.0",
54 | "gulp-autoprefixer": "^6.0.0",
55 | "gulp-csso": "^3.0.1",
56 | "gulp-if": "^2.0.2",
57 | "gulp-imagemin": "^5.0.3",
58 | "gulp-rename": "^1.4.0",
59 | "gulp-sass": "^4.0.2",
60 | "gulp-sourcemaps": "^2.6.4",
61 | "minimist": "^1.2.0",
62 | "node-sass": "^4.12.0",
63 | "prettier": "1.15.2",
64 | "webpack": "^4.26.0"
65 | },
66 | "dependencies": {
67 | "@babel/polyfill": "^7.0.0"
68 | },
69 | "optionalDependencies": {}
70 | }
71 |
--------------------------------------------------------------------------------
/src/assets/fabricator/scripts/fabricator.js:
--------------------------------------------------------------------------------
1 | require('./prism');
2 |
3 | /**
4 | * Global `fabricator` object
5 | * @namespace
6 | */
7 | const fabricator = window.fabricator = {};
8 |
9 |
10 | /**
11 | * Default options
12 | * @type {Object}
13 | */
14 | fabricator.options = {
15 | toggles: {
16 | labels: true,
17 | notes: true,
18 | code: false,
19 | },
20 | menu: false,
21 | mq: '(min-width: 60em)',
22 | };
23 |
24 | // open menu by default if large screen
25 | fabricator.options.menu = window.matchMedia(fabricator.options.mq).matches;
26 |
27 | /**
28 | * Feature detection
29 | * @type {Object}
30 | */
31 | fabricator.test = {};
32 |
33 | // test for sessionStorage
34 | fabricator.test.sessionStorage = (() => {
35 | const test = '_f';
36 | try {
37 | sessionStorage.setItem(test, test);
38 | sessionStorage.removeItem(test);
39 | return true;
40 | } catch (e) {
41 | return false;
42 | }
43 | })();
44 |
45 | // create storage object if it doesn't exist; store options
46 | if (fabricator.test.sessionStorage) {
47 | sessionStorage.fabricator = sessionStorage.fabricator || JSON.stringify(fabricator.options);
48 | }
49 |
50 |
51 | /**
52 | * Cache DOM
53 | * @type {Object}
54 | */
55 | fabricator.dom = {
56 | root: document.querySelector('html'),
57 | primaryMenu: document.querySelector('.f-menu'),
58 | menuItems: document.querySelectorAll('.f-menu li a'),
59 | menuToggle: document.querySelector('.f-menu-toggle'),
60 | };
61 |
62 |
63 | /**
64 | * Get current option values from session storage
65 | * @return {Object}
66 | */
67 | fabricator.getOptions = () => {
68 | return (fabricator.test.sessionStorage) ? JSON.parse(sessionStorage.fabricator) : fabricator.options;
69 | };
70 |
71 |
72 | /**
73 | * Build color chips
74 | */
75 | fabricator.buildColorChips = () => {
76 |
77 | const chips = document.querySelectorAll('.f-color-chip');
78 |
79 | for (let i = chips.length - 1; i >= 0; i--) {
80 | const color = chips[i].querySelector('.f-color-chip__color').innerHTML;
81 | chips[i].style.borderTopColor = color;
82 | chips[i].style.borderBottomColor = color;
83 | }
84 |
85 | return fabricator;
86 |
87 | };
88 |
89 |
90 | /**
91 | * Add `f-active` class to active menu item
92 | */
93 | fabricator.setActiveItem = () => {
94 |
95 | /**
96 | * Match the window location with the menu item, set menu item as active
97 | */
98 | const setActive = () => {
99 |
100 | // get current file and hash without first slash
101 | const loc = (window.location.pathname + window.location.hash);
102 | const current = loc.replace(/(^\/)([^#]+)?(#[\w\-\.]+)?$/ig, (match, slash, file, hash) => {
103 | return (file || '') + (hash || '').split('.')[0];
104 | }) || 'index.html';
105 |
106 |
107 | // find the current section in the items array
108 | for (let i = fabricator.dom.menuItems.length - 1; i >= 0; i--) {
109 |
110 | const item = fabricator.dom.menuItems[i];
111 |
112 | // get item href without first slash
113 | const href = item.getAttribute('href').replace(/^\//g, '');
114 |
115 | if (href === current) {
116 | item.classList.add('f-active');
117 | } else {
118 | item.classList.remove('f-active');
119 | }
120 |
121 | }
122 |
123 | };
124 |
125 | window.addEventListener('hashchange', setActive);
126 |
127 | setActive();
128 |
129 | return fabricator;
130 |
131 | };
132 |
133 |
134 | /**
135 | * Click handler to primary menu toggle
136 | * @return {Object} fabricator
137 | */
138 | fabricator.menuToggle = () => {
139 |
140 | // shortcut menu DOM
141 | const toggle = fabricator.dom.menuToggle;
142 | const options = fabricator.getOptions();
143 |
144 | // toggle classes on certain elements
145 | const toggleClasses = () => {
146 | options.menu = !fabricator.dom.root.classList.contains('f-menu-active');
147 | fabricator.dom.root.classList.toggle('f-menu-active');
148 |
149 | if (fabricator.test.sessionStorage) {
150 | sessionStorage.setItem('fabricator', JSON.stringify(options));
151 | }
152 | };
153 |
154 | // toggle classes on ctrl + m press
155 | document.onkeydown = (e) => {
156 | if (e.ctrlKey && e.keyCode === 'M'.charCodeAt(0)) {
157 | toggleClasses();
158 | }
159 | };
160 |
161 | // toggle classes on click
162 | toggle.addEventListener('click', () => {
163 | toggleClasses();
164 | });
165 |
166 | // close menu when clicking on item (for collapsed menu view)
167 | const closeMenu = () => {
168 | if (!window.matchMedia(fabricator.options.mq).matches) {
169 | toggleClasses();
170 | }
171 | };
172 |
173 | for (let i = 0; i < fabricator.dom.menuItems.length; i++) {
174 | fabricator.dom.menuItems[i].addEventListener('click', closeMenu);
175 | }
176 |
177 | return fabricator;
178 |
179 | };
180 |
181 |
182 | /**
183 | * Handler for preview and code toggles
184 | * @return {Object} fabricator
185 | */
186 | fabricator.allItemsToggles = () => {
187 |
188 | const itemCache = {
189 | labels: document.querySelectorAll('[data-f-toggle="labels"]'),
190 | notes: document.querySelectorAll('[data-f-toggle="notes"]'),
191 | code: document.querySelectorAll('[data-f-toggle="code"]'),
192 | };
193 |
194 | const toggleAllControls = document.querySelectorAll('.f-controls [data-f-toggle-control]');
195 | const options = fabricator.getOptions();
196 |
197 | // toggle all
198 | const toggleAllItems = (type, value) => {
199 |
200 | const button = document.querySelector(`.f-controls [data-f-toggle-control=${type}]`);
201 | const items = itemCache[type];
202 |
203 | for (let i = 0; i < items.length; i++) {
204 | if (value) {
205 | items[i].classList.remove('f-item-hidden');
206 | } else {
207 | items[i].classList.add('f-item-hidden');
208 | }
209 | }
210 |
211 | // toggle styles
212 | if (value) {
213 | button.classList.add('f-active');
214 | } else {
215 | button.classList.remove('f-active');
216 | }
217 |
218 | // update options
219 | options.toggles[type] = value;
220 |
221 | if (fabricator.test.sessionStorage) {
222 | sessionStorage.setItem('fabricator', JSON.stringify(options));
223 | }
224 |
225 | };
226 |
227 | for (let i = 0; i < toggleAllControls.length; i++) {
228 |
229 | toggleAllControls[i].addEventListener('click', (e) => {
230 |
231 | // extract info from target node
232 | const type = e.currentTarget.getAttribute('data-f-toggle-control');
233 | const value = e.currentTarget.className.indexOf('f-active') < 0;
234 |
235 | // toggle the items
236 | toggleAllItems(type, value);
237 |
238 | });
239 |
240 | }
241 |
242 | // persist toggle options from page to page
243 | Object.keys(options.toggles).forEach((key) => {
244 | toggleAllItems(key, options.toggles[key]);
245 | });
246 |
247 | return fabricator;
248 |
249 | };
250 |
251 |
252 | /**
253 | * Handler for single item code toggling
254 | */
255 | fabricator.singleItemToggle = () => {
256 |
257 | const itemToggleSingle = document.querySelectorAll('.f-item-group [data-f-toggle-control]');
258 |
259 | // toggle single
260 | const toggleSingleItemCode = (e) => {
261 | const group = e.currentTarget.parentNode.parentNode.parentNode;
262 | const type = e.currentTarget.getAttribute('data-f-toggle-control');
263 | group.querySelector(`[data-f-toggle=${type}]`).classList.toggle('f-item-hidden');
264 | };
265 |
266 | for (let i = 0; i < itemToggleSingle.length; i++) {
267 | itemToggleSingle[i].addEventListener('click', toggleSingleItemCode);
268 | }
269 |
270 | return fabricator;
271 |
272 | };
273 |
274 |
275 | /**
276 | * Automatically select code when code block is clicked
277 | */
278 | fabricator.bindCodeAutoSelect = () => {
279 |
280 | const codeBlocks = document.querySelectorAll('.f-item-code');
281 |
282 | const select = (block) => {
283 | const selection = window.getSelection();
284 | const range = document.createRange();
285 | range.selectNodeContents(block.querySelector('code'));
286 | selection.removeAllRanges();
287 | selection.addRange(range);
288 | };
289 |
290 | for (let i = codeBlocks.length - 1; i >= 0; i--) {
291 | codeBlocks[i].addEventListener('click', select.bind(this, codeBlocks[i]));
292 | }
293 |
294 | };
295 |
296 |
297 | /**
298 | * Open/Close menu based on session var.
299 | * Also attach a media query listener to close the menu when resizing to smaller screen.
300 | */
301 | fabricator.setInitialMenuState = () => {
302 |
303 | // root element
304 | const root = document.querySelector('html');
305 |
306 | const mq = window.matchMedia(fabricator.options.mq);
307 |
308 | // if small screen
309 | const mediaChangeHandler = (list) => {
310 | if (!list.matches) {
311 | root.classList.remove('f-menu-active');
312 | } else {
313 | if (fabricator.getOptions().menu) {
314 | root.classList.add('f-menu-active');
315 | } else {
316 | root.classList.remove('f-menu-active');
317 | }
318 | }
319 | };
320 |
321 | mq.addListener(mediaChangeHandler);
322 | mediaChangeHandler(mq);
323 |
324 | return fabricator;
325 |
326 | };
327 |
328 |
329 | /**
330 | * Initialization
331 | */
332 | fabricator
333 | .setInitialMenuState()
334 | .menuToggle()
335 | .allItemsToggles()
336 | .singleItemToggle()
337 | .buildColorChips()
338 | .setActiveItem()
339 | .bindCodeAutoSelect();
340 |
--------------------------------------------------------------------------------
/src/assets/fabricator/scripts/prism.js:
--------------------------------------------------------------------------------
1 | /* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript */
2 | self = (typeof window !== 'undefined')
3 | ? window // if in browser
4 | : (
5 | (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope)
6 | ? self // if in worker
7 | : {} // if in node js
8 | );
9 |
10 | /**
11 | * Prism: Lightweight, robust, elegant syntax highlighting
12 | * MIT license http://www.opensource.org/licenses/mit-license.php/
13 | * @author Lea Verou http://lea.verou.me
14 | */
15 |
16 | var Prism = (function(){
17 |
18 | // Private helper vars
19 | var lang = /\blang(?:uage)?-(?!\*)(\w+)\b/i;
20 |
21 | var _ = self.Prism = {
22 | util: {
23 | encode: function (tokens) {
24 | if (tokens instanceof Token) {
25 | return new Token(tokens.type, _.util.encode(tokens.content), tokens.alias);
26 | } else if (_.util.type(tokens) === 'Array') {
27 | return tokens.map(_.util.encode);
28 | } else {
29 | return tokens.replace(/&/g, '&').replace(/ text.length) {
270 | // Something went terribly wrong, ABORT, ABORT!
271 | break tokenloop;
272 | }
273 |
274 | if (str instanceof Token) {
275 | continue;
276 | }
277 |
278 | pattern.lastIndex = 0;
279 |
280 | var match = pattern.exec(str);
281 |
282 | if (match) {
283 | if(lookbehind) {
284 | lookbehindLength = match[1].length;
285 | }
286 |
287 | var from = match.index - 1 + lookbehindLength,
288 | match = match[0].slice(lookbehindLength),
289 | len = match.length,
290 | to = from + len,
291 | before = str.slice(0, from + 1),
292 | after = str.slice(to + 1);
293 |
294 | var args = [i, 1];
295 |
296 | if (before) {
297 | args.push(before);
298 | }
299 |
300 | var wrapped = new Token(token, inside? _.tokenize(match, inside) : match, alias);
301 |
302 | args.push(wrapped);
303 |
304 | if (after) {
305 | args.push(after);
306 | }
307 |
308 | Array.prototype.splice.apply(strarr, args);
309 | }
310 | }
311 | }
312 | }
313 |
314 | return strarr;
315 | },
316 |
317 | hooks: {
318 | all: {},
319 |
320 | add: function (name, callback) {
321 | var hooks = _.hooks.all;
322 |
323 | hooks[name] = hooks[name] || [];
324 |
325 | hooks[name].push(callback);
326 | },
327 |
328 | run: function (name, env) {
329 | var callbacks = _.hooks.all[name];
330 |
331 | if (!callbacks || !callbacks.length) {
332 | return;
333 | }
334 |
335 | for (var i=0, callback; callback = callbacks[i++];) {
336 | callback(env);
337 | }
338 | }
339 | }
340 | };
341 |
342 | var Token = _.Token = function(type, content, alias) {
343 | this.type = type;
344 | this.content = content;
345 | this.alias = alias;
346 | };
347 |
348 | Token.stringify = function(o, language, parent) {
349 | if (typeof o == 'string') {
350 | return o;
351 | }
352 |
353 | if (_.util.type(o) === 'Array') {
354 | return o.map(function(element) {
355 | return Token.stringify(element, language, o);
356 | }).join('');
357 | }
358 |
359 | var env = {
360 | type: o.type,
361 | content: Token.stringify(o.content, language, parent),
362 | tag: 'span',
363 | classes: ['token', o.type],
364 | attributes: {},
365 | language: language,
366 | parent: parent
367 | };
368 |
369 | if (env.type == 'comment') {
370 | env.attributes['spellcheck'] = 'true';
371 | }
372 |
373 | if (o.alias) {
374 | var aliases = _.util.type(o.alias) === 'Array' ? o.alias : [o.alias];
375 | Array.prototype.push.apply(env.classes, aliases);
376 | }
377 |
378 | _.hooks.run('wrap', env);
379 |
380 | var attributes = '';
381 |
382 | for (var name in env.attributes) {
383 | attributes += name + '="' + (env.attributes[name] || '') + '"';
384 | }
385 |
386 | return '<' + env.tag + ' class="' + env.classes.join(' ') + '" ' + attributes + '>' + env.content + '' + env.tag + '>';
387 |
388 | };
389 |
390 | if (!self.document) {
391 | if (!self.addEventListener) {
392 | // in Node.js
393 | return self.Prism;
394 | }
395 | // In worker
396 | self.addEventListener('message', function(evt) {
397 | var message = JSON.parse(evt.data),
398 | lang = message.language,
399 | code = message.code;
400 |
401 | self.postMessage(JSON.stringify(_.util.encode(_.tokenize(code, _.languages[lang]))));
402 | self.close();
403 | }, false);
404 |
405 | return self.Prism;
406 | }
407 |
408 | // Get current script and highlight
409 | var script = document.getElementsByTagName('script');
410 |
411 | script = script[script.length - 1];
412 |
413 | if (script) {
414 | _.filename = script.src;
415 |
416 | if (document.addEventListener && !script.hasAttribute('data-manual')) {
417 | document.addEventListener('DOMContentLoaded', _.highlightAll);
418 | }
419 | }
420 |
421 | return self.Prism;
422 |
423 | })();
424 |
425 | if (typeof module !== 'undefined' && module.exports) {
426 | module.exports = Prism;
427 | }
428 | ;
429 | Prism.languages.markup = {
430 | 'comment': //,
431 | 'prolog': /<\?.+?\?>/,
432 | 'doctype': //,
433 | 'cdata': //i,
434 | 'tag': {
435 | pattern: /<\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|[^\s'">=]+))?\s*)*\/?>/i,
436 | inside: {
437 | 'tag': {
438 | pattern: /^<\/?[\w:-]+/i,
439 | inside: {
440 | 'punctuation': /^<\/?/,
441 | 'namespace': /^[\w-]+?:/
442 | }
443 | },
444 | 'attr-value': {
445 | pattern: /=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i,
446 | inside: {
447 | 'punctuation': /=|>|"/
448 | }
449 | },
450 | 'punctuation': /\/?>/,
451 | 'attr-name': {
452 | pattern: /[\w:-]+/,
453 | inside: {
454 | 'namespace': /^[\w-]+?:/
455 | }
456 | }
457 |
458 | }
459 | },
460 | 'entity': /?[\da-z]{1,8};/i
461 | };
462 |
463 | // Plugin to make entity title show the real entity, idea by Roman Komarov
464 | Prism.hooks.add('wrap', function(env) {
465 |
466 | if (env.type === 'entity') {
467 | env.attributes['title'] = env.content.replace(/&/, '&');
468 | }
469 | });
470 | ;
471 | Prism.languages.css = {
472 | 'comment': /\/\*[\w\W]*?\*\//,
473 | 'atrule': {
474 | pattern: /@[\w-]+?.*?(;|(?=\s*\{))/i,
475 | inside: {
476 | 'punctuation': /[;:]/
477 | }
478 | },
479 | 'url': /url\((?:(["'])(\\\n|\\?.)*?\1|.*?)\)/i,
480 | 'selector': /[^\{\}\s][^\{\};]*(?=\s*\{)/,
481 | 'string': /("|')(\\\n|\\?.)*?\1/,
482 | 'property': /(\b|\B)[\w-]+(?=\s*:)/i,
483 | 'important': /\B!important\b/i,
484 | 'punctuation': /[\{\};:]/,
485 | 'function': /[-a-z0-9]+(?=\()/i
486 | };
487 |
488 | if (Prism.languages.markup) {
489 | Prism.languages.insertBefore('markup', 'tag', {
490 | 'style': {
491 | pattern: /