Fuse-box client scripts integration + deps update

This commit is contained in:
NGPixel 2017-04-01 17:07:01 -04:00
parent f6c519c5dc
commit fe0c4ce0c0
23 changed files with 32342 additions and 64195 deletions

47234
assets/js/bundle.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -8,8 +8,6 @@ switch (logic) {
require('./js/login.js')
break
default:
require('./node_modules/highlight.js/styles/tomorrow.css')
require('./node_modules/simplemde/dist/simplemde.min.css')
require('./scss/app.scss')
require('./js/app.js')
break

View File

@ -2,14 +2,14 @@
/* global alertsData */
import jQuery from 'jquery'
import $ from 'jquery'
import _ from 'lodash'
import Sticky from 'sticky-js'
import io from 'socket.io-client'
import Alerts from './components/alerts.js'
/* eslint-disable spaced-comment */
import 'jquery-smooth-scroll'
import Sticky from 'sticky-js'
jQuery(document).ready(function ($) {
$(() => {
// ====================================
// Scroll
// ====================================
@ -45,24 +45,17 @@ jQuery(document).ready(function ($) {
// Establish WebSocket connection
// ====================================
var socket = io(window.location.origin) // eslint-disable-line no-unused-vars
var socket = io(window.location.origin)
//=include components/search.js
require('./components/search.js')(socket)
// ====================================
// Pages logic
// ====================================
//=include pages/view.js
//=include pages/create.js
//=include pages/edit.js
//=include pages/source.js
//=include pages/admin.js
require('./pages/view.js')(alerts)
// require('./pages/create.js')
require('./pages/edit.js')(alerts, socket)
require('./pages/source.js')(alerts)
require('./pages/admin.js')(alerts)
})
//=include helpers/form.js
//=include helpers/pages.js
//=include components/alerts.js
/* eslint-enable spaced-comment */

View File

@ -1,6 +1,12 @@
/* global $, Vue, ace, mde, _ */
'use strict'
import $ from 'jquery'
import Vue from 'vue'
import _ from 'lodash'
import * as ace from 'brace'
import 'brace/theme/tomorrow_night'
import 'brace/mode/markdown'
let modelist = ace.require('ace/ext/modelist')
let codeEditor = null
// ACE - Mode Loader
@ -24,7 +30,9 @@ let loadAceMode = (m) => {
// Vue Code Block instance
let vueCodeBlock = new Vue({
module.exports = (mde, mdeModalOpenState) => {
let modelist = {} // ace.require('ace/ext/modelist')
let vueCodeBlock = new Vue({
el: '#modal-editor-codeblock',
data: {
modes: modelist.modesByName,
@ -72,4 +80,6 @@ let vueCodeBlock = new Vue({
vueCodeBlock.cancel()
}
}
})
})
return vueCodeBlock
}

View File

@ -1,6 +1,13 @@
/* global $, Vue, _, alerts, mde, socket */
'use strict'
let vueFile = new Vue({
import $ from 'jquery'
import Vue from 'vue'
import _ from 'lodash'
import 'jquery-contextmenu'
import 'jquery-simple-upload'
module.exports = (alerts, mde, mdeModalOpenState, socket) => {
let vueFile = new Vue({
el: '#modal-editor-file',
data: {
isLoading: false,
@ -289,9 +296,9 @@ let vueFile = new Vue({
}
}
})
})
$('#btn-editor-file-upload input').on('change', (ev) => {
$('#btn-editor-file-upload input').on('change', (ev) => {
let curFileAmount = vueFile.files.length
$(ev.currentTarget).simpleUpload('/uploads/file', {
@ -349,4 +356,6 @@ $('#btn-editor-file-upload input').on('change', (ev) => {
}
})
})
})
return vueFile
}

View File

@ -1,6 +1,13 @@
/* global $, Vue, mde, _, alerts, socket */
'use strict'
let vueImage = new Vue({
import $ from 'jquery'
import Vue from 'vue'
import _ from 'lodash'
import 'jquery-contextmenu'
import 'jquery-simple-upload'
module.exports = (alerts, mde, mdeModalOpenState, socket) => {
let vueImage = new Vue({
el: '#modal-editor-image',
data: {
isLoading: false,
@ -27,12 +34,12 @@ let vueImage = new Vue({
methods: {
open: () => {
mdeModalOpenState = true // eslint-disable-line no-undef
mdeModalOpenState = true
$('#modal-editor-image').addClass('is-active')
vueImage.refreshFolders()
},
cancel: (ev) => {
mdeModalOpenState = false // eslint-disable-line no-undef
mdeModalOpenState = false
$('#modal-editor-image').removeClass('is-active')
},
@ -332,9 +339,9 @@ let vueImage = new Vue({
}
}
})
})
$('#btn-editor-image-upload input').on('change', (ev) => {
$('#btn-editor-image-upload input').on('change', (ev) => {
let curImageAmount = vueImage.images.length
$(ev.currentTarget).simpleUpload('/uploads/img', {
@ -394,4 +401,6 @@ $('#btn-editor-image-upload input').on('change', (ev) => {
}
})
})
})
return vueImage
}

View File

@ -1,4 +1,8 @@
/* global $, Vue, mde, _ */
'use strict'
import $ from 'jquery'
import Vue from 'vue'
import _ from 'lodash'
const videoRules = {
'youtube': new RegExp(/(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|&v(?:i)?=))([^#&?]*).*/, 'i'),
@ -6,9 +10,10 @@ const videoRules = {
'dailymotion': new RegExp(/(?:dailymotion\.com(?:\/embed)?(?:\/video|\/hub)|dai\.ly)\/([0-9a-z]+)(?:[-_0-9a-zA-Z]+(?:#video=)?([a-z0-9]+)?)?/, 'i')
}
// Vue Video instance
module.exports = (mde, mdeModalOpenState) => {
// Vue Video instance
let vueVideo = new Vue({
let vueVideo = new Vue({
el: '#modal-editor-video',
data: {
link: ''
@ -45,4 +50,6 @@ let vueVideo = new Vue({
vueVideo.cancel()
}
}
})
})
return vueVideo
}

View File

@ -1,27 +1,29 @@
'use strict'
/* global $, Vue, _, filesize, SimpleMDE, alerts, vueImage, vueFile, vueVideo, vueCodeBlock */
import $ from 'jquery'
import Vue from 'vue'
import _ from 'lodash'
import filesize from 'filesize.js'
import SimpleMDE from 'simplemde'
// ====================================
// Markdown Editor
// ====================================
if ($('#mk-editor').length === 1) {
let mdeModalOpenState = false
let mdeCurrentEditor = null // eslint-disable-line no-unused-vars
module.exports = (alerts, pageEntryPath, socket) => {
if ($('#mk-editor').length === 1) {
Vue.filter('filesize', (v) => {
return _.toUpper(filesize(v))
})
/* eslint-disable spaced-comment */
//=include editor-image.js
//=include editor-file.js
//=include editor-video.js
//=include editor-codeblock.js
/* eslint-enable spaced-comment */
let mde
let mdeModalOpenState = false
let vueImage = require('./editor-image.js')(alerts, mde, mdeModalOpenState, socket)
let vueFile = require('./editor-file.js')(alerts, mde, mdeModalOpenState, socket)
let vueVideo = require('./editor-video.js')(mde, mdeModalOpenState)
let vueCodeBlock = require('./editor-codeblock.js')(mde, mdeModalOpenState)
var mde = new SimpleMDE({
mde = new SimpleMDE({
autofocus: true,
autoDownloadFontAwesome: false,
element: $('#mk-editor').get(0),
@ -217,4 +219,5 @@ if ($('#mk-editor').length === 1) {
}
}
})
}
}

View File

@ -1,8 +1,11 @@
'use strict'
/* global $, Vue, _, socket */
import $ from 'jquery'
import _ from 'lodash'
import Vue from 'vue'
if ($('#search-input').length) {
module.exports = (socket) => {
if ($('#search-input').length) {
$('#search-input').focus()
$('.searchresults').css('display', 'block')
@ -80,4 +83,5 @@ if ($('#search-input').length) {
})
$('main').on('click', vueHeader.closeSearch)
}
}

View File

@ -1,6 +1,13 @@
/* eslint-disable no-unused-vars */
'use strict'
function setInputSelection (input, startPos, endPos) {
module.exports = {
/**
* Set Input Selection
* @param {DOMElement} input The input element
* @param {number} startPos The starting position
* @param {nunber} endPos The ending position
*/
setInputSelection: (input, startPos, endPos) => {
input.focus()
if (typeof input.selectionStart !== 'undefined') {
input.selectionStart = startPos
@ -14,6 +21,5 @@ function setInputSelection (input, startPos, endPos) {
range.moveStart('character', startPos)
range.select()
}
}
}
/* eslint-enable no-unused-vars */

View File

@ -1,13 +1,19 @@
/* global _ */
/* eslint-disable no-unused-vars */
'use strict'
function makeSafePath (rawPath) {
import _ from 'lodash'
module.exports = {
/**
* Convert raw path to safe path
* @param {string} rawPath Raw path
* @returns {string} Safe path
*/
makeSafePath: (rawPath) => {
let rawParts = _.split(_.trim(rawPath), '/')
rawParts = _.map(rawParts, (r) => {
return _.kebabCase(_.deburr(_.trim(r)))
})
return _.join(_.filter(rawParts, (r) => { return !_.isEmpty(r) }), '/')
}
}
/* eslint-enable no-unused-vars */

View File

@ -1,8 +1,12 @@
/* global $, Vue, alerts */
'use strict'
import $ from 'jquery'
import Vue from 'vue'
// Vue Create User instance
let vueCreateUser = new Vue({
module.exports = (alerts) => {
let vueCreateUser = new Vue({
el: '#modal-admin-users-create',
data: {
email: '',
@ -46,6 +50,7 @@ let vueCreateUser = new Vue({
})
}
}
})
})
$('.btn-create-prompt').on('click', vueCreateUser.open)
$('.btn-create-prompt').on('click', vueCreateUser.open)
}

View File

@ -1,8 +1,16 @@
/* global $, Vue, usrData, alerts */
'use strict'
/* global usrData */
'use strict'
import $ from 'jquery'
import Vue from 'vue'
// Vue Delete User instance
let vueDeleteUser = new Vue({
module.exports = (alerts) => {
let vueDeleteUser = new Vue({
el: '#modal-admin-users-delete',
data: {
loading: false
@ -29,6 +37,7 @@ let vueDeleteUser = new Vue({
})
}
}
})
})
$('.btn-deluser-prompt').on('click', vueDeleteUser.open)
$('.btn-deluser-prompt').on('click', vueDeleteUser.open)
}

View File

@ -1,28 +1,35 @@
/* global $, _, currentBasePath */
'use strict'
import $ from 'jquery'
import _ from 'lodash'
import { setInputSelection } from '../helpers/form'
import { makeSafePath } from '../helpers/pages'
// -> Create New Document
let suggestedCreatePath = currentBasePath + '/new-page'
module.exports = (currentBasePath) => {
let suggestedCreatePath = currentBasePath + '/new-page'
$('.btn-create-prompt').on('click', (ev) => {
$('.btn-create-prompt').on('click', (ev) => {
$('#txt-create-prompt').val(suggestedCreatePath)
$('#modal-create-prompt').toggleClass('is-active')
setInputSelection($('#txt-create-prompt').get(0), currentBasePath.length + 1, suggestedCreatePath.length) // eslint-disable-line no-undef
setInputSelection($('#txt-create-prompt').get(0), currentBasePath.length + 1, suggestedCreatePath.length)
$('#txt-create-prompt').removeClass('is-danger').next().addClass('is-hidden')
})
})
$('#txt-create-prompt').on('keypress', (ev) => {
$('#txt-create-prompt').on('keypress', (ev) => {
if (ev.which === 13) {
$('.btn-create-go').trigger('click')
}
})
})
$('.btn-create-go').on('click', (ev) => {
let newDocPath = makeSafePath($('#txt-create-prompt').val()) // eslint-disable-line no-undef
$('.btn-create-go').on('click', (ev) => {
let newDocPath = makeSafePath($('#txt-create-prompt').val())
if (_.isEmpty(newDocPath)) {
$('#txt-create-prompt').addClass('is-danger').next().removeClass('is-hidden')
} else {
$('#txt-create-prompt').parent().addClass('is-loading')
window.location.assign('/create/' + newDocPath)
}
})
})
}

View File

@ -1,28 +1,34 @@
/* global $, _, alerts, currentBasePath */
'use strict'
import $ from 'jquery'
import _ from 'lodash'
import { makeSafePath } from '../helpers/form'
import { setInputSelection } from '../helpers/pages'
// -> Move Existing Document
if (currentBasePath !== '') {
module.exports = (currentBasePath, alerts) => {
if (currentBasePath !== '') {
$('.btn-move-prompt').removeClass('is-hidden')
}
}
let moveInitialDocument = _.lastIndexOf(currentBasePath, '/') + 1
let moveInitialDocument = _.lastIndexOf(currentBasePath, '/') + 1
$('.btn-move-prompt').on('click', (ev) => {
$('.btn-move-prompt').on('click', (ev) => {
$('#txt-move-prompt').val(currentBasePath)
$('#modal-move-prompt').toggleClass('is-active')
setInputSelection($('#txt-move-prompt').get(0), moveInitialDocument, currentBasePath.length) // eslint-disable-line no-undef
setInputSelection($('#txt-move-prompt').get(0), moveInitialDocument, currentBasePath.length)
$('#txt-move-prompt').removeClass('is-danger').next().addClass('is-hidden')
})
})
$('#txt-move-prompt').on('keypress', (ev) => {
$('#txt-move-prompt').on('keypress', (ev) => {
if (ev.which === 13) {
$('.btn-move-go').trigger('click')
}
})
})
$('.btn-move-go').on('click', (ev) => {
let newDocPath = makeSafePath($('#txt-move-prompt').val()) // eslint-disable-line no-undef
$('.btn-move-go').on('click', (ev) => {
let newDocPath = makeSafePath($('#txt-move-prompt').val())
if (_.isEmpty(newDocPath) || newDocPath === currentBasePath || newDocPath === 'home') {
$('#txt-move-prompt').addClass('is-danger').next().removeClass('is-hidden')
} else {
@ -44,4 +50,5 @@ $('.btn-move-go').on('click', (ev) => {
alerts.pushError('Something went wrong', 'Save operation failed.')
})
}
})
})
}

View File

@ -1,6 +1,13 @@
/* global $, Vue, alerts, _, usrData, usrDataName */
'use strict'
if ($('#page-type-admin-profile').length) {
/* global usrData, usrDataName */
import $ from 'jquery'
import _ from 'lodash'
import Vue from 'vue'
module.exports = (alerts) => {
if ($('#page-type-admin-profile').length) {
let vueProfile = new Vue({
el: '#page-type-admin-profile',
data: {
@ -28,13 +35,9 @@ if ($('#page-type-admin-profile').length) {
this.name = usrDataName
}
})
} else if ($('#page-type-admin-users').length) {
/* eslint-disable spaced-comment */
//=include ../modals/admin-users-create.js
/* eslint-enable spaced-comment */
} else if ($('#page-type-admin-users-edit').length) {
} else if ($('#page-type-admin-users').length) {
require('../modals/admin-users-create.js')(alerts)
} else if ($('#page-type-admin-users-edit').length) {
let vueEditUser = new Vue({
el: '#page-type-admin-users-edit',
data: {
@ -94,11 +97,8 @@ if ($('#page-type-admin-profile').length) {
}
}
})
/* eslint-disable spaced-comment */
//=include ../modals/admin-users-delete.js
/* eslint-enable spaced-comment */
} else if ($('#page-type-admin-settings').length) {
require('../modals/admin-users-delete.js')(alerts)
} else if ($('#page-type-admin-settings').length) {
let vueSettings = new Vue({ // eslint-disable-line no-unused-vars
el: '#page-type-admin-settings',
data: {
@ -145,4 +145,5 @@ if ($('#page-type-admin-profile').length) {
}
}
})
}
}

View File

@ -1,7 +1,10 @@
/* global $ */
'use strict'
if ($('#page-type-edit').length) {
let pageEntryPath = $('#page-type-edit').data('entrypath') // eslint-disable-line no-unused-vars
import $ from 'jquery'
module.exports = (alerts, socket) => {
if ($('#page-type-edit').length) {
let pageEntryPath = $('#page-type-edit').data('entrypath')
// let pageCleanExit = false
// -> Discard
@ -14,7 +17,6 @@ if ($('#page-type-edit').length) {
// return (pageCleanExit) ? true : 'Unsaved modifications will be lost. Are you sure you want to navigate away from this page?'
// }
/* eslint-disable spaced-comment */
//=include ../components/editor.js
/* eslint-enable spaced-comment */
require('../components/editor.js')(alerts, pageEntryPath, socket)
}
}

View File

@ -1,6 +1,12 @@
/* global $, ace */
'use strict'
if ($('#page-type-source').length) {
import $ from 'jquery'
import * as ace from 'brace'
import 'brace/theme/tomorrow_night'
import 'brace/mode/markdown'
module.exports = (alerts) => {
if ($('#page-type-source').length) {
var scEditor = ace.edit('source-display')
scEditor.setTheme('ace/theme/tomorrow_night')
scEditor.getSession().setMode('ace/mode/markdown')
@ -10,10 +16,9 @@ if ($('#page-type-source').length) {
scEditor.setReadOnly(true)
scEditor.renderer.updateFull()
let currentBasePath = ($('#page-type-source').data('entrypath') !== 'home') ? $('#page-type-source').data('entrypath') : '' // eslint-disable-line no-unused-vars
let currentBasePath = ($('#page-type-source').data('entrypath') !== 'home') ? $('#page-type-source').data('entrypath') : ''
/* eslint-disable spaced-comment */
//=include ../modals/create.js
//=include ../modals/move.js
/* eslint-enable spaced-comment */
require('../modals/create.js')(currentBasePath)
require('../modals/move.js')(currentBasePath, alerts)
}
}

View File

@ -1,10 +1,12 @@
/* global $ */
'use strict'
if ($('#page-type-view').length) {
let currentBasePath = ($('#page-type-view').data('entrypath') !== 'home') ? $('#page-type-view').data('entrypath') : '' // eslint-disable-line no-unused-vars
import $ from 'jquery'
/* eslint-disable spaced-comment */
//=include ../modals/create.js
//=include ../modals/move.js
/* eslint-enable spaced-comment */
module.exports = (alerts) => {
if ($('#page-type-view').length) {
let currentBasePath = ($('#page-type-view').data('entrypath') !== 'home') ? $('#page-type-view').data('entrypath') : ''
require('../modals/create.js')(currentBasePath)
require('../modals/move.js')(currentBasePath, alerts)
}
}

View File

@ -19,6 +19,8 @@ $primary: 'indigo';
@import './libs/twemoji-awesome';
@import './libs/jquery-contextmenu';
@import 'node_modules/highlight.js/styles/tomorrow';
@import 'node_modules/simplemde/dist/simplemde.min';
@import './components/_editor';

35
fuse.js
View File

@ -27,6 +27,21 @@ const args = require('yargs')
.alias('h', 'help')
.argv
// Define aliases
const ALIASES = {
'ace': 'ace-builds/src-min-noconflict/ace.js',
'simplemde': 'simplemde/dist/simplemde.min.js',
'socket.io-client': 'socket.io-client/dist/socket.io.min.js',
'vue': 'vue/dist/vue.js'
}
const SHIMS = {
jquery: {
source: 'node_modules/jquery/dist/jquery.js',
exports: '$'
}
}
if (args.d) {
// =============================================
// DEVELOPER MODE
@ -41,9 +56,8 @@ if (args.d) {
const fuse = fsbx.FuseBox.init({
homeDir: './client',
outFile: './assets/js/bundle.min.js',
alias: {
vue: 'vue/dist/vue.js'
},
alias: ALIASES,
shim: SHIMS,
plugins: [
[ fsbx.SassPlugin({ includePaths: ['../core'] }), fsbx.CSSPlugin() ],
fsbx.BabelPlugin({ comments: false, presets: ['es2015'] }),
@ -55,7 +69,8 @@ if (args.d) {
fuse.devServer('>index.js', {
port: 4444,
httpServer: false
httpServer: false,
hmr: false
})
// Server
@ -80,7 +95,7 @@ if (args.d) {
}, 1000)
} else if (args.c) {
// =============================================
// DEVELOPER MODE
// CONFIGURE - DEVELOPER MODE
// =============================================
console.info(colors.bgWhite.black(' Starting Fuse in CONFIGURE DEVELOPER mode... '))
@ -92,9 +107,8 @@ if (args.d) {
const fuse = fsbx.FuseBox.init({
homeDir: './client',
outFile: './assets/js/configure.min.js',
alias: {
vue: 'vue/dist/vue.js'
},
alias: ALIASES,
shim: SHIMS,
plugins: [
[ fsbx.SassPlugin({ includePaths: ['../core'] }), fsbx.CSSPlugin() ],
fsbx.BabelPlugin({ comments: false, presets: ['es2015'] }),
@ -131,9 +145,8 @@ if (args.d) {
const fuse = fsbx.FuseBox.init({
homeDir: './client',
alias: {
vue: 'vue/dist/vue.js'
},
alias: ALIASES,
shim: SHIMS,
plugins: [
[ fsbx.SassPlugin({ outputStyle: 'compressed', includePaths: ['./node_modules/requarks-core'] }), fsbx.CSSPlugin() ],
fsbx.BabelPlugin({

View File

@ -43,13 +43,13 @@
},
"dependencies": {
"auto-load": "^2.1.0",
"axios": "^0.15.3",
"axios": "^0.16.0",
"bcryptjs-then": "^1.0.1",
"bluebird": "^3.4.7",
"body-parser": "^1.17.1",
"bunyan": "^1.8.9",
"cheerio": "^0.22.0",
"child-process-promise": "^2.2.0",
"child-process-promise": "^2.2.1",
"chokidar": "^1.6.0",
"commander": "^2.9.0",
"compression": "^1.6.2",
@ -61,7 +61,7 @@
"express": "^4.15.2",
"express-brute": "^1.0.0",
"express-brute-mongoose": "0.0.7",
"express-session": "^1.15.1",
"express-session": "^1.15.2",
"file-type": "^4.0.0",
"filesize.js": "^1.0.2",
"follow-redirects": "^1.2.3",
@ -85,19 +85,19 @@
"markdown-it-expand-tabs": "^1.0.11",
"markdown-it-external-links": "0.0.6",
"markdown-it-footnote": "^3.0.1",
"markdown-it-task-lists": "^1.4.1",
"markdown-it-task-lists": "^2.0.0",
"memdown": "^1.2.4",
"mime-types": "^2.1.15",
"moment": "^2.18.1",
"moment-timezone": "^0.5.11",
"mongodb": "^2.2.25",
"mongoose": "^4.9.1",
"mongoose": "^4.9.2",
"multer": "^1.2.1",
"ora": "^1.2.0",
"passport": "^0.3.2",
"passport-local": "^1.0.0",
"passport.socketio": "^3.7.0",
"pm2": "^2.4.2",
"pm2": "^2.4.3",
"pug": "^2.0.0-beta11",
"read-chunk": "^2.0.0",
"remove-markdown": "^0.1.0",
@ -119,33 +119,33 @@
"winston": "^2.3.0"
},
"devDependencies": {
"ace-builds": "^1.2.6",
"babel-cli": "^6.24.0",
"babel-jest": "^19.0.0",
"babel-preset-es2015": "^6.24.0",
"brace": "^0.10.0",
"colors": "^1.1.2",
"eslint": "^3.18.0",
"eslint": "^3.19.0",
"eslint-config-standard": "^7.1.0",
"eslint-plugin-import": "^2.2.0",
"eslint-plugin-node": "^4.2.1",
"eslint-plugin-node": "^4.2.2",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-standard": "^2.1.1",
"fuse-box": "^1.3.128",
"fuse-box": "^1.3.129",
"jest": "^19.0.2",
"jquery": "^3.2.1",
"jquery-contextmenu": "^2.4.4",
"jquery-simple-upload": "^1.0.0",
"jquery-smooth-scroll": "^2.0.0",
"node-sass": "^4.5.1",
"node-sass": "^4.5.2",
"nodemon": "^1.11.0",
"pre-commit": "^1.2.2",
"pug-lint": "^2.4.0",
"snyk": "^1.25.1",
"snyk": "^1.26.1",
"standard": "^9.0.2",
"sticky-js": "^1.1.9",
"twemoji-awesome": "^1.0.4",
"vee-validate": "^2.0.0-beta.25",
"vue": "^2.2.5"
"vue": "^2.2.6"
},
"standard": {
"globals": [