diff --git a/.eslintrc.js b/.eslintrc.js index ce7425f4c8892..ba7dcfb6eb87a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -90,6 +90,10 @@ module.exports = { selector: 'CallExpression[callee.name="deprecated"] Property[key.name="version"][value.value=/' + majorMinorRegExp + '/]', message: 'Deprecated functions must be removed before releasing this version.', }, + { + selector: 'CallExpression[callee.name=/^(invokeMap|get|has|hasIn|invoke|result|set|setWith|unset|update|updateWith)$/] > Literal:nth-child(2)', + message: 'Always pass an array as the path argument', + }, ], }, overrides: [ diff --git a/.travis.yml b/.travis.yml index 8e2746581a23e..62311c62d35bc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,7 @@ cache: before_install: - nvm install && nvm use + - npm install npm -g branches: only: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6854f7290508c..9d41a0b83ae87 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,7 +50,7 @@ For example, `add/gallery-block` means you're working on adding a new gallery bl You can pick among all the tickets, or some of the ones labelled Good First Issue. -The workflow is documented in greater detail in the [repository management](./docs/repository-management.md) document. +The workflow is documented in greater detail in the [repository management](./docs/reference/repository-management.md) document. ## Testing diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index f208a1de7a5db..fa308a22c359e 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -93,3 +93,4 @@ This list is manually curated to include valuable contributions by volunteers th | @thrijith | | @Cloud887 | | @hblackett | +| @vishalkakadiya | diff --git a/bin/build-plugin-zip.sh b/bin/build-plugin-zip.sh index 3009bff901c19..691f28b2dc007 100755 --- a/bin/build-plugin-zip.sh +++ b/bin/build-plugin-zip.sh @@ -91,7 +91,7 @@ rm -f gutenberg.zip php bin/generate-gutenberg-php.php > gutenberg.tmp.php mv gutenberg.tmp.php gutenberg.php -build_files=$(ls **/build/*.{js,css}) +build_files=$(ls build/*/*.{js,css}) # Generate the plugin zip file status "Creating archive..." diff --git a/bin/get-vendor-scripts.php b/bin/get-vendor-scripts.php index 681e813409299..c3248152abb72 100755 --- a/bin/get-vendor-scripts.php +++ b/bin/get-vendor-scripts.php @@ -11,11 +11,17 @@ // Hacks to get lib/client-assets.php to load. define( 'ABSPATH', dirname( dirname( __FILE__ ) ) ); + /** * Hi, phpcs */ function add_action() {} +/** + * Hi, phpcs + */ +function wp_add_inline_script() {} + // Instead of loading script files, just show how they need to be loaded. define( 'GUTENBERG_LIST_VENDOR_ASSETS', true ); diff --git a/blocks/README.md b/blocks/README.md index 8ce7772cdeb4c..77a7624ed0a06 100644 --- a/blocks/README.md +++ b/blocks/README.md @@ -263,113 +263,3 @@ Returns type definitions associated with a registered block. Returns settings associated with a registered control. -## Components - -Because many blocks share the same complex behaviors, the following components -are made available to simplify implementations of your block's `edit` function. - -### `BlockControls` - -When returned by your block's `edit` implementation, renders a toolbar of icon -buttons. This is useful for block-level modifications to be made available when -a block is selected. For example, if your block supports alignment, you may -want to display alignment options in the selected block's toolbar. - -Example: - -```js -( function( blocks, element ) { - var el = element.createElement, - BlockControls = blocks.BlockControls, - AlignmentToolbar = blocks.AlignmentToolbar; - - function edit( props ) { - return [ - // Controls: (only visible when block is selected) - el( BlockControls, { key: 'controls' }, - el( AlignmentToolbar, { - value: props.align, - onChange: function( nextAlign ) { - props.setAttributes( { align: nextAlign } ) - } - } ) - ), - - // Block content: (with alignment as attribute) - el( 'p', { key: 'text', style: { textAlign: props.align } }, - 'Hello World!' - ), - ]; - } -} )( - window.wp.blocks, - window.wp.element -); -``` - -Note in this example that we render `AlignmentToolbar` as a child of the -`BlockControls` element. This is another pre-configured component you can use -to simplify block text alignment. - -Alternatively, you can create your own toolbar controls by passing an array of -`controls` as a prop to the `BlockControls` component. Each control should be -an object with the following properties: - -- `icon: string` - Slug of the Dashicon to be shown in the control's toolbar button -- `title: string` - A human-readable localized text to be shown as the tooltip label of the control's button -- `subscript: ?string` - Optional text to be shown adjacent the button icon as subscript (for example, heading levels) -- `isActive: ?boolean` - Whether the control should be considered active / selected. Defaults to `false`. - -To create divisions between sets of controls within the same `BlockControls` -element, passing `controls` instead as a nested array (array of arrays of -objects). A divider will be shown between each set of controls. - -### `RichText` - -Render a rich -[`contenteditable` input](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Editable_content), -providing users the option to add emphasis to content or links to content. It -behaves similarly to a -[controlled component](https://facebook.github.io/react/docs/forms.html#controlled-components), -except that `onChange` is triggered less frequently than would be expected from -a traditional `input` field, usually when the user exits the field. - -The following properties (non-exhaustive list) are made available: - -- `value: string` - Markup value of the field. Only valid markup is - allowed, as determined by `inline` value and available controls. -- `onChange: Function` - Callback handler when the value of the field changes, - passing the new value as its only argument. -- `placeholder: string` - A text hint to be shown to the user when the field - value is empty, similar to the - [`input` and `textarea` attribute of the same name](https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/HTML5_updates#The_placeholder_attribute). -- `multiline: String` - A tag name to use for the tag that should be inserted - when Enter is pressed. For example: `li` in a list block, and `p` for a - block that can contain multiple paragraphs. The default is that only inline - elements are allowed to be used in inserted into the text, effectively - disabling the behavior of the "Enter" key. - -Example: - -```js -( function( blocks, element ) { - var el = element.createElement, - RichText = blocks.RichText; - - function edit( props ) { - function onChange( value ) { - props.setAttributes( { text: value } ); - } - - return el( RichText, { - value: props.attributes.text, - onChange: onChange - } ); - } - - // blocks.registerBlockType( ..., { edit: edit, ... } ); -} )( - window.wp.blocks, - window.wp.element -); -``` diff --git a/blocks/api/index.js b/blocks/api/index.js index 8770b2609a85f..897aba32f3176 100644 --- a/blocks/api/index.js +++ b/blocks/api/index.js @@ -11,7 +11,7 @@ export { getBlockAttributes, parseWithAttributeSchema, } from './parser'; -export { default as rawHandler } from './raw-handling'; +export { default as rawHandler, getPhrasingContentSchema } from './raw-handling'; export { default as serialize, getBlockContent, diff --git a/blocks/api/index.native.js b/blocks/api/index.native.js new file mode 100644 index 0000000000000..3bef99c41e803 --- /dev/null +++ b/blocks/api/index.native.js @@ -0,0 +1,11 @@ +export { + createBlock, +} from './factory'; +export { + default as serialize, + getBlockContent, +} from './serializer'; +export { + registerBlockType, + getBlockType, +} from './registration'; diff --git a/blocks/api/raw-handling/blockquote-normaliser.js b/blocks/api/raw-handling/blockquote-normaliser.js index 58633866f5a84..f08e89791280b 100644 --- a/blocks/api/raw-handling/blockquote-normaliser.js +++ b/blocks/api/raw-handling/blockquote-normaliser.js @@ -3,16 +3,7 @@ */ import normaliseBlocks from './normalise-blocks'; -/** - * Browser dependencies - */ -const { ELEMENT_NODE } = window.Node; - export default function( node ) { - if ( node.nodeType !== ELEMENT_NODE ) { - return; - } - if ( node.nodeName !== 'BLOCKQUOTE' ) { return; } diff --git a/blocks/api/raw-handling/comment-remover.js b/blocks/api/raw-handling/comment-remover.js deleted file mode 100644 index 93b580b5f44ea..0000000000000 --- a/blocks/api/raw-handling/comment-remover.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Browser dependencies - */ -const { COMMENT_NODE } = window.Node; - -export default function( node ) { - if ( node.nodeType !== COMMENT_NODE ) { - return; - } - - node.parentNode.removeChild( node ); -} diff --git a/blocks/api/raw-handling/create-unwrapper.js b/blocks/api/raw-handling/create-unwrapper.js deleted file mode 100644 index 3266cbe62e3d5..0000000000000 --- a/blocks/api/raw-handling/create-unwrapper.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Browser dependencies - */ -const { ELEMENT_NODE } = window.Node; - -function unwrap( node ) { - const parent = node.parentNode; - - while ( node.firstChild ) { - parent.insertBefore( node.firstChild, node ); - } - - parent.removeChild( node ); -} - -export default function( predicate, after ) { - return ( node ) => { - if ( node.nodeType !== ELEMENT_NODE ) { - return; - } - - if ( ! predicate( node ) ) { - return; - } - - const afterNode = after && after( node ); - - if ( afterNode ) { - node.appendChild( afterNode ); - } - - unwrap( node ); - }; -} diff --git a/blocks/api/raw-handling/embedded-content-reducer.js b/blocks/api/raw-handling/embedded-content-reducer.js deleted file mode 100644 index b2740e8b201fb..0000000000000 --- a/blocks/api/raw-handling/embedded-content-reducer.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Internal dependencies - */ -import { isEmbedded } from './utils'; - -/** - * Browser dependencies - */ -const { ELEMENT_NODE } = window.Node; - -/** - * This filter takes embedded content out of paragraphs. - * - * @param {Node} node The node to filter. - * - * @return {void} - */ -export default function( node ) { - if ( node.nodeType !== ELEMENT_NODE ) { - return; - } - - if ( ! isEmbedded( node ) ) { - return; - } - - let nodeToInsert = node; - // if the embedded is an image and its parent is an anchor with just the image - // take the anchor out instead of just the image - if ( - 'IMG' === node.nodeName && - 1 === node.parentNode.childNodes.length && - 'A' === node.parentNode.nodeName - ) { - nodeToInsert = node.parentNode; - } - - let wrapper = nodeToInsert; - - while ( wrapper && wrapper.nodeName !== 'P' ) { - wrapper = wrapper.parentElement; - } - - if ( wrapper ) { - wrapper.parentNode.insertBefore( nodeToInsert, wrapper ); - } -} diff --git a/blocks/api/raw-handling/figure-content-reducer.js b/blocks/api/raw-handling/figure-content-reducer.js new file mode 100644 index 0000000000000..1fa63b080d1c4 --- /dev/null +++ b/blocks/api/raw-handling/figure-content-reducer.js @@ -0,0 +1,88 @@ +/** + * External dependencies + */ +import { has } from 'lodash'; + +/** + * Internal dependencies + */ +import { isPhrasingContent } from './utils'; + +/** + * Whether or not the given node is figure content. + * + * @param {Node} node The node to check. + * @param {Object} schema The schema to use. + * + * @return {boolean} True if figure content, false if not. + */ +function isFigureContent( node, schema ) { + const tag = node.nodeName.toLowerCase(); + + // We are looking for tags that can be a child of the figure tag, excluding + // `figcaption` and any phrasing content. + if ( tag === 'figcaption' || isPhrasingContent( node ) ) { + return false; + } + + return has( schema, [ 'figure', 'children', tag ] ); +} + +/** + * Whether or not the given node can have an anchor. + * + * @param {Node} node The node to check. + * @param {Object} schema The schema to use. + * + * @return {boolean} True if it can, false if not. + */ +function canHaveAnchor( node, schema ) { + const tag = node.nodeName.toLowerCase(); + + return has( schema, [ 'figure', 'children', 'a', 'children', tag ] ); +} + +/** + * This filter takes figure content out of paragraphs, wraps it in a figure + * element, and moves any anchors with it if needed. + * + * @param {Node} node The node to filter. + * @param {Document} doc The document of the node. + * @param {Object} schema The schema to use. + * + * @return {void} + */ +export default function( node, doc, schema ) { + if ( ! isFigureContent( node, schema ) ) { + return; + } + + let nodeToInsert = node; + const parentNode = node.parentNode; + + // If the figure content can have an anchor and its parent is an anchor with + // only the figure content, take the anchor out instead of just the content. + if ( + canHaveAnchor( node, schema ) && + parentNode.nodeName === 'A' && + parentNode.childNodes.length === 1 + ) { + nodeToInsert = node.parentNode; + } + + let wrapper = nodeToInsert; + + while ( wrapper && wrapper.nodeName !== 'P' ) { + wrapper = wrapper.parentElement; + } + + const figure = doc.createElement( 'figure' ); + + if ( wrapper ) { + wrapper.parentNode.insertBefore( figure, wrapper ); + } else { + nodeToInsert.parentNode.insertBefore( figure, nodeToInsert ); + } + + figure.appendChild( nodeToInsert ); +} diff --git a/blocks/api/raw-handling/formatting-transformer.js b/blocks/api/raw-handling/formatting-transformer.js deleted file mode 100644 index 1b12810d9d913..0000000000000 --- a/blocks/api/raw-handling/formatting-transformer.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Browser dependencies - */ -const { ELEMENT_NODE } = window.Node; - -function replace( node, tagName ) { - const newNode = document.createElement( tagName ); - - while ( node.firstChild ) { - newNode.appendChild( node.firstChild ); - } - - node.parentNode.replaceChild( newNode, node ); -} - -export default function( node ) { - if ( node.nodeType !== ELEMENT_NODE ) { - return; - } - - if ( node.nodeName === 'SPAN' ) { - const fontWeight = node.style.fontWeight; - const fontStyle = node.style.fontStyle; - - if ( fontWeight === 'bold' || fontWeight === '700' ) { - replace( node, 'strong' ); - } else if ( fontStyle === 'italic' ) { - replace( node, 'em' ); - } - } - - if ( node.nodeName === 'B' ) { - replace( node, 'strong' ); - } - - if ( node.nodeName === 'I' ) { - replace( node, 'em' ); - } -} diff --git a/blocks/api/raw-handling/iframe-remover.js b/blocks/api/raw-handling/iframe-remover.js new file mode 100644 index 0000000000000..31733fa042853 --- /dev/null +++ b/blocks/api/raw-handling/iframe-remover.js @@ -0,0 +1,17 @@ +/** + * WordPress dependencies + */ +import { remove } from '@wordpress/utils'; + +/** + * Removes iframes. + * + * @param {Node} node The node to check. + * + * @return {void} + */ +export default function( node ) { + if ( node.nodeName === 'IFRAME' ) { + remove( node ); + } +} diff --git a/blocks/api/raw-handling/image-corrector.js b/blocks/api/raw-handling/image-corrector.js index 142cfdcdd8994..c6266c4d99db8 100644 --- a/blocks/api/raw-handling/image-corrector.js +++ b/blocks/api/raw-handling/image-corrector.js @@ -7,13 +7,8 @@ import { createBlobURL } from '@wordpress/utils'; * Browser dependencies */ const { atob, Blob } = window; -const { ELEMENT_NODE } = window.Node; export default function( node ) { - if ( node.nodeType !== ELEMENT_NODE ) { - return; - } - if ( node.nodeName !== 'IMG' ) { return; } diff --git a/blocks/api/raw-handling/index.js b/blocks/api/raw-handling/index.js index ecf0c5786d63e..453327a639f9f 100644 --- a/blocks/api/raw-handling/index.js +++ b/blocks/api/raw-handling/index.js @@ -1,32 +1,69 @@ /** * External dependencies */ -import { compact } from 'lodash'; -import showdown from 'showdown'; +import { flatMap, filter, compact } from 'lodash'; +// Also polyfills Element#matches. +import 'element-closest'; /** * Internal dependencies */ import { createBlock, getBlockTransforms, findTransform } from '../factory'; -import { getBlockType, getUnknownTypeHandlerName } from '../registration'; +import { getBlockType } from '../registration'; import { getBlockAttributes, parseWithGrammar } from '../parser'; import normaliseBlocks from './normalise-blocks'; -import stripAttributes from './strip-attributes'; import specialCommentConverter from './special-comment-converter'; -import commentRemover from './comment-remover'; -import createUnwrapper from './create-unwrapper'; import isInlineContent from './is-inline-content'; -import formattingTransformer from './formatting-transformer'; +import phrasingContentReducer from './phrasing-content-reducer'; import msListConverter from './ms-list-converter'; import listReducer from './list-reducer'; import imageCorrector from './image-corrector'; import blockquoteNormaliser from './blockquote-normaliser'; -import tableNormaliser from './table-normaliser'; -import inlineContentConverter from './inline-content-converter'; -import embeddedContentReducer from './embedded-content-reducer'; -import { deepFilterHTML, isInvalidInline, isNotWhitelisted, isPlain, isInline } from './utils'; +import figureContentReducer from './figure-content-reducer'; import shortcodeConverter from './shortcode-converter'; -import slackMarkdownVariantCorrector from './slack-markdown-variant-corrector'; +import markdownConverter from './markdown-converter'; +import iframeRemover from './iframe-remover'; +import { + deepFilterHTML, + isPlain, + removeInvalidHTML, + getPhrasingContentSchema, + getBlockContentSchema, +} from './utils'; + +/** + * Browser dependencies + */ +const { log, warn } = window.console; + +export { getPhrasingContentSchema }; + +/** + * Filters HTML to only contain phrasing content. + * + * @param {string} HTML The HTML to filter. + * + * @return {string} HTML only containing phrasing content. + */ +function filterInlineHTML( HTML ) { + HTML = deepFilterHTML( HTML, [ phrasingContentReducer ] ); + HTML = removeInvalidHTML( HTML, getPhrasingContentSchema(), { inline: true } ); + + // Allows us to ask for this information when we get a report. + log( 'Processed inline HTML:\n\n', HTML ); + + return HTML; +} + +function getRawTransformations() { + return filter( getBlockTransforms( 'from' ), { type: 'raw' } ) + .map( ( transform ) => { + return transform.isMatch ? transform : { + ...transform, + isMatch: ( node ) => transform.selector && node.matches( transform.selector ), + }; + } ); +} /** * Converts an HTML string to known blocks. Strips everything else. @@ -38,7 +75,7 @@ import slackMarkdownVariantCorrector from './slack-markdown-variant-corrector'; * * 'INLINE': Always handle as inline content, and return string. * * 'BLOCKS': Always handle as blocks, and return array of blocks. * @param {Array} [options.tagName] The tag into which content will be inserted. - * @param {boolean} [options.canUserUseUnfilteredHTML] Whether or not to user can use unfiltered HTML. + * @param {boolean} [options.canUserUseUnfilteredHTML] Whether or not the user can use unfiltered HTML. * * @return {Array|string} A list of blocks or a string, depending on `handlerMode`. */ @@ -55,17 +92,7 @@ export default function rawHandler( { HTML = '', plainText = '', mode = 'AUTO', // * There is a plain text version. // * There is no HTML version, or it has no formatting. if ( plainText && ( ! HTML || isPlain( HTML ) ) ) { - const converter = new showdown.Converter(); - - converter.setOption( 'noHeaderId', true ); - converter.setOption( 'tables', true ); - converter.setOption( 'literalMidWordUnderscores', true ); - converter.setOption( 'omitExtraWLInCodeBlocks', true ); - converter.setOption( 'simpleLineBreaks', true ); - - plainText = slackMarkdownVariantCorrector( plainText ); - - HTML = converter.makeHtml( plainText ); + HTML = markdownConverter( plainText ); // Switch to inline mode if: // * The current mode is AUTO. @@ -82,101 +109,91 @@ export default function rawHandler( { HTML = '', plainText = '', mode = 'AUTO', } } - // An array of HTML strings and block objects. The blocks replace matched shortcodes. + if ( mode === 'INLINE' ) { + return filterInlineHTML( HTML ); + } + + // An array of HTML strings and block objects. The blocks replace matched + // shortcodes. const pieces = shortcodeConverter( HTML ); - // The call to shortcodeConverter will always return more than one element if shortcodes are matched. - // The reason is when shortcodes are matched empty HTML strings are included. + // The call to shortcodeConverter will always return more than one element + // if shortcodes are matched. The reason is when shortcodes are matched + // empty HTML strings are included. const hasShortcodes = pieces.length > 1; - // True if mode is auto, no shortcode is included and HTML verifies the isInlineContent condition - const isAutoModeInline = mode === 'AUTO' && isInlineContent( HTML, tagName ) && ! hasShortcodes; - - // Return filtered HTML if condition is true - if ( mode === 'INLINE' || isAutoModeInline ) { - HTML = deepFilterHTML( HTML, [ - // Add semantic formatting before attributes are stripped. - formattingTransformer, - stripAttributes, - specialCommentConverter, - commentRemover, - createUnwrapper( ( node ) => ! isInline( node, tagName ) ), - ] ); - - // Allows us to ask for this information when we get a report. - window.console.log( 'Processed inline HTML:\n\n', HTML ); - - return HTML; + if ( mode === 'AUTO' && ! hasShortcodes && isInlineContent( HTML, tagName ) ) { + return filterInlineHTML( HTML ); } - // Before we parse any HTML, extract shorcodes so they don't get messed up. - return pieces.reduce( ( accu, piece ) => { + const rawTransformations = getRawTransformations(); + const phrasingContentSchema = getPhrasingContentSchema(); + const blockContentSchema = getBlockContentSchema( rawTransformations ); + + return compact( flatMap( pieces, ( piece ) => { // Already a block from shortcode. if ( typeof piece !== 'string' ) { - return [ ...accu, piece ]; + return piece; } - // Context dependent filters. Needs to run before we remove nodes. - piece = deepFilterHTML( piece, [ + const filters = [ msListConverter, - ] ); - - piece = deepFilterHTML( piece, compact( [ listReducer, imageCorrector, - // Add semantic formatting before attributes are stripped. - formattingTransformer, - stripAttributes, + phrasingContentReducer, specialCommentConverter, - commentRemover, - ! canUserUseUnfilteredHTML && createUnwrapper( ( element ) => element.nodeName === 'IFRAME' ), - embeddedContentReducer, - createUnwrapper( isNotWhitelisted ), + figureContentReducer, blockquoteNormaliser, - tableNormaliser, - inlineContentConverter, - ] ) ); + ]; - piece = deepFilterHTML( piece, [ - createUnwrapper( isInvalidInline ), - ] ); + if ( ! canUserUseUnfilteredHTML ) { + // Should run before `figureContentReducer`. + filters.unshift( iframeRemover ); + } + + const schema = { + ...blockContentSchema, + // Keep top-level phrasing content, normalised by `normaliseBlocks`. + ...phrasingContentSchema, + }; + piece = deepFilterHTML( piece, filters, blockContentSchema ); + piece = removeInvalidHTML( piece, schema ); piece = normaliseBlocks( piece ); // Allows us to ask for this information when we get a report. - window.console.log( 'Processed HTML piece:\n\n', piece ); + log( 'Processed HTML piece:\n\n', piece ); const doc = document.implementation.createHTMLDocument( '' ); doc.body.innerHTML = piece; - const transformsFrom = getBlockTransforms( 'from' ); - - const blocks = Array.from( doc.body.children ).map( ( node ) => { - const transformation = findTransform( transformsFrom, ( transform ) => ( - transform.type === 'raw' && - transform.isMatch( node ) - ) ); - - if ( transformation ) { - if ( transformation.transform ) { - return transformation.transform( node ); - } - - return createBlock( - transformation.blockName, - getBlockAttributes( - getBlockType( transformation.blockName ), - node.outerHTML - ) + return Array.from( doc.body.children ).map( ( node ) => { + const rawTransformation = findTransform( rawTransformations, ( { isMatch } ) => isMatch( node ) ); + + if ( ! rawTransformation ) { + warn( + 'A block registered a raw transformation schema for `' + node.nodeName + '` but did not match it. ' + + 'Make sure there is a `selector` or `isMatch` property that can match the schema.\n' + + 'Sanitized HTML: `' + node.outerHTML + '`' ); + + return; } - return createBlock( getUnknownTypeHandlerName(), { - content: node.outerHTML, - } ); - } ); + const { transform, blockName } = rawTransformation; + + if ( transform ) { + return transform( node ); + } - return [ ...accu, ...blocks ]; - }, [] ); + return createBlock( + blockName, + getBlockAttributes( + getBlockType( blockName ), + node.outerHTML + ) + ); + } ); + } ) ); } diff --git a/blocks/api/raw-handling/inline-content-converter.js b/blocks/api/raw-handling/inline-content-converter.js deleted file mode 100644 index 1d36ebc8fb465..0000000000000 --- a/blocks/api/raw-handling/inline-content-converter.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Browser dependencies - */ -const { ELEMENT_NODE } = window.Node; - -/** - * Internal dependencies - */ -import { isInlineWrapper, isInline, isAllowedBlock, deepFilterNodeList } from './utils'; -import createUnwrapper from './create-unwrapper'; - -export default function( node, doc ) { - if ( node.nodeType !== ELEMENT_NODE ) { - return; - } - - if ( ! isInlineWrapper( node ) ) { - return; - } - - deepFilterNodeList( node.childNodes, [ - createUnwrapper( - ( childNode ) => ! isInline( childNode ) && ! isAllowedBlock( node, childNode ), - ( childNode ) => childNode.nextElementSibling && doc.createElement( 'BR' ) - ), - ], doc ); -} diff --git a/blocks/api/raw-handling/is-inline-content.js b/blocks/api/raw-handling/is-inline-content.js index e53ab5a142417..28d9587f0ab86 100644 --- a/blocks/api/raw-handling/is-inline-content.js +++ b/blocks/api/raw-handling/is-inline-content.js @@ -1,21 +1,58 @@ +/** + * External dependencies + */ +import { difference } from 'lodash'; + /** * Internal dependencies */ -import { isInline, isDoubleBR } from './utils'; +import { isPhrasingContent } from './utils'; + +/** + * Checks if the given node should be considered inline content, optionally + * depending on a context tag. + * + * @param {Node} node Node name. + * @param {string} contextTag Tag name. + * + * @return {boolean} True if the node is inline content, false if nohe. + */ +function isInline( node, contextTag ) { + if ( isPhrasingContent( node ) ) { + return true; + } + + if ( ! contextTag ) { + return false; + } + + const tag = node.nodeName.toLowerCase(); + const inlineWhitelistTagGroups = [ + [ 'ul', 'li', 'ol' ], + [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ], + ]; -export default function( HTML, tagName ) { + return inlineWhitelistTagGroups.some( ( tagGroup ) => + difference( [ tag, contextTag ], tagGroup ).length === 0 + ); +} + +function deepCheck( nodes, contextTag ) { + return nodes.every( ( node ) => + isInline( node, contextTag ) && deepCheck( Array.from( node.children ), contextTag ) + ); +} + +function isDoubleBR( node ) { + return node.nodeName === 'BR' && node.previousSibling && node.previousSibling.nodeName === 'BR'; +} + +export default function( HTML, contextTag ) { const doc = document.implementation.createHTMLDocument( '' ); doc.body.innerHTML = HTML; const nodes = Array.from( doc.body.children ); - return ! nodes.some( isDoubleBR ) && deepCheck( nodes, tagName ); -} - -function deepCheck( nodes, tagName ) { - return nodes.every( ( node ) => { - return ( 'SPAN' === node.nodeName || isInline( node, tagName ) ) && - deepCheck( Array.from( node.children ), tagName ); - } ); + return ! nodes.some( isDoubleBR ) && deepCheck( nodes, contextTag ); } diff --git a/blocks/api/raw-handling/list-reducer.js b/blocks/api/raw-handling/list-reducer.js index 25c33792de606..3182c703eb2ab 100644 --- a/blocks/api/raw-handling/list-reducer.js +++ b/blocks/api/raw-handling/list-reducer.js @@ -1,7 +1,11 @@ /** - * Browser dependencies + * WordPress dependencies */ -const { ELEMENT_NODE } = window.Node; +import { unwrap } from '@wordpress/utils'; + +function isList( node ) { + return node.nodeName === 'OL' || node.nodeName === 'UL'; +} function shallowTextContent( element ) { return [ ...element.childNodes ] @@ -10,13 +14,7 @@ function shallowTextContent( element ) { } export default function( node ) { - if ( node.nodeType !== ELEMENT_NODE ) { - return; - } - - const type = node.nodeName; - - if ( type !== 'OL' && type !== 'UL' ) { + if ( ! isList( node ) ) { return; } @@ -28,7 +26,7 @@ export default function( node ) { // * There is only one list item. if ( prevElement && - prevElement.nodeName === type && + prevElement.nodeName === node.nodeName && list.children.length === 1 ) { prevElement.appendChild( list.firstChild ); @@ -56,4 +54,15 @@ export default function( node ) { parentList.parentNode.removeChild( parentList ); } } + + // Invalid: OL/UL > OL/UL. + if ( parentElement && isList( parentElement ) ) { + const prevListItem = node.previousElementSibling; + + if ( prevListItem ) { + prevListItem.appendChild( node ); + } else { + unwrap( node ); + } + } } diff --git a/blocks/api/raw-handling/markdown-converter.js b/blocks/api/raw-handling/markdown-converter.js new file mode 100644 index 0000000000000..cd0ee5ea4ebfd --- /dev/null +++ b/blocks/api/raw-handling/markdown-converter.js @@ -0,0 +1,42 @@ +/** + * External dependencies + */ +import showdown from 'showdown'; + +// Reuse the same showdown converter. +const converter = new showdown.Converter( { + noHeaderId: true, + tables: true, + literalMidWordUnderscores: true, + omitExtraWLInCodeBlocks: true, + simpleLineBreaks: true, +} ); + +/** + * Corrects the Slack Markdown variant of the code block. + * If uncorrected, it will be converted to inline code. + * + * @see https://get.slack.help/hc/en-us/articles/202288908-how-can-i-add-formatting-to-my-messages-#code-blocks + * + * @param {string} text The potential Markdown text to correct. + * + * @return {string} The corrected Markdown. + */ +function slackMarkdownVariantCorrector( text ) { + return text.replace( + /((?:^|\n)```)([^\n`]+)(```(?:$|\n))/, + ( match, p1, p2, p3 ) => `${ p1 }\n${ p2 }\n${ p3 }` + ); +} + +/** + * Converts a piece of text into HTML based on any Markdown present. + * Also decodes any encoded HTML. + * + * @param {string} text The plain text to convert. + * + * @return {string} HTML. + */ +export default function( text ) { + return converter.makeHtml( slackMarkdownVariantCorrector( text ) ); +} diff --git a/blocks/api/raw-handling/ms-list-converter.js b/blocks/api/raw-handling/ms-list-converter.js index b5408e4ad6e87..0181517493f79 100644 --- a/blocks/api/raw-handling/ms-list-converter.js +++ b/blocks/api/raw-handling/ms-list-converter.js @@ -2,17 +2,12 @@ * Browser dependencies */ const { parseInt } = window; -const { ELEMENT_NODE } = window.Node; function isList( node ) { return node.nodeName === 'OL' || node.nodeName === 'UL'; } -export default function( node ) { - if ( node.nodeType !== ELEMENT_NODE ) { - return; - } - +export default function( node, doc ) { if ( node.nodeName !== 'P' ) { return; } @@ -43,7 +38,7 @@ export default function( node ) { // See https://html.spec.whatwg.org/multipage/grouping-content.html#attr-ol-type. const type = node.textContent.trim().slice( 0, 1 ); const isNumeric = /[1iIaA]/.test( type ); - const newListNode = document.createElement( isNumeric ? 'ol' : 'ul' ); + const newListNode = doc.createElement( isNumeric ? 'ol' : 'ul' ); if ( isNumeric ) { newListNode.setAttribute( 'type', type ); @@ -54,7 +49,7 @@ export default function( node ) { const listNode = node.previousElementSibling; const listType = listNode.nodeName; - const listItem = document.createElement( 'li' ); + const listItem = doc.createElement( 'li' ); let receivingNode = listNode; @@ -78,7 +73,7 @@ export default function( node ) { // Make sure we append to a list. if ( ! isList( receivingNode ) ) { - receivingNode = receivingNode.appendChild( document.createElement( listType ) ); + receivingNode = receivingNode.appendChild( doc.createElement( listType ) ); } // Append the list item to the list. diff --git a/blocks/api/raw-handling/normalise-blocks.js b/blocks/api/raw-handling/normalise-blocks.js index 9d7ccac4af03b..7bb3b44f2d571 100644 --- a/blocks/api/raw-handling/normalise-blocks.js +++ b/blocks/api/raw-handling/normalise-blocks.js @@ -1,7 +1,7 @@ /** * Internal dependencies */ -import { isInline, isEmpty } from './utils'; +import { isPhrasingContent, isEmpty } from './utils'; /** * Browser dependencies @@ -26,7 +26,7 @@ export default function( HTML ) { decu.removeChild( node ); } else { if ( ! accu.lastChild || accu.lastChild.nodeName !== 'P' ) { - accu.appendChild( document.createElement( 'P' ) ); + accu.appendChild( accuDoc.createElement( 'P' ) ); } accu.lastChild.appendChild( node ); @@ -36,7 +36,7 @@ export default function( HTML ) { // BR nodes: create a new paragraph on double, or append to previous. if ( node.nodeName === 'BR' ) { if ( node.nextSibling && node.nextSibling.nodeName === 'BR' ) { - accu.appendChild( document.createElement( 'P' ) ); + accu.appendChild( accuDoc.createElement( 'P' ) ); decu.removeChild( node.nextSibling ); } @@ -57,9 +57,9 @@ export default function( HTML ) { } else { accu.appendChild( node ); } - } else if ( isInline( node ) ) { + } else if ( isPhrasingContent( node ) ) { if ( ! accu.lastChild || accu.lastChild.nodeName !== 'P' ) { - accu.appendChild( document.createElement( 'P' ) ); + accu.appendChild( accuDoc.createElement( 'P' ) ); } accu.lastChild.appendChild( node ); } else { diff --git a/blocks/api/raw-handling/phrasing-content-reducer.js b/blocks/api/raw-handling/phrasing-content-reducer.js new file mode 100644 index 0000000000000..1f60c8d1ee823 --- /dev/null +++ b/blocks/api/raw-handling/phrasing-content-reducer.js @@ -0,0 +1,37 @@ +/** + * WordPress dependencies + */ +import { unwrap, replaceTag } from '@wordpress/utils'; + +/** + * Internal dependencies + */ +import { isPhrasingContent } from './utils'; + +function isBlockContent( node, schema = {} ) { + return schema.hasOwnProperty( node.nodeName.toLowerCase() ); +} + +export default function( node, doc, schema ) { + if ( node.nodeName === 'SPAN' ) { + const { fontWeight, fontStyle } = node.style; + + if ( fontWeight === 'bold' || fontWeight === '700' ) { + node = replaceTag( node, 'strong', doc ); + } else if ( fontStyle === 'italic' ) { + node = replaceTag( node, 'em', doc ); + } + } else if ( node.nodeName === 'B' ) { + node = replaceTag( node, 'strong', doc ); + } else if ( node.nodeName === 'I' ) { + node = replaceTag( node, 'em', doc ); + } + + if ( + isPhrasingContent( node ) && + node.hasChildNodes() && + Array.from( node.childNodes ).some( ( child ) => isBlockContent( child, schema ) ) + ) { + unwrap( node ); + } +} diff --git a/blocks/api/raw-handling/shortcode-converter.js b/blocks/api/raw-handling/shortcode-converter.js index 11a9491326f2f..013615a3a6c49 100644 --- a/blocks/api/raw-handling/shortcode-converter.js +++ b/blocks/api/raw-handling/shortcode-converter.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { some, castArray, first, mapValues, pickBy } from 'lodash'; +import { some, castArray, first, mapValues, pickBy, includes } from 'lodash'; /** * Internal dependencies @@ -15,7 +15,7 @@ import { getBlockAttributes } from '../parser'; */ const { shortcode } = window.wp; -function segmentHTMLToShortcodeBlock( HTML ) { +function segmentHTMLToShortcodeBlock( HTML, lastIndex = 0 ) { // Get all matches. const transformsFrom = getBlockTransforms( 'from' ); @@ -32,11 +32,23 @@ function segmentHTMLToShortcodeBlock( HTML ) { const transformTag = first( transformTags ); let match; - let lastIndex = 0; if ( ( match = shortcode.next( transformTag, HTML, lastIndex ) ) ) { + const beforeHTML = HTML.substr( 0, match.index ); + lastIndex = match.index + match.content.length; + // If the shortcode content does not contain HTML and the shortcode is + // not on a new line (or in paragraph from Markdown converter), + // consider the shortcode as inline text, and thus skip conversion for + // this segment. + if ( + ! includes( match.shortcode.content || '', '<' ) && + ! /(\n|

)\s*$/.test( beforeHTML ) + ) { + return segmentHTMLToShortcodeBlock( HTML, lastIndex ); + } + const attributes = mapValues( pickBy( transformation.attributes, ( schema ) => schema.shortcode ), // Passing all of `match` as second argument is intentionally broad @@ -59,9 +71,9 @@ function segmentHTMLToShortcodeBlock( HTML ) { ); return [ - HTML.substr( 0, match.index ), + beforeHTML, block, - ...segmentHTMLToShortcodeBlock( HTML.substr( match.index + match.content.length ) ), + ...segmentHTMLToShortcodeBlock( HTML.substr( lastIndex ) ), ]; } diff --git a/blocks/api/raw-handling/slack-markdown-variant-corrector.js b/blocks/api/raw-handling/slack-markdown-variant-corrector.js deleted file mode 100644 index d3ae3bea7c1d4..0000000000000 --- a/blocks/api/raw-handling/slack-markdown-variant-corrector.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Corrects the Slack Markdown variant of the code block. - * If uncorrected, it will be converted to inline code. - * - * @see https://get.slack.help/hc/en-us/articles/202288908-how-can-i-add-formatting-to-my-messages-#code-blocks - * - * @param {string} text The potential Markdown text to correct. - * - * @return {string} The corrected Markdown. - */ -export default function( text ) { - return text.replace( - /((?:^|\n)```)([^\n`]+)(```(?:$|\n))/, - ( match, p1, p2, p3 ) => `${ p1 }\n${ p2 }\n${ p3 }` - ); -} diff --git a/blocks/api/raw-handling/special-comment-converter.js b/blocks/api/raw-handling/special-comment-converter.js index 8146d671ce639..cbf4dd320821e 100644 --- a/blocks/api/raw-handling/special-comment-converter.js +++ b/blocks/api/raw-handling/special-comment-converter.js @@ -20,16 +20,17 @@ const { COMMENT_NODE } = window.Node; * The custom element is then expected to be recognized by any registered * block's `raw` transform. * - * @param {Node} node The node to be processed. + * @param {Node} node The node to be processed. + * @param {Document} doc The document of the node. * @return {void} */ -export default function( node ) { +export default function( node, doc ) { if ( node.nodeType !== COMMENT_NODE ) { return; } if ( node.nodeValue === 'nextpage' ) { - replace( node, createNextpage() ); + replace( node, createNextpage( doc ) ); return; } @@ -55,12 +56,12 @@ export default function( node ) { } } - replace( node, createMore( customText, noTeaser ) ); + replace( node, createMore( customText, noTeaser, doc ) ); } } -function createMore( customText, noTeaser ) { - const node = document.createElement( 'wp-block' ); +function createMore( customText, noTeaser, doc ) { + const node = doc.createElement( 'wp-block' ); node.dataset.block = 'core/more'; if ( customText ) { node.dataset.customText = customText; @@ -72,8 +73,8 @@ function createMore( customText, noTeaser ) { return node; } -function createNextpage() { - const node = document.createElement( 'wp-block' ); +function createNextpage( doc ) { + const node = doc.createElement( 'wp-block' ); node.dataset.block = 'core/nextpage'; return node; diff --git a/blocks/api/raw-handling/strip-attributes.js b/blocks/api/raw-handling/strip-attributes.js deleted file mode 100644 index 832f71c9e36d5..0000000000000 --- a/blocks/api/raw-handling/strip-attributes.js +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Browser dependencies - */ -const { ELEMENT_NODE } = window.Node; - -/** - * Internal dependencies - */ -import { isAttributeWhitelisted, isClassWhitelisted } from './utils'; - -export default function( node ) { - if ( node.nodeType !== ELEMENT_NODE ) { - return; - } - - if ( ! node.hasAttributes() ) { - return; - } - - const tag = node.nodeName.toLowerCase(); - - Array.from( node.attributes ).forEach( ( { name } ) => { - if ( name === 'class' || isAttributeWhitelisted( tag, name ) ) { - return; - } - - node.removeAttribute( name ); - } ); - - const oldClasses = node.getAttribute( 'class' ); - - if ( ! oldClasses ) { - return; - } - - const newClasses = oldClasses - .split( ' ' ) - .filter( ( name ) => name && isClassWhitelisted( tag, name ) ) - .join( ' ' ); - - if ( newClasses.length ) { - node.setAttribute( 'class', newClasses ); - } else { - node.removeAttribute( 'class' ); - } -} diff --git a/blocks/api/raw-handling/table-normaliser.js b/blocks/api/raw-handling/table-normaliser.js deleted file mode 100644 index 34ee6ed666faf..0000000000000 --- a/blocks/api/raw-handling/table-normaliser.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Browser dependencies - */ -const { TEXT_NODE } = window.Node; - -export default function( node ) { - if ( node.nodeType !== TEXT_NODE ) { - return; - } - - const parentNode = node.parentNode; - - if ( [ 'TR', 'TBODY', 'THEAD', 'TFOOT', 'TABLE' ].indexOf( parentNode.nodeName ) === -1 ) { - return; - } - - parentNode.removeChild( node ); -} diff --git a/blocks/api/raw-handling/test/comment-remover.js b/blocks/api/raw-handling/test/comment-remover.js deleted file mode 100644 index 91222acfdd8a7..0000000000000 --- a/blocks/api/raw-handling/test/comment-remover.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * External dependencies - */ -import { equal } from 'assert'; - -/** - * Internal dependencies - */ -import commentRemover from '../comment-remover'; -import { deepFilterHTML } from '../utils'; - -describe( 'commentRemover', () => { - it( 'should remove comments', () => { - equal( deepFilterHTML( '', [ commentRemover ] ), '' ); - } ); - - it( 'should deep remove comments', () => { - equal( deepFilterHTML( '

test

', [ commentRemover ] ), '

test

' ); - } ); -} ); diff --git a/blocks/api/raw-handling/test/create-unwrapper.js b/blocks/api/raw-handling/test/create-unwrapper.js deleted file mode 100644 index 772ccff0d2c9e..0000000000000 --- a/blocks/api/raw-handling/test/create-unwrapper.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * External dependencies - */ -import { equal } from 'assert'; - -/** - * Internal dependencies - */ -import createUnwrapper from '../create-unwrapper'; -import { deepFilterHTML } from '../utils'; - -const unwrapper = createUnwrapper( ( node ) => node.nodeName === 'SPAN' ); -const unwrapperWithAfter = createUnwrapper( - ( node ) => node.nodeName === 'P', - () => document.createElement( 'BR' ) -); - -describe( 'createUnwrapper', () => { - it( 'should remove spans', () => { - equal( deepFilterHTML( 'test', [ unwrapper ] ), 'test' ); - } ); - - it( 'should remove wrapped spans', () => { - equal( deepFilterHTML( '

test

', [ unwrapper ] ), '

test

' ); - } ); - - it( 'should remove spans with attributes', () => { - equal( deepFilterHTML( '

test

', [ unwrapper ] ), '

test

' ); - } ); - - it( 'should remove nested spans', () => { - equal( deepFilterHTML( '

test

', [ unwrapper ] ), '

test

' ); - } ); - - it( 'should remove spans, but preserve nested structure', () => { - equal( deepFilterHTML( '

test test

', [ unwrapper ] ), '

test test

' ); - } ); - - it( 'should remove paragraphs and insert line break', () => { - equal( deepFilterHTML( '

test

', [ unwrapperWithAfter ] ), 'test
' ); - } ); -} ); diff --git a/blocks/api/raw-handling/test/embedded-content-reducer.js b/blocks/api/raw-handling/test/embedded-content-reducer.js deleted file mode 100644 index a05e2f5eb8ab9..0000000000000 --- a/blocks/api/raw-handling/test/embedded-content-reducer.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Internal dependencies - */ -import embeddedContentReducer from '../embedded-content-reducer'; -import { deepFilterHTML } from '../utils'; - -describe( 'embeddedContentReducer', () => { - it( 'should move embedded content from paragraph', () => { - expect( deepFilterHTML( '

test

', [ embeddedContentReducer ] ) ) - .toEqual( '

test

' ); - } ); - - it( 'should move an anchor with just an image from paragraph', () => { - expect( deepFilterHTML( '

test

', [ embeddedContentReducer ] ) ) - .toEqual( '

test

' ); - } ); - - it( 'should move multiple images', () => { - expect( deepFilterHTML( '

test

', [ embeddedContentReducer ] ) ) - .toEqual( '

test

' ); - } ); -} ); diff --git a/blocks/api/raw-handling/test/figure-content-reducer.js b/blocks/api/raw-handling/test/figure-content-reducer.js new file mode 100644 index 0000000000000..d8346b81fb9a2 --- /dev/null +++ b/blocks/api/raw-handling/test/figure-content-reducer.js @@ -0,0 +1,41 @@ +/** + * Internal dependencies + */ +import figureContentReducer from '../figure-content-reducer'; +import { deepFilterHTML } from '../utils'; + +describe( 'figureContentReducer', () => { + const schema = { + figure: { + children: { + img: {}, + a: { + children: { + img: {}, + }, + }, + }, + }, + }; + + it( 'should move embedded content from paragraph', () => { + const input = '

test

'; + const output = '

test

'; + + expect( deepFilterHTML( input, [ figureContentReducer ], schema ) ).toEqual( output ); + } ); + + it( 'should move an anchor with just an image from paragraph', () => { + const input = '

test

'; + const output = '

test

'; + + expect( deepFilterHTML( input, [ figureContentReducer ], schema ) ).toEqual( output ); + } ); + + it( 'should move multiple images', () => { + const input = '

test

'; + const output = '

test

'; + + expect( deepFilterHTML( input, [ figureContentReducer ], schema ) ).toEqual( output ); + } ); +} ); diff --git a/blocks/api/raw-handling/test/index.js b/blocks/api/raw-handling/test/index.js index 8e41a4e838170..49809d0ae394e 100644 --- a/blocks/api/raw-handling/test/index.js +++ b/blocks/api/raw-handling/test/index.js @@ -1,99 +1,20 @@ /** * External dependencies */ -import { equal, deepEqual } from 'assert'; +import { equal } from 'assert'; /** * Internal dependencies */ import rawHandler from '../index'; -import { registerBlockType, unregisterBlockType, setUnknownTypeHandlerName } from '../../registration'; -import { createBlock } from '../../factory'; import { getBlockContent } from '../../serializer'; +import { registerCoreBlocks } from '../../../../core-blocks'; describe( 'rawHandler', () => { - it( 'should convert recognised raw content', () => { - registerBlockType( 'test/figure', { - category: 'common', - title: 'test figure', - attributes: { - content: { - type: 'array', - source: 'children', - selector: 'figure', - }, - }, - transforms: { - from: [ - { - type: 'raw', - isMatch: ( node ) => node.nodeName === 'FIGURE', - }, - ], - }, - save: () => {}, - } ); - - const block = rawHandler( { HTML: '
test
' } )[ 0 ]; - const { name, attributes } = createBlock( 'test/figure', { content: [ 'test' ] } ); - - equal( block.name, name ); - deepEqual( block.attributes, attributes ); - - unregisterBlockType( 'test/figure' ); - } ); - - it( 'should handle unknown raw content', () => { - registerBlockType( 'test/unknown', { - category: 'common', - title: 'test unknown', - attributes: { - content: { - type: 'string', - source: 'property', - property: 'innerHTML', - }, - }, - save: () => {}, - } ); - setUnknownTypeHandlerName( 'test/unknown' ); - - const block = rawHandler( { HTML: '
test
' } )[ 0 ]; - - equal( block.name, 'test/unknown' ); - equal( block.attributes.content, '
test
' ); - - unregisterBlockType( 'test/unknown' ); - setUnknownTypeHandlerName( undefined ); - } ); - - it( 'should handle raw content with transform', () => { - registerBlockType( 'test/transform', { - category: 'common', - title: 'test figure', - attributes: { - content: { - type: 'array', - }, - }, - transforms: { - from: [ - { - type: 'raw', - isMatch: ( node ) => node.nodeName === 'FIGURE', - transform: ( node ) => createBlock( 'test/transform', { content: node.nodeName } ), - }, - ], - }, - save: () => {}, - } ); - - const block = rawHandler( { HTML: '
test
' } )[ 0 ]; - - equal( block.name, 'test/transform' ); - equal( block.attributes.content, 'FIGURE' ); - - unregisterBlockType( 'test/transform' ); + beforeAll( () => { + // Load all hooks that modify blocks + require( 'editor/hooks' ); + registerCoreBlocks(); } ); it( 'should filter inline content', () => { @@ -144,6 +65,15 @@ describe( 'rawHandler', () => { equal( filtered, '

Some heading

' ); } ); + + it( 'should break up forced inline content', () => { + const filtered = rawHandler( { + HTML: '

test

test

', + mode: 'INLINE', + } ); + + equal( filtered, 'test
test' ); + } ); } ); import './integration'; diff --git a/blocks/api/raw-handling/test/inline-content-converter.js b/blocks/api/raw-handling/test/inline-content-converter.js deleted file mode 100644 index ee5decf487aad..0000000000000 --- a/blocks/api/raw-handling/test/inline-content-converter.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * External dependencies - */ -import { equal } from 'assert'; - -/** - * Internal dependencies - */ -import inlineContentConverter from '../inline-content-converter'; -import { deepFilterHTML } from '../utils'; - -describe( 'inlineContentConverter', () => { - it( 'should remove non-inline content from inline wrapper', () => { - equal( - deepFilterHTML( '

test

test

', [ inlineContentConverter ] ), - '
test
test
' - ); - } ); -} ); diff --git a/blocks/api/raw-handling/test/integration/apple-out.html b/blocks/api/raw-handling/test/integration/apple-out.html index ece5eb1f6190a..84bdfad50c7e1 100644 --- a/blocks/api/raw-handling/test/integration/apple-out.html +++ b/blocks/api/raw-handling/test/integration/apple-out.html @@ -13,10 +13,11 @@ diff --git a/blocks/api/raw-handling/test/integration/google-docs-out.html b/blocks/api/raw-handling/test/integration/google-docs-out.html index 84e1fdf8f5300..1c54f0c4b23ec 100644 --- a/blocks/api/raw-handling/test/integration/google-docs-out.html +++ b/blocks/api/raw-handling/test/integration/google-docs-out.html @@ -13,10 +13,11 @@

This is a heading

diff --git a/blocks/api/raw-handling/test/integration/iframe-embed-out.html b/blocks/api/raw-handling/test/integration/iframe-embed-out.html index 08cf7f9625a46..6eeb487da82b5 100644 --- a/blocks/api/raw-handling/test/integration/iframe-embed-out.html +++ b/blocks/api/raw-handling/test/integration/iframe-embed-out.html @@ -1,3 +1,3 @@ - +
diff --git a/blocks/api/raw-handling/test/integration/index.js b/blocks/api/raw-handling/test/integration/index.js index 9fc1cc57266f0..2a66cf04a5ff6 100644 --- a/blocks/api/raw-handling/test/integration/index.js +++ b/blocks/api/raw-handling/test/integration/index.js @@ -23,23 +23,30 @@ const types = [ 'iframe-embed', 'one-image', 'two-images', + 'markdown', + 'wordpress', ]; +function readFile( filePath ) { + return fs.existsSync( filePath ) ? fs.readFileSync( filePath, 'utf8' ).trim() : ''; +} + describe( 'raw handling: integration', () => { beforeAll( () => { // Load all hooks that modify blocks - require( 'blocks/hooks' ); + require( 'editor/hooks' ); registerCoreBlocks(); } ); types.forEach( ( type ) => { it( type, () => { - const input = fs.readFileSync( path.join( __dirname, `${ type }-in.html` ), 'utf8' ).trim(); - const output = fs.readFileSync( path.join( __dirname, `${ type }-out.html` ), 'utf8' ).trim(); - const converted = rawHandler( { HTML: input, canUserUseUnfilteredHTML: true } ); + const HTML = readFile( path.join( __dirname, `${ type }-in.html` ) ); + const plainText = readFile( path.join( __dirname, `${ type }-in.txt` ) ); + const output = readFile( path.join( __dirname, `${ type }-out.html` ) ); + const converted = rawHandler( { HTML, plainText, canUserUseUnfilteredHTML: true } ); const serialized = typeof converted === 'string' ? converted : serialize( converted ); - equal( output, serialized ); + equal( serialized, output ); } ); } ); } ); diff --git a/blocks/api/raw-handling/test/integration/markdown-in.txt b/blocks/api/raw-handling/test/integration/markdown-in.txt new file mode 100644 index 0000000000000..ef2b6187d1e27 --- /dev/null +++ b/blocks/api/raw-handling/test/integration/markdown-in.txt @@ -0,0 +1,38 @@ +# This is a heading with *italic* + +This is a paragraph with a [link](https://w.org/) and **bold**. + +Preserve +line breaks please. + +## Lists + +* A +* Bulleted + * Indented +* List + +1. One +2. Two +3. Three + +## Table + +First Header | Second Header +------------ | ------------- +Content from cell 1 | Content from cell 2 +Content in the first column | Content in the second column + +## Quote + +> First +> +> Second + +## Code + +Inline `code` tags should work. + +```html +This is a code block. +``` diff --git a/blocks/api/raw-handling/test/integration/markdown-out.html b/blocks/api/raw-handling/test/integration/markdown-out.html new file mode 100644 index 0000000000000..867fa64ebea50 --- /dev/null +++ b/blocks/api/raw-handling/test/integration/markdown-out.html @@ -0,0 +1,83 @@ + +

This is a heading with italic

+ + + +

This is a paragraph with a link and bold.

+ + + +

Preserve
line breaks please.

+ + + +

Lists

+ + + + + + + +
    +
  1. One
  2. +
  3. Two
  4. +
  5. Three
  6. +
+ + + +

Table

+ + + + + + + + + + + + + + + + + + + + +
First HeaderSecond Header
Content from cell 1Content from cell 2
Content in the first columnContent in the second column
+ + + +

Quote

+ + + +
+

First

+

Second

+
+ + + +

Code

+ + + +

Inline code tags should work.

+ + + +
This is a code block.
+ diff --git a/blocks/api/raw-handling/test/integration/wordpress-in.html b/blocks/api/raw-handling/test/integration/wordpress-in.html new file mode 100644 index 0000000000000..3336bcf194042 --- /dev/null +++ b/blocks/api/raw-handling/test/integration/wordpress-in.html @@ -0,0 +1,4 @@ +

Howdy

+

This is a paragraph.

+

More tag

+

diff --git a/blocks/api/raw-handling/test/integration/wordpress-out.html b/blocks/api/raw-handling/test/integration/wordpress-out.html new file mode 100644 index 0000000000000..5bc8b8b8d4d7b --- /dev/null +++ b/blocks/api/raw-handling/test/integration/wordpress-out.html @@ -0,0 +1,15 @@ + +

Howdy

+ + + +

This is a paragraph.

+ + + +

More tag

+ + + + + diff --git a/blocks/api/raw-handling/test/is-inline-content.js b/blocks/api/raw-handling/test/is-inline-content.js index 56aa123eb72fc..3b9816fa42e6a 100644 --- a/blocks/api/raw-handling/test/is-inline-content.js +++ b/blocks/api/raw-handling/test/is-inline-content.js @@ -8,7 +8,7 @@ import { equal } from 'assert'; */ import isInlineContent from '../is-inline-content'; -describe( 'stripWrappers', () => { +describe( 'isInlineContent', () => { it( 'should be inline content', () => { equal( isInlineContent( 'test' ), true ); equal( isInlineContent( 'test' ), true ); diff --git a/blocks/api/raw-handling/test/list-reducer.js b/blocks/api/raw-handling/test/list-reducer.js index 77d8f9012b3e8..7947aca487530 100644 --- a/blocks/api/raw-handling/test/list-reducer.js +++ b/blocks/api/raw-handling/test/list-reducer.js @@ -37,14 +37,26 @@ describe( 'listReducer', () => { equal( deepFilterHTML( input, [ listReducer ] ), output ); } ); - it( 'Should remove empty list wrappers', () => { + it( 'should remove empty list wrappers', () => { const input = ''; const output = ''; equal( deepFilterHTML( input, [ listReducer ] ), output ); } ); - it( 'Should not remove filled list wrappers', () => { + it( 'should not remove filled list wrappers', () => { const input = ''; equal( deepFilterHTML( input, [ listReducer ] ), input ); } ); + + it( 'should adjust wrong indentation (1)', () => { + const input = ''; + const output = ''; + equal( deepFilterHTML( input, [ listReducer ] ), output ); + } ); + + it( 'should adjust wrong indentation (2)', () => { + const input = ''; + const output = ''; + equal( deepFilterHTML( input, [ listReducer ] ), output ); + } ); } ); diff --git a/blocks/api/raw-handling/test/markdown-converter.js b/blocks/api/raw-handling/test/markdown-converter.js new file mode 100644 index 0000000000000..58eb0c90a4dde --- /dev/null +++ b/blocks/api/raw-handling/test/markdown-converter.js @@ -0,0 +1,35 @@ +/** + * External dependencies + */ +import { equal } from 'assert'; + +/** + * Internal dependencies + */ +import markdownConverter from '../markdown-converter'; + +describe( 'markdownConverter', () => { + it( 'should correct Slack variant', () => { + const input = '```test```'; + const output = '
test
'; + equal( markdownConverter( input ), output ); + } ); + + it( 'should correct Slack variant on own line', () => { + const input = 'test\n```test```\ntest'; + const output = '

test

\n
test
\n

test

'; + equal( markdownConverter( input ), output ); + } ); + + it( 'should not correct inline code', () => { + const input = 'test ```test``` test'; + const output = '

test test test

'; + equal( markdownConverter( input ), output ); + } ); + + it( 'should not correct code with line breaks', () => { + const input = '```js\ntest\n```'; + const output = '
test
'; + equal( markdownConverter( input ), output ); + } ); +} ); diff --git a/blocks/api/raw-handling/test/formatting-transformer.js b/blocks/api/raw-handling/test/phrasing-content-reducer.js similarity index 50% rename from blocks/api/raw-handling/test/formatting-transformer.js rename to blocks/api/raw-handling/test/phrasing-content-reducer.js index 4389a3ba26176..c9ea0c8c90280 100644 --- a/blocks/api/raw-handling/test/formatting-transformer.js +++ b/blocks/api/raw-handling/test/phrasing-content-reducer.js @@ -6,19 +6,23 @@ import { equal } from 'assert'; /** * Internal dependencies */ -import formattingTransformer from '../formatting-transformer'; +import phrasingContentReducer from '../phrasing-content-reducer'; import { deepFilterHTML } from '../utils'; -describe( 'formattingTransformer', () => { +describe( 'phrasingContentReducer', () => { it( 'should transform font weight', () => { - equal( deepFilterHTML( 'test', [ formattingTransformer ] ), 'test' ); + equal( deepFilterHTML( 'test', [ phrasingContentReducer ], {} ), 'test' ); } ); it( 'should transform numeric font weight', () => { - equal( deepFilterHTML( 'test', [ formattingTransformer ] ), 'test' ); + equal( deepFilterHTML( 'test', [ phrasingContentReducer ], {} ), 'test' ); } ); it( 'should transform font style', () => { - equal( deepFilterHTML( 'test', [ formattingTransformer ] ), 'test' ); + equal( deepFilterHTML( 'test', [ phrasingContentReducer ], {} ), 'test' ); + } ); + + it( 'should remove invalid phrasing content', () => { + equal( deepFilterHTML( '

test

', [ phrasingContentReducer ], { p: {} } ), '

test

' ); } ); } ); diff --git a/blocks/api/raw-handling/test/slack-markdown-variant-corrector.js b/blocks/api/raw-handling/test/slack-markdown-variant-corrector.js deleted file mode 100644 index 610595bb634df..0000000000000 --- a/blocks/api/raw-handling/test/slack-markdown-variant-corrector.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * External dependencies - */ -import { equal } from 'assert'; - -/** - * Internal dependencies - */ -import slackMarkdownVariantCorrector from '../slack-markdown-variant-corrector'; - -describe( 'slackMarkdownVariantCorrector', () => { - it( 'should correct Slack variant', () => { - equal( slackMarkdownVariantCorrector( '```test```' ), '```\ntest\n```' ); - } ); - - it( 'should correct Slack variant on own line', () => { - equal( slackMarkdownVariantCorrector( 'test\n```test```\ntest' ), 'test\n```\ntest\n```\ntest' ); - } ); - - it( 'should not correct inline code', () => { - const text = 'test ```test``` test'; - equal( slackMarkdownVariantCorrector( text ), text ); - } ); - - it( 'should not correct code with line breaks', () => { - const text = '```js\ntest\n```'; - equal( slackMarkdownVariantCorrector( text ), text ); - } ); -} ); diff --git a/blocks/api/raw-handling/test/strip-attributes.js b/blocks/api/raw-handling/test/strip-attributes.js deleted file mode 100644 index a68469cb202f4..0000000000000 --- a/blocks/api/raw-handling/test/strip-attributes.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * External dependencies - */ -import { equal } from 'assert'; - -/** - * Internal dependencies - */ -import stripAttributes from '../strip-attributes'; -import { deepFilterHTML } from '../utils'; - -describe( 'stripAttributes', () => { - it( 'should remove attributes', () => { - equal( deepFilterHTML( '

test

', [ stripAttributes ] ), '

test

' ); - } ); - - it( 'should remove multiple attributes', () => { - equal( deepFilterHTML( '

test

', [ stripAttributes ] ), '

test

' ); - } ); - - it( 'should deep remove attributes', () => { - equal( deepFilterHTML( '

test test

', [ stripAttributes ] ), '

test test

' ); - } ); - - it( 'should remove data-* attributes', () => { - equal( deepFilterHTML( '

test

', [ stripAttributes ] ), '

test

' ); - } ); - - it( 'should keep some attributes', () => { - equal( deepFilterHTML( 'test', [ stripAttributes ] ), 'test' ); - } ); - - it( 'should keep some classes', () => { - equal( deepFilterHTML( '', [ stripAttributes ] ), '' ); - } ); -} ); diff --git a/blocks/api/raw-handling/test/table-normaliser.js b/blocks/api/raw-handling/test/table-normaliser.js deleted file mode 100644 index 27295ba22bcc4..0000000000000 --- a/blocks/api/raw-handling/test/table-normaliser.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * External dependencies - */ -import { equal } from 'assert'; - -/** - * Internal dependencies - */ -import tableNormaliser from '../table-normaliser'; -import { deepFilterHTML } from '../utils'; - -describe( 'tableNormaliser', () => { - it( 'should remove invalid text nodes in table', () => { - equal( - deepFilterHTML( '\n\n\n\n\n\n\n
\n
\n', [ tableNormaliser ] ), - '\n
\n
\n' - ); - } ); -} ); diff --git a/blocks/api/raw-handling/test/utils.js b/blocks/api/raw-handling/test/utils.js index 411ff1a44b8a5..c5a2bd68ff219 100644 --- a/blocks/api/raw-handling/test/utils.js +++ b/blocks/api/raw-handling/test/utils.js @@ -6,18 +6,7 @@ import { equal } from 'assert'; /** * Internal dependencies */ -import createUnwrapper from '../create-unwrapper'; -import { deepFilterHTML, isEmpty, isInvalidInline, isPlain } from '../utils'; - -const spanUnwrapper = createUnwrapper( ( node ) => node.nodeName === 'SPAN' ); -const inlineUnwrapper = createUnwrapper( ( node ) => node.nodeName === 'EM' ); - -describe( 'deepFilterHTML', () => { - it( 'should not error', () => { - equal( deepFilterHTML( 'test', [ spanUnwrapper, inlineUnwrapper ] ), 'test' ); - equal( deepFilterHTML( 'test', [ spanUnwrapper, inlineUnwrapper ] ), 'test' ); - } ); -} ); +import { isEmpty, isPlain, removeInvalidHTML, getPhrasingContentSchema } from '../utils'; describe( 'isEmpty', () => { function isEmptyHTML( HTML ) { @@ -57,38 +46,119 @@ describe( 'isEmpty', () => { } ); } ); -describe( 'isInvalidInline', () => { - function isInvalidInlineHTML( HTML ) { - const doc = document.implementation.createHTMLDocument( '' ); +describe( 'isPlain', () => { + it( 'should return true for plain text', () => { + equal( isPlain( 'test' ), true ); + } ); - doc.body.innerHTML = HTML; + it( 'should return true for only line breaks', () => { + equal( isPlain( 'test
test' ), true ); + equal( isPlain( 'test
test' ), true ); + equal( isPlain( 'test
test' ), true ); + equal( isPlain( 'test
test' ), true ); + } ); - return isInvalidInline( doc.body.firstChild ); - } + it( 'should return false for formatted text', () => { + equal( isPlain( 'test' ), false ); + equal( isPlain( 'test
' ), false ); + equal( isPlain( 'testtest' ), false ); + } ); +} ); - it( 'should return true for div element', () => { - equal( isInvalidInlineHTML( '
test
' ), true ); +describe( 'removeInvalidHTML', () => { + const phrasingContentSchema = getPhrasingContentSchema(); + const schema = { + p: { + children: phrasingContentSchema, + }, + figure: { + require: [ 'img' ], + children: { + img: { + attributes: [ 'src', 'alt' ], + classes: [ 'alignleft' ], + }, + figcaption: { + children: phrasingContentSchema, + }, + }, + }, + ...phrasingContentSchema, + }; + + it( 'should leave plain text alone', () => { + const input = 'test'; + equal( removeInvalidHTML( input, schema ), input ); } ); - it( 'should return true for deep div element', () => { - equal( isInvalidInlineHTML( '
test
' ), true ); + it( 'should leave valid phrasing content alone', () => { + const input = 'test'; + equal( removeInvalidHTML( input, schema ), input ); } ); - it( 'should return false for valid structure', () => { - equal( isInvalidInlineHTML( 'test' ), false ); + it( 'should remove unrecognised tags from phrasing content', () => { + const input = '
test
'; + const output = 'test'; + equal( removeInvalidHTML( input, schema ), output ); } ); -} ); -describe( 'isPlain', () => { - it( 'should return true for plain text', () => { - equal( isPlain( 'test' ), true ); + it( 'should remove unwanted whitespace outside phrasing content', () => { + const input = '
'; + const output = '
'; + equal( removeInvalidHTML( input, schema ), output ); } ); - it( 'should return true for only line breaks', () => { - equal( isPlain( 'test
test' ), true ); + it( 'should remove attributes', () => { + const input = '

test

'; + const output = '

test

'; + equal( removeInvalidHTML( input, schema ), output ); } ); - it( 'should return false for formatted text', () => { - equal( isPlain( 'test' ), false ); + it( 'should remove multiple attributes', () => { + const input = '

test

'; + const output = '

test

'; + equal( removeInvalidHTML( input, schema ), output ); + } ); + + it( 'should deep remove attributes', () => { + const input = '

test test

'; + const output = '

test test

'; + equal( removeInvalidHTML( input, schema ), output ); + } ); + + it( 'should remove data-* attributes', () => { + const input = '

test

'; + const output = '

test

'; + equal( removeInvalidHTML( input, schema ), output ); + } ); + + it( 'should keep some attributes', () => { + const input = 'test'; + const output = 'test'; + equal( removeInvalidHTML( input, schema ), output ); + } ); + + it( 'should keep some classes', () => { + const input = '
'; + const output = '
'; + equal( removeInvalidHTML( input, schema ), output ); + } ); + + it( 'should remove empty nodes that should have children', () => { + const input = '
'; + const output = ''; + equal( removeInvalidHTML( input, schema ), output ); + } ); + + it( 'should break up block content with phrasing schema', () => { + const input = '

test

test

'; + const output = 'test
test'; + equal( removeInvalidHTML( input, phrasingContentSchema, true ), output ); + } ); + + it( 'should unwrap node that does not satisfy require', () => { + const input = '

test

test
'; + const output = '

test

test'; + equal( removeInvalidHTML( input, schema ), output ); } ); } ); diff --git a/blocks/api/raw-handling/utils.js b/blocks/api/raw-handling/utils.js index 3d9961c2b6dd0..046b77ddd4268 100644 --- a/blocks/api/raw-handling/utils.js +++ b/blocks/api/raw-handling/utils.js @@ -1,24 +1,19 @@ /** * External dependencies */ -import { includes } from 'lodash'; +import { omit, mergeWith, includes } from 'lodash'; /** - * Browser dependencies + * WordPress dependencies */ -const { ELEMENT_NODE, TEXT_NODE } = window.Node; +import { unwrap, insertAfter, remove } from '@wordpress/utils'; /** - * An array of tag groups used by isInlineForTag function. - * If tagName and nodeName are present in the same group, the node should be treated as inline. - * @type {Array} + * Browser dependencies */ -const inlineWhitelistTagGroups = [ - [ 'ul', 'li', 'ol' ], - [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ], -]; +const { ELEMENT_NODE, TEXT_NODE } = window.Node; -const inlineWhitelist = { +const phrasingContentSchema = { strong: {}, em: {}, del: {}, @@ -29,146 +24,72 @@ const inlineWhitelist = { sub: {}, sup: {}, br: {}, + '#text': {}, }; -const embeddedWhiteList = { - img: { attributes: [ 'src', 'alt' ], classes: [ 'alignleft', 'aligncenter', 'alignright', 'alignnone' ] }, - iframe: { attributes: [ 'src', 'allowfullscreen', 'height', 'width' ] }, -}; - -const inlineWrapperWhiteList = { - figcaption: {}, - h1: {}, - h2: {}, - h3: {}, - h4: {}, - h5: {}, - h6: {}, - p: {}, - li: { children: [ 'ul', 'ol', 'li' ] }, - pre: {}, - td: {}, - th: {}, -}; - -const whitelist = { - ...inlineWhitelist, - ...inlineWrapperWhiteList, - ...embeddedWhiteList, - figure: {}, - blockquote: {}, - hr: {}, - ul: {}, - ol: { attributes: [ 'type' ] }, - table: {}, - thead: {}, - tfoot: {}, - tbody: {}, - tr: {}, -}; - -export function isWhitelisted( element ) { - return whitelist.hasOwnProperty( element.nodeName.toLowerCase() ); -} - -export function isNotWhitelisted( element ) { - return ! isWhitelisted( element ); -} - -export function isAttributeWhitelisted( tag, attribute ) { - return ( - whitelist[ tag ] && - whitelist[ tag ].attributes && - whitelist[ tag ].attributes.indexOf( attribute ) !== -1 - ); -} +// Recursion is needed. +// Possible: strong > em > strong. +// Impossible: strong > strong. +[ 'strong', 'em', 'del', 'ins', 'a', 'code', 'abbr', 'sub', 'sup' ].forEach( ( tag ) => { + phrasingContentSchema[ tag ].children = omit( phrasingContentSchema, tag ); +} ); /** - * Checks if nodeName should be treated as inline when being added to tagName. - * This happens if nodeName and tagName are in the same group defined in inlineWhitelistTagGroups. + * Get schema of possible paths for phrasing content. * - * @param {string} nodeName Node name. - * @param {string} tagName Tag name. + * @see https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Phrasing_content * - * @return {boolean} True if nodeName is inline in the context of tagName and - * false otherwise. + * @return {Object} Schema. */ -function isInlineForTag( nodeName, tagName ) { - if ( ! tagName || ! nodeName ) { - return false; - } - return inlineWhitelistTagGroups.some( ( tagGroup ) => - includes( tagGroup, nodeName ) && includes( tagGroup, tagName ) - ); -} - -export function isInline( node, tagName ) { - const nodeName = node.nodeName.toLowerCase(); - return inlineWhitelist.hasOwnProperty( nodeName ) || isInlineForTag( nodeName, tagName ); -} - -export function isClassWhitelisted( tag, name ) { - return ( - whitelist[ tag ] && - whitelist[ tag ].classes && - whitelist[ tag ].classes.indexOf( name ) !== -1 - ); +export function getPhrasingContentSchema() { + return phrasingContentSchema; } /** - * Whether or not the given node is embedded content. + * Find out whether or not the given node is phrasing content. * - * @see https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Embedded_content + * @see https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Phrasing_content * - * @param {Node} node The node to check. + * @param {Element} node The node to test. * - * @return {boolean} True if embedded content, false if not. + * @return {boolean} True if phrasing content, false if not. */ -export function isEmbedded( node ) { - return embeddedWhiteList.hasOwnProperty( node.nodeName.toLowerCase() ); +export function isPhrasingContent( node ) { + const tag = node.nodeName.toLowerCase(); + return getPhrasingContentSchema().hasOwnProperty( tag ) || tag === 'span'; } -export function isInlineWrapper( node ) { - return inlineWrapperWhiteList.hasOwnProperty( node.nodeName.toLowerCase() ); -} - -export function isAllowedBlock( parentNode, node ) { - const parentNodeTag = parentNode.nodeName.toLowerCase(); - const nodeTag = node.nodeName.toLowerCase(); - - return ( - whitelist[ parentNodeTag ] && - whitelist[ parentNodeTag ].children && - whitelist[ parentNodeTag ].children.indexOf( nodeTag ) !== -1 - ); -} - -export function isInvalidInline( element ) { - if ( ! isInline( element ) ) { - return false; - } - - if ( ! element.hasChildNodes() ) { - return false; - } +/** + * Given raw transforms from blocks, merges all schemas into one. + * + * @param {Array} transforms Block transforms, of the `raw` type. + * + * @return {Object} A complete block content schema. + */ +export function getBlockContentSchema( transforms ) { + const schemas = transforms.map( ( { schema } ) => schema ); - return Array.from( element.childNodes ).some( ( node ) => { - if ( node.nodeType === ELEMENT_NODE ) { - if ( ! isInline( node ) ) { - return true; + return mergeWith( {}, ...schemas, ( objValue, srcValue, key ) => { + if ( key === 'children' ) { + if ( objValue === '*' || srcValue === '*' ) { + return '*'; } - return isInvalidInline( node ); + return { ...objValue, ...srcValue }; + } else if ( key === 'attributes' || key === 'require' ) { + return [ ...( objValue || [] ), ...( srcValue || [] ) ]; } - - return false; } ); } -export function isDoubleBR( node ) { - return node.nodeName === 'BR' && node.previousSibling && node.previousSibling.nodeName === 'BR'; -} - +/** + * Recursively checks if an element is empty. An element is not empty if it + * contains text or contains elements with attributes such as images. + * + * @param {Element} element The element to check. + * + * @return {boolean} Wether or not the element is empty. + */ export function isEmpty( element ) { if ( ! element.hasChildNodes() ) { return true; @@ -193,23 +114,16 @@ export function isEmpty( element ) { } ); } +/** + * Checks wether HTML can be considered plain text. That is, it does not contain + * any elements that are not line breaks. + * + * @param {string} HTML The HTML to check. + * + * @return {boolean} Wether the HTML can be considered plain text. + */ export function isPlain( HTML ) { - const doc = document.implementation.createHTMLDocument( '' ); - - doc.body.innerHTML = HTML; - - const brs = doc.querySelectorAll( 'br' ); - - // Remove all BR nodes. - Array.from( brs ).forEach( ( node ) => { - node.parentNode.replaceChild( document.createTextNode( '\n' ), node ); - } ); - - // Merge all text nodes. - doc.body.normalize(); - - // If it's plain text, there should only be one node left. - return doc.body.childNodes.length === 1 && doc.body.firstChild.nodeType === TEXT_NODE; + return ! /<(?!br[ />])/i.test( HTML ); } /** @@ -218,36 +132,144 @@ export function isPlain( HTML ) { * @param {NodeList} nodeList The nodeList to filter. * @param {Array} filters An array of functions that can mutate with the provided node. * @param {Document} doc The document of the nodeList. + * @param {Object} schema The schema to use. */ -export function deepFilterNodeList( nodeList, filters, doc ) { +export function deepFilterNodeList( nodeList, filters, doc, schema ) { Array.from( nodeList ).forEach( ( node ) => { - deepFilterNodeList( node.childNodes, filters, doc ); + deepFilterNodeList( node.childNodes, filters, doc, schema ); - filters.forEach( ( filter ) => { + filters.forEach( ( item ) => { // Make sure the node is still attached to the document. if ( ! doc.contains( node ) ) { return; } - filter( node, doc ); + item( node, doc, schema ); } ); } ); } /** * Given node filters, deeply filters HTML tags. + * Filters from the deepest nodes to the top. * * @param {string} HTML The HTML to filter. * @param {Array} filters An array of functions that can mutate with the provided node. + * @param {Object} schema The schema to use. * * @return {string} The filtered HTML. */ -export function deepFilterHTML( HTML, filters = [] ) { +export function deepFilterHTML( HTML, filters = [], schema ) { + const doc = document.implementation.createHTMLDocument( '' ); + + doc.body.innerHTML = HTML; + + deepFilterNodeList( doc.body.childNodes, filters, doc, schema ); + + return doc.body.innerHTML; +} + +/** + * Given a schema, unwraps or removes nodes, attributes and classes on a node + * list. + * + * @param {NodeList} nodeList The nodeList to filter. + * @param {Document} doc The document of the nodeList. + * @param {Object} schema An array of functions that can mutate with the provided node. + * @param {Object} inline Whether to clean for inline mode. + */ +function cleanNodeList( nodeList, doc, schema, inline ) { + Array.from( nodeList ).forEach( ( node ) => { + const tag = node.nodeName.toLowerCase(); + + // It's a valid child. + if ( schema.hasOwnProperty( tag ) ) { + if ( node.nodeType === ELEMENT_NODE ) { + const { attributes = [], classes = [], children, require = [] } = schema[ tag ]; + + // If the node is empty and it's supposed to have children, + // remove the node. + if ( isEmpty( node ) && children ) { + remove( node ); + return; + } + + if ( node.hasAttributes() ) { + // Strip invalid attributes. + Array.from( node.attributes ).forEach( ( { name } ) => { + if ( name !== 'class' && ! includes( attributes, name ) ) { + node.removeAttribute( name ); + } + } ); + + // Strip invalid classes. + if ( node.classList.length ) { + const newClasses = classes.filter( ( name ) => + node.classList.contains( name ) + ); + + if ( newClasses.length ) { + node.setAttribute( 'class', newClasses.join( ' ' ) ); + } else { + node.removeAttribute( 'class' ); + } + } + } + + if ( node.hasChildNodes() ) { + // Do not filter any content. + if ( children === '*' ) { + return; + } + + // Continue if the node is supposed to have children. + if ( children ) { + // If a parent requires certain children, but it does + // not have them, drop the parent and continue. + if ( require.length && ! node.querySelector( require.join( ',' ) ) ) { + cleanNodeList( node.childNodes, doc, schema, inline ); + unwrap( node ); + } + + cleanNodeList( node.childNodes, doc, children, inline ); + // Remove children if the node is not supposed to have any. + } else { + while ( node.firstChild ) { + remove( node.firstChild ); + } + } + } + } + // Invalid child. Continue with schema at the same place and unwrap. + } else { + cleanNodeList( node.childNodes, doc, schema, inline ); + + // For inline mode, insert a line break when unwrapping nodes that + // are not phrasing content. + if ( inline && ! isPhrasingContent( node ) && node.nextElementSibling ) { + insertAfter( doc.createElement( 'br' ), node ); + } + + unwrap( node ); + } + } ); +} + +/** + * Given a schema, unwraps or removes nodes, attributes and classes on HTML. + * + * @param {string} HTML The HTML to clean up. + * @param {Object} schema Schema for the HTML. + * @param {Object} inline Whether to clean for inline mode. + * + * @return {string} The cleaned up HTML. + */ +export function removeInvalidHTML( HTML, schema, inline ) { const doc = document.implementation.createHTMLDocument( '' ); doc.body.innerHTML = HTML; - deepFilterNodeList( doc.body.childNodes, filters, doc ); + cleanNodeList( doc.body.childNodes, doc, schema, inline ); return doc.body.innerHTML; } diff --git a/blocks/api/serializer.js b/blocks/api/serializer.js index cb9fe63535f43..b206e472627e9 100644 --- a/blocks/api/serializer.js +++ b/blocks/api/serializer.js @@ -3,13 +3,13 @@ */ import { isEmpty, reduce, isObject, castArray, startsWith } from 'lodash'; import { html as beautifyHtml } from 'js-beautify'; -import isShallowEqual from 'shallowequal'; /** * WordPress dependencies */ import { Component, cloneElement, renderToString } from '@wordpress/element'; import { hasFilter, applyFilters } from '@wordpress/hooks'; +import isShallowEqual from '@wordpress/is-shallow-equal'; /** * Internal dependencies diff --git a/blocks/api/test/factory.js b/blocks/api/test/factory.js index 98f44734cd379..553d337245fd0 100644 --- a/blocks/api/test/factory.js +++ b/blocks/api/test/factory.js @@ -31,7 +31,7 @@ describe( 'block factory', () => { beforeAll( () => { // Load all hooks that modify blocks - require( 'blocks/hooks' ); + require( 'editor/hooks' ); } ); afterEach( () => { diff --git a/blocks/api/test/parser.js b/blocks/api/test/parser.js index 36208909138aa..1eacdc317e37f 100644 --- a/blocks/api/test/parser.js +++ b/blocks/api/test/parser.js @@ -43,7 +43,7 @@ describe( 'block parser', () => { beforeAll( () => { // Load all hooks that modify blocks - require( 'blocks/hooks' ); + require( 'editor/hooks' ); } ); afterEach( () => { diff --git a/blocks/api/test/registration.js b/blocks/api/test/registration.js index e68e8471c3220..0c6317ba11c96 100644 --- a/blocks/api/test/registration.js +++ b/blocks/api/test/registration.js @@ -30,7 +30,7 @@ describe( 'blocks', () => { beforeAll( () => { // Load all hooks that modify blocks - require( 'blocks/hooks' ); + require( 'editor/hooks' ); } ); afterEach( () => { diff --git a/blocks/api/test/serializer.js b/blocks/api/test/serializer.js index 78a8394e6e41a..345c3ef3cc2f7 100644 --- a/blocks/api/test/serializer.js +++ b/blocks/api/test/serializer.js @@ -23,12 +23,14 @@ import { setUnknownTypeHandlerName, } from '../registration'; import { createBlock } from '../'; -import InnerBlocks from '../../inner-blocks'; + +// Todo: move the test to the inner-blocks folder +import InnerBlocks from '../../../editor/components/inner-blocks'; describe( 'block serializer', () => { beforeAll( () => { // Load all hooks that modify blocks - require( 'blocks/hooks' ); + require( 'editor/hooks' ); } ); afterEach( () => { diff --git a/blocks/color-palette/index.js b/blocks/color-palette/index.js deleted file mode 100644 index cb1d9417d1377..0000000000000 --- a/blocks/color-palette/index.js +++ /dev/null @@ -1,89 +0,0 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; -import { ChromePicker } from 'react-color'; -import { map } from 'lodash'; - -/** - * WordPress dependencies - */ -import { Dropdown } from '@wordpress/components'; -import { __, sprintf } from '@wordpress/i18n'; - -/** - * Internal dependencies - */ -import './style.scss'; -import { withEditorSettings } from '../editor-settings'; - -export function ColorPalette( { colors, disableCustomColors = false, value, onChange } ) { - function applyOrUnset( color ) { - return () => onChange( value === color ? undefined : color ); - } - - return ( -
- { map( colors, ( { color } ) => { - const style = { color: color }; - const className = classnames( 'blocks-color-palette__item', { 'is-active': value === color } ); - - return ( -
-
- ); - } ) } - - { ! disableCustomColors && - ( - - ) } - renderContent={ () => ( - onChange( color.hex ) } - style={ { width: '100%' } } - disableAlpha - /> - ) } - /> - } - - -
- ); -} - -export default withEditorSettings( - ( settings, props ) => ( { - colors: props.colors || settings.colors, - disableCustomColors: props.disableCustomColors !== undefined ? - props.disableCustomColors : - settings.disableCustomColors, - } ) -)( ColorPalette ); diff --git a/blocks/color-palette/test/__snapshots__/index.js.snap b/blocks/color-palette/test/__snapshots__/index.js.snap deleted file mode 100644 index 2111c8b5f2f07..0000000000000 --- a/blocks/color-palette/test/__snapshots__/index.js.snap +++ /dev/null @@ -1,200 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ColorPalette Dropdown .renderContent should render dropdown content 1`] = ` - -`; - -exports[`ColorPalette Dropdown .renderToggle should render dropdown content 1`] = ` - -`; - -exports[`ColorPalette Dropdown should render it correctly 1`] = ` - -`; - -exports[`ColorPalette should allow disabling custom color picker 1`] = ` -
-
-
-
-
-
-
- -
-`; - -exports[`ColorPalette should render a dynamic toolbar of colors 1`] = ` -
-
-
-
-
-
-
- - -
-`; diff --git a/blocks/colors/with-colors.js b/blocks/colors/with-colors.js deleted file mode 100644 index 03f06f30ef65b..0000000000000 --- a/blocks/colors/with-colors.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * External dependencies - */ -import { get } from 'lodash'; - -/** - * WordPress dependencies - */ -import { createHigherOrderComponent } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import { getColorValue, getColorClass, setColorValue } from './utils'; -import { withEditorSettings } from '../editor-settings'; - -/** - * Higher-order component, which handles color logic for class generation - * color value, retrieval and color attribute setting. - * - * @param {WPElement} WrappedComponent The wrapped component. - * - * @return {Component} Component with a new colors prop. - */ -export default createHigherOrderComponent( - withEditorSettings( - ( settings, props ) => { - const colors = get( settings, [ 'colors' ], [] ); - return { - initializeColor: ( { colorContext, colorAttribute, customColorAttribute } ) => ( { - value: getColorValue( - colors, - props.attributes[ colorAttribute ], - props.attributes[ customColorAttribute ] - ), - class: getColorClass( colorContext, props.attributes[ colorAttribute ] ), - set: setColorValue( colors, colorAttribute, customColorAttribute, props.setAttributes ), - } ), - }; - } ), - 'withColors' -); diff --git a/blocks/contrast-checker/style.scss b/blocks/contrast-checker/style.scss deleted file mode 100644 index 2456bd495e4ec..0000000000000 --- a/blocks/contrast-checker/style.scss +++ /dev/null @@ -1,3 +0,0 @@ -.blocks-contrast-checker > .notice { - margin: 0; -} diff --git a/blocks/index.js b/blocks/index.js index 182be4f09f99f..624f66f8396ad 100644 --- a/blocks/index.js +++ b/blocks/index.js @@ -1,8 +1,3 @@ -/** - * Internal dependencies - */ -import './hooks'; - // A "block" is the abstract term used to describe units of markup that, // when composed together, form the content or layout of a page. // The API for blocks is exposed via `wp.blocks`. @@ -13,27 +8,6 @@ import './hooks'; // Blocks are inferred from the HTML source of a post through a parsing mechanism // and then stored as objects in state, from which it is then rendered for editing. export * from './api'; -export * from './autocompleters'; -export * from './colors'; export { default as editorMediaUpload } from './editor-media-upload'; -export { default as AlignmentToolbar } from './alignment-toolbar'; -export { default as Autocomplete } from './autocomplete'; -export { default as BlockAlignmentToolbar } from './block-alignment-toolbar'; -export { default as BlockControls } from './block-controls'; -export { default as BlockFormatControls } from './block-format-controls'; -export { default as BlockEdit } from './block-edit'; -export { default as BlockIcon } from './block-icon'; -export { default as ColorPalette } from './color-palette'; -export { default as ContrastChecker } from './contrast-checker'; -export { default as ImagePlaceholder } from './image-placeholder'; -export { default as InnerBlocks } from './inner-blocks'; -export { default as InspectorControls } from './inspector-controls'; -export { default as InspectorAdvancedControls } from './inspector-advanced-controls'; -export { default as PlainText } from './plain-text'; -export { default as MediaUpload } from './media-upload'; -export { default as RichText } from './rich-text'; -export { default as RichTextProvider } from './rich-text/provider'; -export { default as UrlInput } from './url-input'; -export { default as UrlInputButton } from './url-input/button'; export { default as EditorSettings, withEditorSettings } from './editor-settings'; diff --git a/blocks/index.native.js b/blocks/index.native.js new file mode 100644 index 0000000000000..2219a7dcfd3a9 --- /dev/null +++ b/blocks/index.native.js @@ -0,0 +1,11 @@ +// A "block" is the abstract term used to describe units of markup that, +// when composed together, form the content or layout of a page. +// The API for blocks is exposed via `wp.blocks`. +// +// Supported blocks are registered by calling `registerBlockType`. Once registered, +// the block is made available as an option to the editor interface. +// +// Blocks are inferred from the HTML source of a post through a parsing mechanism +// and then stored as objects in state, from which it is then rendered for editing. +export * from './api'; + diff --git a/components/autocomplete/README.md b/components/autocomplete/README.md index a3a3f8a979b9f..e71da089c7909 100644 --- a/components/autocomplete/README.md +++ b/components/autocomplete/README.md @@ -91,6 +91,13 @@ A class name to apply to the autocompletion popup menu. - Type: `String` - Required: No +#### isDebounced + +Whether to apply debouncing for the autocompleter. Set to true to enable debouncing. + +- Type: `Boolean` +- Required: No + ### Examples The following is a contrived completer for fresh fruit. diff --git a/components/autocomplete/completer-compat.js b/components/autocomplete/completer-compat.js deleted file mode 100644 index aedffd61b16d9..0000000000000 --- a/components/autocomplete/completer-compat.js +++ /dev/null @@ -1,61 +0,0 @@ -/** - * This mod - */ - -/** - * WordPress dependencies. - */ -import { deprecated } from '@wordpress/utils'; - -const generateCompleterName = ( () => { - let count = 0; - return () => `backcompat-completer-${ count++ }`; -} )(); - -export function isDeprecatedCompleter( completer ) { - return 'onSelect' in completer; -} - -export function toCompatibleCompleter( deprecatedCompleter ) { - deprecated( 'Original autocompleter interface in wp.components.Autocomplete', { - version: '2.8', - alternative: 'latest autocompleter interface', - plugin: 'Gutenberg', - link: 'https://github.com/WordPress/gutenberg/blob/master/components/autocomplete/README.md', - } ); - - const optionalProperties = [ 'className', 'allowNode', 'allowContext' ] - .filter( ( key ) => key in deprecatedCompleter ) - .reduce( ( properties, key ) => { - return { - ...properties, - [ key ]: deprecatedCompleter[ key ], - }; - }, {} ); - - return { - name: generateCompleterName(), - triggerPrefix: deprecatedCompleter.triggerPrefix, - - options() { - return deprecatedCompleter.getOptions(); - }, - - getOptionLabel( option ) { - return option.label; - }, - - getOptionKeywords( option ) { - return option.keywords; - }, - - getOptionCompletion() { - return { - action: 'backcompat', - value: deprecatedCompleter.onSelect.bind( deprecatedCompleter ), - }; - }, - - ...optionalProperties, - }; -} diff --git a/components/autocomplete/index.js b/components/autocomplete/index.js index 514c0e55bf456..b08b10899105b 100644 --- a/components/autocomplete/index.js +++ b/components/autocomplete/index.js @@ -15,7 +15,6 @@ import { __, _n, sprintf } from '@wordpress/i18n'; * Internal dependencies */ import './style.scss'; -import { isDeprecatedCompleter, toCompatibleCompleter } from './completer-compat'; import withFocusOutside from '../higher-order/with-focus-outside'; import Button from '../button'; import Popover from '../popover'; @@ -207,36 +206,6 @@ export class Autocomplete extends Component { }; } - /* - * NOTE: This is necessary for backwards compatibility with the - * previous completer interface. Once we no longer support the - * old interface, we should be able to use the `completers` prop - * directly. - */ - static getDerivedStateFromProps( nextProps, prevState ) { - const { completers: nextCompleters } = nextProps; - const { lastAppliedCompleters } = prevState; - - if ( nextCompleters !== lastAppliedCompleters ) { - let completers = nextCompleters; - - if ( completers.some( isDeprecatedCompleter ) ) { - completers = completers.map( ( completer ) => { - return isDeprecatedCompleter( completer ) ? - toCompatibleCompleter( completer ) : - completer; - } ); - } - - return { - completers, - lastAppliedCompleters: nextCompleters, - }; - } - - return null; - } - constructor() { super( ...arguments ); @@ -466,7 +435,8 @@ export class Autocomplete extends Component { } search( event ) { - const { completers, open: wasOpen, suppress: wasSuppress, query: wasQuery } = this.state; + const { completers } = this.props; + const { open: wasOpen, suppress: wasSuppress, query: wasQuery } = this.state; const container = event.target; // ensure that the cursor location is unambiguous diff --git a/components/button/index.js b/components/button/index.js index 26d576b78d47e..d824433d4f502 100644 --- a/components/button/index.js +++ b/components/button/index.js @@ -6,67 +6,47 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { Component, createElement } from '@wordpress/element'; +import { createElement, forwardRef } from '@wordpress/element'; /** * Internal dependencies */ import './style.scss'; -class Button extends Component { - constructor( props ) { - super( props ); - this.setRef = this.setRef.bind( this ); - } - - componentDidMount() { - if ( this.props.focus ) { - this.ref.focus(); - } - } - - setRef( ref ) { - this.ref = ref; - } - - focus() { - this.ref.focus(); - } - - render() { - const { - href, - target, - isPrimary, - isLarge, - isSmall, - isToggled, - isBusy, - className, - disabled, - ...additionalProps - } = this.props; - const classes = classnames( 'components-button', className, { - button: ( isPrimary || isLarge || isSmall ), - 'button-primary': isPrimary, - 'button-large': isLarge, - 'button-small': isSmall, - 'is-toggled': isToggled, - 'is-busy': isBusy, - } ); - - const tag = href !== undefined && ! disabled ? 'a' : 'button'; - const tagProps = tag === 'a' ? { href, target } : { type: 'button', disabled }; - - delete additionalProps.focus; - - return createElement( tag, { - ...tagProps, - ...additionalProps, - className: classes, - ref: this.setRef, - } ); - } +export function Button( props, ref ) { + const { + href, + target, + isPrimary, + isLarge, + isSmall, + isToggled, + isBusy, + className, + disabled, + focus, + ...additionalProps + } = props; + + const classes = classnames( 'components-button', className, { + button: ( isPrimary || isLarge || isSmall ), + 'button-primary': isPrimary, + 'button-large': isLarge, + 'button-small': isSmall, + 'is-toggled': isToggled, + 'is-busy': isBusy, + } ); + + const tag = href !== undefined && ! disabled ? 'a' : 'button'; + const tagProps = tag === 'a' ? { href, target } : { type: 'button', disabled }; + + return createElement( tag, { + ...tagProps, + ...additionalProps, + className: classes, + autoFocus: focus, + ref, + } ); } -export default Button; +export default forwardRef( Button ); diff --git a/components/button/style.scss b/components/button/style.scss index f891e35353088..5f35afa9c610d 100644 --- a/components/button/style.scss +++ b/components/button/style.scss @@ -14,11 +14,11 @@ opacity: 0.3; } - &:not( :disabled ) { + &:not( :disabled ):not( [aria-disabled="true"] ) { cursor: pointer; } - &:focus { + &:not( :disabled ):not( [aria-disabled="true"] ):focus { @include button-style__focus-active; } diff --git a/components/button/test/index.js b/components/button/test/index.js index 089d40721ec98..1d2848f77c5da 100644 --- a/components/button/test/index.js +++ b/components/button/test/index.js @@ -1,12 +1,20 @@ /** * External dependencies */ -import { shallow } from 'enzyme'; +import { mount, shallow } from 'enzyme'; + +/** + * WordPress dependencies + */ +import { createRef } from '@wordpress/element'; /** * Internal dependencies */ -import Button from '../'; +import ButtonWithForwardedRef, { Button } from '../'; + +// [TEMPORARY]: Only needed so long as Enzyme does not support React.forwardRef +jest.unmock( '../' ); describe( 'Button', () => { describe( 'basic rendering', () => { @@ -93,4 +101,18 @@ describe( 'Button', () => { expect( button.type() ).toBe( 'button' ); } ); } ); + + // Disable reason: This test is desirable, but unsupported by Enzyme in + // the current version, as it depends on features new to React in 16.3.0. + // + // eslint-disable-next-line jest/no-disabled-tests + describe.skip( 'ref forwarding', () => { + it( 'should enable access to DOM element', () => { + const ref = createRef(); + + mount( ); + + expect( ref.current.nodeName ).toBe( 'button' ); + } ); + } ); } ); diff --git a/components/clipboard-button/index.js b/components/clipboard-button/index.js index 79f550acc60cb..c974c9a21595a 100644 --- a/components/clipboard-button/index.js +++ b/components/clipboard-button/index.js @@ -12,7 +12,8 @@ import { Component } from '@wordpress/element'; /** * Internal dependencies */ -import { Button } from '../'; +import IconButton from '../icon-button'; +import Button from '../button'; class ClipboardButton extends Component { constructor() { @@ -78,13 +79,15 @@ class ClipboardButton extends Component { // Disable reason: Exclude from spread props passed to Button // eslint-disable-next-line no-unused-vars const { className, children, onCopy, onFinishCopy, text, ...buttonProps } = this.props; + const { icon } = buttonProps; const classes = classnames( 'components-clipboard-button', className ); + const ComponentToUse = icon ? IconButton : Button; return ( - + ); } diff --git a/components/code-editor/test/editor.js b/components/code-editor/test/editor.js index 8579eb42b2f4f..623fff8da0b51 100644 --- a/components/code-editor/test/editor.js +++ b/components/code-editor/test/editor.js @@ -11,7 +11,7 @@ import CodeEditor from '../editor'; describe( 'CodeEditor', () => { it( 'should render without an error', () => { - set( global, 'wp.codeEditor.initialize', () => ( { + set( global, [ 'wp', 'codeEditor', 'initialize' ], () => ( { codemirror: { on: noop, hasFocus: () => false, diff --git a/components/color-palette/index.js b/components/color-palette/index.js new file mode 100644 index 0000000000000..b63db30d6f014 --- /dev/null +++ b/components/color-palette/index.js @@ -0,0 +1,84 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; +import { ChromePicker } from 'react-color'; +import { map } from 'lodash'; + +/** + * WordPress dependencies + */ +import Dropdown from '../dropdown'; +import Tooltip from '../tooltip'; +import { __, sprintf } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import './style.scss'; + +export default function ColorPalette( { colors, disableCustomColors = false, value, onChange } ) { + function applyOrUnset( color ) { + return () => onChange( value === color ? undefined : color ); + } + const customColorPickerLabel = __( 'Custom color picker' ); + return ( +
+ { map( colors, ( { color, name } ) => { + const style = { color: color }; + const className = classnames( 'components-color-palette__item', { 'is-active': value === color } ); + + return ( +
+ +
+ ); + } ) } + + { ! disableCustomColors && + ( + + + + ) } + renderContent={ () => ( + onChange( color.hex ) } + style={ { width: '100%' } } + disableAlpha + /> + ) } + /> + } + + +
+ ); +} diff --git a/blocks/color-palette/style.scss b/components/color-palette/style.scss similarity index 85% rename from blocks/color-palette/style.scss rename to components/color-palette/style.scss index adaf230671865..d895f7f7d7ec7 100644 --- a/blocks/color-palette/style.scss +++ b/components/color-palette/style.scss @@ -1,16 +1,16 @@ $color-palette-circle-size: 28px; $color-palette-circle-spacing: 14px; -.blocks-color-palette { +.components-color-palette { margin-right: -14px; - .blocks-color-palette__clear { + .components-color-palette__clear { float: right; margin-right: 20px; } } -.blocks-color-palette__item-wrapper { +.components-color-palette__item-wrapper { display: inline-block; height: $color-palette-circle-size; width: $color-palette-circle-size; @@ -30,7 +30,7 @@ $color-palette-circle-spacing: 14px; } } -.blocks-color-palette__item { +.components-color-palette__item { display: inline-block; vertical-align: top; height: 100%; @@ -72,12 +72,12 @@ $color-palette-circle-spacing: 14px; } } -.blocks-color-palette__clear-color .blocks-color-palette__item { +.components-color-palette__clear-color .components-color-palette__item { color: $white; background: $white; } -.blocks-color-palette__clear-color-line { +.components-color-palette__clear-color-line { display: block; position: absolute; border: 2px solid $alert-red; @@ -101,12 +101,12 @@ $color-palette-circle-spacing: 14px; } } -.blocks-color-palette__custom-color .blocks-color-palette__item { +.components-color-palette__custom-color .components-color-palette__item { position: relative; box-shadow: none; } -.blocks-color-palette__custom-color .blocks-color-palette__custom-color-gradient { +.components-color-palette__custom-color .components-color-palette__custom-color-gradient { display: block; width: 100%; height: 100%; @@ -117,7 +117,7 @@ $color-palette-circle-spacing: 14px; overflow: hidden; } -.blocks-color-palette__custom-color .blocks-color-palette__custom-color-gradient:before { +.components-color-palette__custom-color .components-color-palette__custom-color-gradient:before { box-sizing: border-box; content: ''; filter: blur( 6px ) saturate( 0.7 ) brightness( 1.1 ); diff --git a/components/color-palette/test/__snapshots__/index.js.snap b/components/color-palette/test/__snapshots__/index.js.snap new file mode 100644 index 0000000000000..74f3cf00b2636 --- /dev/null +++ b/components/color-palette/test/__snapshots__/index.js.snap @@ -0,0 +1,224 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ColorPalette Dropdown .renderContent should render dropdown content 1`] = ` + +`; + +exports[`ColorPalette Dropdown .renderToggle should render dropdown content 1`] = ` + +`; + +exports[`ColorPalette Dropdown should render it correctly 1`] = ` + +`; + +exports[`ColorPalette should allow disabling custom color picker 1`] = ` +
+
+ +
+
+ +
+
+ +
+ +
+`; + +exports[`ColorPalette should render a dynamic toolbar of colors 1`] = ` +
+
+ +
+
+ +
+
+ +
+ + +
+`; diff --git a/blocks/color-palette/test/index.js b/components/color-palette/test/index.js similarity index 81% rename from blocks/color-palette/test/index.js rename to components/color-palette/test/index.js index f8bc82d7ce8e7..cc38b7c095a2a 100644 --- a/blocks/color-palette/test/index.js +++ b/components/color-palette/test/index.js @@ -6,15 +6,15 @@ import { shallow } from 'enzyme'; /** * Internal dependencies */ -import { ColorPalette } from '../'; +import ColorPalette from '../'; describe( 'ColorPalette', () => { - const colors = [ { name: 'red', color: 'red' }, { name: 'white', color: 'white' }, { name: 'blue', color: 'blue' } ]; - const currentColor = 'red'; + const colors = [ { name: 'red', color: '#f00' }, { name: 'white', color: '#fff' }, { name: 'blue', color: '#00f' } ]; + const currentColor = '#f00'; const onChange = jest.fn(); const wrapper = shallow( ); - const buttons = wrapper.find( '.blocks-color-palette__item-wrapper > button' ); + const buttons = wrapper.find( '.components-color-palette__item-wrapper button' ); beforeEach( () => { onChange.mockClear(); @@ -44,7 +44,7 @@ describe( 'ColorPalette', () => { } ); test( 'should call onClick with undefined, when the clearButton onClick is triggered', () => { - const clearButton = wrapper.find( '.button-link.blocks-color-palette__clear' ); + const clearButton = wrapper.find( '.button-link.components-color-palette__clear' ); expect( clearButton ).toHaveLength( 1 ); @@ -69,14 +69,14 @@ describe( 'ColorPalette', () => { const isOpen = true; const onToggle = jest.fn(); - const renderedToggle = shallow( dropdown.props().renderToggle( { isOpen, onToggle } ) ); + const renderedToggleButton = shallow( dropdown.props().renderToggle( { isOpen, onToggle } ).props.children ); test( 'should render dropdown content', () => { - expect( renderedToggle ).toMatchSnapshot(); + expect( renderedToggleButton ).toMatchSnapshot(); } ); test( 'should call onToggle on click.', () => { - renderedToggle.simulate( 'click' ); + renderedToggleButton.simulate( 'click' ); expect( onToggle ).toHaveBeenCalledTimes( 1 ); } ); diff --git a/components/drop-zone/provider.js b/components/drop-zone/provider.js index 7c8b8cca259af..4629c1a02917c 100644 --- a/components/drop-zone/provider.js +++ b/components/drop-zone/provider.js @@ -2,12 +2,12 @@ * External dependencies */ import { isEqual, find, some, filter, noop, throttle } from 'lodash'; -import isShallowEqual from 'shallowequal'; /** * WordPress dependencies */ import { Component, findDOMNode } from '@wordpress/element'; +import isShallowEqual from '@wordpress/is-shallow-equal'; class DropZoneProvider extends Component { constructor() { diff --git a/components/font-size-picker/README.md b/components/font-size-picker/README.md new file mode 100644 index 0000000000000..e3edbdf493911 --- /dev/null +++ b/components/font-size-picker/README.md @@ -0,0 +1,61 @@ +FontSizePicker +======== + +FontSizePicker is a React component that renders a UI that allows users to select a font size. +The component renders a series of buttons that allow the user to select predefined (common) font sizes and contains a range slider that enables the user to select custom font sizes (by choosing the value. + +## Usage + + +```jsx +import { Dropdown } from '@wordpress/components'; + +function MyFontSizePicker() { + return ( + + ); +} +``` + +## Props + +The component accepts the following props: + +### fontSizes + +An array of font size objects. The object should contain properties size, name, shortName. +The property "size" contains a number with the font size value. The "shortName" property includes a small label used in the buttons. Property "name" is used as the label when shortName is not provided. + +- Type: `Array` +- Required: No + +### fallbackFontSize + +In no value exists this prop contains the font size picker slider starting position. + +- Type: `Number` +- Required: No + +### value + +The current font size value. If a button value matches the font size value that button is pressed. RangeControl is rendered with this value. + +- Type: `Number` +- Required: No + +## onChange + +A function that receives the new font size value. +If onChange is called without any parameter, it should reset the value, attending to what reset means in that context, e.g., set the font size to undefined or set the font size a starting value. + +- Type: `function` +- Required: Yes + diff --git a/components/font-size-picker/index.js b/components/font-size-picker/index.js new file mode 100644 index 0000000000000..30f34ebb25cf1 --- /dev/null +++ b/components/font-size-picker/index.js @@ -0,0 +1,57 @@ +/** + * External dependencies + */ +import { map } from 'lodash'; + +/** + * WordPress dependencies + */ +import { Fragment } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import './style.scss'; +import Button from '../button'; +import ButtonGroup from '../button-group'; +import RangeControl from '../range-control'; + +export default function FontSizePicker( { fontSizes = [], fallbackFontSize, value, onChange } ) { + return ( + +
+ + { map( fontSizes, ( { name, size, shortName } ) => ( + + ) ) } + + +
+ +
+ ); +} diff --git a/core-blocks/paragraph/editor.scss b/components/font-size-picker/style.scss similarity index 65% rename from core-blocks/paragraph/editor.scss rename to components/font-size-picker/style.scss index a93686fa68e21..8a522831093dd 100644 --- a/core-blocks/paragraph/editor.scss +++ b/components/font-size-picker/style.scss @@ -1,10 +1,10 @@ -.blocks-font-size__main { +.components-font-size-picker__buttons { display: flex; justify-content: space-between; margin-bottom: 1em; } -.blocks-paragraph__custom-size-slider { +.components-font-size-picker__custom-input { .components-range-control__slider + .dashicon { width: 30px; height: 30px; diff --git a/components/icon-button/index.js b/components/icon-button/index.js index b9daa115f7cd3..c6c35863bab98 100644 --- a/components/icon-button/index.js +++ b/components/icon-button/index.js @@ -21,18 +21,25 @@ import Dashicon from '../dashicon'; // is common to apply a ref to the button element (only supported in class) class IconButton extends Component { render() { - const { icon, children, label, className, tooltip, focus, ...additionalProps } = this.props; + const { icon, children, label, className, tooltip, focus, shortcut, ...additionalProps } = this.props; const classes = classnames( 'components-icon-button', className ); const tooltipText = tooltip || label; - // Should show the tooltip if an explicit tooltip is passed - // or if there's a label and the children are empty and the tooltip is not explicitely disabled - const showTooltip = !! tooltip || + // Should show the tooltip if... + const showTooltip = ( + // an explicit tooltip is passed or... + tooltip || + // there's a shortcut or... + shortcut || ( - label && + // there's a label and... + !! label && + // the children are empty and... ( ! children || ( isArray( children ) && ! children.length ) ) && + // the tooltip is not explicitly disabled. false !== tooltip - ); + ) + ); let element = ( + + + { __( 'Upload' ) } + + ( + + ) } + /> + + ); + } + + /* eslint-disable jsx-a11y/no-static-element-interactions, jsx-a11y/onclick-has-role, jsx-a11y/click-events-have-key-events */ + return ( + + + + + + +
+
+
+ ); + /* eslint-enable jsx-a11y/no-static-element-interactions, jsx-a11y/onclick-has-role, jsx-a11y/click-events-have-key-events */ + } +} diff --git a/core-blocks/audio/index.js b/core-blocks/audio/index.js index 6f0f47fe37110..670041bb5078b 100644 --- a/core-blocks/audio/index.js +++ b/core-blocks/audio/index.js @@ -2,33 +2,20 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { - Button, - FormFileUpload, - IconButton, - Placeholder, - Toolbar, -} from '@wordpress/components'; -import { Component, Fragment } from '@wordpress/element'; -import { - editorMediaUpload, - MediaUpload, - RichText, - BlockControls, -} from '@wordpress/blocks'; +import { RichText } from '@wordpress/editor'; /** * Internal dependencies */ import './style.scss'; -import './editor.scss'; +import edit from './edit'; export const name = 'core/audio'; export const settings = { title: __( 'Audio' ), - description: __( 'The Audio block allows you to embed audio files and play them back using a simple player.' ), + description: __( 'Embed an audio file and a simple audio player.' ), icon: 'format-audio', @@ -55,116 +42,7 @@ export const settings = { align: true, }, - edit: class extends Component { - constructor() { - super( ...arguments ); - // edit component has its own src in the state so it can be edited - // without setting the actual value outside of the edit UI - this.state = { - editing: ! this.props.attributes.src, - src: this.props.attributes.src, - }; - } - - render() { - const { caption, id } = this.props.attributes; - const { setAttributes, isSelected, className } = this.props; - const { editing, src } = this.state; - const switchToEditing = () => { - this.setState( { editing: true } ); - }; - const onSelectAudio = ( media ) => { - if ( media && media.url ) { - // sets the block's attribute and updates the edit component from the - // selected media, then switches off the editing UI - setAttributes( { src: media.url, id: media.id } ); - this.setState( { src: media.url, editing: false } ); - } - }; - const onSelectUrl = ( event ) => { - event.preventDefault(); - if ( src ) { - // set the block's src from the edit component's state, and switch off the editing UI - setAttributes( { src } ); - this.setState( { editing: false } ); - } - return false; - }; - const setAudio = ( [ audio ] ) => onSelectAudio( audio ); - const uploadFromFiles = ( event ) => editorMediaUpload( event.target.files, setAudio, 'audio' ); - - if ( editing ) { - return ( - -
- this.setState( { src: event.target.value } ) } - value={ src || '' } /> - -
- - { __( 'Upload' ) } - - ( - - ) } - /> -
- ); - } - - /* eslint-disable jsx-a11y/no-static-element-interactions, jsx-a11y/onclick-has-role, jsx-a11y/click-events-have-key-events */ - return ( - - - - - - -
-
-
- ); - /* eslint-enable jsx-a11y/no-static-element-interactions, jsx-a11y/onclick-has-role, jsx-a11y/click-events-have-key-events */ - } - }, + edit, save( { attributes } ) { const { src, caption } = attributes; diff --git a/core-blocks/block/edit.js b/core-blocks/block/edit.js new file mode 100644 index 0000000000000..deb56593f1827 --- /dev/null +++ b/core-blocks/block/edit.js @@ -0,0 +1,169 @@ +/** + * External dependencies + */ +import { noop, partial } from 'lodash'; + +/** + * WordPress dependencies + */ +import { Component, Fragment, compose } from '@wordpress/element'; +import { Placeholder, Spinner, Disabled } from '@wordpress/components'; +import { withSelect, withDispatch } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; +import { BlockEdit } from '@wordpress/editor'; + +/** + * Internal dependencies + */ +import SharedBlockEditPanel from './edit-panel'; +import SharedBlockIndicator from './indicator'; + +class SharedBlockEdit extends Component { + constructor( { sharedBlock } ) { + super( ...arguments ); + + this.startEditing = this.startEditing.bind( this ); + this.stopEditing = this.stopEditing.bind( this ); + this.setAttributes = this.setAttributes.bind( this ); + this.setTitle = this.setTitle.bind( this ); + this.save = this.save.bind( this ); + + this.state = { + isEditing: !! ( sharedBlock && sharedBlock.isTemporary ), + title: null, + changedAttributes: null, + }; + } + + componentDidMount() { + if ( ! this.props.sharedBlock ) { + this.props.fetchSharedBlock(); + } + } + + startEditing() { + const { sharedBlock } = this.props; + + this.setState( { + isEditing: true, + title: sharedBlock.title, + changedAttributes: {}, + } ); + } + + stopEditing() { + this.setState( { + isEditing: false, + title: null, + changedAttributes: null, + } ); + } + + setAttributes( attributes ) { + this.setState( ( prevState ) => { + if ( prevState.changedAttributes !== null ) { + return { changedAttributes: { ...prevState.changedAttributes, ...attributes } }; + } + } ); + } + + setTitle( title ) { + this.setState( { title } ); + } + + save() { + const { sharedBlock, onUpdateTitle, updateAttributes, block, onSave } = this.props; + const { title, changedAttributes } = this.state; + + if ( title !== sharedBlock.title ) { + onUpdateTitle( title ); + } + + updateAttributes( block.uid, changedAttributes ); + onSave(); + + this.stopEditing(); + } + + render() { + const { isSelected, sharedBlock, block, isFetching, isSaving } = this.props; + const { isEditing, title, changedAttributes } = this.state; + + if ( ! sharedBlock && isFetching ) { + return ; + } + + if ( ! sharedBlock || ! block ) { + return { __( 'Block has been deleted or is unavailable.' ) }; + } + + let element = ( + + ); + + if ( ! isEditing ) { + element = { element }; + } + + return ( + + { element } + { ( isSelected || isEditing ) && ( + + ) } + { ! isSelected && ! isEditing && } + + ); + } +} + +export default compose( [ + withSelect( ( select, ownProps ) => { + const { + getSharedBlock, + isFetchingSharedBlock, + isSavingSharedBlock, + getBlock, + } = select( 'core/editor' ); + const { ref } = ownProps.attributes; + const sharedBlock = getSharedBlock( ref ); + + return { + sharedBlock, + isFetching: isFetchingSharedBlock( ref ), + isSaving: isSavingSharedBlock( ref ), + block: sharedBlock ? getBlock( sharedBlock.uid ) : null, + }; + } ), + withDispatch( ( dispatch, ownProps ) => { + const { + fetchSharedBlocks, + updateBlockAttributes, + updateSharedBlockTitle, + saveSharedBlock, + } = dispatch( 'core/editor' ); + const { ref } = ownProps.attributes; + + return { + fetchSharedBlock: partial( fetchSharedBlocks, ref ), + updateAttributes: updateBlockAttributes, + onUpdateTitle: partial( updateSharedBlockTitle, ref ), + onSave: partial( saveSharedBlock, ref ), + }; + } ), +] )( SharedBlockEdit ); diff --git a/core-blocks/block/index.js b/core-blocks/block/index.js index 971ee4e3cd4d4..20fffc3dcaf3c 100644 --- a/core-blocks/block/index.js +++ b/core-blocks/block/index.js @@ -1,172 +1,12 @@ -/** - * External dependencies - */ -import { noop, partial } from 'lodash'; - /** * WordPress dependencies */ -import { Component, Fragment, compose } from '@wordpress/element'; -import { Placeholder, Spinner, Disabled } from '@wordpress/components'; -import { withSelect, withDispatch } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; -import { BlockEdit } from '@wordpress/blocks'; /** * Internal dependencies */ -import SharedBlockEditPanel from './edit-panel'; -import SharedBlockIndicator from './indicator'; - -class SharedBlockEdit extends Component { - constructor( { sharedBlock } ) { - super( ...arguments ); - - this.startEditing = this.startEditing.bind( this ); - this.stopEditing = this.stopEditing.bind( this ); - this.setAttributes = this.setAttributes.bind( this ); - this.setTitle = this.setTitle.bind( this ); - this.save = this.save.bind( this ); - - this.state = { - isEditing: !! ( sharedBlock && sharedBlock.isTemporary ), - title: null, - changedAttributes: null, - }; - } - - componentDidMount() { - if ( ! this.props.sharedBlock ) { - this.props.fetchSharedBlock(); - } - } - - startEditing() { - const { sharedBlock } = this.props; - - this.setState( { - isEditing: true, - title: sharedBlock.title, - changedAttributes: {}, - } ); - } - - stopEditing() { - this.setState( { - isEditing: false, - title: null, - changedAttributes: null, - } ); - } - - setAttributes( attributes ) { - this.setState( ( prevState ) => { - if ( prevState.changedAttributes !== null ) { - return { changedAttributes: { ...prevState.changedAttributes, ...attributes } }; - } - } ); - } - - setTitle( title ) { - this.setState( { title } ); - } - - save() { - const { sharedBlock, onUpdateTitle, updateAttributes, block, onSave } = this.props; - const { title, changedAttributes } = this.state; - - if ( title !== sharedBlock.title ) { - onUpdateTitle( title ); - } - - updateAttributes( block.uid, changedAttributes ); - onSave(); - - this.stopEditing(); - } - - render() { - const { isSelected, sharedBlock, block, isFetching, isSaving } = this.props; - const { isEditing, title, changedAttributes } = this.state; - - if ( ! sharedBlock && isFetching ) { - return ; - } - - if ( ! sharedBlock || ! block ) { - return { __( 'Block has been deleted or is unavailable.' ) }; - } - - let element = ( - - ); - - if ( ! isEditing ) { - element = { element }; - } - - return ( - - { element } - { ( isSelected || isEditing ) && ( - - ) } - { ! isSelected && ! isEditing && } - - ); - } -} - -const EnhancedSharedBlockEdit = compose( [ - withSelect( ( select, ownProps ) => { - const { - getSharedBlock, - isFetchingSharedBlock, - isSavingSharedBlock, - getBlock, - } = select( 'core/editor' ); - const { ref } = ownProps.attributes; - const sharedBlock = getSharedBlock( ref ); - - return { - sharedBlock, - isFetching: isFetchingSharedBlock( ref ), - isSaving: isSavingSharedBlock( ref ), - block: sharedBlock ? getBlock( sharedBlock.uid ) : null, - }; - } ), - withDispatch( ( dispatch, ownProps ) => { - const { - fetchSharedBlocks, - updateBlockAttributes, - updateSharedBlockTitle, - saveSharedBlock, - } = dispatch( 'core/editor' ); - const { ref } = ownProps.attributes; - - return { - fetchSharedBlock: partial( fetchSharedBlocks, ref ), - updateAttributes: updateBlockAttributes, - onUpdateTitle: partial( updateSharedBlockTitle, ref ), - onSave: partial( saveSharedBlock, ref ), - }; - } ), -] )( SharedBlockEdit ); +import edit from './edit'; export const name = 'core/block'; @@ -186,6 +26,7 @@ export const settings = { html: false, }, - edit: EnhancedSharedBlockEdit, + edit, + save: () => null, }; diff --git a/core-blocks/button/edit.js b/core-blocks/button/edit.js new file mode 100644 index 0000000000000..8823f96f9749b --- /dev/null +++ b/core-blocks/button/edit.js @@ -0,0 +1,167 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { Component, Fragment } from '@wordpress/element'; +import { + Dashicon, + IconButton, + PanelBody, + ToggleControl, + withFallbackStyles, +} from '@wordpress/components'; +import { + UrlInput, + RichText, + BlockControls, + BlockAlignmentToolbar, + ContrastChecker, + InspectorControls, + withColors, + PanelColor, +} from '@wordpress/editor'; + +/** + * Internal dependencies + */ +import './editor.scss'; + +const { getComputedStyle } = window; + +const ContrastCheckerWithFallbackStyles = withFallbackStyles( ( node, ownProps ) => { + const { textColor, backgroundColor } = ownProps; + //avoid the use of querySelector if textColor color is known and verify if node is available. + const textNode = ! textColor && node ? node.querySelector( '[contenteditable="true"]' ) : null; + return { + fallbackBackgroundColor: backgroundColor || ! node ? undefined : getComputedStyle( node ).backgroundColor, + fallbackTextColor: textColor || ! textNode ? undefined : getComputedStyle( textNode ).color, + }; +} )( ContrastChecker ); + +class ButtonEdit extends Component { + constructor() { + super( ...arguments ); + this.nodeRef = null; + this.bindRef = this.bindRef.bind( this ); + this.updateAlignment = this.updateAlignment.bind( this ); + this.toggleClear = this.toggleClear.bind( this ); + } + + updateAlignment( nextAlign ) { + this.props.setAttributes( { align: nextAlign } ); + } + + toggleClear() { + const { attributes, setAttributes } = this.props; + setAttributes( { clear: ! attributes.clear } ); + } + + bindRef( node ) { + if ( ! node ) { + return; + } + this.nodeRef = node; + } + + render() { + const { + attributes, + backgroundColor, + textColor, + setBackgroundColor, + setTextColor, + setAttributes, + isSelected, + className, + } = this.props; + + const { + text, + url, + title, + align, + clear, + } = attributes; + + return ( + + + + + + setAttributes( { text: value } ) } + formattingControls={ [ 'bold', 'italic', 'strikethrough' ] } + className={ classnames( + 'wp-block-button__link', { + 'has-background': backgroundColor.value, + [ backgroundColor.class ]: backgroundColor.class, + 'has-text-color': textColor.value, + [ textColor.class ]: textColor.class, + } + ) } + style={ { + backgroundColor: backgroundColor.class ? undefined : backgroundColor.value, + color: textColor.class ? undefined : textColor.value, + } } + keepPlaceholderOnFocus + /> + + + + + + { this.nodeRef && } + + + + { isSelected && ( +
event.preventDefault() }> + + setAttributes( { url: value } ) } + /> + + + ) } +
+ ); + } +} + +export default withColors( ( getColor, setColor, { attributes, setAttributes } ) => { + return { + backgroundColor: getColor( attributes.backgroundColor, attributes.customBackgroundColor, 'background-color' ), + setBackgroundColor: setColor( 'backgroundColor', 'customBackgroundColor', setAttributes ), + textColor: getColor( attributes.textColor, attributes.customTextColor, 'color' ), + setTextColor: setColor( 'textColor', 'customTextColor', setAttributes ), + }; +} )( ButtonEdit ); diff --git a/core-blocks/button/editor.scss b/core-blocks/button/editor.scss index 2522496515dc5..a899a488b36f6 100644 --- a/core-blocks/button/editor.scss +++ b/core-blocks/button/editor.scss @@ -13,7 +13,7 @@ margin-bottom: 0; position: relative; - .blocks-rich-text__tinymce { + .editor-rich-text__tinymce { cursor: text; } } @@ -30,13 +30,13 @@ $blocks-button__link-input-width: 280px; width: $blocks-button__link-input-width; - .blocks-url-input { + .editor-url-input { width: auto; } - .blocks-url-input__suggestions { + .editor-url-input__suggestions { width: $blocks-button__link-input-width - $icon-button-size - $icon-button-size; - z-index: z-index( '.blocks-button__inline-link .blocks-url-input__suggestions' ); + z-index: z-index( '.blocks-button__inline-link .editor-url-input__suggestions' ); } > .dashicon { @@ -47,7 +47,7 @@ color: $dark-gray-100; } - .blocks-url-input input[type=text]::placeholder { + .editor-url-input input[type=text]::placeholder { color: $dark-gray-100; } diff --git a/core-blocks/button/index.js b/core-blocks/button/index.js index f18432689dd3c..4cce323c99c31 100644 --- a/core-blocks/button/index.js +++ b/core-blocks/button/index.js @@ -1,150 +1,23 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; +import { omit, pick } from 'lodash'; + /** * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { Component, Fragment } from '@wordpress/element'; -import { - Dashicon, - IconButton, - PanelBody, - PanelColor, - ToggleControl, - withFallbackStyles, -} from '@wordpress/components'; import { - UrlInput, RichText, - BlockControls, - BlockAlignmentToolbar, - ColorPalette, - ContrastChecker, - InspectorControls, -} from '@wordpress/blocks'; + getColorClass, +} from '@wordpress/editor'; /** * Internal dependencies */ -import './editor.scss'; import './style.scss'; - -const { getComputedStyle } = window; - -const ContrastCheckerWithFallbackStyles = withFallbackStyles( ( node, ownProps ) => { - const { textColor, backgroundColor } = ownProps; - //avoid the use of querySelector if textColor color is known and verify if node is available. - const textNode = ! textColor && node ? node.querySelector( '[contenteditable="true"]' ) : null; - return { - fallbackBackgroundColor: backgroundColor || ! node ? undefined : getComputedStyle( node ).backgroundColor, - fallbackTextColor: textColor || ! textNode ? undefined : getComputedStyle( textNode ).color, - }; -} )( ContrastChecker ); - -class ButtonBlock extends Component { - constructor() { - super( ...arguments ); - this.nodeRef = null; - this.bindRef = this.bindRef.bind( this ); - this.updateAlignment = this.updateAlignment.bind( this ); - this.toggleClear = this.toggleClear.bind( this ); - } - - updateAlignment( nextAlign ) { - this.props.setAttributes( { align: nextAlign } ); - } - - toggleClear() { - const { attributes, setAttributes } = this.props; - setAttributes( { clear: ! attributes.clear } ); - } - - bindRef( node ) { - if ( ! node ) { - return; - } - this.nodeRef = node; - } - - render() { - const { - attributes, - setAttributes, - isSelected, - className, - } = this.props; - - const { - text, - url, - title, - align, - color, - textColor, - clear, - } = attributes; - - return ( - - - - - - setAttributes( { text: value } ) } - formattingControls={ [ 'bold', 'italic', 'strikethrough' ] } - className="wp-block-button__link" - style={ { - backgroundColor: color, - color: textColor, - } } - keepPlaceholderOnFocus - /> - - - - - setAttributes( { color: colorValue } ) } - /> - - - setAttributes( { textColor: colorValue } ) } - /> - - { this.nodeRef && } - - - - { isSelected && ( -
event.preventDefault() }> - - setAttributes( { url: value } ) } - /> - - - ) } -
- ); - } -} +import edit from './edit'; const blockAttributes = { url: { @@ -168,20 +41,34 @@ const blockAttributes = { type: 'string', default: 'none', }, - color: { + backgroundColor: { type: 'string', }, textColor: { type: 'string', }, + customBackgroundColor: { + type: 'string', + }, + customTextColor: { + type: 'string', + }, }; export const name = 'core/button'; +const colorsMigration = ( attributes ) => { + return omit( { + ...attributes, + customTextColor: attributes.textColor && '#' === attributes.textColor[ 0 ] ? attributes.textColor : undefined, + customBackgroundColor: attributes.color && '#' === attributes.color[ 0 ] ? attributes.color : undefined, + }, [ 'color', 'textColor' ] ); +}; + export const settings = { title: __( 'Button' ), - description: __( 'A nice little button. Call something out with it.' ), + description: __( 'Want visitors to click to subscribe, buy, or read more? Get their attention with a button.' ), icon: 'button', @@ -204,23 +91,40 @@ export const settings = { return props; }, - edit: ButtonBlock, + edit, save( { attributes } ) { - const { url, text, title, align, color, textColor } = attributes; + const { + url, + text, + title, + align, + backgroundColor, + textColor, + customBackgroundColor, + customTextColor, + } = attributes; + + const textClass = getColorClass( 'color', textColor ); + const backgroundClass = getColorClass( 'background-color', backgroundColor ); + + const buttonClasses = classnames( 'wp-block-button__link', { + 'has-text-color': textColor || customTextColor, + [ textClass ]: textClass, + 'has-background': backgroundColor || customBackgroundColor, + [ backgroundClass ]: backgroundClass, + } ); const buttonStyle = { - backgroundColor: color, - color: textColor, + backgroundColor: backgroundClass ? undefined : customBackgroundColor, + color: textClass ? undefined : customTextColor, }; - const linkClass = 'wp-block-button__link'; - return (
+ +
+ ); + }, + migrate: colorsMigration, + }, + { + attributes: { + ...pick( blockAttributes, [ 'url', 'title', 'text', 'align' ] ), + color: { + type: 'string', + }, + textColor: { + type: 'string', + }, + }, save( { attributes } ) { const { url, text, title, align, color, textColor } = attributes; @@ -248,5 +196,7 @@ export const settings = { ); }, - } ], + migrate: colorsMigration, + }, + ], }; diff --git a/core-blocks/button/style.scss b/core-blocks/button/style.scss index ff5f7fee05a58..bedd6a874fefb 100644 --- a/core-blocks/button/style.scss +++ b/core-blocks/button/style.scss @@ -5,11 +5,9 @@ $blocks-button__line-height: $big-font-size + 6px; margin-bottom: 1.5em; & .wp-block-button__link { - background-color: $dark-gray-700; border: none; border-radius: $blocks-button__height / 2; box-shadow: none; - color: $white; cursor: pointer; display: inline-block; font-size: $big-font-size; @@ -20,13 +18,6 @@ $blocks-button__line-height: $big-font-size + 6px; text-decoration: none; white-space: normal; word-break: break-all; - - &:hover, - &:focus, - &:active { - background-color: $dark-gray-700; - color: $white; - } } &.aligncenter { @@ -37,3 +28,21 @@ $blocks-button__line-height: $big-font-size + 6px; text-align: right; } } + +.wp-block-button__link:not(.has-background) { + background-color: $dark-gray-700; + &:hover, + &:focus, + &:active { + background-color: $dark-gray-700; + } +} + +.wp-block-button__link:not(.has-text-color) { + color: $white; + &:hover, + &:focus, + &:active { + color: $white; + } +} diff --git a/core-blocks/button/test/__snapshots__/index.js.snap b/core-blocks/button/test/__snapshots__/index.js.snap index 05d5185451e85..08b41dc9bc20a 100644 --- a/core-blocks/button/test/__snapshots__/index.js.snap +++ b/core-blocks/button/test/__snapshots__/index.js.snap @@ -5,7 +5,7 @@ exports[`core/button block edit matches snapshot 1`] = ` class="wp-block-button" >
@@ -17,13 +17,13 @@ exports[`core/button block edit matches snapshot 1`] = ` aria-expanded="false" aria-label="Add text…" aria-multiline="false" - class="wp-block-button__link blocks-rich-text__tinymce" + class="wp-block-button__link editor-rich-text__tinymce" contenteditable="true" data-is-placeholder-visible="true" role="textbox" /> Add text… diff --git a/core-blocks/categories/block.js b/core-blocks/categories/edit.js similarity index 97% rename from core-blocks/categories/block.js rename to core-blocks/categories/edit.js index 21e940834bb14..1e34203a9d136 100644 --- a/core-blocks/categories/block.js +++ b/core-blocks/categories/edit.js @@ -10,15 +10,14 @@ import { InspectorControls, BlockControls, BlockAlignmentToolbar, -} from '@wordpress/blocks'; +} from '@wordpress/editor'; /** * Internal dependencies */ import './editor.scss'; -import './style.scss'; -class CategoriesBlock extends Component { +class CategoriesEdit extends Component { constructor() { super( ...arguments ); @@ -215,4 +214,4 @@ export default withSelect( ( select ) => { categories: getCategories(), isRequesting: isRequestingCategories(), }; -} )( CategoriesBlock ); +} )( CategoriesEdit ); diff --git a/core-blocks/categories/index.js b/core-blocks/categories/index.js index d180b7df245df..b69986dbe1227 100644 --- a/core-blocks/categories/index.js +++ b/core-blocks/categories/index.js @@ -6,16 +6,15 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import './editor.scss'; import './style.scss'; -import CategoriesBlock from './block'; +import edit from './edit'; export const name = 'core/categories'; export const settings = { title: __( 'Categories' ), - description: __( 'Shows a list of your site\'s categories.' ), + description: __( 'Display a list of all your site\'s categories.' ), icon: 'list-view', @@ -50,7 +49,7 @@ export const settings = { } }, - edit: CategoriesBlock, + edit, save() { return null; diff --git a/core-blocks/code/edit.js b/core-blocks/code/edit.js new file mode 100644 index 0000000000000..be2828c5912f4 --- /dev/null +++ b/core-blocks/code/edit.js @@ -0,0 +1,23 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import './editor.scss'; +import { PlainText } from '@wordpress/editor'; + +export default function CodeEdit( { attributes, setAttributes, className } ) { + return ( +
+ setAttributes( { content } ) } + placeholder={ __( 'Write code…' ) } + aria-label={ __( 'Code' ) } + /> + </div> + ); +} diff --git a/core-blocks/code/edit.native.js b/core-blocks/code/edit.native.js new file mode 100644 index 0000000000000..39f31b496a206 --- /dev/null +++ b/core-blocks/code/edit.native.js @@ -0,0 +1,30 @@ +import { View } from 'react-native'; + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { PlainText } from '@wordpress/editor'; + +// Note: styling is applied directly to the (nested) PlainText component. Web-side components +// apply it to the container 'div' but we don't have a proper proposal for cascading styling yet. +export default function CodeEdit( { attributes, setAttributes, style } ) { + return ( + <View> + <PlainText + value={ attributes.content } + style={ style } + multiline={ true } + underlineColorAndroid="transparent" + onChange={ ( content ) => setAttributes( { content } ) } + placeholder={ __( 'Write code…' ) } + aria-label={ __( 'Code' ) } + /> + </View> + ); +} + diff --git a/core-blocks/code/editor.scss b/core-blocks/code/editor.scss index d354bdb7e2912..f5d46e8c4e845 100644 --- a/core-blocks/code/editor.scss +++ b/core-blocks/code/editor.scss @@ -1,4 +1,4 @@ -.wp-block-code .blocks-plain-text { +.wp-block-code .editor-plain-text { font-family: $editor-html-font; font-size: $text-editor-font-size; color: $dark-gray-800; diff --git a/core-blocks/code/index.js b/core-blocks/code/index.js index 7587d697e3fb6..487cd5c966519 100644 --- a/core-blocks/code/index.js +++ b/core-blocks/code/index.js @@ -2,22 +2,19 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { - createBlock, - PlainText, -} from '@wordpress/blocks'; +import { createBlock } from '@wordpress/blocks'; /** * Internal dependencies */ -import './editor.scss'; +import edit from './edit'; export const name = 'core/code'; export const settings = { title: __( 'Code' ), - description: __( 'The code block maintains spaces and tabs, great for showing code snippets.' ), + description: __( 'Add text that respects your spacing and tabs -- perfect for displaying code.' ), icon: 'editor-code', @@ -51,22 +48,22 @@ export const settings = { node.children.length === 1 && node.firstChild.nodeName === 'CODE' ), + schema: { + pre: { + children: { + code: { + children: { + '#text': {}, + }, + }, + }, + }, + }, }, ], }, - edit( { attributes, setAttributes, className } ) { - return ( - <div className={ className }> - <PlainText - value={ attributes.content } - onChange={ ( content ) => setAttributes( { content } ) } - placeholder={ __( 'Write code…' ) } - aria-label={ __( 'Code' ) } - /> - </div> - ); - }, + edit, save( { attributes } ) { return <pre><code>{ attributes.content }</code></pre>; diff --git a/core-blocks/code/test/__snapshots__/index.js.snap b/core-blocks/code/test/__snapshots__/index.js.snap index b5453f0faa1d4..4d44efaa90a74 100644 --- a/core-blocks/code/test/__snapshots__/index.js.snap +++ b/core-blocks/code/test/__snapshots__/index.js.snap @@ -6,7 +6,7 @@ exports[`core/code block edit matches snapshot 1`] = ` > <textarea aria-label="Code" - class="blocks-plain-text" + class="editor-plain-text" placeholder="Write code…" rows="1" /> diff --git a/core-blocks/columns/index.js b/core-blocks/columns/index.js index e37bc3f9c4127..fe7f6fed3232d 100644 --- a/core-blocks/columns/index.js +++ b/core-blocks/columns/index.js @@ -12,11 +12,9 @@ import { __, sprintf } from '@wordpress/i18n'; import { PanelBody, RangeControl } from '@wordpress/components'; import { Fragment } from '@wordpress/element'; import { - BlockControls, InspectorControls, - BlockAlignmentToolbar, InnerBlocks, -} from '@wordpress/blocks'; +} from '@wordpress/editor'; /** * Internal dependencies @@ -58,34 +56,20 @@ export const settings = { type: 'number', default: 2, }, - align: { - type: 'string', - }, }, - description: __( 'A multi-column layout of content.' ), - - getEditWrapperProps( attributes ) { - const { align } = attributes; + description: __( 'Add a block that displays content in multiple columns, then add whatever content blocks you\'d like.' ), - return { 'data-align': align }; + supports: { + align: [ 'wide', 'full' ], }, edit( { attributes, setAttributes, className } ) { - const { align, columns } = attributes; + const { columns } = attributes; const classes = classnames( className, `has-${ columns }-columns` ); return ( <Fragment> - <BlockControls> - <BlockAlignmentToolbar - controls={ [ 'wide', 'full' ] } - value={ align } - onChange={ ( nextAlign ) => { - setAttributes( { align: nextAlign } ); - } } - /> - </BlockControls> <InspectorControls> <PanelBody> <RangeControl diff --git a/core-blocks/cover-image/editor.scss b/core-blocks/cover-image/editor.scss index 29db22108e5d9..0fb1490a55502 100644 --- a/core-blocks/cover-image/editor.scss +++ b/core-blocks/cover-image/editor.scss @@ -1,11 +1,11 @@ .wp-block-cover-image { margin: 0; - .blocks-rich-text__tinymce[data-is-empty="true"]:before { + .editor-rich-text__tinymce[data-is-empty="true"]:before { position: inherit; } - .blocks-rich-text__tinymce:focus a[data-mce-selected] { + .editor-rich-text__tinymce:focus a[data-mce-selected] { padding: 0 2px; margin: 0 -2px; border-radius: 2px; @@ -13,7 +13,7 @@ background: rgba( 255, 255, 255, 0.3 ); } - .blocks-rich-text strong { + .editor-rich-text strong { font-weight: 300; } diff --git a/core-blocks/cover-image/index.js b/core-blocks/cover-image/index.js index 94b0ad2ed680d..c29e512ac25d7 100644 --- a/core-blocks/cover-image/index.js +++ b/core-blocks/cover-image/index.js @@ -2,6 +2,7 @@ * External dependencies */ import { isEmpty } from 'lodash'; +import classnames from 'classnames'; /** * WordPress dependencies @@ -9,9 +10,8 @@ import { isEmpty } from 'lodash'; import { IconButton, PanelBody, RangeControl, ToggleControl, Toolbar } from '@wordpress/components'; import { Fragment } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; -import classnames from 'classnames'; +import { createBlock } from '@wordpress/blocks'; import { - createBlock, BlockControls, InspectorControls, BlockAlignmentToolbar, @@ -19,7 +19,7 @@ import { MediaUpload, AlignmentToolbar, RichText, -} from '@wordpress/blocks'; +} from '@wordpress/editor'; /** * Internal dependencies @@ -63,7 +63,7 @@ export const name = 'core/cover-image'; export const settings = { title: __( 'Cover Image' ), - description: __( 'Cover Image is a bold image block with an optional title.' ), + description: __( 'Add a full-width image, and layer text over it -- great for headers.' ), icon: 'cover-image', @@ -117,14 +117,6 @@ export const settings = { } ); - const alignmentToolbar = ( - <AlignmentToolbar - value={ contentAlign } - onChange={ ( nextAlign ) => { - setAttributes( { contentAlign: nextAlign } ); - } } - /> - ); const controls = ( <Fragment> <BlockControls> @@ -132,8 +124,12 @@ export const settings = { value={ align } onChange={ updateAlignment } /> - - { alignmentToolbar } + <AlignmentToolbar + value={ contentAlign } + onChange={ ( nextAlign ) => { + setAttributes( { contentAlign: nextAlign } ); + } } + /> <Toolbar> <MediaUpload onSelect={ onSelectImage } @@ -150,26 +146,25 @@ export const settings = { /> </Toolbar> </BlockControls> - <InspectorControls> - <PanelBody title={ __( 'Cover Image Settings' ) }> - <ToggleControl - label={ __( 'Fixed Background' ) } - checked={ !! hasParallax } - onChange={ toggleParallax } - /> - <RangeControl - label={ __( 'Background Dimness' ) } - value={ dimRatio } - onChange={ setDimRatio } - min={ 0 } - max={ 100 } - step={ 10 } - /> - </PanelBody> - <PanelBody title={ __( 'Text Alignment' ) }> - { alignmentToolbar } - </PanelBody> - </InspectorControls> + { !! url && ( + <InspectorControls> + <PanelBody title={ __( 'Cover Image Settings' ) }> + <ToggleControl + label={ __( 'Fixed Background' ) } + checked={ !! hasParallax } + onChange={ toggleParallax } + /> + <RangeControl + label={ __( 'Background Dimness' ) } + value={ dimRatio } + onChange={ setDimRatio } + min={ 0 } + max={ 100 } + step={ 10 } + /> + </PanelBody> + </InspectorControls> + ) } </Fragment> ); diff --git a/core-blocks/embed/index.js b/core-blocks/embed/index.js index d080cee3acbda..42ba0e7beea8e 100644 --- a/core-blocks/embed/index.js +++ b/core-blocks/embed/index.js @@ -4,6 +4,7 @@ import { parse } from 'url'; import { includes, kebabCase, toLower } from 'lodash'; import { stringify } from 'querystring'; +import classnames from 'classnames'; /** * WordPress dependencies @@ -11,13 +12,12 @@ import { stringify } from 'querystring'; import { __, sprintf } from '@wordpress/i18n'; import { Component, Fragment, renderToString } from '@wordpress/element'; import { Button, Placeholder, Spinner, SandBox } from '@wordpress/components'; -import classnames from 'classnames'; +import { createBlock } from '@wordpress/blocks'; import { - createBlock, BlockControls, BlockAlignmentToolbar, RichText, -} from '@wordpress/blocks'; +} from '@wordpress/editor'; /** * Internal dependencies @@ -29,9 +29,11 @@ import './editor.scss'; const HOSTS_NO_PREVIEWS = [ 'facebook.com' ]; function getEmbedBlockSettings( { title, description, icon, category = 'embed', transforms, keywords = [] } ) { + // translators: %s: Name of service (e.g. VideoPress, YouTube) + const blockDescription = description || sprintf( __( 'Add a block that displays content pulled from other sites, like Twitter, Instagram or YouTube.' ), title ); return { title, - description: description || __( `Paste URLs from ${ title } to embed the content in this block.` ), + description: blockDescription, icon, category, keywords, diff --git a/core-blocks/freeform/old-editor.js b/core-blocks/freeform/edit.js similarity index 97% rename from core-blocks/freeform/old-editor.js rename to core-blocks/freeform/edit.js index b95241c486c40..470cd74b10c5f 100644 --- a/core-blocks/freeform/old-editor.js +++ b/core-blocks/freeform/edit.js @@ -5,6 +5,11 @@ import { Component } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { keycodes } from '@wordpress/utils'; +/** + * Internal dependencies + */ +import './editor.scss'; + const { BACKSPACE, DELETE, F10 } = keycodes; function isTmceEmpty( editor ) { @@ -23,7 +28,7 @@ function isTmceEmpty( editor ) { return /^\n?$/.test( body.innerText || body.textContent ); } -export default class OldEditor extends Component { +export default class FreeformEdit extends Component { constructor( props ) { super( props ); this.initialize = this.initialize.bind( this ); diff --git a/core-blocks/freeform/editor.scss b/core-blocks/freeform/editor.scss index cba0ec09fbb67..2a3a35ffecc51 100644 --- a/core-blocks/freeform/editor.scss +++ b/core-blocks/freeform/editor.scss @@ -1,4 +1,4 @@ -.wp-block-freeform.blocks-rich-text__tinymce { +.wp-block-freeform.editor-rich-text__tinymce { overflow: hidden; margin: -4px; padding: 4px; diff --git a/core-blocks/freeform/index.js b/core-blocks/freeform/index.js index 4903a6a54e928..2d4cd52fdff55 100644 --- a/core-blocks/freeform/index.js +++ b/core-blocks/freeform/index.js @@ -7,15 +7,14 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import './editor.scss'; -import OldEditor from './old-editor'; +import edit from './edit'; export const name = 'core/freeform'; export const settings = { title: __( 'Classic' ), - description: __( 'The classic editor, in block form.' ), + description: __( 'It\'s the classic WordPress editor and it\'s block! Drop the editor right in.' ), icon: 'editor-kitchensink', @@ -33,7 +32,7 @@ export const settings = { customClassName: false, }, - edit: OldEditor, + edit, save( { attributes } ) { const { content } = attributes; diff --git a/core-blocks/gallery/block.js b/core-blocks/gallery/edit.js similarity index 97% rename from core-blocks/gallery/block.js rename to core-blocks/gallery/edit.js index 3a7b50319ecde..5f9db010ca240 100644 --- a/core-blocks/gallery/block.js +++ b/core-blocks/gallery/edit.js @@ -18,18 +18,19 @@ import { ToggleControl, Toolbar, } from '@wordpress/components'; +import { editorMediaUpload } from '@wordpress/blocks'; import { - editorMediaUpload, BlockControls, BlockAlignmentToolbar, MediaUpload, ImagePlaceholder, InspectorControls, -} from '@wordpress/blocks'; +} from '@wordpress/editor'; /** * Internal dependencies */ +import './editor.scss'; import GalleryImage from './gallery-image'; const MAX_COLUMNS = 8; @@ -43,7 +44,7 @@ export function defaultColumnsNumber( attributes ) { return Math.min( 3, attributes.images.length ); } -class GalleryBlock extends Component { +export default class GalleryEdit extends Component { constructor() { super( ...arguments ); @@ -270,5 +271,3 @@ class GalleryBlock extends Component { ); } } - -export default GalleryBlock; diff --git a/core-blocks/gallery/editor.scss b/core-blocks/gallery/editor.scss index 50eb1c425136b..462e68af8a79d 100644 --- a/core-blocks/gallery/editor.scss +++ b/core-blocks/gallery/editor.scss @@ -15,19 +15,19 @@ @include loading_fade; } - .blocks-rich-text { + .editor-rich-text { position: absolute; width: 100%; max-height: 100%; overflow-y: auto; } - .blocks-rich-text figcaption:not( [data-is-placeholder-visible="true"] ) { + .editor-rich-text figcaption:not( [data-is-placeholder-visible="true"] ) { position: relative; overflow: hidden; } - .is-selected .blocks-rich-text { + .is-selected .editor-rich-text { width: calc( 100% - 8px ); left: 4px; margin-top: -4px; diff --git a/core-blocks/gallery/gallery-image.js b/core-blocks/gallery/gallery-image.js index f76f9dd458d99..e8891f5ccd622 100644 --- a/core-blocks/gallery/gallery-image.js +++ b/core-blocks/gallery/gallery-image.js @@ -11,7 +11,7 @@ import { IconButton, Spinner } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { keycodes } from '@wordpress/utils'; import { withSelect } from '@wordpress/data'; -import { RichText } from '@wordpress/blocks'; +import { RichText } from '@wordpress/editor'; /** * Module constants diff --git a/core-blocks/gallery/index.js b/core-blocks/gallery/index.js index ac16beebdf3c3..3321d94972df2 100644 --- a/core-blocks/gallery/index.js +++ b/core-blocks/gallery/index.js @@ -7,18 +7,17 @@ import { filter, every } from 'lodash'; * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { mediaUpload } from '@wordpress/utils'; import { createBlock, - RichText, + editorMediaUpload, } from '@wordpress/blocks'; +import { RichText } from '@wordpress/editor'; /** * Internal dependencies */ -import './editor.scss'; import './style.scss'; -import { default as GalleryBlock, defaultColumnsNumber } from './block'; +import { default as edit, defaultColumnsNumber } from './edit'; const blockAttributes = { align: { @@ -76,7 +75,7 @@ export const name = 'core/gallery'; export const settings = { title: __( 'Gallery' ), - description: __( 'Image galleries are a great way to share groups of pictures on your site.' ), + description: __( 'Display multiple images in an elegantly organized tiled layout.' ), icon: 'format-gallery', category: 'common', keywords: [ __( 'images' ), __( 'photos' ) ], @@ -135,7 +134,7 @@ export const settings = { }, transform( files, onChange ) { const block = createBlock( 'core/gallery' ); - mediaUpload( + editorMediaUpload( files, ( images ) => onChange( block.uid, { images } ), 'image' @@ -165,7 +164,7 @@ export const settings = { } }, - edit: GalleryBlock, + edit, save( { attributes } ) { const { images, columns = defaultColumnsNumber( attributes ), align, imageCrop, linkTo } = attributes; diff --git a/core-blocks/heading/edit.js b/core-blocks/heading/edit.js new file mode 100644 index 0000000000000..229232515ed08 --- /dev/null +++ b/core-blocks/heading/edit.js @@ -0,0 +1,82 @@ +/** + * WordPress dependencies + */ + +import { __, sprintf } from '@wordpress/i18n'; +import { Fragment } from '@wordpress/element'; +import { PanelBody, Toolbar } from '@wordpress/components'; +import { createBlock } from '@wordpress/blocks'; +import { RichText, BlockControls, InspectorControls, AlignmentToolbar } from '@wordpress/editor'; + +/** + * Internal dependencies + */ +import './editor.scss'; + +export default function HeadingEdit( { + attributes, + setAttributes, + mergeBlocks, + insertBlocksAfter, + onReplace, + className, +} ) { + const { align, content, nodeName, placeholder } = attributes; + + return ( + <Fragment> + <BlockControls + controls={ '234'.split( '' ).map( ( level ) => ( { + icon: 'heading', + title: sprintf( __( 'Heading %s' ), level ), + isActive: 'H' + level === nodeName, + onClick: () => setAttributes( { nodeName: 'H' + level } ), + subscript: level, + } ) ) } + /> + <InspectorControls> + <PanelBody title={ __( 'Heading Settings' ) }> + <p>{ __( 'Level' ) }</p> + <Toolbar + controls={ '123456'.split( '' ).map( ( level ) => ( { + icon: 'heading', + title: sprintf( __( 'Heading %s' ), level ), + isActive: 'H' + level === nodeName, + onClick: () => setAttributes( { nodeName: 'H' + level } ), + subscript: level, + } ) ) } + /> + <p>{ __( 'Text Alignment' ) }</p> + <AlignmentToolbar + value={ align } + onChange={ ( nextAlign ) => { + setAttributes( { align: nextAlign } ); + } } + /> + </PanelBody> + </InspectorControls> + <RichText + wrapperClassName="wp-block-heading" + tagName={ nodeName.toLowerCase() } + value={ content } + onChange={ ( value ) => setAttributes( { content: value } ) } + onMerge={ mergeBlocks } + onSplit={ + insertBlocksAfter ? + ( before, after, ...blocks ) => { + setAttributes( { content: before } ); + insertBlocksAfter( [ + ...blocks, + createBlock( 'core/paragraph', { content: after } ), + ] ); + } : + undefined + } + onRemove={ () => onReplace( [] ) } + style={ { textAlign: align } } + className={ className } + placeholder={ placeholder || __( 'Write heading…' ) } + /> + </Fragment> + ); +} diff --git a/core-blocks/heading/index.js b/core-blocks/heading/index.js index 2e9dc97e43099..def9872def011 100644 --- a/core-blocks/heading/index.js +++ b/core-blocks/heading/index.js @@ -1,28 +1,22 @@ /** * WordPress dependencies */ -import { __, sprintf } from '@wordpress/i18n'; -import { concatChildren, Fragment } from '@wordpress/element'; -import { PanelBody, Toolbar } from '@wordpress/components'; -import { - createBlock, - RichText, - BlockControls, - InspectorControls, - AlignmentToolbar, -} from '@wordpress/blocks'; +import { __ } from '@wordpress/i18n'; +import { concatChildren } from '@wordpress/element'; +import { createBlock, getPhrasingContentSchema } from '@wordpress/blocks'; +import { RichText } from '@wordpress/editor'; /** * Internal dependencies */ -import './editor.scss'; +import edit from './edit'; export const name = 'core/heading'; export const settings = { title: __( 'Heading' ), - description: __( 'Search engines use the headings to index the structure and content of your web pages.' ), + description: __( 'Insert a headline above your post or page content.' ), icon: 'heading', @@ -69,7 +63,15 @@ export const settings = { }, { type: 'raw', - isMatch: ( node ) => /H\d/.test( node.nodeName ), + selector: 'h1,h2,h3,h4,h5,h6', + schema: { + h1: { children: getPhrasingContentSchema() }, + h2: { children: getPhrasingContentSchema() }, + h3: { children: getPhrasingContentSchema() }, + h4: { children: getPhrasingContentSchema() }, + h5: { children: getPhrasingContentSchema() }, + h6: { children: getPhrasingContentSchema() }, + }, }, { type: 'pattern', @@ -103,69 +105,7 @@ export const settings = { }; }, - edit( { attributes, setAttributes, mergeBlocks, insertBlocksAfter, onReplace, className } ) { - const { align, content, nodeName, placeholder } = attributes; - - return ( - <Fragment> - <BlockControls - controls={ - '234'.split( '' ).map( ( level ) => ( { - icon: 'heading', - title: sprintf( __( 'Heading %s' ), level ), - isActive: 'H' + level === nodeName, - onClick: () => setAttributes( { nodeName: 'H' + level } ), - subscript: level, - } ) ) - } - /> - <InspectorControls> - <PanelBody title={ __( 'Heading Settings' ) }> - <p>{ __( 'Level' ) }</p> - <Toolbar - controls={ - '123456'.split( '' ).map( ( level ) => ( { - icon: 'heading', - title: sprintf( __( 'Heading %s' ), level ), - isActive: 'H' + level === nodeName, - onClick: () => setAttributes( { nodeName: 'H' + level } ), - subscript: level, - } ) ) - } - /> - <p>{ __( 'Text Alignment' ) }</p> - <AlignmentToolbar - value={ align } - onChange={ ( nextAlign ) => { - setAttributes( { align: nextAlign } ); - } } - /> - </PanelBody> - </InspectorControls> - <RichText - wrapperClassName="wp-block-heading" - tagName={ nodeName.toLowerCase() } - value={ content } - onChange={ ( value ) => setAttributes( { content: value } ) } - onMerge={ mergeBlocks } - onSplit={ - insertBlocksAfter ? - ( unused, after, ...blocks ) => { - insertBlocksAfter( [ - ...blocks, - createBlock( 'core/paragraph', { content: after } ), - ] ); - } : - undefined - } - onRemove={ () => onReplace( [] ) } - style={ { textAlign: align } } - className={ className } - placeholder={ placeholder || __( 'Write heading…' ) } - /> - </Fragment> - ); - }, + edit, save( { attributes } ) { const { align, nodeName, content } = attributes; diff --git a/core-blocks/heading/test/__snapshots__/index.js.snap b/core-blocks/heading/test/__snapshots__/index.js.snap index 3da981c56f1bd..6a817645c35e2 100644 --- a/core-blocks/heading/test/__snapshots__/index.js.snap +++ b/core-blocks/heading/test/__snapshots__/index.js.snap @@ -2,7 +2,7 @@ exports[`core/heading block edit matches snapshot 1`] = ` <div - class="wp-block-heading blocks-rich-text" + class="wp-block-heading editor-rich-text" > <div> <div> @@ -14,13 +14,13 @@ exports[`core/heading block edit matches snapshot 1`] = ` aria-expanded="false" aria-label="Write heading…" aria-multiline="false" - class="blocks-rich-text__tinymce" + class="editor-rich-text__tinymce" contenteditable="true" data-is-placeholder-visible="true" role="textbox" /> <h2 - class="blocks-rich-text__tinymce" + class="editor-rich-text__tinymce" > Write heading… </h2> diff --git a/core-blocks/html/index.js b/core-blocks/html/index.js index 3764afd736b9b..6e78001453591 100644 --- a/core-blocks/html/index.js +++ b/core-blocks/html/index.js @@ -4,7 +4,8 @@ import { RawHTML } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { withState, SandBox, CodeEditor } from '@wordpress/components'; -import { BlockControls } from '@wordpress/blocks'; +import { getPhrasingContentSchema } from '@wordpress/blocks'; +import { BlockControls } from '@wordpress/editor'; /** * Internal dependencies @@ -16,7 +17,7 @@ export const name = 'core/html'; export const settings = { title: __( 'Custom HTML' ), - description: __( 'Add custom HTML code and preview it right here in the editor.' ), + description: __( 'Add your own HTML (and view it right here as you edit!).' ), icon: 'html', @@ -41,7 +42,20 @@ export const settings = { from: [ { type: 'raw', - isMatch: ( node ) => node.nodeName === 'IFRAME', + isMatch: ( node ) => node.nodeName === 'FIGURE' && !! node.querySelector( 'iframe' ), + schema: { + figure: { + require: [ 'iframe' ], + children: { + iframe: { + attributes: [ 'src', 'allowfullscreen', 'height', 'width' ], + }, + figcaption: { + children: getPhrasingContentSchema(), + }, + }, + }, + }, }, ], }, diff --git a/core-blocks/image/block.js b/core-blocks/image/edit.js similarity index 98% rename from core-blocks/image/block.js rename to core-blocks/image/edit.js index 22556c27535ae..4b27c04f5b944 100644 --- a/core-blocks/image/block.js +++ b/core-blocks/image/edit.js @@ -30,6 +30,9 @@ import { import { withSelect } from '@wordpress/data'; import { editorMediaUpload, + withEditorSettings, +} from '@wordpress/blocks'; +import { RichText, BlockControls, InspectorControls, @@ -37,12 +40,12 @@ import { MediaUpload, BlockAlignmentToolbar, UrlInputButton, - withEditorSettings, -} from '@wordpress/blocks'; +} from '@wordpress/editor'; /** * Internal dependencies */ +import './editor.scss'; import ImageSize from './image-size'; /** @@ -50,7 +53,7 @@ import ImageSize from './image-size'; */ const MIN_SIZE = 20; -class ImageBlock extends Component { +class ImageEdit extends Component { constructor() { super( ...arguments ); this.updateAlt = this.updateAlt.bind( this ); @@ -397,4 +400,4 @@ export default compose( [ image: id ? getMedia( id ) : null, }; } ), -] )( ImageBlock ); +] )( ImageEdit ); diff --git a/core-blocks/image/editor.scss b/core-blocks/image/editor.scss index 6236b7b54320b..edf2a48fb5e85 100644 --- a/core-blocks/image/editor.scss +++ b/core-blocks/image/editor.scss @@ -114,7 +114,7 @@ } } -.editor-block-list__block[data-type="core/image"] .editor-block-toolbar .blocks-format-toolbar__link-modal { +.editor-block-list__block[data-type="core/image"] .editor-block-toolbar .editor-format-toolbar__link-modal { position: absolute; left: 0; right: 0; @@ -123,4 +123,4 @@ @include break-small() { margin: -1px; } -} \ No newline at end of file +} diff --git a/core-blocks/image/index.js b/core-blocks/image/index.js index e689b31d1139a..816ee8a6367ab 100644 --- a/core-blocks/image/index.js +++ b/core-blocks/image/index.js @@ -6,15 +6,15 @@ import { createBlock, getBlockAttributes, getBlockType, - RichText, + getPhrasingContentSchema, } from '@wordpress/blocks'; +import { RichText } from '@wordpress/editor'; /** * Internal dependencies */ import './style.scss'; -import './editor.scss'; -import ImageBlock from './block'; +import edit from './edit'; export const name = 'core/image'; @@ -57,10 +57,33 @@ const blockAttributes = { }, }; +const imageSchema = { + img: { + attributes: [ 'src', 'alt' ], + classes: [ 'alignleft', 'aligncenter', 'alignright', 'alignnone' ], + }, +}; + +const schema = { + figure: { + require: [ 'img' ], + children: { + ...imageSchema, + a: { + attributes: [ 'href' ], + children: imageSchema, + }, + figcaption: { + children: getPhrasingContentSchema(), + }, + }, + }, +}; + export const settings = { title: __( 'Image' ), - description: __( 'Worth a thousand words.' ), + description: __( 'They\'re worth 1,000 words! Insert a single image.' ), icon: 'format-image', @@ -74,13 +97,9 @@ export const settings = { from: [ { type: 'raw', - isMatch( node ) { - const tag = node.nodeName.toLowerCase(); - const hasImage = node.querySelector( 'img' ); - - return tag === 'img' || ( hasImage && tag === 'figure' ); - }, - transform( node ) { + isMatch: ( node ) => node.nodeName === 'FIGURE' && !! node.querySelector( 'img' ), + schema, + transform: ( node ) => { const matches = /align(left|center|right)/.exec( node.className ); const align = matches ? matches[ 1 ] : undefined; const blockType = getBlockType( 'core/image' ); @@ -160,7 +179,7 @@ export const settings = { } }, - edit: ImageBlock, + edit, save( { attributes } ) { const { url, alt, caption, align, href, width, height, id } = attributes; diff --git a/core-blocks/index.js b/core-blocks/index.js index 8c2600219d6ff..a6c83b08da036 100644 --- a/core-blocks/index.js +++ b/core-blocks/index.js @@ -35,6 +35,7 @@ import * as pullquote from './pullquote'; import * as sharedBlock from './block'; import * as separator from './separator'; import * as shortcode from './shortcode'; +import * as spacer from './spacer'; import * as subhead from './subhead'; import * as table from './table'; import * as textColumns from './text-columns'; @@ -72,6 +73,7 @@ export const registerCoreBlocks = () => { pullquote, separator, sharedBlock, + spacer, subhead, table, textColumns, diff --git a/core-blocks/index.native.js b/core-blocks/index.native.js new file mode 100644 index 0000000000000..df6b8b623b293 --- /dev/null +++ b/core-blocks/index.native.js @@ -0,0 +1,21 @@ +/** + * WordPress dependencies + */ +import { + registerBlockType, +} from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import * as code from './code'; +import * as more from './more'; + +export const registerCoreBlocks = () => { + [ + code, + more, + ].forEach( ( { name, settings } ) => { + registerBlockType( name, settings ); + } ); +}; diff --git a/core-blocks/latest-posts/block.js b/core-blocks/latest-posts/edit.js similarity index 96% rename from core-blocks/latest-posts/block.js rename to core-blocks/latest-posts/edit.js index 7b2819fef4b7b..859b32f0a2b0d 100644 --- a/core-blocks/latest-posts/block.js +++ b/core-blocks/latest-posts/edit.js @@ -26,17 +26,16 @@ import { InspectorControls, BlockAlignmentToolbar, BlockControls, -} from '@wordpress/blocks'; +} from '@wordpress/editor'; /** * Internal dependencies */ import './editor.scss'; -import './style.scss'; const MAX_POSTS_COLUMNS = 6; -class LatestPostsBlock extends Component { +class LatestPostsEdit extends Component { constructor() { super( ...arguments ); @@ -61,7 +60,7 @@ class LatestPostsBlock extends Component { <QueryControls { ...{ order, orderBy } } numberOfItems={ postsToShow } - categoriesList={ get( categoriesList, 'data', {} ) } + categoriesList={ get( categoriesList, [ 'data' ], {} ) } selectedCategoryId={ categories } onOrderChange={ ( value ) => setAttributes( { order: value } ) } onOrderByChange={ ( value ) => setAttributes( { orderBy: value } ) } @@ -164,7 +163,7 @@ export default withAPIData( ( props ) => { const latestPostsQuery = stringify( pickBy( { categories, order, - orderBy, + orderby: orderBy, per_page: postsToShow, _fields: [ 'date_gmt', 'link', 'title' ], }, ( value ) => ! isUndefined( value ) ) ); @@ -176,4 +175,4 @@ export default withAPIData( ( props ) => { latestPosts: `/wp/v2/posts?${ latestPostsQuery }`, categoriesList: `/wp/v2/categories?${ categoriesListQuery }`, }; -} )( LatestPostsBlock ); +} )( LatestPostsEdit ); diff --git a/core-blocks/latest-posts/index.js b/core-blocks/latest-posts/index.js index 3762b61e2a68b..2a36e4161607c 100644 --- a/core-blocks/latest-posts/index.js +++ b/core-blocks/latest-posts/index.js @@ -6,16 +6,15 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import './editor.scss'; import './style.scss'; -import LatestPostsBlock from './block'; +import edit from './edit'; export const name = 'core/latest-posts'; export const settings = { title: __( 'Latest Posts' ), - description: __( 'Shows a list of your site\'s most recent posts.' ), + description: __( 'Display a list of your most recent posts.' ), icon: 'list-view', @@ -34,7 +33,7 @@ export const settings = { } }, - edit: LatestPostsBlock, + edit, save() { return null; diff --git a/core-blocks/list/editor.scss b/core-blocks/list/editor.scss index cae73bd5c1658..902c3ea86af98 100644 --- a/core-blocks/list/editor.scss +++ b/core-blocks/list/editor.scss @@ -1,6 +1,6 @@ -.blocks-list .blocks-rich-text__tinymce, -.blocks-list .blocks-rich-text__tinymce ul, -.blocks-list .blocks-rich-text__tinymce ol { +.blocks-list .editor-rich-text__tinymce, +.blocks-list .editor-rich-text__tinymce ul, +.blocks-list .editor-rich-text__tinymce ol { padding-left: 1.3em; margin-left: 1.3em; } diff --git a/core-blocks/list/index.js b/core-blocks/list/index.js index 59a4d3a811557..e3150ea2b021d 100644 --- a/core-blocks/list/index.js +++ b/core-blocks/list/index.js @@ -1,29 +1,46 @@ /** * External dependencies */ -import { find, compact, get, isEmpty } from 'lodash'; +import { find, compact, get, initial, last, isEmpty } from 'lodash'; /** * WordPress dependencies */ import { Component, Fragment } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; +import { createBlock, getPhrasingContentSchema } from '@wordpress/blocks'; import { - createBlock, BlockControls, RichText, -} from '@wordpress/blocks'; +} from '@wordpress/editor'; /** * Internal dependencies */ import './editor.scss'; +const listContentSchema = { + ...getPhrasingContentSchema(), + ul: {}, + ol: { attributes: [ 'type' ] }, +}; + +// Recursion is needed. +// Possible: ul > li > ul. +// Impossible: ul > ul. +[ 'ul', 'ol' ].forEach( ( tag ) => { + listContentSchema[ tag ].children = { + li: { + children: listContentSchema, + }, + }; +} ); + export const name = 'core/list'; export const settings = { title: __( 'List' ), - description: __( 'List. Numbered or bulleted.' ), + description: __( 'Numbers, bullets, up to you. Add a list of items.' ), icon: 'editor-ul', category: 'common', keywords: [ __( 'bullet list' ), __( 'ordered list' ), __( 'numbered list' ) ], @@ -63,9 +80,28 @@ export const settings = { } ); }, }, + { + type: 'block', + blocks: [ 'core/quote' ], + transform: ( { value, citation } ) => { + const items = value.map( ( p ) => get( p, [ 'children', 'props', 'children' ] ) ); + if ( ! isEmpty( citation ) ) { + items.push( citation ); + } + const hasItems = ! items.every( isEmpty ); + return createBlock( 'core/list', { + nodeName: 'UL', + values: hasItems ? items.map( ( content, index ) => <li key={ index }>{ content }</li> ) : [], + } ); + }, + }, { type: 'raw', - isMatch: ( node ) => node.nodeName === 'OL' || node.nodeName === 'UL', + selector: 'ol,ul', + schema: { + ol: listContentSchema.ol, + ul: listContentSchema.ul, + }, }, { type: 'pattern', @@ -93,11 +129,23 @@ export const settings = { type: 'block', blocks: [ 'core/paragraph' ], transform: ( { values } ) => - compact( values.map( ( value ) => get( value, 'props.children', null ) ) ) + compact( values.map( ( value ) => get( value, [ 'props', 'children' ], null ) ) ) .map( ( content ) => createBlock( 'core/paragraph', { content: [ content ], } ) ), }, + { + type: 'block', + blocks: [ 'core/quote' ], + transform: ( { values } ) => { + return createBlock( 'core/quote', { + value: compact( ( values.length === 1 ? values : initial( values ) ) + .map( ( value ) => get( value, [ 'props', 'children' ], null ) ) ) + .map( ( children ) => ( { children: <p>{ children }</p> } ) ), + citation: ( values.length === 1 ? undefined : [ get( last( values ), [ 'props', 'children' ] ) ] ), + } ); + }, + }, ], }, @@ -205,6 +253,7 @@ export const settings = { const { attributes, insertBlocksAfter, + setAttributes, mergeBlocks, onReplace, className, @@ -252,7 +301,7 @@ export const settings = { onMerge={ mergeBlocks } onSplit={ insertBlocksAfter ? - ( unused, after, ...blocks ) => { + ( before, after, ...blocks ) => { if ( ! blocks.length ) { blocks.push( createBlock( 'core/paragraph' ) ); } @@ -264,6 +313,7 @@ export const settings = { } ) ); } + setAttributes( { values: before } ); insertBlocksAfter( blocks ); } : undefined diff --git a/core-blocks/list/test/__snapshots__/index.js.snap b/core-blocks/list/test/__snapshots__/index.js.snap index 0dfeabf5c9554..86d020fb14cc6 100644 --- a/core-blocks/list/test/__snapshots__/index.js.snap +++ b/core-blocks/list/test/__snapshots__/index.js.snap @@ -2,7 +2,7 @@ exports[`core/list block edit matches snapshot 1`] = ` <div - class="blocks-list blocks-rich-text" + class="blocks-list editor-rich-text" > <div> <div> @@ -14,12 +14,12 @@ exports[`core/list block edit matches snapshot 1`] = ` aria-expanded="false" aria-label="Write list…" aria-multiline="true" - class="blocks-rich-text__tinymce" + class="editor-rich-text__tinymce" contenteditable="true" data-is-placeholder-visible="true" /> <ul - class="blocks-rich-text__tinymce" + class="editor-rich-text__tinymce" > <li> Write list… diff --git a/core-blocks/more/edit.js b/core-blocks/more/edit.js new file mode 100644 index 0000000000000..72aebf0288f78 --- /dev/null +++ b/core-blocks/more/edit.js @@ -0,0 +1,65 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { PanelBody, ToggleControl } from '@wordpress/components'; +import { Component, Fragment } from '@wordpress/element'; +import { InspectorControls } from '@wordpress/editor'; + +/** + * Internal dependencies + */ +import './editor.scss'; + +export default class MoreEdit extends Component { + constructor() { + super( ...arguments ); + this.onChangeInput = this.onChangeInput.bind( this ); + + this.state = { + defaultText: __( 'Read more' ), + }; + } + + onChangeInput( event ) { + // Set defaultText to an empty string, allowing the user to clear/replace the input field's text + this.setState( { + defaultText: '', + } ); + + const value = event.target.value.length === 0 ? undefined : event.target.value; + this.props.setAttributes( { customText: value } ); + } + + render() { + const { customText, noTeaser } = this.props.attributes; + const { setAttributes } = this.props; + + const toggleNoTeaser = () => setAttributes( { noTeaser: ! noTeaser } ); + const { defaultText } = this.state; + const value = customText !== undefined ? customText : defaultText; + const inputLength = value.length + 1; + + return ( + <Fragment> + <InspectorControls> + <PanelBody> + <ToggleControl + label={ __( 'Hide the teaser before the "More" tag' ) } + checked={ !! noTeaser } + onChange={ toggleNoTeaser } + /> + </PanelBody> + </InspectorControls> + <div className="wp-block-more"> + <input + type="text" + value={ value } + size={ inputLength } + onChange={ this.onChangeInput } + /> + </div> + </Fragment> + ); + } +} diff --git a/core-blocks/more/edit.native.js b/core-blocks/more/edit.native.js new file mode 100644 index 0000000000000..d63d1f5b0b666 --- /dev/null +++ b/core-blocks/more/edit.native.js @@ -0,0 +1,36 @@ +/** @format */ + +import { View, Text } from 'react-native'; + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { PlainText } from '@wordpress/editor'; +import styles from './editor.scss'; + +export default function MoreEdit( { attributes, setAttributes } ) { + const { customText } = attributes; + const defaultText = __( 'Read more' ); + const value = customText !== undefined ? customText : defaultText; + + return ( + <View className={ styles[ 'blocks-more-container' ] }> + <View className={ styles[ 'blocks-more-sub-container' ] }> + <Text className={ styles[ 'blocks-more-left-marker' ] }>&lt;!--</Text> + <PlainText + className={ styles[ 'blocks-more-plain-text' ] } + value={ value } + multiline={ true } + underlineColorAndroid="transparent" + onChange={ ( newValue ) => setAttributes( { customText: newValue } ) } + placeholder={ defaultText } + /> + <Text className={ styles[ 'blocks-more-right-marker' ] }>--&gt;</Text> + </View> + </View> ); +} diff --git a/core-blocks/more/editor.native.scss b/core-blocks/more/editor.native.scss new file mode 100644 index 0000000000000..bd86b75072404 --- /dev/null +++ b/core-blocks/more/editor.native.scss @@ -0,0 +1,22 @@ +// @format + +.blocks-more-container { + align-items: center; + padding-left: 4; + padding-right: 4; + padding-top: 4; + padding-bottom: 4; +} + +.blocks-more-sub-container { + align-items: center; + flex-direction: row; +} + +.blocks-more-left-marker { + padding-right: 4; +} + +.blocks-more-right-marker { + padding-left: 4; +} diff --git a/core-blocks/more/index.js b/core-blocks/more/index.js index 20a8ee5175cd4..8577c0a28e292 100644 --- a/core-blocks/more/index.js +++ b/core-blocks/more/index.js @@ -7,24 +7,20 @@ import { compact } from 'lodash'; * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { PanelBody, ToggleControl } from '@wordpress/components'; -import { Component, Fragment, RawHTML } from '@wordpress/element'; -import { - createBlock, - InspectorControls, -} from '@wordpress/blocks'; +import { RawHTML } from '@wordpress/element'; +import { createBlock } from '@wordpress/blocks'; /** * Internal dependencies */ -import './editor.scss'; +import edit from './edit'; export const name = 'core/more'; export const settings = { title: __( 'More' ), - description: __( '"More" allows you to break your post into a part shown on index pages, and the subsequent after clicking a "Read More" link.' ), + description: __( 'Want to show only part of this post on your blog\'s home page? Insert a "More" block where you want the split.' ), icon: 'editor-insertmore', @@ -52,6 +48,9 @@ export const settings = { from: [ { type: 'raw', + schema: { + 'wp-block': { attributes: [ 'data-block' ] }, + }, isMatch: ( node ) => node.dataset && node.dataset.block === 'core/more', transform( node ) { const { customText, noTeaser } = node.dataset; @@ -70,58 +69,7 @@ export const settings = { ], }, - edit: class extends Component { - constructor() { - super( ...arguments ); - this.onChangeInput = this.onChangeInput.bind( this ); - - this.state = { - defaultText: __( 'Read more' ), - }; - } - - onChangeInput( event ) { - // Set defaultText to an empty string, allowing the user to clear/replace the input field's text - this.setState( { - defaultText: '', - } ); - - const value = event.target.value.length === 0 ? undefined : event.target.value; - this.props.setAttributes( { customText: value } ); - } - - render() { - const { customText, noTeaser } = this.props.attributes; - const { setAttributes } = this.props; - - const toggleNoTeaser = () => setAttributes( { noTeaser: ! noTeaser } ); - const { defaultText } = this.state; - const value = customText !== undefined ? customText : defaultText; - const inputLength = value.length + 1; - - return ( - <Fragment> - <InspectorControls> - <PanelBody> - <ToggleControl - label={ __( 'Hide the teaser before the "More" tag' ) } - checked={ !! noTeaser } - onChange={ toggleNoTeaser } - /> - </PanelBody> - </InspectorControls> - <div className="wp-block-more"> - <input - type="text" - value={ value } - size={ inputLength } - onChange={ this.onChangeInput } - /> - </div> - </Fragment> - ); - } - }, + edit, save( { attributes } ) { const { customText, noTeaser } = attributes; diff --git a/core-blocks/nextpage/index.js b/core-blocks/nextpage/index.js index 86420233d3014..e3668e5fb7b04 100644 --- a/core-blocks/nextpage/index.js +++ b/core-blocks/nextpage/index.js @@ -35,6 +35,9 @@ export const settings = { from: [ { type: 'raw', + schema: { + 'wp-block': { attributes: [ 'data-block' ] }, + }, isMatch: ( node ) => node.dataset && node.dataset.block === 'core/nextpage', transform() { return createBlock( 'core/nextpage', {} ); diff --git a/core-blocks/paragraph/index.js b/core-blocks/paragraph/index.js index 41ca5bee2baea..0ef4c76a82ba4 100644 --- a/core-blocks/paragraph/index.js +++ b/core-blocks/paragraph/index.js @@ -2,7 +2,7 @@ * External dependencies */ import classnames from 'classnames'; -import { findKey, isFinite, map, omit } from 'lodash'; +import { isFinite, find, omit } from 'lodash'; /** * WordPress dependencies @@ -16,33 +16,26 @@ import { RawHTML, } from '@wordpress/element'; import { + FontSizePicker, PanelBody, - PanelColor, - RangeControl, ToggleControl, - Button, - ButtonGroup, withFallbackStyles, } from '@wordpress/components'; import { - createBlock, - blockAutocompleter, getColorClass, withColors, - userAutocompleter, AlignmentToolbar, - BlockAlignmentToolbar, BlockControls, - ColorPalette, ContrastChecker, InspectorControls, + PanelColor, RichText, -} from '@wordpress/blocks'; +} from '@wordpress/editor'; +import { createBlock, getPhrasingContentSchema } from '@wordpress/blocks'; /** * Internal dependencies */ -import './editor.scss'; import './style.scss'; const { getComputedStyle } = window; @@ -59,14 +52,28 @@ const FallbackStyles = withFallbackStyles( ( node, ownProps ) => { }; } ); -const FONT_SIZES = { - small: 14, - regular: 16, - large: 36, - larger: 48, -}; - -const autocompleters = [ blockAutocompleter, userAutocompleter ]; +const FONT_SIZES = [ + { + name: 'small', + shortName: 'S', + size: 14, + }, + { + name: 'regular', + shortName: 'M', + size: 16, + }, + { + name: 'large', + shortName: 'L', + size: 36, + }, + { + name: 'larger', + shortName: 'XL', + size: 48, + }, +]; class ParagraphBlock extends Component { constructor() { @@ -96,10 +103,17 @@ class ParagraphBlock extends Component { setAttributes( { dropCap: ! attributes.dropCap } ); } + getDropCapHelp( checked ) { + return checked ? __( 'Showing large initial letter.' ) : __( 'Toggle to show a large initial letter.' ); + } + getFontSize() { const { customFontSize, fontSize } = this.props.attributes; if ( fontSize ) { - return FONT_SIZES[ fontSize ]; + const fontSizeObj = find( FONT_SIZES, { name: fontSize } ); + if ( fontSizeObj ) { + return fontSizeObj.size; + } } if ( customFontSize ) { @@ -109,10 +123,10 @@ class ParagraphBlock extends Component { setFontSize( fontSizeValue ) { const { setAttributes } = this.props; - const thresholdFontSize = findKey( FONT_SIZES, ( size ) => size === fontSizeValue ); + const thresholdFontSize = find( FONT_SIZES, { size: fontSizeValue } ); if ( thresholdFontSize ) { setAttributes( { - fontSize: thresholdFontSize, + fontSize: thresholdFontSize.name, customFontSize: undefined, } ); return; @@ -131,7 +145,10 @@ class ParagraphBlock extends Component { mergeBlocks, onReplace, className, - initializeColor, + backgroundColor, + textColor, + setBackgroundColor, + setTextColor, fallbackBackgroundColor, fallbackTextColor, fallbackFontSize, @@ -142,20 +159,9 @@ class ParagraphBlock extends Component { content, dropCap, placeholder, - width, } = attributes; const fontSize = this.getFontSize(); - const textColor = initializeColor( { - colorContext: 'color', - colorAttribute: 'textColor', - customColorAttribute: 'customTextColor', - } ); - const backgroundColor = initializeColor( { - colorContext: 'background-color', - colorAttribute: 'backgroundColor', - customColorAttribute: 'customBackgroundColor', - } ); return ( <Fragment> @@ -169,61 +175,31 @@ class ParagraphBlock extends Component { </BlockControls> <InspectorControls> <PanelBody title={ __( 'Text Settings' ) } className="blocks-font-size"> - <div className="blocks-font-size__main"> - <ButtonGroup aria-label={ __( 'Font Size' ) }> - { map( { - S: 'small', - M: 'regular', - L: 'large', - XL: 'larger', - }, ( size, label ) => ( - <Button - key={ label } - isLarge - isPrimary={ fontSize === FONT_SIZES[ size ] } - aria-pressed={ fontSize === FONT_SIZES[ size ] } - onClick={ () => this.setFontSize( FONT_SIZES[ size ] ) } - > - { label } - </Button> - ) ) } - </ButtonGroup> - <Button - isLarge - onClick={ () => this.setFontSize( undefined ) } - > - { __( 'Reset' ) } - </Button> - </div> - <RangeControl - className="blocks-paragraph__custom-size-slider" - label={ __( 'Custom Size' ) } - value={ fontSize || '' } - initialPosition={ fallbackFontSize } - onChange={ ( value ) => this.setFontSize( value ) } - min={ 12 } - max={ 100 } - beforeIcon="editor-textcolor" - afterIcon="editor-textcolor" + <FontSizePicker + fontSizes={ FONT_SIZES } + fallbackFontSize={ fallbackFontSize } + value={ fontSize } + onChange={ this.setFontSize } /> <ToggleControl label={ __( 'Drop Cap' ) } checked={ !! dropCap } onChange={ this.toggleDropCap } + help={ this.getDropCapHelp } /> </PanelBody> - <PanelColor title={ __( 'Background Color' ) } colorValue={ backgroundColor.value } initialOpen={ false }> - <ColorPalette - value={ backgroundColor.value } - onChange={ backgroundColor.set } - /> - </PanelColor> - <PanelColor title={ __( 'Text Color' ) } colorValue={ textColor.value } initialOpen={ false }> - <ColorPalette - value={ textColor.value } - onChange={ textColor.set } - /> - </PanelColor> + <PanelColor + colorValue={ backgroundColor.value } + initialOpen={ false } + title={ __( 'Background Color' ) } + onChange={ setBackgroundColor } + /> + <PanelColor + colorValue={ textColor.value } + initialOpen={ false } + title={ __( 'Text Color' ) } + onChange={ setTextColor } + /> <ContrastChecker textColor={ textColor.value } backgroundColor={ backgroundColor.value } @@ -233,12 +209,6 @@ class ParagraphBlock extends Component { } } isLargeText={ fontSize >= 18 } /> - <PanelBody title={ __( 'Block Alignment' ) }> - <BlockAlignmentToolbar - value={ width } - onChange={ ( nextWidth ) => setAttributes( { width: nextWidth } ) } - /> - </PanelBody> </InspectorControls> <div> <RichText @@ -262,7 +232,8 @@ class ParagraphBlock extends Component { } ); } } onSplit={ insertBlocksAfter ? - ( unused, after, ...blocks ) => { + ( before, after, ...blocks ) => { + setAttributes( { content: before } ); insertBlocksAfter( [ ...blocks, createBlock( 'core/paragraph', { content: after } ), @@ -274,7 +245,6 @@ class ParagraphBlock extends Component { onReplace={ this.onReplace } onRemove={ () => onReplace( [] ) } placeholder={ placeholder || __( 'Add text or type / to add content' ) } - autocompleters={ autocompleters } /> </div> </Fragment> @@ -303,9 +273,6 @@ const schema = { placeholder: { type: 'string', }, - width: { - type: 'string', - }, textColor: { type: 'string', }, @@ -331,7 +298,7 @@ export const name = 'core/paragraph'; export const settings = { title: __( 'Paragraph' ), - description: __( 'This is a simple text only block for adding a single paragraph of content.' ), + description: __( 'Add some basic text.' ), icon: 'editor-paragraph', @@ -347,17 +314,71 @@ export const settings = { from: [ { type: 'raw', + // Paragraph is a fallback and should be matched last. priority: 20, - isMatch: ( node ) => ( - node.nodeName === 'P' && - // Do not allow embedded content. - ! node.querySelector( 'audio, canvas, embed, iframe, img, math, object, svg, video' ) - ), + selector: 'p', + schema: { + p: { + children: getPhrasingContentSchema(), + }, + }, }, ], }, deprecated: [ + { + supports, + attributes: { + ...schema, + width: { + type: 'string', + }, + }, + save( { attributes } ) { + const { + width, + align, + content, + dropCap, + backgroundColor, + textColor, + customBackgroundColor, + customTextColor, + fontSize, + customFontSize, + } = attributes; + + const textClass = getColorClass( 'color', textColor ); + const backgroundClass = getColorClass( 'background-color', backgroundColor ); + const fontSizeClass = fontSize && `is-${ fontSize }-text`; + + const className = classnames( { + [ `align${ width }` ]: width, + 'has-background': backgroundColor || customBackgroundColor, + 'has-drop-cap': dropCap, + [ fontSizeClass ]: fontSizeClass, + [ textClass ]: textClass, + [ backgroundClass ]: backgroundClass, + } ); + + const styles = { + backgroundColor: backgroundClass ? undefined : customBackgroundColor, + color: textClass ? undefined : customTextColor, + fontSize: fontSizeClass ? undefined : customFontSize, + textAlign: align, + }; + + return ( + <RichText.Content + tagName="p" + style={ styles } + className={ className ? className : undefined } + value={ content } + /> + ); + }, + }, { supports, attributes: omit( { @@ -428,13 +449,19 @@ export const settings = { }, edit: compose( - withColors, + withColors( ( getColor, setColor, { attributes, setAttributes } ) => { + return { + backgroundColor: getColor( attributes.backgroundColor, attributes.customBackgroundColor, 'background-color' ), + setBackgroundColor: setColor( 'backgroundColor', 'customBackgroundColor', setAttributes ), + textColor: getColor( attributes.textColor, attributes.customTextColor, 'color' ), + setTextColor: setColor( 'textColor', 'customTextColor', setAttributes ), + }; + } ), FallbackStyles, )( ParagraphBlock ), save( { attributes } ) { const { - width, align, content, dropCap, @@ -448,10 +475,9 @@ export const settings = { const textClass = getColorClass( 'color', textColor ); const backgroundClass = getColorClass( 'background-color', backgroundColor ); - const fontSizeClass = fontSize && FONT_SIZES[ fontSize ] && `is-${ fontSize }-text`; + const fontSizeClass = fontSize && `is-${ fontSize }-text`; const className = classnames( { - [ `align${ width }` ]: width, 'has-background': backgroundColor || customBackgroundColor, 'has-drop-cap': dropCap, [ fontSizeClass ]: fontSizeClass, diff --git a/core-blocks/paragraph/test/__snapshots__/index.js.snap b/core-blocks/paragraph/test/__snapshots__/index.js.snap index 0369b0add114d..95143b5865588 100644 --- a/core-blocks/paragraph/test/__snapshots__/index.js.snap +++ b/core-blocks/paragraph/test/__snapshots__/index.js.snap @@ -5,7 +5,7 @@ exports[`core/paragraph block edit matches snapshot 1`] = ` <div> <div - class="blocks-rich-text" + class="editor-rich-text" > <div> <div> @@ -17,13 +17,13 @@ exports[`core/paragraph block edit matches snapshot 1`] = ` aria-expanded="false" aria-label="Add text or type / to add content" aria-multiline="false" - class="wp-block-paragraph blocks-rich-text__tinymce" + class="wp-block-paragraph editor-rich-text__tinymce" contenteditable="true" data-is-placeholder-visible="true" role="textbox" /> <p - class="blocks-rich-text__tinymce wp-block-paragraph" + class="editor-rich-text__tinymce wp-block-paragraph" > Add text or type / to add content </p> diff --git a/core-blocks/preformatted/index.js b/core-blocks/preformatted/index.js index 82a594da4ab5a..c3466a5e8e116 100644 --- a/core-blocks/preformatted/index.js +++ b/core-blocks/preformatted/index.js @@ -2,10 +2,8 @@ * WordPress */ import { __ } from '@wordpress/i18n'; -import { - createBlock, - RichText, -} from '@wordpress/blocks'; +import { createBlock, getPhrasingContentSchema } from '@wordpress/blocks'; +import { RichText } from '@wordpress/editor'; /** * Internal dependencies @@ -17,7 +15,7 @@ export const name = 'core/preformatted'; export const settings = { title: __( 'Preformatted' ), - description: __( 'Preformatted text keeps your spaces, tabs and linebreaks as they are.' ), + description: __( 'Add text that respects your spacing and tabs, and also allows styling.' ), icon: 'text', @@ -48,6 +46,11 @@ export const settings = { node.firstChild.nodeName === 'CODE' ) ), + schema: { + pre: { + children: getPhrasingContentSchema(), + }, + }, }, ], to: [ diff --git a/core-blocks/preformatted/test/__snapshots__/index.js.snap b/core-blocks/preformatted/test/__snapshots__/index.js.snap index 89317044d5b73..ff76887661dcf 100644 --- a/core-blocks/preformatted/test/__snapshots__/index.js.snap +++ b/core-blocks/preformatted/test/__snapshots__/index.js.snap @@ -2,7 +2,7 @@ exports[`core/preformatted block edit matches snapshot 1`] = ` <div - class="wp-block-preformatted blocks-rich-text" + class="wp-block-preformatted editor-rich-text" > <div> <div> @@ -14,13 +14,13 @@ exports[`core/preformatted block edit matches snapshot 1`] = ` aria-expanded="false" aria-label="Write preformatted text…" aria-multiline="false" - class="blocks-rich-text__tinymce" + class="editor-rich-text__tinymce" contenteditable="true" data-is-placeholder-visible="true" role="textbox" /> <pre - class="blocks-rich-text__tinymce" + class="editor-rich-text__tinymce" > Write preformatted text… </pre> diff --git a/core-blocks/pullquote/editor.scss b/core-blocks/pullquote/editor.scss index 974861f8f6c54..b52db1b10d61e 100644 --- a/core-blocks/pullquote/editor.scss +++ b/core-blocks/pullquote/editor.scss @@ -1,8 +1,8 @@ .editor-block-list__block[data-type="core/pullquote"] { &[data-align="left"], &[data-align="right"] { - & .blocks-pullquote__content .blocks-rich-text__tinymce[data-is-empty="true"]:before, - & .blocks-rich-text p { + & .blocks-pullquote__content .editor-rich-text__tinymce[data-is-empty="true"]:before, + & .editor-rich-text p { font-size: 20px; } } @@ -13,19 +13,19 @@ display: block; } - cite .blocks-rich-text__tinymce[data-is-empty="true"]:before { + cite .editor-rich-text__tinymce[data-is-empty="true"]:before { font-size: 14px; font-family: $default-font; } - .blocks-rich-text__tinymce[data-is-empty="true"]:before { + .editor-rich-text__tinymce[data-is-empty="true"]:before { width: 100%; left: 50%; transform: translateX( -50% ); } - & > .blocks-pullquote__content .blocks-rich-text__tinymce[data-is-empty="true"]:before, - & > .blocks-rich-text p { + & > .blocks-pullquote__content .editor-rich-text__tinymce[data-is-empty="true"]:before, + & > .editor-rich-text p { font-size: 24px; line-height: 1.6; } diff --git a/core-blocks/pullquote/index.js b/core-blocks/pullquote/index.js index 47d84e99fd089..fd949bdeaff69 100644 --- a/core-blocks/pullquote/index.js +++ b/core-blocks/pullquote/index.js @@ -1,21 +1,18 @@ /** * External dependencies */ -import { castArray } from 'lodash'; +import { map } from 'lodash'; /** * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { withState } from '@wordpress/components'; import { Fragment } from '@wordpress/element'; import { - createBlock, BlockControls, BlockAlignmentToolbar, RichText, - InnerBlocks, -} from '@wordpress/blocks'; +} from '@wordpress/editor'; /** * Internal dependencies @@ -23,7 +20,21 @@ import { import './editor.scss'; import './style.scss'; +const toRichTextValue = ( value ) => map( value, ( ( subValue ) => subValue.children ) ); +const fromRichTextValue = ( value ) => map( value, ( subValue ) => ( { + children: subValue, +} ) ); const blockAttributes = { + value: { + type: 'array', + source: 'query', + selector: 'blockquote > p', + query: { + children: { + source: 'node', + }, + }, + }, citation: { type: 'array', source: 'children', @@ -41,7 +52,7 @@ export const settings = { title: __( 'Pullquote' ), - description: __( 'A pullquote is a brief, attention-catching quotation taken from the main text of an article and used as a subheading or graphic feature.' ), + description: __( 'Highlight a quote from your post or page by displaying it as a graphic element.' ), icon: 'format-quote', @@ -56,12 +67,9 @@ export const settings = { } }, - edit: withState( { - editable: 'content', - } )( ( { attributes, setAttributes, isSelected, className, editable, setState } ) => { - const { citation, align } = attributes; + edit( { attributes, setAttributes, isSelected, className } ) { + const { value, citation, align } = attributes; const updateAlignment = ( nextAlign ) => setAttributes( { align: nextAlign } ); - const onSetActiveEditable = ( newEditable ) => () => setState( { editable: newEditable } ); return ( <Fragment> @@ -72,7 +80,18 @@ export const settings = { /> </BlockControls> <blockquote className={ className }> - <InnerBlocks /> + <RichText + multiline="p" + value={ toRichTextValue( value ) } + onChange={ + ( nextValue ) => setAttributes( { + value: fromRichTextValue( nextValue ), + } ) + } + /* translators: the text of the quotation */ + placeholder={ __( 'Write quote…' ) } + wrapperClassName="blocks-pullquote__content" + /> { ( citation || isSelected ) && ( <RichText tagName="cite" @@ -84,21 +103,19 @@ export const settings = { citation: nextCitation, } ) } - isSelected={ isSelected && editable === 'cite' } - onFocus={ onSetActiveEditable( 'cite' ) } /> ) } </blockquote> </Fragment> ); - } ), + }, save( { attributes } ) { - const { citation, align } = attributes; + const { value, citation, align } = attributes; return ( <blockquote className={ `align${ align }` }> - <InnerBlocks.Content /> + <RichText.Content value={ toRichTextValue( value ) } /> { citation && citation.length > 0 && <RichText.Content tagName="cite" value={ citation } /> } </blockquote> ); @@ -107,54 +124,6 @@ export const settings = { deprecated: [ { attributes: { ...blockAttributes, - value: { - type: 'array', - source: 'query', - selector: 'blockquote > p', - query: { - children: { - source: 'node', - }, - }, - }, - }, - - migrate( { value = [], ...attributes } ) { - return [ - attributes, - value.map( ( { children: paragraph } ) => - createBlock( 'core/paragraph', { - content: castArray( paragraph.props.children ), - } ) - ), - ]; - }, - - save( { attributes } ) { - const { value, citation, align } = attributes; - - return ( - <blockquote className={ `align${ align }` }> - { value && value.map( ( paragraph, i ) => - <p key={ i }>{ paragraph.children && paragraph.children.props.children }</p> - ) } - { citation && citation.length > 0 && <RichText.Content tagName="cite" value={ citation } /> } - </blockquote> - ); - }, - }, { - attributes: { - ...blockAttributes, - value: { - type: 'array', - source: 'query', - selector: 'blockquote > p', - query: { - children: { - source: 'node', - }, - }, - }, citation: { type: 'array', source: 'children', @@ -167,9 +136,7 @@ export const settings = { return ( <blockquote className={ `align${ align }` }> - { value && value.map( ( paragraph, i ) => - <p key={ i }>{ paragraph.children && paragraph.children.props.children }</p> - ) } + <RichText.Content value={ toRichTextValue( value ) } /> { citation && citation.length > 0 && <RichText.Content tagName="footer" value={ citation } /> } </blockquote> ); diff --git a/core-blocks/pullquote/test/__snapshots__/index.js.snap b/core-blocks/pullquote/test/__snapshots__/index.js.snap index cb551f1f95101..3844d8a78013e 100644 --- a/core-blocks/pullquote/test/__snapshots__/index.js.snap +++ b/core-blocks/pullquote/test/__snapshots__/index.js.snap @@ -3,5 +3,35 @@ exports[`core/pullquote block edit matches snapshot 1`] = ` <blockquote class="wp-block-pullquote" -/> +> + <div + class="blocks-pullquote__content editor-rich-text" + > + <div> + <div> + <div + class="components-autocomplete" + > + <div + aria-autocomplete="list" + aria-expanded="false" + aria-label="Write quote…" + aria-multiline="true" + class="editor-rich-text__tinymce" + contenteditable="true" + data-is-placeholder-visible="true" + role="textbox" + /> + <div + class="editor-rich-text__tinymce" + > + <p> + Write quote… + </p> + </div> + </div> + </div> + </div> + </div> +</blockquote> `; diff --git a/core-blocks/quote/index.js b/core-blocks/quote/index.js index 2e0f57984079d..5e0760957182f 100644 --- a/core-blocks/quote/index.js +++ b/core-blocks/quote/index.js @@ -1,23 +1,21 @@ /** * External dependencies */ +import { castArray, get, isString } from 'lodash'; import classnames from 'classnames'; -import { castArray } from 'lodash'; /** * WordPress dependencies */ import { __, sprintf } from '@wordpress/i18n'; -import { Toolbar, withState } from '@wordpress/components'; +import { Toolbar } from '@wordpress/components'; import { Fragment } from '@wordpress/element'; +import { createBlock, getPhrasingContentSchema } from '@wordpress/blocks'; import { - createBlock, - rawHandler, BlockControls, AlignmentToolbar, RichText, - InnerBlocks, -} from '@wordpress/blocks'; +} from '@wordpress/editor'; /** * Internal dependencies @@ -25,7 +23,23 @@ import { import './style.scss'; import './editor.scss'; +const toRichTextValue = ( value ) => value.map( ( ( subValue ) => subValue.children ) ); +const fromRichTextValue = ( value ) => value.map( ( subValue ) => ( { + children: subValue, +} ) ); + const blockAttributes = { + value: { + type: 'array', + source: 'query', + selector: 'blockquote > p', + query: { + children: { + source: 'node', + }, + }, + default: [], + }, citation: { type: 'array', source: 'children', @@ -44,7 +58,7 @@ export const name = 'core/quote'; export const settings = { title: __( 'Quote' ), - description: __( 'Quote. In quoting others, we cite ourselves. (Julio Cortázar)' ), + description: __( 'Maybe someone else said it better -- add some quoted text.' ), icon: 'format-quote', category: 'common', @@ -52,41 +66,117 @@ export const settings = { transforms: { from: [ - ...[ 'core/paragraph', 'core/heading' ].map( ( fromName ) => ( { + { + type: 'block', + blocks: [ 'core/paragraph' ], + transform: ( { content } ) => { + return createBlock( 'core/quote', { + value: [ + { children: <p key="1">{ content }</p> }, + ], + } ); + }, + }, + { type: 'block', - blocks: [ fromName ], - transform: ( attributes ) => createBlock( name, {}, [ - createBlock( fromName, attributes ), - ] ), - } ) ), + blocks: [ 'core/heading' ], + transform: ( { content } ) => { + return createBlock( 'core/quote', { + value: [ + { children: <p key="1">{ content }</p> }, + ], + } ); + }, + }, { type: 'pattern', regExp: /^>\s/, - transform: ( attributes ) => createBlock( name, {}, [ - createBlock( 'core/paragraph', attributes ), - ] ), + transform: ( { content } ) => { + return createBlock( 'core/quote', { + value: [ + { children: <p key="1">{ content }</p> }, + ], + } ); + }, }, { type: 'raw', - isMatch: ( node ) => node.nodeName === 'BLOCKQUOTE', - transform( node ) { - return createBlock( name, {}, rawHandler( { - HTML: node.innerHTML, - mode: 'BLOCKS', - } ) ); + selector: 'blockquote', + schema: { + blockquote: { + children: { + p: { + children: getPhrasingContentSchema(), + }, + }, + }, + }, + }, + ], + to: [ + { + type: 'block', + blocks: [ 'core/paragraph' ], + transform: ( { value, citation } ) => { + // transforming an empty quote + if ( ( ! value || ! value.length ) && ! citation ) { + return createBlock( 'core/paragraph' ); + } + // transforming a quote with content + return ( value || [] ).map( ( item ) => createBlock( 'core/paragraph', { + content: [ get( item, [ 'children', 'props', 'children' ], '' ) ], + } ) ).concat( citation ? createBlock( 'core/paragraph', { + content: citation, + } ) : [] ); + }, + }, + { + type: 'block', + blocks: [ 'core/heading' ], + transform: ( { value, citation, ...attrs } ) => { + // if no text content exist just transform the quote into an heading block + // using citation as the content, it may be empty creating an empty heading block. + if ( ( ! value || ! value.length ) ) { + return createBlock( 'core/heading', { + content: citation, + } ); + } + + const firstValue = get( value, [ 0, 'children' ] ); + const headingContent = castArray( isString( firstValue ) ? + firstValue : + get( firstValue, [ 'props', 'children' ], '' ) + ); + + // if the quote content just contains a paragraph and no citation exist + // convert the quote content into and heading block. + if ( ! citation && value.length === 1 ) { + return createBlock( 'core/heading', { + content: headingContent, + } ); + } + + // In the normal case convert the first paragraph of quote into an heading + // and create a new quote block equal tl what we had excluding the first paragraph + const heading = createBlock( 'core/heading', { + content: headingContent, + } ); + + const quote = createBlock( 'core/quote', { + ...attrs, + citation, + value: value.slice( 1 ), + } ); + + return [ heading, quote ]; }, }, ], }, - edit: withState( { - editable: 'content', - } )( ( { attributes, setAttributes, isSelected, className, editable, setState } ) => { - const { align, citation, style } = attributes; + edit( { attributes, setAttributes, isSelected, mergeBlocks, onReplace, className } ) { + const { align, value, citation, style } = attributes; const containerClassname = classnames( className, style === 2 ? 'is-large' : '' ); - const onSetActiveEditable = ( newEditable ) => () => { - setState( { editable: newEditable } ); - }; return ( <Fragment> @@ -110,7 +200,24 @@ export const settings = { className={ containerClassname } style={ { textAlign: align } } > - <InnerBlocks /> + <RichText + multiline="p" + value={ toRichTextValue( value ) } + onChange={ + ( nextValue ) => setAttributes( { + value: fromRichTextValue( nextValue ), + } ) + } + onMerge={ mergeBlocks } + onRemove={ ( forward ) => { + const hasEmptyCitation = ! citation || citation.length === 0; + if ( ! forward && hasEmptyCitation ) { + onReplace( [] ); + } + } } + /* translators: the text of the quotation */ + placeholder={ __( 'Write quote…' ) } + /> { ( ( citation && citation.length > 0 ) || isSelected ) && ( <RichText tagName="cite" @@ -122,24 +229,22 @@ export const settings = { } /* translators: the individual or entity quoted */ placeholder={ __( 'Write citation…' ) } - isSelected={ isSelected && editable === 'cite' } - onFocus={ onSetActiveEditable( 'cite' ) } /> ) } </blockquote> </Fragment> ); - } ), + }, save( { attributes } ) { - const { align, citation, style } = attributes; + const { align, value, citation, style } = attributes; return ( <blockquote className={ style === 2 ? 'is-large' : '' } style={ { textAlign: align ? align : null } } > - <InnerBlocks.Content /> + <RichText.Content value={ toRichTextValue( value ) } /> { citation && citation.length > 0 && <RichText.Content tagName="cite" value={ citation } /> } </blockquote> ); @@ -149,60 +254,6 @@ export const settings = { { attributes: { ...blockAttributes, - value: { - type: 'array', - source: 'query', - selector: 'blockquote > p', - query: { - children: { - source: 'node', - }, - }, - default: [], - }, - }, - - migrate( { value = [], ...attributes } ) { - return [ - attributes, - value.map( ( { children: paragraph } ) => - createBlock( 'core/paragraph', { - content: castArray( paragraph.props.children ), - } ) - ), - ]; - }, - - save( { attributes } ) { - const { align, value, citation, style } = attributes; - - return ( - <blockquote - className={ style === 2 ? 'is-large' : '' } - style={ { textAlign: align ? align : null } } - > - { value.map( ( paragraph, i ) => ( - <p key={ i }>{ paragraph.children && paragraph.children.props.children }</p> - ) ) } - { citation && citation.length > 0 && <RichText.Content tagName="cite" value={ citation } /> } - </blockquote> - ); - }, - }, - { - attributes: { - ...blockAttributes, - value: { - type: 'array', - source: 'query', - selector: 'blockquote > p', - query: { - children: { - source: 'node', - }, - }, - default: [], - }, citation: { type: 'array', source: 'children', @@ -218,9 +269,7 @@ export const settings = { className={ `blocks-quote-style-${ style }` } style={ { textAlign: align ? align : null } } > - { value.map( ( paragraph, i ) => ( - <p key={ i }>{ paragraph.children && paragraph.children.props.children }</p> - ) ) } + <RichText.Content value={ toRichTextValue( value ) } /> { citation && citation.length > 0 && <RichText.Content tagName="footer" value={ citation } /> } </blockquote> ); diff --git a/core-blocks/quote/test/__snapshots__/index.js.snap b/core-blocks/quote/test/__snapshots__/index.js.snap index dd11350eb8d88..813b6d8417cb6 100644 --- a/core-blocks/quote/test/__snapshots__/index.js.snap +++ b/core-blocks/quote/test/__snapshots__/index.js.snap @@ -3,5 +3,35 @@ exports[`core/quote block edit matches snapshot 1`] = ` <blockquote class="wp-block-quote" -/> +> + <div + class="editor-rich-text" + > + <div> + <div> + <div + class="components-autocomplete" + > + <div + aria-autocomplete="list" + aria-expanded="false" + aria-label="Write quote…" + aria-multiline="true" + class="editor-rich-text__tinymce" + contenteditable="true" + data-is-placeholder-visible="true" + role="textbox" + /> + <div + class="editor-rich-text__tinymce" + > + <p> + Write quote… + </p> + </div> + </div> + </div> + </div> + </div> +</blockquote> `; diff --git a/core-blocks/separator/index.js b/core-blocks/separator/index.js index c0d4607d7f385..17ec90ca938c2 100644 --- a/core-blocks/separator/index.js +++ b/core-blocks/separator/index.js @@ -14,7 +14,7 @@ export const name = 'core/separator'; export const settings = { title: __( 'Separator' ), - description: __( 'Use the separator to indicate a thematic change in the content.' ), + description: __( 'Insert a horizontal line where you want to create a break between ideas.' ), icon: 'minus', @@ -32,7 +32,10 @@ export const settings = { }, { type: 'raw', - isMatch: ( node ) => node.nodeName === 'HR', + selector: 'hr', + schema: { + hr: {}, + }, }, ], }, diff --git a/core-blocks/shortcode/editor.scss b/core-blocks/shortcode/editor.scss index 864856a84ccb1..9eac821b9d656 100644 --- a/core-blocks/shortcode/editor.scss +++ b/core-blocks/shortcode/editor.scss @@ -15,7 +15,7 @@ font-weight: 600; } - .blocks-plain-text { + .editor-plain-text { /* * Unit required on zero value to work around IE bug. * https://github.com/philipwalton/flexbugs#flexbug-4 diff --git a/core-blocks/shortcode/index.js b/core-blocks/shortcode/index.js index a0fe03a30ddf0..264e1a7566a0c 100644 --- a/core-blocks/shortcode/index.js +++ b/core-blocks/shortcode/index.js @@ -4,7 +4,7 @@ import { RawHTML } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { withInstanceId, Dashicon } from '@wordpress/components'; -import { PlainText } from '@wordpress/blocks'; +import { PlainText } from '@wordpress/editor'; /** * Internal dependencies @@ -16,7 +16,7 @@ export const name = 'core/shortcode'; export const settings = { title: __( 'Shortcode' ), - description: __( 'A shortcode is a WordPress-specific code snippet that is written between square brackets as [shortcode]. ' ), + description: __( 'Add a shortcode -- a WordPress-specific snippet of code written between square brackets.' ), icon: 'shortcode', diff --git a/core-blocks/shortcode/test/__snapshots__/index.js.snap b/core-blocks/shortcode/test/__snapshots__/index.js.snap index f45b727243d03..5ef96b79116b0 100644 --- a/core-blocks/shortcode/test/__snapshots__/index.js.snap +++ b/core-blocks/shortcode/test/__snapshots__/index.js.snap @@ -24,7 +24,7 @@ exports[`core/shortcode block edit matches snapshot 1`] = ` Shortcode </label> <textarea - class="blocks-plain-text input-control" + class="editor-plain-text input-control" id="blocks-shortcode-input-0" placeholder="Write shortcode here…" rows="1" diff --git a/core-blocks/spacer/editor.scss b/core-blocks/spacer/editor.scss new file mode 100644 index 0000000000000..e82a228b60f11 --- /dev/null +++ b/core-blocks/spacer/editor.scss @@ -0,0 +1,24 @@ +.editor-block-list__block[data-type="core/spacer"].is-selected .editor-block-list__block-edit { + background: $light-gray-200; + + .wp-block-spacer__resize-handler-top, + .wp-block-spacer__resize-handler-bottom { + display: block; + } +} + +.wp-block-spacer__resize-handler-top, +.wp-block-spacer__resize-handler-bottom { + display: none; + border-radius: 50%; + border: 2px solid white; + width: 15px !important; + height: 15px !important; + position: absolute; + background: $blue-medium-500; + padding: 0 3px 3px 0; + box-sizing: border-box; + cursor: se-resize; + left: 50% !important; + margin-left: -7.5px; +} diff --git a/core-blocks/spacer/index.js b/core-blocks/spacer/index.js new file mode 100644 index 0000000000000..055fba1f1dac1 --- /dev/null +++ b/core-blocks/spacer/index.js @@ -0,0 +1,96 @@ +/** + * External dependencies + */ +import ResizableBox from 're-resizable'; + +/** + * WordPress + */ +import { Fragment } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { InspectorControls } from '@wordpress/editor'; +import { BaseControl, PanelBody } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import './editor.scss'; + +export const name = 'core/spacer'; + +export const settings = { + title: __( 'Spacer' ), + + description: __( 'Add an element with empty space and custom height.' ), + + icon: 'image-flip-vertical', + + category: 'layout', + + attributes: { + height: { + type: 'number', + default: 100, + }, + }, + + edit( { attributes, setAttributes, toggleSelection } ) { + const { height } = attributes; + + return ( + <Fragment> + <ResizableBox + size={ { + height, + } } + minHeight="20" + handleClasses={ { + top: 'wp-block-spacer__resize-handler-top', + bottom: 'wp-block-spacer__resize-handler-bottom', + } } + enable={ { + top: true, + right: false, + bottom: true, + left: false, + topRight: false, + bottomRight: false, + bottomLeft: false, + topLeft: false, + } } + onResizeStop={ ( event, direction, elt, delta ) => { + setAttributes( { + height: parseInt( height + delta.height, 10 ), + } ); + toggleSelection( true ); + } } + onResizeStart={ () => { + toggleSelection( false ); + } } + /> + <InspectorControls> + <PanelBody title={ __( 'Spacer Settings' ) }> + <BaseControl label={ __( 'Height in pixels' ) }> + <input + type="number" + onChange={ ( event ) => { + setAttributes( { + height: parseInt( event.target.value, 10 ), + } ); + } } + aria-label={ __( 'Height for the spacer element in pixels.' ) } + value={ height } + min="20" + step="10" + /> + </BaseControl> + </PanelBody> + </InspectorControls> + </Fragment> + ); + }, + + save( { attributes } ) { + return <div style={ { height: attributes.height } } aria-hidden />; + }, +}; diff --git a/core-blocks/subhead/index.js b/core-blocks/subhead/index.js index 12ca9a99d358c..0ce7407453ea2 100644 --- a/core-blocks/subhead/index.js +++ b/core-blocks/subhead/index.js @@ -2,10 +2,8 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { - createBlock, - RichText, -} from '@wordpress/blocks'; +import { createBlock } from '@wordpress/blocks'; +import { RichText } from '@wordpress/editor'; /** * Internal dependencies @@ -18,7 +16,7 @@ export const name = 'core/subhead'; export const settings = { title: __( 'Subhead' ), - description: __( 'Explanatory text under the main heading of an article.' ), + description: __( 'What\'s a subhead? Smaller than a headline, bigger than basic text.' ), icon: 'text', diff --git a/core-blocks/table/index.js b/core-blocks/table/index.js index 42c24adc941c9..f36b900aa2693 100644 --- a/core-blocks/table/index.js +++ b/core-blocks/table/index.js @@ -7,11 +7,12 @@ import { __ } from '@wordpress/i18n'; * WordPress dependencies */ import { Fragment } from '@wordpress/element'; +import { getPhrasingContentSchema } from '@wordpress/blocks'; import { BlockControls, BlockAlignmentToolbar, RichText, -} from '@wordpress/blocks'; +} from '@wordpress/editor'; /** * Internal dependencies @@ -20,11 +21,40 @@ import './editor.scss'; import './style.scss'; import TableBlock from './table-block'; +const tableContentSchema = { + tr: { + children: { + th: { + children: getPhrasingContentSchema(), + }, + td: { + children: getPhrasingContentSchema(), + }, + }, + }, +}; + +const tableSchema = { + table: { + children: { + thead: { + children: tableContentSchema, + }, + tfoot: { + children: tableContentSchema, + }, + tbody: { + children: tableContentSchema, + }, + }, + }, +}; + export const name = 'core/table'; export const settings = { title: __( 'Table' ), - description: __( 'Tables. Best used for tabular data.' ), + description: __( 'Insert a table -- perfect for sharing charts and data.' ), icon: 'editor-table', category: 'formatting', @@ -49,7 +79,8 @@ export const settings = { from: [ { type: 'raw', - isMatch: ( node ) => node.nodeName === 'TABLE', + selector: 'table', + schema: tableSchema, }, ], }, diff --git a/core-blocks/table/table-block.js b/core-blocks/table/table-block.js index eb866e58f96dd..642db9fbccdbd 100644 --- a/core-blocks/table/table-block.js +++ b/core-blocks/table/table-block.js @@ -7,7 +7,7 @@ import { __ } from '@wordpress/i18n'; import { BlockControls, RichText, -} from '@wordpress/blocks'; +} from '@wordpress/editor'; function isTableSelected( editor ) { return editor.dom.getParent( diff --git a/core-blocks/table/test/__snapshots__/index.js.snap b/core-blocks/table/test/__snapshots__/index.js.snap index 77337c6c8b1fb..8214a201bb281 100644 --- a/core-blocks/table/test/__snapshots__/index.js.snap +++ b/core-blocks/table/test/__snapshots__/index.js.snap @@ -2,7 +2,7 @@ exports[`core/embed block edit matches snapshot 1`] = ` <div - class="wp-block-table blocks-rich-text" + class="wp-block-table editor-rich-text" > <div> <div> @@ -13,7 +13,7 @@ exports[`core/embed block edit matches snapshot 1`] = ` aria-autocomplete="list" aria-expanded="false" aria-multiline="false" - class="blocks-rich-text__tinymce" + class="editor-rich-text__tinymce" contenteditable="true" > <tbody> diff --git a/core-blocks/test/fixtures/core__pullquote.html b/core-blocks/test/fixtures/core__pullquote.html index 8794112577094..06d1ea9ab6114 100644 --- a/core-blocks/test/fixtures/core__pullquote.html +++ b/core-blocks/test/fixtures/core__pullquote.html @@ -1,8 +1,5 @@ <!-- wp:core/pullquote --> <blockquote class="wp-block-pullquote alignnone"> - <!-- wp:core/paragraph --> - <p>Testing pullquote block...</p> - <!-- /wp:core/paragraph --> - <cite>...with a caption</cite> +<p>Testing pullquote block...</p><cite>...with a caption</cite> </blockquote> <!-- /wp:core/pullquote --> diff --git a/core-blocks/test/fixtures/core__pullquote.json b/core-blocks/test/fixtures/core__pullquote.json index 0b477b21e7015..e044ae447f65f 100644 --- a/core-blocks/test/fixtures/core__pullquote.json +++ b/core-blocks/test/fixtures/core__pullquote.json @@ -4,26 +4,26 @@ "name": "core/pullquote", "isValid": true, "attributes": { + "value": [ + { + "children": { + "type": "p", + "key": null, + "ref": null, + "props": { + "children": "Testing pullquote block..." + }, + "_owner": null, + "_store": {} + } + } + ], "citation": [ "...with a caption" ], "align": "none" }, - "innerBlocks": [ - { - "name": "core/paragraph", - "uid": "_uid_0", - "isValid": true, - "attributes": { - "content": [ - "Testing pullquote block..." - ], - "dropCap": false - }, - "innerBlocks": [], - "originalContent": "<p>Testing pullquote block...</p>" - } - ], - "originalContent": "<blockquote class=\"wp-block-pullquote alignnone\">\n\t\n\t<cite>...with a caption</cite>\n</blockquote>" + "innerBlocks": [], + "originalContent": "<blockquote class=\"wp-block-pullquote alignnone\">\n<p>Testing pullquote block...</p><cite>...with a caption</cite>\n</blockquote>" } ] diff --git a/core-blocks/test/fixtures/core__pullquote.parsed.json b/core-blocks/test/fixtures/core__pullquote.parsed.json index 248585392f75d..4a96da0d5574b 100644 --- a/core-blocks/test/fixtures/core__pullquote.parsed.json +++ b/core-blocks/test/fixtures/core__pullquote.parsed.json @@ -2,15 +2,8 @@ { "blockName": "core/pullquote", "attrs": null, - "innerBlocks": [ - { - "blockName": "core/paragraph", - "attrs": null, - "innerBlocks": [], - "innerHTML": "\n\t<p>Testing pullquote block...</p>\n\t" - } - ], - "innerHTML": "\n<blockquote class=\"wp-block-pullquote alignnone\">\n\t\n\t<cite>...with a caption</cite>\n</blockquote>\n" + "innerBlocks": [], + "innerHTML": "\n<blockquote class=\"wp-block-pullquote alignnone\">\n<p>Testing pullquote block...</p><cite>...with a caption</cite>\n</blockquote>\n" }, { "attrs": {}, diff --git a/core-blocks/test/fixtures/core__pullquote.serialized.html b/core-blocks/test/fixtures/core__pullquote.serialized.html index a8617d21d83bd..02aa277f75653 100644 --- a/core-blocks/test/fixtures/core__pullquote.serialized.html +++ b/core-blocks/test/fixtures/core__pullquote.serialized.html @@ -1,6 +1,4 @@ <!-- wp:pullquote --> <blockquote class="wp-block-pullquote alignnone"> - <!-- wp:paragraph --> - <p>Testing pullquote block...</p> - <!-- /wp:paragraph --><cite>...with a caption</cite></blockquote> + <p>Testing pullquote block...</p><cite>...with a caption</cite></blockquote> <!-- /wp:pullquote --> diff --git a/core-blocks/test/fixtures/core__pullquote__multi-paragraph.html b/core-blocks/test/fixtures/core__pullquote__multi-paragraph.html index 4c6800b8f1e50..113d829d01329 100644 --- a/core-blocks/test/fixtures/core__pullquote__multi-paragraph.html +++ b/core-blocks/test/fixtures/core__pullquote__multi-paragraph.html @@ -1,11 +1,7 @@ <!-- wp:core/pullquote --> <blockquote class="wp-block-pullquote alignnone"> - <!-- wp:core/paragraph --> <p>Paragraph <strong>one</strong></p> - <!-- /wp:core/paragraph --> - <!-- wp:core/paragraph --> <p>Paragraph two</p> - <!-- /wp:core/paragraph --> <cite>by whomever</cite> </blockquote> <!-- /wp:core/pullquote --> diff --git a/core-blocks/test/fixtures/core__pullquote__multi-paragraph.json b/core-blocks/test/fixtures/core__pullquote__multi-paragraph.json index ac24bc35d1500..c2a6c0d770de7 100644 --- a/core-blocks/test/fixtures/core__pullquote__multi-paragraph.json +++ b/core-blocks/test/fixtures/core__pullquote__multi-paragraph.json @@ -4,43 +4,50 @@ "name": "core/pullquote", "isValid": true, "attributes": { + "value": [ + { + "children": { + "type": "p", + "key": null, + "ref": null, + "props": { + "children": [ + "Paragraph ", + { + "type": "strong", + "key": "_domReact71", + "ref": null, + "props": { + "children": "one" + }, + "_owner": null, + "_store": {} + } + ] + }, + "_owner": null, + "_store": {} + } + }, + { + "children": { + "type": "p", + "key": null, + "ref": null, + "props": { + "children": "Paragraph two" + }, + "_owner": null, + "_store": {} + } + } + ], "citation": [ "by whomever" ], "align": "none" }, - "innerBlocks": [ - { - "uid": "_uid_0", - "name": "core/paragraph", - "isValid": true, - "attributes": { - "content": [ - "Paragraph ", - { - "type": "strong", - "children": "one" - } - ], - "dropCap": false - }, - "innerBlocks": [], - "originalContent": "<p>Paragraph <strong>one</strong></p>" - }, - { - "uid": "_uid_1", - "name": "core/paragraph", - "isValid": true, - "attributes": { - "content": [ - "Paragraph two" - ], - "dropCap": false - }, - "innerBlocks": [], - "originalContent": "<p>Paragraph two</p>" - } - ], - "originalContent": "<blockquote class=\"wp-block-pullquote alignnone\">\n\t\n \n <cite>by whomever</cite>\n</blockquote>" + "innerBlocks": [], + "originalContent": "<blockquote class=\"wp-block-pullquote alignnone\">\n <p>Paragraph <strong>one</strong></p>\n <p>Paragraph two</p>\n <cite>by whomever</cite>\n</blockquote>" } ] diff --git a/core-blocks/test/fixtures/core__pullquote__multi-paragraph.parsed.json b/core-blocks/test/fixtures/core__pullquote__multi-paragraph.parsed.json index 85048516b2797..4821344abd80a 100644 --- a/core-blocks/test/fixtures/core__pullquote__multi-paragraph.parsed.json +++ b/core-blocks/test/fixtures/core__pullquote__multi-paragraph.parsed.json @@ -2,21 +2,8 @@ { "blockName": "core/pullquote", "attrs": null, - "innerBlocks": [ - { - "blockName": "core/paragraph", - "attrs": null, - "innerBlocks": [], - "innerHTML": "\n <p>Paragraph <strong>one</strong></p>\n " - }, - { - "blockName": "core/paragraph", - "attrs": null, - "innerBlocks": [], - "innerHTML": "\n <p>Paragraph two</p>\n " - } - ], - "innerHTML": "\n<blockquote class=\"wp-block-pullquote alignnone\">\n\t\n \n <cite>by whomever</cite>\n</blockquote>\n" + "innerBlocks": [], + "innerHTML": "\n<blockquote class=\"wp-block-pullquote alignnone\">\n <p>Paragraph <strong>one</strong></p>\n <p>Paragraph two</p>\n <cite>by whomever</cite>\n</blockquote>\n" }, { "attrs": {}, diff --git a/core-blocks/test/fixtures/core__pullquote__multi-paragraph.serialized.html b/core-blocks/test/fixtures/core__pullquote__multi-paragraph.serialized.html index c57ac475a512b..58d4022398773 100644 --- a/core-blocks/test/fixtures/core__pullquote__multi-paragraph.serialized.html +++ b/core-blocks/test/fixtures/core__pullquote__multi-paragraph.serialized.html @@ -1,10 +1,5 @@ <!-- wp:pullquote --> <blockquote class="wp-block-pullquote alignnone"> - <!-- wp:paragraph --> <p>Paragraph <strong>one</strong></p> - <!-- /wp:paragraph --> - - <!-- wp:paragraph --> - <p>Paragraph two</p> - <!-- /wp:paragraph --><cite>by whomever</cite></blockquote> + <p>Paragraph two</p><cite>by whomever</cite></blockquote> <!-- /wp:pullquote --> diff --git a/core-blocks/test/fixtures/core__quote__style-1.html b/core-blocks/test/fixtures/core__quote__style-1.html index 2517aea08d419..50f330921b5a3 100644 --- a/core-blocks/test/fixtures/core__quote__style-1.html +++ b/core-blocks/test/fixtures/core__quote__style-1.html @@ -1,8 +1,3 @@ <!-- wp:core/quote {"style":"1"} --> -<blockquote class="wp-block-quote"> - <!-- wp:core/paragraph --> - <p>The editor will endeavour to create a new page and post building experience that makes writing rich posts effortless, and has “blocks” to make it easy what today might take shortcodes, custom HTML, or “mystery meat” embed discovery.</p> - <!-- /wp:core/paragraph --> - <cite>Matt Mullenweg, 2017</cite> -</blockquote> +<blockquote class="wp-block-quote"><p>The editor will endeavour to create a new page and post building experience that makes writing rich posts effortless, and has “blocks” to make it easy what today might take shortcodes, custom HTML, or “mystery meat” embed discovery.</p><cite>Matt Mullenweg, 2017</cite></blockquote> <!-- /wp:core/quote --> diff --git a/core-blocks/test/fixtures/core__quote__style-1.json b/core-blocks/test/fixtures/core__quote__style-1.json index 64f98a8efab36..1a1f57668d6bb 100644 --- a/core-blocks/test/fixtures/core__quote__style-1.json +++ b/core-blocks/test/fixtures/core__quote__style-1.json @@ -4,26 +4,26 @@ "name": "core/quote", "isValid": true, "attributes": { + "value": [ + { + "children": { + "type": "p", + "key": null, + "ref": null, + "props": { + "children": "The editor will endeavour to create a new page and post building experience that makes writing rich posts effortless, and has “blocks” to make it easy what today might take shortcodes, custom HTML, or “mystery meat” embed discovery." + }, + "_owner": null, + "_store": {} + } + } + ], "citation": [ "Matt Mullenweg, 2017" ], "style": 1 }, - "innerBlocks": [ - { - "uid": "_uid_0", - "name": "core/paragraph", - "isValid": true, - "attributes": { - "content": [ - "The editor will endeavour to create a new page and post building experience that makes writing rich posts effortless, and has “blocks” to make it easy what today might take shortcodes, custom HTML, or “mystery meat” embed discovery." - ], - "dropCap": false - }, - "innerBlocks": [], - "originalContent": "<p>The editor will endeavour to create a new page and post building experience that makes writing rich posts effortless, and has “blocks” to make it easy what today might take shortcodes, custom HTML, or “mystery meat” embed discovery.</p>" - } - ], - "originalContent": "<blockquote class=\"wp-block-quote\">\n\t\n\t<cite>Matt Mullenweg, 2017</cite>\n</blockquote>" + "innerBlocks": [], + "originalContent": "<blockquote class=\"wp-block-quote\"><p>The editor will endeavour to create a new page and post building experience that makes writing rich posts effortless, and has “blocks” to make it easy what today might take shortcodes, custom HTML, or “mystery meat” embed discovery.</p><cite>Matt Mullenweg, 2017</cite></blockquote>" } ] diff --git a/core-blocks/test/fixtures/core__quote__style-1.parsed.json b/core-blocks/test/fixtures/core__quote__style-1.parsed.json index 6feb5ac802d7e..f09e5b0b9d384 100644 --- a/core-blocks/test/fixtures/core__quote__style-1.parsed.json +++ b/core-blocks/test/fixtures/core__quote__style-1.parsed.json @@ -4,15 +4,8 @@ "attrs": { "style": "1" }, - "innerBlocks": [ - { - "blockName": "core/paragraph", - "attrs": null, - "innerBlocks": [], - "innerHTML": "\n\t<p>The editor will endeavour to create a new page and post building experience that makes writing rich posts effortless, and has “blocks” to make it easy what today might take shortcodes, custom HTML, or “mystery meat” embed discovery.</p>\n\t" - } - ], - "innerHTML": "\n<blockquote class=\"wp-block-quote\">\n\t\n\t<cite>Matt Mullenweg, 2017</cite>\n</blockquote>\n" + "innerBlocks": [], + "innerHTML": "\n<blockquote class=\"wp-block-quote\"><p>The editor will endeavour to create a new page and post building experience that makes writing rich posts effortless, and has “blocks” to make it easy what today might take shortcodes, custom HTML, or “mystery meat” embed discovery.</p><cite>Matt Mullenweg, 2017</cite></blockquote>\n" }, { "attrs": {}, diff --git a/core-blocks/test/fixtures/core__quote__style-1.serialized.html b/core-blocks/test/fixtures/core__quote__style-1.serialized.html index 68fb9ec55b95b..a56c5859bd335 100644 --- a/core-blocks/test/fixtures/core__quote__style-1.serialized.html +++ b/core-blocks/test/fixtures/core__quote__style-1.serialized.html @@ -1,6 +1,4 @@ <!-- wp:quote --> <blockquote class="wp-block-quote"> - <!-- wp:paragraph --> - <p>The editor will endeavour to create a new page and post building experience that makes writing rich posts effortless, and has “blocks” to make it easy what today might take shortcodes, custom HTML, or “mystery meat” embed discovery.</p> - <!-- /wp:paragraph --><cite>Matt Mullenweg, 2017</cite></blockquote> + <p>The editor will endeavour to create a new page and post building experience that makes writing rich posts effortless, and has “blocks” to make it easy what today might take shortcodes, custom HTML, or “mystery meat” embed discovery.</p><cite>Matt Mullenweg, 2017</cite></blockquote> <!-- /wp:quote --> diff --git a/core-blocks/test/fixtures/core__quote__style-2.html b/core-blocks/test/fixtures/core__quote__style-2.html index e44adca0be651..544a6062c1d80 100644 --- a/core-blocks/test/fixtures/core__quote__style-2.html +++ b/core-blocks/test/fixtures/core__quote__style-2.html @@ -1,8 +1,3 @@ <!-- wp:core/quote {"style":"2"} --> -<blockquote class="wp-block-quote is-large"> - <!-- wp:core/paragraph --> - <p>There is no greater agony than bearing an untold story inside you.</p> - <!-- /wp:core/paragraph --> - <cite>Maya Angelou</cite> -</blockquote> +<blockquote class="wp-block-quote is-large"><p>There is no greater agony than bearing an untold story inside you.</p><cite>Maya Angelou</cite></blockquote> <!-- /wp:core/quote --> diff --git a/core-blocks/test/fixtures/core__quote__style-2.json b/core-blocks/test/fixtures/core__quote__style-2.json index 08dd3bb011ad1..2462f8aa0a8fc 100644 --- a/core-blocks/test/fixtures/core__quote__style-2.json +++ b/core-blocks/test/fixtures/core__quote__style-2.json @@ -4,26 +4,26 @@ "name": "core/quote", "isValid": true, "attributes": { + "value": [ + { + "children": { + "type": "p", + "key": null, + "ref": null, + "props": { + "children": "There is no greater agony than bearing an untold story inside you." + }, + "_owner": null, + "_store": {} + } + } + ], "citation": [ "Maya Angelou" ], "style": 2 }, - "innerBlocks": [ - { - "uid": "_uid_0", - "name": "core/paragraph", - "isValid": true, - "attributes": { - "content": [ - "There is no greater agony than bearing an untold story inside you." - ], - "dropCap": false - }, - "innerBlocks": [], - "originalContent": "<p>There is no greater agony than bearing an untold story inside you.</p>" - } - ], - "originalContent": "<blockquote class=\"wp-block-quote is-large\">\n\t\n\t<cite>Maya Angelou</cite>\n</blockquote>" + "innerBlocks": [], + "originalContent": "<blockquote class=\"wp-block-quote is-large\"><p>There is no greater agony than bearing an untold story inside you.</p><cite>Maya Angelou</cite></blockquote>" } ] diff --git a/core-blocks/test/fixtures/core__quote__style-2.parsed.json b/core-blocks/test/fixtures/core__quote__style-2.parsed.json index c0e0d840ace73..5af57f9cc0706 100644 --- a/core-blocks/test/fixtures/core__quote__style-2.parsed.json +++ b/core-blocks/test/fixtures/core__quote__style-2.parsed.json @@ -4,15 +4,8 @@ "attrs": { "style": "2" }, - "innerBlocks": [ - { - "blockName": "core/paragraph", - "attrs": null, - "innerBlocks": [], - "innerHTML": "\n\t<p>There is no greater agony than bearing an untold story inside you.</p>\n\t" - } - ], - "innerHTML": "\n<blockquote class=\"wp-block-quote is-large\">\n\t\n\t<cite>Maya Angelou</cite>\n</blockquote>\n" + "innerBlocks": [], + "innerHTML": "\n<blockquote class=\"wp-block-quote is-large\"><p>There is no greater agony than bearing an untold story inside you.</p><cite>Maya Angelou</cite></blockquote>\n" }, { "attrs": {}, diff --git a/core-blocks/test/fixtures/core__quote__style-2.serialized.html b/core-blocks/test/fixtures/core__quote__style-2.serialized.html index 046b58ec47a9c..e715726fb9cc6 100644 --- a/core-blocks/test/fixtures/core__quote__style-2.serialized.html +++ b/core-blocks/test/fixtures/core__quote__style-2.serialized.html @@ -1,6 +1,4 @@ <!-- wp:quote {"style":2} --> <blockquote class="wp-block-quote is-large"> - <!-- wp:paragraph --> - <p>There is no greater agony than bearing an untold story inside you.</p> - <!-- /wp:paragraph --><cite>Maya Angelou</cite></blockquote> + <p>There is no greater agony than bearing an untold story inside you.</p><cite>Maya Angelou</cite></blockquote> <!-- /wp:quote --> diff --git a/core-blocks/test/fixtures/core__spacer.html b/core-blocks/test/fixtures/core__spacer.html new file mode 100644 index 0000000000000..e7c1e256196de --- /dev/null +++ b/core-blocks/test/fixtures/core__spacer.html @@ -0,0 +1,3 @@ +<!-- wp:spacer --> +<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div> +<!-- /wp:spacer --> diff --git a/core-blocks/test/fixtures/core__spacer.json b/core-blocks/test/fixtures/core__spacer.json new file mode 100644 index 0000000000000..fbc8fa80f61f2 --- /dev/null +++ b/core-blocks/test/fixtures/core__spacer.json @@ -0,0 +1,12 @@ +[ + { + "uid": "_uid_0", + "name": "core/spacer", + "isValid": true, + "attributes": { + "height": 100 + }, + "innerBlocks": [], + "originalContent": "<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"></div>" + } +] diff --git a/core-blocks/test/fixtures/core__spacer.parsed.json b/core-blocks/test/fixtures/core__spacer.parsed.json new file mode 100644 index 0000000000000..e5dad726275f4 --- /dev/null +++ b/core-blocks/test/fixtures/core__spacer.parsed.json @@ -0,0 +1,12 @@ +[ + { + "blockName": "core/spacer", + "attrs": null, + "innerBlocks": [], + "innerHTML": "\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"></div>\n" + }, + { + "attrs": {}, + "innerHTML": "\n" + } +] diff --git a/core-blocks/test/fixtures/core__spacer.serialized.html b/core-blocks/test/fixtures/core__spacer.serialized.html new file mode 100644 index 0000000000000..e7c1e256196de --- /dev/null +++ b/core-blocks/test/fixtures/core__spacer.serialized.html @@ -0,0 +1,3 @@ +<!-- wp:spacer --> +<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div> +<!-- /wp:spacer --> diff --git a/core-blocks/test/full-content.js b/core-blocks/test/full-content.js index ab0154941d923..4e9fa45cdf168 100644 --- a/core-blocks/test/full-content.js +++ b/core-blocks/test/full-content.js @@ -101,7 +101,7 @@ describe( 'full post content fixture', () => { window._wpBlocks = require( './server-registered.json' ); // Load all hooks that modify blocks - require( 'blocks/hooks' ); + require( 'editor/hooks' ); registerCoreBlocks(); } ); diff --git a/core-blocks/test/helpers/index.js b/core-blocks/test/helpers/index.js index 6c46e31e213d5..a2d4042e5c47d 100644 --- a/core-blocks/test/helpers/index.js +++ b/core-blocks/test/helpers/index.js @@ -14,7 +14,7 @@ import { } from '@wordpress/blocks'; // Hack to avoid the wrapping HoCs. -import { BlockEdit } from '../../../blocks/block-edit'; +import { BlockEdit } from '../../../editor/components/block-edit'; export const blockEditRender = ( name, settings ) => { if ( ! getBlockType( name ) ) { diff --git a/core-blocks/text-columns/editor.scss b/core-blocks/text-columns/editor.scss index 440d2736412b4..b60228bb8fff3 100644 --- a/core-blocks/text-columns/editor.scss +++ b/core-blocks/text-columns/editor.scss @@ -1,5 +1,5 @@ .wp-block-text-columns { - .blocks-rich-text__tinymce:focus { + .editor-rich-text__tinymce:focus { outline: 1px solid $light-gray-500; } } diff --git a/core-blocks/text-columns/index.js b/core-blocks/text-columns/index.js index 8b44f50f7f6f1..1d6940d8bf36f 100644 --- a/core-blocks/text-columns/index.js +++ b/core-blocks/text-columns/index.js @@ -1,20 +1,20 @@ /** * External dependencies */ -import { times } from 'lodash'; +import { get, times } from 'lodash'; /** * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { PanelBody, RangeControl, withState } from '@wordpress/components'; +import { PanelBody, RangeControl } from '@wordpress/components'; import { Fragment } from '@wordpress/element'; import { BlockControls, BlockAlignmentToolbar, InspectorControls, RichText, -} from '@wordpress/blocks'; +} from '@wordpress/editor'; /** * Internal dependencies @@ -27,7 +27,7 @@ export const name = 'core/text-columns'; export const settings = { title: __( 'Text Columns' ), - description: __( 'Add text across columns. This block is experimental' ), + description: __( 'Add text, and display it in two or more columns. Like a newspaper! This block is experimental.' ), icon: 'columns', @@ -61,13 +61,8 @@ export const settings = { } }, - edit: withState( { - editable: 'column-1', - } )( ( { attributes, setAttributes, className, isSelected, editable, setState } ) => { + edit: ( ( { attributes, setAttributes, className } ) => { const { width, content, columns } = attributes; - const onSetActiveEditable = ( newEditable ) => () => { - setState( { editable: newEditable } ); - }; return ( <Fragment> @@ -91,12 +86,11 @@ export const settings = { </InspectorControls> <div className={ `${ className } align${ width } columns-${ columns }` }> { times( columns, ( index ) => { - const key = `column-${ index }`; return ( - <div className="wp-block-column" key={ key }> + <div className="wp-block-column" key={ `column-${ index }` }> <RichText tagName="p" - value={ content && content[ index ] && content[ index ].children } + value={ get( content, [ index, 'children' ] ) } onChange={ ( nextContent ) => { setAttributes( { content: [ @@ -107,8 +101,6 @@ export const settings = { } ); } } placeholder={ __( 'New Column' ) } - isSelected={ isSelected && editable === key } - onFocus={ onSetActiveEditable( key ) } /> </div> ); @@ -124,7 +116,7 @@ export const settings = { <div className={ `align${ width } columns-${ columns }` }> { times( columns, ( index ) => <div className="wp-block-column" key={ `column-${ index }` }> - <RichText.Content tagName="p" value={ content && content[ index ].children } /> + <RichText.Content tagName="p" value={ get( content, [ index, 'children' ] ) } /> </div> ) } </div> diff --git a/core-blocks/text-columns/test/__snapshots__/index.js.snap b/core-blocks/text-columns/test/__snapshots__/index.js.snap index c1572062af8c0..439500fc13b23 100644 --- a/core-blocks/text-columns/test/__snapshots__/index.js.snap +++ b/core-blocks/text-columns/test/__snapshots__/index.js.snap @@ -8,7 +8,7 @@ exports[`core/text-columns block edit matches snapshot 1`] = ` class="wp-block-column" > <div - class="blocks-rich-text" + class="editor-rich-text" > <div> <div> @@ -20,13 +20,13 @@ exports[`core/text-columns block edit matches snapshot 1`] = ` aria-expanded="false" aria-label="New Column" aria-multiline="false" - class="blocks-rich-text__tinymce" + class="editor-rich-text__tinymce" contenteditable="true" data-is-placeholder-visible="true" role="textbox" /> <p - class="blocks-rich-text__tinymce" + class="editor-rich-text__tinymce" > New Column </p> @@ -39,7 +39,7 @@ exports[`core/text-columns block edit matches snapshot 1`] = ` class="wp-block-column" > <div - class="blocks-rich-text" + class="editor-rich-text" > <div> <div> @@ -51,13 +51,13 @@ exports[`core/text-columns block edit matches snapshot 1`] = ` aria-expanded="false" aria-label="New Column" aria-multiline="false" - class="blocks-rich-text__tinymce" + class="editor-rich-text__tinymce" contenteditable="true" data-is-placeholder-visible="true" role="textbox" /> <p - class="blocks-rich-text__tinymce" + class="editor-rich-text__tinymce" > New Column </p> diff --git a/core-blocks/verse/index.js b/core-blocks/verse/index.js index f520e72f12636..b832ee613201b 100644 --- a/core-blocks/verse/index.js +++ b/core-blocks/verse/index.js @@ -2,10 +2,8 @@ * WordPress */ import { __ } from '@wordpress/i18n'; -import { - createBlock, - RichText, -} from '@wordpress/blocks'; +import { createBlock } from '@wordpress/blocks'; +import { RichText } from '@wordpress/editor'; /** * Internal dependencies @@ -17,7 +15,7 @@ export const name = 'core/verse'; export const settings = { title: __( 'Verse' ), - description: __( 'Write poetry and other literary expressions honoring all spaces and line-breaks.' ), + description: __( 'A block for haiku? Why not? Blocks for all the things! (See what we did here?)' ), icon: 'edit', diff --git a/core-blocks/verse/test/__snapshots__/index.js.snap b/core-blocks/verse/test/__snapshots__/index.js.snap index 7e514a4fb36fd..b33b596d591bc 100644 --- a/core-blocks/verse/test/__snapshots__/index.js.snap +++ b/core-blocks/verse/test/__snapshots__/index.js.snap @@ -2,7 +2,7 @@ exports[`core/verse block edit matches snapshot 1`] = ` <div - class="wp-block-verse blocks-rich-text" + class="wp-block-verse editor-rich-text" > <div> <div> @@ -14,13 +14,13 @@ exports[`core/verse block edit matches snapshot 1`] = ` aria-expanded="false" aria-label="Write…" aria-multiline="false" - class="blocks-rich-text__tinymce" + class="editor-rich-text__tinymce" contenteditable="true" data-is-placeholder-visible="true" role="textbox" /> <pre - class="blocks-rich-text__tinymce" + class="editor-rich-text__tinymce" > Write… </pre> diff --git a/core-blocks/video/index.js b/core-blocks/video/index.js index ce88df821c538..b0e1fb20d6e56 100644 --- a/core-blocks/video/index.js +++ b/core-blocks/video/index.js @@ -14,13 +14,13 @@ import { Toolbar, } from '@wordpress/components'; import { Component, Fragment } from '@wordpress/element'; +import { editorMediaUpload } from '@wordpress/blocks'; import { - editorMediaUpload, BlockAlignmentToolbar, BlockControls, MediaUpload, RichText, -} from '@wordpress/blocks'; +} from '@wordpress/editor'; /** * Internal dependencies @@ -33,7 +33,7 @@ export const name = 'core/video'; export const settings = { title: __( 'Video' ), - description: __( 'The Video block allows you to embed video files and play them back using a simple player.' ), + description: __( 'Embed an video file and a simple video player.' ), icon: 'format-video', diff --git a/core-data/actions.js b/core-data/actions.js index a0b91f1511c0b..5d82b062f0deb 100644 --- a/core-data/actions.js +++ b/core-data/actions.js @@ -38,30 +38,36 @@ export function receiveTerms( taxonomy, terms ) { } /** - * Returns an action object used in signalling that media have been received. + * Returns an action object used in signalling that authors have been received. * - * @param {Array|Object} media Media received. + * @param {string} queryID Query ID. + * @param {Array|Object} users Users received. * * @return {Object} Action object. */ -export function receiveMedia( media ) { +export function receiveUserQuery( queryID, users ) { return { - type: 'RECEIVE_MEDIA', - media: castArray( media ), + type: 'RECEIVE_USER_QUERY', + users: castArray( users ), + queryID, }; } /** - * Returns an action object used in signalling that post types have been received. + * Returns an action object used in signalling that entity records have been received. * - * @param {Array|Object} postTypes Post Types received. + * @param {string} kind Kind of the received entity. + * @param {string} name Name of the received entity. + * @param {Array|Object} records Recordds received. * * @return {Object} Action object. */ -export function receivePostTypes( postTypes ) { +export function receiveEntityRecords( kind, name, records ) { return { - type: 'RECEIVE_POST_TYPES', - postTypes: castArray( postTypes ), + type: 'RECEIVE_ENTITY_RECORDS', + records: castArray( records ), + kind, + name, }; } diff --git a/core-data/entities.js b/core-data/entities.js new file mode 100644 index 0000000000000..7b361a358c128 --- /dev/null +++ b/core-data/entities.js @@ -0,0 +1,37 @@ +/** + * External dependencies + */ +import { find, upperFirst, camelCase } from 'lodash'; + +const entities = [ + { name: 'postType', kind: 'root', key: 'slug', baseUrl: '/wp/v2/types' }, + { name: 'media', kind: 'root', baseUrl: '/wp/v2/media' }, +]; + +/** + * Returns the entity object given its kind and name. + * + * @param {string} kind Entity kind. + * @param {string} name Entity name. + * + * @return {Object} Entity + */ +export function getEntity( kind, name ) { + return find( entities, { kind, name } ); +} + +/** + * Returns the entity's getter method name given its kind and name. + * + * @param {string} kind Entity kind. + * @param {string} name Entity name. + * + * @return {string} Method name + */ +export const getMethodName = ( kind, name ) => { + const kindPrefix = kind === 'root' ? '' : upperFirst( camelCase( kind ) ); + const nameSuffix = upperFirst( camelCase( name ) ); + return `get${ kindPrefix }${ nameSuffix }`; +}; + +export default entities; diff --git a/core-data/index.js b/core-data/index.js index 1b82d772a4343..195e0db4e6efd 100644 --- a/core-data/index.js +++ b/core-data/index.js @@ -1,5 +1,5 @@ /** - * WordPress Dependencies + * WordPress dependencies */ import { registerStore } from '@wordpress/data'; @@ -10,12 +10,23 @@ import reducer from './reducer'; import * as selectors from './selectors'; import * as actions from './actions'; import * as resolvers from './resolvers'; +import { default as entities, getMethodName } from './entities'; + +const createEntityRecordGetter = ( source ) => entities.reduce( ( result, entity ) => { + const { kind, name } = entity; + const methodName = getMethodName( kind, name ); + result[ methodName ] = ( state, key ) => source.getEntityRecord( state, kind, name, key ); + return result; +}, {} ); + +const entityResolvers = createEntityRecordGetter( resolvers ); +const entitySelectors = createEntityRecordGetter( selectors ); const store = registerStore( 'core', { reducer, actions, - selectors, - resolvers, + selectors: { ...selectors, ...entitySelectors }, + resolvers: { ...resolvers, ...entityResolvers }, } ); export default store; diff --git a/core-data/reducer.js b/core-data/reducer.js index 32dc6ee3a2f13..47f5e09c3d15b 100644 --- a/core-data/reducer.js +++ b/core-data/reducer.js @@ -1,13 +1,18 @@ /** * External dependencies */ -import { keyBy } from 'lodash'; +import { keyBy, map, groupBy } from 'lodash'; /** * WordPress dependencies */ import { combineReducers } from '@wordpress/data'; +/** + * Internal dependencies + */ +import entitiesConfig from './entities'; + /** * Reducer managing terms state. Keyed by taxonomy slug, the value is either * undefined (if no request has been made for given taxonomy), null (if a @@ -43,19 +48,25 @@ export function terms( state = {}, action ) { } /** - * Reducer managing media state. Keyed by id. + * Reducer managing authors state. Keyed by id. * * @param {Object} state Current state. * @param {Object} action Dispatched action. * * @return {Object} Updated state. */ -export function media( state = {}, action ) { +export function users( state = { byId: {}, queries: {} }, action ) { switch ( action.type ) { - case 'RECEIVE_MEDIA': + case 'RECEIVE_USER_QUERY': return { - ...state, - ...keyBy( action.media, 'id' ), + byId: { + ...state.byId, + ...keyBy( action.users, 'id' ), + }, + queries: { + ...state.queries, + [ action.queryID ]: map( action.users, ( user ) => user.id ), + }, }; } @@ -63,19 +74,19 @@ export function media( state = {}, action ) { } /** - * Reducer managing post types state. Keyed by slug. + * Reducer managing theme supports data. * * @param {Object} state Current state. * @param {Object} action Dispatched action. * * @return {Object} Updated state. */ -export function postTypes( state = {}, action ) { +export function themeSupports( state = {}, action ) { switch ( action.type ) { - case 'RECEIVE_POST_TYPES': + case 'RECEIVE_THEME_SUPPORTS': return { ...state, - ...keyBy( action.postTypes, 'slug' ), + ...action.themeSupports, }; } @@ -83,28 +94,58 @@ export function postTypes( state = {}, action ) { } /** - * Reducer managing theme supports data. + * Higher Order Reducer for a given entity config. It supports: * - * @param {Object} state Current state. - * @param {Object} action Dispatched action. + * - Fetching a record by primariy key * - * @return {Object} Updated state. + * @param {Object} entityConfig Entity config. + * + * @return {Function} Reducer. */ -export function themeSupports( state = {}, action ) { - switch ( action.type ) { - case 'RECEIVE_THEME_SUPPORTS': - return { - ...state, - ...action.themeSupports, - }; - } +function entity( entityConfig ) { + const key = entityConfig.key || 'id'; - return state; + return ( state = { byKey: {} }, action ) => { + if ( + ! action.name || + ! action.kind || + action.name !== entityConfig.name || + action.kind !== entityConfig.kind + ) { + return state; + } + + switch ( action.type ) { + case 'RECEIVE_ENTITY_RECORDS': + return { + byKey: { + ...state.key, + ...keyBy( action.records, key ), + }, + }; + default: + return state; + } + }; } +const entitiesByKind = groupBy( entitiesConfig, 'kind' ); +export const entities = combineReducers( Object.entries( entitiesByKind ).reduce( ( memo, [ kind, subEntities ] ) => { + const kindReducer = combineReducers( subEntities.reduce( + ( kindMemo, entityConfig ) => ( { + ...kindMemo, + [ entityConfig.name ]: entity( entityConfig ), + } ), + {} + ) ); + + memo[ kind ] = kindReducer; + return memo; +}, {} ) ); + export default combineReducers( { terms, - media, - postTypes, + users, themeSupports, + entities, } ); diff --git a/core-data/resolvers.js b/core-data/resolvers.js index 642d557ad5956..071e4ce951846 100644 --- a/core-data/resolvers.js +++ b/core-data/resolvers.js @@ -9,10 +9,11 @@ import apiRequest from '@wordpress/api-request'; import { setRequested, receiveTerms, - receiveMedia, - receivePostTypes, + receiveUserQuery, + receiveEntityRecords, receiveThemeSupportsFromIndex, } from './actions'; +import { getEntity } from './entities'; /** * Requests categories from the REST API, yielding action objects on request @@ -25,25 +26,25 @@ export async function* getCategories() { } /** - * Requests a media element from the REST API. - * - * @param {Object} state State tree - * @param {number} id Media id + * Requests authors from the REST API. */ -export async function* getMedia( state, id ) { - const media = await apiRequest( { path: `/wp/v2/media/${ id }` } ); - yield receiveMedia( media ); +export async function* getAuthors() { + const users = await apiRequest( { path: '/wp/v2/users/?who=authors' } ); + yield receiveUserQuery( 'authors', users ); } /** - * Requests a post type element from the REST API. + * Requests a entity's record from the REST API. * - * @param {Object} state State tree - * @param {number} slug Post Type slug + * @param {Object} state State tree + * @param {string} kind Entity kind. + * @param {string} name Entity name. + * @param {number} key Record's key */ -export async function* getPostType( state, slug ) { - const postType = await apiRequest( { path: `/wp/v2/types/${ slug }?context=edit` } ); - yield receivePostTypes( postType ); +export async function* getEntityRecord( state, kind, name, key ) { + const entity = getEntity( kind, name ); + const record = await apiRequest( { path: `${ entity.baseUrl }/${ key }?context=edit` } ); + yield receiveEntityRecords( kind, name, record ); } /** diff --git a/core-data/selectors.js b/core-data/selectors.js index da0a7aa18797f..16ee74ac292de 100644 --- a/core-data/selectors.js +++ b/core-data/selectors.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import { map } from 'lodash'; + /** * Returns all the available terms for the given taxonomy. * @@ -47,27 +52,42 @@ export function isRequestingCategories( state ) { } /** - * Returns the media object by id. + * Returns all available authors. * * @param {Object} state Data state. - * @param {number} id Media id. * - * @return {Object?} Media object. + * @return {Array} Authors list. */ -export function getMedia( state, id ) { - return state.media[ id ]; +export function getAuthors( state ) { + return getUserQueryResults( state, 'authors' ); } /** - * Returns the Post Type object by slug. + * Returns all the users returned by a query ID. * - * @param {Object} state Data state. - * @param {number} slug Post Type slug. + * @param {Object} state Data state. + * @param {string} queryID Query ID. + * + * @return {Array} Users list. + */ +export function getUserQueryResults( state, queryID ) { + const queryResults = state.users.queries[ queryID ]; + + return map( queryResults, ( id ) => state.users.byId[ id ] ); +} + +/** + * Returns the Entity's record object by key. + * + * @param {Object} state State tree + * @param {string} kind Entity kind. + * @param {string} name Entity name. + * @param {number} key Record's key * - * @return {Object?} Post Type object. + * @return {Object?} Record. */ -export function getPostType( state, slug ) { - return state.postTypes[ slug ]; +export function getEntityRecord( state, kind, name, key ) { + return state.entities[ kind ][ name ].byKey[ key ]; } /** diff --git a/core-data/test/entities.js b/core-data/test/entities.js new file mode 100644 index 0000000000000..54dba21440ce1 --- /dev/null +++ b/core-data/test/entities.js @@ -0,0 +1,18 @@ +/** + * Internal dependencies + */ +import { getMethodName } from '../entities'; + +describe( 'getMethodName', () => { + it( 'Should return the right method name for an entity with the root kind', () => { + const methodName = getMethodName( 'root', 'postType' ); + + expect( methodName ).toEqual( 'getPostType' ); + } ); + + it( 'Should include the kind in the method name', () => { + const methodName = getMethodName( 'postType', 'book' ); + + expect( methodName ).toEqual( 'getPostTypeBook' ); + } ); +} ); diff --git a/core-data/test/reducer.js b/core-data/test/reducer.js index 524926579ad25..f38eaa42640fc 100644 --- a/core-data/test/reducer.js +++ b/core-data/test/reducer.js @@ -6,7 +6,7 @@ import deepFreeze from 'deep-freeze'; /** * Internal dependencies */ -import { terms, media, postTypes } from '../reducer'; +import { terms, entities } from '../reducer'; describe( 'terms()', () => { it( 'returns an empty object by default', () => { @@ -68,44 +68,27 @@ describe( 'terms()', () => { } ); } ); -describe( 'media', () => { - it( 'returns an empty object by default', () => { - const state = media( undefined, {} ); - - expect( state ).toEqual( {} ); - } ); - - it( 'returns with received media by id', () => { - const originalState = deepFreeze( {} ); - const state = media( originalState, { - type: 'RECEIVE_MEDIA', - media: [ { id: 1, title: 'beach' }, { id: 2, title: 'sun' } ], - } ); - - expect( state ).toEqual( { - 1: { id: 1, title: 'beach' }, - 2: { id: 2, title: 'sun' }, - } ); - } ); -} ); +describe( 'entities', () => { + it( 'returns the default state for all defined entities', () => { + const state = entities( undefined, {} ); -describe( 'postTypes', () => { - it( 'returns an empty object by default', () => { - const state = postTypes( undefined, {} ); - - expect( state ).toEqual( {} ); + expect( state.root.postType ).toEqual( { byKey: {} } ); } ); it( 'returns with received post types by slug', () => { const originalState = deepFreeze( {} ); - const state = postTypes( originalState, { - type: 'RECEIVE_POST_TYPES', - postTypes: [ { slug: 'b', title: 'beach' }, { slug: 's', title: 'sun' } ], + const state = entities( originalState, { + type: 'RECEIVE_ENTITY_RECORDS', + records: [ { slug: 'b', title: 'beach' }, { slug: 's', title: 'sun' } ], + kind: 'root', + name: 'postType', } ); - expect( state ).toEqual( { - b: { slug: 'b', title: 'beach' }, - s: { slug: 's', title: 'sun' }, + expect( state.root.postType ).toEqual( { + byKey: { + b: { slug: 'b', title: 'beach' }, + s: { slug: 's', title: 'sun' }, + }, } ); } ); } ); diff --git a/core-data/test/resolvers.js b/core-data/test/resolvers.js index 435fe2443c30b..8a88aba9610a5 100644 --- a/core-data/test/resolvers.js +++ b/core-data/test/resolvers.js @@ -6,8 +6,8 @@ import apiRequest from '@wordpress/api-request'; /** * Internal dependencies */ -import { getCategories, getMedia, getPostType } from '../resolvers'; -import { setRequested, receiveTerms, receiveMedia, receivePostTypes } from '../actions'; +import { getCategories, getEntityRecord } from '../resolvers'; +import { setRequested, receiveTerms, receiveEntityRecords } from '../actions'; jest.mock( '@wordpress/api-request' ); @@ -31,25 +31,7 @@ describe( 'getCategories', () => { } ); } ); -describe( 'getMedia', () => { - const MEDIA = { id: 1 }; - - beforeAll( () => { - apiRequest.mockImplementation( ( options ) => { - if ( options.path === '/wp/v2/media/1' ) { - return Promise.resolve( MEDIA ); - } - } ); - } ); - - it( 'yields with requested media', async () => { - const fulfillment = getMedia( {}, 1 ); - const received = ( await fulfillment.next() ).value; - expect( received ).toEqual( receiveMedia( MEDIA ) ); - } ); -} ); - -describe( 'getPostType', () => { +describe( 'getEntityRecord', () => { const POST_TYPE = { slug: 'post' }; beforeAll( () => { @@ -61,8 +43,8 @@ describe( 'getPostType', () => { } ); it( 'yields with requested post type', async () => { - const fulfillment = getPostType( {}, 'post' ); + const fulfillment = getEntityRecord( {}, 'root', 'postType', 'post' ); const received = ( await fulfillment.next() ).value; - expect( received ).toEqual( receivePostTypes( POST_TYPE ) ); + expect( received ).toEqual( receiveEntityRecords( 'root', 'postType', POST_TYPE ) ); } ); } ); diff --git a/core-data/test/selectors.js b/core-data/test/selectors.js index 6b069fe5d0a13..12ab66bcea356 100644 --- a/core-data/test/selectors.js +++ b/core-data/test/selectors.js @@ -6,7 +6,7 @@ import deepFreeze from 'deep-freeze'; /** * Internal dependencies */ -import { getTerms, isRequestingTerms, getMedia, getPostType } from '../selectors'; +import { getTerms, isRequestingTerms, getEntityRecord } from '../selectors'; describe( 'getTerms()', () => { it( 'returns value of terms by taxonomy', () => { @@ -57,38 +57,32 @@ describe( 'isRequestingTerms()', () => { } ); } ); -describe( 'getMedia', () => { - it( 'should return undefined for unknown media', () => { +describe( 'getEntityRecord', () => { + it( 'should return undefined for unknown record\'s key', () => { const state = deepFreeze( { - media: {}, - } ); - expect( getMedia( state, 1 ) ).toBe( undefined ); - } ); - - it( 'should return a media element by id', () => { - const state = deepFreeze( { - media: { - 1: { id: 1 }, + entities: { + root: { + postType: { + byKey: {}, + }, + }, }, } ); - expect( getMedia( state, 1 ) ).toEqual( { id: 1 } ); - } ); -} ); - -describe( 'getPostType', () => { - it( 'should return undefined for unknown post type', () => { - const state = deepFreeze( { - postTypes: {}, - } ); - expect( getPostType( state, 'post' ) ).toBe( undefined ); + expect( getEntityRecord( state, 'root', 'postType', 'post' ) ).toBe( undefined ); } ); - it( 'should return a post type by slug', () => { + it( 'should return a record by key', () => { const state = deepFreeze( { - postTypes: { - post: { slug: 'post' }, + entities: { + root: { + postType: { + byKey: { + post: { slug: 'post' }, + }, + }, + }, }, } ); - expect( getPostType( state, 'post' ) ).toEqual( { slug: 'post' } ); + expect( getEntityRecord( state, 'root', 'postType', 'post' ) ).toEqual( { slug: 'post' } ); } ); } ); diff --git a/data/index.js b/data/index.js index 61cce6b182fb5..eaafa51cd4d3c 100644 --- a/data/index.js +++ b/data/index.js @@ -1,7 +1,6 @@ /** * External dependencies */ -import isShallowEqual from 'shallowequal'; import { combineReducers, createStore } from 'redux'; import { flowRight, without, mapValues, overEvery } from 'lodash'; import EquivalentKeyMap from 'equivalent-key-map'; @@ -9,7 +8,8 @@ import EquivalentKeyMap from 'equivalent-key-map'; /** * WordPress dependencies */ -import { Component, createHigherOrderComponent } from '@wordpress/element'; +import { Component, createHigherOrderComponent, pure, compose } from '@wordpress/element'; +import isShallowEqual from '@wordpress/is-shallow-equal'; /** * Internal dependencies @@ -285,11 +285,19 @@ export const withSelect = ( mapStateToProps ) => createHigherOrderComponent( ( W this.runSelection = this.runSelection.bind( this ); + /** + * Boolean tracking known render conditions (own props or merged + * props update) for `shouldComponentUpdate`. + * + * @type {boolean} + */ + this.shouldComponentUpdate = false; + this.state = {}; } - shouldComponentUpdate( nextProps, nextState ) { - return ! isShallowEqual( nextProps, this.props ) || ! isShallowEqual( nextState, this.state ); + shouldComponentUpdate() { + return this.shouldComponentUpdate; } componentWillMount() { @@ -302,6 +310,7 @@ export const withSelect = ( mapStateToProps ) => createHigherOrderComponent( ( W componentWillReceiveProps( nextProps ) { if ( ! isShallowEqual( nextProps, this.props ) ) { this.runSelection( nextProps ); + this.shouldComponentUpdate = true; } } @@ -331,10 +340,14 @@ export const withSelect = ( mapStateToProps ) => createHigherOrderComponent( ( W this.setState( { mergeProps: nextMergeProps, } ); + + this.shouldComponentUpdate = true; } } render() { + this.shouldComponentUpdate = false; + return <WrappedComponent { ...this.props } { ...this.state.mergeProps } />; } }; @@ -352,48 +365,54 @@ export const withSelect = ( mapStateToProps ) => createHigherOrderComponent( ( W * * @return {Component} Enhanced component with merged dispatcher props. */ -export const withDispatch = ( mapDispatchToProps ) => createHigherOrderComponent( ( WrappedComponent ) => { - return class ComponentWithDispatch extends Component { - constructor() { - super( ...arguments ); - - this.proxyProps = {}; - } - - componentWillMount() { - this.setProxyProps( this.props ); - } +export const withDispatch = ( mapDispatchToProps ) => createHigherOrderComponent( + compose( [ + pure, + ( WrappedComponent ) => { + return class ComponentWithDispatch extends Component { + constructor() { + super( ...arguments ); + + this.proxyProps = {}; + } - componentWillUpdate( nextProps ) { - this.setProxyProps( nextProps ); - } + componentWillMount() { + this.setProxyProps( this.props ); + } - proxyDispatch( propName, ...args ) { - // Original dispatcher is a pre-bound (dispatching) action creator. - mapDispatchToProps( dispatch, this.props )[ propName ]( ...args ); - } + componentWillUpdate( nextProps ) { + this.setProxyProps( nextProps ); + } - setProxyProps( props ) { - // Assign as instance property so that in reconciling subsequent - // renders, the assigned prop values are referentially equal. - const propsToDispatchers = mapDispatchToProps( dispatch, props ); - this.proxyProps = mapValues( propsToDispatchers, ( dispatcher, propName ) => { - // Prebind with prop name so we have reference to the original - // dispatcher to invoke. Track between re-renders to avoid - // creating new function references every render. - if ( this.proxyProps.hasOwnProperty( propName ) ) { - return this.proxyProps[ propName ]; + proxyDispatch( propName, ...args ) { + // Original dispatcher is a pre-bound (dispatching) action creator. + mapDispatchToProps( dispatch, this.props )[ propName ]( ...args ); } - return this.proxyDispatch.bind( this, propName ); - } ); - } + setProxyProps( props ) { + // Assign as instance property so that in reconciling subsequent + // renders, the assigned prop values are referentially equal. + const propsToDispatchers = mapDispatchToProps( dispatch, props ); + this.proxyProps = mapValues( propsToDispatchers, ( dispatcher, propName ) => { + // Prebind with prop name so we have reference to the original + // dispatcher to invoke. Track between re-renders to avoid + // creating new function references every render. + if ( this.proxyProps.hasOwnProperty( propName ) ) { + return this.proxyProps[ propName ]; + } + + return this.proxyDispatch.bind( this, propName ); + } ); + } - render() { - return <WrappedComponent { ...this.props } { ...this.proxyProps } />; - } - }; -}, 'withDispatch' ); + render() { + return <WrappedComponent { ...this.props } { ...this.proxyProps } />; + } + }; + }, + ] ), + 'withDispatch' +); /** * Returns true if the given argument appears to be a dispatchable action. diff --git a/docs/blocks/block-controls-toolbars-and-inspector.md b/docs/blocks/block-controls-toolbars-and-inspector.md index af1eca93c115a..2ddc50c2e542f 100644 --- a/docs/blocks/block-controls-toolbars-and-inspector.md +++ b/docs/blocks/block-controls-toolbars-and-inspector.md @@ -1,6 +1,6 @@ # Block Controls: Toolbars and Inspector -To simplify block customization and ensure a consistent experience for users, there are a number of built-in UI patterns to help generate the editor preview. Like with the `RichText` component covered in the previous chapter, the `wp.blocks` global includes a few other common components to render editing interfaces. In this chapter, we'll explore toolbars and the block inspector. +To simplify block customization and ensure a consistent experience for users, there are a number of built-in UI patterns to help generate the editor preview. Like with the `RichText` component covered in the previous chapter, the `wp.editor` global includes a few other common components to render editing interfaces. In this chapter, we'll explore toolbars and the block inspector. ## Toolbar @@ -15,9 +15,9 @@ You can also customize the toolbar to include controls specific to your block ty ```js var el = wp.element.createElement, registerBlockType = wp.blocks.registerBlockType, - RichText = wp.blocks.RichText, - BlockControls = wp.blocks.BlockControls, - AlignmentToolbar = wp.blocks.AlignmentToolbar; + RichText = wp.editor.RichText, + BlockControls = wp.editor.BlockControls, + AlignmentToolbar = wp.editor.AlignmentToolbar; registerBlockType( 'gutenberg-boilerplate-es5/hello-world-step-04', { title: 'Hello World (Step 4)', @@ -89,13 +89,12 @@ registerBlockType( 'gutenberg-boilerplate-es5/hello-world-step-04', { ``` {% ESNext %} ```js +const { registerBlockType } = wp.blocks; const { - registerBlockType, RichText, BlockControls, AlignmentToolbar, - source -} = wp.blocks; +} = wp.editor; registerBlockType( 'gutenberg-boilerplate-esnext/hello-world-step-04', { title: 'Hello World (Step 4)', diff --git a/docs/blocks/introducing-attributes-and-editable-fields.md b/docs/blocks/introducing-attributes-and-editable-fields.md index ca893b9fcab80..509593527acdc 100644 --- a/docs/blocks/introducing-attributes-and-editable-fields.md +++ b/docs/blocks/introducing-attributes-and-editable-fields.md @@ -13,7 +13,7 @@ One challenge of maintaining the representation of a block as a JavaScript objec ```js var el = wp.element.createElement, registerBlockType = wp.blocks.registerBlockType, - RichText = wp.blocks.RichText; + RichText = wp.editor.RichText; registerBlockType( 'gutenberg-boilerplate-es5/hello-world-step-03', { title: 'Hello World (Step 3)', @@ -60,7 +60,8 @@ registerBlockType( 'gutenberg-boilerplate-es5/hello-world-step-03', { ``` {% ESNext %} ```js -const { registerBlockType, RichText, source } = wp.blocks; +const { registerBlockType } = wp.blocks; +const { RichText } = wp.editor; registerBlockType( 'gutenberg-boilerplate-esnext/hello-world-step-03', { title: 'Hello World (Step 3)', diff --git a/docs/extensibility/autocomplete.md b/docs/extensibility/autocomplete.md index 8549ac1995ec3..914c35af5c229 100644 --- a/docs/extensibility/autocomplete.md +++ b/docs/extensibility/autocomplete.md @@ -3,7 +3,7 @@ Autocomplete Gutenberg provides a `blocks.Autocomplete.completers` filter for extending and overriding the list of autocompleters used by blocks. -The `Autocomplete` component found in `@wordpress/blocks` applies this filter. The `@wordpress/components` package provides the foundational `Autocomplete` component that does not apply such a filter, but blocks should generally use the component provided by `@wordpress/blocks`. +The `Autocomplete` component found in `@wordpress/editor` applies this filter. The `@wordpress/components` package provides the foundational `Autocomplete` component that does not apply such a filter, but blocks should generally use the component provided by `@wordpress/editor`. ### Example @@ -38,8 +38,10 @@ var acronymCompleter = { }; // Our filter function -function appendAcronymCompleter( completers ) { - return completers.concat( acronymCompleter ); +function appendAcronymCompleter( completers, blockName ) { + return blockName === 'my-plugin/foo' ? + completers.concat( acronymCompleter ) : + completers; } // Adding the filter @@ -71,8 +73,10 @@ const acronymCompleter = { }; // Our filter function -function appendAcronymCompleter( completers ) { - return [ ...completers, acronymCompleter ]; +function appendAcronymCompleter( completers, blockName ) { + return blockName === 'my-plugin/foo' ? + [ ...completers, acronymCompleter ] : + completers; } // Adding the filter diff --git a/docs/extensibility/theme-support.md b/docs/extensibility/theme-support.md index 07d8423c9569f..9f4175bce222e 100644 --- a/docs/extensibility/theme-support.md +++ b/docs/extensibility/theme-support.md @@ -8,7 +8,7 @@ To opt-in for one of these features, call `add_theme_support` in the `functions. ```php function mytheme_setup_theme_supported_features() { - add_theme_support( 'editor-color-palette', + add_theme_support( 'editor-color-palette', array( array( 'name' => 'strong magenta', 'color' => '#a156b4', @@ -25,7 +25,7 @@ function mytheme_setup_theme_supported_features() { 'name' => 'very dark gray', 'color' => '#444', ) - ); + ) ); } add_action( 'after_setup_theme', 'mytheme_setup_theme_supported_features' ); @@ -46,7 +46,7 @@ add_theme_support( 'align-wide' ); Different blocks have the possibility of customizing colors. Gutenberg provides a default palette, but a theme can overwrite it and provide its own: ```php -add_theme_support( 'editor-color-palette', +add_theme_support( 'editor-color-palette', array( array( 'name' => 'strong magenta', 'color' => '#a156b4', @@ -63,7 +63,7 @@ add_theme_support( 'editor-color-palette', 'name' => 'very dark gray', 'color' => '#444', ) -); +) ); ``` The colors will be shown in order on the palette, and there's no limit to how many can be specified. @@ -91,3 +91,63 @@ add_theme_support( 'disable-custom-colors' ); ``` This flag will make sure users are only able to choose colors from the `editor-color-palette` the theme provided or from the editor default colors if the theme did not provide one. + +## Editor styles + +A theme can provide a stylesheet that will change the editor's appearance. You can use this to change colors, fonts, and any other visual aspect of the editor. + +### Add the stylesheet + +The first thing to do is to create a new stylesheet file in your theme directory. We'll assume the file is named `style-editor.css`. + +Next, load your newly-created editor stylesheet in your theme: + +```php +/** + * Enqueue block editor style + */ +function mytheme_block_editor_styles() { + wp_enqueue_style( 'mytheme-block-editor-styles', get_theme_file_uri( '/style-editor.css' ), false, '1.0', 'all' ); +} + +add_action( 'enqueue_block_editor_assets', 'mytheme_block_editor_styles' ); +``` + +### Basic colors + +You can style the editor like any other webpage. Here's how to change the background color and the font color to blue: + +```css +/* Add this to your `style-editor.css` file */ +body.gutenberg-editor-page { + background-color: #d3ebf3; + color: #00005d; +} +``` + +### Changing the width of the editor + +To change the main column width of the editor, add the following CSS to `style-editor.css`: + +```css +/* Main column width */ +body.gutenberg-editor-page .editor-post-title, +body.gutenberg-editor-page .editor-default-block-appender, +body.gutenberg-editor-page .editor-block-list__block { + max-width: 720px; +} + +/* Width of "wide" blocks */ +body.gutenberg-editor-page .editor-block-list__block[data-align="wide"] { + max-width: 1080px; +} + +/* Width of "full-wide" blocks */ +body.gutenberg-editor-page .editor-block-list__block[data-align="full"] { + max-width: none; +} +``` + +You can use those editor widths to match those in your theme. You can use any CSS width unit, including `%` or `px`. + +Further reading: [Applying Styles with Stylesheets](https://wordpress.org/gutenberg/handbook/blocks/applying-styles-with-stylesheets/). diff --git a/docs/reference/deprecated.md b/docs/reference/deprecated.md index f270eb5ea7b4f..06bb8ee695fce 100644 --- a/docs/reference/deprecated.md +++ b/docs/reference/deprecated.md @@ -1,8 +1,13 @@ Gutenberg's deprecation policy is intended to support backwards-compatibility for two minor releases, when possible. The current deprecations are listed below and are grouped by _the version at which they will be removed completely_. If your plugin depends on these behaviors, you must update to the recommended alternative before the noted version. +## 3.1.0 + + - All components in `wp.blocks.*` are removed. Please use `wp.editor.*` instead. + ## 3.0.0 - `wp.blocks.registerCoreBlocks` function removed. Please use `wp.coreBlocks.registerCoreBlocks` instead. +- Raw TinyMCE event handlers for `RichText` have been deprecated. Please use [documented props](https://wordpress.org/gutenberg/handbook/block-api/rich-text-api/), ancestor event handler, or onSetup access to the internal editor instance event hub instead. ## 2.8.0 diff --git a/edit-post/README.md b/edit-post/README.md index fc069f7482c37..fdfac66c203cc 100644 --- a/edit-post/README.md +++ b/edit-post/README.md @@ -21,6 +21,7 @@ wp.data.dispatch( 'core/edit-post' ).openGeneralSidebar( 'plugin-name/sidebar-na _Example:_ ```jsx +const { __ } = wp.i18n; const { PanelBody } = wp.components; const { PluginSidebar } = wp.editPost; @@ -30,7 +31,7 @@ const MyPluginSidebar = () => ( title="My sidebar title" > <PanelBody> - My sidebar content + { __( 'My sidebar content' ) } </PanelBody> </PluginSidebar> ); @@ -61,6 +62,7 @@ The text within the component appears as the menu item label. _Example:_ ```jsx +const { __ } = wp.i18n; const { PluginSidebarMoreMenuItem } = wp.editPost; const MySidebarMoreMenuItem = () => ( @@ -68,7 +70,7 @@ const MySidebarMoreMenuItem = () => ( target="my-sidebar" icon="yes" > - My sidebar title + { __( 'My sidebar title' ) } </PluginSidebarMoreMenuItem> ); ``` @@ -90,3 +92,21 @@ The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug st - Required: No +### `PluginPostStatusInfo` + +Renders a row in the Status & Visibility panel of the Document sidebar. +It should be noted that this is named and implemented around the function it serves and not its location, which may change in future iterations. + +_Example:_ +```jsx +const { __ } = wp.i18n; +const { PluginPostStatusInfo } = wp.editPost; + +const MyPluginPostStatusInfo = () => ( + <PluginPostStatusInfo> + { __( 'My post status info' ) } + </PluginPostStatusInfo> +); +``` + + diff --git a/edit-post/assets/stylesheets/_admin-schemes.scss b/edit-post/assets/stylesheets/_admin-schemes.scss index f1025fb68f051..7c440d96d6da4 100644 --- a/edit-post/assets/stylesheets/_admin-schemes.scss +++ b/edit-post/assets/stylesheets/_admin-schemes.scss @@ -2,32 +2,53 @@ * Admin Color Scheme Overrides */ -// All color schemes, body.admin-color ... +// List of spot colors +// - Spot color 1 is used for most decorative elements, eyedropped from the profile color swatches +// - Spot color 2 is use for notifications and switches, this color as such can be overlaid on spot color 1, eye dropped from the Primary button +// - Spot color 2 is not often used in Gutenberg, but can be used when spot color 1 is not a good choice for the element to be recolored, like the red switch in Midnight + // -fresh (default, needs no overriding) -// -light (needs no overriding) -// -blue -// -coffee -// -ectoplasm -// -midnight -// -ocean -// -sunrise - -// List of Gutenberg elements that need to be overridden -// - primary buttons (parent bleeds in) -// - switches -// - tab indicators - -// List of spot colors (eyedropped from the primary button) -$scheme-fresh__spot-color: #0083b8; -$scheme-blue__spot-color: #e1a84b; -$scheme-coffee__spot-color: #c6a488; -$scheme-ectoplasm__spot-color: #a0b748; +$scheme-fresh__spot-color: $blue-medium-500; +$scheme-fresh__spot-color-2: $blue-wordpress; + +// .admin-color-light +$scheme-light__spot-color: $blue-medium-500; +$scheme-light__spot-color-2: #c75726; + +// .admin-color-blue +$scheme-blue__spot-color: #82b4cb; +$scheme-blue__spot-color-2: #d9ab59; + +// .admin-color-coffee +$scheme-coffee__spot-color: #c2a68c; +$scheme-coffee__spot-color-2: #9fa47b; + +// .admin-color-ectoplasm +$scheme-ectoplasm__spot-color: #a7b656; +$scheme-ectoplasm__spot-color-2: #c77430; + +// .admin-color-midnight $scheme-midnight__spot-color: #e34e46; -$scheme-ocean__spot-color: #9bb99f; -$scheme-sunrise__spot-color: #de823f; +$scheme-midnight__spot-color-2: #77a6b9; + +// .admin-color-ocean +$scheme-ocean__spot-color: #a3b9a2; +$scheme-ocean__spot-color-2: #a89d8a; + +// .admin-color-sunrise +$scheme-sunrise__spot-color: #d1864a; +$scheme-sunrise__spot-color-2: #c8b03c; + + +// Apply those colors to these Gutenberg elements +// - primary buttons (spot color) +// - tab indicators (spot color) +// - switches (spot color 2) + + // Color override mixin -@mixin admin-scheme-color-overrides( $scheme: fresh, $spot-color: $scheme-fresh__spot-color ) { +@mixin admin-scheme-color-overrides( $scheme: fresh, $spot-color: $scheme-fresh__spot-color, $spot-color-2: $scheme-fresh__spot-color-2 ) { body.admin-color-#{ ( $scheme ) } { // Tab indicators .edit-post-sidebar__panel-tab.is-active, @@ -38,12 +59,12 @@ $scheme-sunrise__spot-color: #de823f; // Switch .components-form-toggle.is-checked { .components-form-toggle__track { - background-color: $spot-color; - border-color: $spot-color; + background-color: $spot-color-2; + border-color: $spot-color-2; } &:before { - background-color: $spot-color; + background-color: $spot-color-2; } } @@ -66,8 +87,8 @@ $scheme-sunrise__spot-color: #de823f; } // URL suggestions - .blocks-url-input__suggestion:focus, - .blocks-url-input__suggestion.is-selected { + .editor-url-input__suggestion:focus, + .editor-url-input__suggestion.is-selected { background: darken( $spot-color, 15 ); } } diff --git a/edit-post/assets/stylesheets/_colors.scss b/edit-post/assets/stylesheets/_colors.scss index fa3ed999d248f..9f21b513edafa 100644 --- a/edit-post/assets/stylesheets/_colors.scss +++ b/edit-post/assets/stylesheets/_colors.scss @@ -27,8 +27,8 @@ $white: #fff; // Additional colors // some from https://make.wordpress.org/design/handbook/foundations/colors/ -$blue-wordpress-700: #00669b; $blue-wordpress: #0073aa; +$blue-wordpress-700: #00669b; $blue-dark-900: #0071a1; $blue-medium-900: #006589; diff --git a/edit-post/assets/stylesheets/_z-index.scss b/edit-post/assets/stylesheets/_z-index.scss index dd99e2b0b8fc3..f19ab8b2e9531 100644 --- a/edit-post/assets/stylesheets/_z-index.scss +++ b/edit-post/assets/stylesheets/_z-index.scss @@ -11,7 +11,6 @@ $z-layers: ( '.freeform-toolbar': 10, '.editor-warning': 1, '.components-form-toggle__input': 1, - '.editor-format-list__menu': 1, '.editor-inserter__tabs': 1, '.editor-inserter__tab.is-active': 1, '.components-panel__header': 1, @@ -21,13 +20,13 @@ $z-layers: ( '.editor-block-switcher__menu': 5, '.components-popover__close': 5, '.editor-block-list__insertion-point': 5, - '.blocks-format-toolbar__link-modal': 81, // should appear above block controls - '.blocks-format-toolbar__link-container': 81, // link suggestions should also + '.editor-format-toolbar__link-modal': 81, // should appear above block controls + '.editor-format-toolbar__link-container': 81, // link suggestions should also '.blocks-gallery-item__inline-menu': 20, '.editor-block-settings-menu__popover': 21, // Below the header, but above the block toolbar - '.blocks-url-input__suggestions': 30, + '.editor-url-input__suggestions': 30, '.edit-post-header': 30, - '.blocks-button__inline-link .blocks-url-input__suggestions': 6, // URL suggestions for button block above sibling inserter + '.blocks-button__inline-link .editor-url-input__suggestions': 6, // URL suggestions for button block above sibling inserter '.wp-block-image__resize-handlers': 1, // Resize handlers above sibling inserter // Should have lower index than anything else positioned inside the block containers diff --git a/edit-post/assets/stylesheets/main.scss b/edit-post/assets/stylesheets/main.scss index 4e85ea9d25cc4..2ab7413575a2f 100644 --- a/edit-post/assets/stylesheets/main.scss +++ b/edit-post/assets/stylesheets/main.scss @@ -1,11 +1,15 @@ // Output overrides for each scheme -@include admin-scheme-color-overrides( 'blue', $scheme-blue__spot-color ); -@include admin-scheme-color-overrides( 'coffee', $scheme-coffee__spot-color ); -@include admin-scheme-color-overrides( 'ectoplasm', $scheme-ectoplasm__spot-color ); -@include admin-scheme-color-overrides( 'midnight', $scheme-midnight__spot-color ); -@include admin-scheme-color-overrides( 'ocean', $scheme-ocean__spot-color ); -@include admin-scheme-color-overrides( 'sunrise', $scheme-sunrise__spot-color ); - +// Almost all schemes use only one color swatch, a few have exceptions to ensure usability +@include admin-scheme-color-overrides( 'fresh', $scheme-fresh__spot-color, $scheme-fresh__spot-color ); +@include admin-scheme-color-overrides( 'light', $scheme-light__spot-color, $scheme-light__spot-color ); +@include admin-scheme-color-overrides( 'blue', $scheme-blue__spot-color, $scheme-blue__spot-color ); +@include admin-scheme-color-overrides( 'coffee', $scheme-coffee__spot-color, $scheme-coffee__spot-color ); +@include admin-scheme-color-overrides( 'ectoplasm', $scheme-ectoplasm__spot-color, $scheme-ectoplasm__spot-color ); +@include admin-scheme-color-overrides( 'midnight', $scheme-midnight__spot-color, $scheme-midnight__spot-color-2 ); // exception to ensure usability +@include admin-scheme-color-overrides( 'ocean', $scheme-ocean__spot-color, $scheme-ocean__spot-color ); +@include admin-scheme-color-overrides( 'sunrise', $scheme-sunrise__spot-color, $scheme-sunrise__spot-color-2 ); // exception to ensure usability + +// Fade animations @keyframes animate_fade { from { opacity: 0; @@ -152,6 +156,17 @@ body.gutenberg-editor-page { @include input-style__focus(); } } + + select { + padding: 2px; + + &:focus { + border-color: $blue-medium-600; + // Windows High Contrast mode will show this outline + outline: 2px solid transparent; + outline-offset: 0; + } + } } // Placeholder colors diff --git a/edit-post/components/sidebar/document-outline-panel/index.js b/edit-post/components/sidebar/document-outline-panel/index.js deleted file mode 100644 index 19d97c3a9b79c..0000000000000 --- a/edit-post/components/sidebar/document-outline-panel/index.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * WordPress dependencies - */ -import { __ } from '@wordpress/i18n'; -import { PanelBody } from '@wordpress/components'; -import { DocumentOutline, DocumentOutlineCheck } from '@wordpress/editor'; -import { compose } from '@wordpress/element'; -import { withSelect, withDispatch } from '@wordpress/data'; - -/** - * Module constants - */ -const PANEL_NAME = 'table-of-contents'; - -function DocumentOutlinePanel( { isOpened, onTogglePanel } ) { - return ( - <DocumentOutlineCheck> - <PanelBody title={ __( 'Table of Contents' ) } opened={ isOpened } onToggle={ onTogglePanel }> - <DocumentOutline /> - </PanelBody> - </DocumentOutlineCheck> - ); -} - -export default compose( [ - withSelect( ( select ) => { - return { - isOpened: select( 'core/edit-post' ).isEditorSidebarPanelOpened( PANEL_NAME ), - }; - } ), - withDispatch( ( dispatch ) => ( { - onTogglePanel() { - return dispatch( 'core/edit-post' ).toggleGeneralSidebarEditorPanel( PANEL_NAME ); - }, - } ) ), -] )( DocumentOutlinePanel ); diff --git a/edit-post/components/sidebar/document-sidebar/index.js b/edit-post/components/sidebar/document-sidebar/index.js index c9cc416f6f13a..9bcd667a00f4a 100644 --- a/edit-post/components/sidebar/document-sidebar/index.js +++ b/edit-post/components/sidebar/document-sidebar/index.js @@ -14,7 +14,6 @@ import FeaturedImage from '../featured-image'; import DiscussionPanel from '../discussion-panel'; import LastRevision from '../last-revision'; import PageAttributes from '../page-attributes'; -import DocumentOutlinePanel from '../document-outline-panel'; import MetaBoxes from '../../meta-boxes'; import SettingsHeader from '../settings-header'; import Sidebar from '../'; @@ -35,7 +34,6 @@ const DocumentSidebar = () => ( <PostExcerpt /> <DiscussionPanel /> <PageAttributes /> - <DocumentOutlinePanel /> <MetaBoxes location="side" usePanel /> </Panel> </Sidebar> diff --git a/edit-post/components/sidebar/page-attributes/index.js b/edit-post/components/sidebar/page-attributes/index.js index a2ee57264ab7d..72f1eeaf56d81 100644 --- a/edit-post/components/sidebar/page-attributes/index.js +++ b/edit-post/components/sidebar/page-attributes/index.js @@ -24,7 +24,7 @@ export function PageAttributes( { isOpened, onTogglePanel, postType } ) { return ( <PageAttributesCheck> <PanelBody - title={ get( postType, 'labels.attributes', __( 'Page Attributes' ) ) } + title={ get( postType, [ 'labels', 'attributes' ], __( 'Page Attributes' ) ) } opened={ isOpened } onToggle={ onTogglePanel } > diff --git a/edit-post/components/sidebar/plugin-post-status-info/index.js b/edit-post/components/sidebar/plugin-post-status-info/index.js new file mode 100644 index 0000000000000..cbe7efda9f250 --- /dev/null +++ b/edit-post/components/sidebar/plugin-post-status-info/index.js @@ -0,0 +1,18 @@ +/** + * Defines as extensibility slot for the Status & Visibility panel. + */ + +/** + * WordPress dependencies + */ +import { createSlotFill, PanelRow } from '@wordpress/components'; + +export const { Fill: PluginPostStatusInfo, Slot } = createSlotFill( 'PluginPostStatusInfo' ); + +PluginPostStatusInfo.Slot = () => ( + <PanelRow> + <Slot /> + </PanelRow> +); + +export default PluginPostStatusInfo; diff --git a/edit-post/components/sidebar/post-status/index.js b/edit-post/components/sidebar/post-status/index.js index 0143013fa0172..141795d047b22 100644 --- a/edit-post/components/sidebar/post-status/index.js +++ b/edit-post/components/sidebar/post-status/index.js @@ -17,6 +17,7 @@ import PostSticky from '../post-sticky'; import PostAuthor from '../post-author'; import PostFormat from '../post-format'; import PostPendingStatus from '../post-pending-status'; +import PluginPostStatusInfo from '../plugin-post-status-info'; /** * Module Constants @@ -32,6 +33,7 @@ function PostStatus( { isOpened, onTogglePanel } ) { <PostSticky /> <PostPendingStatus /> <PostAuthor /> + <PluginPostStatusInfo.Slot /> <PostTrash /> </PanelBody> ); diff --git a/edit-post/index.js b/edit-post/index.js index 19cfe5d6b024f..04048f80d2840 100644 --- a/edit-post/index.js +++ b/edit-post/index.js @@ -6,6 +6,7 @@ import { get, isString, some } from 'lodash'; /** * WordPress dependencies */ +import { registerCoreBlocks } from '@wordpress/core-blocks'; import { render, unmountComponentAtNode } from '@wordpress/element'; import { deprecated } from '@wordpress/utils'; @@ -59,9 +60,6 @@ export function reinitializeEditor( target, settings ) { * @return {Object} Editor interface. */ export function initializeEditor( id, post, settings ) { - const target = document.getElementById( id ); - const reboot = reinitializeEditor.bind( null, target, settings ); - if ( 'production' !== process.env.NODE_ENV ) { // Remove with 3.0 release. window.console.info( @@ -84,6 +82,11 @@ export function initializeEditor( id, post, settings ) { ); } + const target = document.getElementById( id ); + const reboot = reinitializeEditor.bind( null, target, settings ); + + registerCoreBlocks(); + render( <Editor settings={ migratedSettings || settings } onError={ reboot } post={ post } />, target @@ -96,5 +99,6 @@ export function initializeEditor( id, post, settings ) { }; } +export { default as PluginPostStatusInfo } from './components/sidebar/plugin-post-status-info'; export { default as PluginSidebar } from './components/sidebar/plugin-sidebar'; export { default as PluginSidebarMoreMenuItem } from './components/header/plugin-sidebar-more-menu-item'; diff --git a/edit-post/keyboard-shortcuts.js b/edit-post/keyboard-shortcuts.js index acb6746cc7564..6ef7d09a38c85 100644 --- a/edit-post/keyboard-shortcuts.js +++ b/edit-post/keyboard-shortcuts.js @@ -1,9 +1,13 @@ -const isMac = window.navigator.platform.toUpperCase().indexOf( 'MAC' ) >= 0; -const mod = isMac ? '⌘' : 'Ctrl'; +/** + * WordPress dependencies + */ +import { keycodes } from '@wordpress/utils'; + +const { rawShortcut, displayShortcut } = keycodes; export default { toggleEditorMode: { - value: 'mod+shift+alt+m', - label: `${ mod }+Shift+Alt+M`, + value: rawShortcut.secondary( 'm' ), + label: displayShortcut.secondary( 'm' ), }, }; diff --git a/editor/README.md b/editor/README.md index 78d89e24a60ab..8e271dcc80fc0 100644 --- a/editor/README.md +++ b/editor/README.md @@ -12,3 +12,114 @@ Such a crucial step is handled by the grammar parsing which takes the serialized The *visual editor* is thus a component that contains and renders the list of block nodes from the internal state into the page. This removes any trace of imperative handling when it comes to finding a block and manipulating a block. As a matter of fact, the visual editor or the text editor are just two different—equally valid—views of the same representation of state. The internal representation of the post content is updated as blocks are updated and it is serialized back to be saved in `post_content`. Individual blocks are handled by the `VisualBlock` component, which attaches event handlers and renders the `edit` function of a block definition to the document with the corresponding attributes and local state. The `edit` function is the markup shape of a component while in editing mode. + +## Components + +Because many blocks share the same complex behaviors, reusable components +are made available to simplify implementations of your block's `edit` function. + +### `BlockControls` + +When returned by your block's `edit` implementation, renders a toolbar of icon +buttons. This is useful for block-level modifications to be made available when +a block is selected. For example, if your block supports alignment, you may +want to display alignment options in the selected block's toolbar. + +Example: + +```js +( function( editor, element ) { + var el = element.createElement, + BlockControls = editor.BlockControls, + AlignmentToolbar = editor.AlignmentToolbar; + + function edit( props ) { + return [ + // Controls: (only visible when block is selected) + el( BlockControls, { key: 'controls' }, + el( AlignmentToolbar, { + value: props.align, + onChange: function( nextAlign ) { + props.setAttributes( { align: nextAlign } ) + } + } ) + ), + + // Block content: (with alignment as attribute) + el( 'p', { key: 'text', style: { textAlign: props.align } }, + 'Hello World!' + ), + ]; + } +} )( + window.wp.editor, + window.wp.element +); +``` + +Note in this example that we render `AlignmentToolbar` as a child of the +`BlockControls` element. This is another pre-configured component you can use +to simplify block text alignment. + +Alternatively, you can create your own toolbar controls by passing an array of +`controls` as a prop to the `BlockControls` component. Each control should be +an object with the following properties: + +- `icon: string` - Slug of the Dashicon to be shown in the control's toolbar button +- `title: string` - A human-readable localized text to be shown as the tooltip label of the control's button +- `subscript: ?string` - Optional text to be shown adjacent the button icon as subscript (for example, heading levels) +- `isActive: ?boolean` - Whether the control should be considered active / selected. Defaults to `false`. + +To create divisions between sets of controls within the same `BlockControls` +element, passing `controls` instead as a nested array (array of arrays of +objects). A divider will be shown between each set of controls. + +### `RichText` + +Render a rich +[`contenteditable` input](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Editable_content), +providing users the option to add emphasis to content or links to content. It +behaves similarly to a +[controlled component](https://facebook.github.io/react/docs/forms.html#controlled-components), +except that `onChange` is triggered less frequently than would be expected from +a traditional `input` field, usually when the user exits the field. + +The following properties (non-exhaustive list) are made available: + +- `value: string` - Markup value of the field. Only valid markup is + allowed, as determined by `inline` value and available controls. +- `onChange: Function` - Callback handler when the value of the field changes, + passing the new value as its only argument. +- `placeholder: string` - A text hint to be shown to the user when the field + value is empty, similar to the + [`input` and `textarea` attribute of the same name](https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/HTML5_updates#The_placeholder_attribute). +- `multiline: String` - A tag name to use for the tag that should be inserted + when Enter is pressed. For example: `li` in a list block, and `p` for a + block that can contain multiple paragraphs. The default is that only inline + elements are allowed to be used in inserted into the text, effectively + disabling the behavior of the "Enter" key. + +Example: + +```js +( function( editor, element ) { + var el = element.createElement, + RichText = editor.RichText; + + function edit( props ) { + function onChange( value ) { + props.setAttributes( { text: value } ); + } + + return el( RichText, { + value: props.attributes.text, + onChange: onChange + } ); + } + + // blocks.registerBlockType( ..., { edit: edit, ... } ); +} )( + window.wp.editor, + window.wp.element +); +``` \ No newline at end of file diff --git a/blocks/alignment-toolbar/index.js b/editor/components/alignment-toolbar/index.js similarity index 100% rename from blocks/alignment-toolbar/index.js rename to editor/components/alignment-toolbar/index.js diff --git a/blocks/alignment-toolbar/test/__snapshots__/index.js.snap b/editor/components/alignment-toolbar/test/__snapshots__/index.js.snap similarity index 100% rename from blocks/alignment-toolbar/test/__snapshots__/index.js.snap rename to editor/components/alignment-toolbar/test/__snapshots__/index.js.snap diff --git a/blocks/alignment-toolbar/test/index.js b/editor/components/alignment-toolbar/test/index.js similarity index 100% rename from blocks/alignment-toolbar/test/index.js rename to editor/components/alignment-toolbar/test/index.js diff --git a/blocks/autocomplete/README.md b/editor/components/autocomplete/README.md similarity index 100% rename from blocks/autocomplete/README.md rename to editor/components/autocomplete/README.md diff --git a/blocks/autocomplete/index.js b/editor/components/autocomplete/index.js similarity index 84% rename from blocks/autocomplete/index.js rename to editor/components/autocomplete/index.js index df366d82106c2..ea9f37ea63938 100644 --- a/blocks/autocomplete/index.js +++ b/editor/components/autocomplete/index.js @@ -7,9 +7,14 @@ import { clone } from 'lodash'; * WordPress dependencies */ import { applyFilters, hasFilter } from '@wordpress/hooks'; -import { Component } from '@wordpress/element'; +import { Component, compose } from '@wordpress/element'; import { Autocomplete as OriginalAutocomplete } from '@wordpress/components'; +/** + * Internal dependencies + */ +import { withBlockEditContext } from '../block-edit/context'; + /* * Use one array instance for fallback rather than inline array literals * because the latter may cause rerender due to failed prop equality checks. @@ -69,14 +74,17 @@ export function withFilteredAutocompleters( Autocomplete ) { } updateCompletersState() { - let { completers: nextCompleters } = this.props; + const { blockName, completers } = this.props; + let nextCompleters = completers; const lastFilteredCompletersProp = nextCompleters; + // Todo: Rename filter if ( hasFilter( 'blocks.Autocomplete.completers' ) ) { nextCompleters = applyFilters( 'blocks.Autocomplete.completers', // Provide copies so filters may directly modify them. - nextCompleters && nextCompleters.map( clone ) + nextCompleters && nextCompleters.map( clone ), + blockName, ); } @@ -106,4 +114,11 @@ export function withFilteredAutocompleters( Autocomplete ) { }; } -export default withFilteredAutocompleters( OriginalAutocomplete ); +export default compose( [ + withBlockEditContext( ( { name } ) => { + return { + blockName: name, + }; + } ), + withFilteredAutocompleters, +] )( OriginalAutocomplete ); diff --git a/blocks/autocomplete/test/index.js b/editor/components/autocomplete/test/index.js similarity index 95% rename from blocks/autocomplete/test/index.js rename to editor/components/autocomplete/test/index.js index 493a99d5ebcc7..91b9d442e9532 100644 --- a/blocks/autocomplete/test/index.js +++ b/editor/components/autocomplete/test/index.js @@ -40,15 +40,15 @@ describe( 'Autocomplete', () => { ); const expectedCompleters = [ {}, {}, {} ]; - wrapper = mount( <FilteredComponent completers={ expectedCompleters } /> ); + wrapper = mount( <FilteredComponent completers={ expectedCompleters } blockName="core/foo" /> ); expect( completersFilter ).not.toHaveBeenCalled(); wrapper.find( 'input' ).simulate( 'focus' ); - expect( completersFilter ).toHaveBeenCalledWith( expectedCompleters ); + expect( completersFilter ).toHaveBeenCalledWith( expectedCompleters, 'core/foo' ); } ); it( 'filters completers supplied when already focused', () => { - wrapper = mount( <FilteredComponent completers={ [] } /> ); + wrapper = mount( <FilteredComponent completers={ [] } blockName="core/foo" /> ); wrapper.find( 'input' ).getDOMNode().focus(); expect( wrapper.getDOMNode().contains( document.activeElement ) ).toBeTruthy(); @@ -64,7 +64,7 @@ describe( 'Autocomplete', () => { expect( completersFilter ).not.toHaveBeenCalled(); wrapper.setProps( { completers: expectedCompleters } ); - expect( completersFilter ).toHaveBeenCalledWith( expectedCompleters ); + expect( completersFilter ).toHaveBeenCalledWith( expectedCompleters, 'core/foo' ); } ); it( 'provides copies of completers to filter', () => { diff --git a/blocks/autocompleters/README.md b/editor/components/autocompleters/README.md similarity index 100% rename from blocks/autocompleters/README.md rename to editor/components/autocompleters/README.md diff --git a/blocks/autocompleters/block.js b/editor/components/autocompleters/block.js similarity index 88% rename from blocks/autocompleters/block.js rename to editor/components/autocompleters/block.js index 67bbd72f93057..33d37aab7903d 100644 --- a/blocks/autocompleters/block.js +++ b/editor/components/autocompleters/block.js @@ -3,11 +3,15 @@ */ import { sortBy, once } from 'lodash'; +/** + * WordPress dependencies + */ +import { createBlock, getBlockTypes } from '@wordpress/blocks'; + /** * Internal dependencies */ import './style.scss'; -import { createBlock, getBlockTypes } from '../api'; import BlockIcon from '../block-icon'; /** @@ -17,7 +21,7 @@ import BlockIcon from '../block-icon'; */ export default { name: 'blocks', - className: 'blocks-autocompleters__block', + className: 'editor-autocompleters__block', triggerPrefix: '/', options: once( function options() { return Promise.resolve( diff --git a/blocks/autocompleters/index.js b/editor/components/autocompleters/index.js similarity index 100% rename from blocks/autocompleters/index.js rename to editor/components/autocompleters/index.js diff --git a/blocks/autocompleters/style.scss b/editor/components/autocompleters/style.scss similarity index 70% rename from blocks/autocompleters/style.scss rename to editor/components/autocompleters/style.scss index 422d4f00169c0..d145e3aae4e56 100644 --- a/blocks/autocompleters/style.scss +++ b/editor/components/autocompleters/style.scss @@ -1,9 +1,9 @@ -.blocks-autocompleters__block .dashicon { +.editor-autocompleters__block .dashicon { margin-right: 8px; } -.blocks-autocompleters__user { - .blocks-autocompleters__user-avatar { +.editor-autocompleters__user { + .editor-autocompleters__user-avatar { margin-right: 8px; flex-grow: 0; flex-shrink: 0; @@ -11,7 +11,7 @@ width: 24px; // avoid jarring resize by seting the size upfront height: 24px; } - .blocks-autocompleters__user-name { + .editor-autocompleters__user-name { white-space: nowrap; text-overflow: ellipsis; overflow: hidden; @@ -19,7 +19,7 @@ flex-shrink: 0; flex-grow: 1; } - .blocks-autocompleters__user-slug { + .editor-autocompleters__user-slug { margin-left: 8px; color: $dark-gray-100; white-space: nowrap; @@ -29,7 +29,7 @@ flex-grow: 0; flex-shrink: 0; } - &:hover .blocks-autocompleters__user-slug { + &:hover .editor-autocompleters__user-slug { color: $blue-medium-300; } -} +} diff --git a/blocks/autocompleters/test/block.js b/editor/components/autocompleters/test/block.js similarity index 97% rename from blocks/autocompleters/test/block.js rename to editor/components/autocompleters/test/block.js index e8b2441a602cb..9453c066edfad 100644 --- a/blocks/autocompleters/test/block.js +++ b/editor/components/autocompleters/test/block.js @@ -3,10 +3,14 @@ */ import { noop } from 'lodash'; +/** + * WordPress dependencies + */ +import { registerBlockType, unregisterBlockType, getBlockTypes } from '@wordpress/blocks'; + /** * Internal dependencies */ -import { registerBlockType, unregisterBlockType, getBlockTypes } from '../../api'; import { blockAutocompleter } from '../'; describe( 'block', () => { diff --git a/blocks/autocompleters/user.js b/editor/components/autocompleters/user.js similarity index 72% rename from blocks/autocompleters/user.js rename to editor/components/autocompleters/user.js index 0532472e82b51..36a89249b6b6e 100644 --- a/blocks/autocompleters/user.js +++ b/editor/components/autocompleters/user.js @@ -5,7 +5,7 @@ */ export default { name: 'users', - className: 'blocks-autocompleters__user', + className: 'editor-autocompleters__user', triggerPrefix: '@', options( search ) { let payload = ''; @@ -20,9 +20,9 @@ export default { }, getOptionLabel( user ) { return [ - <img key="avatar" className="blocks-autocompleters__user-avatar" alt="" src={ user.avatar_urls[ 24 ] } />, - <span key="name" className="blocks-autocompleters__user-name">{ user.name }</span>, - <span key="slug" className="blocks-autocompleters__user-slug">{ user.slug }</span>, + <img key="avatar" className="editor-autocompleters__user-avatar" alt="" src={ user.avatar_urls[ 24 ] } />, + <span key="name" className="editor-autocompleters__user-name">{ user.name }</span>, + <span key="slug" className="editor-autocompleters__user-slug">{ user.slug }</span>, ]; }, allowNode() { diff --git a/blocks/block-alignment-toolbar/index.js b/editor/components/block-alignment-toolbar/index.js similarity index 93% rename from blocks/block-alignment-toolbar/index.js rename to editor/components/block-alignment-toolbar/index.js index 53e27107e72fb..029504a7334c5 100644 --- a/blocks/block-alignment-toolbar/index.js +++ b/editor/components/block-alignment-toolbar/index.js @@ -3,11 +3,7 @@ */ import { __ } from '@wordpress/i18n'; import { Toolbar } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import { withEditorSettings } from '../editor-settings'; +import { withEditorSettings } from '@wordpress/blocks'; const BLOCK_ALIGNMENTS_CONTROLS = { left: { diff --git a/blocks/block-alignment-toolbar/test/__snapshots__/index.js.snap b/editor/components/block-alignment-toolbar/test/__snapshots__/index.js.snap similarity index 100% rename from blocks/block-alignment-toolbar/test/__snapshots__/index.js.snap rename to editor/components/block-alignment-toolbar/test/__snapshots__/index.js.snap diff --git a/blocks/block-alignment-toolbar/test/index.js b/editor/components/block-alignment-toolbar/test/index.js similarity index 100% rename from blocks/block-alignment-toolbar/test/index.js rename to editor/components/block-alignment-toolbar/test/index.js diff --git a/blocks/block-controls/index.js b/editor/components/block-controls/index.js similarity index 100% rename from blocks/block-controls/index.js rename to editor/components/block-controls/index.js diff --git a/blocks/block-controls/test/__snapshots__/index.js.snap b/editor/components/block-controls/test/__snapshots__/index.js.snap similarity index 100% rename from blocks/block-controls/test/__snapshots__/index.js.snap rename to editor/components/block-controls/test/__snapshots__/index.js.snap diff --git a/blocks/block-controls/test/index.js b/editor/components/block-controls/test/index.js similarity index 100% rename from blocks/block-controls/test/index.js rename to editor/components/block-controls/test/index.js diff --git a/blocks/block-edit/context.js b/editor/components/block-edit/context.js similarity index 63% rename from blocks/block-edit/context.js rename to editor/components/block-edit/context.js index 939cd9c981e0a..a1f413b94ad58 100644 --- a/blocks/block-edit/context.js +++ b/editor/components/block-edit/context.js @@ -1,10 +1,18 @@ /** * External dependencies */ +import { noop } from 'lodash'; + +/** + * WordPress dependencies + */ import { createContext, createHigherOrderComponent } from '@wordpress/element'; const { Consumer, Provider } = createContext( { + name: '', isSelected: false, + focusedElement: null, + setFocusedElement: noop, } ); export { Provider as BlockEditContextProvider }; @@ -13,17 +21,19 @@ export { Provider as BlockEditContextProvider }; * A Higher Order Component used to inject BlockEdit context to the * wrapped component. * - * @param {Component} OriginalComponent Component to wrap. + * @param {Function} mapContextToProps Function called on every context change, + * expected to return object of props to + * merge with the component's own props. * - * @return {Component} Component which has BlockEdit context injected. + * @return {Component} Enhanced component with injected context as props. */ -export const withBlockEditContext = createHigherOrderComponent( ( OriginalComponent ) => { +export const withBlockEditContext = ( mapContextToProps ) => createHigherOrderComponent( ( OriginalComponent ) => { return ( props ) => ( <Consumer> { ( context ) => ( <OriginalComponent { ...props } - blockEditContext={ context } + { ...mapContextToProps( context, props ) } /> ) } </Consumer> diff --git a/blocks/block-edit/edit.js b/editor/components/block-edit/edit.js similarity index 88% rename from blocks/block-edit/edit.js rename to editor/components/block-edit/edit.js index 7a460c2c57510..9b20a0d286b88 100644 --- a/blocks/block-edit/edit.js +++ b/editor/components/block-edit/edit.js @@ -7,15 +7,7 @@ import classnames from 'classnames'; * WordPress dependencies */ import { withFilters } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import { - getBlockType, - getBlockDefaultClassName, - hasBlockSupport, -} from '../api'; +import { getBlockDefaultClassName, hasBlockSupport, getBlockType } from '@wordpress/blocks'; export const Edit = ( props ) => { const { attributes = {}, name } = props; diff --git a/blocks/block-edit/index.js b/editor/components/block-edit/index.js similarity index 68% rename from blocks/block-edit/index.js rename to editor/components/block-edit/index.js index 19f06f1e09d7c..c5a6c2bcf2dd8 100644 --- a/blocks/block-edit/index.js +++ b/editor/components/block-edit/index.js @@ -19,7 +19,11 @@ import { BlockEditContextProvider } from './context'; export class BlockEdit extends Component { constructor( props ) { super( props ); - this.state = {}; + this.setFocusedElement = this.setFocusedElement.bind( this ); + this.state = { + focusedElement: null, + setFocusedElement: this.setFocusedElement, + }; } getChildContext() { @@ -38,21 +42,33 @@ export class BlockEdit extends Component { }; } - static getDerivedStateFromProps( nextProps, prevState ) { - if ( nextProps.isSelected === get( prevState, [ 'context', 'isSelected' ] ) ) { + setFocusedElement( focusedElement ) { + this.setState( ( prevState ) => { + if ( prevState.focusedElement === focusedElement ) { + return null; + } + return { focusedElement }; + } ); + } + + static getDerivedStateFromProps( { name, isSelected }, prevState ) { + if ( + name === prevState.name && + isSelected === prevState.isSelected + ) { return null; } return { - context: { - isSelected: nextProps.isSelected, - }, + ...prevState, + name, + isSelected, }; } render() { return ( - <BlockEditContextProvider value={ this.state.context }> + <BlockEditContextProvider value={ this.state }> <Edit { ...this.props } /> </BlockEditContextProvider> ); diff --git a/blocks/block-edit/test/edit.js b/editor/components/block-edit/test/edit.js similarity index 96% rename from blocks/block-edit/test/edit.js rename to editor/components/block-edit/test/edit.js index 572630ca0aae1..944831652e6b0 100644 --- a/blocks/block-edit/test/edit.js +++ b/editor/components/block-edit/test/edit.js @@ -5,14 +5,18 @@ import { shallow } from 'enzyme'; import { noop } from 'lodash'; /** - * Internal dependencies + * WordPress dependencies */ -import { Edit } from '../edit'; import { registerBlockType, unregisterBlockType, getBlockTypes, -} from '../../api'; +} from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import { Edit } from '../edit'; describe( 'Edit', () => { afterEach( () => { diff --git a/blocks/block-format-controls/index.js b/editor/components/block-format-controls/index.js similarity index 100% rename from blocks/block-format-controls/index.js rename to editor/components/block-format-controls/index.js diff --git a/blocks/block-icon/index.js b/editor/components/block-icon/index.js similarity index 100% rename from blocks/block-icon/index.js rename to editor/components/block-icon/index.js diff --git a/blocks/block-icon/test/index.js b/editor/components/block-icon/test/index.js similarity index 100% rename from blocks/block-icon/test/index.js rename to editor/components/block-icon/test/index.js diff --git a/editor/components/block-inspector/index.js b/editor/components/block-inspector/index.js index 928b980905d15..bf8786d9d7e22 100644 --- a/editor/components/block-inspector/index.js +++ b/editor/components/block-inspector/index.js @@ -7,12 +7,7 @@ import { isEmpty } from 'lodash'; * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { - BlockIcon, - getBlockType, - InspectorControls, - InspectorAdvancedControls, -} from '@wordpress/blocks'; +import { getBlockType } from '@wordpress/blocks'; import { PanelBody } from '@wordpress/components'; import { withSelect } from '@wordpress/data'; @@ -21,6 +16,9 @@ import { withSelect } from '@wordpress/data'; */ import './style.scss'; import SkipToSelectedBlock from '../skip-to-selected-block'; +import BlockIcon from '../block-icon'; +import InspectorControls from '../inspector-controls'; +import InspectorAdvancedControls from '../inspector-advanced-controls'; const BlockInspector = ( { selectedBlock, count } ) => { if ( count > 1 ) { diff --git a/editor/components/block-list/block.js b/editor/components/block-list/block.js index 40f65ab5f13a1..dae2f692fc931 100644 --- a/editor/components/block-list/block.js +++ b/editor/components/block-list/block.js @@ -17,7 +17,6 @@ import { placeCaretAtVerticalEdge, } from '@wordpress/utils'; import { - BlockEdit, createBlock, cloneBlock, getBlockType, @@ -25,7 +24,6 @@ import { isSharedBlock, isUnmodifiedDefaultBlock, withEditorSettings, - getDefaultBlockName, } from '@wordpress/blocks'; import { withFilters } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; @@ -35,6 +33,7 @@ import { withViewportMatch } from '@wordpress/viewport'; /** * Internal dependencies */ +import BlockEdit from '../block-edit'; import BlockMover from '../block-mover'; import BlockDropZone from '../block-drop-zone'; import BlockSettingsMenu from '../block-settings-menu'; @@ -66,6 +65,7 @@ export class BlockListBlock extends Component { this.maybeHover = this.maybeHover.bind( this ); this.hideHoverEffects = this.hideHoverEffects.bind( this ); this.mergeBlocks = this.mergeBlocks.bind( this ); + this.insertBlocksAfter = this.insertBlocksAfter.bind( this ); this.onFocus = this.onFocus.bind( this ); this.preventDrag = this.preventDrag.bind( this ); this.onPointerDown = this.onPointerDown.bind( this ); @@ -76,6 +76,7 @@ export class BlockListBlock extends Component { this.onDragStart = this.onDragStart.bind( this ); this.onDragEnd = this.onDragEnd.bind( this ); this.selectOnOpen = this.selectOnOpen.bind( this ); + this.createInnerBlockList = this.createInnerBlockList.bind( this ); this.hadTouchStart = false; this.state = { @@ -85,6 +86,11 @@ export class BlockListBlock extends Component { }; } + createInnerBlockList( uid ) { + const { renderBlockMenu } = this.props; + return createInnerBlockList( uid, renderBlockMenu ); + } + /** * Provides context for descendent components for use in block rendering. * @@ -96,10 +102,7 @@ export class BlockListBlock extends Component { // is defined in `@wordpress/blocks`, so to avoid a circular dependency // we inject this function via context. return { - createInnerBlockList: ( uid ) => { - const { renderBlockMenu } = this.props; - return createInnerBlockList( uid, renderBlockMenu ); - }, + createInnerBlockList: this.createInnerBlockList, }; } @@ -270,6 +273,10 @@ export class BlockListBlock extends Component { } } + insertBlocksAfter( blocks ) { + this.props.onInsertBlocks( blocks, this.props.order + 1 ); + } + /** * Marks the block as selected when focused and not already selected. This * specifically handles the case where block does not set focus on its own @@ -347,9 +354,9 @@ export class BlockListBlock extends Component { case ENTER: // Insert default block after current block if enter and event // not already handled by descendant. - this.props.onInsertBlocksAfter( [ - createBlock( getDefaultBlockName() ), - ] ); + this.props.onInsertBlocks( [ + createBlock( 'core/paragraph' ), + ], this.props.order + 1 ); event.preventDefault(); break; @@ -419,11 +426,11 @@ export class BlockListBlock extends Component { // Empty paragraph blocks should always show up as unselected. const isEmptyDefaultBlock = isUnmodifiedDefaultBlock( block ); const isSelectedNotTyping = isSelected && ! isTypingWithinBlock; - const showSideInserter = ( isSelected || isHovered ) && isEmptyDefaultBlock; - const shouldAppearSelected = ! showSideInserter && isSelectedNotTyping; + const showEmptyBlockSideInserter = ( isSelected || isHovered ) && isEmptyDefaultBlock; + const shouldAppearSelected = ! showEmptyBlockSideInserter && isSelectedNotTyping; // We render block movers and block settings to keep them tabbale even if hidden - const shouldRenderMovers = ( isSelected || hoverArea === 'left' ) && ! showSideInserter && ! isMultiSelecting && ! isMultiSelected; - const shouldRenderBlockSettings = ( isSelected || hoverArea === 'right' ) && ! showSideInserter && ! isMultiSelecting && ! isMultiSelected; + const shouldRenderMovers = ( isSelected || hoverArea === 'left' ) && ! showEmptyBlockSideInserter && ! isMultiSelecting && ! isMultiSelected && ! isTypingWithinBlock; + const shouldRenderBlockSettings = ( isSelected || hoverArea === 'right' ) && ! isMultiSelecting && ! isMultiSelected && ! isTypingWithinBlock; const shouldShowBreadcrumb = isHovered; const shouldShowContextualToolbar = shouldAppearSelected && isValid && ( ! hasFixedToolbar || ! isLargeViewport ); const shouldShowMobileToolbar = shouldAppearSelected; @@ -551,7 +558,7 @@ export class BlockListBlock extends Component { isSelected={ isSelected } attributes={ block.attributes } setAttributes={ this.setAttributes } - insertBlocksAfter={ isLocked ? undefined : this.props.onInsertBlocksAfter } + insertBlocksAfter={ isLocked ? undefined : this.insertBlocksAfter } onReplace={ isLocked ? undefined : onReplace } mergeBlocks={ isLocked ? undefined : this.mergeBlocks } id={ uid } @@ -581,10 +588,10 @@ export class BlockListBlock extends Component { ) } </IgnoreNestedEvents> { !! error && <BlockCrashWarning /> } - { showSideInserter && ( + { showEmptyBlockSideInserter && ( <Fragment> <div className="editor-block-list__side-inserter"> - <InserterWithShortcuts uid={ uid } layout={ layout } onToggle={ this.selectOnOpen } /> + <InserterWithShortcuts uid={ uid } rootUID={ rootUID } layout={ layout } onToggle={ this.selectOnOpen } /> </div> <div className="editor-block-list__empty-block-inserter"> <Inserter @@ -616,7 +623,6 @@ const applyWithSelect = withSelect( ( select, { uid, rootUID } ) => { isSelectionEnabled, getSelectedBlocksInitialCaretPosition, getBlockSelectionEnd, - getBlockRootUID, } = select( 'core/editor' ); const isSelected = isBlockSelected( uid ); return { @@ -636,8 +642,6 @@ const applyWithSelect = withSelect( ( select, { uid, rootUID } ) => { isSelectionEnabled: isSelectionEnabled(), initialPosition: getSelectedBlocksInitialCaretPosition(), isSelected, - rootUIDOfRoot: getBlockRootUID( rootUID ), - orderOfRoot: getBlockIndex( rootUID, getBlockRootUID( rootUID ) ), }; } ); @@ -651,7 +655,6 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps ) => { replaceBlocks, editPost, toggleSelection, - moveBlockToPosition, } = dispatch( 'core/editor' ); return { @@ -661,24 +664,10 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps ) => { onSelect( uid = ownProps.uid, initialPosition ) { selectBlock( uid, initialPosition ); }, - onInsertBlocksAfter( blocks ) { - const { block, order, isLast, rootUID, orderOfRoot, rootUIDOfRoot, layout } = ownProps; - - blocks = blocks.map( ( oldBlock ) => cloneBlock( oldBlock, { layout } ) ); - - // If the current block is the last nested empty paragraph block, - // and we're about to insert another empty paragraph block, then - // move the empty paragraph block behind the wrapping block. - // This is a way for the user to escape out of wrapping blocks. - if ( - rootUID && isLast && blocks.length === 1 && - isUnmodifiedDefaultBlock( first( blocks ) ) && - isUnmodifiedDefaultBlock( block ) - ) { - moveBlockToPosition( block.uid, rootUID, rootUIDOfRoot, layout, orderOfRoot + 1 ); - } else { - insertBlocks( blocks, order + 1, rootUID ); - } + onInsertBlocks( blocks, index ) { + const { rootUID, layout } = ownProps; + blocks = blocks.map( ( block ) => cloneBlock( block, { layout } ) ); + insertBlocks( blocks, index, rootUID ); }, onRemove( uid ) { removeBlock( uid ); @@ -719,5 +708,5 @@ export default compose( }; } ), withFilters( 'editor.BlockListBlock' ), - withHoverAreas + withHoverAreas, )( BlockListBlock ); diff --git a/editor/components/block-list/breadcrumb.js b/editor/components/block-list/breadcrumb.js index ce310e2616b7d..528e69db07330 100644 --- a/editor/components/block-list/breadcrumb.js +++ b/editor/components/block-list/breadcrumb.js @@ -7,14 +7,13 @@ import classnames from 'classnames'; * WordPress dependencies */ import { compose, Component } from '@wordpress/element'; -import { Dashicon, Tooltip, Toolbar, Button } from '@wordpress/components'; +import { IconButton, Toolbar } from '@wordpress/components'; import { withDispatch, withSelect } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import NavigableToolbar from '../navigable-toolbar'; import BlockTitle from '../block-title'; /** @@ -58,24 +57,22 @@ export class BlockBreadcrumb extends Component { const { isFocused } = this.state; return ( - <NavigableToolbar className={ classnames( 'editor-block-list__breadcrumb', { + <div className={ classnames( 'editor-block-list__breadcrumb', { 'is-visible': ! isHidden || isFocused, } ) }> <Toolbar> { rootUID && ( - <Tooltip text={ __( 'Select parent block' ) }> - <Button - onClick={ selectRootBlock } - onFocus={ this.onFocus } - onBlur={ this.onBlur } - > - <Dashicon icon="arrow-left-alt" uid={ uid } /> - </Button> - </Tooltip> + <IconButton + onClick={ selectRootBlock } + onFocus={ this.onFocus } + onBlur={ this.onBlur } + label={ __( 'Select parent block' ) } + icon="arrow-left-alt" + /> ) } <BlockTitle uid={ uid } /> </Toolbar> - </NavigableToolbar> + </div> ); } } diff --git a/editor/components/block-preview/index.js b/editor/components/block-preview/index.js index 4b77a80ca48fc..104e885ce6744 100644 --- a/editor/components/block-preview/index.js +++ b/editor/components/block-preview/index.js @@ -8,11 +8,12 @@ import { noop } from 'lodash'; */ import { __ } from '@wordpress/i18n'; import { Component } from '@wordpress/element'; -import { createBlock, BlockEdit } from '@wordpress/blocks'; +import { createBlock } from '@wordpress/blocks'; /** * Internal dependencies */ +import BlockEdit from '../block-edit'; import { createInnerBlockList } from '../../utils/block-list'; import './style.scss'; @@ -29,9 +30,7 @@ class BlockPreview extends Component { // is defined in `@wordpress/blocks`, so to avoid a circular dependency // we inject this function via context. return { - createInnerBlockList: ( uid ) => { - return createInnerBlockList( uid ); - }, + createInnerBlockList, }; } diff --git a/editor/components/block-settings-menu/shared-block-settings.js b/editor/components/block-settings-menu/shared-block-settings.js index 50ad4fb401f53..f95d8dc9af847 100644 --- a/editor/components/block-settings-menu/shared-block-settings.js +++ b/editor/components/block-settings-menu/shared-block-settings.js @@ -26,7 +26,7 @@ export function SharedBlockSettings( { sharedBlock, onConvertToStatic, onConvert </IconButton> ) } { sharedBlock && ( - <div className="editor-block-settings-menu__section"> + <Fragment> <IconButton className="editor-block-settings-menu__control" icon="controls-repeat" @@ -44,7 +44,7 @@ export function SharedBlockSettings( { sharedBlock, onConvertToStatic, onConvert > { __( 'Delete Shared Block' ) } </IconButton> - </div> + </Fragment> ) } </Fragment> ); diff --git a/editor/components/block-switcher/index.js b/editor/components/block-switcher/index.js index 0cd9392bf05c3..071e37307f0d7 100644 --- a/editor/components/block-switcher/index.js +++ b/editor/components/block-switcher/index.js @@ -3,7 +3,7 @@ */ import { __ } from '@wordpress/i18n'; import { Dropdown, Dashicon, IconButton, Toolbar, NavigableMenu } from '@wordpress/components'; -import { getBlockType, getPossibleBlockTransformations, switchToBlockType, BlockIcon, withEditorSettings } from '@wordpress/blocks'; +import { getBlockType, getPossibleBlockTransformations, switchToBlockType, withEditorSettings } from '@wordpress/blocks'; import { compose } from '@wordpress/element'; import { keycodes } from '@wordpress/utils'; import { withSelect, withDispatch } from '@wordpress/data'; @@ -12,6 +12,7 @@ import { withSelect, withDispatch } from '@wordpress/data'; * Internal dependencies */ import './style.scss'; +import BlockIcon from '../block-icon'; /** * Module Constants diff --git a/editor/components/block-toolbar/index.js b/editor/components/block-toolbar/index.js index 75826e7cd5dc1..d6d4bb3035af8 100644 --- a/editor/components/block-toolbar/index.js +++ b/editor/components/block-toolbar/index.js @@ -1,7 +1,6 @@ /** * WordPress Dependencies */ -import { BlockControls, BlockFormatControls } from '@wordpress/blocks'; import { withSelect } from '@wordpress/data'; /** @@ -9,6 +8,8 @@ import { withSelect } from '@wordpress/data'; */ import './style.scss'; import BlockSwitcher from '../block-switcher'; +import BlockControls from '../block-controls'; +import BlockFormatControls from '../block-format-controls'; function BlockToolbar( { block, mode } ) { if ( ! block || ! block.isValid || mode !== 'visual' ) { diff --git a/editor/components/color-palette/index.js b/editor/components/color-palette/index.js new file mode 100644 index 0000000000000..1601ea4f08ec3 --- /dev/null +++ b/editor/components/color-palette/index.js @@ -0,0 +1,11 @@ +/** + * WordPress dependencies + */ +import { ColorPalette } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import withColorContext from './with-color-context'; + +export default withColorContext( ColorPalette ); diff --git a/editor/components/color-palette/with-color-context.js b/editor/components/color-palette/with-color-context.js new file mode 100644 index 0000000000000..154136be50e15 --- /dev/null +++ b/editor/components/color-palette/with-color-context.js @@ -0,0 +1,33 @@ + +/** + * External dependencies + */ +import { isEmpty } from 'lodash'; + +/** + * WordPress dependencies + */ +import { deprecated } from '@wordpress/utils'; +import { createHigherOrderComponent } from '@wordpress/element'; +import { withEditorSettings } from '@wordpress/blocks'; + +export default createHigherOrderComponent( + withEditorSettings( + ( settings, ownProps ) => { + if ( ownProps.colors || ownProps.disableCustomColors ) { + deprecated( 'Passing props "colors" or "disableCustomColors" to @editor/PanelColor or @editor/ColorPalette', { + version: '2.9', + alternative: 'remove the props and rely on the editor settings or use @wordpress/PanelColor and @wordpress/ColorPalette', + } ); + } + const colors = ownProps.colors || settings.colors; + const disableCustomColors = ownProps.disableCustomColors || settings.disableCustomColors; + return { + colors, + disableCustomColors, + hasColorsToChoose: ! isEmpty( colors ) || ! disableCustomColors, + }; + } + ), + 'withColorContext' +); diff --git a/blocks/colors/index.js b/editor/components/colors/index.js similarity index 50% rename from blocks/colors/index.js rename to editor/components/colors/index.js index 29b52a42d88e5..0bbf185ad0a50 100644 --- a/blocks/colors/index.js +++ b/editor/components/colors/index.js @@ -1,2 +1,2 @@ -export { getColorClass } from './utils'; +export { getColorClass, getColorName } from './utils'; export { default as withColors } from './with-colors'; diff --git a/blocks/colors/utils.js b/editor/components/colors/utils.js similarity index 81% rename from blocks/colors/utils.js rename to editor/components/colors/utils.js index 6c7d7b8814b41..9659c87cdfa9d 100644 --- a/blocks/colors/utils.js +++ b/editor/components/colors/utils.js @@ -23,6 +23,19 @@ export const getColorValue = ( colors, namedColor, customColor ) => { } }; +/** +* Provided an array of named colors and a color value returns the color name. +* +* @param {Array} colors Array of color objects containing the "name" and "color" value as properties. +* @param {?string} colorValue A string containing the color value. +* +* @return {?string} If colorValue is defined and matches a color part of the colors array, it returns the color name for that color. +*/ +export const getColorName = ( colors, colorValue ) => { + const colorObj = find( colors, { color: colorValue } ); + return colorObj ? colorObj.name : undefined; +}; + /** * Returns a function that receives the color value and sets it using the attribute for named colors or for custom colors. * diff --git a/editor/components/colors/with-colors.js b/editor/components/colors/with-colors.js new file mode 100644 index 0000000000000..c522d9b01cf81 --- /dev/null +++ b/editor/components/colors/with-colors.js @@ -0,0 +1,43 @@ +/** + * External dependencies + */ +import { get } from 'lodash'; + +/** + * WordPress dependencies + */ +import { createHigherOrderComponent } from '@wordpress/element'; +import { withEditorSettings } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import { getColorValue, getColorClass, setColorValue } from './utils'; + +/** + * Higher-order component, which handles color logic for class generation + * color value, retrieval and color attribute setting. + * + * @param {Function} mapGetSetColorToProps Function that receives getColor, setColor, and props, + * and returns additional props to pass to the component. + * + * @return {Function} Higher-order component. + */ +export default ( mapGetSetColorToProps ) => createHigherOrderComponent( + withEditorSettings( + ( settings, props ) => { + const colors = get( settings, [ 'colors' ], [] ); + const getColor = ( colorName, customColorValue, colorContext ) => { + return { + name: colorName, + class: getColorClass( colorContext, colorName ), + value: getColorValue( colors, colorName, customColorValue ), + }; + }; + const setColor = ( colorNameAttribute, customColorAttribute, setAttributes ) => { + return setColorValue( colors, colorNameAttribute, customColorAttribute, setAttributes ); + }; + return mapGetSetColorToProps( getColor, setColor, props ); + } ), + 'withColors' +); diff --git a/blocks/contrast-checker/index.js b/editor/components/contrast-checker/index.js similarity index 96% rename from blocks/contrast-checker/index.js rename to editor/components/contrast-checker/index.js index db973045eb2ef..8f37b816f5601 100644 --- a/blocks/contrast-checker/index.js +++ b/editor/components/contrast-checker/index.js @@ -33,7 +33,7 @@ function ContrastChecker( { backgroundColor, textColor, isLargeText, fallbackBac __( 'This color combination may be hard for people to read. Try using a darker background color and/or a brighter text color.' ) : __( 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker text color.' ); return ( - <div className="blocks-contrast-checker"> + <div className="editor-contrast-checker"> <Notice status="warning" isDismissible={ false }> { msg } </Notice> diff --git a/editor/components/contrast-checker/style.scss b/editor/components/contrast-checker/style.scss new file mode 100644 index 0000000000000..7d2f3ddc1e029 --- /dev/null +++ b/editor/components/contrast-checker/style.scss @@ -0,0 +1,3 @@ +.editor-contrast-checker > .notice { + margin: 0; +} diff --git a/blocks/contrast-checker/test/__snapshots__/index.js.snap b/editor/components/contrast-checker/test/__snapshots__/index.js.snap similarity index 94% rename from blocks/contrast-checker/test/__snapshots__/index.js.snap rename to editor/components/contrast-checker/test/__snapshots__/index.js.snap index 0e158f162dffd..18e13975a560c 100644 --- a/blocks/contrast-checker/test/__snapshots__/index.js.snap +++ b/editor/components/contrast-checker/test/__snapshots__/index.js.snap @@ -9,7 +9,7 @@ exports[`ContrastChecker should render component when the colors do not meet AA textColor="#666" > <div - className="blocks-contrast-checker" + className="editor-contrast-checker" > <Notice isDismissible={false} @@ -36,7 +36,7 @@ exports[`ContrastChecker should render different message matching snapshot when textColor="#666" > <div - className="blocks-contrast-checker" + className="editor-contrast-checker" > <Notice isDismissible={false} @@ -60,7 +60,7 @@ exports[`ContrastChecker should render messages when the textColor is valid, but textColor="#000000" > <div - className="blocks-contrast-checker" + className="editor-contrast-checker" > <Notice isDismissible={false} diff --git a/blocks/contrast-checker/test/index.js b/editor/components/contrast-checker/test/index.js similarity index 100% rename from blocks/contrast-checker/test/index.js rename to editor/components/contrast-checker/test/index.js diff --git a/editor/components/default-block-appender/index.js b/editor/components/default-block-appender/index.js index 39a8a1521fd95..8e4cfda27b0b3 100644 --- a/editor/components/default-block-appender/index.js +++ b/editor/components/default-block-appender/index.js @@ -64,7 +64,7 @@ export default compose( } = select( 'core/editor' ); const isEmpty = ! getBlockCount( ownProps.rootUID ); const lastBlock = getBlock( ownProps.lastBlockUID ); - const isLastBlockDefault = get( lastBlock, 'name' ) === getDefaultBlockName(); + const isLastBlockDefault = get( lastBlock, [ 'name' ] ) === getDefaultBlockName(); return { isVisible: isEmpty || ! isLastBlockDefault, diff --git a/editor/components/default-block-appender/style.scss b/editor/components/default-block-appender/style.scss index 50222e558338f..bf2ef1bf5ce9f 100644 --- a/editor/components/default-block-appender/style.scss +++ b/editor/components/default-block-appender/style.scss @@ -29,7 +29,7 @@ $empty-paragraph-height: $text-editor-font-size * 4; } // Don't show inserter until mousing - .editor-inserter { + .editor-inserter__toggle:not( [aria-expanded="true"] ) { opacity: 0; } @@ -42,7 +42,7 @@ $empty-paragraph-height: $text-editor-font-size * 4; } } - .editor-inserter { + .editor-inserter__toggle { opacity: 1; } } @@ -57,8 +57,8 @@ $empty-paragraph-height: $text-editor-font-size * 4; .editor-block-list__empty-block-inserter, .editor-default-block-appender .editor-inserter { position: absolute; - top: $item-spacing; - right: $item-spacing; // show on the right on mobile + top: $item-spacing; + right: $item-spacing; // show on the right on mobile transition: opacity 0.2s; @include break-small { diff --git a/editor/components/default-block-appender/test/__snapshots__/index.js.snap b/editor/components/default-block-appender/test/__snapshots__/index.js.snap index 8b998c42a4dd4..6794a3a5b83dc 100644 --- a/editor/components/default-block-appender/test/__snapshots__/index.js.snap +++ b/editor/components/default-block-appender/test/__snapshots__/index.js.snap @@ -36,7 +36,7 @@ exports[`DefaultBlockAppender should append a default block when input focused 1 value="Write your story" /> <WithEditorSettings(WithSelect(WithDispatch(InserterWithShortcuts))) /> - <WithSelect(WithDispatch(WithEditorSettings(Inserter))) + <WithEditorSettings(WithSelect(WithDispatch(Inserter))) position="top right" /> </div> @@ -60,7 +60,7 @@ exports[`DefaultBlockAppender should match snapshot 1`] = ` value="Write your story" /> <WithEditorSettings(WithSelect(WithDispatch(InserterWithShortcuts))) /> - <WithSelect(WithDispatch(WithEditorSettings(Inserter))) + <WithEditorSettings(WithSelect(WithDispatch(Inserter))) position="top right" /> </div> @@ -84,7 +84,7 @@ exports[`DefaultBlockAppender should optionally show without prompt 1`] = ` value="" /> <WithEditorSettings(WithSelect(WithDispatch(InserterWithShortcuts))) /> - <WithSelect(WithDispatch(WithEditorSettings(Inserter))) + <WithEditorSettings(WithSelect(WithDispatch(Inserter))) position="top right" /> </div> diff --git a/editor/components/editor-global-keyboard-shortcuts/index.js b/editor/components/editor-global-keyboard-shortcuts/index.js index 74e6df91e970a..9c4e8c00ee003 100644 --- a/editor/components/editor-global-keyboard-shortcuts/index.js +++ b/editor/components/editor-global-keyboard-shortcuts/index.js @@ -10,6 +10,9 @@ import { Component, Fragment, compose } from '@wordpress/element'; import { KeyboardShortcuts } from '@wordpress/components'; import { withSelect, withDispatch } from '@wordpress/data'; import { withEditorSettings } from '@wordpress/blocks'; +import { keycodes } from '@wordpress/utils'; + +const { rawShortcut } = keycodes; class EditorGlobalKeyboardShortcuts extends Component { constructor() { @@ -30,6 +33,7 @@ class EditorGlobalKeyboardShortcuts extends Component { undoOrRedo( event ) { const { onRedo, onUndo } = this.props; + if ( event.shiftKey ) { onRedo(); } else { @@ -69,9 +73,9 @@ class EditorGlobalKeyboardShortcuts extends Component { <Fragment> <KeyboardShortcuts shortcuts={ { - 'mod+a': this.selectAll, - 'mod+z': this.undoOrRedo, - 'mod+shift+z': this.undoOrRedo, + [ rawShortcut.primary( 'a' ) ]: this.selectAll, + [ rawShortcut.primary( 'z' ) ]: this.undoOrRedo, + [ rawShortcut.primaryShift( 'z' ) ]: this.undoOrRedo, backspace: this.deleteSelectedBlocks, del: this.deleteSelectedBlocks, escape: this.clearMultiSelection, @@ -80,7 +84,7 @@ class EditorGlobalKeyboardShortcuts extends Component { <KeyboardShortcuts bindGlobal shortcuts={ { - 'mod+s': this.save, + [ rawShortcut.primary( 's' ) ]: this.save, } } /> </Fragment> diff --git a/editor/components/editor-history/redo.js b/editor/components/editor-history/redo.js index 6636d78a94f67..edf2af589fa8c 100644 --- a/editor/components/editor-history/redo.js +++ b/editor/components/editor-history/redo.js @@ -5,12 +5,16 @@ import { __ } from '@wordpress/i18n'; import { IconButton } from '@wordpress/components'; import { withSelect, withDispatch } from '@wordpress/data'; import { compose } from '@wordpress/element'; +import { keycodes } from '@wordpress/utils'; + +const { displayShortcut } = keycodes; function EditorHistoryRedo( { hasRedo, redo } ) { return ( <IconButton icon="redo" label={ __( 'Redo' ) } + shortcut={ displayShortcut.primaryShift( 'z' ) } disabled={ ! hasRedo } onClick={ redo } className="editor-history__undo" diff --git a/editor/components/editor-history/undo.js b/editor/components/editor-history/undo.js index 3a3f060fa173e..35c08f87697ce 100644 --- a/editor/components/editor-history/undo.js +++ b/editor/components/editor-history/undo.js @@ -5,12 +5,16 @@ import { __ } from '@wordpress/i18n'; import { IconButton } from '@wordpress/components'; import { withSelect, withDispatch } from '@wordpress/data'; import { compose } from '@wordpress/element'; +import { keycodes } from '@wordpress/utils'; + +const { displayShortcut } = keycodes; function EditorHistoryUndo( { hasUndo, undo } ) { return ( <IconButton icon="undo" label={ __( 'Undo' ) } + shortcut={ displayShortcut.primary( 'z' ) } disabled={ ! hasUndo } onClick={ undo } className="editor-history__undo" diff --git a/blocks/image-placeholder/index.js b/editor/components/image-placeholder/index.js similarity index 95% rename from blocks/image-placeholder/index.js rename to editor/components/image-placeholder/index.js index 523aeeaeb65e3..e7b33dfb65e48 100644 --- a/blocks/image-placeholder/index.js +++ b/editor/components/image-placeholder/index.js @@ -8,13 +8,12 @@ import { map } from 'lodash'; */ import { DropZone, FormFileUpload, Placeholder, Button } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; +import { editorMediaUpload, rawHandler } from '@wordpress/blocks'; /** * Internal dependencies */ -import editorMediaUpload from '../editor-media-upload'; import MediaUpload from '../media-upload'; -import { rawHandler } from '../api'; /** * ImagePlaceholder is a react component used by blocks containing user configurable images e.g: image and cover image. diff --git a/editor/components/index.js b/editor/components/index.js index 57bb528463ca8..c85751f725373 100644 --- a/editor/components/index.js +++ b/editor/components/index.js @@ -1,3 +1,28 @@ +// Block Creation Components +export { default as Autocomplete } from './autocomplete'; +export * from './autocompleters'; +export { default as AlignmentToolbar } from './alignment-toolbar'; +export { default as BlockAlignmentToolbar } from './block-alignment-toolbar'; +export { default as BlockControls } from './block-controls'; +export { default as BlockEdit } from './block-edit'; +export { default as BlockFormatControls } from './block-format-controls'; +export { default as BlockIcon } from './block-icon'; +export { default as ColorPalette } from './color-palette'; +export { default as withColorContext } from './color-palette/with-color-context'; +export * from './colors'; +export { default as ContrastChecker } from './contrast-checker'; +export { default as ImagePlaceholder } from './image-placeholder'; +export { default as InnerBlocks } from './inner-blocks'; +export { default as InspectorAdvancedControls } from './inspector-advanced-controls'; +export { default as InspectorControls } from './inspector-controls'; +export { default as PanelColor } from './panel-color'; +export { default as PlainText } from './plain-text'; +export { default as RichText } from './rich-text'; +export { default as RichTextProvider } from './rich-text/provider'; +export { default as MediaUpload } from './media-upload'; +export { default as UrlInput } from './url-input'; +export { default as UrlInputButton } from './url-input/button'; + // Post Related Components export { default as AutosaveMonitor } from './autosave-monitor'; export { default as DocumentOutline } from './document-outline'; diff --git a/editor/components/index.native.js b/editor/components/index.native.js new file mode 100644 index 0000000000000..2c2b5eaf2957a --- /dev/null +++ b/editor/components/index.native.js @@ -0,0 +1 @@ +export { default as PlainText } from './plain-text'; diff --git a/blocks/inner-blocks/README.md b/editor/components/inner-blocks/README.md similarity index 100% rename from blocks/inner-blocks/README.md rename to editor/components/inner-blocks/README.md diff --git a/blocks/inner-blocks/index.js b/editor/components/inner-blocks/index.js similarity index 64% rename from blocks/inner-blocks/index.js rename to editor/components/inner-blocks/index.js index 4d25e0bd4b28f..6b1d56c44932d 100644 --- a/blocks/inner-blocks/index.js +++ b/editor/components/inner-blocks/index.js @@ -3,14 +3,14 @@ */ import { withContext } from '@wordpress/components'; -function InnerBlocks( { BlockList, layouts } ) { - return BlockList ? <BlockList layouts={ layouts } /> : null; +function InnerBlocks( { BlockList, layouts, allowedBlocks, template } ) { + return <BlockList { ...{ layouts, allowedBlocks, template } } />; } InnerBlocks = withContext( 'BlockList' )()( InnerBlocks ); InnerBlocks.Content = ( { BlockContent } ) => { - return BlockContent ? <BlockContent /> : null; + return <BlockContent />; }; InnerBlocks.Content = withContext( 'BlockContent' )()( InnerBlocks.Content ); diff --git a/editor/components/inserter-with-shortcuts/index.js b/editor/components/inserter-with-shortcuts/index.js index 009c50056759b..6827203ac0bc0 100644 --- a/editor/components/inserter-with-shortcuts/index.js +++ b/editor/components/inserter-with-shortcuts/index.js @@ -6,7 +6,7 @@ import { filter, isEmpty } from 'lodash'; /** * WordPress dependencies */ -import { BlockIcon, createBlock, getDefaultBlockName, withEditorSettings } from '@wordpress/blocks'; +import { createBlock, getDefaultBlockName, withEditorSettings } from '@wordpress/blocks'; import { compose } from '@wordpress/element'; import { IconButton } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; @@ -15,6 +15,7 @@ import { withDispatch, withSelect } from '@wordpress/data'; /** * Internal dependencies */ +import BlockIcon from '../block-icon'; import './style.scss'; function InserterWithShortcuts( { items, isLocked, onInsert } ) { @@ -52,9 +53,13 @@ export default compose( allowedBlockTypes, }; } ), - withSelect( ( select, { allowedBlockTypes } ) => ( { - items: select( 'core/editor' ).getFrecentInserterItems( allowedBlockTypes, 4 ), - } ) ), + withSelect( ( select, { allowedBlockTypes, rootUID } ) => { + const { getFrecentInserterItems, getSupportedBlocks } = select( 'core/editor' ); + const supportedBlocks = getSupportedBlocks( rootUID, allowedBlockTypes ); + return { + items: getFrecentInserterItems( supportedBlocks, 4 ), + }; + } ), withDispatch( ( dispatch, ownProps ) => { const { uid, rootUID, layout } = ownProps; diff --git a/editor/components/inserter/group.js b/editor/components/inserter/group.js index cdbe9a9c2f684..d36db856441d4 100644 --- a/editor/components/inserter/group.js +++ b/editor/components/inserter/group.js @@ -8,7 +8,11 @@ import { isEqual } from 'lodash'; */ import { Component } from '@wordpress/element'; import { NavigableMenu } from '@wordpress/components'; -import { BlockIcon } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import BlockIcon from '../block-icon'; function deriveActiveItems( items ) { return items.filter( ( item ) => ! item.isDisabled ); diff --git a/editor/components/inserter/index.js b/editor/components/inserter/index.js index 6814c79aa598b..b1ddc21ce0025 100644 --- a/editor/components/inserter/index.js +++ b/editor/components/inserter/index.js @@ -87,11 +87,32 @@ class Inserter extends Component { } export default compose( [ - withSelect( ( select ) => ( { - title: select( 'core/editor' ).getEditedPostAttribute( 'title' ), - insertionPoint: select( 'core/editor' ).getBlockInsertionPoint(), - selectedBlock: select( 'core/editor' ).getSelectedBlock(), - } ) ), + withEditorSettings( ( settings ) => { + const { allowedBlockTypes, templateLock } = settings; + + return { + allowedBlockTypes, + isLocked: !! templateLock, + }; + } ), + withSelect( ( select, { allowedBlockTypes } ) => { + const { + getEditedPostAttribute, + getBlockInsertionPoint, + getSelectedBlock, + getSupportedBlocks, + } = select( 'core/editor' ); + + const insertionPoint = getBlockInsertionPoint(); + const { rootUID } = insertionPoint; + const supportedBlocks = getSupportedBlocks( rootUID, allowedBlockTypes ); + return { + title: getEditedPostAttribute( 'title' ), + insertionPoint, + selectedBlock: getSelectedBlock(), + hasSupportedBlocks: true === supportedBlocks || ! isEmpty( supportedBlocks ), + }; + } ), withDispatch( ( dispatch, ownProps ) => ( { showInsertionPoint: dispatch( 'core/editor' ).showInsertionPoint, hideInsertionPoint: dispatch( 'core/editor' ).hideInsertionPoint, @@ -106,12 +127,4 @@ export default compose( [ return dispatch( 'core/editor' ).insertBlock( insertedBlock, index, rootUID ); }, } ) ), - withEditorSettings( ( settings ) => { - const { allowedBlockTypes, templateLock } = settings; - - return { - hasSupportedBlocks: true === allowedBlockTypes || ! isEmpty( allowedBlockTypes ), - isLocked: !! templateLock, - }; - } ), ] )( Inserter ); diff --git a/editor/components/inserter/menu.js b/editor/components/inserter/menu.js index f863ad697f337..1d7c058a1fae2 100644 --- a/editor/components/inserter/menu.js +++ b/editor/components/inserter/menu.js @@ -344,10 +344,17 @@ export default compose( }; } ), withSelect( ( select, { allowedBlockTypes } ) => { - const { getInserterItems, getFrecentInserterItems } = select( 'core/editor' ); + const { + getBlockInsertionPoint, + getInserterItems, + getFrecentInserterItems, + getSupportedBlocks, + } = select( 'core/editor' ); + const { rootUID } = getBlockInsertionPoint(); + const supportedBlocks = getSupportedBlocks( rootUID, allowedBlockTypes ); return { - items: getInserterItems( allowedBlockTypes ), - frecentItems: getFrecentInserterItems( allowedBlockTypes ), + items: getInserterItems( supportedBlocks ), + frecentItems: getFrecentInserterItems( supportedBlocks ), }; } ), withDispatch( ( dispatch ) => ( { diff --git a/blocks/inspector-advanced-controls/index.js b/editor/components/inspector-advanced-controls/index.js similarity index 100% rename from blocks/inspector-advanced-controls/index.js rename to editor/components/inspector-advanced-controls/index.js diff --git a/blocks/inspector-controls/index.js b/editor/components/inspector-controls/index.js similarity index 100% rename from blocks/inspector-controls/index.js rename to editor/components/inspector-controls/index.js diff --git a/blocks/media-upload/README.md b/editor/components/media-upload/README.md similarity index 89% rename from blocks/media-upload/README.md rename to editor/components/media-upload/README.md index e590177cbb593..ce9a748535446 100644 --- a/blocks/media-upload/README.md +++ b/editor/components/media-upload/README.md @@ -27,7 +27,7 @@ You can check how this component is implemented for the edit post page using `wp ```jsx import { Button } from '@wordpress/components'; -import { MediaUpload } from '@wordpress/blocks'; +import { MediaUpload } from '@wordpress/editor'; function MyMediaUploader() { return ( @@ -78,6 +78,21 @@ Callback called when the media modal is closed, the selected media are passed as - Type: `Function` - Required: Yes +### title + +Title displayed in the media modal. + +- Type: `String` +- Required: No +- Default: `Select or Upload Media` + +### modalClass + +CSS class added to the media modal frame. + +- Type: `String` +- Required: No + ## render A callback invoked to render the Button opening the media library. diff --git a/blocks/media-upload/index.js b/editor/components/media-upload/index.js similarity index 95% rename from blocks/media-upload/index.js rename to editor/components/media-upload/index.js index 1a6e1e46657c9..9485a0c856618 100644 --- a/blocks/media-upload/index.js +++ b/editor/components/media-upload/index.js @@ -12,4 +12,5 @@ import { withFilters } from '@wordpress/components'; */ const MediaUpload = () => null; +// Todo: rename the filter export default withFilters( 'blocks.MediaUpload' )( MediaUpload ); diff --git a/editor/components/page-attributes/check.js b/editor/components/page-attributes/check.js index 6ea5bafbec089..4ec295456acf4 100644 --- a/editor/components/page-attributes/check.js +++ b/editor/components/page-attributes/check.js @@ -11,7 +11,7 @@ import { compose } from '@wordpress/element'; import { withSelect } from '@wordpress/data'; export function PageAttributesCheck( { availableTemplates, postType, children } ) { - const supportsPageAttributes = get( postType, 'supports.page-attributes', false ); + const supportsPageAttributes = get( postType, [ 'supports', 'page-attributes' ], false ); // Only render fields if post type supports page attributes or available templates exist. if ( ! supportsPageAttributes && isEmpty( availableTemplates ) ) { diff --git a/editor/components/page-attributes/parent.js b/editor/components/page-attributes/parent.js index e03de14e647d6..0b45d8e01747c 100644 --- a/editor/components/page-attributes/parent.js +++ b/editor/components/page-attributes/parent.js @@ -14,9 +14,9 @@ import { buildTermsTree } from '@wordpress/utils'; import { withSelect, withDispatch } from '@wordpress/data'; export function PageAttributesParent( { parent, postType, items, onUpdateParent } ) { - const isHierarchical = get( postType, 'hierarchical', false ); - const parentPageLabel = get( postType, 'labels.parent_item_colon' ); - const pageItems = get( items, 'data', [] ); + const isHierarchical = get( postType, [ 'hierarchical' ], false ); + const parentPageLabel = get( postType, [ 'labels', 'parent_item_colon' ] ); + const pageItems = get( items, [ 'data' ], [] ); if ( ! isHierarchical || ! parentPageLabel || ! pageItems.length ) { return null; } @@ -60,7 +60,7 @@ const applyWithDispatch = withDispatch( ( dispatch ) => { const applyWithAPIDataItems = withAPIData( ( props, { type } ) => { const { postTypeSlug, postId } = props; - const isHierarchical = get( props, 'postType.hierarchical', false ); + const isHierarchical = get( props, [ 'postType', 'hierarchical' ], false ); const queryString = stringify( { context: 'edit', per_page: 100, diff --git a/editor/components/panel-color/index.js b/editor/components/panel-color/index.js new file mode 100644 index 0000000000000..316cd7826ef5c --- /dev/null +++ b/editor/components/panel-color/index.js @@ -0,0 +1,34 @@ +/** + * External dependencies + */ +import { omit } from 'lodash'; + +/** + * WordPress dependencies + */ +import { ifCondition, PanelColor as PanelColorComponent } from '@wordpress/components'; +import { compose } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import ColorPalette from '../color-palette'; +import withColorContext from '../color-palette/with-color-context'; +import { getColorName } from '../colors'; + +function PanelColor( { colors, title, colorValue, initialOpen, ...props } ) { + const colorName = getColorName( colors, colorValue ); + return ( + <PanelColorComponent { ...{ title, colorName, colorValue, initialOpen } } > + <ColorPalette + value={ colorValue } + { ...omit( props, [ 'disableCustomColors' ] ) } + /> + </PanelColorComponent> + ); +} + +export default compose( [ + withColorContext, + ifCondition( ( { hasColorsToChoose } ) => hasColorsToChoose ), +] )( PanelColor ); diff --git a/blocks/plain-text/README.md b/editor/components/plain-text/README.md similarity index 88% rename from blocks/plain-text/README.md rename to editor/components/plain-text/README.md index 377e86b7caf39..36909597bc6aa 100644 --- a/blocks/plain-text/README.md +++ b/editor/components/plain-text/README.md @@ -29,7 +29,7 @@ wp.blocks.registerBlockType( /* ... */, { }, edit: function( props ) { - return wp.element.createElement( wp.blocks.PlainText, { + return wp.element.createElement( wp.editor.PlainText, { className: props.className, value: props.attributes.content, onChange: function( content ) { @@ -41,7 +41,8 @@ wp.blocks.registerBlockType( /* ... */, { ``` {% ESNext %} ```js -const { registerBlockType, PlainText } = wp.blocks; +const { registerBlockType } = wp.blocks; +const { PlainText } = wp.editor; registerBlockType( /* ... */, { // ... diff --git a/blocks/plain-text/index.js b/editor/components/plain-text/index.js similarity index 86% rename from blocks/plain-text/index.js rename to editor/components/plain-text/index.js index e7b665e0ff4e6..13da032771715 100644 --- a/blocks/plain-text/index.js +++ b/editor/components/plain-text/index.js @@ -12,7 +12,7 @@ import './style.scss'; function PlainText( { onChange, className, ...props } ) { return ( <TextareaAutosize - className={ classnames( 'blocks-plain-text', className ) } + className={ classnames( 'editor-plain-text', className ) } onChange={ ( event ) => onChange( event.target.value ) } { ...props } /> diff --git a/editor/components/plain-text/index.native.js b/editor/components/plain-text/index.native.js new file mode 100644 index 0000000000000..c9ee8c6978071 --- /dev/null +++ b/editor/components/plain-text/index.native.js @@ -0,0 +1,21 @@ +/** + * External dependencies + */ +import { TextInput } from 'react-native'; + +/** + * Internal dependencies + */ +import styles from './style.scss'; + +function PlainText( { onChange, className, ...props } ) { + return ( + <TextInput + className={ [ styles[ 'editor-plain-text' ], className ] } + onChangeText={ ( text ) => onChange( text ) } + { ...props } + /> + ); +} + +export default PlainText; diff --git a/editor/components/plain-text/style.native.scss b/editor/components/plain-text/style.native.scss new file mode 100644 index 0000000000000..5e0face119c73 --- /dev/null +++ b/editor/components/plain-text/style.native.scss @@ -0,0 +1,8 @@ +.editor-plain-text { + box-shadow: none; + + border-width: 0; + + padding: 0; + margin: 0; +} diff --git a/blocks/plain-text/style.scss b/editor/components/plain-text/style.scss similarity index 83% rename from blocks/plain-text/style.scss rename to editor/components/plain-text/style.scss index 32187f45b9faf..ef8e884b6e844 100644 --- a/blocks/plain-text/style.scss +++ b/editor/components/plain-text/style.scss @@ -1,4 +1,4 @@ -.gutenberg .blocks-plain-text { +.gutenberg .editor-plain-text { box-shadow: none; font-family: inherit; font-size: inherit; diff --git a/editor/components/post-author/check.js b/editor/components/post-author/check.js index 0d1edc47a405e..e1d678263cb39 100644 --- a/editor/components/post-author/check.js +++ b/editor/components/post-author/check.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { filter, get } from 'lodash'; +import { get } from 'lodash'; /** * WordPress dependencies @@ -15,8 +15,7 @@ import { withSelect } from '@wordpress/data'; */ import PostTypeSupportCheck from '../post-type-support-check'; -export function PostAuthorCheck( { user, users, children } ) { - const authors = filter( users.data, ( { capabilities } ) => get( capabilities, [ 'level_1' ], false ) ); +export function PostAuthorCheck( { user, authors, children } ) { const userCanPublishPosts = get( user.data, [ 'post_type_capabilities', 'publish_posts' ], false ); if ( ! userCanPublishPosts || authors.length < 2 ) { @@ -30,13 +29,13 @@ export default compose( [ withSelect( ( select ) => { return { postType: select( 'core/editor' ).getCurrentPostType(), + authors: select( 'core' ).getAuthors(), }; } ), withAPIData( ( props ) => { const { postType } = props; return { - users: '/wp/v2/users?context=edit&per_page=100', user: `/wp/v2/users/me?post_type=${ postType }&context=edit`, }; } ), diff --git a/editor/components/post-author/index.js b/editor/components/post-author/index.js index 13217619eaf55..46e3b4b3fe220 100644 --- a/editor/components/post-author/index.js +++ b/editor/components/post-author/index.js @@ -1,13 +1,8 @@ -/** - * External dependencies - */ -import { get, filter } from 'lodash'; - /** * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { withAPIData, withInstanceId } from '@wordpress/components'; +import { withInstanceId } from '@wordpress/components'; import { Component, compose } from '@wordpress/element'; import { withSelect, withDispatch } from '@wordpress/data'; @@ -29,21 +24,8 @@ export class PostAuthor extends Component { onUpdateAuthor( Number( value ) ); } - getAuthors() { - // While User Levels are officially deprecated, the behavior of the - // existing users dropdown on `who=authors` tests `user_level != 0` - // - // See: https://github.com/WordPress/WordPress/blob/a193916/wp-includes/class-wp-user-query.php#L322-L327 - // See: https://codex.wordpress.org/Roles_and_Capabilities#User_Levels - const { users } = this.props; - return filter( users.data, ( user ) => { - return get( user, [ 'capabilities', 'level_1' ], false ); - } ); - } - render() { - const { postAuthor, instanceId } = this.props; - const authors = this.getAuthors(); + const { postAuthor, instanceId, authors } = this.props; const selectId = 'post-author-selector-' + instanceId; // Disable reason: A select with an onchange throws a warning @@ -72,6 +54,7 @@ export default compose( [ withSelect( ( select ) => { return { postAuthor: select( 'core/editor' ).getEditedPostAttribute( 'author' ), + authors: select( 'core' ).getAuthors(), }; } ), withDispatch( ( dispatch ) => ( { @@ -79,10 +62,5 @@ export default compose( [ dispatch( 'core/editor' ).editPost( { author } ); }, } ) ), - withAPIData( () => { - return { - users: '/wp/v2/users?context=edit&per_page=100', - }; - } ), withInstanceId, ] )( PostAuthor ); diff --git a/editor/components/post-author/test/check.js b/editor/components/post-author/test/check.js index 9e652a53ad421..6ec62435d68b4 100644 --- a/editor/components/post-author/test/check.js +++ b/editor/components/post-author/test/check.js @@ -43,36 +43,14 @@ describe( 'PostAuthorCheck', () => { }, }; - it( 'should not render anything if the user doesn\'t have the right capabilities', () => { - let wrapper = shallow( <PostAuthorCheck users={ users } user={ {} }>authors</PostAuthorCheck> ); - expect( wrapper.type() ).toBe( null ); - wrapper = shallow( - <PostAuthorCheck users={ users } user={ - { data: { post_type_capabilities: { publish_posts: false } } } - }> - authors - </PostAuthorCheck> - ); - expect( wrapper.type() ).toBe( null ); - } ); - it( 'should not render anything if users unknown', () => { - const wrapper = shallow( <PostAuthorCheck users={ {} } user={ user }>authors</PostAuthorCheck> ); + const wrapper = shallow( <PostAuthorCheck authors={ [] } user={ user }>authors</PostAuthorCheck> ); expect( wrapper.type() ).toBe( null ); } ); it( 'should not render anything if single user', () => { const wrapper = shallow( - <PostAuthorCheck users={ { data: users.data.slice( 0, 1 ) } } user={ user }> - authors - </PostAuthorCheck> - ); - expect( wrapper.type() ).toBe( null ); - } ); - - it( 'should not render anything if single filtered user', () => { - const wrapper = shallow( - <PostAuthorCheck users={ { data: users.data.slice( 0, 2 ) } } user={ user }> + <PostAuthorCheck authors={ users.data.slice( 0, 1 ) } user={ user }> authors </PostAuthorCheck> ); @@ -81,7 +59,7 @@ describe( 'PostAuthorCheck', () => { it( 'should render control', () => { const wrapper = shallow( - <PostAuthorCheck users={ users } user={ user }> + <PostAuthorCheck authors={ users } user={ user }> authors </PostAuthorCheck> ); diff --git a/editor/components/post-author/test/index.js b/editor/components/post-author/test/index.js index 7d34abcf9b9e1..4bce35e977b0b 100644 --- a/editor/components/post-author/test/index.js +++ b/editor/components/post-author/test/index.js @@ -9,31 +9,29 @@ import { shallow } from 'enzyme'; import { PostAuthor } from '../'; describe( 'PostAuthor', () => { - const users = { - data: [ - { - id: 1, - name: 'admin', - capabilities: { - level_1: true, - }, + const authors = [ + { + id: 1, + name: 'admin', + capabilities: { + level_1: true, }, - { - id: 2, - name: 'subscriber', - capabilities: { - level_0: true, - }, + }, + { + id: 2, + name: 'subscriber', + capabilities: { + level_0: true, }, - { - id: 3, - name: 'andrew', - capabilities: { - level_1: true, - }, + }, + { + id: 3, + name: 'andrew', + capabilities: { + level_1: true, }, - ], - }; + }, + ]; const user = { data: { @@ -43,30 +41,12 @@ describe( 'PostAuthor', () => { }, }; - describe( '#getAuthors()', () => { - it( 'returns empty array on unknown users', () => { - const wrapper = shallow( <PostAuthor users={ {} } user={ user } /> ); - - const authors = wrapper.instance().getAuthors(); - - expect( authors ).toEqual( [] ); - } ); - - it( 'filters users to authors', () => { - const wrapper = shallow( <PostAuthor users={ users } user={ user } /> ); - - const authors = wrapper.instance().getAuthors(); - - expect( authors.map( ( author ) => author.id ).sort() ).toEqual( [ 1, 3 ] ); - } ); - } ); - describe( '#render()', () => { it( 'should update author', () => { const onUpdateAuthor = jest.fn(); const wrapper = shallow( <PostAuthor - users={ users } + authors={ authors } user={ user } onUpdateAuthor={ onUpdateAuthor } /> ); diff --git a/editor/components/post-featured-image/check.js b/editor/components/post-featured-image/check.js index cc2a17e091f75..5481deaa81604 100644 --- a/editor/components/post-featured-image/check.js +++ b/editor/components/post-featured-image/check.js @@ -2,9 +2,14 @@ * Internal dependencies */ import PostTypeSupportCheck from '../post-type-support-check'; +import ThemeSupportCheck from '../theme-support-check'; function PostFeaturedImageCheck( props ) { - return <PostTypeSupportCheck { ...props } supportKeys="thumbnail" />; + return ( + <ThemeSupportCheck supportKeys="post-thumbnails"> + <PostTypeSupportCheck { ...props } supportKeys="thumbnail" /> + </ThemeSupportCheck> + ); } export default PostFeaturedImageCheck; diff --git a/editor/components/post-featured-image/index.js b/editor/components/post-featured-image/index.js index 015ac6d793257..d24c4385bf26d 100644 --- a/editor/components/post-featured-image/index.js +++ b/editor/components/post-featured-image/index.js @@ -8,7 +8,6 @@ import { get } from 'lodash'; */ import { __ } from '@wordpress/i18n'; import { Button, Spinner, ResponsiveWrapper } from '@wordpress/components'; -import { MediaUpload } from '@wordpress/blocks'; import { compose } from '@wordpress/element'; import { withSelect, withDispatch } from '@wordpress/data'; @@ -17,13 +16,14 @@ import { withSelect, withDispatch } from '@wordpress/data'; */ import './style.scss'; import PostFeaturedImageCheck from './check'; +import MediaUpload from '../media-upload'; -//used when labels from post tyoe were not yet loaded or when they are not present. +// Used when labels from post type were not yet loaded or when they are not present. const DEFAULT_SET_FEATURE_IMAGE_LABEL = __( 'Set featured image' ); const DEFAULT_REMOVE_FEATURE_IMAGE_LABEL = __( 'Remove featured image' ); function PostFeaturedImage( { featuredImageId, onUpdateImage, onRemoveImage, media, postType } ) { - const postLabel = get( postType, 'labels', {} ); + const postLabel = get( postType, [ 'labels' ], {} ); return ( <PostFeaturedImageCheck> diff --git a/editor/components/post-format/index.js b/editor/components/post-format/index.js index 92f3d40aa1c69..2353759d0893a 100644 --- a/editor/components/post-format/index.js +++ b/editor/components/post-format/index.js @@ -75,7 +75,7 @@ export default compose( [ const themeSupports = select( 'core' ).getThemeSupports(); // Ensure current format is always in the set. // The current format may not be a format supported by the theme. - const supportedFormats = union( [ postFormat ], get( themeSupports, 'formats', [] ) ); + const supportedFormats = union( [ postFormat ], get( themeSupports, [ 'formats' ], [] ) ); return { postFormat, supportedFormats, diff --git a/editor/components/post-permalink/editor.js b/editor/components/post-permalink/editor.js index b122797ff5130..18624916969a6 100644 --- a/editor/components/post-permalink/editor.js +++ b/editor/components/post-permalink/editor.js @@ -29,7 +29,7 @@ class PostPermalinkEditor extends Component { this.props.onSave(); - if ( ! postName || postName === this.props.postName ) { + if ( postName === this.props.postName ) { return; } @@ -63,7 +63,6 @@ class PostPermalinkEditor extends Component { value={ editedPostName } onChange={ ( event ) => this.setState( { editedPostName: event.target.value } ) } type="text" - required autoFocus /> <span className="editor-post-permalink-editor__suffix"> diff --git a/editor/components/post-permalink/index.js b/editor/components/post-permalink/index.js index 962ea6a7d83bb..b1ba0dec54c96 100644 --- a/editor/components/post-permalink/index.js +++ b/editor/components/post-permalink/index.js @@ -1,10 +1,15 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + /** * WordPress dependencies */ import { withDispatch, withSelect } from '@wordpress/data'; import { Component, compose } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; -import { Dashicon, ClipboardButton, Button, Tooltip } from '@wordpress/components'; +import { ClipboardButton, Button } from '@wordpress/components'; /** * Internal Dependencies @@ -21,7 +26,7 @@ class PostPermalink extends Component { this.onVisibilityChange = this.onVisibilityChange.bind( this ); this.state = { - iconClass: '', + isCopied: false, isEditingPermalink: false, }; } @@ -52,7 +57,8 @@ class PostPermalink extends Component { render() { const { isNew, previewLink, isEditable, samplePermalink, isPublished } = this.props; - const { iconClass, isEditingPermalink } = this.state; + const { isCopied, isEditingPermalink } = this.state; + const ariaLabel = isCopied ? __( 'Permalink copied' ) : __( 'Copy the permalink' ); if ( isNew || ! previewLink ) { return null; @@ -60,15 +66,14 @@ class PostPermalink extends Component { return ( <div className="editor-post-permalink"> - <Tooltip text={ __( 'Copy the permalink to your clipboard' ) }> - <ClipboardButton - className="editor-post-permalink__copy" - text={ samplePermalink } - onCopy={ () => this.setState( { iconClass: 'is-copied' } ) } - > - <Dashicon icon="admin-links" className={ iconClass } /> - </ClipboardButton> - </Tooltip> + <ClipboardButton + className={ classnames( 'editor-post-permalink__copy', { 'is-copied': isCopied } ) } + text={ samplePermalink } + label={ ariaLabel } + onCopy={ () => this.setState( { isCopied: true } ) } + aria-disabled={ isCopied } + icon="admin-links" + /> <span className="editor-post-permalink__label">{ __( 'Permalink:' ) }</span> @@ -132,4 +137,3 @@ export default compose( [ return { refreshPost }; } ), ] )( PostPermalink ); - diff --git a/editor/components/post-permalink/style.scss b/editor/components/post-permalink/style.scss index f539b70ce6120..646f22098b9df 100644 --- a/editor/components/post-permalink/style.scss +++ b/editor/components/post-permalink/style.scss @@ -19,15 +19,16 @@ } .editor-post-permalink__copy { - margin-top: 4px; + border-radius: 4px; + padding: 6px; } -.editor-post-permalink__copy .is-copied { +.editor-post-permalink__copy.is-copied { opacity: 0.3; } .editor-post-permalink__label { - margin: 0 10px; + margin: 0 10px 0 5px; } .editor-post-permalink__link { diff --git a/editor/components/post-preview-button/index.js b/editor/components/post-preview-button/index.js index 4d4022881b83a..00244ec04afd1 100644 --- a/editor/components/post-preview-button/index.js +++ b/editor/components/post-preview-button/index.js @@ -132,7 +132,7 @@ export default compose( [ isDirty: isEditedPostDirty(), isNew: isEditedPostNew(), isSaveable: isEditedPostSaveable(), - isViewable: get( postType, 'viewable', false ), + isViewable: get( postType, [ 'viewable' ], false ), modified: getEditedPostAttribute( 'modified' ), }; } ), diff --git a/editor/components/post-saved-state/index.js b/editor/components/post-saved-state/index.js index 2a49c7146b433..f535562269038 100644 --- a/editor/components/post-saved-state/index.js +++ b/editor/components/post-saved-state/index.js @@ -5,6 +5,7 @@ import { __ } from '@wordpress/i18n'; import { Dashicon, IconButton, withSafeTimeout } from '@wordpress/components'; import { Component, compose } from '@wordpress/element'; import { withSelect, withDispatch } from '@wordpress/data'; +import { keycodes } from '@wordpress/utils'; /** * Internal dependencies @@ -12,6 +13,8 @@ import { withSelect, withDispatch } from '@wordpress/data'; import './style.scss'; import PostSwitchToDraftButton from '../post-switch-to-draft-button'; +const { displayShortcut } = keycodes; + /** * Component showing whether the post is saved or not and displaying save links. * @@ -68,6 +71,7 @@ export class PostSavedState extends Component { className="editor-post-save-draft" onClick={ onSave } icon="cloud-upload" + shortcut={ displayShortcut.primary( 's' ) } > { __( 'Save Draft' ) } </IconButton> diff --git a/editor/components/post-sticky/check.js b/editor/components/post-sticky/check.js index f84631f447ae4..08fd8fbd56318 100644 --- a/editor/components/post-sticky/check.js +++ b/editor/components/post-sticky/check.js @@ -6,17 +6,13 @@ import { get } from 'lodash'; /** * WordPress dependencies */ -import { withAPIData } from '@wordpress/components'; import { compose } from '@wordpress/element'; import { withSelect } from '@wordpress/data'; -export function PostStickyCheck( { postType, children, user } ) { - const userCan = get( user.data, 'post_type_capabilities', false ); - +export function PostStickyCheck( { hasStickyAction, postType, children } ) { if ( postType !== 'post' || - ! userCan.publish_posts || - ! userCan.edit_others_posts + ! hasStickyAction ) { return null; } @@ -26,15 +22,10 @@ export function PostStickyCheck( { postType, children, user } ) { export default compose( [ withSelect( ( select ) => { + const post = select( 'core/editor' ).getCurrentPost(); return { + hasStickyAction: get( post, [ '_links', 'wp:action-sticky' ], false ), postType: select( 'core/editor' ).getCurrentPostType(), }; } ), - withAPIData( ( props ) => { - const { postType } = props; - - return { - user: `/wp/v2/users/me?post_type=${ postType }&context=edit`, - }; - } ), ] )( PostStickyCheck ); diff --git a/editor/components/post-sticky/test/index.js b/editor/components/post-sticky/test/index.js index 4d3b2c1d590ff..716b48b13f14d 100644 --- a/editor/components/post-sticky/test/index.js +++ b/editor/components/post-sticky/test/index.js @@ -9,54 +9,27 @@ import { shallow } from 'enzyme'; import { PostStickyCheck } from '../check'; describe( 'PostSticky', () => { - const user = { - data: { - post_type_capabilities: { - edit_others_posts: true, - publish_posts: true, - }, - }, - }; - it( 'should not render anything if the post type is not "post"', () => { const wrapper = shallow( - <PostStickyCheck postType="page" user={ user }> + <PostStickyCheck postType="page" hasStickyAction={ true }> Can Toggle Sticky </PostStickyCheck> ); expect( wrapper.type() ).toBe( null ); } ); - it( 'should not render anything if the user doesn\'t have the right capabilities', () => { - let wrapper = shallow( - <PostStickyCheck postType="post" user={ {} }> - Can Toggle Sticky - </PostStickyCheck> - ); - expect( wrapper.type() ).toBe( null ); - - wrapper = shallow( - <PostStickyCheck postType="post" user={ - { data: { post_type_capabilities: { edit_others_posts: false, publish_posts: true } } } - }> - Can Toggle Sticky - </PostStickyCheck> - ); - expect( wrapper.type() ).toBe( null ); - - wrapper = shallow( - <PostStickyCheck postType="post" user={ - { data: { post_type_capabilities: { edit_others_posts: true, publish_posts: false } } } - }> + it( 'should not render anything if post doesn\'t support stickying', () => { + const wrapper = shallow( + <PostStickyCheck postType="post" hasStickyAction={ false }> Can Toggle Sticky </PostStickyCheck> ); expect( wrapper.type() ).toBe( null ); } ); - it( 'should render if the user has the correct capability', () => { + it( 'should render if the post supports stickying', () => { const wrapper = shallow( - <PostStickyCheck postType="post" user={ user }> + <PostStickyCheck postType="post" hasStickyAction={ true }> Can Toggle Sticky </PostStickyCheck> ); diff --git a/editor/components/provider/index.js b/editor/components/provider/index.js index 37dd459c721a3..512b18cac625d 100644 --- a/editor/components/provider/index.js +++ b/editor/components/provider/index.js @@ -7,7 +7,7 @@ import { flow, pick } from 'lodash'; * WordPress Dependencies */ import { createElement, Component } from '@wordpress/element'; -import { RichTextProvider, EditorSettings } from '@wordpress/blocks'; +import { EditorSettings } from '@wordpress/blocks'; import { APIProvider, DropZoneProvider, @@ -15,6 +15,11 @@ import { } from '@wordpress/components'; import { withDispatch } from '@wordpress/data'; +/** + * Internal dependencies + */ +import RichTextProvider from '../rich-text/provider'; + class EditorProvider extends Component { constructor( props ) { super( ...arguments ); diff --git a/blocks/rich-text/README.md b/editor/components/rich-text/README.md similarity index 91% rename from blocks/rich-text/README.md rename to editor/components/rich-text/README.md index f5a89c2d20998..302365b026cdf 100644 --- a/blocks/rich-text/README.md +++ b/editor/components/rich-text/README.md @@ -39,7 +39,7 @@ a traditional `input` field, usually when the user exits the field. ### `onSplit( before: Array|String, after: Array|String, ...blocks: Object ): Function` -*Optional.* Called when the content can be split with `after` as the split off value. There might be blocks present, which should be inserted before the `after` value. Note: the `before` value should no longer be used. +*Optional.* Called when the content can be split with `before` and `after`. There might be blocks present, which should be inserted in between. ### `onReplace( blocks: Array ): Function` @@ -91,7 +91,7 @@ wp.blocks.registerBlockType( /* ... */, { }, edit: function( props ) { - return wp.element.createElement( wp.blocks.RichText, { + return wp.element.createElement( wp.editor.RichText, { tagName: 'h2', className: props.className, value: props.attributes.content, @@ -102,7 +102,7 @@ wp.blocks.registerBlockType( /* ... */, { }, save: function() { - return wp.element.createElement( wp.blocks.RichText.Content, { + return wp.element.createElement( wp.editor.RichText.Content, { tagName: 'h2', value: props.attributes.content } ); } @@ -110,7 +110,8 @@ wp.blocks.registerBlockType( /* ... */, { ``` {% ESNext %} ```js -const { registerBlockType, RichText } = wp.blocks; +const { registerBlockType } = wp.blocks; +const { RichText } = wp.editor; registerBlockType( /* ... */, { // ... diff --git a/blocks/rich-text/aria.js b/editor/components/rich-text/aria.js similarity index 100% rename from blocks/rich-text/aria.js rename to editor/components/rich-text/aria.js diff --git a/blocks/rich-text/constants.js b/editor/components/rich-text/constants.js similarity index 100% rename from blocks/rich-text/constants.js rename to editor/components/rich-text/constants.js diff --git a/blocks/rich-text/format-toolbar/index.js b/editor/components/rich-text/format-toolbar/index.js similarity index 85% rename from blocks/rich-text/format-toolbar/index.js rename to editor/components/rich-text/format-toolbar/index.js index 8f997a71ec438..925ba0efa8894 100644 --- a/blocks/rich-text/format-toolbar/index.js +++ b/editor/components/rich-text/format-toolbar/index.js @@ -11,33 +11,40 @@ import { withSpokenMessages, } from '@wordpress/components'; import { keycodes } from '@wordpress/utils'; +import { prependHTTP } from '@wordpress/url'; /** * Internal dependencies */ import './style.scss'; import UrlInput from '../../url-input'; -import { filterURLForDisplay } from '../../../editor/utils/url'; +import { filterURLForDisplay } from '../../../utils/url'; -const { ESCAPE, LEFT, RIGHT, UP, DOWN, BACKSPACE, ENTER } = keycodes; +const { ESCAPE, LEFT, RIGHT, UP, DOWN, BACKSPACE, ENTER, displayShortcut } = keycodes; const FORMATTING_CONTROLS = [ { icon: 'editor-bold', title: __( 'Bold' ), + shortcut: displayShortcut.primary( 'b' ), format: 'bold', }, { icon: 'editor-italic', title: __( 'Italic' ), + shortcut: displayShortcut.primary( 'i' ), format: 'italic', }, { icon: 'editor-strikethrough', title: __( 'Strikethrough' ), + shortcut: displayShortcut.access( 'd' ), format: 'strikethrough', }, { + icon: 'admin-links', + title: __( 'Link' ), + shortcut: displayShortcut.primary( 'k' ), format: 'link', }, ]; @@ -84,13 +91,16 @@ class FormatToolbar extends Component { componentWillReceiveProps( nextProps ) { if ( this.props.selectedNodeId !== nextProps.selectedNodeId ) { this.setState( { - isAddingLink: false, isEditingLink: false, settingsVisible: false, opensInNewWindow: !! nextProps.formats.link && !! nextProps.formats.link.target, newLinkValue: '', } ); } + + this.setState( { + isAddingLink: !! nextProps.formats.link && nextProps.formats.link.isAdding, + } ); } onChangeLinkValue( value ) { @@ -138,7 +148,7 @@ class FormatToolbar extends Component { event.preventDefault(); this.setState( { isEditingLink: false, isAddingLink: false, newLinkValue: '' } ); this.props.onChange( { link: { - value: this.state.newLinkValue, + value: prependHTTP( this.state.newLinkValue ), target: this.state.opensInNewWindow ? '_blank' : null, rel: this.state.opensInNewWindow ? 'noreferrer noopener' : null, } } ); @@ -178,7 +188,7 @@ class FormatToolbar extends Component { } ); const linkSettings = settingsVisible && ( - <div className="blocks-format-toolbar__link-modal-line blocks-format-toolbar__link-settings"> + <div className="editor-format-toolbar__link-modal-line editor-format-toolbar__link-settings"> <ToggleControl label={ __( 'Open in new window' ) } checked={ opensInNewWindow } @@ -187,25 +197,25 @@ class FormatToolbar extends Component { ); return ( - <div className="blocks-format-toolbar"> + <div className="editor-format-toolbar"> <Toolbar controls={ toolbarControls } /> { ( isAddingLink || isEditingLink || formats.link ) && ( <Fill name="RichText.Siblings"> - <div className="blocks-format-toolbar__link-container" style={ { ...focusPosition } }> + <div className="editor-format-toolbar__link-container" style={ { ...focusPosition } }> { ( isAddingLink || isEditingLink ) && ( // Disable reason: KeyPress must be suppressed so the block doesn't hide the toolbar /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ <form - className="blocks-format-toolbar__link-modal" + className="editor-format-toolbar__link-modal" onKeyPress={ stopKeyPropagation } onKeyDown={ this.onKeyDown } onSubmit={ this.submitLink }> - <div className="blocks-format-toolbar__link-modal-line"> + <div className="editor-format-toolbar__link-modal-line"> <UrlInput value={ newLinkValue } onChange={ this.onChangeLinkValue } /> <IconButton icon="editor-break" label={ __( 'Apply' ) } type="submit" /> <IconButton - className="blocks-format-toolbar__link-settings-toggle" + className="editor-format-toolbar__link-settings-toggle" icon="ellipsis" label={ __( 'Link Settings' ) } onClick={ this.toggleLinkSettingsVisibility } @@ -221,12 +231,12 @@ class FormatToolbar extends Component { // Disable reason: KeyPress must be suppressed so the block doesn't hide the toolbar /* eslint-disable jsx-a11y/no-static-element-interactions */ <div - className="blocks-format-toolbar__link-modal" + className="editor-format-toolbar__link-modal" onKeyPress={ stopKeyPropagation } > - <div className="blocks-format-toolbar__link-modal-line"> + <div className="editor-format-toolbar__link-modal-line"> <a - className="blocks-format-toolbar__link-value" + className="editor-format-toolbar__link-value" href={ formats.link.value } target="_blank" > @@ -234,7 +244,7 @@ class FormatToolbar extends Component { </a> <IconButton icon="edit" label={ __( 'Edit' ) } onClick={ this.editLink } /> <IconButton - className="blocks-format-toolbar__link-settings-toggle" + className="editor-format-toolbar__link-settings-toggle" icon="ellipsis" label={ __( 'Link Settings' ) } onClick={ this.toggleLinkSettingsVisibility } diff --git a/blocks/rich-text/format-toolbar/style.scss b/editor/components/rich-text/format-toolbar/style.scss similarity index 72% rename from blocks/rich-text/format-toolbar/style.scss rename to editor/components/rich-text/format-toolbar/style.scss index 4ac059e2e2805..13f20e839f222 100644 --- a/blocks/rich-text/format-toolbar/style.scss +++ b/editor/components/rich-text/format-toolbar/style.scss @@ -1,8 +1,8 @@ -.blocks-format-toolbar { +.editor-format-toolbar { display: inline-flex; } -.blocks-format-toolbar__link-modal { +.editor-format-toolbar__link-modal { position: relative; box-shadow: $shadow-popover; border: 1px solid $light-gray-500; @@ -12,7 +12,7 @@ font-family: $default-font; font-size: $default-font-size; line-height: $default-line-height; - z-index: z-index( '.blocks-format-toolbar__link-modal' ); + z-index: z-index( '.editor-format-toolbar__link-modal' ); .edit-post-header-toolbar__block-toolbar & { position: absolute; @@ -21,13 +21,13 @@ } } -.blocks-format-toolbar__link-container { +.editor-format-toolbar__link-container { position: absolute; transform: translateX( -50% ); - z-index: z-index( '.blocks-format-toolbar__link-container' ); + z-index: z-index( '.editor-format-toolbar__link-container' ); } -.blocks-format-toolbar__link-modal-line { +.editor-format-toolbar__link-modal-line { display: flex; flex-direction: row; flex-grow: 1; @@ -42,11 +42,11 @@ } } -.blocks-format-toolbar__link-settings-toggle .dashicon { +.editor-format-toolbar__link-settings-toggle .dashicon { transform: rotate(90deg); } -.blocks-format-toolbar__link-value { +.editor-format-toolbar__link-value { margin: $item-spacing - 1px; // subtract border flex-grow: 1; flex-shrink: 1; @@ -58,7 +58,7 @@ max-width: 500px; } -.blocks-format-toolbar__link-settings { +.editor-format-toolbar__link-settings { padding: 7px 8px; border-top: 1px solid $light-gray-500; padding-top: 8px; // add 1px for the border diff --git a/blocks/rich-text/format.js b/editor/components/rich-text/format.js similarity index 88% rename from blocks/rich-text/format.js rename to editor/components/rich-text/format.js index 595bff937aa8d..221966b81fa7e 100644 --- a/blocks/rich-text/format.js +++ b/editor/components/rich-text/format.js @@ -108,20 +108,3 @@ export function domToFormat( value, format, editor ) { return domToElement( value ); } } - -/** - * Checks whether the value is empty or not - * - * @param {Array|string} value Value. - * @param {string} format Format (string or element) - * - * @return {boolean} Is value empty. - */ -export function isEmpty( value, format ) { - switch ( format ) { - case 'string': - return value === ''; - default: - return ! value.length; - } -} diff --git a/blocks/rich-text/index.js b/editor/components/rich-text/index.js similarity index 85% rename from blocks/rich-text/index.js rename to editor/components/rich-text/index.js index c809effa7d7dc..200c34eacd354 100644 --- a/blocks/rich-text/index.js +++ b/editor/components/rich-text/index.js @@ -18,16 +18,23 @@ import 'element-closest'; /** * WordPress dependencies */ -import { Component, Fragment, compose, RawHTML } from '@wordpress/element'; -import { keycodes, createBlobURL, isHorizontalEdge, getRectangleFromRange, getScrollContainer } from '@wordpress/utils'; -import { withSafeTimeout, Slot } from '@wordpress/components'; +import { Component, Fragment, compose, RawHTML, createRef } from '@wordpress/element'; +import { + keycodes, + createBlobURL, + isHorizontalEdge, + getRectangleFromRange, + getScrollContainer, + deprecated, +} from '@wordpress/utils'; +import { withInstanceId, withSafeTimeout, Slot } from '@wordpress/components'; import { withSelect } from '@wordpress/data'; +import { rawHandler } from '@wordpress/blocks'; /** * Internal dependencies */ import './style.scss'; -import { rawHandler } from '../api'; import Autocomplete from '../autocomplete'; import BlockFormatControls from '../block-format-controls'; import FormatToolbar from './format-toolbar'; @@ -36,14 +43,9 @@ import { pickAriaProps } from './aria'; import patterns from './patterns'; import { EVENTS } from './constants'; import { withBlockEditContext } from '../block-edit/context'; -import { domToFormat, valueToString, isEmpty } from './format'; +import { domToFormat, valueToString } from './format'; -/** - * Browser dependencies - */ -const { console } = window; - -const { BACKSPACE, DELETE, ENTER } = keycodes; +const { BACKSPACE, DELETE, ENTER, rawShortcut } = keycodes; /** * Returns true if the node is the inline node boundary. This is used in node @@ -100,10 +102,10 @@ export function getFormatProperties( formatName, parents ) { } } -const DEFAULT_FORMATS = [ 'bold', 'italic', 'strikethrough', 'link' ]; +const DEFAULT_FORMATS = [ 'bold', 'italic', 'strikethrough', 'link', 'code' ]; export class RichText extends Component { - constructor( { value } ) { + constructor() { super( ...arguments ); this.onInit = this.onInit.bind( this ); @@ -119,14 +121,14 @@ export class RichText extends Component { this.onPastePreProcess = this.onPastePreProcess.bind( this ); this.onPaste = this.onPaste.bind( this ); this.onCreateUndoLevel = this.onCreateUndoLevel.bind( this ); + this.setFocusedElement = this.setFocusedElement.bind( this ); this.state = { formats: {}, selectedNodeId: 0, }; - this.isEmpty = ! value || ! value.length; - this.savedContent = value; + this.containerRef = createRef(); } /** @@ -159,6 +161,19 @@ export class RichText extends Component { this.editor = editor; EVENTS.forEach( ( name ) => { + if ( ! this.props.hasOwnProperty( 'on' + name ) ) { + return; + } + + deprecated( 'Raw TinyMCE event handlers for RichText', { + version: '3.0', + alternative: ( + 'Documented props, ancestor event handler, or onSetup ' + + 'access to the internal editor instance event hub' + ), + plugin: 'gutenberg', + } ); + editor.on( name, this.proxyPropHandler( name ) ); } ); @@ -181,6 +196,12 @@ export class RichText extends Component { } } + setFocusedElement() { + if ( this.props.setFocusedElement ) { + this.props.setFocusedElement( this.props.instanceId ); + } + } + /** * Allows prop event handlers to handle an event. * @@ -206,6 +227,18 @@ export class RichText extends Component { onInit() { this.registerCustomFormatters(); + + this.editor.shortcuts.add( rawShortcut.primary( 'k' ), '', () => this.changeFormats( { link: { isAdding: true } } ) ); + this.editor.shortcuts.add( rawShortcut.access( 'a' ), '', () => this.changeFormats( { link: { isAdding: true } } ) ); + this.editor.shortcuts.add( rawShortcut.access( 's' ), '', () => this.changeFormats( { link: undefined } ) ); + this.editor.shortcuts.add( rawShortcut.access( 'd' ), '', () => this.changeFormats( { strikethrough: ! this.state.formats.strikethrough } ) ); + this.editor.shortcuts.add( rawShortcut.access( 'x' ), '', () => this.changeFormats( { code: ! this.state.formats.code } ) ); + this.editor.shortcuts.add( rawShortcut.primary( 'z' ), '', 'Undo' ); + this.editor.shortcuts.add( rawShortcut.primaryShift( 'z' ), '', 'Redo' ); + + // Remove TinyMCE Core shortcut for consistency with global editor + // shortcuts. Also clashes with Mac browsers. + this.editor.shortcuts.remove( 'meta+y', '', 'Redo' ); } adaptFormatter( options ) { @@ -269,8 +302,7 @@ export class RichText extends Component { // Note: a pasted file may have the URL as plain text. if ( item && ! HTML ) { const blob = item.getAsFile ? item.getAsFile() : item; - const rootNode = this.editor.getBody(); - const isEmptyEditor = this.editor.dom.isEmpty( rootNode ); + const isEmptyEditor = this.isEmpty(); const content = rawHandler( { HTML: `<img src="${ createBlobURL( blob ) }">`, mode: 'BLOCKS', @@ -278,7 +310,7 @@ export class RichText extends Component { } ); // Allows us to ask for this information when we get a report. - console.log( 'Received item:\n\n', blob ); + window.console.log( 'Received item:\n\n', blob ); if ( isEmptyEditor && this.props.onReplace ) { // Necessary to allow the paste bin to be removed without errors. @@ -309,8 +341,8 @@ export class RichText extends Component { onPastePreProcess( event ) { const HTML = this.isPlainTextPaste ? '' : event.content; // Allows us to ask for this information when we get a report. - console.log( 'Received HTML:\n\n', HTML ); - console.log( 'Received plain text:\n\n', this.pastedPlainText ); + window.console.log( 'Received HTML:\n\n', HTML ); + window.console.log( 'Received plain text:\n\n', this.pastedPlainText ); // There is a selection, check if a link is pasted. if ( ! this.editor.selection.isCollapsed() ) { @@ -325,7 +357,7 @@ export class RichText extends Component { } ); // Allows us to ask for this information when we get a report. - console.log( 'Created link:\n\n', pastedText ); + window.console.log( 'Created link:\n\n', pastedText ); event.preventDefault(); @@ -333,8 +365,7 @@ export class RichText extends Component { } } - const rootNode = this.editor.getBody(); - const isEmptyEditor = this.editor.dom.isEmpty( rootNode ); + const isEmptyEditor = this.isEmpty(); let mode = 'INLINE'; @@ -363,7 +394,7 @@ export class RichText extends Component { return; } - if ( isEmpty && this.props.onReplace ) { + if ( isEmptyEditor && this.props.onReplace ) { this.props.onReplace( content ); } else { this.splitContent( content ); @@ -377,7 +408,6 @@ export class RichText extends Component { onChange() { this.savedContent = this.getContent(); - this.isEmpty = isEmpty( this.savedContent, this.props.format ); this.props.onChange( this.savedContent ); } @@ -392,24 +422,15 @@ export class RichText extends Component { * * Based on the selection of the text inside this element a position is * calculated where the toolbar should be. This can be used downstream to - * absolutely position the toolbar. It does this by finding the closest - * relative element. + * absolutely position the toolbar. * * @param {DOMRect} position Caret range rectangle. * * @return {{top: number, left: number}} The desired position of the toolbar. */ getFocusPosition( position ) { - // Find the parent "relative" or "absolute" positioned container - const findRelativeParent = ( node ) => { - const style = window.getComputedStyle( node ); - if ( style.position === 'relative' || style.position === 'absolute' ) { - return node; - } - return findRelativeParent( node.parentNode ); - }; - const container = findRelativeParent( this.editor.getBody() ); - const containerPosition = container.getBoundingClientRect(); + // The container is relatively positioned. + const containerPosition = this.containerRef.current.getBoundingClientRect(); const toolbarOffset = { top: 10, left: 0 }; return { @@ -443,7 +464,7 @@ export class RichText extends Component { this.props.onMerge( forward ); } - if ( this.props.onRemove && dom.isEmpty( rootNode ) ) { + if ( this.props.onRemove && this.isEmpty() ) { this.props.onRemove( forward ); } @@ -493,10 +514,6 @@ export class RichText extends Component { if ( event.shiftKey || ! this.props.onSplit ) { this.editor.execCommand( 'InsertLineBreak', false, event ); } else { - // Splitting the content might destroy the editor, so it's - // important that we stop other handlers (e.g. ones - // registered by TinyMCE) from also handling this event. - event.stopImmediatePropagation(); this.splitContent(); } } @@ -581,7 +598,7 @@ export class RichText extends Component { const afterFragment = afterRange.extractContents(); const { format } = this.props; - const before = domToFormat( beforeFragment.childNodes, format, this.editor ); + const before = domToFormat( filterEmptyNodes( beforeFragment.childNodes ), format, this.editor ); const after = domToFormat( filterEmptyNodes( afterFragment.childNodes ), format, this.editor ); this.restoreContentAndSplit( before, after, blocks ); @@ -629,8 +646,10 @@ export class RichText extends Component { return memo; }, [] ); - const { format } = this.props; + // Splitting into two blocks + this.setContent( this.props.value ); + const { format } = this.props; this.restoreContentAndSplit( domToFormat( before, format, this.editor ), domToFormat( after, format, this.editor ) @@ -709,16 +728,15 @@ export class RichText extends Component { !! this.editor && this.props.tagName === prevProps.tagName && this.props.value !== prevProps.value && - this.props.value !== this.savedContent + this.props.value !== this.savedContent && + + // Comparing using isEqual is necessary especially to avoid unnecessary updateContent calls + // This fixes issues in multi richText blocks like quotes when moving the focus between + // the different editables. + ! isEqual( this.props.value, prevProps.value ) && + ! isEqual( this.props.value, this.savedContent ) ) { this.updateContent(); - - if ( - 'development' === process.env.NODE_ENV && - isEqual( this.props.value, prevProps.value ) - ) { - console.warn( 'The current and previous value props are not strictly equal but the contents are the same. Please ensure the value prop reference does not change.' ); - } } } @@ -731,6 +749,16 @@ export class RichText extends Component { } } + /** + * Returns true if the field is currently empty, or false otherwise. + * + * @return {boolean} Whether field is empty. + */ + isEmpty() { + const { value } = this.props; + return ! value || ! value.length; + } + isFormatActive( format ) { return this.state.formats[ format ] && this.state.formats[ format ].isActive; } @@ -749,6 +777,10 @@ export class RichText extends Component { forEach( formats, ( formatValue, format ) => { if ( format === 'link' ) { if ( formatValue !== undefined ) { + if ( formatValue.isAdding ) { + return; + } + const anchor = this.editor.dom.getParent( this.editor.selection.getNode(), 'a' ); if ( ! anchor ) { this.removeFormat( 'link' ); @@ -775,15 +807,14 @@ export class RichText extends Component { /** * Calling onSplit means we need to abort the change done by TinyMCE. - * we need to call setContent to restore the initial content before calling onSplit. + * we need to call updateContent to restore the initial content before calling onSplit. * * @param {Array} before content before the split position * @param {Array} after content after the split position * @param {?Array} blocks blocks to insert at the split position */ restoreContentAndSplit( before, after, blocks = [] ) { - this.setContent( before ); - this.onChange(); + this.updateContent(); this.props.onSplit( before, after, ...blocks ); } @@ -811,8 +842,8 @@ export class RichText extends Component { // changes, we unmount and destroy the previous TinyMCE element, then // mount and initialize a new child element in its place. const key = [ 'editor', Tagname ].join(); - const isPlaceholderVisible = placeholder && ( ! isSelected || keepPlaceholderOnFocus ) && this.isEmpty; - const classes = classnames( wrapperClassName, 'blocks-rich-text' ); + const isPlaceholderVisible = placeholder && ( ! isSelected || keepPlaceholderOnFocus ) && this.isEmpty(); + const classes = classnames( wrapperClassName, 'editor-rich-text' ); const formatToolbar = ( <FormatToolbar @@ -826,7 +857,10 @@ export class RichText extends Component { ); return ( - <div className={ classes }> + <div className={ classes } + ref={ this.containerRef } + onFocus={ this.setFocusedElement } + > { isSelected && ! inlineToolbar && ( <BlockFormatControls> { formatToolbar } @@ -859,7 +893,7 @@ export class RichText extends Component { /> { isPlaceholderVisible && <Tagname - className={ classnames( 'blocks-rich-text__tinymce', className ) } + className={ classnames( 'editor-rich-text__tinymce', className ) } style={ style } > { MultilineTag ? <MultilineTag>{ placeholder }</MultilineTag> : placeholder } @@ -888,13 +922,29 @@ RichText.defaultProps = { }; const RichTextContainer = compose( [ - withBlockEditContext, - withSelect( ( select, { isSelected, blockEditContext } ) => { + withInstanceId, + withBlockEditContext( ( context, ownProps ) => { + // When explicitly set as not selected, do nothing. + if ( ownProps.isSelected === false ) { + return {}; + } + // When explicitly set as selected, use the value stored in the context instead. + if ( ownProps.isSelected === true ) { + return { + isSelected: context.isSelected, + }; + } + // Ensures that only one RichText component can be focused. + return { + isSelected: context.isSelected && context.focusedElement === ownProps.instanceId, + setFocusedElement: context.setFocusedElement, + }; + } ), + withSelect( ( select ) => { const { isViewportMatch = identity } = select( 'core/viewport' ) || {}; return { isViewportSmall: isViewportMatch( '< small' ), - isSelected: isSelected !== false && blockEditContext.isSelected, }; } ), withSafeTimeout, diff --git a/blocks/rich-text/patterns.js b/editor/components/rich-text/patterns.js similarity index 98% rename from blocks/rich-text/patterns.js rename to editor/components/rich-text/patterns.js index 5277e3531d876..e7e4ae0d4addc 100644 --- a/blocks/rich-text/patterns.js +++ b/editor/components/rich-text/patterns.js @@ -8,11 +8,11 @@ import { filter, escapeRegExp, groupBy, drop } from 'lodash'; * WordPress dependencies */ import { keycodes } from '@wordpress/utils'; +import { getBlockTransforms, findTransform } from '@wordpress/blocks'; /** * Internal dependencies */ -import { getBlockTransforms, findTransform } from '../api/factory'; const { ESCAPE, ENTER, SPACE, BACKSPACE } = keycodes; diff --git a/blocks/rich-text/provider.js b/editor/components/rich-text/provider.js similarity index 100% rename from blocks/rich-text/provider.js rename to editor/components/rich-text/provider.js diff --git a/blocks/rich-text/style.scss b/editor/components/rich-text/style.scss similarity index 91% rename from blocks/rich-text/style.scss rename to editor/components/rich-text/style.scss index c081d35bd4070..0e749d976865c 100644 --- a/blocks/rich-text/style.scss +++ b/editor/components/rich-text/style.scss @@ -1,8 +1,10 @@ -.blocks-rich-text { + +.editor-rich-text { + // This is needed to position the formatting toolbar. position: relative; } -.blocks-rich-text__tinymce { +.editor-rich-text__tinymce { margin: 0; position: relative; @@ -70,7 +72,7 @@ } } - & + .blocks-rich-text__tinymce { + & + .editor-rich-text__tinymce { opacity: 0.5; pointer-events: none; } diff --git a/blocks/rich-text/test/__snapshots__/format.js.snap b/editor/components/rich-text/test/__snapshots__/format.js.snap similarity index 100% rename from blocks/rich-text/test/__snapshots__/format.js.snap rename to editor/components/rich-text/test/__snapshots__/format.js.snap diff --git a/blocks/rich-text/test/format.js b/editor/components/rich-text/test/format.js similarity index 100% rename from blocks/rich-text/test/format.js rename to editor/components/rich-text/test/format.js diff --git a/blocks/rich-text/test/index.js b/editor/components/rich-text/test/index.js similarity index 100% rename from blocks/rich-text/test/index.js rename to editor/components/rich-text/test/index.js diff --git a/blocks/rich-text/tinymce.js b/editor/components/rich-text/tinymce.js similarity index 97% rename from blocks/rich-text/tinymce.js rename to editor/components/rich-text/tinymce.js index 986040ea8006e..c88044cf12e4c 100644 --- a/blocks/rich-text/tinymce.js +++ b/editor/components/rich-text/tinymce.js @@ -46,7 +46,7 @@ export default class TinyMCE extends Component { } if ( ! isEqual( this.props.className, nextProps.className ) ) { - this.editorNode.className = classnames( nextProps.className, 'blocks-rich-text__tinymce' ); + this.editorNode.className = classnames( nextProps.className, 'editor-rich-text__tinymce' ); } const { removedKeys, updatedKeys } = diffAriaProps( this.props, nextProps ); @@ -109,7 +109,7 @@ export default class TinyMCE extends Component { return createElement( tagName, { ...ariaProps, - className: classnames( className, 'blocks-rich-text__tinymce' ), + className: classnames( className, 'editor-rich-text__tinymce' ), contentEditable: true, [ IS_PLACEHOLDER_VISIBLE_ATTR_NAME ]: isPlaceholderVisible, ref: ( node ) => this.editorNode = node, diff --git a/editor/components/table-of-contents/style.scss b/editor/components/table-of-contents/style.scss index d7b6947450ee0..e5b70ec131309 100644 --- a/editor/components/table-of-contents/style.scss +++ b/editor/components/table-of-contents/style.scss @@ -5,6 +5,12 @@ .table-of-contents__popover { .components-popover__content { padding: 16px; + + @include break-small { + max-height: calc(100vh - 120px); + overflow-y: auto; + box-shadow: inset 0 -5px 5px -4px rgba( $dark-gray-900, .1 ); + } } hr { diff --git a/editor/components/theme-support-check/index.js b/editor/components/theme-support-check/index.js new file mode 100644 index 0000000000000..3aa5075b2bbf4 --- /dev/null +++ b/editor/components/theme-support-check/index.js @@ -0,0 +1,28 @@ +/** + * External dependencies + */ +import { get, some, castArray } from 'lodash'; + +/** + * WordPress dependencies + */ +import { withSelect } from '@wordpress/data'; + +export function ThemeSupportCheck( { themeSupports, children, supportKeys } ) { + const isSupported = some( + castArray( supportKeys ), ( key ) => get( themeSupports, [ key ], false ) + ); + + if ( ! isSupported ) { + return null; + } + + return children; +} + +export default withSelect( ( select ) => { + const { getThemeSupports } = select( 'core' ); + return { + themeSupports: getThemeSupports(), + }; +} )( ThemeSupportCheck ); diff --git a/editor/components/theme-support-check/test/index.js b/editor/components/theme-support-check/test/index.js new file mode 100644 index 0000000000000..30bd1bd457795 --- /dev/null +++ b/editor/components/theme-support-check/test/index.js @@ -0,0 +1,38 @@ +/** + * External dependencies + */ +import { shallow } from 'enzyme'; + +/** + * Internal dependencies + */ +import { ThemeSupportCheck } from '../index'; + +describe( 'ThemeSupportCheck', () => { + it( 'should not render if there\'s no support check provided', () => { + const wrapper = shallow( <ThemeSupportCheck>foobar</ThemeSupportCheck> ); + expect( wrapper.type() ).toBe( null ); + } ); + + it( 'should render if post-thumbnails are supported', () => { + const themeSupports = { + 'post-thumbnails': true, + }; + const supportKeys = 'post-thumbnails'; + const wrapper = shallow( <ThemeSupportCheck + supportKeys={ supportKeys } + themeSupports={ themeSupports }>foobar</ThemeSupportCheck> ); + expect( wrapper.type() ).not.toBe( null ); + } ); + + it( 'should not render if theme doesn\'t support post-thumbnails', () => { + const themeSupports = { + 'post-thumbnails': false, + }; + const supportKeys = 'post-thumbnails'; + const wrapper = shallow( <ThemeSupportCheck + supportKeys={ supportKeys } + themeSupports={ themeSupports }>foobar</ThemeSupportCheck> ); + expect( wrapper.type() ).toBe( null ); + } ); +} ); diff --git a/blocks/url-input/button.js b/editor/components/url-input/button.js similarity index 88% rename from blocks/url-input/button.js rename to editor/components/url-input/button.js index cad6ee2ed8a74..0ebd3b835f867 100644 --- a/blocks/url-input/button.js +++ b/editor/components/url-input/button.js @@ -41,7 +41,7 @@ class UrlInputButton extends Component { const buttonLabel = url ? __( 'Edit Link' ) : __( 'Insert Link' ); return ( - <div className="blocks-url-input__button"> + <div className="editor-url-input__button"> <IconButton icon="admin-links" label={ buttonLabel } @@ -52,11 +52,11 @@ class UrlInputButton extends Component { /> { expanded && <form - className="blocks-format-toolbar__link-modal" + className="editor-format-toolbar__link-modal" onSubmit={ this.submitLink }> - <div className="blocks-format-toolbar__link-modal-line"> + <div className="editor-format-toolbar__link-modal-line"> <IconButton - className="blocks-url-input__back" + className="editor-url-input__back" icon="arrow-left-alt" label={ __( 'Close' ) } onClick={ this.toggle } diff --git a/blocks/url-input/index.js b/editor/components/url-input/index.js similarity index 87% rename from blocks/url-input/index.js rename to editor/components/url-input/index.js index de1064948e248..294076f7990bc 100644 --- a/blocks/url-input/index.js +++ b/editor/components/url-input/index.js @@ -51,8 +51,9 @@ class UrlInput extends Component { this.suggestionsRequest.abort(); } - // Show the suggestions after typing at least 3 characters - if ( value.length < 2 ) { + // Show the suggestions after typing at least 2 characters + // and also for URLs + if ( value.length < 2 || /^https?:/.test( value ) ) { this.setState( { showSuggestions: false, selectedSuggestion: null, @@ -111,10 +112,10 @@ class UrlInput extends Component { } onKeyDown( event ) { - const { selectedSuggestion, posts } = this.state; - // If the suggestions are not shown, we shouldn't handle the arrow keys + const { showSuggestions, selectedSuggestion, posts, loading } = this.state; + // If the suggestions are not shown or loading, we shouldn't handle the arrow keys // We shouldn't preventDefault to allow block arrow keys navigation - if ( ! this.state.showSuggestions || ! this.state.posts.length ) { + if ( ! showSuggestions || ! posts.length || loading ) { return; } @@ -182,7 +183,7 @@ class UrlInput extends Component { const { showSuggestions, posts, selectedSuggestion, loading } = this.state; /* eslint-disable jsx-a11y/no-autofocus */ return ( - <div className="blocks-url-input"> + <div className="editor-url-input"> <input autoFocus type="text" @@ -196,17 +197,17 @@ class UrlInput extends Component { role="combobox" aria-expanded={ showSuggestions } aria-autocomplete="list" - aria-owns={ `blocks-url-input-suggestions-${ instanceId }` } - aria-activedescendant={ selectedSuggestion !== null ? `blocks-url-input-suggestion-${ instanceId }-${ selectedSuggestion }` : undefined } + aria-owns={ `editor-url-input-suggestions-${ instanceId }` } + aria-activedescendant={ selectedSuggestion !== null ? `editor-url-input-suggestion-${ instanceId }-${ selectedSuggestion }` : undefined } /> { ( loading ) && <Spinner /> } { showSuggestions && !! posts.length && <div - id={ `blocks-url-input-suggestions-${ instanceId }` } + id={ `editor-url-input-suggestions-${ instanceId }` } tabIndex="-1" - className="blocks-url-input__suggestions" + className="editor-url-input__suggestions" ref={ this.bindListNode } role="listbox" > @@ -215,9 +216,9 @@ class UrlInput extends Component { key={ post.id } role="option" tabIndex="-1" - id={ `blocks-url-input-suggestion-${ instanceId }-${ index }` } + id={ `editor-url-input-suggestion-${ instanceId }-${ index }` } ref={ this.bindSuggestionNode( index ) } - className={ classnames( 'blocks-url-input__suggestion', { + className={ classnames( 'editor-url-input__suggestion', { 'is-selected': index === selectedSuggestion, } ) } onClick={ () => this.selectLink( post.link ) } diff --git a/blocks/url-input/style.scss b/editor/components/url-input/style.scss similarity index 81% rename from blocks/url-input/style.scss rename to editor/components/url-input/style.scss index 50b60bc9d3267..4f82c594047ad 100644 --- a/blocks/url-input/style.scss +++ b/editor/components/url-input/style.scss @@ -2,8 +2,8 @@ $input-padding: 10px; $input-size: 230px; -.editor-block-list__block .blocks-url-input, -.editor-block-toolbar .blocks-url-input { +.editor-block-list__block .editor-url-input, +.editor-block-toolbar .editor-url-input { width: 100%; flex-grow: 1; position: relative; @@ -29,7 +29,7 @@ $input-size: 230px; } // Suggestions -.blocks-url-input__suggestions { +.editor-url-input__suggestions { position: absolute; background: $white; box-shadow: $shadow-popover; @@ -44,15 +44,15 @@ $input-size: 230px; } // Hide suggestions on mobile until we @todo find a better way to show them -.blocks-url-input__suggestions, -.blocks-url-input .spinner { +.editor-url-input__suggestions, +.editor-url-input .spinner { display: none; @include break-small() { display: inherit; } } -.blocks-url-input__suggestion { +.editor-url-input__suggestion { padding: 4px #{ $icon-button-size + $input-padding } 4px $input-padding; color: $dark-gray-300; // lightest we can use for contrast display: block; @@ -77,11 +77,11 @@ $input-size: 230px; } // Toolbar button -.components-toolbar > .blocks-url-input__button { +.components-toolbar > .editor-url-input__button { position: inherit; // let the dialog position according to parent } -.blocks-url-input__button .blocks-url-input__back { +.editor-url-input__button .editor-url-input__back { margin-right: 4px; overflow: visible; @@ -96,7 +96,7 @@ $input-size: 230px; } } -.blocks-url-input__button .blocks-url-input__suggestions { +.editor-url-input__button .editor-url-input__suggestions { left: -40px; right: -32px; } diff --git a/blocks/url-input/test/button.js b/editor/components/url-input/test/button.js similarity index 95% rename from blocks/url-input/test/button.js rename to editor/components/url-input/test/button.js index 09c3898634a7f..19bfc72c36b9c 100644 --- a/blocks/url-input/test/button.js +++ b/editor/components/url-input/test/button.js @@ -13,7 +13,7 @@ describe( 'UrlInputButton', () => { it( 'should have a valid class name in the wrapper tag', () => { const wrapper = shallow( <UrlInputButton /> ); - expect( wrapper.hasClass( 'blocks-url-input__button' ) ).toBe( true ); + expect( wrapper.hasClass( 'editor-url-input__button' ) ).toBe( true ); } ); it( 'should not have is-active class when url prop not defined', () => { const wrapper = shallow( <UrlInputButton /> ); @@ -53,7 +53,7 @@ describe( 'UrlInputButton', () => { const wrapper = shallow( <UrlInputButton /> ); clickEditLink( wrapper ); expect( wrapper.state().expanded ).toBe( true ); - wrapper.find( '.blocks-url-input__back' ).simulate( 'click' ); + wrapper.find( '.editor-url-input__back' ).simulate( 'click' ); expect( wrapper.state().expanded ).toBe( false ); } ); it( 'should close the form when user submits it', () => { diff --git a/editor/components/writing-flow/index.js b/editor/components/writing-flow/index.js index 48f7c08ce305d..45fe750446e2f 100644 --- a/editor/components/writing-flow/index.js +++ b/editor/components/writing-flow/index.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { overEvery, find, findLast, reverse, get } from 'lodash'; +import { overEvery, find, findLast, reverse } from 'lodash'; /** * WordPress dependencies @@ -137,19 +137,26 @@ class WritingFlow extends Component { return find( focusableNodes, isTabCandidate ); } - expandSelection( currentStartUid, isReverse ) { - const { previousBlockUid, nextBlockUid } = this.props; + expandSelection( isReverse ) { + const { + selectedBlockUID, + selectionStartUID, + selectionBeforeEndUID, + selectionAfterEndUID, + } = this.props; + + const nextSelectionEndUID = isReverse ? selectionBeforeEndUID : selectionAfterEndUID; - const expandedBlockUid = isReverse ? previousBlockUid : nextBlockUid; - if ( expandedBlockUid ) { - this.props.onMultiSelect( currentStartUid, expandedBlockUid ); + if ( nextSelectionEndUID ) { + this.props.onMultiSelect( selectionStartUID || selectedBlockUID, nextSelectionEndUID ); } } moveSelection( isReverse ) { - const { previousBlockUid, nextBlockUid } = this.props; + const { selectedFirstUid, selectedLastUid } = this.props; + + const focusedBlockUid = isReverse ? selectedFirstUid : selectedLastUid; - const focusedBlockUid = isReverse ? previousBlockUid : nextBlockUid; if ( focusedBlockUid ) { this.props.onSelectBlock( focusedBlockUid ); } @@ -168,11 +175,11 @@ class WritingFlow extends Component { */ isTabbableEdge( target, isReverse ) { const closestTabbable = this.getClosestTabbable( target, isReverse ); - return ! isInSameBlock( target, closestTabbable ); + return ! closestTabbable || ! isInSameBlock( target, closestTabbable ); } onKeyDown( event ) { - const { selectedBlockUID, selectionStart, hasMultiSelection } = this.props; + const { hasMultiSelection } = this.props; const { keyCode, target } = event; const isUp = keyCode === UP; @@ -193,20 +200,24 @@ class WritingFlow extends Component { this.verticalRect = computeCaretRect( target ); } - if ( isNav && isShift && hasMultiSelection ) { - // Shift key is down and existing block multi-selection - event.preventDefault(); - this.expandSelection( selectionStart, isReverse ); - } else if ( isNav && isShift && this.isTabbableEdge( target, isReverse ) && isNavEdge( target, isReverse, true ) ) { - // Shift key is down, but no existing block multi-selection + if ( ! isNav ) { + return; + } + + if ( isShift && ( hasMultiSelection || ( + this.isTabbableEdge( target, isReverse ) && + isNavEdge( target, isReverse, true ) + ) ) ) { + // Shift key is down, and there is multi selection or we're at the end of the current block. + this.expandSelection( isReverse ); event.preventDefault(); - this.expandSelection( selectedBlockUID, isReverse ); - } else if ( isNav && hasMultiSelection ) { + } else if ( hasMultiSelection ) { // Moving from block multi-selection to single block selection - event.preventDefault(); this.moveSelection( isReverse ); + event.preventDefault(); } else if ( isVertical && isVerticalEdge( target, isReverse, isShift ) ) { const closestTabbable = this.getClosestTabbable( target, isReverse ); + if ( closestTabbable ) { placeCaretAtVerticalEdge( closestTabbable, isReverse, this.verticalRect ); event.preventDefault(); @@ -259,18 +270,28 @@ class WritingFlow extends Component { export default compose( [ withSelect( ( select ) => { const { + getSelectedBlockUID, + getMultiSelectedBlocksStartUid, + getMultiSelectedBlocksEndUid, getPreviousBlockUid, getNextBlockUid, - getMultiSelectedBlocksStartUid, - getMultiSelectedBlocks, - getSelectedBlock, + getFirstMultiSelectedBlockUid, + getLastMultiSelectedBlockUid, + hasMultiSelection, } = select( 'core/editor' ); + + const selectedBlockUID = getSelectedBlockUID(); + const selectionStartUID = getMultiSelectedBlocksStartUid(); + const selectionEndUID = getMultiSelectedBlocksEndUid(); + return { - previousBlockUid: getPreviousBlockUid(), - nextBlockUid: getNextBlockUid(), - selectionStart: getMultiSelectedBlocksStartUid(), - hasMultiSelection: getMultiSelectedBlocks().length > 1, - selectedBlockUID: get( getSelectedBlock(), [ 'uid' ] ), + selectedBlockUID, + selectionStartUID, + selectionBeforeEndUID: getPreviousBlockUid( selectionEndUID || selectedBlockUID ), + selectionAfterEndUID: getNextBlockUid( selectionEndUID || selectedBlockUID ), + selectedFirstUid: getFirstMultiSelectedBlockUid(), + selectedLastUid: getLastMultiSelectedBlockUid(), + hasMultiSelection: hasMultiSelection(), }; } ), withDispatch( ( dispatch ) => { diff --git a/editor/deprecated.js b/editor/deprecated.js new file mode 100644 index 0000000000000..1db630e393996 --- /dev/null +++ b/editor/deprecated.js @@ -0,0 +1,95 @@ +/** + * External dependencies + */ +import { forEach } from 'lodash'; + +/** + * WordPress dependencies + */ +import { Component } from '@wordpress/element'; +import { deprecated } from '@wordpress/utils'; + +import { + Autocomplete, + AlignmentToolbar, + BlockAlignmentToolbar, + BlockControls, + BlockEdit, + BlockFormatControls, + BlockIcon, + ColorPalette, + ContrastChecker, + ImagePlaceholder, + InnerBlocks, + InspectorAdvancedControls, + InspectorControls, + PlainText, + RichText, + RichTextProvider, + MediaUpload, + UrlInput, + UrlInputButton, + withColorContext, + getColorClass, + getColorName, + withColors, +} from './components'; + +const componentsToDepreacate = { + Autocomplete, + AlignmentToolbar, + BlockAlignmentToolbar, + BlockControls, + BlockEdit, + BlockFormatControls, + BlockIcon, + ColorPalette, + ContrastChecker, + ImagePlaceholder, + InnerBlocks, + InspectorAdvancedControls, + InspectorControls, + PlainText, + RichText, + RichTextProvider, + MediaUpload, + UrlInput, + UrlInputButton, +}; + +const functionsToDeprecate = { + withColorContext, + getColorClass, + getColorName, + withColors, +}; + +forEach( componentsToDepreacate, ( WrappedComponent, key ) => { + wp.blocks[ key ] = class extends Component { + constructor() { + super( ...arguments ); + + deprecated( 'wp.blocks.' + key, { + version: '3.1', + alternative: 'wp.editor.' + key, + plugin: 'Gutenberg', + } ); + } + + render() { + return <WrappedComponent { ...this.props } />; + } + }; +} ); + +forEach( functionsToDeprecate, ( wrappedFunction, key ) => { + wp.blocks[ key ] = ( ...args ) => { + deprecated( 'wp.blocks.' + key, { + version: '3.1', + alternative: 'wp.editor.' + key, + plugin: 'Gutenberg', + } ); + return wrappedFunction( ...args ); + }; +} ); + diff --git a/blocks/hooks/align.js b/editor/hooks/align.js similarity index 95% rename from blocks/hooks/align.js rename to editor/hooks/align.js index b724a6ef4f45d..4341d5a9299ea 100644 --- a/blocks/hooks/align.js +++ b/editor/hooks/align.js @@ -9,13 +9,12 @@ import { assign, includes } from 'lodash'; */ import { createHigherOrderComponent } from '@wordpress/element'; import { addFilter } from '@wordpress/hooks'; -import BlockControls from '../block-controls'; -import BlockAlignmentToolbar from '../block-alignment-toolbar'; +import { hasBlockSupport, getBlockSupport } from '@wordpress/blocks'; /** * Internal dependencies */ -import { getBlockSupport, hasBlockSupport } from '../api'; +import { BlockControls, BlockAlignmentToolbar } from '../components'; /** * Filters registered block settings, extending attributes to include `align`. diff --git a/blocks/hooks/anchor.js b/editor/hooks/anchor.js similarity index 96% rename from blocks/hooks/anchor.js rename to editor/hooks/anchor.js index a61b194f144a6..2eb54e44aeb9d 100644 --- a/blocks/hooks/anchor.js +++ b/editor/hooks/anchor.js @@ -10,12 +10,12 @@ import { createHigherOrderComponent, Fragment } from '@wordpress/element'; import { addFilter } from '@wordpress/hooks'; import { TextControl } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; +import { hasBlockSupport } from '@wordpress/blocks'; /** * Internal dependencies */ -import { hasBlockSupport } from '../api'; -import InspectorAdvancedControls from '../inspector-advanced-controls'; +import { InspectorAdvancedControls } from '../components'; /** * Regular expression matching invalid anchor characters for replacement. diff --git a/blocks/hooks/custom-class-name.js b/editor/hooks/custom-class-name.js similarity index 96% rename from blocks/hooks/custom-class-name.js rename to editor/hooks/custom-class-name.js index bd58312857fcb..86bf4e45a3918 100644 --- a/blocks/hooks/custom-class-name.js +++ b/editor/hooks/custom-class-name.js @@ -11,12 +11,12 @@ import { createHigherOrderComponent, Fragment } from '@wordpress/element'; import { addFilter } from '@wordpress/hooks'; import { TextControl } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; +import { hasBlockSupport } from '@wordpress/blocks'; /** * Internal dependencies */ -import { hasBlockSupport } from '../api'; -import InspectorAdvancedControls from '../inspector-advanced-controls'; +import { InspectorAdvancedControls } from '../components'; /** * Filters registered block settings, extending attributes with anchor using ID diff --git a/blocks/hooks/default-autocompleters.js b/editor/hooks/default-autocompleters.js similarity index 60% rename from blocks/hooks/default-autocompleters.js rename to editor/hooks/default-autocompleters.js index 1a37ce45a8f84..8e132e417bfa8 100644 --- a/blocks/hooks/default-autocompleters.js +++ b/editor/hooks/default-autocompleters.js @@ -7,18 +7,23 @@ import { clone } from 'lodash'; * WordPress dependencies */ import { addFilter } from '@wordpress/hooks'; +import { getDefaultBlockName } from '@wordpress/blocks'; /** * Internal dependencies */ -import { userAutocompleter } from '../autocompleters'; +import { blockAutocompleter, userAutocompleter } from '../components'; const defaultAutocompleters = [ userAutocompleter ]; -function setDefaultCompleters( completers ) { +function setDefaultCompleters( completers, blockName ) { if ( ! completers ) { // Provide copies so filters may directly modify them. completers = defaultAutocompleters.map( clone ); + // Add blocks autocompleter for Paragraph block + if ( blockName === getDefaultBlockName() ) { + completers.push( clone( blockAutocompleter ) ); + } } return completers; } diff --git a/blocks/hooks/generated-class-name.js b/editor/hooks/generated-class-name.js similarity index 92% rename from blocks/hooks/generated-class-name.js rename to editor/hooks/generated-class-name.js index 4c7b723d7b296..c5f7d1b49502c 100644 --- a/blocks/hooks/generated-class-name.js +++ b/editor/hooks/generated-class-name.js @@ -7,11 +7,7 @@ import { uniq } from 'lodash'; * WordPress dependencies */ import { addFilter } from '@wordpress/hooks'; - -/** - * Internal dependencies - */ -import { hasBlockSupport, getBlockDefaultClassName } from '../api'; +import { hasBlockSupport, getBlockDefaultClassName } from '@wordpress/blocks'; /** * Override props assigned to save component to inject generated className if diff --git a/blocks/hooks/index.js b/editor/hooks/index.js similarity index 100% rename from blocks/hooks/index.js rename to editor/hooks/index.js diff --git a/blocks/hooks/layout.js b/editor/hooks/layout.js similarity index 100% rename from blocks/hooks/layout.js rename to editor/hooks/layout.js diff --git a/blocks/hooks/test/align.js b/editor/hooks/test/align.js similarity index 99% rename from blocks/hooks/test/align.js rename to editor/hooks/test/align.js index 2deb57197b52d..ea4c491d98c1d 100644 --- a/blocks/hooks/test/align.js +++ b/editor/hooks/test/align.js @@ -8,15 +8,15 @@ import { noop } from 'lodash'; * WordPress dependencies */ import { applyFilters } from '@wordpress/hooks'; - -/** - * Internal dependencies - */ import { getBlockTypes, registerBlockType, unregisterBlockType, -} from '../../api'; +} from '@wordpress/blocks'; + +/** + * Internal dependencies + */ import { getBlockValidAlignments, withToolbarControls, diff --git a/blocks/hooks/test/anchor.js b/editor/hooks/test/anchor.js similarity index 100% rename from blocks/hooks/test/anchor.js rename to editor/hooks/test/anchor.js diff --git a/blocks/hooks/test/custom-class-name.js b/editor/hooks/test/custom-class-name.js similarity index 100% rename from blocks/hooks/test/custom-class-name.js rename to editor/hooks/test/custom-class-name.js diff --git a/blocks/hooks/test/default-autocompleters.js b/editor/hooks/test/default-autocompleters.js similarity index 89% rename from blocks/hooks/test/default-autocompleters.js rename to editor/hooks/test/default-autocompleters.js index 8595f5e0a315d..8afe8c43a6c4b 100644 --- a/blocks/hooks/test/default-autocompleters.js +++ b/editor/hooks/test/default-autocompleters.js @@ -7,13 +7,14 @@ import { applyFilters } from '@wordpress/hooks'; * Internal dependencies */ import '../default-autocompleters'; -import { userAutocompleter } from '../../autocompleters'; +import { userAutocompleter } from '../../components'; describe( 'default-autocompleters', () => { + const BLOCK_NAME = 'core/foo'; const defaultAutocompleters = [ userAutocompleter ]; it( 'provides default completers if none are provided', () => { - const result = applyFilters( 'blocks.Autocomplete.completers', null ); + const result = applyFilters( 'blocks.Autocomplete.completers', null, BLOCK_NAME ); /* * Assert structural equality because defaults are provided as a * list of cloned completers (and not referentially equal). @@ -23,20 +24,20 @@ describe( 'default-autocompleters', () => { it( 'does not provide default completers for empty completer list', () => { const emptyList = []; - const result = applyFilters( 'blocks.Autocomplete.completers', emptyList ); + const result = applyFilters( 'blocks.Autocomplete.completers', emptyList, BLOCK_NAME ); // Assert referential equality because the list should be unchanged. expect( result ).toBe( emptyList ); } ); it( 'does not provide default completers for a populated completer list', () => { const populatedList = [ {}, {} ]; - const result = applyFilters( 'blocks.Autocomplete.completers', populatedList ); + const result = applyFilters( 'blocks.Autocomplete.completers', populatedList, BLOCK_NAME ); // Assert referential equality because the list should be unchanged. expect( result ).toBe( populatedList ); } ); it( 'provides copies of defaults so they may be directly modified', () => { - const result = applyFilters( 'blocks.Autocomplete.completers', null ); + const result = applyFilters( 'blocks.Autocomplete.completers', null, BLOCK_NAME ); result.forEach( ( completer, i ) => { const defaultCompleter = defaultAutocompleters[ i ]; expect( completer ).not.toBe( defaultCompleter ); diff --git a/blocks/hooks/test/generated-class-name.js b/editor/hooks/test/generated-class-name.js similarity index 100% rename from blocks/hooks/test/generated-class-name.js rename to editor/hooks/test/generated-class-name.js diff --git a/blocks/hooks/test/layout.js b/editor/hooks/test/layout.js similarity index 100% rename from blocks/hooks/test/layout.js rename to editor/hooks/test/layout.js diff --git a/editor/index.js b/editor/index.js index 58d40d036eb58..ed8e1be7dd2ce 100644 --- a/editor/index.js +++ b/editor/index.js @@ -1,3 +1,5 @@ +import './deprecated'; import './store'; +import './hooks'; export * from './components'; diff --git a/editor/index.native.js b/editor/index.native.js new file mode 100644 index 0000000000000..07635cbbc8e7a --- /dev/null +++ b/editor/index.native.js @@ -0,0 +1 @@ +export * from './components'; diff --git a/editor/store/actions.js b/editor/store/actions.js index 07a22bc3ec3a5..c78cdc25ec9ac 100644 --- a/editor/store/actions.js +++ b/editor/store/actions.js @@ -666,3 +666,19 @@ export function insertDefaultBlock( attributes, rootUID, index ) { isProvisional: true, }; } + +/** + * Returns an action object that changes the nested settings of a given block. + * + * @param {string} id UID of the block whose nested setting. + * @param {Object} settings Object with the new settings for the nested block. + * + * @return {Object} Action object + */ +export function updateBlockListSettings( id, settings ) { + return { + type: 'UPDATE_BLOCK_LIST_SETTINGS', + id, + settings, + }; +} diff --git a/editor/store/effects.js b/editor/store/effects.js index a58f5d2f6c40f..ad08123a54118 100644 --- a/editor/store/effects.js +++ b/editor/store/effects.js @@ -124,7 +124,7 @@ export default { ( err ) => { dispatch( { type: 'REQUEST_POST_UPDATE_FAILURE', - error: get( err, 'responseJSON', { + error: get( err, [ 'responseJSON' ], { code: 'unknown_error', message: __( 'An unknown error occurred.' ), } ), @@ -176,7 +176,7 @@ export default { ) ); } - if ( get( window.history.state, 'id' ) !== post.id ) { + if ( get( window.history.state, [ 'id' ] ) !== post.id ) { window.history.replaceState( { id: post.id }, 'Post ' + post.id, @@ -218,7 +218,7 @@ export default { dispatch( { ...action, type: 'TRASH_POST_FAILURE', - error: get( err, 'responseJSON', { + error: get( err, [ 'responseJSON' ], { code: 'unknown_error', message: __( 'An unknown error occurred.' ), } ), @@ -464,7 +464,7 @@ export default { ( error ) => { dispatch( { type: 'SAVE_SHARED_BLOCK_FAILURE', id } ); const message = __( 'An unknown error occurred.' ); - dispatch( createErrorNotice( get( error.responseJSON, 'message', message ), { + dispatch( createErrorNotice( get( error.responseJSON, [ 'message' ], message ), { id: SHARED_BLOCK_NOTICE_ID, spokenMessage: message, } ) ); @@ -524,7 +524,7 @@ export default { optimist: { type: REVERT, id: transactionId }, } ); const message = __( 'An unknown error occurred.' ); - dispatch( createErrorNotice( get( error.responseJSON, 'message', message ), { + dispatch( createErrorNotice( get( error.responseJSON, [ 'message' ], message ), { id: SHARED_BLOCK_NOTICE_ID, spokenMessage: message, } ) ); @@ -573,7 +573,7 @@ export default { }, EDIT_POST( action, { getState } ) { - const format = get( action, 'edits.format' ); + const format = get( action, [ 'edits', 'format' ] ); if ( ! format ) { return; } diff --git a/editor/store/reducer.js b/editor/store/reducer.js index 6d981f278de58..ee6dfab934abe 100644 --- a/editor/store/reducer.js +++ b/editor/store/reducer.js @@ -224,8 +224,8 @@ export const editor = flow( [ // Track whether changes exist, resetting at each post save. Relies on // editor initialization firing post reset as an effect. withChangeDetection( { - resetTypes: [ 'SETUP_EDITOR_STATE', 'RESET_POST' ], - ignoreTypes: [ 'RECEIVE_BLOCKS' ], + resetTypes: [ 'SETUP_EDITOR_STATE', 'UPDATE_POST' ], + ignoreTypes: [ 'RECEIVE_BLOCKS', 'RESET_POST' ], } ), ] )( { edits( state = {}, action ) { @@ -1006,6 +1006,42 @@ export const sharedBlocks = combineReducers( { }, } ); +/** + * Reducer that for each block uid stores an object that represents its nested settings. + * E.g: what blocks can be nested inside a block. + * + * @param {Object} state Current state. + * @param {Object} action Dispatched action. + * + * @return {Object} Updated state. + */ +export const blockListSettings = ( state = {}, action ) => { + switch ( action.type ) { + // even if the replaced blocks have the same uid our logic should correct the state. + case 'REPLACE_BLOCKS' : + case 'REMOVE_BLOCKS': { + return omit( state, action.uids ); + } + case 'UPDATE_BLOCK_LIST_SETTINGS': { + const { id, settings } = action; + if ( id && ! settings ) { + return omit( state, id ); + } + const blockSettings = state[ id ]; + const updateIsRequired = ! isEqual( blockSettings, settings ); + if ( updateIsRequired ) { + return { + ...state, + [ id ]: { + ...settings, + }, + }; + } + } + } + return state; +}; + export default optimist( combineReducers( { editor, currentPost, @@ -1013,6 +1049,7 @@ export default optimist( combineReducers( { blockSelection, provisionalBlockUID, blocksMode, + blockListSettings, isInsertionPointVisible, preferences, saving, diff --git a/editor/store/selectors.js b/editor/store/selectors.js index 0d10c2106d396..62fa1c7686b70 100644 --- a/editor/store/selectors.js +++ b/editor/store/selectors.js @@ -6,6 +6,7 @@ import { first, get, has, + intersection, last, reduce, size, @@ -25,7 +26,6 @@ import { serialize, getBlockType, getBlockTypes } from '@wordpress/blocks'; import { __ } from '@wordpress/i18n'; import { addQueryArgs } from '@wordpress/url'; import { moment } from '@wordpress/date'; -import { deprecated } from '@wordpress/utils'; /*** * Module constants @@ -89,7 +89,7 @@ export function isEditedPostNew( state ) { * @return {boolean} Whether unsaved values exist. */ export function isEditedPostDirty( state ) { - return state.editor.isDirty; + return state.editor.isDirty || inSomeHistory( state, isEditedPostDirty ); } /** @@ -148,7 +148,7 @@ export function getCurrentPostId( state ) { * @return {number} Number of revisions. */ export function getCurrentPostRevisionsCount( state ) { - return get( getCurrentPost( state ), 'revisions.count', 0 ); + return get( getCurrentPost( state ), [ 'revisions', 'count' ], 0 ); } /** @@ -160,7 +160,7 @@ export function getCurrentPostRevisionsCount( state ) { * @return {?number} ID of the last revision. */ export function getCurrentPostLastRevisionId( state ) { - return get( getCurrentPost( state ), 'revisions.last_id', null ); + return get( getCurrentPost( state ), [ 'revisions', 'last_id' ], null ); } /** @@ -584,6 +584,19 @@ export function hasSelectedBlock( state ) { return !! start && start === end; } +/** + * Returns the currently selected block UID, or null if there is no selected + * block. + * + * @param {Object} state Global application state. + * + * @return {?Object} Selected block UID. + */ +export function getSelectedBlockUID( state ) { + const { start, end } = state.blockSelection; + return start === end && start ? start : null; +} + /** * Returns the currently selected block, or null if there is no selected block. * @@ -592,12 +605,8 @@ export function hasSelectedBlock( state ) { * @return {?Object} Selected block. */ export function getSelectedBlock( state ) { - const { start, end } = state.blockSelection; - if ( start !== end || ! start ) { - return null; - } - - return getBlock( state, start ); + const uid = getSelectedBlockUID( state ); + return uid ? getBlock( state, uid ) : null; } /** @@ -635,7 +644,7 @@ export function getBlockRootUID( state, uid ) { export function getAdjacentBlockUid( state, startUID, modifier = 1 ) { // Default to selected block. if ( startUID === undefined ) { - startUID = get( getSelectedBlock( state ), 'uid' ); + startUID = get( getSelectedBlock( state ), [ 'uid' ] ); } // Try multi-selection starting at extent based on modifier. @@ -1293,15 +1302,6 @@ function buildInserterItemFromSharedBlock( state, allowedBlockTypes, sharedBlock * @return {Editor.InserterItem[]} Items that appear in inserter. */ export function getInserterItems( state, allowedBlockTypes ) { - if ( allowedBlockTypes === undefined ) { - allowedBlockTypes = true; - deprecated( 'getInserterItems with no allowedBlockTypes argument', { - version: '2.8', - alternative: 'getInserterItems with an explcit allowedBlockTypes argument', - plugin: 'Gutenberg', - } ); - } - if ( ! allowedBlockTypes ) { return []; } @@ -1363,15 +1363,6 @@ function getItemsFromInserts( state, inserts, allowedBlockTypes, maximum = MAX_R * @return {Editor.InserterItem[]} Items that appear in the 'Recent' tab. */ export function getFrecentInserterItems( state, allowedBlockTypes, maximum = MAX_RECENT_BLOCKS ) { - if ( allowedBlockTypes === undefined ) { - allowedBlockTypes = true; - deprecated( 'getFrecentInserterItems with no allowedBlockTypes argument', { - version: '2.8', - alternative: 'getFrecentInserterItems with an explcit allowedBlockTypes argument', - plugin: 'Gutenberg', - } ); - } - const calculateFrecency = ( time, count ) => { if ( ! time ) { return count; @@ -1558,7 +1549,7 @@ export function getPermalink( state ) { */ export function getPermalinkParts( state ) { const permalinkTemplate = getEditedPostAttribute( state, 'permalink_template' ); - const postName = getEditedPostAttribute( state, 'slug' ) || getEditedPostAttribute( state, 'draft_slug' ); + const postName = getEditedPostAttribute( state, 'slug' ) || getEditedPostAttribute( state, 'generated_slug' ); const [ prefix, suffix ] = permalinkTemplate.split( PERMALINK_POSTNAME_REGEX ); @@ -1568,3 +1559,66 @@ export function getPermalinkParts( state ) { suffix, }; } + +/** + * Returns true if an optimistic transaction is pending commit, for which the + * before state satisfies the given predicate function. + * + * @param {Object} state Editor state. + * @param {Function} predicate Function given state, returning true if match. + * + * @return {boolean} Whether predicate matches for some history. + */ +export function inSomeHistory( state, predicate ) { + const { optimist } = state; + + // In recursion, optimist state won't exist. Assume exhausted options. + if ( ! optimist ) { + return false; + } + + return optimist.some( ( { beforeState } ) => ( + beforeState && predicate( beforeState ) + ) ); +} + +/** + * Returns the Block List settings of a block if any. + * + * @param {Object} state Editor state. + * @param {?string} uid Block UID. + * + * @return {?Object} Block settings of the block if set. + */ +export function getBlockListSettings( state, uid ) { + return state.blockListSettings[ uid ]; +} + +/** + * Determines the blocks that can be nested inside a given block. Or globally if a block is not specified. + * + * @param {Object} state Global application state. + * @param {?string} uid Block UID. + * @param {string[]|boolean} globallyEnabledBlockTypes Globally enabled block types, or true/false to enable/disable all types. + * + * @return {string[]|boolean} Blocks that can be nested inside the block with the specified uid, or true/false to enable/disable all types. + */ +export function getSupportedBlocks( state, uid, globallyEnabledBlockTypes ) { + if ( ! globallyEnabledBlockTypes ) { + return false; + } + + const supportedNestedBlocks = get( getBlockListSettings( state, uid ), [ 'supportedBlocks' ] ); + if ( supportedNestedBlocks === true || supportedNestedBlocks === undefined ) { + return globallyEnabledBlockTypes; + } + + if ( ! supportedNestedBlocks ) { + return false; + } + + if ( globallyEnabledBlockTypes === true ) { + return supportedNestedBlocks; + } + return intersection( globallyEnabledBlockTypes, supportedNestedBlocks ); +} diff --git a/editor/store/test/actions.js b/editor/store/test/actions.js index 97a0291b9f23d..12462f8d91f7e 100644 --- a/editor/store/test/actions.js +++ b/editor/store/test/actions.js @@ -42,6 +42,7 @@ import { createErrorNotice, createWarningNotice, removeNotice, + updateBlockListSettings, } from '../actions'; describe( 'actions', () => { @@ -530,4 +531,22 @@ describe( 'actions', () => { } ); } ); } ); + + describe( 'updateBlockListSettings', () => { + it( 'should return the UPDATE_BLOCK_LIST_SETTINGS with undefined settings', () => { + expect( updateBlockListSettings( 'chicken' ) ).toEqual( { + type: 'UPDATE_BLOCK_LIST_SETTINGS', + id: 'chicken', + settings: undefined, + } ); + } ); + + it( 'should return the UPDATE_BLOCK_LIST_SETTINGS action with the passed settings', () => { + expect( updateBlockListSettings( 'chicken', { chicken: 'ribs' } ) ).toEqual( { + type: 'UPDATE_BLOCK_LIST_SETTINGS', + id: 'chicken', + settings: { chicken: 'ribs' }, + } ); + } ); + } ); } ); diff --git a/editor/store/test/effects.js b/editor/store/test/effects.js index 4aa18d9a53114..ce515768a6c31 100644 --- a/editor/store/test/effects.js +++ b/editor/store/test/effects.js @@ -607,8 +607,8 @@ describe( 'effects', () => { }, ] ); - set( global, 'wp.api.getPostTypeRoute', () => 'blocks' ); - set( global, 'wp.apiRequest', () => promise ); + set( global, [ 'wp', 'api', 'getPostTypeRoute' ], () => 'blocks' ); + set( global, [ 'wp', 'apiRequest' ], () => promise ); const dispatch = jest.fn(); const store = { getState: noop, dispatch }; @@ -645,8 +645,8 @@ describe( 'effects', () => { content: '<!-- wp:test-block {"name":"Big Bird"} /-->', } ); - set( global, 'wp.api.getPostTypeRoute', () => 'blocks' ); - set( global, 'wp.apiRequest', () => promise ); + set( global, [ 'wp', 'api', 'getPostTypeRoute' ], () => 'blocks' ); + set( global, [ 'wp', 'apiRequest' ], () => promise ); const dispatch = jest.fn(); const store = { getState: noop, dispatch }; @@ -679,8 +679,8 @@ describe( 'effects', () => { it( 'should handle an API error', () => { const promise = Promise.reject( {} ); - set( global, 'wp.api.getPostTypeRoute', () => 'blocks' ); - set( global, 'wp.apiRequest', () => promise ); + set( global, [ 'wp', 'api', 'getPostTypeRoute' ], () => 'blocks' ); + set( global, [ 'wp', 'apiRequest' ], () => promise ); const dispatch = jest.fn(); const store = { getState: noop, dispatch }; @@ -722,8 +722,8 @@ describe( 'effects', () => { let modelAttributes; const promise = Promise.resolve( { id: 456 } ); - set( global, 'wp.api.getPostTypeRoute', () => 'blocks' ); - set( global, 'wp.apiRequest', ( request ) => { + set( global, [ 'wp', 'api', 'getPostTypeRoute' ], () => 'blocks' ); + set( global, [ 'wp', 'apiRequest' ], ( request ) => { modelAttributes = request.data; return promise; } ); @@ -759,8 +759,8 @@ describe( 'effects', () => { it( 'should handle an API error', () => { const promise = Promise.reject( {} ); - set( global, 'wp.api.getPostTypeRoute', () => 'blocks' ); - set( global, 'wp.apiRequest', () => promise ); + set( global, [ 'wp', 'api', 'getPostTypeRoute' ], () => 'blocks' ); + set( global, [ 'wp', 'apiRequest' ], () => promise ); const sharedBlock = { id: 123, title: 'My cool block' }; const parsedBlock = createBlock( 'core/test-block', { name: 'Big Bird' } ); @@ -790,8 +790,8 @@ describe( 'effects', () => { it( 'should delete a shared block', () => { const promise = Promise.resolve( {} ); - set( global, 'wp.api.getPostTypeRoute', () => 'blocks' ); - set( global, 'wp.apiRequest', () => promise ); + set( global, [ 'wp', 'api', 'getPostTypeRoute' ], () => 'blocks' ); + set( global, [ 'wp', 'apiRequest' ], () => promise ); const associatedBlock = createBlock( 'core/block', { ref: 123 } ); const sharedBlock = { id: 123, title: 'My cool block' }; @@ -830,8 +830,8 @@ describe( 'effects', () => { it( 'should handle an API error', () => { const promise = Promise.reject( {} ); - set( global, 'wp.api.getPostTypeRoute', () => 'blocks' ); - set( global, 'wp.apiRequest', () => promise ); + set( global, [ 'wp', 'api', 'getPostTypeRoute' ], () => 'blocks' ); + set( global, [ 'wp', 'apiRequest' ], () => promise ); const sharedBlock = { id: 123, title: 'My cool block' }; const parsedBlock = createBlock( 'core/test-block', { name: 'Big Bird' } ); diff --git a/editor/store/test/reducer.js b/editor/store/test/reducer.js index 005173b127b79..26b0354cd0203 100644 --- a/editor/store/test/reducer.js +++ b/editor/store/test/reducer.js @@ -35,6 +35,7 @@ import { isInsertionPointVisible, sharedBlocks, template, + blockListSettings, } from '../reducer'; describe( 'state', () => { @@ -2189,4 +2190,81 @@ describe( 'state', () => { expect( state ).toEqual( { isValid: true, template: [] } ); } ); } ); + + describe( 'blockListSettings', () => { + it( 'should add new settings', () => { + const original = deepFreeze( {} ); + const state = blockListSettings( original, { + type: 'UPDATE_BLOCK_LIST_SETTINGS', + id: 'chicken', + settings: { + chicken: 'ribs', + }, + } ); + expect( state ).toEqual( { + chicken: { + chicken: 'ribs', + }, + } ); + } ); + + it( 'should update the settings of a block', () => { + const original = deepFreeze( { + chicken: { + chicken: 'ribs', + }, + otherBlock: { + setting1: true, + }, + } ); + const state = blockListSettings( original, { + type: 'UPDATE_BLOCK_LIST_SETTINGS', + id: 'chicken', + settings: { + ribs: 'not-chicken', + }, + } ); + expect( state ).toEqual( { + chicken: { + ribs: 'not-chicken', + }, + otherBlock: { + setting1: true, + }, + } ); + } ); + + it( 'should remove the settings of a block when it is replaced', () => { + const original = deepFreeze( { + chicken: { + chicken: 'ribs', + }, + otherBlock: { + setting1: true, + }, + } ); + const state = blockListSettings( original, { + type: 'REPLACE_BLOCKS', + uids: [ 'otherBlock' ], + } ); + expect( state ).toEqual( { + chicken: { + chicken: 'ribs', + }, + } ); + } ); + + it( 'should remove the settings of a block when it is removed', () => { + const original = deepFreeze( { + otherBlock: { + setting1: true, + }, + } ); + const state = blockListSettings( original, { + type: 'REPLACE_BLOCKS', + uids: [ 'otherBlock' ], + } ); + expect( state ).toEqual( {} ); + } ); + } ); } ); diff --git a/editor/store/test/selectors.js b/editor/store/test/selectors.js index 53aa2a2bcfd59..1d05720b337ab 100644 --- a/editor/store/test/selectors.js +++ b/editor/store/test/selectors.js @@ -46,6 +46,7 @@ const { getBlockCount, hasSelectedBlock, getSelectedBlock, + getSelectedBlockUID, getBlockRootUID, getEditedPostAttribute, getGlobalBlockCount, @@ -84,6 +85,8 @@ const { isValidTemplate, getTemplate, getTemplateLock, + getBlockListSettings, + getSupportedBlocks, POST_UPDATE_TRANSACTION_ID, isPermalinkEditable, getPermalink, @@ -204,6 +207,28 @@ describe( 'selectors', () => { editor: { isDirty: true, }, + saving: { + requesting: false, + }, + }; + + expect( isEditedPostDirty( state ) ).toBe( true ); + } ); + + it( 'should return true if pending transaction with dirty state', () => { + const state = { + optimist: [ + { + beforeState: { + editor: { + isDirty: true, + }, + }, + }, + ], + editor: { + isDirty: false, + }, }; expect( isEditedPostDirty( state ) ).toBe( true ); @@ -214,6 +239,9 @@ describe( 'selectors', () => { editor: { isDirty: false, }, + saving: { + requesting: false, + }, }; expect( isEditedPostDirty( state ) ).toBe( false ); @@ -230,6 +258,9 @@ describe( 'selectors', () => { id: 1, status: 'auto-draft', }, + saving: { + requesting: false, + }, }; expect( isCleanNewPost( state ) ).toBe( true ); @@ -244,6 +275,9 @@ describe( 'selectors', () => { id: 1, status: 'draft', }, + saving: { + requesting: false, + }, }; expect( isCleanNewPost( state ) ).toBe( false ); @@ -258,6 +292,9 @@ describe( 'selectors', () => { id: 1, status: 'auto-draft', }, + saving: { + requesting: false, + }, }; expect( isCleanNewPost( state ) ).toBe( false ); @@ -432,6 +469,9 @@ describe( 'selectors', () => { }, isDirty: false, }, + saving: { + requesting: false, + }, }; expect( getDocumentTitle( state ) ).toBe( 'The Title' ); @@ -450,6 +490,9 @@ describe( 'selectors', () => { }, }, }, + saving: { + requesting: false, + }, }; expect( getDocumentTitle( state ) ).toBe( 'Modified Title' ); @@ -470,6 +513,9 @@ describe( 'selectors', () => { }, isDirty: false, }, + saving: { + requesting: false, + }, }; expect( getDocumentTitle( state ) ).toBe( __( 'New post' ) ); @@ -490,6 +536,9 @@ describe( 'selectors', () => { }, isDirty: true, }, + saving: { + requesting: false, + }, }; expect( getDocumentTitle( state ) ).toBe( __( '(Untitled)' ) ); @@ -719,6 +768,9 @@ describe( 'selectors', () => { currentPost: { status: 'pending', }, + saving: { + requesting: false, + }, }; expect( isEditedPostPublishable( state ) ).toBe( true ); @@ -732,6 +784,9 @@ describe( 'selectors', () => { currentPost: { status: 'draft', }, + saving: { + requesting: false, + }, }; expect( isEditedPostPublishable( state ) ).toBe( true ); @@ -745,6 +800,9 @@ describe( 'selectors', () => { currentPost: { status: 'publish', }, + saving: { + requesting: false, + }, }; expect( isEditedPostPublishable( state ) ).toBe( false ); @@ -758,6 +816,9 @@ describe( 'selectors', () => { currentPost: { status: 'publish', }, + saving: { + requesting: false, + }, }; expect( isEditedPostPublishable( state ) ).toBe( true ); @@ -771,6 +832,9 @@ describe( 'selectors', () => { currentPost: { status: 'private', }, + saving: { + requesting: false, + }, }; expect( isEditedPostPublishable( state ) ).toBe( false ); @@ -784,6 +848,9 @@ describe( 'selectors', () => { currentPost: { status: 'future', }, + saving: { + requesting: false, + }, }; expect( isEditedPostPublishable( state ) ).toBe( false ); @@ -797,6 +864,9 @@ describe( 'selectors', () => { editor: { isDirty: true, }, + saving: { + requesting: false, + }, }; expect( isEditedPostPublishable( state ) ).toBe( true ); @@ -1537,6 +1607,33 @@ describe( 'selectors', () => { expect( getGlobalBlockCount( state, 'core/heading' ) ).toBe( 0 ); } ); } ); + + describe( 'getSelectedBlockUID', () => { + it( 'should return null if no block is selected', () => { + const state = { + blockSelection: { start: null, end: null }, + }; + + expect( getSelectedBlockUID( state ) ).toBe( null ); + } ); + + it( 'should return null if there is multi selection', () => { + const state = { + blockSelection: { start: 23, end: 123 }, + }; + + expect( getSelectedBlockUID( state ) ).toBe( null ); + } ); + + it( 'should return the selected block UID', () => { + const state = { + blockSelection: { start: 23, end: 23 }, + }; + + expect( getSelectedBlockUID( state ) ).toEqual( 23 ); + } ); + } ); + describe( 'getSelectedBlock', () => { it( 'should return null if no block is selected', () => { const state = { @@ -3186,4 +3283,119 @@ describe( 'selectors', () => { expect( getPermalinkParts( state ) ).toEqual( parts ); } ); } ); + + describe( 'getBlockListSettings', () => { + it( 'should return the settings of a block', () => { + const state = { + blockListSettings: { + chicken: { + setting1: false, + }, + ribs: { + setting2: true, + }, + }, + }; + + expect( getBlockListSettings( state, 'chicken' ) ).toEqual( { + setting1: false, + } ); + } ); + + it( 'should return undefined if settings for the block don\'t exist', () => { + const state = { + blockListSettings: {}, + }; + + expect( getBlockListSettings( state, 'chicken' ) ).toBe( undefined ); + } ); + } ); + + describe( 'getSupportedBlocks', () => { + it( 'should return false if all blocks are disabled globally', () => { + const state = { + blockListSettings: { + block1: { + supportedBlocks: [ 'core/block1' ], + }, + }, + }; + + expect( getSupportedBlocks( state, 'block1', false ) ).toBe( false ); + } ); + + it( 'should return the supportedBlocks of root block if all blocks are supported globally', () => { + const state = { + blockListSettings: { + block1: { + supportedBlocks: [ 'core/block1' ], + }, + }, + }; + + expect( getSupportedBlocks( state, 'block1', true ) ).toEqual( [ 'core/block1' ] ); + } ); + + it( 'should return the globally supported blocks if all blocks are enable inside the root block', () => { + const state = { + blockListSettings: { + block1: { + supportedBlocks: true, + }, + }, + }; + + expect( getSupportedBlocks( state, 'block1', [ 'core/block1' ] ) ).toEqual( [ 'core/block1' ] ); + } ); + + it( 'should return the globally supported blocks if the root block does not sets the supported blocks', () => { + const state = { + blockListSettings: { + block1: { + chicken: 'ribs', + }, + }, + }; + + expect( getSupportedBlocks( state, 'block1', [ 'core/block1' ] ) ).toEqual( [ 'core/block1' ] ); + } ); + + it( 'should return the globally supported blocks if there are no settings for the root block', () => { + const state = { + blockListSettings: { + block1: { + supportedBlocks: true, + }, + }, + }; + + expect( getSupportedBlocks( state, 'block2', [ 'core/block1' ] ) ).toEqual( [ 'core/block1' ] ); + } ); + + it( 'should return false if all blocks are disabled inside the root block ', () => { + const state = { + blockListSettings: { + block1: { + supportedBlocks: false, + }, + }, + }; + + expect( getSupportedBlocks( state, 'block1', [ 'core/block1' ] ) ).toBe( false ); + } ); + + it( 'should return the intersection of globally supported blocks with the supported blocks of the root block if both sets are defined', () => { + const state = { + blockListSettings: { + block1: { + supportedBlocks: [ 'core/block1', 'core/block2', 'core/block3' ], + }, + }, + }; + + expect( getSupportedBlocks( state, 'block1', [ 'core/block2', 'core/block4', 'core/block5' ] ) ).toEqual( + [ 'core/block2' ] + ); + } ); + } ); } ); diff --git a/editor/utils/block-list.js b/editor/utils/block-list.js index 0f68869c783d8..2bbfe34cdc326 100644 --- a/editor/utils/block-list.js +++ b/editor/utils/block-list.js @@ -1,12 +1,16 @@ /** * External dependencies */ -import { noop } from 'lodash'; +import { isEqual, noop, omit } from 'lodash'; /** * WordPress dependencies */ -import { Component } from '@wordpress/element'; +import { Component, compose } from '@wordpress/element'; +import { + synchronizeBlocksWithTemplate, +} from '@wordpress/blocks'; +import { withSelect, withDispatch } from '@wordpress/data'; /** * Internal dependencies @@ -35,33 +39,87 @@ const INNER_BLOCK_LIST_CACHE = {}; */ export function createInnerBlockList( uid, renderBlockMenu = noop ) { if ( ! INNER_BLOCK_LIST_CACHE[ uid ] ) { - INNER_BLOCK_LIST_CACHE[ uid ] = [ - // The component class: - class extends Component { - componentWillMount() { - INNER_BLOCK_LIST_CACHE[ uid ][ 1 ]++; + const InnerBlockListComponent = class extends Component { + componentWillReceiveProps( nextProps ) { + this.updateNestedSettings( { + supportedBlocks: nextProps.allowedBlocks, + } ); + } + + componentWillUnmount() { + // If, after decrementing the tracking count, there are no + // remaining instances of the component, remove from cache. + if ( ! INNER_BLOCK_LIST_CACHE[ uid ][ 1 ]-- ) { + delete INNER_BLOCK_LIST_CACHE[ uid ]; } + } + + componentDidMount() { + INNER_BLOCK_LIST_CACHE[ uid ][ 1 ]++; + this.updateNestedSettings( { + supportedBlocks: this.props.allowedBlocks, + } ); + this.insertTemplateBlocks( this.props.template ); + } - componentWillUnmount() { - // If, after decrementing the tracking count, there are no - // remaining instances of the component, remove from cache. - if ( ! INNER_BLOCK_LIST_CACHE[ uid ][ 1 ]-- ) { - delete INNER_BLOCK_LIST_CACHE[ uid ]; - } + insertTemplateBlocks( template ) { + const { block, insertBlocks } = this.props; + if ( template && ! block.innerBlocks.length ) { + // synchronizeBlocksWithTemplate( [], template ) parses the template structure, + // and returns/creates the necessary blocks to represent it. + insertBlocks( synchronizeBlocksWithTemplate( [], template ) ); } + } - render() { - return ( - <BlockList - rootUID={ uid } - renderBlockMenu={ renderBlockMenu } - { ...this.props } /> - ); + updateNestedSettings( newSettings ) { + if ( ! isEqual( this.props.blockListSettings, newSettings ) ) { + this.props.updateNestedSettings( newSettings ); } - }, + } - // A counter tracking active mounted instances: - 0, + render() { + return ( + <BlockList + rootUID={ uid } + renderBlockMenu={ renderBlockMenu } + { ...omit( + this.props, [ + 'allowedBlocks', + 'block', + 'blockListSettings', + 'insertBlocks', + 'template', + 'updateNestedSettings', + ] + ) } /> + ); + } + }; + + const InnerBlockListComponentContainer = compose( + withSelect( ( select ) => { + const { getBlock, getBlockListSettings } = select( 'core/editor' ); + return { + block: getBlock( uid ), + blockListSettings: getBlockListSettings( uid ), + }; + } ), + withDispatch( ( dispatch ) => { + const { insertBlocks, updateBlockListSettings } = dispatch( 'core/editor' ); + return { + insertBlocks( blocks ) { + dispatch( insertBlocks( blocks, undefined, uid ) ); + }, + updateNestedSettings( settings ) { + dispatch( updateBlockListSettings( uid, settings ) ); + }, + }; + } ), + )( InnerBlockListComponent ); + + INNER_BLOCK_LIST_CACHE[ uid ] = [ + InnerBlockListComponentContainer, + 0, // A counter tracking active mounted instances: ]; } diff --git a/editor/utils/with-change-detection/index.js b/editor/utils/with-change-detection/index.js index 8b738e50015d6..e86db9a9905d4 100644 --- a/editor/utils/with-change-detection/index.js +++ b/editor/utils/with-change-detection/index.js @@ -16,11 +16,7 @@ import { includes } from 'lodash'; */ const withChangeDetection = ( options = {} ) => ( reducer ) => { return ( state, action ) => { - const nextState = reducer( state, action ); - - if ( includes( options.ignoreTypes, action.type ) ) { - return nextState; - } + let nextState = reducer( state, action ); // Reset at: // - Initial state @@ -30,17 +26,26 @@ const withChangeDetection = ( options = {} ) => ( reducer ) => { includes( options.resetTypes, action.type ) ); - if ( isReset ) { - return { - ...nextState, - isDirty: false, - }; + const isChanging = state !== nextState; + + // If not intending to update dirty flag, return early and avoid clone. + if ( ! isChanging && ! isReset ) { + return state; } - const isChanging = state !== nextState; + // Avoid mutating state, unless it's already changing by original + // reducer and not initial. + if ( ! isChanging || state === undefined ) { + nextState = { ...nextState }; + } + + const isIgnored = includes( options.ignoreTypes, action.type ); - if ( isChanging ) { - nextState.isDirty = true; + if ( isIgnored ) { + // Preserve the original value if ignored. + nextState.isDirty = state.isDirty; + } else { + nextState.isDirty = ! isReset && isChanging; } return nextState; diff --git a/editor/utils/with-change-detection/test/index.js b/editor/utils/with-change-detection/test/index.js index 728ef8b22a550..06abccb50fc5b 100644 --- a/editor/utils/with-change-detection/test/index.js +++ b/editor/utils/with-change-detection/test/index.js @@ -14,10 +14,14 @@ describe( 'withChangeDetection()', () => { function originalReducer( state = initialState, action ) { switch ( action.type ) { case 'INCREMENT': - return { ...state, count: state.count + 1 }; + return { + count: state.count + 1, + }; case 'RESET_AND_CHANGE_REFERENCE': - return { ...state }; + return { + count: state.count, + }; } return state; @@ -72,8 +76,9 @@ describe( 'withChangeDetection()', () => { state = reducer( deepFreeze( state ), { type: 'INCREMENT' } ); expect( state ).toEqual( { count: 1, isDirty: true } ); - state = reducer( deepFreeze( state ), {} ); - expect( state ).toEqual( { count: 1, isDirty: true } ); + const afterState = reducer( deepFreeze( state ), {} ); + expect( afterState ).toEqual( { count: 1, isDirty: true } ); + expect( afterState ).toBe( state ); } ); it( 'should maintain separate states', () => { diff --git a/element/index.js b/element/index.js index ff524b1b7645f..0cfbb49d5e167 100644 --- a/element/index.js +++ b/element/index.js @@ -5,6 +5,7 @@ import { createElement, createContext, createRef, + forwardRef, Component, cloneElement, Children, @@ -18,6 +19,11 @@ import { upperFirst, } from 'lodash'; +/** + * WordPress dependencies + */ +import isShallowEqual from '@wordpress/is-shallow-equal'; + /** * Internal dependencies */ @@ -46,6 +52,19 @@ export { createElement }; */ export { createRef }; +/** + * Component enhancer used to enable passing a ref to its wrapped component. + * Pass a function argument which receives `props` and `ref` as its arguments, + * returning an element using the forwarded ref. The return value is a new + * component which forwards its ref. + * + * @param {Function} forwarder Function passed `props` and `ref`, expected to + * return an element. + * + * @return {WPComponent} Enhanced component. + */ +export { forwardRef }; + /** * Renders a given element into the target DOM node. * @@ -209,3 +228,34 @@ export function RawHTML( { children, ...props } ) { ...props, } ); } + +/** + * Given a component returns the enhanced component augmented with a component + * only rerendering when its props/state change + * + * @param {Function} mapComponentToEnhancedComponent Function mapping component + * to enhanced component. + * @param {string} modifierName Seed name from which to + * generated display name. + * + * @return {WPComponent} Component class with generated display name assigned. + */ +export const pure = createHigherOrderComponent( ( Wrapped ) => { + if ( Wrapped.prototype instanceof Component ) { + return class extends Wrapped { + shouldComponentUpdate( nextProps, nextState ) { + return ! isShallowEqual( nextProps, this.props ) || ! isShallowEqual( nextState, this.state ); + } + }; + } + + return class extends Component { + shouldComponentUpdate( nextProps ) { + return ! isShallowEqual( nextProps, this.props ); + } + + render() { + return <Wrapped { ...this.props } />; + } + }; +}, 'pure' ); diff --git a/element/index.native.js b/element/index.native.js new file mode 100644 index 0000000000000..2976db326fbfd --- /dev/null +++ b/element/index.native.js @@ -0,0 +1,40 @@ +/** + * External dependencies + */ +import { + createElement, + Component, +} from 'react'; + +/** + * Internal dependencies + */ +import serialize from './serialize'; + +/** + * Returns a new element of given type. Type can be either a string tag name or + * another function which itself returns an element. + * + * @param {?(string|Function)} type Tag name or element creator + * @param {Object} props Element properties, either attribute + * set to apply to DOM node or values to + * pass through to element creator + * @param {...WPElement} children Descendant elements + * + * @return {WPElement} Element. + */ +export { createElement }; + +/** + * A base class to create WordPress Components (Refs, state and lifecycle hooks) + */ +export { Component }; + +/** + * Renders a given element into a string. + * + * @param {WPElement} element Element to render + * + * @return {string} HTML. + */ +export { serialize as renderToString }; diff --git a/element/test/index.js b/element/test/index.js index daf61f07033c2..80d06d5545292 100644 --- a/element/test/index.js +++ b/element/test/index.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { shallow } from 'enzyme'; +import { shallow, mount } from 'enzyme'; /** * Internal dependencies @@ -14,6 +14,7 @@ import { renderToString, switchChildrenNodeName, RawHTML, + pure, } from '../'; describe( 'element', () => { @@ -212,4 +213,48 @@ describe( 'element', () => { expect( element.prop( 'children' ) ).toBe( undefined ); } ); } ); + + describe( 'pure', () => { + it( 'functional component should rerender only when props change', () => { + let i = 0; + const MyComp = pure( () => { + return <p>{ ++i }</p>; + } ); + const wrapper = mount( <MyComp /> ); + wrapper.update(); // Updating with same props doesn't rerender + expect( wrapper.html() ).toBe( '<p>1</p>' ); + wrapper.setProps( { prop: 'a' } ); // New prop should trigger a rerender + expect( wrapper.html() ).toBe( '<p>2</p>' ); + wrapper.setProps( { prop: 'a' } ); // Keeping the same prop value should not rerender + expect( wrapper.html() ).toBe( '<p>2</p>' ); + wrapper.setProps( { prop: 'b' } ); // Changing the prop value should rerender + expect( wrapper.html() ).toBe( '<p>3</p>' ); + } ); + + it( 'class component should rerender if the props or state change', () => { + let i = 0; + const MyComp = pure( class extends Component { + constructor() { + super( ...arguments ); + this.state = {}; + } + render() { + return <p>{ ++i }</p>; + } + } ); + const wrapper = mount( <MyComp /> ); + wrapper.update(); // Updating with same props doesn't rerender + expect( wrapper.html() ).toBe( '<p>1</p>' ); + wrapper.setProps( { prop: 'a' } ); // New prop should trigger a rerender + expect( wrapper.html() ).toBe( '<p>2</p>' ); + wrapper.setProps( { prop: 'a' } ); // Keeping the same prop value should not rerender + expect( wrapper.html() ).toBe( '<p>2</p>' ); + wrapper.setProps( { prop: 'b' } ); // Changing the prop value should rerender + expect( wrapper.html() ).toBe( '<p>3</p>' ); + wrapper.setState( { state: 'a' } ); // New state value should trigger a rerender + expect( wrapper.html() ).toBe( '<p>4</p>' ); + wrapper.setState( { state: 'a' } ); // Keeping the same state value should not trigger a rerender + expect( wrapper.html() ).toBe( '<p>4</p>' ); + } ); + } ); } ); diff --git a/gutenberg.php b/gutenberg.php index 5a6c06a03c147..21d98c59ce32a 100644 --- a/gutenberg.php +++ b/gutenberg.php @@ -3,7 +3,7 @@ * Plugin Name: Gutenberg * Plugin URI: https://github.com/WordPress/gutenberg * Description: Printing since 1440. This is the development plugin for the new block editor in core. - * Version: 2.7.0 + * Version: 2.8.0 * Author: Gutenberg Team * * @package gutenberg @@ -109,7 +109,7 @@ function gutenberg_build_files_notice() { * @since 1.5.0 */ function gutenberg_pre_init() { - if ( defined( 'GUTENBERG_DEVELOPMENT_MODE' ) && GUTENBERG_DEVELOPMENT_MODE && ! file_exists( dirname( __FILE__ ) . '/blocks/build' ) ) { + if ( defined( 'GUTENBERG_DEVELOPMENT_MODE' ) && GUTENBERG_DEVELOPMENT_MODE && ! file_exists( dirname( __FILE__ ) . '/build/blocks' ) ) { add_action( 'admin_notices', 'gutenberg_build_files_notice' ); return; } diff --git a/lib/client-assets.php b/lib/client-assets.php index 1e4732d1431a9..bfd33bb3118d7 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -75,42 +75,72 @@ function gutenberg_get_script_polyfill( $tests ) { function gutenberg_register_scripts_and_styles() { gutenberg_register_vendor_scripts(); + // WordPress packages. + wp_register_script( + 'wp-dom-ready', + gutenberg_url( 'build/dom-ready/index.js' ), + array(), + filemtime( gutenberg_dir_path() . 'build/dom-ready/index.js' ), + true + ); + wp_register_script( + 'wp-a11y', + gutenberg_url( 'build/a11y/index.js' ), + array( 'wp-dom-ready' ), + filemtime( gutenberg_dir_path() . 'build/a11y/index.js' ), + true + ); + wp_register_script( + 'wp-hooks', + gutenberg_url( 'build/hooks/index.js' ), + array(), + filemtime( gutenberg_dir_path() . 'build/hooks/index.js' ), + true + ); + wp_register_script( + 'wp-i18n', + gutenberg_url( 'build/i18n/index.js' ), + array(), + filemtime( gutenberg_dir_path() . 'build/i18n/index.js' ), + true + ); + wp_register_script( + 'wp-is-shallow-equal', + gutenberg_url( 'build/is-shallow-equal/index.js' ), + array(), + filemtime( gutenberg_dir_path() . 'build/is-shallow-equal/index.js' ), + true + ); + // Editor Scripts. wp_register_script( 'wp-data', - gutenberg_url( 'data/build/index.js' ), - array( 'wp-element', 'wp-utils', 'lodash' ), - filemtime( gutenberg_dir_path() . 'data/build/index.js' ), + gutenberg_url( 'build/data/index.js' ), + array( 'wp-element', 'wp-utils', 'wp-is-shallow-equal', 'lodash' ), + filemtime( gutenberg_dir_path() . 'build/data/index.js' ), true ); wp_register_script( 'wp-core-data', - gutenberg_url( 'core-data/build/index.js' ), + gutenberg_url( 'build/core-data/index.js' ), array( 'wp-data', 'wp-api-request', 'lodash' ), - filemtime( gutenberg_dir_path() . 'core-data/build/index.js' ), + filemtime( gutenberg_dir_path() . 'build/core-data/index.js' ), true ); wp_register_script( 'wp-utils', - gutenberg_url( 'utils/build/index.js' ), + gutenberg_url( 'build/utils/index.js' ), array( 'tinymce-latest', 'lodash' ), - filemtime( gutenberg_dir_path() . 'utils/build/index.js' ), + filemtime( gutenberg_dir_path() . 'build/utils/index.js' ), true ); wp_add_inline_script( 'wp-utils', 'var originalUtils = window.wp && window.wp.utils ? window.wp.utils : {};', 'before' ); wp_add_inline_script( 'wp-utils', 'for ( var key in originalUtils ) wp.utils[ key ] = originalUtils[ key ];' ); - wp_register_script( - 'wp-hooks', - gutenberg_url( 'hooks/build/index.js' ), - array(), - filemtime( gutenberg_dir_path() . 'hooks/build/index.js' ), - true - ); wp_register_script( 'wp-date', - gutenberg_url( 'date/build/index.js' ), + gutenberg_url( 'build/date/index.js' ), array( 'moment' ), - filemtime( gutenberg_dir_path() . 'date/build/index.js' ), + filemtime( gutenberg_dir_path() . 'build/date/index.js' ), true ); global $wp_locale; @@ -139,32 +169,35 @@ function gutenberg_register_scripts_and_styles() { 'string' => get_option( 'timezone_string', 'UTC' ), ), ) ), 'before' ); - wp_register_script( - 'wp-i18n', - gutenberg_url( 'i18n/build/index.js' ), - array(), - filemtime( gutenberg_dir_path() . 'i18n/build/index.js' ), - true - ); wp_register_script( 'wp-element', - gutenberg_url( 'element/build/index.js' ), - array( 'react', 'react-dom', 'wp-utils', 'lodash' ), - filemtime( gutenberg_dir_path() . 'element/build/index.js' ), + gutenberg_url( 'build/element/index.js' ), + array( 'react', 'react-dom', 'wp-utils', 'wp-is-shallow-equal', 'lodash' ), + filemtime( gutenberg_dir_path() . 'build/element/index.js' ), true ); wp_register_script( 'wp-components', - gutenberg_url( 'components/build/index.js' ), - array( 'wp-element', 'wp-i18n', 'wp-utils', 'wp-hooks', 'wp-api-request', 'moment', 'lodash' ), - filemtime( gutenberg_dir_path() . 'components/build/index.js' ), + gutenberg_url( 'build/components/index.js' ), + array( + 'lodash', + 'moment', + 'wp-a11y', + 'wp-api-request', + 'wp-element', + 'wp-hooks', + 'wp-i18n', + 'wp-is-shallow-equal', + 'wp-utils', + ), + filemtime( gutenberg_dir_path() . 'build/components/index.js' ), true ); wp_register_script( 'wp-blocks', - gutenberg_url( 'blocks/build/index.js' ), - array( 'wp-element', 'wp-components', 'wp-utils', 'wp-hooks', 'wp-i18n', 'tinymce-latest', 'tinymce-latest-lists', 'tinymce-latest-paste', 'tinymce-latest-table', 'shortcode', 'wp-core-data', 'lodash' ), - filemtime( gutenberg_dir_path() . 'blocks/build/index.js' ), + gutenberg_url( 'build/blocks/index.js' ), + array( 'wp-element', 'wp-utils', 'wp-hooks', 'wp-i18n', 'shortcode', 'wp-data', 'lodash' ), + filemtime( gutenberg_dir_path() . 'build/blocks/index.js' ), true ); wp_add_inline_script( @@ -177,16 +210,16 @@ function gutenberg_register_scripts_and_styles() { ); wp_register_script( 'wp-viewport', - gutenberg_url( 'viewport/build/index.js' ), + gutenberg_url( 'build/viewport/index.js' ), array( 'wp-element', 'wp-data', 'wp-components', 'lodash' ), - filemtime( gutenberg_dir_path() . 'viewport/build/index.js' ), + filemtime( gutenberg_dir_path() . 'build/viewport/index.js' ), true ); wp_register_script( 'wp-core-blocks', - gutenberg_url( 'core-blocks/build/index.js' ), - array( 'wp-element', 'wp-components', 'wp-utils', 'wp-blocks', 'wp-i18n', 'editor', 'wp-core-data', 'lodash' ), - filemtime( gutenberg_dir_path() . 'core-blocks/build/index.js' ), + gutenberg_url( 'build/core-blocks/index.js' ), + array( 'wp-element', 'wp-components', 'wp-utils', 'wp-blocks', 'wp-editor', 'wp-i18n', 'editor', 'wp-core-data', 'lodash' ), + filemtime( gutenberg_dir_path() . 'build/core-blocks/index.js' ), true ); // Loading the old editor and its config to ensure the classic block works as expected. @@ -260,33 +293,55 @@ function gutenberg_register_scripts_and_styles() { wp_register_script( 'wp-editor', - gutenberg_url( 'editor/build/index.js' ), + gutenberg_url( 'build/editor/index.js' ), array( - 'postbox', + 'editor', 'jquery', + 'lodash', + 'postbox', + 'wp-a11y', 'wp-api', + 'wp-blocks', + 'wp-components', + 'wp-core-data', 'wp-data', 'wp-date', 'wp-i18n', - 'wp-blocks', 'wp-element', - 'wp-components', + 'wp-plugins', 'wp-utils', 'wp-viewport', - 'wp-plugins', - 'wp-core-data', - 'editor', - 'lodash', + 'tinymce-latest', + 'tinymce-latest-lists', + 'tinymce-latest-paste', + 'tinymce-latest-table', ), - filemtime( gutenberg_dir_path() . 'editor/build/index.js' ), - true + filemtime( gutenberg_dir_path() . 'build/editor/index.js' ) ); wp_register_script( 'wp-edit-post', - gutenberg_url( 'edit-post/build/index.js' ), - array( 'jquery', 'media-views', 'media-models', 'wp-element', 'wp-components', 'wp-editor', 'wp-i18n', 'wp-date', 'wp-utils', 'wp-data', 'wp-embed', 'wp-viewport', 'wp-plugins', 'wp-core-blocks', 'lodash' ), - filemtime( gutenberg_dir_path() . 'edit-post/build/index.js' ), + gutenberg_url( 'build/edit-post/index.js' ), + array( + 'jquery', + 'lodash', + 'media-models', + 'media-views', + 'wp-a11y', + 'wp-components', + 'wp-core-blocks', + 'wp-date', + 'wp-data', + 'wp-dom-ready', + 'wp-editor', + 'wp-element', + 'wp-embed', + 'wp-i18n', + 'wp-plugins', + 'wp-viewport', + 'wp-utils', + ), + filemtime( gutenberg_dir_path() . 'build/edit-post/index.js' ), true ); wp_add_inline_script( @@ -303,57 +358,49 @@ function gutenberg_register_scripts_and_styles() { wp_register_style( 'wp-editor', - gutenberg_url( 'editor/build/style.css' ), - array( 'wp-components', 'wp-blocks', 'wp-editor-font' ), - filemtime( gutenberg_dir_path() . 'editor/build/style.css' ) + gutenberg_url( 'build/editor/style.css' ), + array( 'wp-components', 'wp-editor-font' ), + filemtime( gutenberg_dir_path() . 'build/editor/style.css' ) ); wp_style_add_data( 'wp-editor', 'rtl', 'replace' ); wp_register_style( 'wp-edit-post', - gutenberg_url( 'edit-post/build/style.css' ), + gutenberg_url( 'build/edit-post/style.css' ), array( 'wp-components', 'wp-editor', 'wp-edit-blocks', 'wp-core-blocks' ), - filemtime( gutenberg_dir_path() . 'edit-post/build/style.css' ) + filemtime( gutenberg_dir_path() . 'build/edit-post/style.css' ) ); wp_style_add_data( 'wp-edit-post', 'rtl', 'replace' ); wp_register_style( 'wp-components', - gutenberg_url( 'components/build/style.css' ), + gutenberg_url( 'build/components/style.css' ), array(), - filemtime( gutenberg_dir_path() . 'components/build/style.css' ) + filemtime( gutenberg_dir_path() . 'build/components/style.css' ) ); wp_style_add_data( 'wp-components', 'rtl', 'replace' ); - wp_register_style( - 'wp-blocks', - gutenberg_url( 'blocks/build/style.css' ), - array(), - filemtime( gutenberg_dir_path() . 'blocks/build/style.css' ) - ); - wp_style_add_data( 'wp-blocks', 'rtl', 'replace' ); - wp_register_style( 'wp-core-blocks', - gutenberg_url( 'core-blocks/build/style.css' ), + gutenberg_url( 'build/core-blocks/style.css' ), array(), - filemtime( gutenberg_dir_path() . 'core-blocks/build/style.css' ) + filemtime( gutenberg_dir_path() . 'build/core-blocks/style.css' ) ); wp_style_add_data( 'wp-core-blocks', 'rtl', 'replace' ); wp_register_style( 'wp-edit-blocks', - gutenberg_url( 'core-blocks/build/edit-blocks.css' ), - array( 'wp-components', 'wp-blocks' ), - filemtime( gutenberg_dir_path() . 'core-blocks/build/edit-blocks.css' ) + gutenberg_url( 'build/core-blocks/edit-blocks.css' ), + array( 'wp-components', 'wp-editor' ), + filemtime( gutenberg_dir_path() . 'build/core-blocks/edit-blocks.css' ) ); wp_style_add_data( 'wp-edit-blocks', 'rtl', 'replace' ); wp_register_script( 'wp-plugins', - gutenberg_url( 'plugins/build/index.js' ), + gutenberg_url( 'build/plugins/index.js' ), array( 'wp-element', 'wp-components', 'wp-utils', 'wp-data' ), - filemtime( gutenberg_dir_path() . 'plugins/build/index.js' ) + filemtime( gutenberg_dir_path() . 'build/plugins/index.js' ) ); } add_action( 'wp_enqueue_scripts', 'gutenberg_register_scripts_and_styles', 5 ); @@ -864,6 +911,7 @@ function gutenberg_editor_scripts_and_styles( $hook ) { // Preload common data. $preload_paths = array( + '/', sprintf( '/wp/v2/types/%s?context=edit', $post_type ), sprintf( '/wp/v2/users/me?post_type=%s&context=edit', $post_type ), '/wp/v2/taxonomies?context=edit', @@ -929,7 +977,18 @@ function gutenberg_editor_scripts_and_styles( $hook ) { // Initialize the editor. $gutenberg_theme_support = get_theme_support( 'gutenberg' ); $align_wide = get_theme_support( 'align-wide' ); - $color_palette = get_theme_support( 'editor-color-palette' ); + $color_palette = current( (array) get_theme_support( 'editor-color-palette' ) ); + + // Backcompat for Color Palette set as multiple parameters. + if ( is_string( $color_palette ) || isset( $color_palette['color'] ) ) { + $color_palette = get_theme_support( 'editor-color-palette' ); + wp_add_inline_script( + 'wp-edit-post', + 'console.warn( "' . + __( 'Setting colors using multiple parameters is deprecated. Please pass a single parameter with an array of colors. See https://wordpress.org/gutenberg/handbook/extensibility/theme-support/ for details.', 'gutenberg' ) . + '");' + ); + } // Backcompat for Color Palette set through `gutenberg` array. if ( empty( $color_palette ) && ! empty( $gutenberg_theme_support[0]['colors'] ) ) { @@ -979,9 +1038,12 @@ function gutenberg_editor_scripts_and_styles( $hook ) { $script = '( function() {'; $script .= sprintf( 'var editorSettings = %s;', wp_json_encode( $editor_settings ) ); $script .= <<<JS - window._wpLoadGutenbergEditor = wp.api.init().then( function() { - wp.coreBlocks.registerCoreBlocks(); - return wp.editPost.initializeEditor( 'editor', window._wpGutenbergPost, editorSettings ); + window._wpLoadGutenbergEditor = new Promise( function( resolve ) { + wp.api.init().then( function() { + wp.domReady.default( function() { + resolve( wp.editPost.initializeEditor( 'editor', window._wpGutenbergPost, editorSettings ) ); + } ); + } ); } ); JS; $script .= '} )();'; @@ -1012,3 +1074,14 @@ function gutenberg_editor_scripts_and_styles( $hook ) { */ do_action( 'enqueue_block_editor_assets' ); } + +/** + * Ensure the editor module is loaded before third party plugins. + * + * Remove this in Gutenberg 3.1 + */ +function polyfill_blocks_module_in_scripts() { + wp_enqueue_script( 'wp-editor' ); +} + +add_action( 'enqueue_block_editor_assets', 'polyfill_blocks_module_in_scripts', 9 ); diff --git a/lib/compat.php b/lib/compat.php index 85f2a2939ece0..ad72ef60d3349 100644 --- a/lib/compat.php +++ b/lib/compat.php @@ -213,33 +213,8 @@ function gutenberg_add_rest_nonce_to_heartbeat_response_headers( $response ) { $response['rest-nonce'] = wp_create_nonce( 'wp_rest' ); return $response; } - add_filter( 'wp_refresh_nonces', 'gutenberg_add_rest_nonce_to_heartbeat_response_headers' ); -/** - * Ensure that the wp-json index contains the 'theme-supports' setting as - * part of its site info elements. - * - * @param WP_REST_Response $response WP REST API response of the wp-json index. - * @return WP_REST_Response Response that contains theme-supports. - */ -function gutenberg_ensure_wp_json_has_theme_supports( $response ) { - $site_info = $response->get_data(); - if ( ! array_key_exists( 'theme_supports', $site_info ) ) { - $site_info['theme_supports'] = array(); - } - if ( ! array_key_exists( 'formats', $site_info['theme_supports'] ) ) { - $formats = get_theme_support( 'post-formats' ); - $formats = is_array( $formats ) ? array_values( $formats[0] ) : array(); - $formats = array_merge( array( 'standard' ), $formats ); - - $site_info['theme_supports']['formats'] = $formats; - } - $response->set_data( $site_info ); - return $response; -} -add_filter( 'rest_index', 'gutenberg_ensure_wp_json_has_theme_supports' ); - /** * As a substitute for the default content `wpautop` filter, applies autop * behavior only for posts where content does not contain blocks. @@ -256,261 +231,3 @@ function gutenberg_wpautop( $content ) { } remove_filter( 'the_content', 'wpautop' ); add_filter( 'the_content', 'gutenberg_wpautop', 8 ); - -/** - * Includes the value for the custom field `post_type_capabities` inside the REST API response of user. - * - * TODO: This is a temporary solution. Next step would be to edit the WP_REST_Users_Controller, - * once merged into Core. - * - * @since ? - * - * @param array $user An array containing user properties. - * @param string $name The name of the custom field. - * @param WP_REST_Request $request Full details about the REST API request. - * @return object The Post Type capabilities. - */ -function gutenberg_get_post_type_capabilities( $user, $name, $request ) { - $post_type = $request->get_param( 'post_type' ); - $value = new stdClass; - - if ( ! empty( $user['id'] ) && $post_type && post_type_exists( $post_type ) ) { - // The Post Type object contains the Post Type's specific caps. - $post_type_object = get_post_type_object( $post_type ); - - // Loop in the Post Type's caps to validate the User's caps for it. - foreach ( $post_type_object->cap as $post_cap => $post_type_cap ) { - // Ignore caps requiring a post ID. - if ( in_array( $post_cap, array( 'edit_post', 'read_post', 'delete_post' ) ) ) { - continue; - } - - // Set the User's post type capability. - $value->{$post_cap} = user_can( $user['id'], $post_type_cap ); - } - } - - return $value; -} - -/** - * Adds the custom field `post_type_capabities` to the REST API response of user. - * - * TODO: This is a temporary solution. Next step would be to edit the WP_REST_Users_Controller, - * once merged into Core. - * - * @since ? - */ -function gutenberg_register_rest_api_post_type_capabilities() { - register_rest_field( 'user', - 'post_type_capabilities', - array( - 'get_callback' => 'gutenberg_get_post_type_capabilities', - 'schema' => array( - 'description' => __( 'Post Type capabilities for the user.', 'gutenberg' ), - 'type' => 'object', - 'context' => array( 'edit' ), - 'readonly' => true, - ), - ) - ); -} -add_action( 'rest_api_init', 'gutenberg_register_rest_api_post_type_capabilities' ); - -/** - * Make sure oEmbed REST Requests apply the WP Embed security mechanism for WordPress embeds. - * - * @see https://core.trac.wordpress.org/ticket/32522 - * - * TODO: This is a temporary solution. Next step would be to edit the WP_oEmbed_Controller, - * once merged into Core. - * - * @since 2.3.0 - * - * @param WP_HTTP_Response|WP_Error $response The REST Request response. - * @param WP_REST_Server $handler ResponseHandler instance (usually WP_REST_Server). - * @param WP_REST_Request $request Request used to generate the response. - * @return WP_HTTP_Response|object|WP_Error The REST Request response. - */ -function gutenberg_filter_oembed_result( $response, $handler, $request ) { - if ( 'GET' !== $request->get_method() ) { - return $response; - } - - if ( is_wp_error( $response ) && 'oembed_invalid_url' !== $response->get_error_code() ) { - return $response; - } - - // External embeds. - if ( '/oembed/1.0/proxy' === $request->get_route() ) { - if ( is_wp_error( $response ) ) { - // It's possibly a local post, so lets try and retrieve it that way. - $post_id = url_to_postid( $_GET['url'] ); - $data = get_oembed_response_data( $post_id, apply_filters( 'oembed_default_width', 600 ) ); - - if ( ! $data ) { - // Not a local post, return the original error. - return $response; - } - $response = (object) $data; - } - - // Make sure the HTML is run through the oembed sanitisation routines. - $response->html = wp_oembed_get( $_GET['url'], $_GET ); - } - - return $response; -} -add_filter( 'rest_request_after_callbacks', 'gutenberg_filter_oembed_result', 10, 3 ); - -/** - * Add additional 'visibility' rest api field to taxonomies. - * - * Used so private taxonomies are not displayed in the UI. - * - * @see https://core.trac.wordpress.org/ticket/42707 - */ -function gutenberg_add_taxonomy_visibility_field() { - register_rest_field( - 'taxonomy', - 'visibility', - array( - 'get_callback' => 'gutenberg_get_taxonomy_visibility_data', - 'schema' => array( - 'description' => __( 'The visibility settings for the taxonomy.', 'gutenberg' ), - 'type' => 'object', - 'context' => array( 'edit' ), - 'readonly' => true, - 'properties' => array( - 'public' => array( - 'description' => __( 'Whether a taxonomy is intended for use publicly either via the admin interface or by front-end users.', 'gutenberg' ), - 'type' => 'boolean', - ), - 'publicly_queryable' => array( - 'description' => __( 'Whether the taxonomy is publicly queryable.', 'gutenberg' ), - 'type' => 'boolean', - ), - 'show_ui' => array( - 'description' => __( 'Whether to generate a default UI for managing this taxonomy.', 'gutenberg' ), - 'type' => 'boolean', - ), - 'show_admin_column' => array( - 'description' => __( 'Whether to allow automatic creation of taxonomy columns on associated post-types table.', 'gutenberg' ), - 'type' => 'boolean', - ), - 'show_in_nav_menus' => array( - 'description' => __( 'Whether to make the taxonomy available for selection in navigation menus.', 'gutenberg' ), - 'type' => 'boolean', - ), - 'show_in_quick_edit' => array( - 'description' => __( 'Whether to show the taxonomy in the quick/bulk edit panel.', 'gutenberg' ), - 'type' => 'boolean', - ), - ), - ), - ) - ); -} - -/** - * Gets taxonomy visibility property data. - * - * @see https://core.trac.wordpress.org/ticket/42707 - * - * @param array $object Taxonomy data from REST API. - * @return array Array of taxonomy visibility data. - */ -function gutenberg_get_taxonomy_visibility_data( $object ) { - // Just return existing data for up-to-date Core. - if ( isset( $object['visibility'] ) ) { - return $object['visibility']; - } - - $taxonomy = get_taxonomy( $object['slug'] ); - - return array( - 'public' => $taxonomy->public, - 'publicly_queryable' => $taxonomy->publicly_queryable, - 'show_ui' => $taxonomy->show_ui, - 'show_admin_column' => $taxonomy->show_admin_column, - 'show_in_nav_menus' => $taxonomy->show_in_nav_menus, - 'show_in_quick_edit' => $taxonomy->show_ui, - ); -} - -add_action( 'rest_api_init', 'gutenberg_add_taxonomy_visibility_field' ); - -/** - * Add a permalink template to posts in the post REST API response. - * - * @param WP_REST_Response $response WP REST API response of a post. - * @param WP_Post $post The post being returned. - * @param WP_REST_Request $request WP REST API request. - * @return WP_REST_Response Response containing the permalink_template. - */ -function gutenberg_add_permalink_template_to_posts( $response, $post, $request ) { - if ( 'edit' !== $request['context'] ) { - return $response; - } - - if ( ! function_exists( 'get_sample_permalink' ) ) { - require_once ABSPATH . '/wp-admin/includes/post.php'; - } - - $sample_permalink = get_sample_permalink( $post->ID ); - - $response->data['permalink_template'] = $sample_permalink[0]; - - if ( 'draft' === $post->post_status && ! $post->post_name ) { - $response->data['draft_slug'] = $sample_permalink[1]; - } - - return $response; -} - -/** - * Whenever a post type is registered, ensure we're hooked into it's WP REST API response. - * - * @param string $post_type The newly registered post type. - * @return string That same post type. - */ -function gutenberg_register_permalink_template_function( $post_type ) { - add_filter( "rest_prepare_{$post_type}", 'gutenberg_add_permalink_template_to_posts', 10, 3 ); - return $post_type; -} -add_filter( 'registered_post_type', 'gutenberg_register_permalink_template_function' ); - -/** - * Includes the value for the 'viewable' attribute of a post type resource. - * - * @see https://core.trac.wordpress.org/ticket/43739 - * - * @param object $post_type Post type response object. - * @return boolean Whether or not the post type can be viewed. - */ -function gutenberg_get_post_type_viewable( $post_type ) { - return is_post_type_viewable( $post_type['slug'] ); -} - -/** - * Adds extra fields to the REST API post type response. - * - * @see https://core.trac.wordpress.org/ticket/43739 - * @see https://core.trac.wordpress.org/ticket/43817 - */ -function gutenberg_register_rest_api_post_type_fields() { - register_rest_field( 'type', - 'viewable', - array( - 'get_callback' => 'gutenberg_get_post_type_viewable', - 'schema' => array( - 'description' => __( 'Whether or not the post type can be viewed', 'gutenberg' ), - 'type' => 'boolean', - 'context' => array( 'edit' ), - 'readonly' => true, - ), - ) - ); - -} -add_action( 'rest_api_init', 'gutenberg_register_rest_api_post_type_fields' ); diff --git a/lib/load.php b/lib/load.php index e196610c848ba..4b017d6c5890c 100644 --- a/lib/load.php +++ b/lib/load.php @@ -17,6 +17,7 @@ require dirname( __FILE__ ) . '/blocks.php'; require dirname( __FILE__ ) . '/client-assets.php'; require dirname( __FILE__ ) . '/compat.php'; +require dirname( __FILE__ ) . '/rest-api.php'; require dirname( __FILE__ ) . '/plugin-compat.php'; require dirname( __FILE__ ) . '/i18n.php'; require dirname( __FILE__ ) . '/parser.php'; diff --git a/lib/plugin-compat.php b/lib/plugin-compat.php index 43ca01c3fcd8c..980d4b8236f79 100644 --- a/lib/plugin-compat.php +++ b/lib/plugin-compat.php @@ -17,7 +17,7 @@ /** * WPCOM markdown support causes issues when saving a Gutenberg post by * stripping out the <p> tags. This adds a filter prior to saving the post via - * REST API to disable markdown support. Fixes markdown support provided by + * REST API to disable markdown support. Disables markdown support provided by * plugins Jetpack, JP-Markdown, and WP Editor.MD * * @since 1.3.0 diff --git a/lib/register.php b/lib/register.php index 0103fd9ff9e0c..d5265eb6092f8 100644 --- a/lib/register.php +++ b/lib/register.php @@ -282,7 +282,7 @@ function gutenberg_can_edit_post( $post ) { * @since 1.5.2 * * @param string $post_type The post type. - * @return bool Wehther the post type can be edited with Gutenberg. + * @return bool Whether the post type can be edited with Gutenberg. */ function gutenberg_can_edit_post_type( $post_type ) { $can_edit = true; @@ -344,6 +344,21 @@ function gutenberg_content_has_blocks( $content ) { return false !== strpos( $content, '<!-- wp:' ); } +/** + * Returns the current version of the block format that the content string is using. + * + * If the string doesn't contain blocks, it returns 0. + * + * @since 2.8.0 + * @see gutenberg_content_has_blocks() + * + * @param string $content Content to test. + * @return int The block format version. + */ +function gutenberg_content_block_version( $content ) { + return gutenberg_content_has_blocks( $content ) ? 1 : 0; +} + /** * Adds a "Gutenberg" post state for post tables, if the post contains blocks. * @@ -431,57 +446,6 @@ function gutenberg_register_post_types() { } add_action( 'init', 'gutenberg_register_post_types' ); -/** - * Registers the REST API routes needed by the Gutenberg editor. - * - * @since 2.8.0 - */ -function gutenberg_register_rest_routes() { - $controller = new WP_REST_Block_Renderer_Controller(); - $controller->register_routes(); -} -add_action( 'rest_api_init', 'gutenberg_register_rest_routes' ); - -/** - * Gets revisions details for the selected post. - * - * @since 1.6.0 - * - * @param array $post The post object from the response. - * @return array|null Revisions details or null when no revisions present. - */ -function gutenberg_get_post_revisions( $post ) { - $revisions = wp_get_post_revisions( $post['id'] ); - $revisions_count = count( $revisions ); - if ( 0 === $revisions_count ) { - return null; - } - - $last_revision = array_shift( $revisions ); - - return array( - 'count' => $revisions_count, - 'last_id' => $last_revision->ID, - ); -} - -/** - * Adds the custom field `revisions` to the REST API response of post. - * - * TODO: This is a temporary solution. Next step would be to find a solution that is limited to the editor. - * - * @since 1.6.0 - */ -function gutenberg_register_rest_api_post_revisions() { - register_rest_field( get_post_types( '', 'names' ), - 'revisions', - array( - 'get_callback' => 'gutenberg_get_post_revisions', - ) - ); -} -add_action( 'rest_api_init', 'gutenberg_register_rest_api_post_revisions' ); - /** * Injects a hidden input in the edit form to propagate the information that classic editor is selected. * diff --git a/lib/rest-api.php b/lib/rest-api.php new file mode 100644 index 0000000000000..bc61159010156 --- /dev/null +++ b/lib/rest-api.php @@ -0,0 +1,483 @@ +<?php +/** + * PHP and WordPress configuration compatibility functions for the Gutenberg + * editor plugin changes related to REST API. + * + * @package gutenberg + */ + +if ( ! defined( 'ABSPATH' ) ) { + die( 'Silence is golden.' ); +} + +/** + * Registers the REST API routes needed by the Gutenberg editor. + * + * @since 2.8.0 + */ +function gutenberg_register_rest_routes() { + $controller = new WP_REST_Block_Renderer_Controller(); + $controller->register_routes(); +} +add_action( 'rest_api_init', 'gutenberg_register_rest_routes' ); + +/** + * Includes the value for the custom field `post_type_capabities` inside the REST API response of user. + * + * TODO: This is a temporary solution. Next step would be to edit the WP_REST_Users_Controller, + * once merged into Core. + * + * @since ? + * + * @param array $user An array containing user properties. + * @param string $name The name of the custom field. + * @param WP_REST_Request $request Full details about the REST API request. + * @return object The Post Type capabilities. + */ +function gutenberg_get_post_type_capabilities( $user, $name, $request ) { + $post_type = $request->get_param( 'post_type' ); + $value = new stdClass; + + if ( ! empty( $user['id'] ) && $post_type && post_type_exists( $post_type ) ) { + // The Post Type object contains the Post Type's specific caps. + $post_type_object = get_post_type_object( $post_type ); + + // Loop in the Post Type's caps to validate the User's caps for it. + foreach ( $post_type_object->cap as $post_cap => $post_type_cap ) { + // Ignore caps requiring a post ID. + if ( in_array( $post_cap, array( 'edit_post', 'read_post', 'delete_post' ) ) ) { + continue; + } + + // Set the User's post type capability. + $value->{$post_cap} = user_can( $user['id'], $post_type_cap ); + } + } + + return $value; +} + +/** + * Adds the custom field `post_type_capabities` to the REST API response of user. + * + * TODO: This is a temporary solution. Next step would be to edit the WP_REST_Users_Controller, + * once merged into Core. + * + * @since ? + */ +function gutenberg_register_rest_api_post_type_capabilities() { + register_rest_field( 'user', + 'post_type_capabilities', + array( + 'get_callback' => 'gutenberg_get_post_type_capabilities', + 'schema' => array( + 'description' => __( 'Post Type capabilities for the user.', 'gutenberg' ), + 'type' => 'object', + 'context' => array( 'edit' ), + 'readonly' => true, + ), + ) + ); +} +add_action( 'rest_api_init', 'gutenberg_register_rest_api_post_type_capabilities' ); + +/** + * Make sure oEmbed REST Requests apply the WP Embed security mechanism for WordPress embeds. + * + * @see https://core.trac.wordpress.org/ticket/32522 + * + * TODO: This is a temporary solution. Next step would be to edit the WP_oEmbed_Controller, + * once merged into Core. + * + * @since 2.3.0 + * + * @param WP_HTTP_Response|WP_Error $response The REST Request response. + * @param WP_REST_Server $handler ResponseHandler instance (usually WP_REST_Server). + * @param WP_REST_Request $request Request used to generate the response. + * @return WP_HTTP_Response|object|WP_Error The REST Request response. + */ +function gutenberg_filter_oembed_result( $response, $handler, $request ) { + if ( 'GET' !== $request->get_method() ) { + return $response; + } + + if ( is_wp_error( $response ) && 'oembed_invalid_url' !== $response->get_error_code() ) { + return $response; + } + + // External embeds. + if ( '/oembed/1.0/proxy' === $request->get_route() ) { + if ( is_wp_error( $response ) ) { + // It's possibly a local post, so lets try and retrieve it that way. + $post_id = url_to_postid( $_GET['url'] ); + $data = get_oembed_response_data( $post_id, apply_filters( 'oembed_default_width', 600 ) ); + + if ( $data ) { + // It's a local post! + $response = (object) $data; + } else { + // Try using a classic embed, instead. + global $wp_embed; + $html = $wp_embed->shortcode( array(), $_GET['url'] ); + if ( $html ) { + return array( + 'provider_name' => __( 'Embed Handler', 'gutenberg' ), + 'html' => $html, + ); + } + } + } + + // Make sure the HTML is run through the oembed sanitisation routines. + $response->html = wp_oembed_get( $_GET['url'], $_GET ); + } + + return $response; +} +add_filter( 'rest_request_after_callbacks', 'gutenberg_filter_oembed_result', 10, 3 ); + +/** + * Add additional 'visibility' rest api field to taxonomies. + * + * Used so private taxonomies are not displayed in the UI. + * + * @see https://core.trac.wordpress.org/ticket/42707 + */ +function gutenberg_add_taxonomy_visibility_field() { + register_rest_field( + 'taxonomy', + 'visibility', + array( + 'get_callback' => 'gutenberg_get_taxonomy_visibility_data', + 'schema' => array( + 'description' => __( 'The visibility settings for the taxonomy.', 'gutenberg' ), + 'type' => 'object', + 'context' => array( 'edit' ), + 'readonly' => true, + 'properties' => array( + 'public' => array( + 'description' => __( 'Whether a taxonomy is intended for use publicly either via the admin interface or by front-end users.', 'gutenberg' ), + 'type' => 'boolean', + ), + 'publicly_queryable' => array( + 'description' => __( 'Whether the taxonomy is publicly queryable.', 'gutenberg' ), + 'type' => 'boolean', + ), + 'show_ui' => array( + 'description' => __( 'Whether to generate a default UI for managing this taxonomy.', 'gutenberg' ), + 'type' => 'boolean', + ), + 'show_admin_column' => array( + 'description' => __( 'Whether to allow automatic creation of taxonomy columns on associated post-types table.', 'gutenberg' ), + 'type' => 'boolean', + ), + 'show_in_nav_menus' => array( + 'description' => __( 'Whether to make the taxonomy available for selection in navigation menus.', 'gutenberg' ), + 'type' => 'boolean', + ), + 'show_in_quick_edit' => array( + 'description' => __( 'Whether to show the taxonomy in the quick/bulk edit panel.', 'gutenberg' ), + 'type' => 'boolean', + ), + ), + ), + ) + ); +} + +/** + * Gets taxonomy visibility property data. + * + * @see https://core.trac.wordpress.org/ticket/42707 + * + * @param array $object Taxonomy data from REST API. + * @return array Array of taxonomy visibility data. + */ +function gutenberg_get_taxonomy_visibility_data( $object ) { + // Just return existing data for up-to-date Core. + if ( isset( $object['visibility'] ) ) { + return $object['visibility']; + } + + $taxonomy = get_taxonomy( $object['slug'] ); + + return array( + 'public' => $taxonomy->public, + 'publicly_queryable' => $taxonomy->publicly_queryable, + 'show_ui' => $taxonomy->show_ui, + 'show_admin_column' => $taxonomy->show_admin_column, + 'show_in_nav_menus' => $taxonomy->show_in_nav_menus, + 'show_in_quick_edit' => $taxonomy->show_ui, + ); +} + +add_action( 'rest_api_init', 'gutenberg_add_taxonomy_visibility_field' ); + +/** + * Add a permalink template to posts in the post REST API response. + * + * @param WP_REST_Response $response WP REST API response of a post. + * @param WP_Post $post The post being returned. + * @param WP_REST_Request $request WP REST API request. + * @return WP_REST_Response Response containing the permalink_template. + */ +function gutenberg_add_permalink_template_to_posts( $response, $post, $request ) { + if ( 'edit' !== $request['context'] ) { + return $response; + } + + if ( ! function_exists( 'get_sample_permalink' ) ) { + require_once ABSPATH . '/wp-admin/includes/post.php'; + } + + $sample_permalink = get_sample_permalink( $post->ID, $post->post_title, '' ); + + $response->data['permalink_template'] = $sample_permalink[0]; + $response->data['generated_slug'] = $sample_permalink[1]; + + return $response; +} + +/** + * Add the block format version to post content in the post REST API response. + * + * @todo This will need to be registered to the schema too. + * + * @param WP_REST_Response $response WP REST API response of a post. + * @param WP_Post $post The post being returned. + * @param WP_REST_Request $request WP REST API request. + * @return WP_REST_Response Response containing the block_format. + */ +function gutenberg_add_block_format_to_post_content( $response, $post, $request ) { + if ( 'edit' !== $request['context'] ) { + return $response; + } + + $response_data = $response->get_data(); + if ( isset( $response_data['content'] ) && is_array( $response_data['content'] ) && isset( $response_data['content']['raw'] ) ) { + $response_data['content']['block_format'] = gutenberg_content_block_version( $response_data['content']['raw'] ); + $response->set_data( $response_data ); + } + + return $response; +} + +/** + * Include target schema attributes to links, based on whether the user can. + * + * @param WP_REST_Response $response WP REST API response of a post. + * @param WP_Post $post The post being returned. + * @param WP_REST_Request $request WP REST API request. + * @return WP_REST_Response Response containing the new links. + */ +function gutenberg_add_target_schema_to_links( $response, $post, $request ) { + $new_links = array(); + $orig_links = $response->get_links(); + $post_type = get_post_type_object( $post->post_type ); + // Only Posts can be sticky. + if ( 'post' === $post->post_type && 'edit' === $request['context'] ) { + if ( current_user_can( $post_type->cap->edit_others_posts ) + && current_user_can( $post_type->cap->publish_posts ) ) { + $new_links['https://api.w.org/action-sticky'] = array( + array( + 'title' => __( 'The current user can sticky this post.', 'gutenberg' ), + 'href' => $orig_links['self'][0]['href'], + 'targetSchema' => array( + 'type' => 'object', + 'properties' => array( + 'sticky' => array( + 'type' => 'boolean', + ), + ), + ), + ), + ); + } + } + + $response->add_links( $new_links ); + return $response; +} + +/** + * Whenever a post type is registered, ensure we're hooked into it's WP REST API response. + * + * @param string $post_type The newly registered post type. + * @return string That same post type. + */ +function gutenberg_register_post_prepare_functions( $post_type ) { + add_filter( "rest_prepare_{$post_type}", 'gutenberg_add_permalink_template_to_posts', 10, 3 ); + add_filter( "rest_prepare_{$post_type}", 'gutenberg_add_block_format_to_post_content', 10, 3 ); + add_filter( "rest_prepare_{$post_type}", 'gutenberg_add_target_schema_to_links', 10, 3 ); + return $post_type; +} +add_filter( 'registered_post_type', 'gutenberg_register_post_prepare_functions' ); + +/** + * Includes the value for the 'viewable' attribute of a post type resource. + * + * @see https://core.trac.wordpress.org/ticket/43739 + * + * @param object $post_type Post type response object. + * @return boolean Whether or not the post type can be viewed. + */ +function gutenberg_get_post_type_viewable( $post_type ) { + return is_post_type_viewable( $post_type['slug'] ); +} + +/** + * Adds the 'viewable' attribute to the REST API response of a post type. + * + * @see https://core.trac.wordpress.org/ticket/43739 + */ +function gutenberg_register_rest_api_post_type_viewable() { + register_rest_field( 'type', + 'viewable', + array( + 'get_callback' => 'gutenberg_get_post_type_viewable', + 'schema' => array( + 'description' => __( 'Whether or not the post type can be viewed', 'gutenberg' ), + 'type' => 'boolean', + 'context' => array( 'edit' ), + 'readonly' => true, + ), + ) + ); +} +add_action( 'rest_api_init', 'gutenberg_register_rest_api_post_type_viewable' ); + +/** + * Gets revisions details for the selected post. + * + * @since 1.6.0 + * + * @param array $post The post object from the response. + * @return array|null Revisions details or null when no revisions present. + */ +function gutenberg_get_post_revisions( $post ) { + $revisions = wp_get_post_revisions( $post['id'] ); + $revisions_count = count( $revisions ); + if ( 0 === $revisions_count ) { + return null; + } + + $last_revision = array_shift( $revisions ); + + return array( + 'count' => $revisions_count, + 'last_id' => $last_revision->ID, + ); +} + +/** + * Adds the custom field `revisions` to the REST API response of post. + * + * TODO: This is a temporary solution. Next step would be to find a solution that is limited to the editor. + * + * @since 1.6.0 + */ +function gutenberg_register_rest_api_post_revisions() { + register_rest_field( get_post_types( '', 'names' ), + 'revisions', + array( + 'get_callback' => 'gutenberg_get_post_revisions', + ) + ); +} +add_action( 'rest_api_init', 'gutenberg_register_rest_api_post_revisions' ); + +/** + * Ensure that the wp-json index contains the 'theme-supports' setting as + * part of its site info elements. + * + * @param WP_REST_Response $response WP REST API response of the wp-json index. + * @return WP_REST_Response Response that contains theme-supports. + */ +function gutenberg_ensure_wp_json_has_theme_supports( $response ) { + $site_info = $response->get_data(); + if ( ! array_key_exists( 'theme_supports', $site_info ) ) { + $site_info['theme_supports'] = array(); + } + if ( ! array_key_exists( 'formats', $site_info['theme_supports'] ) ) { + $formats = get_theme_support( 'post-formats' ); + $formats = is_array( $formats ) ? array_values( $formats[0] ) : array(); + $formats = array_merge( array( 'standard' ), $formats ); + + $site_info['theme_supports']['formats'] = $formats; + } + if ( ! array_key_exists( 'post-thumbnails', $site_info['theme_supports'] ) ) { + if ( get_theme_support( 'post-thumbnails' ) ) { + $site_info['theme_supports']['post-thumbnails'] = true; + } + } + $response->set_data( $site_info ); + return $response; +} +add_filter( 'rest_index', 'gutenberg_ensure_wp_json_has_theme_supports' ); + +/** + * Handle any necessary checks early. + * + * @param WP_HTTP_Response $response Result to send to the client. Usually a WP_REST_Response. + * @param WP_REST_Server $handler ResponseHandler instance (usually WP_REST_Server). + * @param WP_REST_Request $request Request used to generate the response. + */ +function gutenberg_handle_early_callback_checks( $response, $handler, $request ) { + if ( '/wp/v2/users' === $request->get_route() ) { + if ( ! empty( $request['who'] ) && 'authors' === $request['who'] ) { + $can_view = false; + $types = get_post_types( array( 'show_in_rest' => true ), 'objects' ); + foreach ( $types as $type ) { + if ( post_type_supports( $type->name, 'author' ) + && current_user_can( $type->cap->edit_posts ) ) { + $can_view = true; + } + } + if ( ! $can_view ) { + return new WP_Error( 'rest_forbidden_who', __( 'Sorry, you are not allowed to query users by this parameter.', 'gutenberg' ), array( 'status' => rest_authorization_required_code() ) ); + } + } + } + return $response; +} +add_filter( 'rest_request_before_callbacks', 'gutenberg_handle_early_callback_checks', 10, 3 ); + +/** + * Include additional query parameters on the user query endpoint. + * + * @see https://core.trac.wordpress.org/ticket/42202 + * + * @param array $query_params JSON Schema-formatted collection parameters. + * @return array + */ +function gutenberg_filter_user_collection_parameters( $query_params ) { + $query_params['who'] = array( + 'description' => __( 'Limit result set to users who are considered authors.', 'gutenberg' ), + 'type' => 'string', + 'enum' => array( + 'authors', + ), + ); + return $query_params; +} +add_filter( 'rest_user_collection_params', 'gutenberg_filter_user_collection_parameters' ); + +/** + * Filter user collection query parameters to include specific behavior. + * + * @see https://core.trac.wordpress.org/ticket/42202 + * + * @param array $prepared_args Array of arguments for WP_User_Query. + * @param WP_REST_Request $request The current request. + * @return array + */ +function gutenberg_filter_user_query_arguments( $prepared_args, $request ) { + if ( ! empty( $request['who'] ) && 'authors' === $request['who'] ) { + $prepared_args['who'] = 'authors'; + if ( isset( $prepared_args['has_published_posts'] ) ) { + unset( $prepared_args['has_published_posts'] ); + } + } + return $prepared_args; +} +add_filter( 'rest_user_query', 'gutenberg_filter_user_query_arguments', 10, 2 ); diff --git a/package-lock.json b/package-lock.json index 868df2f4af7cd..5f9eec08de3c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "2.7.0", + "version": "2.8.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -40,9 +40,9 @@ "integrity": "sha512-mOhhTrzieV6VO7odgzFGFapiwRK0ei8RZRhfzHhb6cpX3QM8XXuCLXWjN8qBB7JReDdUR80V3LFfFrGUYevhNg==", "dev": true, "requires": { - "chalk": "2.3.1", - "esutils": "2.0.2", - "js-tokens": "3.0.2" + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^3.0.0" }, "dependencies": { "ansi-styles": { @@ -51,7 +51,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -60,9 +60,9 @@ "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "5.2.0" + "ansi-styles": "^3.2.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.2.0" } }, "has-flag": { @@ -77,7 +77,7 @@ "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -91,7 +91,7 @@ "@babel/code-frame": "7.0.0-beta.31", "@babel/types": "7.0.0-beta.31", "babylon": "7.0.0-beta.31", - "lodash": "4.17.5" + "lodash": "^4.2.0" }, "dependencies": { "@babel/code-frame": { @@ -100,9 +100,9 @@ "integrity": "sha512-yd7CkUughvHQoEahQqcMdrZw6o/6PwUxiRkfZuVDVHCDe77mysD/suoNyk5mK6phTnRW1kyIbPHyCJgxw++LXg==", "dev": true, "requires": { - "chalk": "2.3.0", - "esutils": "2.0.2", - "js-tokens": "3.0.2" + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^3.0.0" } }, "ansi-styles": { @@ -111,7 +111,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "babylon": { @@ -126,9 +126,9 @@ "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" + "ansi-styles": "^3.1.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^4.0.0" } }, "supports-color": { @@ -137,7 +137,7 @@ "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "^2.0.0" } } } @@ -152,10 +152,10 @@ "@babel/helper-function-name": "7.0.0-beta.31", "@babel/types": "7.0.0-beta.31", "babylon": "7.0.0-beta.31", - "debug": "3.1.0", - "globals": "10.4.0", - "invariant": "2.2.2", - "lodash": "4.17.5" + "debug": "^3.0.1", + "globals": "^10.0.0", + "invariant": "^2.2.0", + "lodash": "^4.2.0" }, "dependencies": { "@babel/code-frame": { @@ -164,9 +164,9 @@ "integrity": "sha512-yd7CkUughvHQoEahQqcMdrZw6o/6PwUxiRkfZuVDVHCDe77mysD/suoNyk5mK6phTnRW1kyIbPHyCJgxw++LXg==", "dev": true, "requires": { - "chalk": "2.3.0", - "esutils": "2.0.2", - "js-tokens": "3.0.2" + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^3.0.0" } }, "ansi-styles": { @@ -175,7 +175,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "babylon": { @@ -190,9 +190,9 @@ "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" + "ansi-styles": "^3.1.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^4.0.0" } }, "debug": { @@ -216,7 +216,7 @@ "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "^2.0.0" } } } @@ -227,9 +227,9 @@ "integrity": "sha512-exAHB+NeFGxkfQ5dSUD03xl3zYGneeSk2Mw2ldTt/nTvYxuDiuSp3DlxgUBgzbdTFG4fbwPk0WtKWOoTXCmNGg==", "dev": true, "requires": { - "esutils": "2.0.2", - "lodash": "4.17.5", - "to-fast-properties": "2.0.0" + "esutils": "^2.0.2", + "lodash": "^4.2.0", + "to-fast-properties": "^2.0.0" } }, "@romainberger/css-diff": { @@ -238,8 +238,8 @@ "integrity": "sha1-ztOHU11PQqQqwf4TwJ3pf1rhNEw=", "dev": true, "requires": { - "lodash.merge": "4.6.1", - "postcss": "5.2.18" + "lodash.merge": "^4.4.0", + "postcss": "^5.0.21" } }, "@sindresorhus/is": { @@ -259,7 +259,7 @@ "resolved": "https://registry.npmjs.org/@wordpress/a11y/-/a11y-1.0.6.tgz", "integrity": "sha512-IyL7KzYGzMEg+FFyTrQzD/CUfABYCXOvmnm29vBZBA3JMER1ep3/+NFDe6CpWVEEMCw94oj2gUOSQ4YKVgDjUQ==", "requires": { - "@wordpress/dom-ready": "1.0.3" + "@wordpress/dom-ready": "^1.0.3" } }, "@wordpress/autop": { @@ -273,8 +273,8 @@ "integrity": "sha512-YNcXr33fUUHg9HncNhUNkG2jnCAtnvC5hN0ZdDdTVe7r7aR4l+gCTCZXvM/0SHqikrNhRNjSrE5ZM/3u2zsOyw==", "dev": true, "requires": { - "gettext-parser": "1.3.1", - "lodash": "4.17.5" + "gettext-parser": "^1.3.1", + "lodash": "^4.17.5" }, "dependencies": { "gettext-parser": { @@ -283,8 +283,8 @@ "integrity": "sha512-W4t55eB/c7WrH0gbCHFiHuaEnJ1WiPJVnbFFiNEoh2QkOmuSLxs0PmJDGAmCQuTJCU740Fmb6D+2D/2xECWZGQ==", "dev": true, "requires": { - "encoding": "0.1.12", - "safe-buffer": "5.1.1" + "encoding": "^0.1.12", + "safe-buffer": "^5.1.1" } }, "lodash": { @@ -301,11 +301,11 @@ "integrity": "sha512-DJ4P3gwj8gUQy94GeaVdHTh2JVKQ8eNFwCQi4GDhCcq78KDAJjRO497ZwoJWJR0MVq5a3XfsrJ6fVf7S8APfUw==", "dev": true, "requires": { - "@wordpress/browserslist-config": "2.1.3", - "babel-plugin-transform-object-rest-spread": "6.26.0", - "babel-plugin-transform-react-jsx": "6.24.1", - "babel-plugin-transform-runtime": "6.23.0", - "babel-preset-env": "1.6.1" + "@wordpress/browserslist-config": "^2.1.3", + "babel-plugin-transform-object-rest-spread": "^6.23.0", + "babel-plugin-transform-react-jsx": "^6.24.1", + "babel-plugin-transform-runtime": "^6.23.0", + "babel-preset-env": "^1.6.1" } }, "@wordpress/browserslist-config": { @@ -320,13 +320,13 @@ "integrity": "sha512-7GDENg5juXusGye4JqKAjfAr1EcKNNmJ6GUnnUrdOkXgjnT5CoAw2ADJxvtALtl4vx6o/nqzJxp9YQ/bZi85Dg==", "dev": true, "requires": { - "escape-string-regexp": "1.0.5" + "escape-string-regexp": "^1.0.5" } }, "@wordpress/dom-ready": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@wordpress/dom-ready/-/dom-ready-1.0.3.tgz", - "integrity": "sha512-1tmJLO2NDc45wxnUWv6F/4q8/00qSgqLvBXBKl7IayLvJbG25vt7lMQkdSfiY2gTwsujAqOgOuJdO5VOGuqQNg==" + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@wordpress/dom-ready/-/dom-ready-1.0.4.tgz", + "integrity": "sha512-FxSH0A23Xs0t/ZcvfiQly7P1V3pKsHQGjja+oISKV2NosfIcWjc3JTDFZYLcKjmSREozKqMoqVIPY+h1CP2ehw==" }, "@wordpress/hooks": { "version": "1.1.6", @@ -338,10 +338,10 @@ "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-1.1.0.tgz", "integrity": "sha512-zzpyhSaVOv5iLIwkJ4nrPt7FO+50xHlGDSJljfGdS+ypvFAnEHpCkkJ84F3NhHaYIIZqMEn5lC4k1edIaIqAbA==", "requires": { - "gettext-parser": "1.3.1", - "jed": "1.1.1", - "lodash": "4.17.5", - "memize": "1.0.5" + "gettext-parser": "^1.3.1", + "jed": "^1.1.1", + "lodash": "^4.17.5", + "memize": "^1.0.5" }, "dependencies": { "lodash": { @@ -351,14 +351,19 @@ } } }, + "@wordpress/is-shallow-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@wordpress/is-shallow-equal/-/is-shallow-equal-1.0.1.tgz", + "integrity": "sha512-HRvGJHTA5SGONxHfnK4xm66PXnDE7qp/J9eu/WA1cyNauCtRzdS1BTELXn3ONnh73xF2CV/ukLakIlIk6eFA1Q==" + }, "@wordpress/jest-console": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@wordpress/jest-console/-/jest-console-1.0.5.tgz", "integrity": "sha512-PwhDL2H4EI6adnGyQo0v4p8zRokjNu4DJ3EDZpH9dmNK0/G9hKuuIAwGN2e9RGyAiqipddCkt5y4qzH1mx8PJw==", "dev": true, "requires": { - "jest-matcher-utils": "22.4.0", - "lodash": "4.17.5" + "jest-matcher-utils": "^22.4.0", + "lodash": "^4.17.4" } }, "@wordpress/jest-preset-default": { @@ -367,12 +372,12 @@ "integrity": "sha512-1+uREUyhMWBanX4qceevrFEuaFm/gRKIKiDb36Wc5X02ODEaUaQ6qjIvrD6fariYtvxQpjC8TBjKapsvSaqHHw==", "dev": true, "requires": { - "@wordpress/jest-console": "1.0.5", - "babel-jest": "22.4.0", - "enzyme": "3.3.0", - "enzyme-adapter-react-16": "1.1.1", - "jest-enzyme": "4.2.0", - "pegjs": "0.10.0" + "@wordpress/jest-console": "^1.0.5", + "babel-jest": "^22.1.0", + "enzyme": "^3.3.0", + "enzyme-adapter-react-16": "^1.1.1", + "jest-enzyme": "^4.0.2", + "pegjs": "^0.10.0" } }, "@wordpress/scripts": { @@ -381,11 +386,11 @@ "integrity": "sha512-gbtpV6i4SKi7Pya8qeB6N9FGyWAMBtyAsRnFg6Lv6Ejh0Pk9R2Aa71GjX4ZAMzq1LFuVNdhciIybdxDPSsP8sQ==", "dev": true, "requires": { - "@wordpress/babel-preset-default": "1.2.0", - "@wordpress/jest-preset-default": "1.0.3", - "cross-spawn": "5.1.0", - "jest": "22.4.0", - "read-pkg-up": "3.0.0" + "@wordpress/babel-preset-default": "^1.1.1", + "@wordpress/jest-preset-default": "^1.0.3", + "cross-spawn": "^5.1.0", + "jest": "^22.4.0", + "read-pkg-up": "^3.0.0" }, "dependencies": { "find-up": { @@ -394,7 +399,7 @@ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "2.0.0" + "locate-path": "^2.0.0" } }, "load-json-file": { @@ -403,10 +408,10 @@ "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "parse-json": "4.0.0", - "pify": "3.0.0", - "strip-bom": "3.0.0" + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" } }, "parse-json": { @@ -415,8 +420,8 @@ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", "dev": true, "requires": { - "error-ex": "1.3.1", - "json-parse-better-errors": "1.0.1" + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" } }, "path-type": { @@ -425,7 +430,7 @@ "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "dev": true, "requires": { - "pify": "3.0.0" + "pify": "^3.0.0" } }, "pify": { @@ -440,9 +445,9 @@ "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", "dev": true, "requires": { - "load-json-file": "4.0.0", - "normalize-package-data": "2.4.0", - "path-type": "3.0.0" + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" } }, "read-pkg-up": { @@ -451,23 +456,23 @@ "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", "dev": true, "requires": { - "find-up": "2.1.0", - "read-pkg": "3.0.0" + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" } } } }, "@wordpress/url": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@wordpress/url/-/url-1.0.3.tgz", - "integrity": "sha512-0nqf62SWS0DiFnSD5miszPuAey01OrBsdqBFzOCYChjtB7AZm+8Q06qpeV02rpLY5FHaUxVgL+2JRljYIAFlpA==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@wordpress/url/-/url-1.1.0.tgz", + "integrity": "sha512-KT+C4vCh6kYux5fAcyM9Nqj0pVKxYptJde3AcggvZ1rJ71+LNJI63EQFmUhJU0reaNduzQ+YFub9tcQrc7ckmA==" }, "@wordpress/wordcount": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@wordpress/wordcount/-/wordcount-1.0.0.tgz", "integrity": "sha512-N7JyhF+wdFDgDJbxZdscQ0vivdimvk/CjmNdonoSx4QHqgYfZOSSsFgpYg/rzeRBRFfT+e3RSu7LTdOZ/t54LA==", "requires": { - "lodash": "4.17.5" + "lodash": "^4.17.4" } }, "abab": { @@ -493,7 +498,7 @@ "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==", "dev": true, "requires": { - "acorn": "5.4.1" + "acorn": "^5.0.0" } }, "acorn-globals": { @@ -502,7 +507,7 @@ "integrity": "sha512-KjZwU26uG3u6eZcfGbTULzFcsoz6pegNKtHPksZPOUsiKo5bUmiBPa38FuHZ/Eun+XYh/JCCkS9AS3Lu4McQOQ==", "dev": true, "requires": { - "acorn": "5.4.1" + "acorn": "^5.0.0" } }, "acorn-jsx": { @@ -511,7 +516,7 @@ "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", "dev": true, "requires": { - "acorn": "3.3.0" + "acorn": "^3.0.4" }, "dependencies": { "acorn": { @@ -528,7 +533,7 @@ "integrity": "sha512-c+R/U5X+2zz2+UCrCFv6odQzJdoqI+YecuhnAJLa1zYaMc13zPfwMwZrr91Pd1DYNo/yPRbiM4WVf9whgwFsIg==", "dev": true, "requires": { - "es6-promisify": "5.0.0" + "es6-promisify": "^5.0.0" } }, "ajv": { @@ -537,10 +542,10 @@ "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", "dev": true, "requires": { - "co": "4.6.0", - "fast-deep-equal": "1.0.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1" + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" } }, "ajv-keywords": { @@ -555,9 +560,9 @@ "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, "requires": { - "kind-of": "3.2.2", - "longest": "1.0.1", - "repeat-string": "1.6.1" + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" } }, "alphanum-sort": { @@ -601,8 +606,8 @@ "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", "dev": true, "requires": { - "micromatch": "2.3.11", - "normalize-path": "2.1.1" + "micromatch": "^2.1.5", + "normalize-path": "^2.0.0" } }, "append-transform": { @@ -611,7 +616,7 @@ "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", "dev": true, "requires": { - "default-require-extensions": "1.0.0" + "default-require-extensions": "^1.0.0" } }, "aproba": { @@ -626,8 +631,8 @@ "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", "dev": true, "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.3" + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" } }, "argparse": { @@ -636,7 +641,7 @@ "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", "dev": true, "requires": { - "sprintf-js": "1.0.3" + "sprintf-js": "~1.0.2" }, "dependencies": { "sprintf-js": { @@ -660,7 +665,7 @@ "dev": true, "requires": { "ast-types-flow": "0.0.7", - "commander": "2.14.1" + "commander": "^2.11.0" } }, "arr-diff": { @@ -669,7 +674,7 @@ "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, "requires": { - "arr-flatten": "1.1.0" + "arr-flatten": "^1.0.1" } }, "arr-flatten": { @@ -708,8 +713,8 @@ "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", "dev": true, "requires": { - "define-properties": "1.1.2", - "es-abstract": "1.10.0" + "define-properties": "^1.1.2", + "es-abstract": "^1.7.0" } }, "array-union": { @@ -718,7 +723,7 @@ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", "dev": true, "requires": { - "array-uniq": "1.0.3" + "array-uniq": "^1.0.1" } }, "array-uniq": { @@ -756,9 +761,9 @@ "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", "dev": true, "requires": { - "bn.js": "4.11.8", - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" } }, "assert": { @@ -806,7 +811,7 @@ "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", "dev": true, "requires": { - "lodash": "4.17.5" + "lodash": "^4.14.0" } }, "async-each": { @@ -845,12 +850,12 @@ "integrity": "sha512-xBVQpGAcSNNS1PBnEfT+F9VF8ZJeoKZ121I3OVQ0n1F0SqVuj4oLI6yFeEviPV8Z/GjoqBRXcYis0oSS8zjNEg==", "dev": true, "requires": { - "browserslist": "3.2.4", - "caniuse-lite": "1.0.30000821", - "normalize-range": "0.1.2", - "num2fraction": "1.2.2", - "postcss": "6.0.21", - "postcss-value-parser": "3.3.0" + "browserslist": "^3.2.0", + "caniuse-lite": "^1.0.30000817", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^6.0.20", + "postcss-value-parser": "^3.2.3" }, "dependencies": { "ansi-styles": { @@ -859,7 +864,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "browserslist": { @@ -868,8 +873,8 @@ "integrity": "sha512-Dwe62y/fNAcMfknzGJnkh7feISrrN0SmRvMFozb+Y2+qg7rfTIH5MS8yHzaIXcEWl8fPeIcdhZNQi1Lux+7dlg==", "dev": true, "requires": { - "caniuse-lite": "1.0.30000821", - "electron-to-chromium": "1.3.41" + "caniuse-lite": "^1.0.30000821", + "electron-to-chromium": "^1.3.41" } }, "caniuse-lite": { @@ -884,9 +889,9 @@ "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.3.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "electron-to-chromium": { @@ -907,9 +912,9 @@ "integrity": "sha512-y/bKfbQz2Nn/QBC08bwvYUxEFOVGfPIUOTsJ2CK5inzlXW9SdYR1x4pEsG9blRAF/PX+wRNdOah+gx/hv4q7dw==", "dev": true, "requires": { - "chalk": "2.3.2", - "source-map": "0.6.1", - "supports-color": "5.3.0" + "chalk": "^2.3.2", + "source-map": "^0.6.1", + "supports-color": "^5.3.0" } }, "source-map": { @@ -924,7 +929,7 @@ "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -932,7 +937,7 @@ "autosize": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/autosize/-/autosize-4.0.1.tgz", - "integrity": "sha512-Sapd3XwNqZin0VW0DFiAGfgr9s2d1Sudj2+hnE/jj6aJUKXvlaimkY+FFB2Xzm3nfD7tCaMSC2jo4sVB2EOqpw==" + "integrity": "sha1-Ti+J0A4pDdmOH5VVWozLkenHpBo=" }, "aws-sign2": { "version": "0.7.0", @@ -961,9 +966,9 @@ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", "dev": true, "requires": { - "chalk": "1.1.3", - "esutils": "2.0.2", - "js-tokens": "3.0.2" + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" } }, "babel-core": { @@ -972,25 +977,25 @@ "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=", "dev": true, "requires": { - "babel-code-frame": "6.26.0", - "babel-generator": "6.26.1", - "babel-helpers": "6.24.1", - "babel-messages": "6.23.0", - "babel-register": "6.26.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "convert-source-map": "1.5.1", - "debug": "2.6.9", - "json5": "0.5.1", - "lodash": "4.17.5", - "minimatch": "3.0.4", - "path-is-absolute": "1.0.1", - "private": "0.1.8", - "slash": "1.0.0", - "source-map": "0.5.7" + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.0", + "debug": "^2.6.8", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.7", + "slash": "^1.0.0", + "source-map": "^0.5.6" } }, "babel-eslint": { @@ -1011,9 +1016,9 @@ "integrity": "sha512-yd7CkUughvHQoEahQqcMdrZw6o/6PwUxiRkfZuVDVHCDe77mysD/suoNyk5mK6phTnRW1kyIbPHyCJgxw++LXg==", "dev": true, "requires": { - "chalk": "2.3.0", - "esutils": "2.0.2", - "js-tokens": "3.0.2" + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^3.0.0" } }, "ansi-styles": { @@ -1022,7 +1027,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "babylon": { @@ -1037,9 +1042,9 @@ "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" + "ansi-styles": "^3.1.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^4.0.0" } }, "supports-color": { @@ -1048,7 +1053,7 @@ "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "^2.0.0" } } } @@ -1059,14 +1064,14 @@ "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", "dev": true, "requires": { - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "detect-indent": "4.0.0", - "jsesc": "1.3.0", - "lodash": "4.17.5", - "source-map": "0.5.7", - "trim-right": "1.0.1" + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" }, "dependencies": { "jsesc": { @@ -1083,9 +1088,9 @@ "integrity": "sha1-FMGeXxQte0fxmlJDHlKxzLxAozA=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" } }, "babel-helper-builder-binary-assignment-operator-visitor": { @@ -1094,9 +1099,9 @@ "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", "dev": true, "requires": { - "babel-helper-explode-assignable-expression": "6.24.1", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-explode-assignable-expression": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" } }, "babel-helper-builder-react-jsx": { @@ -1105,9 +1110,9 @@ "integrity": "sha1-Of+DE7dci2Xc7/HzHTg+D/KkCKA=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "esutils": "2.0.2" + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "esutils": "^2.0.2" } }, "babel-helper-call-delegate": { @@ -1116,10 +1121,10 @@ "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", "dev": true, "requires": { - "babel-helper-hoist-variables": "6.24.1", - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" } }, "babel-helper-define-map": { @@ -1128,10 +1133,10 @@ "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", "dev": true, "requires": { - "babel-helper-function-name": "6.24.1", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "lodash": "4.17.5" + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" } }, "babel-helper-explode-assignable-expression": { @@ -1140,9 +1145,9 @@ "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" } }, "babel-helper-explode-class": { @@ -1151,10 +1156,10 @@ "integrity": "sha1-fcKjkQ3uAHBW4eMdZAztPVTqqes=", "dev": true, "requires": { - "babel-helper-bindify-decorators": "6.24.1", - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-bindify-decorators": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" } }, "babel-helper-function-name": { @@ -1163,11 +1168,11 @@ "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", "dev": true, "requires": { - "babel-helper-get-function-arity": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" } }, "babel-helper-get-function-arity": { @@ -1176,8 +1181,8 @@ "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" } }, "babel-helper-hoist-variables": { @@ -1186,8 +1191,8 @@ "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" } }, "babel-helper-optimise-call-expression": { @@ -1196,8 +1201,8 @@ "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" } }, "babel-helper-regex": { @@ -1206,9 +1211,9 @@ "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "lodash": "4.17.5" + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" } }, "babel-helper-remap-async-to-generator": { @@ -1217,11 +1222,11 @@ "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", "dev": true, "requires": { - "babel-helper-function-name": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" } }, "babel-helper-replace-supers": { @@ -1230,12 +1235,12 @@ "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", "dev": true, "requires": { - "babel-helper-optimise-call-expression": "6.24.1", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" } }, "babel-helpers": { @@ -1244,8 +1249,8 @@ "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" } }, "babel-jest": { @@ -1254,8 +1259,8 @@ "integrity": "sha512-A/safCd5jSf1D98XoHCN3YYuGurtUPntuPh8b7UxsLNfEp/QC8UwdL+VEGSLN5Fk3+tS/Jdbf5NK/T2it8RGYw==", "dev": true, "requires": { - "babel-plugin-istanbul": "4.1.5", - "babel-preset-jest": "22.2.0" + "babel-plugin-istanbul": "^4.1.5", + "babel-preset-jest": "^22.2.0" } }, "babel-loader": { @@ -1264,9 +1269,9 @@ "integrity": "sha512-/hbyEvPzBJuGpk9o80R0ZyTej6heEOr59GoEUtn8qFKbnx4cJm9FWES6J/iv644sYgrtVw9JJQkjaLW/bqb5gw==", "dev": true, "requires": { - "find-cache-dir": "1.0.0", - "loader-utils": "1.1.0", - "mkdirp": "0.5.1" + "find-cache-dir": "^1.0.0", + "loader-utils": "^1.0.2", + "mkdirp": "^0.5.1" } }, "babel-messages": { @@ -1275,7 +1280,7 @@ "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" } }, "babel-plugin-check-es2015-constants": { @@ -1284,7 +1289,7 @@ "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" } }, "babel-plugin-istanbul": { @@ -1293,9 +1298,9 @@ "integrity": "sha1-Z2DN2Xf0EdPhdbsGTyvDJ9mbK24=", "dev": true, "requires": { - "find-up": "2.1.0", - "istanbul-lib-instrument": "1.9.2", - "test-exclude": "4.2.0" + "find-up": "^2.1.0", + "istanbul-lib-instrument": "^1.7.5", + "test-exclude": "^4.1.1" }, "dependencies": { "find-up": { @@ -1304,7 +1309,7 @@ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "2.0.0" + "locate-path": "^2.0.0" } } } @@ -1393,9 +1398,9 @@ "integrity": "sha1-8FiQAUX9PpkHpt3yjaWfIVJYpds=", "dev": true, "requires": { - "babel-helper-remap-async-to-generator": "6.24.1", - "babel-plugin-syntax-async-generators": "6.13.0", - "babel-runtime": "6.26.0" + "babel-helper-remap-async-to-generator": "^6.24.1", + "babel-plugin-syntax-async-generators": "^6.5.0", + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-async-to-generator": { @@ -1404,9 +1409,9 @@ "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", "dev": true, "requires": { - "babel-helper-remap-async-to-generator": "6.24.1", - "babel-plugin-syntax-async-functions": "6.13.0", - "babel-runtime": "6.26.0" + "babel-helper-remap-async-to-generator": "^6.24.1", + "babel-plugin-syntax-async-functions": "^6.8.0", + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-class-constructor-call": { @@ -1415,9 +1420,9 @@ "integrity": "sha1-gNwoVQWsBn3LjWxl4vbxGrd2Xvk=", "dev": true, "requires": { - "babel-plugin-syntax-class-constructor-call": "6.18.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" + "babel-plugin-syntax-class-constructor-call": "^6.18.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" } }, "babel-plugin-transform-class-properties": { @@ -1426,10 +1431,10 @@ "integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=", "dev": true, "requires": { - "babel-helper-function-name": "6.24.1", - "babel-plugin-syntax-class-properties": "6.13.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" + "babel-helper-function-name": "^6.24.1", + "babel-plugin-syntax-class-properties": "^6.8.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" } }, "babel-plugin-transform-decorators": { @@ -1438,11 +1443,11 @@ "integrity": "sha1-eIAT2PjGtSIr33s0Q5Df13Vp4k0=", "dev": true, "requires": { - "babel-helper-explode-class": "6.24.1", - "babel-plugin-syntax-decorators": "6.13.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-explode-class": "^6.24.1", + "babel-plugin-syntax-decorators": "^6.13.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-types": "^6.24.1" } }, "babel-plugin-transform-es2015-arrow-functions": { @@ -1451,7 +1456,7 @@ "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-block-scoped-functions": { @@ -1460,7 +1465,7 @@ "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-block-scoping": { @@ -1469,11 +1474,11 @@ "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "lodash": "4.17.5" + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" } }, "babel-plugin-transform-es2015-classes": { @@ -1482,15 +1487,15 @@ "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", "dev": true, "requires": { - "babel-helper-define-map": "6.26.0", - "babel-helper-function-name": "6.24.1", - "babel-helper-optimise-call-expression": "6.24.1", - "babel-helper-replace-supers": "6.24.1", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-define-map": "^6.24.1", + "babel-helper-function-name": "^6.24.1", + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-helper-replace-supers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" } }, "babel-plugin-transform-es2015-computed-properties": { @@ -1499,8 +1504,8 @@ "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" } }, "babel-plugin-transform-es2015-destructuring": { @@ -1509,7 +1514,7 @@ "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-duplicate-keys": { @@ -1518,8 +1523,8 @@ "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" } }, "babel-plugin-transform-es2015-for-of": { @@ -1528,7 +1533,7 @@ "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-function-name": { @@ -1537,9 +1542,9 @@ "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", "dev": true, "requires": { - "babel-helper-function-name": "6.24.1", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" } }, "babel-plugin-transform-es2015-literals": { @@ -1548,7 +1553,7 @@ "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-modules-amd": { @@ -1557,9 +1562,9 @@ "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", "dev": true, "requires": { - "babel-plugin-transform-es2015-modules-commonjs": "6.26.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" } }, "babel-plugin-transform-es2015-modules-commonjs": { @@ -1568,10 +1573,10 @@ "integrity": "sha1-DYOUApt9xqvhqX7xgeAHWN0uXYo=", "dev": true, "requires": { - "babel-plugin-transform-strict-mode": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-types": "6.26.0" + "babel-plugin-transform-strict-mode": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-types": "^6.26.0" } }, "babel-plugin-transform-es2015-modules-systemjs": { @@ -1580,9 +1585,9 @@ "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", "dev": true, "requires": { - "babel-helper-hoist-variables": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" } }, "babel-plugin-transform-es2015-modules-umd": { @@ -1591,9 +1596,9 @@ "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", "dev": true, "requires": { - "babel-plugin-transform-es2015-modules-amd": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" } }, "babel-plugin-transform-es2015-object-super": { @@ -1602,8 +1607,8 @@ "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", "dev": true, "requires": { - "babel-helper-replace-supers": "6.24.1", - "babel-runtime": "6.26.0" + "babel-helper-replace-supers": "^6.24.1", + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-parameters": { @@ -1612,12 +1617,12 @@ "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", "dev": true, "requires": { - "babel-helper-call-delegate": "6.24.1", - "babel-helper-get-function-arity": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-call-delegate": "^6.24.1", + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" } }, "babel-plugin-transform-es2015-shorthand-properties": { @@ -1626,8 +1631,8 @@ "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" } }, "babel-plugin-transform-es2015-spread": { @@ -1636,7 +1641,7 @@ "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-sticky-regex": { @@ -1645,9 +1650,9 @@ "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", "dev": true, "requires": { - "babel-helper-regex": "6.26.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" } }, "babel-plugin-transform-es2015-template-literals": { @@ -1656,7 +1661,7 @@ "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-typeof-symbol": { @@ -1665,7 +1670,7 @@ "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-unicode-regex": { @@ -1674,9 +1679,9 @@ "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", "dev": true, "requires": { - "babel-helper-regex": "6.26.0", - "babel-runtime": "6.26.0", - "regexpu-core": "2.0.0" + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "regexpu-core": "^2.0.0" } }, "babel-plugin-transform-exponentiation-operator": { @@ -1685,9 +1690,9 @@ "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", "dev": true, "requires": { - "babel-helper-builder-binary-assignment-operator-visitor": "6.24.1", - "babel-plugin-syntax-exponentiation-operator": "6.13.0", - "babel-runtime": "6.26.0" + "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", + "babel-plugin-syntax-exponentiation-operator": "^6.8.0", + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-export-extensions": { @@ -1696,8 +1701,8 @@ "integrity": "sha1-U3OLR+deghhYnuqUbLvTkQm75lM=", "dev": true, "requires": { - "babel-plugin-syntax-export-extensions": "6.13.0", - "babel-runtime": "6.26.0" + "babel-plugin-syntax-export-extensions": "^6.8.0", + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-flow-strip-types": { @@ -1706,8 +1711,8 @@ "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=", "dev": true, "requires": { - "babel-plugin-syntax-flow": "6.18.0", - "babel-runtime": "6.26.0" + "babel-plugin-syntax-flow": "^6.18.0", + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-object-rest-spread": { @@ -1716,8 +1721,8 @@ "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", "dev": true, "requires": { - "babel-plugin-syntax-object-rest-spread": "6.13.0", - "babel-runtime": "6.26.0" + "babel-plugin-syntax-object-rest-spread": "^6.8.0", + "babel-runtime": "^6.26.0" } }, "babel-plugin-transform-react-jsx": { @@ -1726,9 +1731,9 @@ "integrity": "sha1-hAoCjn30YN/DotKfDA2R9jduZqM=", "dev": true, "requires": { - "babel-helper-builder-react-jsx": "6.26.0", - "babel-plugin-syntax-jsx": "6.18.0", - "babel-runtime": "6.26.0" + "babel-helper-builder-react-jsx": "^6.24.1", + "babel-plugin-syntax-jsx": "^6.8.0", + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-regenerator": { @@ -1737,7 +1742,7 @@ "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", "dev": true, "requires": { - "regenerator-transform": "0.10.1" + "regenerator-transform": "^0.10.0" } }, "babel-plugin-transform-runtime": { @@ -1746,7 +1751,7 @@ "integrity": "sha1-iEkNRGUC6puOfvsP4J7E2ZR5se4=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-strict-mode": { @@ -1755,8 +1760,8 @@ "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" } }, "babel-preset-env": { @@ -1765,36 +1770,36 @@ "integrity": "sha512-W6VIyA6Ch9ePMI7VptNn2wBM6dbG0eSz25HEiL40nQXCsXGTGZSTZu1Iap+cj3Q0S5a7T9+529l/5Bkvd+afNA==", "dev": true, "requires": { - "babel-plugin-check-es2015-constants": "6.22.0", - "babel-plugin-syntax-trailing-function-commas": "6.22.0", - "babel-plugin-transform-async-to-generator": "6.24.1", - "babel-plugin-transform-es2015-arrow-functions": "6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "6.22.0", - "babel-plugin-transform-es2015-block-scoping": "6.26.0", - "babel-plugin-transform-es2015-classes": "6.24.1", - "babel-plugin-transform-es2015-computed-properties": "6.24.1", - "babel-plugin-transform-es2015-destructuring": "6.23.0", - "babel-plugin-transform-es2015-duplicate-keys": "6.24.1", - "babel-plugin-transform-es2015-for-of": "6.23.0", - "babel-plugin-transform-es2015-function-name": "6.24.1", - "babel-plugin-transform-es2015-literals": "6.22.0", - "babel-plugin-transform-es2015-modules-amd": "6.24.1", - "babel-plugin-transform-es2015-modules-commonjs": "6.26.0", - "babel-plugin-transform-es2015-modules-systemjs": "6.24.1", - "babel-plugin-transform-es2015-modules-umd": "6.24.1", - "babel-plugin-transform-es2015-object-super": "6.24.1", - "babel-plugin-transform-es2015-parameters": "6.24.1", - "babel-plugin-transform-es2015-shorthand-properties": "6.24.1", - "babel-plugin-transform-es2015-spread": "6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "6.24.1", - "babel-plugin-transform-es2015-template-literals": "6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "6.23.0", - "babel-plugin-transform-es2015-unicode-regex": "6.24.1", - "babel-plugin-transform-exponentiation-operator": "6.24.1", - "babel-plugin-transform-regenerator": "6.26.0", - "browserslist": "2.11.3", - "invariant": "2.2.2", - "semver": "5.3.0" + "babel-plugin-check-es2015-constants": "^6.22.0", + "babel-plugin-syntax-trailing-function-commas": "^6.22.0", + "babel-plugin-transform-async-to-generator": "^6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoping": "^6.23.0", + "babel-plugin-transform-es2015-classes": "^6.23.0", + "babel-plugin-transform-es2015-computed-properties": "^6.22.0", + "babel-plugin-transform-es2015-destructuring": "^6.23.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0", + "babel-plugin-transform-es2015-for-of": "^6.23.0", + "babel-plugin-transform-es2015-function-name": "^6.22.0", + "babel-plugin-transform-es2015-literals": "^6.22.0", + "babel-plugin-transform-es2015-modules-amd": "^6.22.0", + "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0", + "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0", + "babel-plugin-transform-es2015-modules-umd": "^6.23.0", + "babel-plugin-transform-es2015-object-super": "^6.22.0", + "babel-plugin-transform-es2015-parameters": "^6.23.0", + "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0", + "babel-plugin-transform-es2015-spread": "^6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.22.0", + "babel-plugin-transform-es2015-template-literals": "^6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", + "babel-plugin-transform-exponentiation-operator": "^6.22.0", + "babel-plugin-transform-regenerator": "^6.22.0", + "browserslist": "^2.1.2", + "invariant": "^2.2.2", + "semver": "^5.3.0" } }, "babel-preset-es2015": { @@ -1803,30 +1808,30 @@ "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=", "dev": true, "requires": { - "babel-plugin-check-es2015-constants": "6.22.0", - "babel-plugin-transform-es2015-arrow-functions": "6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "6.22.0", - "babel-plugin-transform-es2015-block-scoping": "6.26.0", - "babel-plugin-transform-es2015-classes": "6.24.1", - "babel-plugin-transform-es2015-computed-properties": "6.24.1", - "babel-plugin-transform-es2015-destructuring": "6.23.0", - "babel-plugin-transform-es2015-duplicate-keys": "6.24.1", - "babel-plugin-transform-es2015-for-of": "6.23.0", - "babel-plugin-transform-es2015-function-name": "6.24.1", - "babel-plugin-transform-es2015-literals": "6.22.0", - "babel-plugin-transform-es2015-modules-amd": "6.24.1", - "babel-plugin-transform-es2015-modules-commonjs": "6.26.0", - "babel-plugin-transform-es2015-modules-systemjs": "6.24.1", - "babel-plugin-transform-es2015-modules-umd": "6.24.1", - "babel-plugin-transform-es2015-object-super": "6.24.1", - "babel-plugin-transform-es2015-parameters": "6.24.1", - "babel-plugin-transform-es2015-shorthand-properties": "6.24.1", - "babel-plugin-transform-es2015-spread": "6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "6.24.1", - "babel-plugin-transform-es2015-template-literals": "6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "6.23.0", - "babel-plugin-transform-es2015-unicode-regex": "6.24.1", - "babel-plugin-transform-regenerator": "6.26.0" + "babel-plugin-check-es2015-constants": "^6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoping": "^6.24.1", + "babel-plugin-transform-es2015-classes": "^6.24.1", + "babel-plugin-transform-es2015-computed-properties": "^6.24.1", + "babel-plugin-transform-es2015-destructuring": "^6.22.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.24.1", + "babel-plugin-transform-es2015-for-of": "^6.22.0", + "babel-plugin-transform-es2015-function-name": "^6.24.1", + "babel-plugin-transform-es2015-literals": "^6.22.0", + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-umd": "^6.24.1", + "babel-plugin-transform-es2015-object-super": "^6.24.1", + "babel-plugin-transform-es2015-parameters": "^6.24.1", + "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1", + "babel-plugin-transform-es2015-spread": "^6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.24.1", + "babel-plugin-transform-es2015-template-literals": "^6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "^6.22.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.24.1", + "babel-plugin-transform-regenerator": "^6.24.1" } }, "babel-preset-jest": { @@ -1835,8 +1840,8 @@ "integrity": "sha512-p61cPMGYlSgfNScn1yQuVnLguWE4bjhB/br4KQDMbYZG+v6ryE5Ch7TKukjA6mRuIQj1zhyou7Sbpqrh4/N6Pg==", "dev": true, "requires": { - "babel-plugin-jest-hoist": "22.2.0", - "babel-plugin-syntax-object-rest-spread": "6.13.0" + "babel-plugin-jest-hoist": "^22.2.0", + "babel-plugin-syntax-object-rest-spread": "^6.13.0" } }, "babel-preset-stage-1": { @@ -1845,9 +1850,9 @@ "integrity": "sha1-dpLNfc1oSZB+auSgqFWJz7niv7A=", "dev": true, "requires": { - "babel-plugin-transform-class-constructor-call": "6.24.1", - "babel-plugin-transform-export-extensions": "6.22.0", - "babel-preset-stage-2": "6.24.1" + "babel-plugin-transform-class-constructor-call": "^6.24.1", + "babel-plugin-transform-export-extensions": "^6.22.0", + "babel-preset-stage-2": "^6.24.1" } }, "babel-preset-stage-2": { @@ -1856,10 +1861,10 @@ "integrity": "sha1-2eKWD7PXEYfw5k7sYrwHdnIZvcE=", "dev": true, "requires": { - "babel-plugin-syntax-dynamic-import": "6.18.0", - "babel-plugin-transform-class-properties": "6.24.1", - "babel-plugin-transform-decorators": "6.24.1", - "babel-preset-stage-3": "6.24.1" + "babel-plugin-syntax-dynamic-import": "^6.18.0", + "babel-plugin-transform-class-properties": "^6.24.1", + "babel-plugin-transform-decorators": "^6.24.1", + "babel-preset-stage-3": "^6.24.1" } }, "babel-preset-stage-3": { @@ -1868,11 +1873,11 @@ "integrity": "sha1-g2raCp56f6N8sTj7kyb4eTSkg5U=", "dev": true, "requires": { - "babel-plugin-syntax-trailing-function-commas": "6.22.0", - "babel-plugin-transform-async-generator-functions": "6.24.1", - "babel-plugin-transform-async-to-generator": "6.24.1", - "babel-plugin-transform-exponentiation-operator": "6.24.1", - "babel-plugin-transform-object-rest-spread": "6.26.0" + "babel-plugin-syntax-trailing-function-commas": "^6.22.0", + "babel-plugin-transform-async-generator-functions": "^6.24.1", + "babel-plugin-transform-async-to-generator": "^6.24.1", + "babel-plugin-transform-exponentiation-operator": "^6.24.1", + "babel-plugin-transform-object-rest-spread": "^6.22.0" } }, "babel-register": { @@ -1881,13 +1886,13 @@ "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", "dev": true, "requires": { - "babel-core": "6.26.0", - "babel-runtime": "6.26.0", - "core-js": "2.5.3", - "home-or-tmp": "2.0.0", - "lodash": "4.17.5", - "mkdirp": "0.5.1", - "source-map-support": "0.4.18" + "babel-core": "^6.26.0", + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "home-or-tmp": "^2.0.0", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.15" }, "dependencies": { "core-js": { @@ -1902,7 +1907,7 @@ "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", "dev": true, "requires": { - "source-map": "0.5.7" + "source-map": "^0.5.6" } } } @@ -1913,8 +1918,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.3", - "regenerator-runtime": "0.11.1" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" }, "dependencies": { "core-js": { @@ -1931,11 +1936,11 @@ "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "lodash": "4.17.5" + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" } }, "babel-traverse": { @@ -1944,15 +1949,15 @@ "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", "dev": true, "requires": { - "babel-code-frame": "6.26.0", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "debug": "2.6.9", - "globals": "9.18.0", - "invariant": "2.2.2", - "lodash": "4.17.5" + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" } }, "babel-types": { @@ -1961,10 +1966,10 @@ "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "esutils": "2.0.2", - "lodash": "4.17.5", - "to-fast-properties": "1.0.3" + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" }, "dependencies": { "to-fast-properties": { @@ -1992,13 +1997,13 @@ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, "requires": { - "cache-base": "1.0.1", - "class-utils": "0.3.6", - "component-emitter": "1.2.1", - "define-property": "1.0.0", - "isobject": "3.0.1", - "mixin-deep": "1.3.1", - "pascalcase": "0.1.1" + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" }, "dependencies": { "define-property": { @@ -2007,7 +2012,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "isobject": { @@ -2031,7 +2036,7 @@ "dev": true, "optional": true, "requires": { - "tweetnacl": "0.14.5" + "tweetnacl": "^0.14.3" } }, "big.js": { @@ -2058,7 +2063,7 @@ "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", "dev": true, "requires": { - "inherits": "2.0.3" + "inherits": "~2.0.0" } }, "bluebird": { @@ -2084,7 +2089,7 @@ "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", "dev": true, "requires": { - "hoek": "4.2.0" + "hoek": "4.x.x" } }, "brace-expansion": { @@ -2092,7 +2097,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -2102,9 +2107,9 @@ "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, "requires": { - "expand-range": "1.8.2", - "preserve": "0.2.0", - "repeat-element": "1.1.2" + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" } }, "brorand": { @@ -2142,12 +2147,12 @@ "integrity": "sha512-UGnTYAnB2a3YuYKIRy1/4FB2HdM866E0qC46JXvVTYKlBlZlnvfpSfY6OKfXZAkv70eJ2a1SqzpAo5CRhZGDFg==", "dev": true, "requires": { - "buffer-xor": "1.0.3", - "cipher-base": "1.0.4", - "create-hash": "1.1.3", - "evp_bytestokey": "1.0.3", - "inherits": "2.0.3", - "safe-buffer": "5.1.1" + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" } }, "browserify-cipher": { @@ -2156,9 +2161,9 @@ "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=", "dev": true, "requires": { - "browserify-aes": "1.1.1", - "browserify-des": "1.0.0", - "evp_bytestokey": "1.0.3" + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" } }, "browserify-des": { @@ -2167,9 +2172,9 @@ "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=", "dev": true, "requires": { - "cipher-base": "1.0.4", - "des.js": "1.0.0", - "inherits": "2.0.3" + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1" } }, "browserify-rsa": { @@ -2178,8 +2183,8 @@ "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { - "bn.js": "4.11.8", - "randombytes": "2.0.6" + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" } }, "browserify-sign": { @@ -2188,13 +2193,13 @@ "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", "dev": true, "requires": { - "bn.js": "4.11.8", - "browserify-rsa": "4.0.1", - "create-hash": "1.1.3", - "create-hmac": "1.1.6", - "elliptic": "6.4.0", - "inherits": "2.0.3", - "parse-asn1": "5.1.0" + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" } }, "browserify-zlib": { @@ -2203,7 +2208,7 @@ "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", "dev": true, "requires": { - "pako": "1.0.6" + "pako": "~1.0.5" } }, "browserslist": { @@ -2212,8 +2217,8 @@ "integrity": "sha512-yWu5cXT7Av6mVwzWc8lMsJMHWn4xyjSuGYi4IozbVTLUOEYPSagUB8kiMDUHA1fS3zjr8nkxkn9jdvug4BBRmA==", "dev": true, "requires": { - "caniuse-lite": "1.0.30000828", - "electron-to-chromium": "1.3.33" + "caniuse-lite": "^1.0.30000792", + "electron-to-chromium": "^1.3.30" } }, "bser": { @@ -2222,7 +2227,7 @@ "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=", "dev": true, "requires": { - "node-int64": "0.4.0" + "node-int64": "^0.4.0" } }, "buffer": { @@ -2231,9 +2236,9 @@ "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { - "base64-js": "1.2.3", - "ieee754": "1.1.11", - "isarray": "1.0.0" + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" } }, "buffer-xor": { @@ -2245,7 +2250,8 @@ "builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true }, "builtin-status-codes": { "version": "3.0.0", @@ -2259,19 +2265,19 @@ "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", "dev": true, "requires": { - "bluebird": "3.5.1", - "chownr": "1.0.1", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "lru-cache": "4.1.2", - "mississippi": "2.0.0", - "mkdirp": "0.5.1", - "move-concurrently": "1.0.1", - "promise-inflight": "1.0.1", - "rimraf": "2.6.2", - "ssri": "5.3.0", - "unique-filename": "1.1.0", - "y18n": "4.0.0" + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.1", + "mississippi": "^2.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^5.2.4", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" }, "dependencies": { "lru-cache": { @@ -2280,8 +2286,8 @@ "integrity": "sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ==", "dev": true, "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" } }, "y18n": { @@ -2298,15 +2304,15 @@ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "dev": true, "requires": { - "collection-visit": "1.0.0", - "component-emitter": "1.2.1", - "get-value": "2.0.6", - "has-value": "1.0.0", - "isobject": "3.0.1", - "set-value": "2.0.0", - "to-object-path": "0.3.0", - "union-value": "1.0.0", - "unset-value": "1.0.0" + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" }, "dependencies": { "isobject": { @@ -2344,9 +2350,9 @@ "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==", "dev": true, "requires": { - "prepend-http": "2.0.0", - "query-string": "5.1.1", - "sort-keys": "2.0.0" + "prepend-http": "^2.0.0", + "query-string": "^5.0.1", + "sort-keys": "^2.0.0" } }, "prepend-http": { @@ -2361,9 +2367,9 @@ "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", "dev": true, "requires": { - "decode-uri-component": "0.2.0", - "object-assign": "4.1.1", - "strict-uri-encode": "1.1.0" + "decode-uri-component": "^0.2.0", + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" } }, "sort-keys": { @@ -2372,7 +2378,7 @@ "integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=", "dev": true, "requires": { - "is-plain-obj": "1.1.0" + "is-plain-obj": "^1.0.0" } } } @@ -2383,7 +2389,7 @@ "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", "dev": true, "requires": { - "callsites": "0.2.0" + "callsites": "^0.2.0" }, "dependencies": { "callsites": { @@ -2411,8 +2417,8 @@ "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, "requires": { - "camelcase": "2.1.1", - "map-obj": "1.0.1" + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" }, "dependencies": { "camelcase": { @@ -2429,10 +2435,10 @@ "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", "dev": true, "requires": { - "browserslist": "1.7.7", - "caniuse-db": "1.0.30000804", - "lodash.memoize": "4.1.2", - "lodash.uniq": "4.5.0" + "browserslist": "^1.3.6", + "caniuse-db": "^1.0.30000529", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" }, "dependencies": { "browserslist": { @@ -2441,8 +2447,8 @@ "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "dev": true, "requires": { - "caniuse-db": "1.0.30000804", - "electron-to-chromium": "1.3.33" + "caniuse-db": "^1.0.30000639", + "electron-to-chromium": "^1.2.7" } } } @@ -2472,8 +2478,8 @@ "dev": true, "optional": true, "requires": { - "align-text": "0.1.4", - "lazy-cache": "1.0.4" + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" } }, "chalk": { @@ -2482,11 +2488,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" } }, "chardet": { @@ -2501,13 +2507,13 @@ "integrity": "sha512-52fHDe/0pbidY3InI33Beyb/oarySfLANlXxLGBl9lLVrLIW88XWIwu4jGJrQ1imuWzX5ukNGWXUyCgmgVUD8A==", "dev": true, "requires": { - "chalk": "2.3.0", - "map-values": "1.0.1", - "minimist": "1.2.0", - "object-filter": "1.0.2", - "object.assign": "4.1.0", - "run-parallel": "1.1.6", - "semver": "5.3.0" + "chalk": "^2.3.0", + "map-values": "^1.0.1", + "minimist": "^1.2.0", + "object-filter": "^1.0.2", + "object.assign": "^4.0.4", + "run-parallel": "^1.1.4", + "semver": "^5.0.3" }, "dependencies": { "ansi-styles": { @@ -2516,7 +2522,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -2525,9 +2531,9 @@ "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" + "ansi-styles": "^3.1.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^4.0.0" } }, "minimist": { @@ -2542,7 +2548,7 @@ "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "^2.0.0" } } } @@ -2553,12 +2559,12 @@ "integrity": "sha1-S59TqBsn5NXawxwP/Qz6A8xoMNs=", "dev": true, "requires": { - "css-select": "1.2.0", - "dom-serializer": "0.1.0", - "entities": "1.1.1", - "htmlparser2": "3.9.2", - "lodash": "4.17.5", - "parse5": "3.0.3" + "css-select": "~1.2.0", + "dom-serializer": "~0.1.0", + "entities": "~1.1.1", + "htmlparser2": "^3.9.1", + "lodash": "^4.15.0", + "parse5": "^3.0.1" } }, "chokidar": { @@ -2567,18 +2573,18 @@ "integrity": "sha512-zW8iXYZtXMx4kux/nuZVXjkLP+CyIK5Al5FHnj1OgTKGZfp4Oy6/ymtMSKFv3GD8DviEmUPmJg9eFdJ/JzudMg==", "dev": true, "requires": { - "anymatch": "2.0.0", - "async-each": "1.0.1", - "braces": "2.3.1", - "fsevents": "1.1.3", - "glob-parent": "3.1.0", - "inherits": "2.0.3", - "is-binary-path": "1.0.1", - "is-glob": "4.0.0", - "normalize-path": "2.1.1", - "path-is-absolute": "1.0.1", - "readdirp": "2.1.0", - "upath": "1.0.4" + "anymatch": "^2.0.0", + "async-each": "^1.0.0", + "braces": "^2.3.0", + "fsevents": "^1.1.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^2.1.1", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0", + "upath": "^1.0.0" }, "dependencies": { "anymatch": { @@ -2587,8 +2593,8 @@ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, "requires": { - "micromatch": "3.1.10", - "normalize-path": "2.1.1" + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" } }, "arr-diff": { @@ -2609,18 +2615,18 @@ "integrity": "sha512-SO5lYHA3vO6gz66erVvedSCkp7AKWdv6VcQ2N4ysXfPxdAlxAMMAdwegGGcv1Bqwm7naF1hNdk5d6AAIEHV2nQ==", "dev": true, "requires": { - "arr-flatten": "1.1.0", - "array-unique": "0.3.2", - "define-property": "1.0.0", - "extend-shallow": "2.0.1", - "fill-range": "4.0.0", - "isobject": "3.0.1", - "kind-of": "6.0.2", - "repeat-element": "1.1.2", - "snapdragon": "0.8.2", - "snapdragon-node": "2.1.1", - "split-string": "3.1.0", - "to-regex": "3.0.2" + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "kind-of": "^6.0.2", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" }, "dependencies": { "define-property": { @@ -2629,7 +2635,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "extend-shallow": { @@ -2638,7 +2644,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -2649,13 +2655,13 @@ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "posix-character-classes": "0.1.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "define-property": { @@ -2664,7 +2670,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "extend-shallow": { @@ -2673,7 +2679,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } }, "is-descriptor": { @@ -2682,9 +2688,9 @@ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" } }, "kind-of": { @@ -2701,14 +2707,14 @@ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, "requires": { - "array-unique": "0.3.2", - "define-property": "1.0.0", - "expand-brackets": "2.1.4", - "extend-shallow": "2.0.1", - "fragment-cache": "0.2.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "define-property": { @@ -2717,7 +2723,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "extend-shallow": { @@ -2726,7 +2732,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -2737,10 +2743,10 @@ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "extend-shallow": "2.0.1", - "is-number": "3.0.0", - "repeat-string": "1.6.1", - "to-regex-range": "2.1.1" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" }, "dependencies": { "extend-shallow": { @@ -2749,7 +2755,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -2760,8 +2766,8 @@ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, "requires": { - "is-glob": "3.1.0", - "path-dirname": "1.0.2" + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" }, "dependencies": { "is-glob": { @@ -2770,7 +2776,7 @@ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, "requires": { - "is-extglob": "2.1.1" + "is-extglob": "^2.1.0" } } } @@ -2781,7 +2787,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -2790,7 +2796,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -2801,7 +2807,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -2810,7 +2816,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -2827,7 +2833,7 @@ "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", "dev": true, "requires": { - "is-extglob": "2.1.1" + "is-extglob": "^2.1.1" } }, "is-number": { @@ -2836,7 +2842,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -2845,7 +2851,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -2868,19 +2874,19 @@ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "braces": "2.3.1", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "extglob": "2.0.4", - "fragment-cache": "0.2.1", - "kind-of": "6.0.2", - "nanomatch": "1.2.9", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" } } } @@ -2909,8 +2915,8 @@ "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", "dev": true, "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" } }, "circular-json": { @@ -2931,7 +2937,7 @@ "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==", "dev": true, "requires": { - "chalk": "1.1.3" + "chalk": "^1.1.3" } }, "class-utils": { @@ -2940,10 +2946,10 @@ "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, "requires": { - "arr-union": "3.1.0", - "define-property": "0.2.5", - "isobject": "3.0.1", - "static-extend": "0.1.2" + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" }, "dependencies": { "define-property": { @@ -2952,7 +2958,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "is-accessor-descriptor": { @@ -2961,7 +2967,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -2970,7 +2976,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -2981,7 +2987,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -2990,7 +2996,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -3001,9 +3007,9 @@ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" } }, "isobject": { @@ -3031,7 +3037,7 @@ "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", "dev": true, "requires": { - "restore-cursor": "2.0.0" + "restore-cursor": "^2.0.0" } }, "cli-spinners": { @@ -3064,7 +3070,7 @@ "dev": true, "requires": { "slice-ansi": "0.0.4", - "string-width": "1.0.2" + "string-width": "^1.0.1" }, "dependencies": { "string-width": { @@ -3073,9 +3079,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } } } @@ -3091,29 +3097,31 @@ "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-1.7.1.tgz", "integrity": "sha1-Ng1taUbpmnof7zleQrqStem1oWs=", "requires": { - "good-listener": "1.2.2", - "select": "1.1.2", - "tiny-emitter": "2.0.2" + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" } }, "cliui": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wrap-ansi": "2.1.0" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" }, "dependencies": { "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } } } @@ -3136,10 +3144,10 @@ "integrity": "sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==", "dev": true, "requires": { - "for-own": "1.0.0", - "is-plain-object": "2.0.4", - "kind-of": "6.0.2", - "shallow-clone": "1.0.0" + "for-own": "^1.0.0", + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.0", + "shallow-clone": "^1.0.0" }, "dependencies": { "for-own": { @@ -3148,7 +3156,7 @@ "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", "dev": true, "requires": { - "for-in": "1.0.2" + "for-in": "^1.0.1" } }, "kind-of": { @@ -3165,7 +3173,7 @@ "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", "dev": true, "requires": { - "mimic-response": "1.0.0" + "mimic-response": "^1.0.0" } }, "clone-stats": { @@ -3180,9 +3188,9 @@ "integrity": "sha512-Bq6+4t+lbM8vhTs/Bef5c5AdEMtapp/iFb6+s4/Hh9MVTt8OLKH7ZOOZSCT+Ys7hsHvqv0GuMPJ1lnQJVHvxpg==", "dev": true, "requires": { - "inherits": "2.0.3", - "process-nextick-args": "2.0.0", - "readable-stream": "2.3.5" + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" }, "dependencies": { "process-nextick-args": { @@ -3197,13 +3205,13 @@ "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==", "dev": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.0.3", + "util-deprecate": "~1.0.1" } } } @@ -3220,7 +3228,7 @@ "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", "dev": true, "requires": { - "q": "1.5.1" + "q": "^1.1.2" } }, "code-point-at": { @@ -3245,8 +3253,8 @@ "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", "dev": true, "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" } }, "assert-plus": { @@ -3267,7 +3275,7 @@ "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", "dev": true, "requires": { - "hoek": "2.16.3" + "hoek": "2.x.x" } }, "cryptiles": { @@ -3276,7 +3284,7 @@ "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", "dev": true, "requires": { - "boom": "2.10.1" + "boom": "2.x.x" } }, "form-data": { @@ -3285,9 +3293,9 @@ "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", "dev": true, "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.17" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.5", + "mime-types": "^2.1.12" } }, "har-schema": { @@ -3302,8 +3310,8 @@ "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", "dev": true, "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" + "ajv": "^4.9.1", + "har-schema": "^1.0.5" } }, "hawk": { @@ -3312,10 +3320,10 @@ "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", "dev": true, "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" + "boom": "2.x.x", + "cryptiles": "2.x.x", + "hoek": "2.x.x", + "sntp": "1.x.x" } }, "hoek": { @@ -3330,9 +3338,9 @@ "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", "dev": true, "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.1", - "sshpk": "1.13.1" + "assert-plus": "^0.2.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" } }, "performance-now": { @@ -3353,28 +3361,28 @@ "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", "dev": true, "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.1.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.6.0", - "uuid": "3.1.0" + "aws-sign2": "~0.6.0", + "aws4": "^1.2.1", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.0", + "forever-agent": "~0.6.1", + "form-data": "~2.1.1", + "har-validator": "~4.2.1", + "hawk": "~3.1.3", + "http-signature": "~1.1.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.7", + "oauth-sign": "~0.8.1", + "performance-now": "^0.2.0", + "qs": "~6.4.0", + "safe-buffer": "^5.0.1", + "stringstream": "~0.0.4", + "tough-cookie": "~2.3.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.0.0" } }, "sntp": { @@ -3383,7 +3391,7 @@ "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", "dev": true, "requires": { - "hoek": "2.16.3" + "hoek": "2.x.x" } } } @@ -3394,8 +3402,8 @@ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, "requires": { - "map-visit": "1.0.0", - "object-visit": "1.0.1" + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" } }, "color": { @@ -3404,9 +3412,9 @@ "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", "dev": true, "requires": { - "clone": "1.0.3", - "color-convert": "1.9.1", - "color-string": "0.3.0" + "clone": "^1.0.2", + "color-convert": "^1.3.0", + "color-string": "^0.3.0" } }, "color-convert": { @@ -3415,7 +3423,7 @@ "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", "dev": true, "requires": { - "color-name": "1.1.3" + "color-name": "^1.1.1" } }, "color-name": { @@ -3430,7 +3438,7 @@ "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", "dev": true, "requires": { - "color-name": "1.1.3" + "color-name": "^1.0.0" } }, "colormin": { @@ -3439,9 +3447,9 @@ "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=", "dev": true, "requires": { - "color": "0.11.4", + "color": "^0.11.0", "css-color-names": "0.0.4", - "has": "1.0.1" + "has": "^1.0.1" } }, "colors": { @@ -3456,7 +3464,7 @@ "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", "dev": true, "requires": { - "delayed-stream": "1.0.0" + "delayed-stream": "~1.0.0" } }, "commander": { @@ -3469,7 +3477,7 @@ "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-0.4.2.tgz", "integrity": "sha1-+lo/eAEwcBFIZtx7jpzzF6ljX3Q=", "requires": { - "readable-stream": "2.3.3" + "readable-stream": "^2.0.4" } }, "commondir": { @@ -3500,9 +3508,9 @@ "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", "dev": true, "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.3", - "typedarray": "0.0.6" + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" } }, "concurrently": { @@ -3513,12 +3521,12 @@ "requires": { "chalk": "0.5.1", "commander": "2.6.0", - "date-fns": "1.29.0", - "lodash": "4.17.5", + "date-fns": "^1.23.0", + "lodash": "^4.5.1", "rx": "2.3.24", - "spawn-command": "0.0.2-1", - "supports-color": "3.2.3", - "tree-kill": "1.2.0" + "spawn-command": "^0.0.2-1", + "supports-color": "^3.2.3", + "tree-kill": "^1.1.0" }, "dependencies": { "ansi-regex": { @@ -3539,11 +3547,11 @@ "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", "dev": true, "requires": { - "ansi-styles": "1.1.0", - "escape-string-regexp": "1.0.5", - "has-ansi": "0.1.0", - "strip-ansi": "0.3.0", - "supports-color": "0.2.0" + "ansi-styles": "^1.1.0", + "escape-string-regexp": "^1.0.0", + "has-ansi": "^0.1.0", + "strip-ansi": "^0.3.0", + "supports-color": "^0.2.0" }, "dependencies": { "supports-color": { @@ -3566,7 +3574,7 @@ "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=", "dev": true, "requires": { - "ansi-regex": "0.2.1" + "ansi-regex": "^0.2.0" } }, "has-flag": { @@ -3581,7 +3589,7 @@ "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", "dev": true, "requires": { - "ansi-regex": "0.2.1" + "ansi-regex": "^0.2.1" } }, "supports-color": { @@ -3590,7 +3598,7 @@ "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "1.0.0" + "has-flag": "^1.0.0" } } } @@ -3600,8 +3608,8 @@ "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.11.tgz", "integrity": "sha1-q6CXR9++TD5w52am5BWG4YWfxvI=", "requires": { - "ini": "1.3.5", - "proto-list": "1.2.4" + "ini": "^1.3.4", + "proto-list": "~1.2.1" } }, "console-browserify": { @@ -3610,7 +3618,7 @@ "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", "dev": true, "requires": { - "date-now": "0.1.4" + "date-now": "^0.1.4" } }, "console-control-strings": { @@ -3625,11 +3633,6 @@ "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", "dev": true }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=" - }, "content-type-parser": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/content-type-parser/-/content-type-parser-1.0.2.tgz", @@ -3648,12 +3651,12 @@ "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", "dev": true, "requires": { - "aproba": "1.2.0", - "fs-write-stream-atomic": "1.0.10", - "iferr": "0.1.5", - "mkdirp": "0.5.1", - "rimraf": "2.6.2", - "run-queue": "1.0.3" + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" } }, "copy-descriptor": { @@ -3679,13 +3682,13 @@ "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", "dev": true, "requires": { - "is-directory": "0.3.1", - "js-yaml": "3.10.0", - "minimist": "1.2.0", - "object-assign": "4.1.1", - "os-homedir": "1.0.2", - "parse-json": "2.2.0", - "require-from-string": "1.2.1" + "is-directory": "^0.3.1", + "js-yaml": "^3.4.3", + "minimist": "^1.2.0", + "object-assign": "^4.1.0", + "os-homedir": "^1.0.1", + "parse-json": "^2.2.0", + "require-from-string": "^1.1.0" }, "dependencies": { "minimist": { @@ -3702,8 +3705,8 @@ "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=", "dev": true, "requires": { - "bn.js": "4.11.8", - "elliptic": "6.4.0" + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" } }, "create-hash": { @@ -3712,10 +3715,10 @@ "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=", "dev": true, "requires": { - "cipher-base": "1.0.4", - "inherits": "2.0.3", - "ripemd160": "2.0.1", - "sha.js": "2.4.11" + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "sha.js": "^2.4.0" } }, "create-hmac": { @@ -3724,12 +3727,12 @@ "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=", "dev": true, "requires": { - "cipher-base": "1.0.4", - "create-hash": "1.1.3", - "inherits": "2.0.3", - "ripemd160": "2.0.1", - "safe-buffer": "5.1.1", - "sha.js": "2.4.11" + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" } }, "cross-env": { @@ -3738,8 +3741,8 @@ "integrity": "sha1-ngWF8neGTtQhznVvgamA/w1piro=", "dev": true, "requires": { - "cross-spawn": "5.1.0", - "is-windows": "1.0.1" + "cross-spawn": "^5.1.0", + "is-windows": "^1.0.0" } }, "cross-spawn": { @@ -3747,9 +3750,9 @@ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "requires": { - "lru-cache": "4.1.1", - "shebang-command": "1.2.0", - "which": "1.3.0" + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" }, "dependencies": { "lru-cache": { @@ -3757,8 +3760,8 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" } } } @@ -3769,7 +3772,7 @@ "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", "dev": true, "requires": { - "boom": "5.2.0" + "boom": "5.x.x" }, "dependencies": { "boom": { @@ -3778,7 +3781,7 @@ "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", "dev": true, "requires": { - "hoek": "4.2.0" + "hoek": "4.x.x" } } } @@ -3789,17 +3792,17 @@ "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", "dev": true, "requires": { - "browserify-cipher": "1.0.0", - "browserify-sign": "4.0.4", - "create-ecdh": "4.0.0", - "create-hash": "1.1.3", - "create-hmac": "1.1.6", - "diffie-hellman": "5.0.2", - "inherits": "2.0.3", - "pbkdf2": "3.0.14", - "public-encrypt": "4.0.0", - "randombytes": "2.0.6", - "randomfill": "1.0.4" + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" } }, "css-color-names": { @@ -3814,10 +3817,10 @@ "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "dev": true, "requires": { - "boolbase": "1.0.0", - "css-what": "2.1.0", + "boolbase": "~1.0.0", + "css-what": "2.1", "domutils": "1.5.1", - "nth-check": "1.0.1" + "nth-check": "~1.0.1" } }, "css-what": { @@ -3832,38 +3835,38 @@ "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", "dev": true, "requires": { - "autoprefixer": "6.7.7", - "decamelize": "1.2.0", - "defined": "1.0.0", - "has": "1.0.1", - "object-assign": "4.1.1", - "postcss": "5.2.18", - "postcss-calc": "5.3.1", - "postcss-colormin": "2.2.2", - "postcss-convert-values": "2.6.1", - "postcss-discard-comments": "2.0.4", - "postcss-discard-duplicates": "2.1.0", - "postcss-discard-empty": "2.1.0", - "postcss-discard-overridden": "0.1.1", - "postcss-discard-unused": "2.2.3", - "postcss-filter-plugins": "2.0.2", - "postcss-merge-idents": "2.1.7", - "postcss-merge-longhand": "2.0.2", - "postcss-merge-rules": "2.1.2", - "postcss-minify-font-values": "1.0.5", - "postcss-minify-gradients": "1.0.5", - "postcss-minify-params": "1.2.2", - "postcss-minify-selectors": "2.1.1", - "postcss-normalize-charset": "1.1.1", - "postcss-normalize-url": "3.0.8", - "postcss-ordered-values": "2.2.3", - "postcss-reduce-idents": "2.4.0", - "postcss-reduce-initial": "1.0.1", - "postcss-reduce-transforms": "1.0.4", - "postcss-svgo": "2.1.6", - "postcss-unique-selectors": "2.0.2", - "postcss-value-parser": "3.3.0", - "postcss-zindex": "2.2.0" + "autoprefixer": "^6.3.1", + "decamelize": "^1.1.2", + "defined": "^1.0.0", + "has": "^1.0.1", + "object-assign": "^4.0.1", + "postcss": "^5.0.14", + "postcss-calc": "^5.2.0", + "postcss-colormin": "^2.1.8", + "postcss-convert-values": "^2.3.4", + "postcss-discard-comments": "^2.0.4", + "postcss-discard-duplicates": "^2.0.1", + "postcss-discard-empty": "^2.0.1", + "postcss-discard-overridden": "^0.1.1", + "postcss-discard-unused": "^2.2.1", + "postcss-filter-plugins": "^2.0.0", + "postcss-merge-idents": "^2.1.5", + "postcss-merge-longhand": "^2.0.1", + "postcss-merge-rules": "^2.0.3", + "postcss-minify-font-values": "^1.0.2", + "postcss-minify-gradients": "^1.0.1", + "postcss-minify-params": "^1.0.4", + "postcss-minify-selectors": "^2.0.4", + "postcss-normalize-charset": "^1.1.0", + "postcss-normalize-url": "^3.0.7", + "postcss-ordered-values": "^2.1.0", + "postcss-reduce-idents": "^2.2.2", + "postcss-reduce-initial": "^1.0.0", + "postcss-reduce-transforms": "^1.0.3", + "postcss-svgo": "^2.1.1", + "postcss-unique-selectors": "^2.0.2", + "postcss-value-parser": "^3.2.3", + "postcss-zindex": "^2.0.1" }, "dependencies": { "autoprefixer": { @@ -3872,12 +3875,12 @@ "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", "dev": true, "requires": { - "browserslist": "1.7.7", - "caniuse-db": "1.0.30000804", - "normalize-range": "0.1.2", - "num2fraction": "1.2.2", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" + "browserslist": "^1.7.6", + "caniuse-db": "^1.0.30000634", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^5.2.16", + "postcss-value-parser": "^3.2.3" } }, "browserslist": { @@ -3886,8 +3889,8 @@ "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "dev": true, "requires": { - "caniuse-db": "1.0.30000804", - "electron-to-chromium": "1.3.33" + "caniuse-db": "^1.0.30000639", + "electron-to-chromium": "^1.2.7" } } } @@ -3898,8 +3901,8 @@ "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", "dev": true, "requires": { - "clap": "1.2.3", - "source-map": "0.5.7" + "clap": "^1.0.9", + "source-map": "^0.5.3" } }, "cssom": { @@ -3914,7 +3917,7 @@ "integrity": "sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ=", "dev": true, "requires": { - "cssom": "0.3.2" + "cssom": "0.3.x" } }, "currently-unhandled": { @@ -3923,7 +3926,7 @@ "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", "dev": true, "requires": { - "array-find-index": "1.0.2" + "array-find-index": "^1.0.1" } }, "cyclist": { @@ -3950,7 +3953,7 @@ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "dev": true, "requires": { - "assert-plus": "1.0.0" + "assert-plus": "^1.0.0" } }, "date-fns": { @@ -3975,6 +3978,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, "requires": { "ms": "2.0.0" } @@ -3996,7 +4000,7 @@ "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", "dev": true, "requires": { - "mimic-response": "1.0.0" + "mimic-response": "^1.0.0" } }, "deep-equal-ident": { @@ -4005,7 +4009,7 @@ "integrity": "sha1-BvS4nlNxDNbOpKd4HHqVZkLejck=", "dev": true, "requires": { - "lodash.isequal": "3.0.4" + "lodash.isequal": "^3.0" } }, "deep-extend": { @@ -4032,7 +4036,7 @@ "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", "dev": true, "requires": { - "strip-bom": "2.0.0" + "strip-bom": "^2.0.0" }, "dependencies": { "strip-bom": { @@ -4041,7 +4045,7 @@ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "0.2.1" + "is-utf8": "^0.2.0" } } } @@ -4052,8 +4056,8 @@ "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", "dev": true, "requires": { - "foreach": "2.0.5", - "object-keys": "1.0.11" + "foreach": "^2.0.5", + "object-keys": "^1.0.8" } }, "define-property": { @@ -4062,8 +4066,8 @@ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, "requires": { - "is-descriptor": "1.0.2", - "isobject": "3.0.1" + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" }, "dependencies": { "isobject": { @@ -4086,13 +4090,13 @@ "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", "dev": true, "requires": { - "globby": "5.0.0", - "is-path-cwd": "1.0.0", - "is-path-in-cwd": "1.0.0", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "rimraf": "2.6.2" + "globby": "^5.0.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "rimraf": "^2.2.8" } }, "delayed-stream": { @@ -4118,8 +4122,8 @@ "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", "dev": true, "requires": { - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" } }, "detect-conflict": { @@ -4134,7 +4138,7 @@ "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", "dev": true, "requires": { - "repeating": "2.0.1" + "repeating": "^2.0.0" } }, "detect-newline": { @@ -4155,9 +4159,9 @@ "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=", "dev": true, "requires": { - "bn.js": "4.11.8", - "miller-rabin": "4.0.1", - "randombytes": "2.0.6" + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" } }, "discontinuous-range": { @@ -4166,19 +4170,10 @@ "integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=", "dev": true }, - "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "requires": { - "esutils": "2.0.2", - "isarray": "1.0.0" - } - }, "dom-react": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/dom-react/-/dom-react-2.2.0.tgz", - "integrity": "sha1-3GJwYI7VbL35DJo+w1U/m1oL17M=" + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-react/-/dom-react-2.2.1.tgz", + "integrity": "sha512-kqvoG+Q5oiJMQzQi245ZVA/X2Py2lBCebGcQzQeR51jOJqVghWBodKoJcitX8VRV+e6ku+9hRS+Bev/zmlSPsg==" }, "dom-scroll-into-view": { "version": "1.2.1", @@ -4191,8 +4186,8 @@ "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", "dev": true, "requires": { - "domelementtype": "1.1.3", - "entities": "1.1.1" + "domelementtype": "~1.1.1", + "entities": "~1.1.1" }, "dependencies": { "domelementtype": { @@ -4221,7 +4216,7 @@ "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", "dev": true, "requires": { - "webidl-conversions": "4.0.2" + "webidl-conversions": "^4.0.2" } }, "domhandler": { @@ -4230,7 +4225,7 @@ "integrity": "sha1-iS5HAAqZvlW783dP/qBWHYh5wlk=", "dev": true, "requires": { - "domelementtype": "1.3.0" + "domelementtype": "1" } }, "domutils": { @@ -4239,8 +4234,8 @@ "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", "dev": true, "requires": { - "dom-serializer": "0.1.0", - "domelementtype": "1.3.0" + "dom-serializer": "0", + "domelementtype": "1" } }, "duplexer3": { @@ -4252,13 +4247,13 @@ "duplexify": { "version": "3.5.4", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.4.tgz", - "integrity": "sha512-JzYSLYMhoVVBe8+mbHQ4KgpvHpm0DZpJuL8PY93Vyv1fW7jYJ90LoXa1di/CVbJM+TgMs91rbDapE/RNIfnJsA==", + "integrity": "sha1-S7RsF5bqvr7sTKmi5muAjLej2LQ=", "dev": true, "requires": { - "end-of-stream": "1.4.1", - "inherits": "2.0.3", - "readable-stream": "2.3.3", - "stream-shift": "1.0.0" + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" } }, "ecc-jsbn": { @@ -4268,7 +4263,7 @@ "dev": true, "optional": true, "requires": { - "jsbn": "0.1.1" + "jsbn": "~0.1.0" } }, "editions": { @@ -4282,11 +4277,11 @@ "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.13.3.tgz", "integrity": "sha512-WkjsUNVCu+ITKDj73QDvi0trvpdDWdkDyHybDGSXPfekLCqwmpD7CP7iPbvBgosNuLcI96XTDwNa75JyFl7tEQ==", "requires": { - "bluebird": "3.5.1", - "commander": "2.14.1", - "lru-cache": "3.2.0", - "semver": "5.3.0", - "sigmund": "1.0.1" + "bluebird": "^3.0.5", + "commander": "^2.9.0", + "lru-cache": "^3.2.0", + "semver": "^5.1.0", + "sigmund": "^1.0.1" } }, "ejs": { @@ -4318,13 +4313,13 @@ "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", "dev": true, "requires": { - "bn.js": "4.11.8", - "brorand": "1.1.0", - "hash.js": "1.1.3", - "hmac-drbg": "1.0.1", - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0", - "minimalistic-crypto-utils": "1.0.1" + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" } }, "emoji-regex": { @@ -4344,7 +4339,7 @@ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", "requires": { - "iconv-lite": "0.4.19" + "iconv-lite": "~0.4.13" } }, "end-of-stream": { @@ -4353,7 +4348,7 @@ "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "dev": true, "requires": { - "once": "1.4.0" + "once": "^1.4.0" } }, "enhanced-resolve": { @@ -4362,9 +4357,9 @@ "integrity": "sha512-jox/62b2GofV1qTUQTMPEJSDIGycS43evqYzD/KVtEb9OCoki9cnacUPxCrZa7JfPzZSYOCZhu9O9luaMxAX8g==", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "memory-fs": "0.4.1", - "tapable": "1.0.0" + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "tapable": "^1.0.0" } }, "entities": { @@ -4379,22 +4374,22 @@ "integrity": "sha512-l8csyPyLmtxskTz6pX9W8eDOyH1ckEtDttXk/vlFWCjv00SkjTjtoUrogqp4yEvMyneU9dUJoOLnqFoiHb8IHA==", "dev": true, "requires": { - "cheerio": "1.0.0-rc.2", - "function.prototype.name": "1.1.0", - "has": "1.0.1", - "is-boolean-object": "1.0.0", - "is-callable": "1.1.3", - "is-number-object": "1.0.3", - "is-string": "1.0.4", - "is-subset": "0.1.1", - "lodash": "4.17.5", - "object-inspect": "1.5.0", - "object-is": "1.0.1", - "object.assign": "4.1.0", - "object.entries": "1.0.4", - "object.values": "1.0.4", - "raf": "3.4.0", - "rst-selector-parser": "2.2.3" + "cheerio": "^1.0.0-rc.2", + "function.prototype.name": "^1.0.3", + "has": "^1.0.1", + "is-boolean-object": "^1.0.0", + "is-callable": "^1.1.3", + "is-number-object": "^1.0.3", + "is-string": "^1.0.4", + "is-subset": "^0.1.1", + "lodash": "^4.17.4", + "object-inspect": "^1.5.0", + "object-is": "^1.0.1", + "object.assign": "^4.1.0", + "object.entries": "^1.0.4", + "object.values": "^1.0.4", + "raf": "^3.4.0", + "rst-selector-parser": "^2.2.3" } }, "enzyme-adapter-react-16": { @@ -4403,13 +4398,13 @@ "integrity": "sha512-kC8pAtU2Jk3OJ0EG8Y2813dg9Ol0TXi7UNxHzHiWs30Jo/hj7alc//G1YpKUsPP1oKl9X+Lkx+WlGJpPYA+nvw==", "dev": true, "requires": { - "enzyme-adapter-utils": "1.3.0", - "lodash": "4.17.5", - "object.assign": "4.1.0", - "object.values": "1.0.4", - "prop-types": "15.6.0", - "react-reconciler": "0.7.0", - "react-test-renderer": "16.2.0" + "enzyme-adapter-utils": "^1.3.0", + "lodash": "^4.17.4", + "object.assign": "^4.0.4", + "object.values": "^1.0.4", + "prop-types": "^15.6.0", + "react-reconciler": "^0.7.0", + "react-test-renderer": "^16.0.0-0" }, "dependencies": { "prop-types": { @@ -4418,9 +4413,9 @@ "integrity": "sha1-zq8IMCL8RrSjX2nhPvda7Q1jmFY=", "dev": true, "requires": { - "fbjs": "0.8.16", - "loose-envify": "1.3.1", - "object-assign": "4.1.1" + "fbjs": "^0.8.16", + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" } }, "react-test-renderer": { @@ -4429,9 +4424,9 @@ "integrity": "sha512-Kd4gJFtpNziR9ElOE/C23LeflKLZPRpNQYWP3nQBY43SJ5a+xyEGSeMrm2zxNKXcnCbBS/q1UpD9gqd5Dv+rew==", "dev": true, "requires": { - "fbjs": "0.8.16", - "object-assign": "4.1.1", - "prop-types": "15.6.0" + "fbjs": "^0.8.16", + "object-assign": "^4.1.1", + "prop-types": "^15.6.0" } } } @@ -4442,9 +4437,9 @@ "integrity": "sha512-vVXSt6uDv230DIv+ebCG66T1Pm36Kv+m74L1TrF4kaE7e1V7Q/LcxO0QRkajk5cA6R3uu9wJf5h13wOTezTbjA==", "dev": true, "requires": { - "lodash": "4.17.5", - "object.assign": "4.1.0", - "prop-types": "15.6.0" + "lodash": "^4.17.4", + "object.assign": "^4.0.4", + "prop-types": "^15.6.0" }, "dependencies": { "prop-types": { @@ -4453,9 +4448,9 @@ "integrity": "sha1-zq8IMCL8RrSjX2nhPvda7Q1jmFY=", "dev": true, "requires": { - "fbjs": "0.8.16", - "loose-envify": "1.3.1", - "object-assign": "4.1.1" + "fbjs": "^0.8.16", + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" } } } @@ -4466,8 +4461,8 @@ "integrity": "sha512-5Gf/mAVYx6KPAUuxuDhAGt/gu9ndPd6duFcVnH2rbEad2clgTpHZL4Df49FHFukrjEEubX9rhfeAKx0/sbfVkQ==", "dev": true, "requires": { - "circular-json-es6": "2.0.2", - "deep-equal-ident": "1.1.1" + "circular-json-es6": "^2.0.1", + "deep-equal-ident": "^1.1.1" } }, "enzyme-to-json": { @@ -4476,7 +4471,7 @@ "integrity": "sha512-PrgRyZAgEwOrh5/8BtBWrwGcv1mC7yNohytIciAX6SUqDaXg1BlU8CepYQ9BgnDP1i1jTB65qJJITMMCph+T6A==", "dev": true, "requires": { - "lodash": "4.17.5" + "lodash": "^4.17.4" } }, "equivalent-key-map": { @@ -4490,7 +4485,7 @@ "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", "dev": true, "requires": { - "prr": "1.0.1" + "prr": "~1.0.1" } }, "error": { @@ -4499,16 +4494,17 @@ "integrity": "sha1-pfdf/02ZJhJt2sDqXcOOaJFTywI=", "dev": true, "requires": { - "string-template": "0.2.1", - "xtend": "4.0.1" + "string-template": "~0.2.1", + "xtend": "~4.0.0" } }, "error-ex": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "dev": true, "requires": { - "is-arrayish": "0.2.1" + "is-arrayish": "^0.2.1" } }, "es-abstract": { @@ -4517,11 +4513,11 @@ "integrity": "sha512-/uh/DhdqIOSkAWifU+8nG78vlQxdLckUdI/sPgy0VhuXi2qJ7T8czBmqIYtLQVpCIFYafChnsRsB5pyb1JdmCQ==", "dev": true, "requires": { - "es-to-primitive": "1.1.1", - "function-bind": "1.1.1", - "has": "1.0.1", - "is-callable": "1.1.3", - "is-regex": "1.0.4" + "es-to-primitive": "^1.1.1", + "function-bind": "^1.1.1", + "has": "^1.0.1", + "is-callable": "^1.1.3", + "is-regex": "^1.0.4" } }, "es-to-primitive": { @@ -4530,9 +4526,9 @@ "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", "dev": true, "requires": { - "is-callable": "1.1.3", - "is-date-object": "1.0.1", - "is-symbol": "1.0.1" + "is-callable": "^1.1.1", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.1" } }, "es6-promise": { @@ -4547,7 +4543,7 @@ "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", "dev": true, "requires": { - "es6-promise": "4.2.4" + "es6-promise": "^4.0.3" } }, "escape-string-regexp": { @@ -4561,11 +4557,11 @@ "integrity": "sha512-v0MYvNQ32bzwoG2OSFzWAkuahDQHK92JBN0pTAALJ4RIxEZe766QJPDR8Hqy7XNUy5K3fnVL76OqYAdc4TZEIw==", "dev": true, "requires": { - "esprima": "3.1.3", - "estraverse": "4.2.0", - "esutils": "2.0.2", - "optionator": "0.8.2", - "source-map": "0.5.7" + "esprima": "^3.1.3", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.5.6" }, "dependencies": { "esprima": { @@ -4582,43 +4578,43 @@ "integrity": "sha512-YVXV4bDhNoHHcv0qzU4Meof7/P26B4EuaktMi5L1Tnt52Aov85KmYA8c5D+xyZr/BkhvwUqr011jDSD/QTULxg==", "dev": true, "requires": { - "ajv": "5.5.2", - "babel-code-frame": "6.26.0", - "chalk": "2.3.0", - "concat-stream": "1.6.0", - "cross-spawn": "5.1.0", - "debug": "3.1.0", - "doctrine": "2.1.0", - "eslint-scope": "3.7.1", - "eslint-visitor-keys": "1.0.0", - "espree": "3.5.3", - "esquery": "1.0.0", - "esutils": "2.0.2", - "file-entry-cache": "2.0.0", - "functional-red-black-tree": "1.0.1", - "glob": "7.1.2", - "globals": "11.3.0", - "ignore": "3.3.7", - "imurmurhash": "0.1.4", - "inquirer": "3.3.0", - "is-resolvable": "1.1.0", - "js-yaml": "3.10.0", - "json-stable-stringify-without-jsonify": "1.0.1", - "levn": "0.3.0", - "lodash": "4.17.5", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "natural-compare": "1.4.0", - "optionator": "0.8.2", - "path-is-inside": "1.0.2", - "pluralize": "7.0.0", - "progress": "2.0.0", - "require-uncached": "1.0.3", - "semver": "5.3.0", - "strip-ansi": "4.0.0", - "strip-json-comments": "2.0.1", - "table": "4.0.2", - "text-table": "0.2.0" + "ajv": "^5.3.0", + "babel-code-frame": "^6.22.0", + "chalk": "^2.1.0", + "concat-stream": "^1.6.0", + "cross-spawn": "^5.1.0", + "debug": "^3.1.0", + "doctrine": "^2.1.0", + "eslint-scope": "^3.7.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^3.5.2", + "esquery": "^1.0.0", + "esutils": "^2.0.2", + "file-entry-cache": "^2.0.0", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.0.1", + "ignore": "^3.3.3", + "imurmurhash": "^0.1.4", + "inquirer": "^3.0.6", + "is-resolvable": "^1.0.0", + "js-yaml": "^3.9.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.4", + "minimatch": "^3.0.2", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "pluralize": "^7.0.0", + "progress": "^2.0.0", + "require-uncached": "^1.0.3", + "semver": "^5.3.0", + "strip-ansi": "^4.0.0", + "strip-json-comments": "~2.0.1", + "table": "^4.0.1", + "text-table": "~0.2.0" }, "dependencies": { "ansi-regex": { @@ -4633,7 +4629,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -4642,9 +4638,9 @@ "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" + "ansi-styles": "^3.1.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^4.0.0" } }, "debug": { @@ -4659,10 +4655,10 @@ "doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "integrity": "sha1-XNAfwQFiG0LEzX9dGmYkNxbT850=", "dev": true, "requires": { - "esutils": "2.0.2" + "esutils": "^2.0.2" } }, "globals": { @@ -4683,7 +4679,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } }, "supports-color": { @@ -4692,7 +4688,7 @@ "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "^2.0.0" } } } @@ -4703,46 +4699,11 @@ "integrity": "sha1-UgEgbGlk1kgxUjLt9t+9LpJeTNY=", "dev": true }, - "eslint-import-resolver-node": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", - "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", - "requires": { - "debug": "2.6.9", - "resolve": "1.5.0" - } - }, - "eslint-module-utils": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz", - "integrity": "sha512-jDI/X5l/6D1rRD/3T43q8Qgbls2nq5km5KSqiwlyUbGo5+04fXhMKdCPhjwbqAa6HXWaMxj8Q4hQDIh7IadJQw==", - "requires": { - "debug": "2.6.9", - "pkg-dir": "1.0.0" - } - }, "eslint-plugin-i18n": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/eslint-plugin-i18n/-/eslint-plugin-i18n-1.2.0.tgz", "integrity": "sha1-SfP0OA7e/oyHbwyXlh9lw6w3zao=" }, - "eslint-plugin-import": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.8.0.tgz", - "integrity": "sha512-Rf7dfKJxZ16QuTgVv1OYNxkZcsu/hULFnC+e+w0Gzi6jMC3guQoWQgxYxc54IDRinlb6/0v5z/PxxIKmVctN+g==", - "requires": { - "builtin-modules": "1.1.1", - "contains-path": "0.1.0", - "debug": "2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "0.3.2", - "eslint-module-utils": "2.1.1", - "has": "1.0.1", - "lodash.cond": "4.5.2", - "minimatch": "3.0.4", - "read-pkg-up": "2.0.0" - } - }, "eslint-plugin-jest": { "version": "21.5.0", "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-21.5.0.tgz", @@ -4754,8 +4715,8 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-3.5.0.tgz", "integrity": "sha512-qoNpVicVWGjGBXAJsqRoqVuAnajgX7PWtSa2Men36XKRiXe3RS/QmRv215PXZwo4OHskYOsUoJUeiPiWtS9ULA==", "requires": { - "comment-parser": "0.4.2", - "lodash": "4.17.5" + "comment-parser": "^0.4.2", + "lodash": "^4.17.4" } }, "eslint-plugin-jsx-a11y": { @@ -4764,13 +4725,13 @@ "integrity": "sha1-ZZJ3p1iwNsMFp+ShMFfDAc075z8=", "dev": true, "requires": { - "aria-query": "0.7.1", - "array-includes": "3.0.3", + "aria-query": "^0.7.0", + "array-includes": "^3.0.3", "ast-types-flow": "0.0.7", - "axobject-query": "0.1.0", - "damerau-levenshtein": "1.0.4", - "emoji-regex": "6.5.1", - "jsx-ast-utils": "1.4.1" + "axobject-query": "^0.1.0", + "damerau-levenshtein": "^1.0.0", + "emoji-regex": "^6.1.0", + "jsx-ast-utils": "^1.4.0" } }, "eslint-plugin-node": { @@ -4778,10 +4739,10 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-6.0.1.tgz", "integrity": "sha512-Q/Cc2sW1OAISDS+Ji6lZS2KV4b7ueA/WydVWd1BECTQwVvfQy5JAi3glhINoKzoMnfnuRgNP+ZWKrGAbp3QDxw==", "requires": { - "ignore": "3.3.7", - "minimatch": "3.0.4", - "resolve": "1.5.0", - "semver": "5.5.0" + "ignore": "^3.3.6", + "minimatch": "^3.0.4", + "resolve": "^1.3.3", + "semver": "^5.4.1" }, "dependencies": { "semver": { @@ -4797,19 +4758,19 @@ "integrity": "sha512-KC7Snr4YsWZD5flu6A5c0AcIZidzW3Exbqp7OT67OaD2AppJtlBr/GuPrW/vaQM/yfZotEvKAdrxrO+v8vwYJA==", "dev": true, "requires": { - "doctrine": "2.1.0", - "has": "1.0.1", - "jsx-ast-utils": "2.0.1", - "prop-types": "15.6.1" + "doctrine": "^2.0.2", + "has": "^1.0.1", + "jsx-ast-utils": "^2.0.1", + "prop-types": "^15.6.0" }, "dependencies": { "doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "integrity": "sha1-XNAfwQFiG0LEzX9dGmYkNxbT850=", "dev": true, "requires": { - "esutils": "2.0.2" + "esutils": "^2.0.2" } }, "jsx-ast-utils": { @@ -4818,7 +4779,7 @@ "integrity": "sha1-6AGxs5mF4g//yHtA43SAgOLcrH8=", "dev": true, "requires": { - "array-includes": "3.0.3" + "array-includes": "^3.0.3" } }, "prop-types": { @@ -4827,21 +4788,22 @@ "integrity": "sha512-4ec7bY1Y66LymSUOH/zARVYObB23AT2h8cf6e/O6ZALB/N0sqZFEx7rq6EYPX2MkOdKORuooI/H5k9TlR4q7kQ==", "dev": true, "requires": { - "fbjs": "0.8.16", - "loose-envify": "1.3.1", - "object-assign": "4.1.1" + "fbjs": "^0.8.16", + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" } } } }, "eslint-plugin-wordpress": { "version": "git://github.com/WordPress-Coding-Standards/eslint-plugin-wordpress.git#1774343f6226052a46b081e01db3fca8793cc9f1", + "from": "git://github.com/WordPress-Coding-Standards/eslint-plugin-wordpress.git#1774343f6226052a46b081e01db3fca8793cc9f1", "requires": { - "eslint-plugin-i18n": "1.2.0", - "eslint-plugin-jsdoc": "3.5.0", - "eslint-plugin-node": "6.0.1", - "eslint-plugin-wpcalypso": "4.0.1", - "merge": "1.2.0" + "eslint-plugin-i18n": "~1.2.0", + "eslint-plugin-jsdoc": "~3.5.0", + "eslint-plugin-node": "~6.0.1", + "eslint-plugin-wpcalypso": "~4.0.1", + "merge": "~1.2.0" } }, "eslint-plugin-wpcalypso": { @@ -4849,7 +4811,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-wpcalypso/-/eslint-plugin-wpcalypso-4.0.1.tgz", "integrity": "sha512-fU5NSc0XGdel/tlEIUoESOdqphBWQN2FfSgXXNHpXKX7ftTcqXacqgzXU8OVziyhXz6s2RUzK0+JSJaNxhZ+Mw==", "requires": { - "requireindex": "1.2.0" + "requireindex": "^1.1.0" } }, "eslint-scope": { @@ -4858,8 +4820,8 @@ "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", "dev": true, "requires": { - "esrecurse": "4.2.0", - "estraverse": "4.2.0" + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" } }, "eslint-visitor-keys": { @@ -4874,8 +4836,8 @@ "integrity": "sha512-Zy3tAJDORxQZLl2baguiRU1syPERAIg0L+JB2MWorORgTu/CplzvxS9WWA7Xh4+Q+eOQihNs/1o1Xep8cvCxWQ==", "dev": true, "requires": { - "acorn": "5.4.1", - "acorn-jsx": "3.0.1" + "acorn": "^5.4.0", + "acorn-jsx": "^3.0.0" } }, "esprima": { @@ -4890,7 +4852,7 @@ "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=", "dev": true, "requires": { - "estraverse": "4.2.0" + "estraverse": "^4.0.0" } }, "esrecurse": { @@ -4899,8 +4861,8 @@ "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", "dev": true, "requires": { - "estraverse": "4.2.0", - "object-assign": "4.1.1" + "estraverse": "^4.1.0", + "object-assign": "^4.0.1" } }, "estraverse": { @@ -4912,7 +4874,8 @@ "esutils": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true }, "events": { "version": "1.1.1", @@ -4926,8 +4889,8 @@ "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", "dev": true, "requires": { - "md5.js": "1.3.4", - "safe-buffer": "5.1.1" + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" } }, "exec-sh": { @@ -4936,7 +4899,7 @@ "integrity": "sha512-aLt95pexaugVtQerpmE51+4QfWrNc304uez7jvj6fWnN8GeEHpttB8F36n8N7uVhUMbH/1enbxQ9HImZ4w/9qg==", "dev": true, "requires": { - "merge": "1.2.0" + "merge": "^1.1.3" } }, "execa": { @@ -4944,13 +4907,13 @@ "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "requires": { - "cross-spawn": "5.1.0", - "get-stream": "3.0.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" } }, "exit": { @@ -4971,7 +4934,7 @@ "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, "requires": { - "is-posix-bracket": "0.1.1" + "is-posix-bracket": "^0.1.0" } }, "expand-range": { @@ -4980,7 +4943,7 @@ "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", "dev": true, "requires": { - "fill-range": "2.2.3" + "fill-range": "^2.1.0" } }, "expand-tilde": { @@ -4989,7 +4952,7 @@ "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", "dev": true, "requires": { - "homedir-polyfill": "1.0.1" + "homedir-polyfill": "^1.0.1" } }, "expect": { @@ -4998,12 +4961,12 @@ "integrity": "sha512-Fiy862jT3qc70hwIHwwCBNISmaqBrfWKKrtqyMJ6iwZr+6KXtcnHojZFtd63TPRvRl8EQTJ+YXYy2lK6/6u+Hw==", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "jest-diff": "22.4.0", - "jest-get-type": "22.1.0", - "jest-matcher-utils": "22.4.0", - "jest-message-util": "22.4.0", - "jest-regex-util": "22.1.0" + "ansi-styles": "^3.2.0", + "jest-diff": "^22.4.0", + "jest-get-type": "^22.1.0", + "jest-matcher-utils": "^22.4.0", + "jest-message-util": "^22.4.0", + "jest-regex-util": "^22.1.0" }, "dependencies": { "ansi-styles": { @@ -5012,7 +4975,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } } } @@ -5029,8 +4992,8 @@ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, "requires": { - "assign-symbols": "1.0.0", - "is-extendable": "1.0.1" + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" }, "dependencies": { "is-extendable": { @@ -5039,7 +5002,7 @@ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "is-plain-object": "2.0.4" + "is-plain-object": "^2.0.4" } } } @@ -5050,9 +5013,9 @@ "integrity": "sha512-E44iT5QVOUJBKij4IIV3uvxuNlbKS38Tw1HiupxEIHPv9qtC2PrDYohbXV5U+1jnfIXttny8gUhj+oZvflFlzA==", "dev": true, "requires": { - "chardet": "0.4.2", - "iconv-lite": "0.4.19", - "tmp": "0.0.33" + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" }, "dependencies": { "tmp": { @@ -5061,7 +5024,7 @@ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "requires": { - "os-tmpdir": "1.0.2" + "os-tmpdir": "~1.0.2" } } } @@ -5072,7 +5035,7 @@ "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true, "requires": { - "is-extglob": "1.0.0" + "is-extglob": "^1.0.0" } }, "extract-text-webpack-plugin": { @@ -5081,10 +5044,10 @@ "integrity": "sha512-Hypkn9jUTnFr0DpekNam53X47tXn3ucY08BQumv7kdGgeVUBLq3DJHJTi6HNxv4jl9W+Skxjz9+RnK0sJyqqjA==", "dev": true, "requires": { - "async": "2.6.0", - "loader-utils": "1.1.0", - "schema-utils": "0.4.5", - "webpack-sources": "1.1.0" + "async": "^2.4.1", + "loader-utils": "^1.1.0", + "schema-utils": "^0.4.5", + "webpack-sources": "^1.1.0" } }, "extract-zip": { @@ -5140,7 +5103,7 @@ "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=", "dev": true, "requires": { - "bser": "2.0.0" + "bser": "^2.0.0" } }, "fbjs": { @@ -5148,13 +5111,13 @@ "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz", "integrity": "sha1-XmdDL1UNxBtXK/VYR7ispk5TN9s=", "requires": { - "core-js": "1.2.7", - "isomorphic-fetch": "2.2.1", - "loose-envify": "1.3.1", - "object-assign": "4.1.1", - "promise": "7.3.1", - "setimmediate": "1.0.5", - "ua-parser-js": "0.7.17" + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.9" }, "dependencies": { "core-js": { @@ -5170,7 +5133,7 @@ "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", "dev": true, "requires": { - "pend": "1.2.0" + "pend": "~1.2.0" } }, "figures": { @@ -5179,7 +5142,7 @@ "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", "dev": true, "requires": { - "escape-string-regexp": "1.0.5" + "escape-string-regexp": "^1.0.5" } }, "file-entry-cache": { @@ -5188,8 +5151,8 @@ "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", "dev": true, "requires": { - "flat-cache": "1.3.0", - "object-assign": "4.1.1" + "flat-cache": "^1.2.1", + "object-assign": "^4.0.1" } }, "filename-regex": { @@ -5204,8 +5167,8 @@ "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", "dev": true, "requires": { - "glob": "7.1.2", - "minimatch": "3.0.4" + "glob": "^7.0.3", + "minimatch": "^3.0.3" } }, "fill-range": { @@ -5214,11 +5177,11 @@ "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", "dev": true, "requires": { - "is-number": "2.1.0", - "isobject": "2.1.0", - "randomatic": "1.1.7", - "repeat-element": "1.1.2", - "repeat-string": "1.6.1" + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^1.1.3", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" } }, "find-cache-dir": { @@ -5227,9 +5190,9 @@ "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", "dev": true, "requires": { - "commondir": "1.0.1", - "make-dir": "1.2.0", - "pkg-dir": "2.0.0" + "commondir": "^1.0.1", + "make-dir": "^1.0.0", + "pkg-dir": "^2.0.0" }, "dependencies": { "find-up": { @@ -5238,7 +5201,7 @@ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "2.0.0" + "locate-path": "^2.0.0" } }, "pkg-dir": { @@ -5247,7 +5210,7 @@ "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", "dev": true, "requires": { - "find-up": "2.1.0" + "find-up": "^2.1.0" } } } @@ -5256,9 +5219,10 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, "requires": { - "path-exists": "2.1.0", - "pinkie-promise": "2.0.1" + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, "findup": { @@ -5267,8 +5231,8 @@ "integrity": "sha1-itkpozk7rGJ5V6fl3kYjsGsOLOs=", "dev": true, "requires": { - "colors": "0.6.2", - "commander": "2.1.0" + "colors": "~0.6.0-1", + "commander": "~2.1.0" }, "dependencies": { "colors": { @@ -5291,7 +5255,7 @@ "integrity": "sha1-G97NuOCDwGZLkZRVgVd6Q6nzHXA=", "dev": true, "requires": { - "readable-stream": "2.3.3" + "readable-stream": "^2.0.2" } }, "flat-cache": { @@ -5300,10 +5264,10 @@ "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", "dev": true, "requires": { - "circular-json": "0.3.3", - "del": "2.2.2", - "graceful-fs": "4.1.11", - "write": "0.2.1" + "circular-json": "^0.3.1", + "del": "^2.0.2", + "graceful-fs": "^4.1.2", + "write": "^0.2.1" } }, "flatten": { @@ -5324,8 +5288,8 @@ "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", "dev": true, "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.3" + "inherits": "^2.0.1", + "readable-stream": "^2.0.4" } }, "for-in": { @@ -5340,7 +5304,7 @@ "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", "dev": true, "requires": { - "for-in": "1.0.2" + "for-in": "^1.0.1" } }, "foreach": { @@ -5361,9 +5325,9 @@ "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", "dev": true, "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.17" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.5", + "mime-types": "^2.1.12" } }, "fragment-cache": { @@ -5372,7 +5336,7 @@ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "dev": true, "requires": { - "map-cache": "0.2.2" + "map-cache": "^0.2.2" } }, "from2": { @@ -5381,8 +5345,8 @@ "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", "dev": true, "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.3" + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" } }, "fs-write-stream-atomic": { @@ -5391,10 +5355,10 @@ "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "iferr": "0.1.5", - "imurmurhash": "0.1.4", - "readable-stream": "2.3.3" + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" } }, "fs.realpath": { @@ -5410,8 +5374,8 @@ "dev": true, "optional": true, "requires": { - "nan": "2.8.0", - "node-pre-gyp": "0.6.39" + "nan": "^2.3.0", + "node-pre-gyp": "^0.6.39" }, "dependencies": { "abbrev": { @@ -5428,8 +5392,8 @@ "dev": true, "optional": true, "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" } }, "ansi-regex": { @@ -5452,8 +5416,8 @@ "dev": true, "optional": true, "requires": { - "delegates": "1.0.0", - "readable-stream": "2.2.9" + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" } }, "asn1": { @@ -5504,7 +5468,7 @@ "dev": true, "optional": true, "requires": { - "tweetnacl": "0.14.5" + "tweetnacl": "^0.14.3" } }, "block-stream": { @@ -5513,7 +5477,7 @@ "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", "dev": true, "requires": { - "inherits": "2.0.3" + "inherits": "~2.0.0" } }, "boom": { @@ -5522,7 +5486,7 @@ "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", "dev": true, "requires": { - "hoek": "2.16.3" + "hoek": "2.x.x" } }, "brace-expansion": { @@ -5531,7 +5495,7 @@ "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k=", "dev": true, "requires": { - "balanced-match": "0.4.2", + "balanced-match": "^0.4.1", "concat-map": "0.0.1" } }, @@ -5567,7 +5531,7 @@ "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", "dev": true, "requires": { - "delayed-stream": "1.0.0" + "delayed-stream": "~1.0.0" } }, "concat-map": { @@ -5594,7 +5558,7 @@ "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", "dev": true, "requires": { - "boom": "2.10.1" + "boom": "2.x.x" } }, "dashdash": { @@ -5604,7 +5568,7 @@ "dev": true, "optional": true, "requires": { - "assert-plus": "1.0.0" + "assert-plus": "^1.0.0" }, "dependencies": { "assert-plus": { @@ -5660,7 +5624,7 @@ "dev": true, "optional": true, "requires": { - "jsbn": "0.1.1" + "jsbn": "~0.1.0" } }, "extend": { @@ -5690,9 +5654,9 @@ "dev": true, "optional": true, "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.15" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.5", + "mime-types": "^2.1.12" } }, "fs.realpath": { @@ -5707,10 +5671,10 @@ "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.1" + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" } }, "fstream-ignore": { @@ -5720,9 +5684,9 @@ "dev": true, "optional": true, "requires": { - "fstream": "1.0.11", - "inherits": "2.0.3", - "minimatch": "3.0.4" + "fstream": "^1.0.0", + "inherits": "2", + "minimatch": "^3.0.0" } }, "gauge": { @@ -5732,14 +5696,14 @@ "dev": true, "optional": true, "requires": { - "aproba": "1.1.1", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" } }, "getpass": { @@ -5749,7 +5713,7 @@ "dev": true, "optional": true, "requires": { - "assert-plus": "1.0.0" + "assert-plus": "^1.0.0" }, "dependencies": { "assert-plus": { @@ -5767,12 +5731,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "graceful-fs": { @@ -5795,8 +5759,8 @@ "dev": true, "optional": true, "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" + "ajv": "^4.9.1", + "har-schema": "^1.0.5" } }, "has-unicode": { @@ -5812,10 +5776,10 @@ "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", "dev": true, "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" + "boom": "2.x.x", + "cryptiles": "2.x.x", + "hoek": "2.x.x", + "sntp": "1.x.x" } }, "hoek": { @@ -5831,9 +5795,9 @@ "dev": true, "optional": true, "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.0", - "sshpk": "1.13.0" + "assert-plus": "^0.2.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" } }, "inflight": { @@ -5842,8 +5806,8 @@ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -5865,7 +5829,7 @@ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "is-typedarray": { @@ -5895,7 +5859,7 @@ "dev": true, "optional": true, "requires": { - "jsbn": "0.1.1" + "jsbn": "~0.1.0" } }, "jsbn": { @@ -5919,7 +5883,7 @@ "dev": true, "optional": true, "requires": { - "jsonify": "0.0.0" + "jsonify": "~0.0.0" } }, "json-stringify-safe": { @@ -5970,7 +5934,7 @@ "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=", "dev": true, "requires": { - "mime-db": "1.27.0" + "mime-db": "~1.27.0" } }, "minimatch": { @@ -5979,7 +5943,7 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "1.1.7" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -6011,17 +5975,17 @@ "dev": true, "optional": true, "requires": { - "detect-libc": "1.0.2", + "detect-libc": "^1.0.2", "hawk": "3.1.3", - "mkdirp": "0.5.1", - "nopt": "4.0.1", - "npmlog": "4.1.0", - "rc": "1.2.1", + "mkdirp": "^0.5.1", + "nopt": "^4.0.1", + "npmlog": "^4.0.2", + "rc": "^1.1.7", "request": "2.81.0", - "rimraf": "2.6.1", - "semver": "5.3.0", - "tar": "2.2.1", - "tar-pack": "3.4.0" + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^2.2.1", + "tar-pack": "^3.4.0" } }, "nopt": { @@ -6031,8 +5995,8 @@ "dev": true, "optional": true, "requires": { - "abbrev": "1.1.0", - "osenv": "0.1.4" + "abbrev": "1", + "osenv": "^0.1.4" } }, "npmlog": { @@ -6042,10 +6006,10 @@ "dev": true, "optional": true, "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" } }, "number-is-nan": { @@ -6074,7 +6038,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "os-homedir": { @@ -6098,8 +6062,8 @@ "dev": true, "optional": true, "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" } }, "path-is-absolute": { @@ -6142,10 +6106,10 @@ "dev": true, "optional": true, "requires": { - "deep-extend": "0.4.2", - "ini": "1.3.4", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" + "deep-extend": "~0.4.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, "dependencies": { "minimist": { @@ -6163,13 +6127,13 @@ "integrity": "sha1-z3jsb0ptHrQ9JkiMrJfwQudLf8g=", "dev": true, "requires": { - "buffer-shims": "1.0.0", - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "1.0.1", - "util-deprecate": "1.0.2" + "buffer-shims": "~1.0.0", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~1.0.0", + "util-deprecate": "~1.0.1" } }, "request": { @@ -6179,28 +6143,28 @@ "dev": true, "optional": true, "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.15", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.0.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.2", - "tunnel-agent": "0.6.0", - "uuid": "3.0.1" + "aws-sign2": "~0.6.0", + "aws4": "^1.2.1", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.0", + "forever-agent": "~0.6.1", + "form-data": "~2.1.1", + "har-validator": "~4.2.1", + "hawk": "~3.1.3", + "http-signature": "~1.1.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.7", + "oauth-sign": "~0.8.1", + "performance-now": "^0.2.0", + "qs": "~6.4.0", + "safe-buffer": "^5.0.1", + "stringstream": "~0.0.4", + "tough-cookie": "~2.3.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.0.0" } }, "rimraf": { @@ -6209,7 +6173,7 @@ "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", "dev": true, "requires": { - "glob": "7.1.2" + "glob": "^7.0.5" } }, "safe-buffer": { @@ -6245,7 +6209,7 @@ "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", "dev": true, "requires": { - "hoek": "2.16.3" + "hoek": "2.x.x" } }, "sshpk": { @@ -6255,15 +6219,15 @@ "dev": true, "optional": true, "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jodid25519": "1.0.2", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jodid25519": "^1.0.0", + "jsbn": "~0.1.0", + "tweetnacl": "~0.14.0" }, "dependencies": { "assert-plus": { @@ -6281,9 +6245,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "string_decoder": { @@ -6292,7 +6256,7 @@ "integrity": "sha1-YuIA8DmVWmgQ2N8KM//A8BNmLZg=", "dev": true, "requires": { - "safe-buffer": "5.0.1" + "safe-buffer": "^5.0.1" } }, "stringstream": { @@ -6308,7 +6272,7 @@ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "strip-json-comments": { @@ -6324,9 +6288,9 @@ "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", "dev": true, "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" + "block-stream": "*", + "fstream": "^1.0.2", + "inherits": "2" } }, "tar-pack": { @@ -6336,14 +6300,14 @@ "dev": true, "optional": true, "requires": { - "debug": "2.6.8", - "fstream": "1.0.11", - "fstream-ignore": "1.0.5", - "once": "1.4.0", - "readable-stream": "2.2.9", - "rimraf": "2.6.1", - "tar": "2.2.1", - "uid-number": "0.0.6" + "debug": "^2.2.0", + "fstream": "^1.0.10", + "fstream-ignore": "^1.0.5", + "once": "^1.3.3", + "readable-stream": "^2.1.4", + "rimraf": "^2.5.1", + "tar": "^2.2.1", + "uid-number": "^0.0.6" } }, "tough-cookie": { @@ -6353,7 +6317,7 @@ "dev": true, "optional": true, "requires": { - "punycode": "1.4.1" + "punycode": "^1.4.1" } }, "tunnel-agent": { @@ -6363,7 +6327,7 @@ "dev": true, "optional": true, "requires": { - "safe-buffer": "5.0.1" + "safe-buffer": "^5.0.1" } }, "tweetnacl": { @@ -6410,7 +6374,7 @@ "dev": true, "optional": true, "requires": { - "string-width": "1.0.2" + "string-width": "^1.0.2" } }, "wrappy": { @@ -6427,16 +6391,17 @@ "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.2" + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" } }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true }, "function.prototype.name": { "version": "1.1.0", @@ -6444,9 +6409,9 @@ "integrity": "sha512-Bs0VRrTz4ghD8pTmbJQD1mZ8A/mN0ur/jGz+A6FBxPDUPkm1tNfF6bhTYPA7i7aF4lZJVr+OXTNNrnnIl58Wfg==", "dev": true, "requires": { - "define-properties": "1.1.2", - "function-bind": "1.1.1", - "is-callable": "1.1.3" + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "is-callable": "^1.1.3" } }, "functional-red-black-tree": { @@ -6461,14 +6426,14 @@ "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" }, "dependencies": { "string-width": { @@ -6477,9 +6442,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } } } @@ -6490,7 +6455,7 @@ "integrity": "sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU=", "dev": true, "requires": { - "globule": "1.2.0" + "globule": "^1.0.0" } }, "generate-function": { @@ -6505,7 +6470,7 @@ "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", "dev": true, "requires": { - "is-property": "1.0.2" + "is-property": "^1.0.0" } }, "get-caller-file": { @@ -6536,7 +6501,7 @@ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "dev": true, "requires": { - "assert-plus": "1.0.0" + "assert-plus": "^1.0.0" } }, "gettext-parser": { @@ -6544,8 +6509,8 @@ "resolved": "https://registry.npmjs.org/gettext-parser/-/gettext-parser-1.3.1.tgz", "integrity": "sha512-W4t55eB/c7WrH0gbCHFiHuaEnJ1WiPJVnbFFiNEoh2QkOmuSLxs0PmJDGAmCQuTJCU740Fmb6D+2D/2xECWZGQ==", "requires": { - "encoding": "0.1.12", - "safe-buffer": "5.1.1" + "encoding": "^0.1.12", + "safe-buffer": "^5.1.1" } }, "gh-got": { @@ -6554,8 +6519,8 @@ "integrity": "sha512-F/mS+fsWQMo1zfgG9MD8KWvTWPPzzhuVwY++fhQ5Ggd+0P+CAMHtzMZhNxG+TqGfHDChJKsbh6otfMGqO2AKBw==", "dev": true, "requires": { - "got": "7.1.0", - "is-plain-obj": "1.1.0" + "got": "^7.0.0", + "is-plain-obj": "^1.1.0" }, "dependencies": { "got": { @@ -6564,20 +6529,20 @@ "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", "dev": true, "requires": { - "decompress-response": "3.3.0", - "duplexer3": "0.1.4", - "get-stream": "3.0.0", - "is-plain-obj": "1.1.0", - "is-retry-allowed": "1.1.0", - "is-stream": "1.1.0", - "isurl": "1.0.0", - "lowercase-keys": "1.0.1", - "p-cancelable": "0.3.0", - "p-timeout": "1.2.1", - "safe-buffer": "5.1.1", - "timed-out": "4.0.1", - "url-parse-lax": "1.0.0", - "url-to-options": "1.0.1" + "decompress-response": "^3.2.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-plain-obj": "^1.1.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "p-cancelable": "^0.3.0", + "p-timeout": "^1.1.1", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "url-parse-lax": "^1.0.0", + "url-to-options": "^1.0.1" } }, "p-cancelable": { @@ -6592,7 +6557,7 @@ "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", "dev": true, "requires": { - "p-finally": "1.0.0" + "p-finally": "^1.0.0" } }, "url-parse-lax": { @@ -6601,7 +6566,7 @@ "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", "dev": true, "requires": { - "prepend-http": "1.0.4" + "prepend-http": "^1.0.1" } } } @@ -6612,7 +6577,7 @@ "integrity": "sha1-y+KABBiDIG2kISrp5LXxacML9Bc=", "dev": true, "requires": { - "gh-got": "6.0.0" + "gh-got": "^6.0.0" } }, "glob": { @@ -6621,12 +6586,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "glob-all": { @@ -6635,8 +6600,8 @@ "integrity": "sha1-iRPd+17hrHgSZWJBsD1SF8ZLAqs=", "dev": true, "requires": { - "glob": "7.1.2", - "yargs": "1.2.6" + "glob": "^7.0.5", + "yargs": "~1.2.6" }, "dependencies": { "minimist": { @@ -6651,7 +6616,7 @@ "integrity": "sha1-nHtKgv1dWVsr8Xq23MQxNUMv40s=", "dev": true, "requires": { - "minimist": "0.1.0" + "minimist": "^0.1.0" } } } @@ -6662,8 +6627,8 @@ "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", "dev": true, "requires": { - "glob-parent": "2.0.0", - "is-glob": "2.0.1" + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" } }, "glob-parent": { @@ -6672,7 +6637,7 @@ "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, "requires": { - "is-glob": "2.0.1" + "is-glob": "^2.0.0" } }, "global-modules": { @@ -6681,9 +6646,9 @@ "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", "dev": true, "requires": { - "global-prefix": "1.0.2", - "is-windows": "1.0.1", - "resolve-dir": "1.0.1" + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" } }, "global-prefix": { @@ -6692,11 +6657,11 @@ "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", "dev": true, "requires": { - "expand-tilde": "2.0.2", - "homedir-polyfill": "1.0.1", - "ini": "1.3.5", - "is-windows": "1.0.1", - "which": "1.3.0" + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" } }, "globals": { @@ -6711,12 +6676,12 @@ "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", "dev": true, "requires": { - "array-union": "1.0.2", - "arrify": "1.0.1", - "glob": "7.1.2", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, "globule": { @@ -6725,9 +6690,9 @@ "integrity": "sha1-HcScaCLdnoovoAuiopUAboZkvQk=", "dev": true, "requires": { - "glob": "7.1.2", - "lodash": "4.17.5", - "minimatch": "3.0.4" + "glob": "~7.1.1", + "lodash": "~4.17.4", + "minimatch": "~3.0.2" } }, "good-listener": { @@ -6735,32 +6700,32 @@ "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", "requires": { - "delegate": "3.2.0" + "delegate": "^3.1.2" } }, "got": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/got/-/got-8.3.0.tgz", - "integrity": "sha512-kBNy/S2CGwrYgDSec5KTWGKUvupwkkTVAjIsVFF2shXO13xpZdFP4d4kxa//CLX2tN/rV0aYwK8vY6UKWGn2vQ==", - "dev": true, - "requires": { - "@sindresorhus/is": "0.7.0", - "cacheable-request": "2.1.4", - "decompress-response": "3.3.0", - "duplexer3": "0.1.4", - "get-stream": "3.0.0", - "into-stream": "3.1.0", - "is-retry-allowed": "1.1.0", - "isurl": "1.0.0", - "lowercase-keys": "1.0.1", - "mimic-response": "1.0.0", - "p-cancelable": "0.4.0", - "p-timeout": "2.0.1", - "pify": "3.0.0", - "safe-buffer": "5.1.1", - "timed-out": "4.0.1", - "url-parse-lax": "3.0.0", - "url-to-options": "1.0.1" + "integrity": "sha1-a6JudfimzExrPrH+fOT+x6ushTM=", + "dev": true, + "requires": { + "@sindresorhus/is": "^0.7.0", + "cacheable-request": "^2.1.1", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "into-stream": "^3.1.0", + "is-retry-allowed": "^1.1.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "mimic-response": "^1.0.0", + "p-cancelable": "^0.4.0", + "p-timeout": "^2.0.1", + "pify": "^3.0.0", + "safe-buffer": "^5.1.1", + "timed-out": "^4.0.1", + "url-parse-lax": "^3.0.0", + "url-to-options": "^1.0.1" }, "dependencies": { "pify": { @@ -6774,7 +6739,8 @@ "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true }, "grouped-queue": { "version": "0.3.3", @@ -6782,7 +6748,7 @@ "integrity": "sha1-wWfSpTGcWg4JZO9qJbfC34mWyFw=", "dev": true, "requires": { - "lodash": "4.17.5" + "lodash": "^4.17.2" } }, "growly": { @@ -6797,10 +6763,10 @@ "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", "dev": true, "requires": { - "async": "1.5.2", - "optimist": "0.6.1", - "source-map": "0.4.4", - "uglify-js": "2.8.29" + "async": "^1.4.0", + "optimist": "^0.6.1", + "source-map": "^0.4.4", + "uglify-js": "^2.6" }, "dependencies": { "async": { @@ -6815,7 +6781,7 @@ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { - "amdefine": "1.0.1" + "amdefine": ">=0.0.4" } } } @@ -6832,16 +6798,17 @@ "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", "dev": true, "requires": { - "ajv": "5.5.2", - "har-schema": "2.0.0" + "ajv": "^5.1.0", + "har-schema": "^2.0.0" } }, "has": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "dev": true, "requires": { - "function-bind": "1.1.1" + "function-bind": "^1.0.2" } }, "has-ansi": { @@ -6850,7 +6817,7 @@ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "dev": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "has-color": { @@ -6883,7 +6850,7 @@ "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", "dev": true, "requires": { - "has-symbol-support-x": "1.4.2" + "has-symbol-support-x": "^1.4.1" } }, "has-unicode": { @@ -6898,9 +6865,9 @@ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, "requires": { - "get-value": "2.0.6", - "has-values": "1.0.0", - "isobject": "3.0.1" + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" }, "dependencies": { "isobject": { @@ -6917,8 +6884,8 @@ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "dev": true, "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" + "is-number": "^3.0.0", + "kind-of": "^4.0.0" }, "dependencies": { "is-number": { @@ -6927,7 +6894,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -6936,7 +6903,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -6947,7 +6914,7 @@ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -6958,7 +6925,7 @@ "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=", "dev": true, "requires": { - "inherits": "2.0.3" + "inherits": "^2.0.1" } }, "hash.js": { @@ -6967,8 +6934,8 @@ "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", "dev": true, "requires": { - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.0" } }, "hawk": { @@ -6977,10 +6944,10 @@ "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", "dev": true, "requires": { - "boom": "4.3.1", - "cryptiles": "3.1.2", - "hoek": "4.2.0", - "sntp": "2.1.0" + "boom": "4.x.x", + "cryptiles": "3.x.x", + "hoek": "4.x.x", + "sntp": "2.x.x" } }, "hmac-drbg": { @@ -6989,9 +6956,9 @@ "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", "dev": true, "requires": { - "hash.js": "1.1.3", - "minimalistic-assert": "1.0.0", - "minimalistic-crypto-utils": "1.0.1" + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" } }, "hoek": { @@ -7011,8 +6978,8 @@ "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", "dev": true, "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.1" } }, "homedir-polyfill": { @@ -7021,13 +6988,14 @@ "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", "dev": true, "requires": { - "parse-passwd": "1.0.0" + "parse-passwd": "^1.0.0" } }, "hosted-git-info": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", - "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==" + "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==", + "dev": true }, "hpq": { "version": "1.2.0", @@ -7046,7 +7014,7 @@ "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", "dev": true, "requires": { - "whatwg-encoding": "1.0.3" + "whatwg-encoding": "^1.0.1" } }, "htmlparser2": { @@ -7055,12 +7023,12 @@ "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=", "dev": true, "requires": { - "domelementtype": "1.3.0", - "domhandler": "2.4.1", - "domutils": "1.5.1", - "entities": "1.1.1", - "inherits": "2.0.3", - "readable-stream": "2.3.3" + "domelementtype": "^1.3.0", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^2.0.2" } }, "http-cache-semantics": { @@ -7075,9 +7043,9 @@ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "dev": true, "requires": { - "assert-plus": "1.0.0", - "jsprim": "1.4.1", - "sshpk": "1.13.1" + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" } }, "https-browserify": { @@ -7092,14 +7060,14 @@ "integrity": "sha512-uUWcfXHvy/dwfM9bqa6AozvAjS32dZSTUYd/4SEpYKRg6LEcPLshksnQYRudM9AyNvUARMfAg5TLjUDyX/K4vA==", "dev": true, "requires": { - "agent-base": "4.2.0", - "debug": "3.1.0" + "agent-base": "^4.1.0", + "debug": "^3.1.0" }, "dependencies": { "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -7135,8 +7103,8 @@ "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==", "dev": true, "requires": { - "pkg-dir": "2.0.0", - "resolve-cwd": "2.0.0" + "pkg-dir": "^2.0.0", + "resolve-cwd": "^2.0.0" }, "dependencies": { "find-up": { @@ -7145,7 +7113,7 @@ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "2.0.0" + "locate-path": "^2.0.0" } }, "pkg-dir": { @@ -7154,7 +7122,7 @@ "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", "dev": true, "requires": { - "find-up": "2.1.0" + "find-up": "^2.1.0" } } } @@ -7177,7 +7145,7 @@ "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", "dev": true, "requires": { - "repeating": "2.0.1" + "repeating": "^2.0.0" } }, "indexes-of": { @@ -7198,8 +7166,8 @@ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -7218,20 +7186,20 @@ "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", "dev": true, "requires": { - "ansi-escapes": "3.0.0", - "chalk": "2.3.0", - "cli-cursor": "2.1.0", - "cli-width": "2.2.0", - "external-editor": "2.1.0", - "figures": "2.0.0", - "lodash": "4.17.5", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.0.4", + "figures": "^2.0.0", + "lodash": "^4.3.0", "mute-stream": "0.0.7", - "run-async": "2.3.0", - "rx-lite": "4.0.8", - "rx-lite-aggregates": "4.0.8", - "string-width": "2.1.1", - "strip-ansi": "4.0.0", - "through": "2.3.8" + "run-async": "^2.2.0", + "rx-lite": "^4.0.8", + "rx-lite-aggregates": "^4.0.8", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" }, "dependencies": { "ansi-regex": { @@ -7246,7 +7214,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -7255,9 +7223,9 @@ "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" + "ansi-styles": "^3.1.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^4.0.0" } }, "cli-cursor": { @@ -7266,7 +7234,7 @@ "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", "dev": true, "requires": { - "restore-cursor": "2.0.0" + "restore-cursor": "^2.0.0" } }, "figures": { @@ -7275,7 +7243,7 @@ "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", "dev": true, "requires": { - "escape-string-regexp": "1.0.5" + "escape-string-regexp": "^1.0.5" } }, "onetime": { @@ -7284,7 +7252,7 @@ "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", "dev": true, "requires": { - "mimic-fn": "1.2.0" + "mimic-fn": "^1.0.0" } }, "restore-cursor": { @@ -7293,8 +7261,8 @@ "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", "dev": true, "requires": { - "onetime": "2.0.1", - "signal-exit": "3.0.2" + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" } }, "strip-ansi": { @@ -7303,7 +7271,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } }, "supports-color": { @@ -7312,7 +7280,7 @@ "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "^2.0.0" } } } @@ -7329,8 +7297,8 @@ "integrity": "sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=", "dev": true, "requires": { - "from2": "2.3.0", - "p-is-promise": "1.1.0" + "from2": "^2.1.1", + "p-is-promise": "^1.1.0" } }, "invariant": { @@ -7339,7 +7307,7 @@ "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", "dev": true, "requires": { - "loose-envify": "1.3.1" + "loose-envify": "^1.0.0" } }, "invert-kv": { @@ -7359,7 +7327,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" }, "dependencies": { "kind-of": { @@ -7373,7 +7341,8 @@ "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true }, "is-binary-path": { "version": "1.0.1", @@ -7381,7 +7350,7 @@ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, "requires": { - "binary-extensions": "1.11.0" + "binary-extensions": "^1.0.0" } }, "is-boolean-object": { @@ -7400,8 +7369,9 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true, "requires": { - "builtin-modules": "1.1.1" + "builtin-modules": "^1.0.0" } }, "is-callable": { @@ -7416,7 +7386,7 @@ "integrity": "sha512-c7TnwxLePuqIlxHgr7xtxzycJPegNHFuIrBkwbf8hc58//+Op1CqFkyS+xnIMkwn9UsJIwc174BIjkyBmSpjKg==", "dev": true, "requires": { - "ci-info": "1.1.2" + "ci-info": "^1.0.0" } }, "is-data-descriptor": { @@ -7425,7 +7395,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" }, "dependencies": { "kind-of": { @@ -7448,9 +7418,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" }, "dependencies": { "kind-of": { @@ -7479,7 +7449,7 @@ "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", "dev": true, "requires": { - "is-primitive": "2.0.0" + "is-primitive": "^2.0.0" } }, "is-extendable": { @@ -7500,7 +7470,7 @@ "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", "dev": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "is-fullwidth-code-point": { @@ -7508,7 +7478,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "is-generator-fn": { @@ -7523,7 +7493,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "1.0.0" + "is-extglob": "^1.0.0" } }, "is-my-json-valid": { @@ -7532,10 +7502,10 @@ "integrity": "sha512-Q2khNw+oBlWuaYvEEHtKSw/pCxD2L5Rc1C+UQme9X6JdRDh7m5D7HkozA0qa3DUkQ6VzCnEm8mVIQPyIRkI5sQ==", "dev": true, "requires": { - "generate-function": "2.0.0", - "generate-object-property": "1.2.0", - "jsonpointer": "4.0.1", - "xtend": "4.0.1" + "generate-function": "^2.0.0", + "generate-object-property": "^1.1.0", + "jsonpointer": "^4.0.0", + "xtend": "^4.0.0" } }, "is-number": { @@ -7544,7 +7514,7 @@ "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" } }, "is-number-object": { @@ -7565,7 +7535,7 @@ "integrity": "sha1-s2ExHYPG5dcmyr9eJQsCNxBvWuI=", "dev": true, "requires": { - "symbol-observable": "0.2.4" + "symbol-observable": "^0.2.2" }, "dependencies": { "symbol-observable": { @@ -7582,7 +7552,7 @@ "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", "dev": true, "requires": { - "is-number": "4.0.0" + "is-number": "^4.0.0" }, "dependencies": { "is-number": { @@ -7605,7 +7575,7 @@ "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", "dev": true, "requires": { - "is-path-inside": "1.0.1" + "is-path-inside": "^1.0.0" } }, "is-path-inside": { @@ -7614,7 +7584,7 @@ "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", "dev": true, "requires": { - "path-is-inside": "1.0.2" + "path-is-inside": "^1.0.1" } }, "is-plain-obj": { @@ -7629,7 +7599,7 @@ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.1" }, "dependencies": { "isobject": { @@ -7670,7 +7640,7 @@ "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", "dev": true, "requires": { - "has": "1.0.1" + "has": "^1.0.1" } }, "is-resolvable": { @@ -7691,7 +7661,7 @@ "integrity": "sha1-RJypgpnnEwOCViieyytUDcQ3yzA=", "dev": true, "requires": { - "scoped-regex": "1.0.0" + "scoped-regex": "^1.0.0" } }, "is-stream": { @@ -7717,7 +7687,7 @@ "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", "dev": true, "requires": { - "html-comment-regex": "1.1.1" + "html-comment-regex": "^1.1.0" } }, "is-symbol": { @@ -7768,8 +7738,8 @@ "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", "requires": { - "node-fetch": "1.7.3", - "whatwg-fetch": "2.0.3" + "node-fetch": "^1.0.1", + "whatwg-fetch": ">=0.10.0" } }, "isstream": { @@ -7784,17 +7754,17 @@ "integrity": "sha512-kH5YRdqdbs5hiH4/Rr1Q0cSAGgjh3jTtg8vu9NLebBAoK3adVO4jk81J+TYOkTr2+Q4NLeb1ACvmEt65iG/Vbw==", "dev": true, "requires": { - "async": "2.6.0", - "fileset": "2.0.3", - "istanbul-lib-coverage": "1.1.2", - "istanbul-lib-hook": "1.1.0", - "istanbul-lib-instrument": "1.9.2", - "istanbul-lib-report": "1.1.3", - "istanbul-lib-source-maps": "1.2.3", - "istanbul-reports": "1.1.4", - "js-yaml": "3.10.0", - "mkdirp": "0.5.1", - "once": "1.4.0" + "async": "^2.1.4", + "fileset": "^2.0.2", + "istanbul-lib-coverage": "^1.1.2", + "istanbul-lib-hook": "^1.1.0", + "istanbul-lib-instrument": "^1.9.2", + "istanbul-lib-report": "^1.1.3", + "istanbul-lib-source-maps": "^1.2.3", + "istanbul-reports": "^1.1.4", + "js-yaml": "^3.7.0", + "mkdirp": "^0.5.1", + "once": "^1.4.0" } }, "istanbul-lib-coverage": { @@ -7809,7 +7779,7 @@ "integrity": "sha512-U3qEgwVDUerZ0bt8cfl3dSP3S6opBoOtk3ROO5f2EfBr/SRiD9FQqzwaZBqFORu8W7O0EXpai+k7kxHK13beRg==", "dev": true, "requires": { - "append-transform": "0.4.0" + "append-transform": "^0.4.0" } }, "istanbul-lib-instrument": { @@ -7818,13 +7788,13 @@ "integrity": "sha512-nz8t4HQ2206a/3AXi+NHFWEa844DMpPsgbcUteJbt1j8LX1xg56H9rOMnhvcvVvPbW60qAIyrSk44H8ZDqaSSA==", "dev": true, "requires": { - "babel-generator": "6.26.1", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "istanbul-lib-coverage": "1.1.2", - "semver": "5.3.0" + "babel-generator": "^6.18.0", + "babel-template": "^6.16.0", + "babel-traverse": "^6.18.0", + "babel-types": "^6.18.0", + "babylon": "^6.18.0", + "istanbul-lib-coverage": "^1.1.2", + "semver": "^5.3.0" } }, "istanbul-lib-report": { @@ -7833,10 +7803,10 @@ "integrity": "sha512-D4jVbMDtT2dPmloPJS/rmeP626N5Pr3Rp+SovrPn1+zPChGHcggd/0sL29jnbm4oK9W0wHjCRsdch9oLd7cm6g==", "dev": true, "requires": { - "istanbul-lib-coverage": "1.1.2", - "mkdirp": "0.5.1", - "path-parse": "1.0.5", - "supports-color": "3.2.3" + "istanbul-lib-coverage": "^1.1.2", + "mkdirp": "^0.5.1", + "path-parse": "^1.0.5", + "supports-color": "^3.1.2" }, "dependencies": { "has-flag": { @@ -7851,7 +7821,7 @@ "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "1.0.0" + "has-flag": "^1.0.0" } } } @@ -7862,11 +7832,11 @@ "integrity": "sha512-fDa0hwU/5sDXwAklXgAoCJCOsFsBplVQ6WBldz5UwaqOzmDhUK4nfuR7/G//G2lERlblUNJB8P6e8cXq3a7MlA==", "dev": true, "requires": { - "debug": "3.1.0", - "istanbul-lib-coverage": "1.1.2", - "mkdirp": "0.5.1", - "rimraf": "2.6.2", - "source-map": "0.5.7" + "debug": "^3.1.0", + "istanbul-lib-coverage": "^1.1.2", + "mkdirp": "^0.5.1", + "rimraf": "^2.6.1", + "source-map": "^0.5.3" }, "dependencies": { "debug": { @@ -7886,7 +7856,7 @@ "integrity": "sha512-DfSTVOTkuO+kRmbO8Gk650Wqm1WRGr6lrdi2EwDK1vxpS71vdlLd613EpzOKdIFioB5f/scJTjeWBnvd1FWejg==", "dev": true, "requires": { - "handlebars": "4.0.11" + "handlebars": "^4.0.3" } }, "istextorbinary": { @@ -7895,9 +7865,9 @@ "integrity": "sha512-TS+hoFl8Z5FAFMK38nhBkdLt44CclNRgDHWeMgsV8ko3nDlr/9UI2Sf839sW7enijf8oKsZYXRvM8g0it9Zmcw==", "dev": true, "requires": { - "binaryextensions": "2.1.1", - "editions": "1.3.4", - "textextensions": "2.2.0" + "binaryextensions": "2", + "editions": "^1.3.3", + "textextensions": "2" } }, "isurl": { @@ -7906,8 +7876,8 @@ "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", "dev": true, "requires": { - "has-to-string-tag-x": "1.4.1", - "is-object": "1.0.1" + "has-to-string-tag-x": "^1.2.0", + "is-object": "^1.0.1" } }, "jed": { @@ -7921,8 +7891,8 @@ "integrity": "sha512-eze1JLbBDkrbZMnE6xIlBxHkqPAmuHbz4GQbED8qRVtnpea3o6Tt/Dc3SBs3qnlTo7svema8Ho5bqLfdHyabyQ==", "dev": true, "requires": { - "import-local": "1.0.0", - "jest-cli": "22.4.0" + "import-local": "^1.0.0", + "jest-cli": "^22.4.0" }, "dependencies": { "ansi-regex": { @@ -7937,7 +7907,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -7946,9 +7916,9 @@ "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "5.2.0" + "ansi-styles": "^3.2.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.2.0" } }, "cliui": { @@ -7957,9 +7927,9 @@ "integrity": "sha512-nY3W5Gu2racvdDk//ELReY+dHjb9PlIcVDFXP72nVIhq2Gy3LuVXYwJoPVudwQnv1shtohpgkdCKT2YaKY0CKw==", "dev": true, "requires": { - "string-width": "2.1.1", - "strip-ansi": "4.0.0", - "wrap-ansi": "2.1.0" + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" } }, "find-up": { @@ -7968,7 +7938,7 @@ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "2.0.0" + "locate-path": "^2.0.0" } }, "has-flag": { @@ -7983,40 +7953,40 @@ "integrity": "sha512-0JlBb/PvHGQZR2I9GZwsycHgWHhriBmvBWPaaPYUT186oiIIDY4ezDxFOFt2Ts0yNTRg3iY9mTyHsfWbT5VRWA==", "dev": true, "requires": { - "ansi-escapes": "3.0.0", - "chalk": "2.3.1", - "exit": "0.1.2", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "import-local": "1.0.0", - "is-ci": "1.1.0", - "istanbul-api": "1.2.2", - "istanbul-lib-coverage": "1.1.2", - "istanbul-lib-instrument": "1.9.2", - "istanbul-lib-source-maps": "1.2.3", - "jest-changed-files": "22.2.0", - "jest-config": "22.4.0", - "jest-environment-jsdom": "22.4.0", - "jest-get-type": "22.1.0", - "jest-haste-map": "22.4.0", - "jest-message-util": "22.4.0", - "jest-regex-util": "22.1.0", - "jest-resolve-dependencies": "22.1.0", - "jest-runner": "22.4.0", - "jest-runtime": "22.4.0", - "jest-snapshot": "22.4.0", - "jest-util": "22.4.0", - "jest-validate": "22.4.0", - "jest-worker": "22.2.2", - "micromatch": "2.3.11", - "node-notifier": "5.2.1", - "realpath-native": "1.0.0", - "rimraf": "2.6.2", - "slash": "1.0.0", - "string-length": "2.0.0", - "strip-ansi": "4.0.0", - "which": "1.3.0", - "yargs": "10.1.2" + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "import-local": "^1.0.0", + "is-ci": "^1.0.10", + "istanbul-api": "^1.1.14", + "istanbul-lib-coverage": "^1.1.1", + "istanbul-lib-instrument": "^1.8.0", + "istanbul-lib-source-maps": "^1.2.1", + "jest-changed-files": "^22.2.0", + "jest-config": "^22.4.0", + "jest-environment-jsdom": "^22.4.0", + "jest-get-type": "^22.1.0", + "jest-haste-map": "^22.4.0", + "jest-message-util": "^22.4.0", + "jest-regex-util": "^22.1.0", + "jest-resolve-dependencies": "^22.1.0", + "jest-runner": "^22.4.0", + "jest-runtime": "^22.4.0", + "jest-snapshot": "^22.4.0", + "jest-util": "^22.4.0", + "jest-validate": "^22.4.0", + "jest-worker": "^22.2.2", + "micromatch": "^2.3.11", + "node-notifier": "^5.2.1", + "realpath-native": "^1.0.0", + "rimraf": "^2.5.4", + "slash": "^1.0.0", + "string-length": "^2.0.0", + "strip-ansi": "^4.0.0", + "which": "^1.2.12", + "yargs": "^10.0.3" } }, "strip-ansi": { @@ -8025,7 +7995,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } }, "supports-color": { @@ -8034,36 +8004,36 @@ "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } }, "yargs": { "version": "10.1.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-10.1.2.tgz", - "integrity": "sha512-ivSoxqBGYOqQVruxD35+EyCFDYNEFL/Uo6FcOnz+9xZdZzK0Zzw4r4KhbrME1Oo2gOggwJod2MnsdamSG7H9ig==", + "integrity": "sha1-RU0HTCsWpRpD4vt4B+T53mnMtcU=", "dev": true, "requires": { - "cliui": "4.0.0", - "decamelize": "1.2.0", - "find-up": "2.1.0", - "get-caller-file": "1.0.2", - "os-locale": "2.1.0", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "2.1.1", - "which-module": "2.0.0", - "y18n": "3.2.1", - "yargs-parser": "8.1.0" + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^8.1.0" } }, "yargs-parser": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz", - "integrity": "sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==", + "integrity": "sha1-8TdqM7Ziml0GN4KUTacyYx6WaVA=", "dev": true, "requires": { - "camelcase": "4.1.0" + "camelcase": "^4.1.0" } } } @@ -8074,7 +8044,7 @@ "integrity": "sha512-SzqOvoPMrXB0NPvDrSPeKETpoUNCtNDOsFbCzAGWxqWVvNyrIMLpUjVExT3u3LfdVrENlrNGCfh5YoFd8+ZeXg==", "dev": true, "requires": { - "throat": "4.1.0" + "throat": "^4.0.0" } }, "jest-config": { @@ -8083,17 +8053,17 @@ "integrity": "sha512-hZs8qHjCybOpqni0Kwt40eAavYN/3KnJJwYxSJsBRedJ98IgGSiI18SjybCSccKayA7eHgw1A+dLkHcfI4LItQ==", "dev": true, "requires": { - "chalk": "2.3.1", - "glob": "7.1.2", - "jest-environment-jsdom": "22.4.0", - "jest-environment-node": "22.4.0", - "jest-get-type": "22.1.0", - "jest-jasmine2": "22.4.0", - "jest-regex-util": "22.1.0", - "jest-resolve": "22.4.0", - "jest-util": "22.4.0", - "jest-validate": "22.4.0", - "pretty-format": "22.4.0" + "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^22.4.0", + "jest-environment-node": "^22.4.0", + "jest-get-type": "^22.1.0", + "jest-jasmine2": "^22.4.0", + "jest-regex-util": "^22.1.0", + "jest-resolve": "^22.4.0", + "jest-util": "^22.4.0", + "jest-validate": "^22.4.0", + "pretty-format": "^22.4.0" }, "dependencies": { "ansi-styles": { @@ -8102,7 +8072,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -8111,9 +8081,9 @@ "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "5.2.0" + "ansi-styles": "^3.2.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.2.0" } }, "has-flag": { @@ -8128,7 +8098,7 @@ "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -8139,10 +8109,10 @@ "integrity": "sha512-+/t20WmnkOkB8MOaGaPziI8zWKxquMvYw4Ub+wOzi7AUhmpFXz43buWSxVoZo4J5RnCozpGbX3/FssjJ5KV9Nw==", "dev": true, "requires": { - "chalk": "2.3.1", - "diff": "3.4.0", - "jest-get-type": "22.1.0", - "pretty-format": "22.4.0" + "chalk": "^2.0.1", + "diff": "^3.2.0", + "jest-get-type": "^22.1.0", + "pretty-format": "^22.4.0" }, "dependencies": { "ansi-styles": { @@ -8151,7 +8121,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -8160,9 +8130,9 @@ "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "5.2.0" + "ansi-styles": "^3.2.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.2.0" } }, "has-flag": { @@ -8177,7 +8147,7 @@ "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -8188,7 +8158,7 @@ "integrity": "sha512-lDY7GZ+/CJb02oULYLBDj7Hs5shBhVpDYpIm8LUyqw9X2J22QRsM19gmGQwIFqGSJmpc/LRrSYudeSrG510xlQ==", "dev": true, "requires": { - "detect-newline": "2.1.0" + "detect-newline": "^2.1.0" } }, "jest-environment-jsdom": { @@ -8197,9 +8167,9 @@ "integrity": "sha512-SAUCte4KFLaD2YhYwHFVEI2GkR4BHqHJsnbFgmQMGgHnZ2CfjSZE8Bnb+jlarbxIG4GXl31+2e9rjBpzbY9gKQ==", "dev": true, "requires": { - "jest-mock": "22.2.0", - "jest-util": "22.4.0", - "jsdom": "11.6.2" + "jest-mock": "^22.2.0", + "jest-util": "^22.4.0", + "jsdom": "^11.5.1" } }, "jest-environment-node": { @@ -8208,8 +8178,8 @@ "integrity": "sha512-ihSKa2MU5jkAhmRJ17FU4nisbbfW6spvl6Jtwmm5W9kmTVa2sa9UoHWbOWAb7HXuLi3PGGjzTfEt5o3uIzisnQ==", "dev": true, "requires": { - "jest-mock": "22.2.0", - "jest-util": "22.4.0" + "jest-mock": "^22.2.0", + "jest-util": "^22.4.0" } }, "jest-enzyme": { @@ -8218,8 +8188,8 @@ "integrity": "sha512-nna99NnU6sDbWqVX0153c81RUuxI/spTgw4Xobh049NcKihu0OAtAawbuSzZUnlCqdZOoXlKMudfjUPm0sCTsg==", "dev": true, "requires": { - "enzyme-matchers": "4.2.0", - "enzyme-to-json": "3.3.1" + "enzyme-matchers": "^4.2.0", + "enzyme-to-json": "^3.3.0" } }, "jest-get-type": { @@ -8234,13 +8204,13 @@ "integrity": "sha512-znYomZ+GaRcuFLQz7hmwQOfLkHY2Y2Aoyd29ZcXLrwBEWts5U/c7lFsqo54XUJUlMhrM5M2IOaAUWjZ1CRqAOQ==", "dev": true, "requires": { - "fb-watchman": "2.0.0", - "graceful-fs": "4.1.11", - "jest-docblock": "22.4.0", - "jest-serializer": "22.4.0", - "jest-worker": "22.2.2", - "micromatch": "2.3.11", - "sane": "2.4.1" + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.1.11", + "jest-docblock": "^22.4.0", + "jest-serializer": "^22.4.0", + "jest-worker": "^22.2.2", + "micromatch": "^2.3.11", + "sane": "^2.0.0" } }, "jest-jasmine2": { @@ -8249,17 +8219,17 @@ "integrity": "sha512-oL7bNLfEL9jPVjmiwqQuwrAJ/5ddmKHSpns0kCpAmv1uQ47Q5aC9zBTXZbDWP5GVbVHj2hbYtNbkwTiXJr0e8w==", "dev": true, "requires": { - "callsites": "2.0.0", - "chalk": "2.3.1", - "co": "4.6.0", - "expect": "22.4.0", - "graceful-fs": "4.1.11", - "is-generator-fn": "1.0.0", - "jest-diff": "22.4.0", - "jest-matcher-utils": "22.4.0", - "jest-message-util": "22.4.0", - "jest-snapshot": "22.4.0", - "source-map-support": "0.5.3" + "callsites": "^2.0.0", + "chalk": "^2.0.1", + "co": "^4.6.0", + "expect": "^22.4.0", + "graceful-fs": "^4.1.11", + "is-generator-fn": "^1.0.0", + "jest-diff": "^22.4.0", + "jest-matcher-utils": "^22.4.0", + "jest-message-util": "^22.4.0", + "jest-snapshot": "^22.4.0", + "source-map-support": "^0.5.0" }, "dependencies": { "ansi-styles": { @@ -8268,7 +8238,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -8277,9 +8247,9 @@ "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "5.2.0" + "ansi-styles": "^3.2.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.2.0" } }, "has-flag": { @@ -8294,7 +8264,7 @@ "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -8305,7 +8275,7 @@ "integrity": "sha512-r3NEIVNh4X3fEeJtUIrKXWKhNokwUM2ILp5LD8w1KrEanPsFtZmYjmyZYjDTX2dXYr33TW65OvbRE3hWFAyq6g==", "dev": true, "requires": { - "pretty-format": "22.4.0" + "pretty-format": "^22.4.0" } }, "jest-matcher-utils": { @@ -8314,9 +8284,9 @@ "integrity": "sha512-03m3issxUXpWMwDYTfmL8hRNewUB0yCRTeXPm+eq058rZxLHD9f5NtSSO98CWHqe4UyISIxd9Ao9iDVjHWd2qg==", "dev": true, "requires": { - "chalk": "2.3.1", - "jest-get-type": "22.1.0", - "pretty-format": "22.4.0" + "chalk": "^2.0.1", + "jest-get-type": "^22.1.0", + "pretty-format": "^22.4.0" }, "dependencies": { "ansi-styles": { @@ -8325,7 +8295,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -8334,9 +8304,9 @@ "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "5.2.0" + "ansi-styles": "^3.2.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.2.0" } }, "has-flag": { @@ -8351,7 +8321,7 @@ "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -8362,11 +8332,11 @@ "integrity": "sha512-eyCJB0T3hrlpFF2FqQoIB093OulP+1qvATQmD3IOgJgMGqPL6eYw8TbC5P/VCWPqKhGL51xvjIIhow5eZ2wHFw==", "dev": true, "requires": { - "@babel/code-frame": "7.0.0-beta.40", - "chalk": "2.3.1", - "micromatch": "2.3.11", - "slash": "1.0.0", - "stack-utils": "1.0.1" + "@babel/code-frame": "^7.0.0-beta.35", + "chalk": "^2.0.1", + "micromatch": "^2.3.11", + "slash": "^1.0.0", + "stack-utils": "^1.0.1" }, "dependencies": { "ansi-styles": { @@ -8375,7 +8345,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -8384,9 +8354,9 @@ "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "5.2.0" + "ansi-styles": "^3.2.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.2.0" } }, "has-flag": { @@ -8401,7 +8371,7 @@ "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -8424,8 +8394,8 @@ "integrity": "sha512-Vs/5VeJEHLpB0ubpYuU9QpBjcCUZRHoHnoV58ZC+N3EXyMJr/MgoqUNpo4OHGQERWlUpvl4YLAAO5uxSMF2VIg==", "dev": true, "requires": { - "browser-resolve": "1.11.2", - "chalk": "2.3.1" + "browser-resolve": "^1.11.2", + "chalk": "^2.0.1" }, "dependencies": { "ansi-styles": { @@ -8434,7 +8404,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -8443,9 +8413,9 @@ "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "5.2.0" + "ansi-styles": "^3.2.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.2.0" } }, "has-flag": { @@ -8460,7 +8430,7 @@ "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -8471,7 +8441,7 @@ "integrity": "sha512-76Ll61bD/Sus8wK8d+lw891EtiBJGJkWG8OuVDTEX0z3z2+jPujvQqSB2eQ+kCHyCsRwJ2PSjhn3UHqae/oEtA==", "dev": true, "requires": { - "jest-regex-util": "22.1.0" + "jest-regex-util": "^22.1.0" } }, "jest-runner": { @@ -8480,17 +8450,17 @@ "integrity": "sha512-x5QJQrSQs/oaZq2UxtKJxCjGq3fNF7guKRLxAIS39QIaRSAynS4agniMyvHMnLaYsBh6yzUea2SDeNHayQh+TQ==", "dev": true, "requires": { - "exit": "0.1.2", - "jest-config": "22.4.0", - "jest-docblock": "22.4.0", - "jest-haste-map": "22.4.0", - "jest-jasmine2": "22.4.0", - "jest-leak-detector": "22.4.0", - "jest-message-util": "22.4.0", - "jest-runtime": "22.4.0", - "jest-util": "22.4.0", - "jest-worker": "22.2.2", - "throat": "4.1.0" + "exit": "^0.1.2", + "jest-config": "^22.4.0", + "jest-docblock": "^22.4.0", + "jest-haste-map": "^22.4.0", + "jest-jasmine2": "^22.4.0", + "jest-leak-detector": "^22.4.0", + "jest-message-util": "^22.4.0", + "jest-runtime": "^22.4.0", + "jest-util": "^22.4.0", + "jest-worker": "^22.2.2", + "throat": "^4.0.0" } }, "jest-runtime": { @@ -8499,26 +8469,26 @@ "integrity": "sha512-aixL2DIXoFQ2ubnurzK4kbNXLl3+m0m7wIBb5VWaJdl1/3nV1UCSjZ9/dJZzpWGGfXsoGw2RZd8sS0nS5s+tdw==", "dev": true, "requires": { - "babel-core": "6.26.0", - "babel-jest": "22.4.0", - "babel-plugin-istanbul": "4.1.5", - "chalk": "2.3.1", - "convert-source-map": "1.5.1", - "exit": "0.1.2", - "graceful-fs": "4.1.11", - "jest-config": "22.4.0", - "jest-haste-map": "22.4.0", - "jest-regex-util": "22.1.0", - "jest-resolve": "22.4.0", - "jest-util": "22.4.0", - "jest-validate": "22.4.0", - "json-stable-stringify": "1.0.1", - "micromatch": "2.3.11", - "realpath-native": "1.0.0", - "slash": "1.0.0", + "babel-core": "^6.0.0", + "babel-jest": "^22.4.0", + "babel-plugin-istanbul": "^4.1.5", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "exit": "^0.1.2", + "graceful-fs": "^4.1.11", + "jest-config": "^22.4.0", + "jest-haste-map": "^22.4.0", + "jest-regex-util": "^22.1.0", + "jest-resolve": "^22.4.0", + "jest-util": "^22.4.0", + "jest-validate": "^22.4.0", + "json-stable-stringify": "^1.0.1", + "micromatch": "^2.3.11", + "realpath-native": "^1.0.0", + "slash": "^1.0.0", "strip-bom": "3.0.0", - "write-file-atomic": "2.3.0", - "yargs": "10.1.2" + "write-file-atomic": "^2.1.0", + "yargs": "^10.0.3" }, "dependencies": { "ansi-regex": { @@ -8533,7 +8503,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -8542,9 +8512,9 @@ "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "5.2.0" + "ansi-styles": "^3.2.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.2.0" } }, "cliui": { @@ -8553,9 +8523,9 @@ "integrity": "sha512-nY3W5Gu2racvdDk//ELReY+dHjb9PlIcVDFXP72nVIhq2Gy3LuVXYwJoPVudwQnv1shtohpgkdCKT2YaKY0CKw==", "dev": true, "requires": { - "string-width": "2.1.1", - "strip-ansi": "4.0.0", - "wrap-ansi": "2.1.0" + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" } }, "find-up": { @@ -8564,7 +8534,7 @@ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "2.0.0" + "locate-path": "^2.0.0" } }, "has-flag": { @@ -8579,7 +8549,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } }, "supports-color": { @@ -8588,36 +8558,36 @@ "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } }, "yargs": { "version": "10.1.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-10.1.2.tgz", - "integrity": "sha512-ivSoxqBGYOqQVruxD35+EyCFDYNEFL/Uo6FcOnz+9xZdZzK0Zzw4r4KhbrME1Oo2gOggwJod2MnsdamSG7H9ig==", + "integrity": "sha1-RU0HTCsWpRpD4vt4B+T53mnMtcU=", "dev": true, "requires": { - "cliui": "4.0.0", - "decamelize": "1.2.0", - "find-up": "2.1.0", - "get-caller-file": "1.0.2", - "os-locale": "2.1.0", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "2.1.1", - "which-module": "2.0.0", - "y18n": "3.2.1", - "yargs-parser": "8.1.0" + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^8.1.0" } }, "yargs-parser": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz", - "integrity": "sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==", + "integrity": "sha1-8TdqM7Ziml0GN4KUTacyYx6WaVA=", "dev": true, "requires": { - "camelcase": "4.1.0" + "camelcase": "^4.1.0" } } } @@ -8634,12 +8604,12 @@ "integrity": "sha512-6Zz4F9G1Nbr93kfm5h3A2+OkE+WGpgJlskYE4iSNN2uYfoTL5b9W6aB9Orpx+ueReHyqmy7HET7Z3EmYlL3hKw==", "dev": true, "requires": { - "chalk": "2.3.1", - "jest-diff": "22.4.0", - "jest-matcher-utils": "22.4.0", - "mkdirp": "0.5.1", - "natural-compare": "1.4.0", - "pretty-format": "22.4.0" + "chalk": "^2.0.1", + "jest-diff": "^22.4.0", + "jest-matcher-utils": "^22.4.0", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^22.4.0" }, "dependencies": { "ansi-styles": { @@ -8648,7 +8618,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -8657,9 +8627,9 @@ "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "5.2.0" + "ansi-styles": "^3.2.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.2.0" } }, "has-flag": { @@ -8674,7 +8644,7 @@ "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -8685,12 +8655,12 @@ "integrity": "sha512-652EArz3XScAGAUMhbny7FrFGlmJkp+56CO+9RTrKPtGfbtVDF2WB2D8G+6D6zorDmDW5hNtKNIGNdGfG2kj1g==", "dev": true, "requires": { - "callsites": "2.0.0", - "chalk": "2.3.1", - "graceful-fs": "4.1.11", - "is-ci": "1.1.0", - "jest-message-util": "22.4.0", - "mkdirp": "0.5.1" + "callsites": "^2.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.11", + "is-ci": "^1.0.10", + "jest-message-util": "^22.4.0", + "mkdirp": "^0.5.1" }, "dependencies": { "ansi-styles": { @@ -8699,7 +8669,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -8708,9 +8678,9 @@ "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "5.2.0" + "ansi-styles": "^3.2.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.2.0" } }, "has-flag": { @@ -8725,7 +8695,7 @@ "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -8736,11 +8706,11 @@ "integrity": "sha512-l5JwbIAso8jGp/5/Dy86BCVjOra/Rb81wyXcFTGa4VxbtIh4AEOp2WixgprHLwp+YlUrHugZwaGyuagjB+iB+A==", "dev": true, "requires": { - "chalk": "2.3.1", - "jest-config": "22.4.0", - "jest-get-type": "22.1.0", - "leven": "2.1.0", - "pretty-format": "22.4.0" + "chalk": "^2.0.1", + "jest-config": "^22.4.0", + "jest-get-type": "^22.1.0", + "leven": "^2.1.0", + "pretty-format": "^22.4.0" }, "dependencies": { "ansi-styles": { @@ -8749,7 +8719,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -8758,9 +8728,9 @@ "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "5.2.0" + "ansi-styles": "^3.2.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.2.0" } }, "has-flag": { @@ -8775,7 +8745,7 @@ "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -8786,7 +8756,7 @@ "integrity": "sha512-ZylDXjrFNt/OP6cUxwJFWwDgazP7hRjtCQbocFHyiwov+04Wm1x5PYzMGNJT53s4nwr0oo9ocYTImS09xOlUnw==", "dev": true, "requires": { - "merge-stream": "1.0.1" + "merge-stream": "^1.0.1" } }, "jquery": { @@ -8805,10 +8775,10 @@ "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.6.14.tgz", "integrity": "sha1-07j3Mi0CuSd9WL0jgmTDJ+WARM0=", "requires": { - "config-chain": "1.1.11", - "editorconfig": "0.13.3", - "mkdirp": "0.5.1", - "nopt": "3.0.6" + "config-chain": "~1.1.5", + "editorconfig": "^0.13.2", + "mkdirp": "~0.5.0", + "nopt": "~3.0.1" } }, "js-tokens": { @@ -8822,8 +8792,8 @@ "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", "dev": true, "requires": { - "argparse": "1.0.9", - "esprima": "4.0.0" + "argparse": "^1.0.7", + "esprima": "^4.0.0" } }, "jsbn": { @@ -8839,21 +8809,21 @@ "integrity": "sha512-JAcQINNMFpdzzpKJN8k5xXjF3XDuckB1/48uScSzcnNyK199iWEc9AxKL9OoX5144M2w5zEx9Qs4/E/eBZZUlw==", "dev": true, "requires": { - "babel-plugin-transform-flow-strip-types": "6.22.0", - "babel-preset-es2015": "6.24.1", - "babel-preset-stage-1": "6.24.1", - "babel-register": "6.26.0", - "babylon": "7.0.0-beta.42", - "colors": "1.2.1", - "flow-parser": "0.69.0", - "lodash": "4.17.5", - "micromatch": "2.3.11", - "neo-async": "2.5.0", + "babel-plugin-transform-flow-strip-types": "^6.8.0", + "babel-preset-es2015": "^6.9.0", + "babel-preset-stage-1": "^6.5.0", + "babel-register": "^6.9.0", + "babylon": "^7.0.0-beta.30", + "colors": "^1.1.2", + "flow-parser": "^0.*", + "lodash": "^4.13.1", + "micromatch": "^2.3.7", + "neo-async": "^2.5.0", "node-dir": "0.1.8", - "nomnom": "1.8.1", - "recast": "0.14.7", - "temp": "0.8.3", - "write-file-atomic": "1.3.4" + "nomnom": "^1.8.1", + "recast": "^0.14.1", + "temp": "^0.8.1", + "write-file-atomic": "^1.2.0" }, "dependencies": { "ansi-styles": { @@ -8874,15 +8844,15 @@ "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=", "dev": true, "requires": { - "ansi-styles": "1.0.0", - "has-color": "0.1.7", - "strip-ansi": "0.1.1" + "ansi-styles": "~1.0.0", + "has-color": "~0.1.0", + "strip-ansi": "~0.1.0" } }, "colors": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.1.tgz", - "integrity": "sha512-s8+wktIuDSLffCywiwSxQOMqtPxML11a/dtHE17tMn4B1MSWw/C22EKf7M2KGUBcDaVFEGT+S8N02geDXeuNKg==", + "integrity": "sha1-9KPTApdqrwQjVroa3jsaLGLZ15Q=", "dev": true }, "nomnom": { @@ -8891,8 +8861,8 @@ "integrity": "sha1-IVH3Ikcrp55Qp2/BJbuMjy5Nwqc=", "dev": true, "requires": { - "chalk": "0.4.0", - "underscore": "1.6.0" + "chalk": "~0.4.0", + "underscore": "~1.6.0" } }, "strip-ansi": { @@ -8913,9 +8883,9 @@ "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "imurmurhash": "0.1.4", - "slide": "1.1.6" + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "slide": "^1.1.5" } } } @@ -8926,38 +8896,38 @@ "integrity": "sha512-pAeZhpbSlUp5yQcS6cBQJwkbzmv4tWFaYxHbFVSxzXefqjvtRA851Z5N2P+TguVG9YeUDcgb8pdeVQRJh0XR3Q==", "dev": true, "requires": { - "abab": "1.0.4", - "acorn": "5.4.1", - "acorn-globals": "4.1.0", - "array-equal": "1.0.0", - "browser-process-hrtime": "0.1.2", - "content-type-parser": "1.0.2", - "cssom": "0.3.2", - "cssstyle": "0.2.37", - "domexception": "1.0.1", - "escodegen": "1.9.0", - "html-encoding-sniffer": "1.0.2", - "left-pad": "1.2.0", - "nwmatcher": "1.4.3", + "abab": "^1.0.4", + "acorn": "^5.3.0", + "acorn-globals": "^4.1.0", + "array-equal": "^1.0.0", + "browser-process-hrtime": "^0.1.2", + "content-type-parser": "^1.0.2", + "cssom": ">= 0.3.2 < 0.4.0", + "cssstyle": ">= 0.2.37 < 0.3.0", + "domexception": "^1.0.0", + "escodegen": "^1.9.0", + "html-encoding-sniffer": "^1.0.2", + "left-pad": "^1.2.0", + "nwmatcher": "^1.4.3", "parse5": "4.0.0", - "pn": "1.1.0", - "request": "2.83.0", - "request-promise-native": "1.0.5", - "sax": "1.2.4", - "symbol-tree": "3.2.2", - "tough-cookie": "2.3.3", - "w3c-hr-time": "1.0.1", - "webidl-conversions": "4.0.2", - "whatwg-encoding": "1.0.3", - "whatwg-url": "6.4.0", - "ws": "4.0.0", - "xml-name-validator": "3.0.0" + "pn": "^1.1.0", + "request": "^2.83.0", + "request-promise-native": "^1.0.5", + "sax": "^1.2.4", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.3.3", + "w3c-hr-time": "^1.0.1", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.3", + "whatwg-url": "^6.4.0", + "ws": "^4.0.0", + "xml-name-validator": "^3.0.0" }, "dependencies": { "parse5": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "integrity": "sha1-bXhlbj2o14tOwLkG98CO8d/j9gg=", "dev": true } } @@ -8998,7 +8968,7 @@ "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", "dev": true, "requires": { - "jsonify": "0.0.0" + "jsonify": "~0.0.0" } }, "json-stable-stringify-without-jsonify": { @@ -9064,7 +9034,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } }, "lazy-cache": { @@ -9079,7 +9049,7 @@ "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "requires": { - "invert-kv": "1.0.0" + "invert-kv": "^1.0.0" } }, "left-pad": { @@ -9100,8 +9070,8 @@ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "dev": true, "requires": { - "prelude-ls": "1.1.2", - "type-check": "0.3.2" + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" } }, "line-height": { @@ -9109,7 +9079,7 @@ "resolved": "https://registry.npmjs.org/line-height/-/line-height-0.3.1.tgz", "integrity": "sha1-SxIF7d4YKHKl76PI9iCzGHqcVMk=", "requires": { - "computed-style": "0.1.4" + "computed-style": "~0.1.3" } }, "listr": { @@ -9118,23 +9088,23 @@ "integrity": "sha1-ILsLowuuZg7oTMBQPfS+PVYjiH0=", "dev": true, "requires": { - "chalk": "1.1.3", - "cli-truncate": "0.2.1", - "figures": "1.7.0", - "indent-string": "2.1.0", - "is-observable": "0.2.0", - "is-promise": "2.1.0", - "is-stream": "1.1.0", - "listr-silent-renderer": "1.1.1", - "listr-update-renderer": "0.4.0", - "listr-verbose-renderer": "0.4.1", - "log-symbols": "1.0.2", - "log-update": "1.0.2", - "ora": "0.2.3", - "p-map": "1.2.0", - "rxjs": "5.5.8", - "stream-to-observable": "0.2.0", - "strip-ansi": "3.0.1" + "chalk": "^1.1.3", + "cli-truncate": "^0.2.1", + "figures": "^1.7.0", + "indent-string": "^2.1.0", + "is-observable": "^0.2.0", + "is-promise": "^2.1.0", + "is-stream": "^1.1.0", + "listr-silent-renderer": "^1.1.1", + "listr-update-renderer": "^0.4.0", + "listr-verbose-renderer": "^0.4.0", + "log-symbols": "^1.0.2", + "log-update": "^1.0.2", + "ora": "^0.2.3", + "p-map": "^1.1.1", + "rxjs": "^5.4.2", + "stream-to-observable": "^0.2.0", + "strip-ansi": "^3.0.1" }, "dependencies": { "figures": { @@ -9143,8 +9113,8 @@ "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", "dev": true, "requires": { - "escape-string-regexp": "1.0.5", - "object-assign": "4.1.1" + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" } }, "log-symbols": { @@ -9153,7 +9123,7 @@ "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", "dev": true, "requires": { - "chalk": "1.1.3" + "chalk": "^1.0.0" } } } @@ -9170,14 +9140,14 @@ "integrity": "sha1-NE2YDaLKLosUW6MFkI8yrj9MyKc=", "dev": true, "requires": { - "chalk": "1.1.3", - "cli-truncate": "0.2.1", - "elegant-spinner": "1.0.1", - "figures": "1.7.0", - "indent-string": "3.2.0", - "log-symbols": "1.0.2", - "log-update": "1.0.2", - "strip-ansi": "3.0.1" + "chalk": "^1.1.3", + "cli-truncate": "^0.2.1", + "elegant-spinner": "^1.0.1", + "figures": "^1.7.0", + "indent-string": "^3.0.0", + "log-symbols": "^1.0.2", + "log-update": "^1.0.2", + "strip-ansi": "^3.0.1" }, "dependencies": { "figures": { @@ -9186,8 +9156,8 @@ "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", "dev": true, "requires": { - "escape-string-regexp": "1.0.5", - "object-assign": "4.1.1" + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" } }, "indent-string": { @@ -9202,7 +9172,7 @@ "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", "dev": true, "requires": { - "chalk": "1.1.3" + "chalk": "^1.0.0" } } } @@ -9213,10 +9183,10 @@ "integrity": "sha1-ggb0z21S3cWCfl/RSYng6WWTOjU=", "dev": true, "requires": { - "chalk": "1.1.3", - "cli-cursor": "1.0.2", - "date-fns": "1.29.0", - "figures": "1.7.0" + "chalk": "^1.1.3", + "cli-cursor": "^1.0.2", + "date-fns": "^1.27.2", + "figures": "^1.7.0" }, "dependencies": { "cli-cursor": { @@ -9225,7 +9195,7 @@ "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", "dev": true, "requires": { - "restore-cursor": "1.0.1" + "restore-cursor": "^1.0.1" } }, "figures": { @@ -9234,8 +9204,8 @@ "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", "dev": true, "requires": { - "escape-string-regexp": "1.0.5", - "object-assign": "4.1.1" + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" } }, "onetime": { @@ -9250,23 +9220,12 @@ "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", "dev": true, "requires": { - "exit-hook": "1.1.1", - "onetime": "1.1.0" + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" } } } }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "strip-bom": "3.0.0" - } - }, "loader-runner": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", @@ -9279,9 +9238,9 @@ "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", "dev": true, "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1" + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0" } }, "locate-path": { @@ -9289,8 +9248,8 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "requires": { - "p-locate": "2.0.0", - "path-exists": "3.0.0" + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" }, "dependencies": { "path-exists": { @@ -9316,9 +9275,9 @@ "integrity": "sha1-2AJfdjOdKTQnZ9zIh85cuVpbUfE=", "dev": true, "requires": { - "lodash.isarray": "3.0.4", - "lodash.istypedarray": "3.0.6", - "lodash.keys": "3.1.2" + "lodash.isarray": "^3.0.0", + "lodash.istypedarray": "^3.0.0", + "lodash.keys": "^3.0.0" } }, "lodash._bindcallback": { @@ -9345,11 +9304,6 @@ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", "dev": true }, - "lodash.cond": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz", - "integrity": "sha1-9HGh2khr5g9quVXRcRVSPdHSVdU=" - }, "lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", @@ -9374,8 +9328,8 @@ "integrity": "sha1-HDXrO27wzR/1F0Pj6jz3/f/ay2Q=", "dev": true, "requires": { - "lodash._baseisequal": "3.0.7", - "lodash._bindcallback": "3.0.1" + "lodash._baseisequal": "^3.0.0", + "lodash._bindcallback": "^3.0.0" } }, "lodash.istypedarray": { @@ -9390,9 +9344,9 @@ "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", "dev": true, "requires": { - "lodash._getnative": "3.9.1", - "lodash.isarguments": "3.1.0", - "lodash.isarray": "3.0.4" + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" } }, "lodash.memoize": { @@ -9437,7 +9391,7 @@ "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", "dev": true, "requires": { - "chalk": "2.3.2" + "chalk": "^2.0.1" }, "dependencies": { "ansi-styles": { @@ -9446,7 +9400,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -9455,9 +9409,9 @@ "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.3.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "has-flag": { @@ -9472,7 +9426,7 @@ "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -9483,8 +9437,8 @@ "integrity": "sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE=", "dev": true, "requires": { - "ansi-escapes": "1.4.0", - "cli-cursor": "1.0.2" + "ansi-escapes": "^1.0.0", + "cli-cursor": "^1.0.2" }, "dependencies": { "ansi-escapes": { @@ -9499,7 +9453,7 @@ "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", "dev": true, "requires": { - "restore-cursor": "1.0.1" + "restore-cursor": "^1.0.1" } }, "onetime": { @@ -9514,8 +9468,8 @@ "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", "dev": true, "requires": { - "exit-hook": "1.1.1", - "onetime": "1.1.0" + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" } } } @@ -9531,7 +9485,7 @@ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", "requires": { - "js-tokens": "3.0.2" + "js-tokens": "^3.0.0" } }, "loud-rejection": { @@ -9540,8 +9494,8 @@ "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", "dev": true, "requires": { - "currently-unhandled": "0.4.1", - "signal-exit": "3.0.2" + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" } }, "lowercase-keys": { @@ -9555,7 +9509,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-3.2.0.tgz", "integrity": "sha1-cXibO39Tmb7IVl3aOKow0qCX7+4=", "requires": { - "pseudomap": "1.0.2" + "pseudomap": "^1.0.1" } }, "macaddress": { @@ -9570,7 +9524,7 @@ "integrity": "sha512-aNUAa4UMg/UougV25bbrU4ZaaKNjJ/3/xnvg/twpmKROPdKZPZ9wGgI0opdZzO8q/zUFawoUuixuOv33eZ61Iw==", "dev": true, "requires": { - "pify": "3.0.0" + "pify": "^3.0.0" }, "dependencies": { "pify": { @@ -9587,7 +9541,7 @@ "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", "dev": true, "requires": { - "tmpl": "1.0.4" + "tmpl": "1.0.x" } }, "map-cache": { @@ -9614,7 +9568,7 @@ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "dev": true, "requires": { - "object-visit": "1.0.1" + "object-visit": "^1.0.0" } }, "material-colors": { @@ -9634,8 +9588,8 @@ "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", "dev": true, "requires": { - "hash-base": "3.0.4", - "inherits": "2.0.3" + "hash-base": "^3.0.0", + "inherits": "^2.0.1" }, "dependencies": { "hash-base": { @@ -9644,8 +9598,8 @@ "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", "dev": true, "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" } } } @@ -9655,7 +9609,7 @@ "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", "requires": { - "mimic-fn": "1.2.0" + "mimic-fn": "^1.0.0" } }, "mem-fs": { @@ -9664,9 +9618,9 @@ "integrity": "sha1-uK6NLj/Lb10/kWXBLUVRoGXZicw=", "dev": true, "requires": { - "through2": "2.0.3", - "vinyl": "1.2.0", - "vinyl-file": "2.0.0" + "through2": "^2.0.0", + "vinyl": "^1.1.0", + "vinyl-file": "^2.0.0" } }, "mem-fs-editor": { @@ -9675,16 +9629,16 @@ "integrity": "sha1-3Qpuryu4prN3QAZ6pUnrUwEFr58=", "dev": true, "requires": { - "commondir": "1.0.1", - "deep-extend": "0.4.2", - "ejs": "2.5.8", - "glob": "7.1.2", - "globby": "6.1.0", - "mkdirp": "0.5.1", - "multimatch": "2.1.0", - "rimraf": "2.6.2", - "through2": "2.0.3", - "vinyl": "2.1.0" + "commondir": "^1.0.1", + "deep-extend": "^0.4.0", + "ejs": "^2.3.1", + "glob": "^7.0.3", + "globby": "^6.1.0", + "mkdirp": "^0.5.0", + "multimatch": "^2.0.0", + "rimraf": "^2.2.8", + "through2": "^2.0.0", + "vinyl": "^2.0.1" }, "dependencies": { "clone": { @@ -9705,11 +9659,11 @@ "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "dev": true, "requires": { - "array-union": "1.0.2", - "glob": "7.1.2", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, "replace-ext": { @@ -9724,12 +9678,12 @@ "integrity": "sha1-Ah+cLPlR1rk5lDyJ617lrdT9kkw=", "dev": true, "requires": { - "clone": "2.1.2", - "clone-buffer": "1.0.0", - "clone-stats": "1.0.0", - "cloneable-readable": "1.1.2", - "remove-trailing-separator": "1.1.0", - "replace-ext": "1.0.0" + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" } } } @@ -9745,8 +9699,8 @@ "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", "dev": true, "requires": { - "errno": "0.1.7", - "readable-stream": "2.3.3" + "errno": "^0.1.3", + "readable-stream": "^2.0.1" } }, "meow": { @@ -9755,16 +9709,16 @@ "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, "requires": { - "camelcase-keys": "2.1.0", - "decamelize": "1.2.0", - "loud-rejection": "1.6.0", - "map-obj": "1.0.1", - "minimist": "1.2.0", - "normalize-package-data": "2.4.0", - "object-assign": "4.1.1", - "read-pkg-up": "1.0.1", - "redent": "1.0.0", - "trim-newlines": "1.0.0" + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" }, "dependencies": { "load-json-file": { @@ -9773,11 +9727,11 @@ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "strip-bom": "2.0.0" + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" } }, "minimist": { @@ -9792,9 +9746,9 @@ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, "read-pkg": { @@ -9803,9 +9757,9 @@ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, "requires": { - "load-json-file": "1.1.0", - "normalize-package-data": "2.4.0", - "path-type": "1.1.0" + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" } }, "read-pkg-up": { @@ -9814,8 +9768,8 @@ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, "requires": { - "find-up": "1.1.2", - "read-pkg": "1.1.0" + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" } }, "strip-bom": { @@ -9824,7 +9778,7 @@ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "0.2.1" + "is-utf8": "^0.2.0" } } } @@ -9840,7 +9794,7 @@ "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", "dev": true, "requires": { - "readable-stream": "2.3.3" + "readable-stream": "^2.0.1" } }, "micromatch": { @@ -9849,19 +9803,19 @@ "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "dev": true, "requires": { - "arr-diff": "2.0.0", - "array-unique": "0.2.1", - "braces": "1.8.5", - "expand-brackets": "0.1.5", - "extglob": "0.3.2", - "filename-regex": "2.0.1", - "is-extglob": "1.0.0", - "is-glob": "2.0.1", - "kind-of": "3.2.2", - "normalize-path": "2.1.1", - "object.omit": "2.0.1", - "parse-glob": "3.0.4", - "regex-cache": "0.4.4" + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" } }, "miller-rabin": { @@ -9870,8 +9824,8 @@ "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", "dev": true, "requires": { - "bn.js": "4.11.8", - "brorand": "1.1.0" + "bn.js": "^4.0.0", + "brorand": "^1.0.1" } }, "mime": { @@ -9892,7 +9846,7 @@ "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", "dev": true, "requires": { - "mime-db": "1.30.0" + "mime-db": "~1.30.0" } }, "mimic-fn": { @@ -9923,7 +9877,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "requires": { - "brace-expansion": "1.1.8" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -9937,16 +9891,16 @@ "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", "dev": true, "requires": { - "concat-stream": "1.6.0", - "duplexify": "3.5.4", - "end-of-stream": "1.4.1", - "flush-write-stream": "1.0.3", - "from2": "2.3.0", - "parallel-transform": "1.1.0", - "pump": "2.0.1", - "pumpify": "1.4.0", - "stream-each": "1.2.2", - "through2": "2.0.3" + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^2.0.1", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" } }, "mixin-deep": { @@ -9955,8 +9909,8 @@ "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", "dev": true, "requires": { - "for-in": "1.0.2", - "is-extendable": "1.0.1" + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" }, "dependencies": { "is-extendable": { @@ -9965,7 +9919,7 @@ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "is-plain-object": "2.0.4" + "is-plain-object": "^2.0.4" } } } @@ -9976,8 +9930,8 @@ "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", "dev": true, "requires": { - "for-in": "0.1.8", - "is-extendable": "0.1.1" + "for-in": "^0.1.3", + "is-extendable": "^0.1.1" }, "dependencies": { "for-in": { @@ -10006,7 +9960,7 @@ "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.13.tgz", "integrity": "sha1-mc5cfYJyYusPH3AgRBd/YHRde5A=", "requires": { - "moment": "2.21.0" + "moment": ">= 2.9.0" } }, "mousetrap": { @@ -10020,18 +9974,19 @@ "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", "dev": true, "requires": { - "aproba": "1.2.0", - "copy-concurrently": "1.0.5", - "fs-write-stream-atomic": "1.0.10", - "mkdirp": "0.5.1", - "rimraf": "2.6.2", - "run-queue": "1.0.3" + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" } }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true }, "multimatch": { "version": "2.1.0", @@ -10039,10 +9994,10 @@ "integrity": "sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis=", "dev": true, "requires": { - "array-differ": "1.0.0", - "array-union": "1.0.2", - "arrify": "1.0.1", - "minimatch": "3.0.4" + "array-differ": "^1.0.0", + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "minimatch": "^3.0.0" } }, "mute-stream": { @@ -10063,18 +10018,18 @@ "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", "dev": true, "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "fragment-cache": "0.2.1", - "is-odd": "2.0.0", - "is-windows": "1.0.2", - "kind-of": "6.0.2", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-odd": "^2.0.0", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "arr-diff": { @@ -10115,10 +10070,10 @@ "integrity": "sha512-1azpqq1JvHKZNPEixS1jNEXf4kDilhFtr8AIZIGjP8N0TcAcUhKgi354niI5pM4JoOsMQ+H6vzCYWQa95LQjcw==", "dev": true, "requires": { - "nomnom": "1.6.2", - "railroad-diagrams": "1.0.0", + "nomnom": "~1.6.2", + "railroad-diagrams": "^1.0.0", "randexp": "0.4.6", - "semver": "5.5.0" + "semver": "^5.4.1" }, "dependencies": { "semver": { @@ -10152,8 +10107,8 @@ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", "requires": { - "encoding": "0.1.12", - "is-stream": "1.1.0" + "encoding": "^0.1.11", + "is-stream": "^1.0.1" } }, "node-gyp": { @@ -10162,19 +10117,19 @@ "integrity": "sha1-m/vlRWIoYoSDjnUOrAUpWFP6HGA=", "dev": true, "requires": { - "fstream": "1.0.11", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "nopt": "3.0.6", - "npmlog": "4.1.2", - "osenv": "0.1.4", - "request": "2.83.0", - "rimraf": "2.6.2", - "semver": "5.3.0", - "tar": "2.2.1", - "which": "1.3.0" + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "minimatch": "^3.0.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "2", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" } }, "node-int64": { @@ -10189,28 +10144,28 @@ "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==", "dev": true, "requires": { - "assert": "1.4.1", - "browserify-zlib": "0.2.0", - "buffer": "4.9.1", - "console-browserify": "1.1.0", - "constants-browserify": "1.0.0", - "crypto-browserify": "3.12.0", - "domain-browser": "1.2.0", - "events": "1.1.1", - "https-browserify": "1.0.0", - "os-browserify": "0.3.0", + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^1.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", "path-browserify": "0.0.0", - "process": "0.11.10", - "punycode": "1.4.1", - "querystring-es3": "0.2.1", - "readable-stream": "2.3.3", - "stream-browserify": "2.0.1", - "stream-http": "2.8.1", - "string_decoder": "1.0.3", - "timers-browserify": "2.0.6", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", "tty-browserify": "0.0.0", - "url": "0.11.0", - "util": "0.10.3", + "url": "^0.11.0", + "util": "^0.10.3", "vm-browserify": "0.0.4" } }, @@ -10220,10 +10175,10 @@ "integrity": "sha512-MIBs+AAd6dJ2SklbbE8RUDRlIVhU8MaNLh1A9SUZDUHPiZkWLFde6UNwG41yQHZEToHgJMXqyVZ9UcS/ReOVTg==", "dev": true, "requires": { - "growly": "1.3.0", - "semver": "5.5.0", - "shellwords": "0.1.1", - "which": "1.3.0" + "growly": "^1.3.0", + "semver": "^5.4.1", + "shellwords": "^0.1.1", + "which": "^1.3.0" }, "dependencies": { "semver": { @@ -10240,25 +10195,25 @@ "integrity": "sha512-CaV+wLqZ7//Jdom5aUFCpGNoECd7BbNhjuwdsX/LkXBrHl8eb1Wjw4HvWqcFvhr5KuNgAk8i/myf/MQ1YYeroA==", "dev": true, "requires": { - "async-foreach": "0.1.3", - "chalk": "1.1.3", - "cross-spawn": "3.0.1", - "gaze": "1.1.2", - "get-stdin": "4.0.1", - "glob": "7.1.2", - "in-publish": "2.0.0", - "lodash.assign": "4.2.0", - "lodash.clonedeep": "4.5.0", - "lodash.mergewith": "4.6.1", - "meow": "3.7.0", - "mkdirp": "0.5.1", - "nan": "2.8.0", - "node-gyp": "3.6.2", - "npmlog": "4.1.2", - "request": "2.79.0", - "sass-graph": "2.2.4", - "stdout-stream": "1.4.0", - "true-case-path": "1.0.2" + "async-foreach": "^0.1.3", + "chalk": "^1.1.1", + "cross-spawn": "^3.0.0", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "in-publish": "^2.0.0", + "lodash.assign": "^4.2.0", + "lodash.clonedeep": "^4.3.2", + "lodash.mergewith": "^4.6.0", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.3.2", + "node-gyp": "^3.3.1", + "npmlog": "^4.0.0", + "request": "~2.79.0", + "sass-graph": "^2.2.4", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" }, "dependencies": { "assert-plus": { @@ -10279,7 +10234,7 @@ "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", "dev": true, "requires": { - "hoek": "2.16.3" + "hoek": "2.x.x" } }, "caseless": { @@ -10294,8 +10249,8 @@ "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", "dev": true, "requires": { - "lru-cache": "4.1.1", - "which": "1.3.0" + "lru-cache": "^4.0.1", + "which": "^1.2.9" } }, "cryptiles": { @@ -10304,7 +10259,7 @@ "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", "dev": true, "requires": { - "boom": "2.10.1" + "boom": "2.x.x" } }, "form-data": { @@ -10313,9 +10268,9 @@ "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", "dev": true, "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.17" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.5", + "mime-types": "^2.1.12" } }, "har-validator": { @@ -10324,10 +10279,10 @@ "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", "dev": true, "requires": { - "chalk": "1.1.3", - "commander": "2.14.1", - "is-my-json-valid": "2.17.1", - "pinkie-promise": "2.0.1" + "chalk": "^1.1.1", + "commander": "^2.9.0", + "is-my-json-valid": "^2.12.4", + "pinkie-promise": "^2.0.0" } }, "hawk": { @@ -10336,10 +10291,10 @@ "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", "dev": true, "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" + "boom": "2.x.x", + "cryptiles": "2.x.x", + "hoek": "2.x.x", + "sntp": "1.x.x" } }, "hoek": { @@ -10354,9 +10309,9 @@ "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", "dev": true, "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.1", - "sshpk": "1.13.1" + "assert-plus": "^0.2.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" } }, "lru-cache": { @@ -10365,8 +10320,8 @@ "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", "dev": true, "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" } }, "qs": { @@ -10381,26 +10336,26 @@ "integrity": "sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4=", "dev": true, "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.11.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "2.0.6", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "oauth-sign": "0.8.2", - "qs": "6.3.2", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.4.3", - "uuid": "3.1.0" + "aws-sign2": "~0.6.0", + "aws4": "^1.2.1", + "caseless": "~0.11.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.0", + "forever-agent": "~0.6.1", + "form-data": "~2.1.1", + "har-validator": "~2.0.6", + "hawk": "~3.1.3", + "http-signature": "~1.1.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.7", + "oauth-sign": "~0.8.1", + "qs": "~6.3.0", + "stringstream": "~0.0.4", + "tough-cookie": "~2.3.0", + "tunnel-agent": "~0.4.1", + "uuid": "^3.0.0" } }, "sntp": { @@ -10409,7 +10364,7 @@ "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", "dev": true, "requires": { - "hoek": "2.16.3" + "hoek": "2.x.x" } }, "tunnel-agent": { @@ -10426,8 +10381,8 @@ "integrity": "sha1-hKZqJgF0QI/Ft3oY+IjszET7aXE=", "dev": true, "requires": { - "colors": "0.5.1", - "underscore": "1.4.4" + "colors": "0.5.x", + "underscore": "~1.4.4" } }, "nopt": { @@ -10435,18 +10390,19 @@ "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", "requires": { - "abbrev": "1.1.1" + "abbrev": "1" } }, "normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "dev": true, "requires": { - "hosted-git-info": "2.5.0", - "is-builtin-module": "1.0.0", - "semver": "5.3.0", - "validate-npm-package-license": "3.0.1" + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, "normalize-path": { @@ -10455,7 +10411,7 @@ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { - "remove-trailing-separator": "1.1.0" + "remove-trailing-separator": "^1.0.1" } }, "normalize-range": { @@ -10470,10 +10426,10 @@ "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", "dev": true, "requires": { - "object-assign": "4.1.1", - "prepend-http": "1.0.4", - "query-string": "4.3.4", - "sort-keys": "1.1.2" + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" } }, "npm-run-path": { @@ -10481,7 +10437,7 @@ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "requires": { - "path-key": "2.0.1" + "path-key": "^2.0.0" } }, "npmlog": { @@ -10490,10 +10446,10 @@ "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" } }, "nth-check": { @@ -10502,7 +10458,7 @@ "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=", "dev": true, "requires": { - "boolbase": "1.0.0" + "boolbase": "~1.0.0" } }, "num2fraction": { @@ -10539,9 +10495,9 @@ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "dev": true, "requires": { - "copy-descriptor": "0.1.1", - "define-property": "0.2.5", - "kind-of": "3.2.2" + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" }, "dependencies": { "define-property": { @@ -10550,7 +10506,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "is-accessor-descriptor": { @@ -10559,7 +10515,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" } }, "is-data-descriptor": { @@ -10568,7 +10524,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" } }, "is-descriptor": { @@ -10577,9 +10533,9 @@ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" }, "dependencies": { "kind-of": { @@ -10601,7 +10557,7 @@ "object-inspect": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.5.0.tgz", - "integrity": "sha512-UmOFbHbwvv+XHj7BerrhVq+knjceBdkvU5AriwLMvhv2qi+e7DJzxfBeFpILEjVzCp+xA+W/pIf06RGPWlZNfw==", + "integrity": "sha1-nYdsEeQPSFx5IVZwKBt2dIj5v+M=", "dev": true }, "object-is": { @@ -10622,7 +10578,7 @@ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.0" }, "dependencies": { "isobject": { @@ -10639,10 +10595,10 @@ "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", "dev": true, "requires": { - "define-properties": "1.1.2", - "function-bind": "1.1.1", - "has-symbols": "1.0.0", - "object-keys": "1.0.11" + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" } }, "object.entries": { @@ -10651,10 +10607,10 @@ "integrity": "sha1-G/mk3SKI9bM/Opk9JXZh8F0WGl8=", "dev": true, "requires": { - "define-properties": "1.1.2", - "es-abstract": "1.10.0", - "function-bind": "1.1.1", - "has": "1.0.1" + "define-properties": "^1.1.2", + "es-abstract": "^1.6.1", + "function-bind": "^1.1.0", + "has": "^1.0.1" } }, "object.getownpropertydescriptors": { @@ -10663,8 +10619,8 @@ "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", "dev": true, "requires": { - "define-properties": "1.1.2", - "es-abstract": "1.10.0" + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" } }, "object.omit": { @@ -10673,8 +10629,8 @@ "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", "dev": true, "requires": { - "for-own": "0.1.5", - "is-extendable": "0.1.1" + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" } }, "object.pick": { @@ -10683,7 +10639,7 @@ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.1" }, "dependencies": { "isobject": { @@ -10700,10 +10656,10 @@ "integrity": "sha1-5STaCbT2b/Bd9FdUbscqyZ8TBpo=", "dev": true, "requires": { - "define-properties": "1.1.2", - "es-abstract": "1.10.0", - "function-bind": "1.1.1", - "has": "1.0.1" + "define-properties": "^1.1.2", + "es-abstract": "^1.6.1", + "function-bind": "^1.1.0", + "has": "^1.0.1" } }, "once": { @@ -10712,7 +10668,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "onetime": { @@ -10721,7 +10677,7 @@ "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", "dev": true, "requires": { - "mimic-fn": "1.2.0" + "mimic-fn": "^1.0.0" } }, "optimist": { @@ -10730,8 +10686,8 @@ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, "requires": { - "minimist": "0.0.8", - "wordwrap": "0.0.3" + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" } }, "optionator": { @@ -10740,12 +10696,12 @@ "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", "dev": true, "requires": { - "deep-is": "0.1.3", - "fast-levenshtein": "2.0.6", - "levn": "0.3.0", - "prelude-ls": "1.1.2", - "type-check": "0.3.2", - "wordwrap": "1.0.0" + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" }, "dependencies": { "wordwrap": { @@ -10762,10 +10718,10 @@ "integrity": "sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q=", "dev": true, "requires": { - "chalk": "1.1.3", - "cli-cursor": "1.0.2", - "cli-spinners": "0.1.2", - "object-assign": "4.1.1" + "chalk": "^1.1.1", + "cli-cursor": "^1.0.2", + "cli-spinners": "^0.1.2", + "object-assign": "^4.0.1" }, "dependencies": { "cli-cursor": { @@ -10774,7 +10730,7 @@ "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", "dev": true, "requires": { - "restore-cursor": "1.0.1" + "restore-cursor": "^1.0.1" } }, "onetime": { @@ -10789,8 +10745,8 @@ "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", "dev": true, "requires": { - "exit-hook": "1.1.1", - "onetime": "1.1.0" + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" } } } @@ -10812,9 +10768,9 @@ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", "requires": { - "execa": "0.7.0", - "lcid": "1.0.0", - "mem": "1.1.0" + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" } }, "os-tmpdir": { @@ -10829,8 +10785,8 @@ "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", "dev": true, "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" } }, "p-cancelable": { @@ -10845,7 +10801,7 @@ "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", "dev": true, "requires": { - "p-reduce": "1.0.0" + "p-reduce": "^1.0.0" } }, "p-finally": { @@ -10870,7 +10826,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", "requires": { - "p-try": "1.0.0" + "p-try": "^1.0.0" } }, "p-locate": { @@ -10878,7 +10834,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "requires": { - "p-limit": "1.2.0" + "p-limit": "^1.1.0" } }, "p-map": { @@ -10899,7 +10855,7 @@ "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==", "dev": true, "requires": { - "p-finally": "1.0.0" + "p-finally": "^1.0.0" } }, "p-try": { @@ -10919,9 +10875,9 @@ "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", "dev": true, "requires": { - "cyclist": "0.2.2", - "inherits": "2.0.3", - "readable-stream": "2.3.3" + "cyclist": "~0.2.2", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" } }, "parse-asn1": { @@ -10930,11 +10886,11 @@ "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=", "dev": true, "requires": { - "asn1.js": "4.10.1", - "browserify-aes": "1.1.1", - "create-hash": "1.1.3", - "evp_bytestokey": "1.0.3", - "pbkdf2": "3.0.14" + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3" } }, "parse-glob": { @@ -10943,18 +10899,19 @@ "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", "dev": true, "requires": { - "glob-base": "0.3.0", - "is-dotfile": "1.0.3", - "is-extglob": "1.0.0", - "is-glob": "2.0.1" + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" } }, "parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, "requires": { - "error-ex": "1.3.1" + "error-ex": "^1.2.0" } }, "parse-passwd": { @@ -10969,7 +10926,7 @@ "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", "dev": true, "requires": { - "@types/node": "9.4.6" + "@types/node": "*" } }, "pascalcase": { @@ -10994,8 +10951,9 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, "requires": { - "pinkie-promise": "2.0.1" + "pinkie-promise": "^2.0.0" } }, "path-is-absolute": { @@ -11020,25 +10978,17 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=" }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "requires": { - "pify": "2.3.0" - } - }, "pbkdf2": { "version": "3.0.14", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.14.tgz", "integrity": "sha512-gjsZW9O34fm0R7PaLHRJmLLVfSoesxztjPjE9o6R+qtVJij90ltg1joIovN9GKrRW3t1PzhDDG3UMEMFfZ+1wA==", "dev": true, "requires": { - "create-hash": "1.1.3", - "create-hmac": "1.1.6", - "ripemd160": "2.0.1", - "safe-buffer": "5.1.1", - "sha.js": "2.4.11" + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" } }, "pegjs": { @@ -11053,7 +11003,7 @@ "integrity": "sha512-ViH8WwUkc/N8H59zuarORrgCi7uxn+gDIq+Ydriw1GFJi/oUg2xvhsgDDujO6dAxRsxXMgqWESx6TKYIqHorqA==", "dev": true, "requires": { - "loader-utils": "0.2.17" + "loader-utils": "^0.2.5" }, "dependencies": { "loader-utils": { @@ -11062,10 +11012,10 @@ "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", "dev": true, "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1", - "object-assign": "4.1.1" + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" } } } @@ -11091,27 +11041,22 @@ "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true }, "pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true }, "pinkie-promise": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, "requires": { - "pinkie": "2.0.4" - } - }, - "pkg-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", - "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", - "requires": { - "find-up": "1.1.2" + "pinkie": "^2.0.0" } }, "pluralize": { @@ -11127,9 +11072,9 @@ "dev": true }, "popper.js": { - "version": "1.12.9", - "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.12.9.tgz", - "integrity": "sha1-DfvC3/lsRRuzMu3Pz6r1ZtMx1bM=" + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.3.tgz", + "integrity": "sha1-FDj5jQRqz3tNeM1QK/QYrGTU8JU=" }, "posix-character-classes": { "version": "0.1.1", @@ -11143,10 +11088,10 @@ "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "dev": true, "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" }, "dependencies": { "has-flag": { @@ -11161,7 +11106,7 @@ "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "1.0.0" + "has-flag": "^1.0.0" } } } @@ -11172,9 +11117,9 @@ "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", "dev": true, "requires": { - "postcss": "5.2.18", - "postcss-message-helpers": "2.0.0", - "reduce-css-calc": "1.3.0" + "postcss": "^5.0.2", + "postcss-message-helpers": "^2.0.0", + "reduce-css-calc": "^1.2.6" } }, "postcss-colormin": { @@ -11183,9 +11128,9 @@ "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", "dev": true, "requires": { - "colormin": "1.1.2", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" + "colormin": "^1.0.5", + "postcss": "^5.0.13", + "postcss-value-parser": "^3.2.3" } }, "postcss-convert-values": { @@ -11194,8 +11139,8 @@ "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", "dev": true, "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" + "postcss": "^5.0.11", + "postcss-value-parser": "^3.1.2" } }, "postcss-discard-comments": { @@ -11204,7 +11149,7 @@ "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", "dev": true, "requires": { - "postcss": "5.2.18" + "postcss": "^5.0.14" } }, "postcss-discard-duplicates": { @@ -11213,7 +11158,7 @@ "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", "dev": true, "requires": { - "postcss": "5.2.18" + "postcss": "^5.0.4" } }, "postcss-discard-empty": { @@ -11222,7 +11167,7 @@ "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", "dev": true, "requires": { - "postcss": "5.2.18" + "postcss": "^5.0.14" } }, "postcss-discard-overridden": { @@ -11231,7 +11176,7 @@ "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", "dev": true, "requires": { - "postcss": "5.2.18" + "postcss": "^5.0.16" } }, "postcss-discard-unused": { @@ -11240,8 +11185,8 @@ "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", "dev": true, "requires": { - "postcss": "5.2.18", - "uniqs": "2.0.0" + "postcss": "^5.0.14", + "uniqs": "^2.0.0" } }, "postcss-filter-plugins": { @@ -11250,8 +11195,8 @@ "integrity": "sha1-bYWGJTTXNaxCDkqFgG4fXUKG2Ew=", "dev": true, "requires": { - "postcss": "5.2.18", - "uniqid": "4.1.1" + "postcss": "^5.0.4", + "uniqid": "^4.0.0" } }, "postcss-load-config": { @@ -11260,10 +11205,10 @@ "integrity": "sha1-U56a/J3chiASHr+djDZz4M5Q0oo=", "dev": true, "requires": { - "cosmiconfig": "2.2.2", - "object-assign": "4.1.1", - "postcss-load-options": "1.2.0", - "postcss-load-plugins": "2.3.0" + "cosmiconfig": "^2.1.0", + "object-assign": "^4.1.0", + "postcss-load-options": "^1.2.0", + "postcss-load-plugins": "^2.3.0" } }, "postcss-load-options": { @@ -11272,8 +11217,8 @@ "integrity": "sha1-sJixVZ3awt8EvAuzdfmaXP4rbYw=", "dev": true, "requires": { - "cosmiconfig": "2.2.2", - "object-assign": "4.1.1" + "cosmiconfig": "^2.1.0", + "object-assign": "^4.1.0" } }, "postcss-load-plugins": { @@ -11282,8 +11227,8 @@ "integrity": "sha1-dFdoEWWZrKLwCfrUJrABdQSdjZI=", "dev": true, "requires": { - "cosmiconfig": "2.2.2", - "object-assign": "4.1.1" + "cosmiconfig": "^2.1.1", + "object-assign": "^4.1.0" } }, "postcss-loader": { @@ -11292,10 +11237,10 @@ "integrity": "sha512-RuBcNE8rjCkIB0IsbmkGFRmQJTeQJfCI88E0VTarPNTvaNSv9OFv1DvTwgtAN/qlzyiELsmmmtX/tEzKp/cdug==", "dev": true, "requires": { - "loader-utils": "1.1.0", - "postcss": "6.0.21", - "postcss-load-config": "1.2.0", - "schema-utils": "0.4.5" + "loader-utils": "^1.1.0", + "postcss": "^6.0.0", + "postcss-load-config": "^1.2.0", + "schema-utils": "^0.4.0" }, "dependencies": { "ansi-styles": { @@ -11304,7 +11249,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -11313,9 +11258,9 @@ "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.3.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "has-flag": { @@ -11330,9 +11275,9 @@ "integrity": "sha512-y/bKfbQz2Nn/QBC08bwvYUxEFOVGfPIUOTsJ2CK5inzlXW9SdYR1x4pEsG9blRAF/PX+wRNdOah+gx/hv4q7dw==", "dev": true, "requires": { - "chalk": "2.3.2", - "source-map": "0.6.1", - "supports-color": "5.3.0" + "chalk": "^2.3.2", + "source-map": "^0.6.1", + "supports-color": "^5.3.0" } }, "source-map": { @@ -11347,7 +11292,7 @@ "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -11358,9 +11303,9 @@ "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", "dev": true, "requires": { - "has": "1.0.1", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" + "has": "^1.0.1", + "postcss": "^5.0.10", + "postcss-value-parser": "^3.1.1" } }, "postcss-merge-longhand": { @@ -11369,7 +11314,7 @@ "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", "dev": true, "requires": { - "postcss": "5.2.18" + "postcss": "^5.0.4" } }, "postcss-merge-rules": { @@ -11378,11 +11323,11 @@ "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", "dev": true, "requires": { - "browserslist": "1.7.7", - "caniuse-api": "1.6.1", - "postcss": "5.2.18", - "postcss-selector-parser": "2.2.3", - "vendors": "1.0.1" + "browserslist": "^1.5.2", + "caniuse-api": "^1.5.2", + "postcss": "^5.0.4", + "postcss-selector-parser": "^2.2.2", + "vendors": "^1.0.0" }, "dependencies": { "browserslist": { @@ -11391,8 +11336,8 @@ "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "dev": true, "requires": { - "caniuse-db": "1.0.30000804", - "electron-to-chromium": "1.3.33" + "caniuse-db": "^1.0.30000639", + "electron-to-chromium": "^1.2.7" } } } @@ -11409,9 +11354,9 @@ "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", "dev": true, "requires": { - "object-assign": "4.1.1", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" + "object-assign": "^4.0.1", + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.2" } }, "postcss-minify-gradients": { @@ -11420,8 +11365,8 @@ "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", "dev": true, "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" + "postcss": "^5.0.12", + "postcss-value-parser": "^3.3.0" } }, "postcss-minify-params": { @@ -11430,10 +11375,10 @@ "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", "dev": true, "requires": { - "alphanum-sort": "1.0.2", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0", - "uniqs": "2.0.0" + "alphanum-sort": "^1.0.1", + "postcss": "^5.0.2", + "postcss-value-parser": "^3.0.2", + "uniqs": "^2.0.0" } }, "postcss-minify-selectors": { @@ -11442,10 +11387,10 @@ "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", "dev": true, "requires": { - "alphanum-sort": "1.0.2", - "has": "1.0.1", - "postcss": "5.2.18", - "postcss-selector-parser": "2.2.3" + "alphanum-sort": "^1.0.2", + "has": "^1.0.1", + "postcss": "^5.0.14", + "postcss-selector-parser": "^2.0.0" } }, "postcss-normalize-charset": { @@ -11454,7 +11399,7 @@ "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", "dev": true, "requires": { - "postcss": "5.2.18" + "postcss": "^5.0.5" } }, "postcss-normalize-url": { @@ -11463,10 +11408,10 @@ "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", "dev": true, "requires": { - "is-absolute-url": "2.1.0", - "normalize-url": "1.9.1", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" + "is-absolute-url": "^2.0.0", + "normalize-url": "^1.4.0", + "postcss": "^5.0.14", + "postcss-value-parser": "^3.2.3" } }, "postcss-ordered-values": { @@ -11475,8 +11420,8 @@ "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", "dev": true, "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.1" } }, "postcss-reduce-idents": { @@ -11485,8 +11430,8 @@ "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", "dev": true, "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.2" } }, "postcss-reduce-initial": { @@ -11495,7 +11440,7 @@ "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", "dev": true, "requires": { - "postcss": "5.2.18" + "postcss": "^5.0.4" } }, "postcss-reduce-transforms": { @@ -11504,9 +11449,9 @@ "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", "dev": true, "requires": { - "has": "1.0.1", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" + "has": "^1.0.1", + "postcss": "^5.0.8", + "postcss-value-parser": "^3.0.1" } }, "postcss-selector-parser": { @@ -11515,9 +11460,9 @@ "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=", "dev": true, "requires": { - "flatten": "1.0.2", - "indexes-of": "1.0.1", - "uniq": "1.0.1" + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" } }, "postcss-svgo": { @@ -11526,10 +11471,10 @@ "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", "dev": true, "requires": { - "is-svg": "2.1.0", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0", - "svgo": "0.7.2" + "is-svg": "^2.0.0", + "postcss": "^5.0.14", + "postcss-value-parser": "^3.2.3", + "svgo": "^0.7.0" } }, "postcss-unique-selectors": { @@ -11538,9 +11483,9 @@ "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", "dev": true, "requires": { - "alphanum-sort": "1.0.2", - "postcss": "5.2.18", - "uniqs": "2.0.0" + "alphanum-sort": "^1.0.1", + "postcss": "^5.0.4", + "uniqs": "^2.0.0" } }, "postcss-value-parser": { @@ -11555,9 +11500,9 @@ "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", "dev": true, "requires": { - "has": "1.0.1", - "postcss": "5.2.18", - "uniqs": "2.0.0" + "has": "^1.0.1", + "postcss": "^5.0.4", + "uniqs": "^2.0.0" } }, "prelude-ls": { @@ -11596,8 +11541,8 @@ "integrity": "sha512-pvCxP2iODIIk9adXlo4S3GRj0BrJiil68kByAa1PrgG97c1tClh9dLMgp3Z6cHFZrclaABt0UH8PIhwHuFLqYA==", "dev": true, "requires": { - "ansi-regex": "3.0.0", - "ansi-styles": "3.2.0" + "ansi-regex": "^3.0.0", + "ansi-styles": "^3.2.0" }, "dependencies": { "ansi-regex": { @@ -11612,7 +11557,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } } } @@ -11645,7 +11590,7 @@ "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", "requires": { - "asap": "2.0.6" + "asap": "~2.0.3" } }, "promise-inflight": { @@ -11659,8 +11604,8 @@ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz", "integrity": "sha1-J5ffwxJhguOpXj37suiT3ddFYVQ=", "requires": { - "fbjs": "0.8.16", - "loose-envify": "1.3.1" + "fbjs": "^0.8.9", + "loose-envify": "^1.3.1" } }, "proto-list": { @@ -11691,11 +11636,11 @@ "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=", "dev": true, "requires": { - "bn.js": "4.11.8", - "browserify-rsa": "4.0.1", - "create-hash": "1.1.3", - "parse-asn1": "5.1.0", - "randombytes": "2.0.6" + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1" } }, "pump": { @@ -11704,19 +11649,19 @@ "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", "dev": true, "requires": { - "end-of-stream": "1.4.1", - "once": "1.4.0" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, "pumpify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.4.0.tgz", - "integrity": "sha512-2kmNR9ry+Pf45opRVirpNuIFotsxUGLaYqxIwuR77AYrYRMuFCz9eryHBS52L360O+NcR383CL4QYlMKPq4zYA==", + "integrity": "sha1-gLfF334kFT0D8OesigWl0Gi9B/s=", "dev": true, "requires": { - "duplexify": "3.5.4", - "inherits": "2.0.3", - "pump": "2.0.1" + "duplexify": "^3.5.3", + "inherits": "^2.0.3", + "pump": "^2.0.0" } }, "punycode": { @@ -11731,14 +11676,14 @@ "integrity": "sha512-4sY/6mB7+kNPGAzPGKq65tH0VG3ohUEkXHuOReB9K/tw3m1TqifYmxnMR/uDeci/UPwyk5K1gWYh8rw0U0Zscw==", "dev": true, "requires": { - "debug": "2.6.9", - "extract-zip": "1.6.6", - "https-proxy-agent": "2.2.0", - "mime": "1.6.0", - "progress": "2.0.0", - "proxy-from-env": "1.0.0", - "rimraf": "2.6.2", - "ws": "3.3.3" + "debug": "^2.6.8", + "extract-zip": "^1.6.5", + "https-proxy-agent": "^2.1.0", + "mime": "^1.3.4", + "progress": "^2.0.0", + "proxy-from-env": "^1.0.0", + "rimraf": "^2.6.1", + "ws": "^3.0.0" }, "dependencies": { "ws": { @@ -11747,9 +11692,9 @@ "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", "dev": true, "requires": { - "async-limiter": "1.0.0", - "safe-buffer": "5.1.1", - "ultron": "1.1.1" + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" } } } @@ -11763,7 +11708,7 @@ "qs": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", + "integrity": "sha1-NJzfbu+J7EXBLX1es/wMhwNDptg=", "dev": true }, "query-string": { @@ -11772,8 +11717,8 @@ "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", "dev": true, "requires": { - "object-assign": "4.1.1", - "strict-uri-encode": "1.1.0" + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" } }, "querystring": { @@ -11799,7 +11744,7 @@ "integrity": "sha512-pDP/NMRAXoTfrhCfyfSEwJAKLaxBU9eApMeBPB1TkDouZmvPerIClV8lTAd+uF8ZiTaVl69e1FCxQrAd/VTjGw==", "dev": true, "requires": { - "performance-now": "2.1.0" + "performance-now": "^2.1.0" } }, "railroad-diagrams": { @@ -11815,7 +11760,7 @@ "dev": true, "requires": { "discontinuous-range": "1.0.0", - "ret": "0.1.15" + "ret": "~0.1.10" } }, "randomatic": { @@ -11824,8 +11769,8 @@ "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", "dev": true, "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" + "is-number": "^3.0.0", + "kind-of": "^4.0.0" }, "dependencies": { "is-number": { @@ -11834,7 +11779,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -11843,7 +11788,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -11854,7 +11799,7 @@ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -11865,7 +11810,7 @@ "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", "dev": true, "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "^5.1.0" } }, "randomfill": { @@ -11874,8 +11819,8 @@ "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", "dev": true, "requires": { - "randombytes": "2.0.6", - "safe-buffer": "5.1.1" + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" } }, "raw-loader": { @@ -11894,10 +11839,10 @@ "resolved": "https://registry.npmjs.org/react/-/react-16.3.0.tgz", "integrity": "sha512-Qh35tNbwY8SLFELkN3PCLO16EARV+lgcmNkQnoZXfzAF1ASRpeucZYUwBlBzsRAzTb7KyfBaLQ4/K/DLC6MYeA==", "requires": { - "fbjs": "0.8.16", - "loose-envify": "1.3.1", - "object-assign": "4.1.1", - "prop-types": "15.6.1" + "fbjs": "^0.8.16", + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.0" }, "dependencies": { "prop-types": { @@ -11905,9 +11850,9 @@ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.1.tgz", "integrity": "sha512-4ec7bY1Y66LymSUOH/zARVYObB23AT2h8cf6e/O6ZALB/N0sqZFEx7rq6EYPX2MkOdKORuooI/H5k9TlR4q7kQ==", "requires": { - "fbjs": "0.8.16", - "loose-envify": "1.3.1", - "object-assign": "4.1.1" + "fbjs": "^0.8.16", + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" } } } @@ -11917,9 +11862,9 @@ "resolved": "https://registry.npmjs.org/react-autosize-textarea/-/react-autosize-textarea-3.0.2.tgz", "integrity": "sha1-K2hApp9xOHGavOpaQp7PcwF2jAc=", "requires": { - "autosize": "4.0.1", - "line-height": "0.3.1", - "prop-types": "15.5.10" + "autosize": "^4.0.0", + "line-height": "^0.3.1", + "prop-types": "^15.5.6" } }, "react-click-outside": { @@ -11927,7 +11872,7 @@ "resolved": "https://registry.npmjs.org/react-click-outside/-/react-click-outside-2.3.1.tgz", "integrity": "sha1-MYc3698IGko7zUaCVmNnTL6YNus=", "requires": { - "hoist-non-react-statics": "1.2.0" + "hoist-non-react-statics": "^1.2.0" } }, "react-color": { @@ -11935,51 +11880,32 @@ "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.13.4.tgz", "integrity": "sha512-rNJTTxMPTImI1NpFaKLggDIvHgKOYRXj0krVh8c+Mo1YNsrLko8O94yiFqqdnSQgtIPteiAcGEJgBo9V5+uqaw==", "requires": { - "lodash": "4.17.5", - "material-colors": "1.2.5", - "prop-types": "15.5.10", - "reactcss": "1.2.3", - "tinycolor2": "1.4.1" + "lodash": "^4.0.1", + "material-colors": "^1.2.1", + "prop-types": "^15.5.4", + "reactcss": "^1.2.0", + "tinycolor2": "^1.1.2" } }, "react-datepicker": { - "version": "0.61.0", - "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-0.61.0.tgz", - "integrity": "sha512-FJnqJCbYaU1ca7Jn9LaJ7iKTYHKAskVkVHCsOBDQd8vJZrESLAu3rtPbj3T6IB++dQO7qb0IlcCXtmC0geIAGA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-1.4.1.tgz", + "integrity": "sha512-O/ExTWLS81pyWJWLFg1BRQEr9S/BDd6iMEkGctxQmVrRw2srW8DNdnQm5UgFNu8LoSZGMDvI55OghYZvDpWJhw==", "requires": { - "classnames": "2.2.5", - "eslint-plugin-import": "2.8.0", - "eslint-plugin-node": "5.2.1", - "moment": "2.20.1", - "prop-types": "15.6.0", - "react-onclickoutside": "6.7.1", - "react-popper": "0.7.5" + "classnames": "^2.2.5", + "prop-types": "^15.6.0", + "react-onclickoutside": "^6.7.1", + "react-popper": "^0.9.1" }, "dependencies": { - "eslint-plugin-node": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-5.2.1.tgz", - "integrity": "sha512-xhPXrh0Vl/b7870uEbaumb2Q+LxaEcOQ3kS1jtIXanBAwpMre1l5q/l2l/hESYJGEFKuI78bp6Uw50hlpr7B+g==", - "requires": { - "ignore": "3.3.7", - "minimatch": "3.0.4", - "resolve": "1.5.0", - "semver": "5.3.0" - } - }, - "moment": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.20.1.tgz", - "integrity": "sha512-Yh9y73JRljxW5QxN08Fner68eFLxM5ynNOAw2LbIB1YAGeQzZT8QFSUvkAz609Zf+IHhhaUxqZK8dG3W/+HEvg==" - }, "prop-types": { - "version": "15.6.0", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.0.tgz", - "integrity": "sha1-zq8IMCL8RrSjX2nhPvda7Q1jmFY=", + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.1.tgz", + "integrity": "sha512-4ec7bY1Y66LymSUOH/zARVYObB23AT2h8cf6e/O6ZALB/N0sqZFEx7rq6EYPX2MkOdKORuooI/H5k9TlR4q7kQ==", "requires": { - "fbjs": "0.8.16", - "loose-envify": "1.3.1", - "object-assign": "4.1.1" + "fbjs": "^0.8.16", + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" } } } @@ -11989,10 +11915,10 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.3.0.tgz", "integrity": "sha512-xT/FxawAurL6AV8YtAP7LkdDJFFX2vvv17AqFLQRF81ZtWLXkV/0dcAaiFIy0lmoQEFT931TU9aaH+5dBUxTcw==", "requires": { - "fbjs": "0.8.16", - "loose-envify": "1.3.1", - "object-assign": "4.1.1", - "prop-types": "15.6.1" + "fbjs": "^0.8.16", + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.0" }, "dependencies": { "prop-types": { @@ -12000,9 +11926,9 @@ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.1.tgz", "integrity": "sha512-4ec7bY1Y66LymSUOH/zARVYObB23AT2h8cf6e/O6ZALB/N0sqZFEx7rq6EYPX2MkOdKORuooI/H5k9TlR4q7kQ==", "requires": { - "fbjs": "0.8.16", - "loose-envify": "1.3.1", - "object-assign": "4.1.1" + "fbjs": "^0.8.16", + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" } } } @@ -12019,12 +11945,24 @@ "integrity": "sha512-p84kBqGaMoa7VYT0vZ/aOYRfJB+gw34yjpda1Z5KeLflg70HipZOT+MXQenEhdkPAABuE2Astq4zEPdMqUQxcg==" }, "react-popper": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-0.7.5.tgz", - "integrity": "sha512-ya9dhhGCf74JTOB2uyksEHhIGw7w9tNZRUJF73lEq2h4H5JT6MBa4PdT4G+sx6fZwq+xKZAL/sVNAIuojPn7Dg==", + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-0.9.5.tgz", + "integrity": "sha1-AqJO8+7DOvnlToNYq3DrDjMe3QU=", "requires": { - "popper.js": "1.12.9", - "prop-types": "15.5.10" + "popper.js": "^1.14.1", + "prop-types": "^15.6.1" + }, + "dependencies": { + "prop-types": { + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.1.tgz", + "integrity": "sha512-4ec7bY1Y66LymSUOH/zARVYObB23AT2h8cf6e/O6ZALB/N0sqZFEx7rq6EYPX2MkOdKORuooI/H5k9TlR4q7kQ==", + "requires": { + "fbjs": "^0.8.16", + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" + } + } } }, "react-reconciler": { @@ -12033,10 +11971,10 @@ "integrity": "sha512-50JwZ3yNyMS8fchN+jjWEJOH3Oze7UmhxeoJLn2j6f3NjpfCRbcmih83XTWmzqtar/ivd5f7tvQhvvhism2fgg==", "dev": true, "requires": { - "fbjs": "0.8.16", - "loose-envify": "1.3.1", - "object-assign": "4.1.1", - "prop-types": "15.6.0" + "fbjs": "^0.8.16", + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.0" }, "dependencies": { "prop-types": { @@ -12045,9 +11983,9 @@ "integrity": "sha1-zq8IMCL8RrSjX2nhPvda7Q1jmFY=", "dev": true, "requires": { - "fbjs": "0.8.16", - "loose-envify": "1.3.1", - "object-assign": "4.1.1" + "fbjs": "^0.8.16", + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" } } } @@ -12058,10 +11996,10 @@ "integrity": "sha512-7FAfgIT8Kvew36b2VFXnhMxjKkwAnicD1dyopq7Ot19WAhfJkyhYNwPzM1AnCfYccvEhO0x4FbqJETyMGQwgIg==", "dev": true, "requires": { - "fbjs": "0.8.16", - "object-assign": "4.1.1", - "prop-types": "15.6.1", - "react-is": "16.3.0" + "fbjs": "^0.8.16", + "object-assign": "^4.1.1", + "prop-types": "^15.6.0", + "react-is": "^16.3.0" }, "dependencies": { "prop-types": { @@ -12070,9 +12008,9 @@ "integrity": "sha512-4ec7bY1Y66LymSUOH/zARVYObB23AT2h8cf6e/O6ZALB/N0sqZFEx7rq6EYPX2MkOdKORuooI/H5k9TlR4q7kQ==", "dev": true, "requires": { - "fbjs": "0.8.16", - "loose-envify": "1.3.1", - "object-assign": "4.1.1" + "fbjs": "^0.8.16", + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" } } } @@ -12082,7 +12020,7 @@ "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz", "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==", "requires": { - "lodash": "4.17.5" + "lodash": "^4.0.1" } }, "read-chunk": { @@ -12091,8 +12029,8 @@ "integrity": "sha1-agTAkoAF7Z1C4aasVgDhnLx/9lU=", "dev": true, "requires": { - "pify": "3.0.0", - "safe-buffer": "5.1.1" + "pify": "^3.0.0", + "safe-buffer": "^5.1.1" }, "dependencies": { "pify": { @@ -12103,47 +12041,18 @@ } } }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "requires": { - "load-json-file": "2.0.0", - "normalize-package-data": "2.4.0", - "path-type": "2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "requires": { - "find-up": "2.1.0", - "read-pkg": "2.0.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "requires": { - "locate-path": "2.0.0" - } - } - } - }, "readable-stream": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.0.3", + "util-deprecate": "~1.0.1" } }, "readdirp": { @@ -12152,10 +12061,10 @@ "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "minimatch": "3.0.4", - "readable-stream": "2.3.3", - "set-immediate-shim": "1.0.1" + "graceful-fs": "^4.1.2", + "minimatch": "^3.0.2", + "readable-stream": "^2.0.2", + "set-immediate-shim": "^1.0.1" } }, "realpath-native": { @@ -12164,7 +12073,7 @@ "integrity": "sha512-XJtlRJ9jf0E1H1SLeJyQ9PGzQD7S65h1pRXEcAeK48doKOnKxcgPeNohJvD5u/2sI9J1oke6E8bZHS/fmW1UiQ==", "dev": true, "requires": { - "util.promisify": "1.0.0" + "util.promisify": "^1.0.0" } }, "recast": { @@ -12174,9 +12083,9 @@ "dev": true, "requires": { "ast-types": "0.11.3", - "esprima": "4.0.0", - "private": "0.1.8", - "source-map": "0.6.1" + "esprima": "~4.0.0", + "private": "~0.1.5", + "source-map": "~0.6.1" }, "dependencies": { "source-map": { @@ -12193,7 +12102,7 @@ "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", "dev": true, "requires": { - "resolve": "1.5.0" + "resolve": "^1.1.6" } }, "redent": { @@ -12202,8 +12111,8 @@ "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", "dev": true, "requires": { - "indent-string": "2.1.0", - "strip-indent": "1.0.1" + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" } }, "reduce-css-calc": { @@ -12212,9 +12121,9 @@ "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", "dev": true, "requires": { - "balanced-match": "0.4.2", - "math-expression-evaluator": "1.2.17", - "reduce-function-call": "1.0.2" + "balanced-match": "^0.4.2", + "math-expression-evaluator": "^1.2.14", + "reduce-function-call": "^1.0.1" }, "dependencies": { "balanced-match": { @@ -12231,7 +12140,7 @@ "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=", "dev": true, "requires": { - "balanced-match": "0.4.2" + "balanced-match": "^0.4.2" }, "dependencies": { "balanced-match": { @@ -12247,10 +12156,10 @@ "resolved": "https://registry.npmjs.org/redux/-/redux-3.7.2.tgz", "integrity": "sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A==", "requires": { - "lodash": "4.17.5", - "lodash-es": "4.17.5", - "loose-envify": "1.3.1", - "symbol-observable": "1.2.0" + "lodash": "^4.2.1", + "lodash-es": "^4.2.1", + "loose-envify": "^1.1.0", + "symbol-observable": "^1.0.3" } }, "redux-multi": { @@ -12286,9 +12195,9 @@ "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "private": "0.1.8" + "babel-runtime": "^6.18.0", + "babel-types": "^6.19.0", + "private": "^0.1.6" } }, "regex-cache": { @@ -12297,7 +12206,7 @@ "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", "dev": true, "requires": { - "is-equal-shallow": "0.1.3" + "is-equal-shallow": "^0.1.3" } }, "regex-not": { @@ -12306,8 +12215,8 @@ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, "requires": { - "extend-shallow": "3.0.2", - "safe-regex": "1.1.0" + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" } }, "regexpu-core": { @@ -12316,9 +12225,9 @@ "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", "dev": true, "requires": { - "regenerate": "1.3.3", - "regjsgen": "0.2.0", - "regjsparser": "0.1.5" + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" } }, "regjsgen": { @@ -12333,7 +12242,7 @@ "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", "dev": true, "requires": { - "jsesc": "0.5.0" + "jsesc": "~0.5.0" } }, "rememo": { @@ -12341,7 +12250,7 @@ "resolved": "https://registry.npmjs.org/rememo/-/rememo-2.4.0.tgz", "integrity": "sha512-4rqlLATPcha9lfdvylUWqSbceiTlYiBJvEJAyUiT/68cYPlNG1zXf7ABeve7s4YPrT6o3Q6zfN6n38ecAL71lw==", "requires": { - "shallow-equal": "1.0.0" + "shallow-equal": "^1.0.0" } }, "remove-trailing-separator": { @@ -12368,7 +12277,7 @@ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, "requires": { - "is-finite": "1.0.2" + "is-finite": "^1.0.0" } }, "replace-ext": { @@ -12383,28 +12292,28 @@ "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", "dev": true, "requires": { - "aws-sign2": "0.7.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.3.1", - "har-validator": "5.0.3", - "hawk": "6.0.2", - "http-signature": "1.2.0", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "oauth-sign": "0.8.2", - "performance-now": "2.1.0", - "qs": "6.5.1", - "safe-buffer": "5.1.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.6.0", - "uuid": "3.1.0" + "aws-sign2": "~0.7.0", + "aws4": "^1.6.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.1", + "forever-agent": "~0.6.1", + "form-data": "~2.3.1", + "har-validator": "~5.0.3", + "hawk": "~6.0.2", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.17", + "oauth-sign": "~0.8.2", + "performance-now": "^2.1.0", + "qs": "~6.5.1", + "safe-buffer": "^5.1.1", + "stringstream": "~0.0.5", + "tough-cookie": "~2.3.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.1.0" } }, "request-promise-core": { @@ -12413,7 +12322,7 @@ "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", "dev": true, "requires": { - "lodash": "4.17.5" + "lodash": "^4.13.1" } }, "request-promise-native": { @@ -12423,8 +12332,8 @@ "dev": true, "requires": { "request-promise-core": "1.1.1", - "stealthy-require": "1.1.1", - "tough-cookie": "2.3.3" + "stealthy-require": "^1.1.0", + "tough-cookie": ">=2.3.3" } }, "require-directory": { @@ -12449,8 +12358,8 @@ "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "dev": true, "requires": { - "caller-path": "0.1.0", - "resolve-from": "1.0.1" + "caller-path": "^0.1.0", + "resolve-from": "^1.0.0" }, "dependencies": { "resolve-from": { @@ -12471,7 +12380,7 @@ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", "requires": { - "path-parse": "1.0.5" + "path-parse": "^1.0.5" } }, "resolve-cwd": { @@ -12480,7 +12389,7 @@ "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", "dev": true, "requires": { - "resolve-from": "3.0.0" + "resolve-from": "^3.0.0" } }, "resolve-dir": { @@ -12489,8 +12398,8 @@ "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", "dev": true, "requires": { - "expand-tilde": "2.0.2", - "global-modules": "1.0.0" + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" } }, "resolve-from": { @@ -12511,7 +12420,7 @@ "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", "dev": true, "requires": { - "lowercase-keys": "1.0.1" + "lowercase-keys": "^1.0.0" } }, "restore-cursor": { @@ -12520,8 +12429,8 @@ "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", "dev": true, "requires": { - "onetime": "2.0.1", - "signal-exit": "3.0.2" + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" } }, "ret": { @@ -12537,7 +12446,7 @@ "dev": true, "optional": true, "requires": { - "align-text": "0.1.4" + "align-text": "^0.1.1" } }, "rimraf": { @@ -12546,7 +12455,7 @@ "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "requires": { - "glob": "7.1.2" + "glob": "^7.0.5" } }, "ripemd160": { @@ -12555,8 +12464,8 @@ "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=", "dev": true, "requires": { - "hash-base": "2.0.2", - "inherits": "2.0.3" + "hash-base": "^2.0.0", + "inherits": "^2.0.1" } }, "rst-selector-parser": { @@ -12565,8 +12474,8 @@ "integrity": "sha1-gbIw6i/MYGbInjRy3nlChdmwPZE=", "dev": true, "requires": { - "lodash.flattendeep": "4.4.0", - "nearley": "2.11.1" + "lodash.flattendeep": "^4.4.0", + "nearley": "^2.7.10" } }, "rtlcss": { @@ -12575,11 +12484,11 @@ "integrity": "sha512-JjQ5DlrmwiItAjlmhoxrJq5ihgZcE0wMFxt7S17bIrt4Lw0WwKKFk+viRhvodB/0falyG/5fiO043ZDh6/aqTw==", "dev": true, "requires": { - "chalk": "2.3.0", - "findup": "0.1.5", - "mkdirp": "0.5.1", - "postcss": "6.0.17", - "strip-json-comments": "2.0.1" + "chalk": "^2.3.0", + "findup": "^0.1.5", + "mkdirp": "^0.5.1", + "postcss": "^6.0.14", + "strip-json-comments": "^2.0.0" }, "dependencies": { "ansi-styles": { @@ -12588,7 +12497,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -12597,9 +12506,9 @@ "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" + "ansi-styles": "^3.1.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^4.0.0" } }, "postcss": { @@ -12608,9 +12517,9 @@ "integrity": "sha512-Bl1nybsSzWYbP8O4gAVD8JIjZIul9hLNOPTGBIlVmZNUnNAGL+W0cpYWzVwfImZOwumct4c1SDvSbncVWKtXUw==", "dev": true, "requires": { - "chalk": "2.3.0", - "source-map": "0.6.1", - "supports-color": "5.1.0" + "chalk": "^2.3.0", + "source-map": "^0.6.1", + "supports-color": "^5.1.0" }, "dependencies": { "supports-color": { @@ -12619,7 +12528,7 @@ "integrity": "sha512-Ry0AwkoKjDpVKK4sV4h6o3UJmNRbjYm2uXhwfj3J56lMVdvnUNqzQVRztOOMGQ++w1K/TjNDFvpJk0F/LoeBCQ==", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "^2.0.0" } } } @@ -12636,7 +12545,7 @@ "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "^2.0.0" } } } @@ -12647,7 +12556,7 @@ "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", "dev": true, "requires": { - "is-promise": "2.1.0" + "is-promise": "^2.1.0" } }, "run-parallel": { @@ -12662,7 +12571,7 @@ "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", "dev": true, "requires": { - "aproba": "1.2.0" + "aproba": "^1.1.1" } }, "rx": { @@ -12683,7 +12592,7 @@ "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", "dev": true, "requires": { - "rx-lite": "4.0.8" + "rx-lite": "*" } }, "rxjs": { @@ -12714,7 +12623,7 @@ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { - "ret": "0.1.15" + "ret": "~0.1.10" } }, "sane": { @@ -12723,14 +12632,14 @@ "integrity": "sha512-fW9svvNd81XzHDZyis9/tEY1bZikDGryy8Hi1BErPyNPYv47CdLseUN+tI5FBHWXEENRtj1SWtX/jBnggLaP0w==", "dev": true, "requires": { - "anymatch": "1.3.2", - "exec-sh": "0.2.1", - "fb-watchman": "2.0.0", - "fsevents": "1.1.3", - "minimatch": "3.0.4", - "minimist": "1.2.0", - "walker": "1.0.7", - "watch": "0.18.0" + "anymatch": "^1.3.0", + "exec-sh": "^0.2.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.1.1", + "minimatch": "^3.0.2", + "minimist": "^1.1.1", + "walker": "~1.0.5", + "watch": "~0.18.0" }, "dependencies": { "minimist": { @@ -12747,10 +12656,10 @@ "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", "dev": true, "requires": { - "glob": "7.1.2", - "lodash": "4.17.5", - "scss-tokenizer": "0.2.3", - "yargs": "7.1.0" + "glob": "^7.0.0", + "lodash": "^4.0.0", + "scss-tokenizer": "^0.2.3", + "yargs": "^7.0.0" }, "dependencies": { "camelcase": { @@ -12765,11 +12674,11 @@ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "strip-bom": "2.0.0" + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" } }, "os-locale": { @@ -12778,7 +12687,7 @@ "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { - "lcid": "1.0.0" + "lcid": "^1.0.0" } }, "path-type": { @@ -12787,9 +12696,9 @@ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, "read-pkg": { @@ -12798,9 +12707,9 @@ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, "requires": { - "load-json-file": "1.1.0", - "normalize-package-data": "2.4.0", - "path-type": "1.1.0" + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" } }, "read-pkg-up": { @@ -12809,8 +12718,8 @@ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, "requires": { - "find-up": "1.1.2", - "read-pkg": "1.1.0" + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" } }, "string-width": { @@ -12819,9 +12728,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "strip-bom": { @@ -12830,7 +12739,7 @@ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "0.2.1" + "is-utf8": "^0.2.0" } }, "which-module": { @@ -12845,19 +12754,19 @@ "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", "dev": true, "requires": { - "camelcase": "3.0.0", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "get-caller-file": "1.0.2", - "os-locale": "1.4.0", - "read-pkg-up": "1.0.1", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "1.0.2", - "which-module": "1.0.0", - "y18n": "3.2.1", - "yargs-parser": "5.0.0" + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" } }, "yargs-parser": { @@ -12866,7 +12775,7 @@ "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", "dev": true, "requires": { - "camelcase": "3.0.0" + "camelcase": "^3.0.0" } } } @@ -12877,11 +12786,11 @@ "integrity": "sha512-JoiyD00Yo1o61OJsoP2s2kb19L1/Y2p3QFcCdWdF6oomBGKVYuZyqHWemRBfQ2uGYsk+CH3eCguXNfpjzlcpaA==", "dev": true, "requires": { - "clone-deep": "2.0.2", - "loader-utils": "1.1.0", - "lodash.tail": "4.1.1", - "neo-async": "2.5.0", - "pify": "3.0.0" + "clone-deep": "^2.0.1", + "loader-utils": "^1.0.1", + "lodash.tail": "^4.1.1", + "neo-async": "^2.5.0", + "pify": "^3.0.0" }, "dependencies": { "pify": { @@ -12904,8 +12813,8 @@ "integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==", "dev": true, "requires": { - "ajv": "6.4.0", - "ajv-keywords": "3.1.0" + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" }, "dependencies": { "ajv": { @@ -12914,10 +12823,10 @@ "integrity": "sha1-06/3jpJ3VJdx2vAWTP9ISCt1T8Y=", "dev": true, "requires": { - "fast-deep-equal": "1.0.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1", - "uri-js": "3.0.2" + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0", + "uri-js": "^3.0.2" } }, "ajv-keywords": { @@ -12940,8 +12849,8 @@ "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", "dev": true, "requires": { - "js-base64": "2.4.3", - "source-map": "0.4.4" + "js-base64": "^2.1.8", + "source-map": "^0.4.2" }, "dependencies": { "source-map": { @@ -12950,7 +12859,7 @@ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { - "amdefine": "1.0.1" + "amdefine": ">=0.0.4" } } } @@ -12988,10 +12897,10 @@ "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", "dev": true, "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "split-string": "3.1.0" + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" }, "dependencies": { "extend-shallow": { @@ -13000,7 +12909,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -13016,8 +12925,8 @@ "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" } }, "shallow-clone": { @@ -13026,9 +12935,9 @@ "integrity": "sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==", "dev": true, "requires": { - "is-extendable": "0.1.1", - "kind-of": "5.1.0", - "mixin-object": "2.0.1" + "is-extendable": "^0.1.1", + "kind-of": "^5.0.0", + "mixin-object": "^2.0.1" }, "dependencies": { "kind-of": { @@ -13044,17 +12953,12 @@ "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.0.0.tgz", "integrity": "sha1-UI0YOLPeWQq4dXsBGyXkMJAJRfc=" }, - "shallowequal": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.0.2.tgz", - "integrity": "sha512-zlVXeVUKvo+HEv1e2KQF/csyeMKx2oHvatQ9l6XjCUj3agvC8XGf6R9HvIPDSmp8FNPvx7b5kaEJTRi7CqxtEw==" - }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "requires": { - "shebang-regex": "1.0.0" + "shebang-regex": "^1.0.0" } }, "shebang-regex": { @@ -13068,9 +12972,9 @@ "integrity": "sha512-YA/iYtZpzFe5HyWVGrb02FjPxc4EMCfpoU/Phg9fQoyMC72u9598OUBrsU8IrtwAKG0tO8IYaqbaLIw+k3IRGA==", "dev": true, "requires": { - "glob": "7.1.2", - "interpret": "1.1.0", - "rechoir": "0.6.2" + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" } }, "shellwords": { @@ -13080,11 +12984,11 @@ "dev": true }, "showdown": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/showdown/-/showdown-1.7.4.tgz", - "integrity": "sha1-a7yd0s2x5f3XSeza3GpHuFZZSuA=", + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/showdown/-/showdown-1.8.6.tgz", + "integrity": "sha1-kepO47elRIqspoIKTifmkMatdxw=", "requires": { - "yargs": "8.0.2" + "yargs": "^10.0.3" } }, "sigmund": { @@ -13126,14 +13030,14 @@ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "dev": true, "requires": { - "base": "0.11.2", - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "map-cache": "0.2.2", - "source-map": "0.5.7", - "source-map-resolve": "0.5.1", - "use": "3.1.0" + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" }, "dependencies": { "define-property": { @@ -13142,7 +13046,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "extend-shallow": { @@ -13151,7 +13055,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } }, "is-accessor-descriptor": { @@ -13160,7 +13064,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -13169,7 +13073,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -13180,7 +13084,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -13189,7 +13093,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -13200,9 +13104,9 @@ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" } }, "kind-of": { @@ -13219,9 +13123,9 @@ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "dev": true, "requires": { - "define-property": "1.0.0", - "isobject": "3.0.1", - "snapdragon-util": "3.0.1" + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" }, "dependencies": { "define-property": { @@ -13230,7 +13134,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "isobject": { @@ -13247,7 +13151,7 @@ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.2.0" } }, "sntp": { @@ -13256,7 +13160,7 @@ "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", "dev": true, "requires": { - "hoek": "4.2.0" + "hoek": "4.x.x" } }, "sort-keys": { @@ -13265,7 +13169,7 @@ "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", "dev": true, "requires": { - "is-plain-obj": "1.1.0" + "is-plain-obj": "^1.0.0" } }, "source-list-map": { @@ -13286,11 +13190,11 @@ "integrity": "sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==", "dev": true, "requires": { - "atob": "2.1.0", - "decode-uri-component": "0.2.0", - "resolve-url": "0.2.1", - "source-map-url": "0.4.0", - "urix": "0.1.0" + "atob": "^2.0.0", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" } }, "source-map-support": { @@ -13299,13 +13203,13 @@ "integrity": "sha512-eKkTgWYeBOQqFGXRfKabMFdnWepo51vWqEdoeikaEPFiJC7MCU5j2h4+6Q8npkZTeLGbSyecZvRxiSoWl3rh+w==", "dev": true, "requires": { - "source-map": "0.6.1" + "source-map": "^0.6.0" }, "dependencies": { "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true } } @@ -13326,19 +13230,22 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", + "dev": true, "requires": { - "spdx-license-ids": "1.2.2" + "spdx-license-ids": "^1.0.2" } }, "spdx-expression-parse": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", - "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=" + "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=", + "dev": true }, "spdx-license-ids": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", - "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=" + "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", + "dev": true }, "split-string": { "version": "3.1.0", @@ -13346,7 +13253,7 @@ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "dev": true, "requires": { - "extend-shallow": "3.0.2" + "extend-shallow": "^3.0.0" } }, "sprintf-js": { @@ -13361,14 +13268,14 @@ "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", "dev": true, "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "tweetnacl": "~0.14.0" } }, "ssri": { @@ -13377,7 +13284,7 @@ "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", "dev": true, "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "^5.1.1" } }, "stack-utils": { @@ -13392,8 +13299,8 @@ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "dev": true, "requires": { - "define-property": "0.2.5", - "object-copy": "0.1.0" + "define-property": "^0.2.5", + "object-copy": "^0.1.0" }, "dependencies": { "define-property": { @@ -13402,7 +13309,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "is-accessor-descriptor": { @@ -13411,7 +13318,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -13420,7 +13327,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -13431,7 +13338,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -13440,7 +13347,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -13451,9 +13358,9 @@ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" } }, "kind-of": { @@ -13470,7 +13377,7 @@ "integrity": "sha1-osfIWH5U2UJ+qe2zrD8s1SLfN4s=", "dev": true, "requires": { - "readable-stream": "2.3.3" + "readable-stream": "^2.0.1" } }, "stealthy-require": { @@ -13485,8 +13392,8 @@ "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", "dev": true, "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.3" + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" } }, "stream-each": { @@ -13495,8 +13402,8 @@ "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==", "dev": true, "requires": { - "end-of-stream": "1.4.1", - "stream-shift": "1.0.0" + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" } }, "stream-http": { @@ -13505,11 +13412,11 @@ "integrity": "sha512-cQ0jo17BLca2r0GfRdZKYAGLU6JRoIWxqSOakUMuKOT6MOK7AAlE856L33QuDmAy/eeOrhLee3dZKX0Uadu93A==", "dev": true, "requires": { - "builtin-status-codes": "3.0.0", - "inherits": "2.0.3", - "readable-stream": "2.3.3", - "to-arraybuffer": "1.0.1", - "xtend": "4.0.1" + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.3", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" } }, "stream-shift": { @@ -13524,7 +13431,7 @@ "integrity": "sha1-WdbqOT2HwsDdrBCqDVYbxrpvDhA=", "dev": true, "requires": { - "any-observable": "0.2.0" + "any-observable": "^0.2.0" } }, "strict-uri-encode": { @@ -13539,8 +13446,8 @@ "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", "dev": true, "requires": { - "astral-regex": "1.0.0", - "strip-ansi": "4.0.0" + "astral-regex": "^1.0.0", + "strip-ansi": "^4.0.0" }, "dependencies": { "ansi-regex": { @@ -13555,7 +13462,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } } } @@ -13571,8 +13478,8 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" }, "dependencies": { "ansi-regex": { @@ -13590,7 +13497,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } } } @@ -13600,7 +13507,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "~5.1.0" } }, "stringstream": { @@ -13614,13 +13521,14 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true }, "strip-bom-stream": { "version": "2.0.0", @@ -13628,8 +13536,8 @@ "integrity": "sha1-+H217yYT9paKpUWr/h7HKLaoKco=", "dev": true, "requires": { - "first-chunk-stream": "2.0.0", - "strip-bom": "2.0.0" + "first-chunk-stream": "^2.0.0", + "strip-bom": "^2.0.0" }, "dependencies": { "strip-bom": { @@ -13638,7 +13546,7 @@ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "0.2.1" + "is-utf8": "^0.2.0" } } } @@ -13654,7 +13562,7 @@ "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", "dev": true, "requires": { - "get-stdin": "4.0.1" + "get-stdin": "^4.0.1" } }, "strip-json-comments": { @@ -13669,8 +13577,8 @@ "integrity": "sha512-2I7AVP73MvK33U7B9TKlYZAqdROyMXDYSMvHLX43qy3GCOaJNiV6i0v/sv9idWIaQ42Yn2dNv79Q5mKXbKhAZg==", "dev": true, "requires": { - "loader-utils": "1.1.0", - "schema-utils": "0.4.5" + "loader-utils": "^1.1.0", + "schema-utils": "^0.4.5" } }, "supports-color": { @@ -13685,13 +13593,13 @@ "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", "dev": true, "requires": { - "coa": "1.0.4", - "colors": "1.1.2", - "csso": "2.3.2", - "js-yaml": "3.7.0", - "mkdirp": "0.5.1", - "sax": "1.2.4", - "whet.extend": "0.9.9" + "coa": "~1.0.1", + "colors": "~1.1.2", + "csso": "~2.3.1", + "js-yaml": "~3.7.0", + "mkdirp": "~0.5.1", + "sax": "~1.2.1", + "whet.extend": "~0.9.9" }, "dependencies": { "colors": { @@ -13712,8 +13620,8 @@ "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", "dev": true, "requires": { - "argparse": "1.0.9", - "esprima": "2.7.3" + "argparse": "^1.0.7", + "esprima": "^2.6.0" } } } @@ -13735,12 +13643,12 @@ "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", "dev": true, "requires": { - "ajv": "5.5.2", - "ajv-keywords": "2.1.1", - "chalk": "2.3.0", - "lodash": "4.17.5", + "ajv": "^5.2.3", + "ajv-keywords": "^2.1.0", + "chalk": "^2.1.0", + "lodash": "^4.17.4", "slice-ansi": "1.0.0", - "string-width": "2.1.1" + "string-width": "^2.1.1" }, "dependencies": { "ansi-styles": { @@ -13749,7 +13657,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -13758,9 +13666,9 @@ "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", "dev": true, "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" + "ansi-styles": "^3.1.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^4.0.0" } }, "is-fullwidth-code-point": { @@ -13775,7 +13683,7 @@ "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0" + "is-fullwidth-code-point": "^2.0.0" } }, "supports-color": { @@ -13784,7 +13692,7 @@ "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "^2.0.0" } } } @@ -13801,9 +13709,9 @@ "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", "dev": true, "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" + "block-stream": "*", + "fstream": "^1.0.2", + "inherits": "2" } }, "temp": { @@ -13812,8 +13720,8 @@ "integrity": "sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k=", "dev": true, "requires": { - "os-tmpdir": "1.0.2", - "rimraf": "2.2.8" + "os-tmpdir": "^1.0.0", + "rimraf": "~2.2.6" }, "dependencies": { "rimraf": { @@ -13830,11 +13738,11 @@ "integrity": "sha512-8hMFzjxbPv6xSlwGhXSvOMJ/vTy3bkng+2pxmf6E1z6VF7I9nIyNfvHtaw+NBPgvz647gADBbMSbwLfZYppT/w==", "dev": true, "requires": { - "arrify": "1.0.1", - "micromatch": "2.3.11", - "object-assign": "4.1.1", - "read-pkg-up": "1.0.1", - "require-main-filename": "1.0.1" + "arrify": "^1.0.1", + "micromatch": "^2.3.11", + "object-assign": "^4.1.0", + "read-pkg-up": "^1.0.1", + "require-main-filename": "^1.0.1" }, "dependencies": { "load-json-file": { @@ -13843,11 +13751,11 @@ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "strip-bom": "2.0.0" + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" } }, "path-type": { @@ -13856,9 +13764,9 @@ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, "read-pkg": { @@ -13867,9 +13775,9 @@ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, "requires": { - "load-json-file": "1.1.0", - "normalize-package-data": "2.4.0", - "path-type": "1.1.0" + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" } }, "read-pkg-up": { @@ -13878,8 +13786,8 @@ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, "requires": { - "find-up": "1.1.2", - "read-pkg": "1.1.0" + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" } }, "strip-bom": { @@ -13888,7 +13796,7 @@ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "0.2.1" + "is-utf8": "^0.2.0" } } } @@ -13923,8 +13831,8 @@ "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", "dev": true, "requires": { - "readable-stream": "2.3.3", - "xtend": "4.0.1" + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" } }, "timed-out": { @@ -13939,7 +13847,7 @@ "integrity": "sha512-HQ3nbYRAowdVd0ckGFvmJPPCOH/CHleFN/Y0YQCX1DVaB7t+KFvisuyN09fuP8Jtp1CpfSh8O8bMkHbdbPe6Pw==", "dev": true, "requires": { - "setimmediate": "1.0.5" + "setimmediate": "^1.0.4" } }, "tiny-emitter": { @@ -13982,7 +13890,7 @@ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" } }, "to-regex": { @@ -13991,10 +13899,10 @@ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", "dev": true, "requires": { - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "regex-not": "1.0.2", - "safe-regex": "1.1.0" + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" } }, "to-regex-range": { @@ -14003,8 +13911,8 @@ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, "requires": { - "is-number": "3.0.0", - "repeat-string": "1.6.1" + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" }, "dependencies": { "is-number": { @@ -14013,7 +13921,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" } } } @@ -14024,7 +13932,7 @@ "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", "dev": true, "requires": { - "punycode": "1.4.1" + "punycode": "^1.4.1" } }, "tr46": { @@ -14033,7 +13941,7 @@ "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", "dev": true, "requires": { - "punycode": "2.1.0" + "punycode": "^2.1.0" }, "dependencies": { "punycode": { @@ -14068,7 +13976,7 @@ "integrity": "sha1-fskRMJJHZsf1c74wIMNPj9/QDWI=", "dev": true, "requires": { - "glob": "6.0.4" + "glob": "^6.0.4" }, "dependencies": { "glob": { @@ -14077,11 +13985,11 @@ "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", "dev": true, "requires": { - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } } } @@ -14098,7 +14006,7 @@ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "dev": true, "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "^5.0.1" } }, "tweetnacl": { @@ -14114,7 +14022,7 @@ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "dev": true, "requires": { - "prelude-ls": "1.1.2" + "prelude-ls": "~1.1.2" } }, "typedarray": { @@ -14126,7 +14034,7 @@ "ua-parser-js": { "version": "0.7.17", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.17.tgz", - "integrity": "sha512-uRdSdu1oA1rncCQL7sCj8vSyZkgtL7faaw9Tc9rZ3mGgraQ7+Pdx7w5mnOSF3gw9ZNG6oc+KXfkon3bKuROm0g==" + "integrity": "sha1-6exflJi57JEOeuOsYmqAXE0J7Kw=" }, "uglify-js": { "version": "2.8.29", @@ -14135,9 +14043,9 @@ "dev": true, "optional": true, "requires": { - "source-map": "0.5.7", - "uglify-to-browserify": "1.0.2", - "yargs": "3.10.0" + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" }, "dependencies": { "camelcase": { @@ -14154,8 +14062,8 @@ "dev": true, "optional": true, "requires": { - "center-align": "0.1.3", - "right-align": "0.1.3", + "center-align": "^0.1.1", + "right-align": "^0.1.1", "wordwrap": "0.0.2" } }, @@ -14173,9 +14081,9 @@ "dev": true, "optional": true, "requires": { - "camelcase": "1.2.1", - "cliui": "2.1.0", - "decamelize": "1.2.0", + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", "window-size": "0.1.0" } } @@ -14194,36 +14102,36 @@ "integrity": "sha512-z0IbjpW8b3O/OVn+TTZN4pI29RN1zktFBXLIzzfZ+++cUtZ1ERSlLWgpE/5OERuEUs1ijVQnpYAkSlpoVmQmSQ==", "dev": true, "requires": { - "cacache": "10.0.4", - "find-cache-dir": "1.0.0", - "schema-utils": "0.4.5", - "serialize-javascript": "1.4.0", - "source-map": "0.6.1", - "uglify-es": "3.3.9", - "webpack-sources": "1.1.0", - "worker-farm": "1.6.0" + "cacache": "^10.0.4", + "find-cache-dir": "^1.0.0", + "schema-utils": "^0.4.5", + "serialize-javascript": "^1.4.0", + "source-map": "^0.6.1", + "uglify-es": "^3.3.4", + "webpack-sources": "^1.1.0", + "worker-farm": "^1.5.2" }, "dependencies": { "commander": { "version": "2.13.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", - "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", + "integrity": "sha1-aWS8pnaF33wfFDDFhPB9dZeIW5w=", "dev": true }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true }, "uglify-es": { "version": "3.3.9", "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", - "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", + "integrity": "sha1-DBxPBwC+2NvBJM2zBNJZLKID5nc=", "dev": true, "requires": { - "commander": "2.13.0", - "source-map": "0.6.1" + "commander": "~2.13.0", + "source-map": "~0.6.1" } } } @@ -14246,10 +14154,10 @@ "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", "dev": true, "requires": { - "arr-union": "3.1.0", - "get-value": "2.0.6", - "is-extendable": "0.1.1", - "set-value": "0.4.3" + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" }, "dependencies": { "extend-shallow": { @@ -14258,7 +14166,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } }, "set-value": { @@ -14267,10 +14175,10 @@ "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", "dev": true, "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "to-object-path": "0.3.0" + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" } } } @@ -14287,7 +14195,7 @@ "integrity": "sha1-iSIN32t1GuUrX3JISGNShZa7hME=", "dev": true, "requires": { - "macaddress": "0.2.8" + "macaddress": "^0.2.8" } }, "uniqs": { @@ -14302,7 +14210,7 @@ "integrity": "sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM=", "dev": true, "requires": { - "unique-slug": "2.0.0" + "unique-slug": "^2.0.0" } }, "unique-slug": { @@ -14311,7 +14219,7 @@ "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", "dev": true, "requires": { - "imurmurhash": "0.1.4" + "imurmurhash": "^0.1.4" } }, "unset-value": { @@ -14320,8 +14228,8 @@ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "dev": true, "requires": { - "has-value": "0.3.1", - "isobject": "3.0.1" + "has-value": "^0.3.1", + "isobject": "^3.0.0" }, "dependencies": { "has-value": { @@ -14330,9 +14238,9 @@ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", "dev": true, "requires": { - "get-value": "2.0.6", - "has-values": "0.1.4", - "isobject": "2.1.0" + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" }, "dependencies": { "isobject": { @@ -14378,7 +14286,7 @@ "integrity": "sha1-+QuFhQf4HepNz7s8TD2/orVX+qo=", "dev": true, "requires": { - "punycode": "2.1.0" + "punycode": "^2.1.0" }, "dependencies": { "punycode": { @@ -14419,7 +14327,7 @@ "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", "dev": true, "requires": { - "prepend-http": "2.0.0" + "prepend-http": "^2.0.0" }, "dependencies": { "prepend-http": { @@ -14448,7 +14356,7 @@ "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.2" }, "dependencies": { "kind-of": { @@ -14487,8 +14395,8 @@ "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", "dev": true, "requires": { - "define-properties": "1.1.2", - "object.getownpropertydescriptors": "2.0.3" + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" } }, "uuid": { @@ -14506,9 +14414,10 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", + "dev": true, "requires": { - "spdx-correct": "1.0.2", - "spdx-expression-parse": "1.0.4" + "spdx-correct": "~1.0.0", + "spdx-expression-parse": "~1.0.0" } }, "vendors": { @@ -14523,9 +14432,9 @@ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "dev": true, "requires": { - "assert-plus": "1.0.0", + "assert-plus": "^1.0.0", "core-util-is": "1.0.2", - "extsprintf": "1.3.0" + "extsprintf": "^1.2.0" } }, "vinyl": { @@ -14534,8 +14443,8 @@ "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, "requires": { - "clone": "1.0.3", - "clone-stats": "0.0.1", + "clone": "^1.0.0", + "clone-stats": "^0.0.1", "replace-ext": "0.0.1" } }, @@ -14545,12 +14454,12 @@ "integrity": "sha1-p+v1/779obfRjRQPyweyI++2dRo=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "strip-bom": "2.0.0", - "strip-bom-stream": "2.0.0", - "vinyl": "1.2.0" + "graceful-fs": "^4.1.2", + "pify": "^2.3.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0", + "strip-bom-stream": "^2.0.0", + "vinyl": "^1.1.0" }, "dependencies": { "strip-bom": { @@ -14559,7 +14468,7 @@ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "0.2.1" + "is-utf8": "^0.2.0" } } } @@ -14579,7 +14488,7 @@ "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", "dev": true, "requires": { - "browser-process-hrtime": "0.1.2" + "browser-process-hrtime": "^0.1.2" } }, "walker": { @@ -14588,7 +14497,7 @@ "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", "dev": true, "requires": { - "makeerror": "1.0.11" + "makeerror": "1.0.x" } }, "watch": { @@ -14597,8 +14506,8 @@ "integrity": "sha1-KAlUdsbffJDJYxOJkMClQj60uYY=", "dev": true, "requires": { - "exec-sh": "0.2.1", - "minimist": "1.2.0" + "exec-sh": "^0.2.0", + "minimist": "^1.2.0" }, "dependencies": { "minimist": { @@ -14615,9 +14524,9 @@ "integrity": "sha512-RSlipNQB1u48cq0wH/BNfCu1tD/cJ8ydFIkNYhp9o+3d+8unClkIovpW5qpFPgmL9OE48wfAnlZydXByWP82AA==", "dev": true, "requires": { - "chokidar": "2.0.3", - "graceful-fs": "4.1.11", - "neo-async": "2.5.0" + "chokidar": "^2.0.2", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" } }, "webidl-conversions": { @@ -14632,25 +14541,25 @@ "integrity": "sha512-iLUJcsEAjaPKWbB32ADr29Pg9fPUVfFEMPK4VXyZGftzhSEFg2BLjHLoBYZ14wdTEA8xqG/hjpuX8qOmabRYvw==", "dev": true, "requires": { - "acorn": "5.4.1", - "acorn-dynamic-import": "3.0.0", - "ajv": "6.4.0", - "ajv-keywords": "3.1.0", - "chrome-trace-event": "0.1.2", - "enhanced-resolve": "4.0.0", - "eslint-scope": "3.7.1", - "loader-runner": "2.3.0", - "loader-utils": "1.1.0", - "memory-fs": "0.4.1", - "micromatch": "3.1.10", - "mkdirp": "0.5.1", - "neo-async": "2.5.0", - "node-libs-browser": "2.1.0", - "schema-utils": "0.4.5", - "tapable": "1.0.0", - "uglifyjs-webpack-plugin": "1.2.4", - "watchpack": "1.5.0", - "webpack-sources": "1.1.0" + "acorn": "^5.0.0", + "acorn-dynamic-import": "^3.0.0", + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "chrome-trace-event": "^0.1.1", + "enhanced-resolve": "^4.0.0", + "eslint-scope": "^3.7.1", + "loader-runner": "^2.3.0", + "loader-utils": "^1.1.0", + "memory-fs": "~0.4.1", + "micromatch": "^3.1.8", + "mkdirp": "~0.5.0", + "neo-async": "^2.5.0", + "node-libs-browser": "^2.0.0", + "schema-utils": "^0.4.2", + "tapable": "^1.0.0", + "uglifyjs-webpack-plugin": "^1.2.4", + "watchpack": "^1.5.0", + "webpack-sources": "^1.0.1" }, "dependencies": { "ajv": { @@ -14659,10 +14568,10 @@ "integrity": "sha1-06/3jpJ3VJdx2vAWTP9ISCt1T8Y=", "dev": true, "requires": { - "fast-deep-equal": "1.0.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1", - "uri-js": "3.0.2" + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0", + "uri-js": "^3.0.2" } }, "ajv-keywords": { @@ -14689,18 +14598,18 @@ "integrity": "sha512-SO5lYHA3vO6gz66erVvedSCkp7AKWdv6VcQ2N4ysXfPxdAlxAMMAdwegGGcv1Bqwm7naF1hNdk5d6AAIEHV2nQ==", "dev": true, "requires": { - "arr-flatten": "1.1.0", - "array-unique": "0.3.2", - "define-property": "1.0.0", - "extend-shallow": "2.0.1", - "fill-range": "4.0.0", - "isobject": "3.0.1", - "kind-of": "6.0.2", - "repeat-element": "1.1.2", - "snapdragon": "0.8.2", - "snapdragon-node": "2.1.1", - "split-string": "3.1.0", - "to-regex": "3.0.2" + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "kind-of": "^6.0.2", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" }, "dependencies": { "define-property": { @@ -14709,7 +14618,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "extend-shallow": { @@ -14718,7 +14627,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -14729,13 +14638,13 @@ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "posix-character-classes": "0.1.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "define-property": { @@ -14744,7 +14653,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "extend-shallow": { @@ -14753,7 +14662,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } }, "is-descriptor": { @@ -14762,9 +14671,9 @@ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" } }, "kind-of": { @@ -14781,14 +14690,14 @@ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, "requires": { - "array-unique": "0.3.2", - "define-property": "1.0.0", - "expand-brackets": "2.1.4", - "extend-shallow": "2.0.1", - "fragment-cache": "0.2.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "define-property": { @@ -14797,7 +14706,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "extend-shallow": { @@ -14806,7 +14715,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -14817,10 +14726,10 @@ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "extend-shallow": "2.0.1", - "is-number": "3.0.0", - "repeat-string": "1.6.1", - "to-regex-range": "2.1.1" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" }, "dependencies": { "extend-shallow": { @@ -14829,7 +14738,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -14840,7 +14749,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -14849,7 +14758,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -14860,7 +14769,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -14869,7 +14778,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -14880,7 +14789,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -14889,7 +14798,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -14912,19 +14821,19 @@ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "braces": "2.3.1", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "extglob": "2.0.4", - "fragment-cache": "0.2.1", - "kind-of": "6.0.2", - "nanomatch": "1.2.9", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" } } } @@ -14935,7 +14844,7 @@ "integrity": "sha512-MGO0nVniCLFAQz1qv22zM02QPjcpAoJdy7ED0i3Zy7SY1IecgXCm460ib7H/Wq7e9oL5VL6S2BxaObxwIcag0g==", "dev": true, "requires": { - "jscodeshift": "0.4.1" + "jscodeshift": "^0.4.0" }, "dependencies": { "ansi-styles": { @@ -14962,15 +14871,15 @@ "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=", "dev": true, "requires": { - "ansi-styles": "1.0.0", - "has-color": "0.1.7", - "strip-ansi": "0.1.1" + "ansi-styles": "~1.0.0", + "has-color": "~0.1.0", + "strip-ansi": "~0.1.0" } }, "colors": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.1.tgz", - "integrity": "sha512-s8+wktIuDSLffCywiwSxQOMqtPxML11a/dtHE17tMn4B1MSWw/C22EKf7M2KGUBcDaVFEGT+S8N02geDXeuNKg==", + "integrity": "sha1-9KPTApdqrwQjVroa3jsaLGLZ15Q=", "dev": true }, "jscodeshift": { @@ -14979,21 +14888,21 @@ "integrity": "sha512-iOX6If+hsw0q99V3n31t4f5VlD1TQZddH08xbT65ZqA7T4Vkx68emrDZMUOLVvCEAJ6NpAk7DECe3fjC/t52AQ==", "dev": true, "requires": { - "async": "1.5.2", - "babel-plugin-transform-flow-strip-types": "6.22.0", - "babel-preset-es2015": "6.24.1", - "babel-preset-stage-1": "6.24.1", - "babel-register": "6.26.0", - "babylon": "6.18.0", - "colors": "1.2.1", - "flow-parser": "0.69.0", - "lodash": "4.17.5", - "micromatch": "2.3.11", + "async": "^1.5.0", + "babel-plugin-transform-flow-strip-types": "^6.8.0", + "babel-preset-es2015": "^6.9.0", + "babel-preset-stage-1": "^6.5.0", + "babel-register": "^6.9.0", + "babylon": "^6.17.3", + "colors": "^1.1.2", + "flow-parser": "^0.*", + "lodash": "^4.13.1", + "micromatch": "^2.3.7", "node-dir": "0.1.8", - "nomnom": "1.8.1", - "recast": "0.12.9", - "temp": "0.8.3", - "write-file-atomic": "1.3.4" + "nomnom": "^1.8.1", + "recast": "^0.12.5", + "temp": "^0.8.1", + "write-file-atomic": "^1.2.0" } }, "nomnom": { @@ -15002,8 +14911,8 @@ "integrity": "sha1-IVH3Ikcrp55Qp2/BJbuMjy5Nwqc=", "dev": true, "requires": { - "chalk": "0.4.0", - "underscore": "1.6.0" + "chalk": "~0.4.0", + "underscore": "~1.6.0" } }, "recast": { @@ -15013,10 +14922,10 @@ "dev": true, "requires": { "ast-types": "0.10.1", - "core-js": "2.5.3", - "esprima": "4.0.0", - "private": "0.1.8", - "source-map": "0.6.1" + "core-js": "^2.4.1", + "esprima": "~4.0.0", + "private": "~0.1.5", + "source-map": "~0.6.1" } }, "source-map": { @@ -15043,9 +14952,9 @@ "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "imurmurhash": "0.1.4", - "slide": "1.1.6" + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "slide": "^1.1.5" } } } @@ -15056,31 +14965,31 @@ "integrity": "sha512-0lnOi3yla8FsZVuMsbfnNRB/8DlfuDugKdekC+4ykydZG0+UOidMi5J5LLWN4c0VJ8PqC19yMXXkYyCq78OuqA==", "dev": true, "requires": { - "chalk": "2.3.2", - "cross-spawn": "6.0.5", - "diff": "3.5.0", - "enhanced-resolve": "4.0.0", - "glob-all": "3.1.0", - "global-modules": "1.0.0", - "got": "8.3.0", - "inquirer": "5.2.0", - "interpret": "1.1.0", - "jscodeshift": "0.5.0", - "listr": "0.13.0", - "loader-utils": "1.1.0", - "lodash": "4.17.5", - "log-symbols": "2.2.0", - "mkdirp": "0.5.1", - "p-each-series": "1.0.0", - "p-lazy": "1.0.0", - "prettier": "1.11.1", - "resolve-cwd": "2.0.0", - "supports-color": "5.3.0", - "v8-compile-cache": "1.1.2", - "webpack-addons": "1.1.5", - "yargs": "11.0.0", - "yeoman-environment": "2.0.5", - "yeoman-generator": "2.0.3" + "chalk": "^2.3.2", + "cross-spawn": "^6.0.5", + "diff": "^3.5.0", + "enhanced-resolve": "^4.0.0", + "glob-all": "^3.1.0", + "global-modules": "^1.0.0", + "got": "^8.2.0", + "inquirer": "^5.1.0", + "interpret": "^1.0.4", + "jscodeshift": "^0.5.0", + "listr": "^0.13.0", + "loader-utils": "^1.1.0", + "lodash": "^4.17.5", + "log-symbols": "^2.2.0", + "mkdirp": "^0.5.1", + "p-each-series": "^1.0.0", + "p-lazy": "^1.0.0", + "prettier": "^1.5.3", + "resolve-cwd": "^2.0.0", + "supports-color": "^5.3.0", + "v8-compile-cache": "^1.1.2", + "webpack-addons": "^1.1.5", + "yargs": "^11.0.0", + "yeoman-environment": "^2.0.0", + "yeoman-generator": "^2.0.3" }, "dependencies": { "ansi-regex": { @@ -15095,7 +15004,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -15104,9 +15013,9 @@ "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.3.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "cliui": { @@ -15115,9 +15024,9 @@ "integrity": "sha512-nY3W5Gu2racvdDk//ELReY+dHjb9PlIcVDFXP72nVIhq2Gy3LuVXYwJoPVudwQnv1shtohpgkdCKT2YaKY0CKw==", "dev": true, "requires": { - "string-width": "2.1.1", - "strip-ansi": "4.0.0", - "wrap-ansi": "2.1.0" + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" } }, "cross-spawn": { @@ -15126,11 +15035,11 @@ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { - "nice-try": "1.0.4", - "path-key": "2.0.1", - "semver": "5.5.0", - "shebang-command": "1.2.0", - "which": "1.3.0" + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" } }, "diff": { @@ -15145,7 +15054,7 @@ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "2.0.0" + "locate-path": "^2.0.0" } }, "has-flag": { @@ -15160,19 +15069,19 @@ "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", "dev": true, "requires": { - "ansi-escapes": "3.0.0", - "chalk": "2.3.2", - "cli-cursor": "2.1.0", - "cli-width": "2.2.0", - "external-editor": "2.1.0", - "figures": "2.0.0", - "lodash": "4.17.5", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.1.0", + "figures": "^2.0.0", + "lodash": "^4.3.0", "mute-stream": "0.0.7", - "run-async": "2.3.0", - "rxjs": "5.5.8", - "string-width": "2.1.1", - "strip-ansi": "4.0.0", - "through": "2.3.8" + "run-async": "^2.2.0", + "rxjs": "^5.5.2", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" } }, "lodash": { @@ -15193,7 +15102,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } }, "supports-color": { @@ -15202,7 +15111,7 @@ "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } }, "yargs": { @@ -15211,18 +15120,18 @@ "integrity": "sha512-Rjp+lMYQOWtgqojx1dEWorjCofi1YN7AoFvYV7b1gx/7dAAeuI4kN5SZiEvr0ZmsZTOpDRcCqrpI10L31tFkBw==", "dev": true, "requires": { - "cliui": "4.0.0", - "decamelize": "1.2.0", - "find-up": "2.1.0", - "get-caller-file": "1.0.2", - "os-locale": "2.1.0", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "2.1.1", - "which-module": "2.0.0", - "y18n": "3.2.1", - "yargs-parser": "9.0.2" + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^9.0.2" } }, "yargs-parser": { @@ -15231,21 +15140,22 @@ "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", "dev": true, "requires": { - "camelcase": "4.1.0" + "camelcase": "^4.1.0" } } } }, "webpack-rtl-plugin": { "version": "github:yoavf/webpack-rtl-plugin#fc5a2f20dd99fde8f86f297844aefde601780fa3", + "from": "github:yoavf/webpack-rtl-plugin#develop", "dev": true, "requires": { - "@romainberger/css-diff": "1.0.3", - "async": "2.6.0", - "cssnano": "3.10.0", - "postcss": "5.2.18", - "rtlcss": "2.2.1", - "webpack-sources": "0.1.5" + "@romainberger/css-diff": "^1.0.3", + "async": "^2.0.0-rc.6", + "cssnano": "^3.7.1", + "postcss": "^5.0.21", + "rtlcss": "^2.0.4", + "webpack-sources": "^0.1.2" }, "dependencies": { "source-list-map": { @@ -15260,8 +15170,8 @@ "integrity": "sha1-qh86vw8NdNtxEcQOUAuE+WZkB1A=", "dev": true, "requires": { - "source-list-map": "0.1.8", - "source-map": "0.5.7" + "source-list-map": "~0.1.7", + "source-map": "~0.5.3" } } } @@ -15272,8 +15182,8 @@ "integrity": "sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw==", "dev": true, "requires": { - "source-list-map": "2.0.0", - "source-map": "0.6.1" + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" }, "dependencies": { "source-map": { @@ -15304,9 +15214,9 @@ "integrity": "sha512-Z0CVh/YE217Foyb488eo+iBv+r7eAQ0wSTyApi9n06jhcA3z6Nidg/EGvl0UFkg7kMdKxfBzzr+o9JF+cevgMg==", "dev": true, "requires": { - "lodash.sortby": "4.7.0", - "tr46": "1.0.1", - "webidl-conversions": "4.0.2" + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.0", + "webidl-conversions": "^4.0.1" } }, "whet.extend": { @@ -15320,7 +15230,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", "requires": { - "isexe": "2.0.0" + "isexe": "^2.0.0" } }, "which-module": { @@ -15334,7 +15244,7 @@ "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", "dev": true, "requires": { - "string-width": "1.0.2" + "string-width": "^1.0.2" }, "dependencies": { "string-width": { @@ -15343,9 +15253,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } } } @@ -15369,7 +15279,7 @@ "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==", "dev": true, "requires": { - "errno": "0.1.7" + "errno": "~0.1.7" } }, "wrap-ansi": { @@ -15377,8 +15287,8 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" }, "dependencies": { "string-width": { @@ -15386,9 +15296,9 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } } } @@ -15405,7 +15315,7 @@ "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", "dev": true, "requires": { - "mkdirp": "0.5.1" + "mkdirp": "^0.5.1" } }, "write-file-atomic": { @@ -15414,9 +15324,9 @@ "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "imurmurhash": "0.1.4", - "signal-exit": "3.0.2" + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" } }, "ws": { @@ -15425,9 +15335,9 @@ "integrity": "sha512-QYslsH44bH8O7/W2815u5DpnCpXWpEK44FmaHffNwgJI4JMaSZONgPBTOfrxJ29mXKbXak+LsJ2uAkDTYq2ptQ==", "dev": true, "requires": { - "async-limiter": "1.0.0", - "safe-buffer": "5.1.1", - "ultron": "1.1.1" + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" } }, "xml-name-validator": { @@ -15453,31 +15363,63 @@ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" }, "yargs": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", - "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", - "requires": { - "camelcase": "4.1.0", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "get-caller-file": "1.0.2", - "os-locale": "2.1.0", - "read-pkg-up": "2.0.0", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "2.1.1", - "which-module": "2.0.0", - "y18n": "3.2.1", - "yargs-parser": "7.0.0" + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-10.1.2.tgz", + "integrity": "sha512-ivSoxqBGYOqQVruxD35+EyCFDYNEFL/Uo6FcOnz+9xZdZzK0Zzw4r4KhbrME1Oo2gOggwJod2MnsdamSG7H9ig==", + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^8.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "requires": { + "locate-path": "^2.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + } } }, "yargs-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", - "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz", + "integrity": "sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==", "requires": { - "camelcase": "4.1.0" + "camelcase": "^4.1.0" } }, "yauzl": { @@ -15486,7 +15428,7 @@ "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", "dev": true, "requires": { - "fd-slicer": "1.0.1" + "fd-slicer": "~1.0.1" } }, "yeoman-environment": { @@ -15495,28 +15437,28 @@ "integrity": "sha512-6/W7/B54OPHJXob0n0+pmkwFsirC8cokuQkPSmT/D0lCcSxkKtg/BA6ZnjUBIwjuGqmw3DTrT4en++htaUju5g==", "dev": true, "requires": { - "chalk": "2.3.2", - "debug": "3.1.0", - "diff": "3.4.0", - "escape-string-regexp": "1.0.5", - "globby": "6.1.0", - "grouped-queue": "0.3.3", - "inquirer": "3.3.0", - "is-scoped": "1.0.0", - "lodash": "4.17.5", - "log-symbols": "2.2.0", - "mem-fs": "1.1.3", - "text-table": "0.2.0", - "untildify": "3.0.2" + "chalk": "^2.1.0", + "debug": "^3.1.0", + "diff": "^3.3.1", + "escape-string-regexp": "^1.0.2", + "globby": "^6.1.0", + "grouped-queue": "^0.3.3", + "inquirer": "^3.3.0", + "is-scoped": "^1.0.0", + "lodash": "^4.17.4", + "log-symbols": "^2.1.0", + "mem-fs": "^1.1.0", + "text-table": "^0.2.0", + "untildify": "^3.0.2" }, "dependencies": { "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -15525,15 +15467,15 @@ "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.3.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -15545,11 +15487,11 @@ "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "dev": true, "requires": { - "array-union": "1.0.2", - "glob": "7.1.2", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, "has-flag": { @@ -15564,7 +15506,7 @@ "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -15575,40 +15517,40 @@ "integrity": "sha512-mODmrZ26a94djmGZZuIiomSGlN4wULdou29ZwcySupb2e9FdvoCl7Ps2FqHFjEHio3kOl/iBeaNqrnx3C3NwWg==", "dev": true, "requires": { - "async": "2.6.0", - "chalk": "2.3.2", - "cli-table": "0.3.1", - "cross-spawn": "5.1.0", - "dargs": "5.1.0", - "dateformat": "3.0.3", - "debug": "3.1.0", - "detect-conflict": "1.0.1", - "error": "7.0.2", - "find-up": "2.1.0", - "github-username": "4.1.0", - "istextorbinary": "2.2.1", - "lodash": "4.17.5", - "make-dir": "1.2.0", - "mem-fs-editor": "3.0.2", - "minimist": "1.2.0", - "pretty-bytes": "4.0.2", - "read-chunk": "2.1.0", - "read-pkg-up": "3.0.0", - "rimraf": "2.6.2", - "run-async": "2.3.0", - "shelljs": "0.8.1", - "text-table": "0.2.0", - "through2": "2.0.3", - "yeoman-environment": "2.0.5" + "async": "^2.6.0", + "chalk": "^2.3.0", + "cli-table": "^0.3.1", + "cross-spawn": "^5.1.0", + "dargs": "^5.1.0", + "dateformat": "^3.0.2", + "debug": "^3.1.0", + "detect-conflict": "^1.0.0", + "error": "^7.0.2", + "find-up": "^2.1.0", + "github-username": "^4.0.0", + "istextorbinary": "^2.1.0", + "lodash": "^4.17.4", + "make-dir": "^1.1.0", + "mem-fs-editor": "^3.0.2", + "minimist": "^1.2.0", + "pretty-bytes": "^4.0.2", + "read-chunk": "^2.1.0", + "read-pkg-up": "^3.0.0", + "rimraf": "^2.6.2", + "run-async": "^2.0.0", + "shelljs": "^0.8.0", + "text-table": "^0.2.0", + "through2": "^2.0.0", + "yeoman-environment": "^2.0.5" }, "dependencies": { "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -15617,15 +15559,15 @@ "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.3.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -15637,7 +15579,7 @@ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "2.0.0" + "locate-path": "^2.0.0" } }, "has-flag": { @@ -15652,10 +15594,10 @@ "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "parse-json": "4.0.0", - "pify": "3.0.0", - "strip-bom": "3.0.0" + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" } }, "minimist": { @@ -15670,17 +15612,17 @@ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", "dev": true, "requires": { - "error-ex": "1.3.1", - "json-parse-better-errors": "1.0.1" + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" } }, "path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "integrity": "sha1-zvMdyOCho7sNEFwM2Xzzv0f0428=", "dev": true, "requires": { - "pify": "3.0.0" + "pify": "^3.0.0" } }, "pify": { @@ -15695,9 +15637,9 @@ "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", "dev": true, "requires": { - "load-json-file": "4.0.0", - "normalize-package-data": "2.4.0", - "path-type": "3.0.0" + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" } }, "read-pkg-up": { @@ -15706,8 +15648,8 @@ "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", "dev": true, "requires": { - "find-up": "2.1.0", - "read-pkg": "3.0.0" + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" } }, "supports-color": { @@ -15716,7 +15658,7 @@ "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } diff --git a/package.json b/package.json index 59c3a654f6bd9..a1e1534261bb3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "2.7.0", + "version": "2.8.0", "description": "A new WordPress editor experience", "main": "build/app.js", "repository": "git+https://github.com/WordPress/gutenberg.git", @@ -12,18 +12,20 @@ ], "engines": { "node": ">=8.0.0", - "npm": ">=5.0.0" + "npm": ">=6.0.0" }, "dependencies": { "@wordpress/a11y": "1.0.6", "@wordpress/autop": "1.0.4", + "@wordpress/dom-ready": "1.0.4", "@wordpress/hooks": "1.1.6", "@wordpress/i18n": "1.1.0", - "@wordpress/url": "1.0.3", + "@wordpress/is-shallow-equal": "1.0.1", + "@wordpress/url": "1.1.0", "@wordpress/wordcount": "1.0.0", "classnames": "2.2.5", "clipboard": "1.7.1", - "dom-react": "2.2.0", + "dom-react": "2.2.1", "dom-scroll-into-view": "1.2.1", "element-closest": "2.0.2", "equivalent-key-map": "0.1.1", @@ -44,15 +46,14 @@ "react-autosize-textarea": "3.0.2", "react-click-outside": "2.3.1", "react-color": "2.13.4", - "react-datepicker": "0.61.0", + "react-datepicker": "1.4.1", "react-dom": "16.3.0", "redux": "3.7.2", "redux-multi": "0.1.12", "redux-optimist": "1.0.0", "refx": "3.0.0", "rememo": "2.4.0", - "shallowequal": "1.0.2", - "showdown": "1.7.4", + "showdown": "1.8.6", "simple-html-tokenizer": "0.4.1", "tinycolor2": "1.4.1", "uuid": "3.1.0" diff --git a/phpunit/class-gutenberg-rest-api-test.php b/phpunit/class-gutenberg-rest-api-test.php index 322b5557f0169..b53a9c496c71f 100644 --- a/phpunit/class-gutenberg-rest-api-test.php +++ b/phpunit/class-gutenberg-rest-api-test.php @@ -21,6 +21,13 @@ function setUp() { $this->editor = $this->factory->user->create( array( 'role' => 'editor', ) ); + $this->subscriber = $this->factory->user->create( + array( + 'role' => 'subscriber', + 'display_name' => 'subscriber', + 'user_email' => 'subscriber@example.com', + ) + ); } function tearDown() { @@ -120,6 +127,35 @@ function test_viewable_field_without_context() { $this->assertFalse( isset( $result['viewable'] ) ); } + /** + * Only returns wp:action-sticky when current user can sticky. + */ + function test_link_sticky_only_appears_for_editor() { + $post_id = $this->factory->post->create(); + $check_key = 'https://api.w.org/action-sticky'; + // authors cannot sticky. + wp_set_current_user( $this->author ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . $post_id ); + $request->set_param( 'context', 'edit' ); + $response = rest_do_request( $request ); + $links = $response->get_links(); + $this->assertFalse( isset( $links[ $check_key ] ) ); + // editors can sticky. + wp_set_current_user( $this->editor ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . $post_id ); + $request->set_param( 'context', 'edit' ); + $response = rest_do_request( $request ); + $links = $response->get_links(); + $this->assertTrue( isset( $links[ $check_key ] ) ); + // editors can sticky but not included for context != edit. + wp_set_current_user( $this->editor ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . $post_id ); + $request->set_param( 'context', 'view' ); + $response = rest_do_request( $request ); + $links = $response->get_links(); + $this->assertFalse( isset( $links[ $check_key ] ) ); + } + /** * Should include relevant data in the 'theme_supports' key of index. */ @@ -131,4 +167,35 @@ function test_theme_supports_index() { $this->assertTrue( isset( $result['theme_supports']['formats'] ) ); $this->assertTrue( in_array( 'standard', $result['theme_supports']['formats'] ) ); } + + public function test_get_items_who_author_query() { + wp_set_current_user( $this->administrator ); + // First request should include subscriber in the set. + $request = new WP_REST_Request( 'GET', '/wp/v2/users' ); + $request->set_param( 'search', 'subscriber' ); + $response = rest_get_server()->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + $this->assertCount( 1, $response->get_data() ); + // Second request should exclude subscriber. + $request = new WP_REST_Request( 'GET', '/wp/v2/users' ); + $request->set_param( 'who', 'authors' ); + $request->set_param( 'search', 'subscriber' ); + $response = rest_get_server()->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + $this->assertCount( 0, $response->get_data() ); + } + + /** + * Any user with 'edit_posts' on a show_in_rest post type + * can view authors. Others (e.g. subscribers) cannot. + */ + public function test_get_items_who_unauthorized_query() { + wp_set_current_user( $this->subscriber ); + $request = new WP_REST_Request( 'GET', '/wp/v2/users' ); + $request->set_param( 'who', 'authors' ); + $response = rest_get_server()->dispatch( $request ); + $this->assertEquals( 403, $response->get_status() ); + $data = $response->get_data(); + $this->assertEquals( 'rest_forbidden_who', $data['code'] ); + } } diff --git a/phpunit/class-rest-block-renderer-controller-test.php b/phpunit/class-rest-block-renderer-controller-test.php index 338736c394834..ba5ea946df813 100644 --- a/phpunit/class-rest-block-renderer-controller-test.php +++ b/phpunit/class-rest-block-renderer-controller-test.php @@ -159,7 +159,7 @@ public function test_register_routes() { $dynamic_block_names = get_dynamic_block_names(); $this->assertContains( self::$block_name, $dynamic_block_names ); - $routes = $this->server->get_routes(); + $routes = rest_get_server()->get_routes(); foreach ( $dynamic_block_names as $dynamic_block_name ) { $this->assertArrayHasKey( "/gutenberg/v1/block-renderer/(?P<name>$dynamic_block_name)", $routes ); } @@ -176,7 +176,7 @@ public function test_get_item_without_permissions() { $request = new WP_REST_Request( 'GET', '/gutenberg/v1/block-renderer/' . self::$block_name ); $request->set_param( 'context', 'edit' ); - $response = $this->server->dispatch( $request ); + $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'gutenberg_block_cannot_read', $response, rest_authorization_required_code() ); } @@ -188,7 +188,7 @@ public function test_get_item_with_invalid_context() { wp_set_current_user( self::$user_id ); $request = new WP_REST_Request( 'GET', '/gutenberg/v1/block-renderer/' . self::$block_name ); - $response = $this->server->dispatch( $request ); + $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); } @@ -203,7 +203,7 @@ public function test_get_item_invalid_block_name() { $request = new WP_REST_Request( 'GET', '/gutenberg/v1/block-renderer/core/123' ); $request->set_param( 'context', 'edit' ); - $response = $this->server->dispatch( $request ); + $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_no_route', $response, 404 ); } @@ -220,7 +220,7 @@ public function test_get_item_invalid_attribute() { $request->set_param( 'attributes', array( 'some_string' => array( 'no!' ), ) ); - $response = $this->server->dispatch( $request ); + $response = rest_get_server()->dispatch( $request ); $this->assertEquals( 400, $response->get_status() ); } @@ -236,7 +236,7 @@ public function test_get_item_unrecognized_attribute() { $request->set_param( 'attributes', array( 'unrecognized' => 'yes', ) ); - $response = $this->server->dispatch( $request ); + $response = rest_get_server()->dispatch( $request ); $this->assertEquals( 400, $response->get_status() ); } @@ -257,7 +257,7 @@ public function test_get_item_default_attributes() { $request = new WP_REST_Request( 'GET', '/gutenberg/v1/block-renderer/' . self::$block_name ); $request->set_param( 'context', 'edit' ); $request->set_param( 'attributes', array() ); - $response = $this->server->dispatch( $request ); + $response = rest_get_server()->dispatch( $request ); $this->assertEquals( 200, $response->get_status() ); $data = $response->get_data(); @@ -290,7 +290,7 @@ public function test_get_item() { $request = new WP_REST_Request( 'GET', '/gutenberg/v1/block-renderer/' . self::$block_name ); $request->set_param( 'context', 'edit' ); $request->set_param( 'attributes', $attributes ); - $response = $this->server->dispatch( $request ); + $response = rest_get_server()->dispatch( $request ); $this->assertEquals( 200, $response->get_status() ); $data = $response->get_data(); @@ -312,7 +312,7 @@ public function test_get_item_with_post_context() { $request->set_param( 'context', 'edit' ); // Test without post ID. - $response = $this->server->dispatch( $request ); + $response = rest_get_server()->dispatch( $request ); $this->assertEquals( 200, $response->get_status() ); $data = $response->get_data(); @@ -321,7 +321,7 @@ public function test_get_item_with_post_context() { // Now test with post ID. $request->set_param( 'post_id', self::$post_id ); - $response = $this->server->dispatch( $request ); + $response = rest_get_server()->dispatch( $request ); $this->assertEquals( 200, $response->get_status() ); $data = $response->get_data(); @@ -340,7 +340,7 @@ public function test_get_item_without_permissions_invalid_post() { // Test with invalid post ID. $request->set_param( 'post_id', PHP_INT_MAX ); - $response = $this->server->dispatch( $request ); + $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'gutenberg_block_cannot_read', $response, 403 ); } @@ -356,7 +356,7 @@ public function test_get_item_without_permissions_cannot_edit_post() { // Test with private post ID. $request->set_param( 'post_id', self::$post_id ); - $response = $this->server->dispatch( $request ); + $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'gutenberg_block_cannot_read', $response, 403 ); } @@ -368,7 +368,7 @@ public function test_get_item_without_permissions_cannot_edit_post() { */ public function test_get_item_schema() { $request = new WP_REST_Request( 'OPTIONS', '/gutenberg/v1/block-renderer/' . self::$block_name ); - $response = $this->server->dispatch( $request ); + $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $this->assertEqualSets( array( 'GET' ), $data['endpoints'][0]['methods'] ); diff --git a/phpunit/class-rest-blocks-controller-test.php b/phpunit/class-rest-blocks-controller-test.php index bafcb2b31b6a7..5c81f1278878f 100644 --- a/phpunit/class-rest-blocks-controller-test.php +++ b/phpunit/class-rest-blocks-controller-test.php @@ -59,7 +59,7 @@ public static function wpTearDownAfterClass() { * Check that our routes get set up properly. */ public function test_register_routes() { - $routes = $this->server->get_routes(); + $routes = rest_get_server()->get_routes(); $this->assertArrayHasKey( '/wp/v2/blocks', $routes ); $this->assertCount( 2, $routes['/wp/v2/blocks'] ); @@ -74,7 +74,7 @@ public function test_get_items() { wp_set_current_user( self::$user_id ); $request = new WP_REST_Request( 'GET', '/wp/v2/blocks' ); - $response = $this->server->dispatch( $request ); + $response = rest_get_server()->dispatch( $request ); $this->assertEquals( 200, $response->get_status() ); $this->assertEquals( @@ -95,7 +95,7 @@ public function test_get_item() { wp_set_current_user( self::$user_id ); $request = new WP_REST_Request( 'GET', '/wp/v2/blocks/' . self::$post_id ); - $response = $this->server->dispatch( $request ); + $response = rest_get_server()->dispatch( $request ); $this->assertEquals( 200, $response->get_status() ); $this->assertEquals( @@ -121,7 +121,7 @@ public function test_create_item() { ) ); - $response = $this->server->dispatch( $request ); + $response = rest_get_server()->dispatch( $request ); $this->assertEquals( 200, $response->get_status() ); @@ -150,7 +150,7 @@ public function test_update_item() { ) ); - $response = $this->server->dispatch( $request ); + $response = rest_get_server()->dispatch( $request ); $this->assertEquals( 200, $response->get_status() ); @@ -173,7 +173,7 @@ public function test_delete_item() { $request = new WP_REST_Request( 'DELETE', '/wp/v2/blocks/' . self::$post_id ); - $response = $this->server->dispatch( $request ); + $response = rest_get_server()->dispatch( $request ); $this->assertEquals( 200, $response->get_status() ); @@ -198,7 +198,7 @@ public function test_delete_item() { */ public function test_get_item_schema() { $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/blocks' ); - $response = $this->server->dispatch( $request ); + $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; @@ -258,7 +258,7 @@ public function test_capabilities( $action, $role, $expected_status ) { ) ); - $response = $this->server->dispatch( $request ); + $response = rest_get_server()->dispatch( $request ); $this->assertEquals( $expected_status, $response->get_status() ); break; @@ -266,7 +266,7 @@ public function test_capabilities( $action, $role, $expected_status ) { case 'read': $request = new WP_REST_Request( 'GET', '/wp/v2/blocks/' . self::$post_id ); - $response = $this->server->dispatch( $request ); + $response = rest_get_server()->dispatch( $request ); $this->assertEquals( $expected_status, $response->get_status() ); break; @@ -290,12 +290,12 @@ public function test_capabilities( $action, $role, $expected_status ) { ) ); - $response = $this->server->dispatch( $request ); + $response = rest_get_server()->dispatch( $request ); $this->assertEquals( $expected_status, $response->get_status() ); $request = new WP_REST_Request( 'DELETE', '/wp/v2/blocks/' . $post_id ); - $response = $this->server->dispatch( $request ); + $response = rest_get_server()->dispatch( $request ); $this->assertEquals( $expected_status, $response->get_status() ); wp_delete_post( $post_id ); @@ -311,12 +311,12 @@ public function test_capabilities( $action, $role, $expected_status ) { ) ); - $response = $this->server->dispatch( $request ); + $response = rest_get_server()->dispatch( $request ); $this->assertEquals( $expected_status, $response->get_status() ); $request = new WP_REST_Request( 'DELETE', '/wp/v2/blocks/' . self::$post_id ); - $response = $this->server->dispatch( $request ); + $response = rest_get_server()->dispatch( $request ); $this->assertEquals( $expected_status, $response->get_status() ); break; diff --git a/post-content.js b/post-content.js index fe042e409ed4d..d556c8c74df1f 100644 --- a/post-content.js +++ b/post-content.js @@ -72,12 +72,7 @@ window._wpGutenbergPost.content = { '<!-- /wp:paragraph -->', '<!-- wp:quote {"style":1} -->', - '<blockquote class="wp-block-quote">', - '<!-- wp:paragraph -->', - '<p>The editor will endeavour to create a new page and post building experience that makes writing rich posts effortless, and has “blocks” to make it easy what today might take shortcodes, custom HTML, or “mystery meat” embed discovery.</p>', - '<!-- /wp:paragraph -->', - '<cite>Matt Mullenweg, 2017</cite>', - '</blockquote>', + '<blockquote class="wp-block-quote"><p>The editor will endeavour to create a new page and post building experience that makes writing rich posts effortless, and has “blocks” to make it easy what today might take shortcodes, custom HTML, or “mystery meat” embed discovery.</p><cite>Matt Mullenweg, 2017</cite></blockquote>', '<!-- /wp:quote -->', '<!-- wp:paragraph -->', @@ -140,12 +135,7 @@ window._wpGutenbergPost.content = { '<!-- /wp:paragraph -->', '<!-- wp:pullquote -->', - '<blockquote class="wp-block-pullquote alignnone">', - '<!-- wp:paragraph -->', - '<p>Code is Poetry</p>', - '<!-- /wp:paragraph -->', - '<cite>The WordPress community</cite>', - '</blockquote>', + '<blockquote class="wp-block-pullquote alignnone"><p>Code is Poetry</p><cite>The WordPress community</cite></blockquote>', '<!-- /wp:pullquote -->', '<!-- wp:paragraph {"align":"center"} -->', diff --git a/test/e2e/specs/__snapshots__/adding-blocks.test.js.snap b/test/e2e/specs/__snapshots__/adding-blocks.test.js.snap index c705e9d9ae54b..adcd480327880 100644 --- a/test/e2e/specs/__snapshots__/adding-blocks.test.js.snap +++ b/test/e2e/specs/__snapshots__/adding-blocks.test.js.snap @@ -11,13 +11,11 @@ exports[`adding blocks Should insert content using the placeholder and the regul <!-- wp:quote --> <blockquote class=\\"wp-block-quote\\"> - <!-- wp:paragraph --> <p>Quote block</p> - <!-- /wp:paragraph --> - - <!-- wp:code --> - <pre class=\\"wp-block-code\\"><code>Code block</code></pre> - <!-- /wp:code --> </blockquote> -<!-- /wp:quote -->" +<!-- /wp:quote --> + +<!-- wp:code --> +<pre class=\\"wp-block-code\\"><code>Code block</code></pre> +<!-- /wp:code -->" `; diff --git a/test/e2e/specs/__snapshots__/splitting-merging.test.js.snap b/test/e2e/specs/__snapshots__/splitting-merging.test.js.snap index e5a7168ef92c1..98d163a480fbd 100644 --- a/test/e2e/specs/__snapshots__/splitting-merging.test.js.snap +++ b/test/e2e/specs/__snapshots__/splitting-merging.test.js.snap @@ -15,17 +15,3 @@ exports[`splitting and merging blocks Should split and merge paragraph blocks us <p>FirstSecond</p> <!-- /wp:paragraph -->" `; - -exports[`splitting and merging blocks should split out of quote block using enter 1`] = ` -"<!-- wp:quote --> -<blockquote class=\\"wp-block-quote\\"> - <!-- wp:paragraph --> - <p>test</p> - <!-- /wp:paragraph --> -</blockquote> -<!-- /wp:quote --> - -<!-- wp:paragraph --> -<p></p> -<!-- /wp:paragraph -->" -`; diff --git a/test/e2e/specs/a11y.test.js b/test/e2e/specs/a11y.test.js index c90224fadd09d..fe0c948be2a5e 100644 --- a/test/e2e/specs/a11y.test.js +++ b/test/e2e/specs/a11y.test.js @@ -2,7 +2,7 @@ * Internal dependencies */ import '../support/bootstrap'; -import { newPost, newDesktopBrowserPage } from '../support/utils'; +import { newPost, newDesktopBrowserPage, pressWithModifier } from '../support/utils'; describe( 'a11y', () => { beforeAll( async () => { @@ -11,9 +11,7 @@ describe( 'a11y', () => { } ); it( 'tabs header bar', async () => { - await page.keyboard.down( 'Control' ); - await page.keyboard.press( '~' ); - await page.keyboard.up( 'Control' ); + await pressWithModifier( 'Control', '~' ); await page.keyboard.press( 'Tab' ); diff --git a/test/e2e/specs/adding-blocks.test.js b/test/e2e/specs/adding-blocks.test.js index 71cfd11ee2204..973063c4613f4 100644 --- a/test/e2e/specs/adding-blocks.test.js +++ b/test/e2e/specs/adding-blocks.test.js @@ -2,7 +2,7 @@ * Internal dependencies */ import '../support/bootstrap'; -import { newPost, newDesktopBrowserPage, getHTMLFromCodeEditor } from '../support/utils'; +import { newPost, newDesktopBrowserPage } from '../support/utils'; describe( 'adding blocks', () => { beforeAll( async () => { @@ -85,6 +85,14 @@ describe( 'adding blocks', () => { await clickAtRightish( inserter ); await page.keyboard.type( 'Second paragraph' ); - expect( await getHTMLFromCodeEditor() ).toMatchSnapshot(); + // Switch to Text Mode to check HTML Output + await page.click( '.edit-post-more-menu [aria-label="More"]' ); + const codeEditorButton = ( await page.$x( '//button[contains(text(), \'Code Editor\')]' ) )[ 0 ]; + await codeEditorButton.click( 'button' ); + + // Assertions + const textEditorContent = await page.$eval( '.editor-post-text-editor', ( element ) => element.value ); + + expect( textEditorContent ).toMatchSnapshot(); } ); } ); diff --git a/test/e2e/specs/change-detection.test.js b/test/e2e/specs/change-detection.test.js new file mode 100644 index 0000000000000..a3c08a51cb784 --- /dev/null +++ b/test/e2e/specs/change-detection.test.js @@ -0,0 +1,179 @@ +/** + * Internal dependencies + */ +import '../support/bootstrap'; +import { newPost, newDesktopBrowserPage, pressWithModifier } from '../support/utils'; + +describe( 'Change detection', () => { + let handleInterceptedRequest; + + beforeAll( async () => { + await newDesktopBrowserPage(); + } ); + + beforeEach( async () => { + await newPost(); + } ); + + async function assertIsDirty( isDirty ) { + let hadDialog = false; + + function handleOnDialog( dialog ) { + dialog.accept(); + hadDialog = true; + } + + try { + page.on( 'dialog', handleOnDialog ); + await page.reload(); + + // Ensure whether it was expected that dialog was encountered. + expect( hadDialog ).toBe( isDirty ); + } catch ( error ) { + throw error; + } finally { + page.removeListener( 'dialog', handleOnDialog ); + } + } + + async function interceptSave() { + await page.setRequestInterception( true ); + + handleInterceptedRequest = ( interceptedRequest ) => { + if ( ! interceptedRequest.url().includes( '/wp/v2/posts' ) ) { + interceptedRequest.continue(); + } + }; + page.on( 'request', handleInterceptedRequest ); + } + + async function releaseSaveIntercept() { + page.removeListener( 'request', handleInterceptedRequest ); + await page.setRequestInterception( false ); + } + + it( 'Should not prompt to confirm unsaved changes', async () => { + await assertIsDirty( false ); + } ); + + it( 'Should prompt if property changed without save', async () => { + await page.type( '.editor-post-title__input', 'Hello World' ); + + await assertIsDirty( true ); + } ); + + it( 'Should prompt if content added without save', async () => { + await page.click( '.editor-default-block-appender' ); + + await assertIsDirty( true ); + } ); + + it( 'Should not prompt if changes saved', async () => { + await page.type( '.editor-post-title__input', 'Hello World' ); + + await Promise.all( [ + // Wait for "Saved" to confirm save complete. + page.waitForSelector( '.editor-post-saved-state.is-saved' ), + + // Keyboard shortcut Ctrl+S save. + pressWithModifier( 'Mod', 'S' ), + ] ); + + await assertIsDirty( false ); + } ); + + it( 'Should prompt if save failed', async () => { + await page.type( '.editor-post-title__input', 'Hello World' ); + + await page.setOfflineMode( true ); + + // Keyboard shortcut Ctrl+S save. + await pressWithModifier( 'Mod', 'S' ); + + // Ensure save update fails and presents button. + await page.waitForXPath( '//p[contains(text(), \'Updating failed\')]' ); + await page.waitForSelector( '.editor-post-save-draft' ); + + // Need to disable offline to allow reload. + await page.setOfflineMode( false ); + + await assertIsDirty( true ); + } ); + + it( 'Should prompt if changes and save is in-flight', async () => { + await page.type( '.editor-post-title__input', 'Hello World' ); + + // Hold the posts request so we don't deal with race conditions of the + // save completing early. Other requests should be allowed to continue, + // for example the page reload test. + await interceptSave(); + + // Keyboard shortcut Ctrl+S save. + await pressWithModifier( 'Mod', 'S' ); + + await releaseSaveIntercept(); + + await assertIsDirty( true ); + } ); + + it( 'Should prompt if changes made while save is in-flight', async () => { + await page.type( '.editor-post-title__input', 'Hello World' ); + + // Hold the posts request so we don't deal with race conditions of the + // save completing early. Other requests should be allowed to continue, + // for example the page reload test. + await interceptSave(); + + // Keyboard shortcut Ctrl+S save. + await pressWithModifier( 'Mod', 'S' ); + + await page.type( '.editor-post-title__input', '!' ); + + await releaseSaveIntercept(); + + await assertIsDirty( true ); + } ); + + it( 'Should prompt if property changes made while save is in-flight, and save completes', async () => { + await page.type( '.editor-post-title__input', 'Hello World' ); + + // Hold the posts request so we don't deal with race conditions of the + // save completing early. + await interceptSave(); + + // Keyboard shortcut Ctrl+S save. + await pressWithModifier( 'Mod', 'S' ); + + // Dirty post while save is in-flight. + await page.type( '.editor-post-title__input', '!' ); + + // Allow save to complete. Disabling interception flushes pending. + await Promise.all( [ + page.waitForSelector( '.editor-post-saved-state.is-saved' ), + releaseSaveIntercept(), + ] ); + + await assertIsDirty( true ); + } ); + + it( 'Should prompt if block revision is made while save is in-flight, and save completes', async () => { + await page.type( '.editor-post-title__input', 'Hello World' ); + + // Hold the posts request so we don't deal with race conditions of the + // save completing early. + await interceptSave(); + + // Keyboard shortcut Ctrl+S save. + await pressWithModifier( 'Mod', 'S' ); + + await page.click( '.editor-default-block-appender' ); + + // Allow save to complete. Disabling interception flushes pending. + await Promise.all( [ + page.waitForSelector( '.editor-post-saved-state.is-saved' ), + releaseSaveIntercept(), + ] ); + + await assertIsDirty( true ); + } ); +} ); diff --git a/test/e2e/specs/hello.test.js b/test/e2e/specs/hello.test.js index 4a9fcfe8554c7..ff6653e7d3e0f 100644 --- a/test/e2e/specs/hello.test.js +++ b/test/e2e/specs/hello.test.js @@ -2,7 +2,7 @@ * Internal dependencies */ import '../support/bootstrap'; -import { newPost, visitAdmin, newDesktopBrowserPage } from '../support/utils'; +import { newPost, newDesktopBrowserPage } from '../support/utils'; describe( 'hello', () => { beforeAll( async () => { @@ -25,9 +25,4 @@ describe( 'hello', () => { expect( undoButton ).toBeNull(); expect( redoButton ).toBeNull(); } ); - - it( 'Should not prompt to confirm unsaved changes', async () => { - await visitAdmin( 'edit.php' ); - expect( page.url() ).not.toEqual( expect.stringContaining( 'post-new.php' ) ); - } ); } ); diff --git a/test/e2e/specs/managing-links.test.js b/test/e2e/specs/managing-links.test.js index a88766b3b528f..e1c8672a45be8 100644 --- a/test/e2e/specs/managing-links.test.js +++ b/test/e2e/specs/managing-links.test.js @@ -32,12 +32,12 @@ describe( 'Managing links', () => { // Typing "left" should not close the dialog await page.keyboard.press( 'ArrowLeft' ); - let modal = await page.$( '.blocks-format-toolbar__link-modal' ); + let modal = await page.$( '.editor-format-toolbar__link-modal' ); expect( modal ).not.toBeNull(); // Escape should close the dialog still. await page.keyboard.press( 'Escape' ); - modal = await page.$( '.blocks-format-toolbar__link-modal' ); + modal = await page.$( '.editor-format-toolbar__link-modal' ); expect( modal ).toBeNull(); } ); @@ -55,12 +55,12 @@ describe( 'Managing links', () => { // Typing "left" should not close the dialog await page.keyboard.press( 'ArrowLeft' ); - let modal = await page.$( '.blocks-format-toolbar__link-modal' ); + let modal = await page.$( '.editor-format-toolbar__link-modal' ); expect( modal ).not.toBeNull(); // Escape should close the dialog still. await page.keyboard.press( 'Escape' ); - modal = await page.$( '.blocks-format-toolbar__link-modal' ); + modal = await page.$( '.editor-format-toolbar__link-modal' ); expect( modal ).toBeNull(); } ); } ); diff --git a/test/e2e/specs/multi-block-selection.test.js b/test/e2e/specs/multi-block-selection.test.js index 85eba730bed69..064540406b4f1 100644 --- a/test/e2e/specs/multi-block-selection.test.js +++ b/test/e2e/specs/multi-block-selection.test.js @@ -2,7 +2,7 @@ * Internal dependencies */ import '../support/bootstrap'; -import { newPost, newDesktopBrowserPage } from '../support/utils'; +import { newPost, newDesktopBrowserPage, pressWithModifier } from '../support/utils'; describe( 'Multi-block selection', () => { beforeAll( async () => { @@ -13,7 +13,7 @@ describe( 'Multi-block selection', () => { it( 'Should select/unselect multiple blocks', async () => { const firstBlockSelector = '[data-type="core/paragraph"]'; const secondBlockSelector = '[data-type="core/image"]'; - const thirdBlockSelector = '[data-type="core/list"]'; + const thirdBlockSelector = '[data-type="core/quote"]'; const multiSelectedCssClass = 'is-multi-selected'; // Creating test blocks @@ -24,10 +24,10 @@ describe( 'Multi-block selection', () => { await page.keyboard.press( 'Tab' ); await page.keyboard.press( 'Enter' ); await page.click( '.edit-post-header [aria-label="Add block"]' ); - await page.keyboard.type( 'List' ); + await page.keyboard.type( 'Quote' ); await page.keyboard.press( 'Tab' ); await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'List Block' ); + await page.keyboard.type( 'Quote Block' ); const blocks = [ firstBlockSelector, secondBlockSelector, thirdBlockSelector ]; const expectMultiSelected = ( selectors, areMultiSelected ) => { @@ -62,9 +62,7 @@ describe( 'Multi-block selection', () => { // Multiselect via keyboard await page.click( 'body' ); - await page.keyboard.down( 'Meta' ); - await page.keyboard.press( 'a' ); - await page.keyboard.up( 'Meta' ); + await pressWithModifier( 'Mod', 'a' ); // Verify selection expectMultiSelected( blocks, true ); diff --git a/test/e2e/specs/splitting-merging.test.js b/test/e2e/specs/splitting-merging.test.js index e11d5682bddbd..4e78b0366eba2 100644 --- a/test/e2e/specs/splitting-merging.test.js +++ b/test/e2e/specs/splitting-merging.test.js @@ -2,10 +2,10 @@ * Internal dependencies */ import '../support/bootstrap'; -import { newPost, newDesktopBrowserPage, getHTMLFromCodeEditor } from '../support/utils'; +import { newPost, newDesktopBrowserPage } from '../support/utils'; describe( 'splitting and merging blocks', () => { - beforeEach( async () => { + beforeAll( async () => { await newDesktopBrowserPage(); await newPost(); } ); @@ -24,26 +24,32 @@ describe( 'splitting and merging blocks', () => { } await page.keyboard.press( 'Enter' ); - expect( await getHTMLFromCodeEditor() ).toMatchSnapshot(); + //Switch to Code Editor to check HTML output + await page.click( '.edit-post-more-menu [aria-label="More"]' ); + let codeEditorButton = ( await page.$x( '//button[contains(text(), \'Code Editor\')]' ) )[ 0 ]; + await codeEditorButton.click( 'button' ); + + //Assert that there are now two paragraph blocks with correct content + let textEditorContent = await page.$eval( '.editor-post-text-editor', ( element ) => element.value ); + expect( textEditorContent ).toMatchSnapshot(); + + //Switch to Visual Editor to continue testing + await page.click( '.edit-post-more-menu [aria-label="More"]' ); + const visualEditorButton = ( await page.$x( '//button[contains(text(), \'Visual Editor\')]' ) )[ 0 ]; + await visualEditorButton.click( 'button' ); //Press Backspace to merge paragraph blocks await page.click( '.is-selected' ); await page.keyboard.press( 'Home' ); await page.keyboard.press( 'Backspace' ); - expect( await getHTMLFromCodeEditor() ).toMatchSnapshot(); - } ); - - it( 'should split out of quote block using enter', async () => { - //Use regular inserter to add paragraph block and text - await page.click( '.edit-post-header [aria-label="Add block"]' ); - await page.keyboard.type( 'quote' ); - await page.keyboard.press( 'Tab' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'test' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.press( 'Enter' ); + //Switch to Code Editor to check HTML output + await page.click( '.edit-post-more-menu [aria-label="More"]' ); + codeEditorButton = ( await page.$x( '//button[contains(text(), \'Code Editor\')]' ) )[ 0 ]; + await codeEditorButton.click( 'button' ); - expect( await getHTMLFromCodeEditor() ).toMatchSnapshot(); + //Assert that there is now one paragraph with correct content + textEditorContent = await page.$eval( '.editor-post-text-editor', ( element ) => element.value ); + expect( textEditorContent ).toMatchSnapshot(); } ); } ); diff --git a/test/e2e/specs/templates.test.js b/test/e2e/specs/templates.test.js index 925e589e0a41e..15d86445d7f24 100644 --- a/test/e2e/specs/templates.test.js +++ b/test/e2e/specs/templates.test.js @@ -2,7 +2,7 @@ * Internal dependencies */ import '../support/bootstrap'; -import { newPost, newDesktopBrowserPage, getHTMLFromCodeEditor } from '../support/utils'; +import { newPost, newDesktopBrowserPage } from '../support/utils'; import { activatePlugin, deactivatePlugin } from '../support/plugins'; describe( 'Using a CPT with a predefined template', () => { @@ -18,6 +18,13 @@ describe( 'Using a CPT with a predefined template', () => { } ); it( 'Should add a custom post types with a predefined template', async () => { - expect( await getHTMLFromCodeEditor() ).toMatchSnapshot(); + //Switch to Code Editor to check HTML output + await page.click( '.edit-post-more-menu [aria-label="More"]' ); + const codeEditorButton = ( await page.$x( '//button[contains(text(), \'Code Editor\')]' ) )[ 0 ]; + await codeEditorButton.click( 'button' ); + + // Assert that the post already contains the template defined blocks + const textEditorContent = await page.$eval( '.editor-post-text-editor', ( element ) => element.value ); + expect( textEditorContent ).toMatchSnapshot(); } ); } ); diff --git a/test/e2e/support/utils.js b/test/e2e/support/utils.js index 7dce353914360..1f6f0950323aa 100644 --- a/test/e2e/support/utils.js +++ b/test/e2e/support/utils.js @@ -10,6 +10,15 @@ const { WP_PASSWORD = 'password', } = process.env; +/** + * Platform-specific modifier key. + * + * @see pressWithModifier + * + * @type {string} + */ +const MOD_KEY = process.platform === 'darwin' ? 'Meta' : 'Control'; + function getUrl( WPPath, query = '' ) { const url = new URL( WP_BASE_URL ); @@ -71,3 +80,22 @@ export async function getHTMLFromCodeEditor() { await switchToEditor( 'Visual' ); return textEditorContent; } + +/** + * Performs a key press with modifier (Shift, Control, Meta, Mod), where "Mod" + * is normalized to platform-specific modifier (Meta in MacOS, else Control). + * + * @param {string} modifier Modifier key. + * @param {string} key Key to press while modifier held. + * + * @return {Promise} Promise resolving when key combination pressed. + */ +export async function pressWithModifier( modifier, key ) { + if ( modifier.toLowerCase() === 'mod' ) { + modifier = MOD_KEY; + } + + await page.keyboard.down( modifier ); + await page.keyboard.press( key ); + return page.keyboard.up( modifier ); +} diff --git a/test/unit/jest.config.json b/test/unit/jest.config.json index 3e309518c9f76..76712378953fc 100644 --- a/test/unit/jest.config.json +++ b/test/unit/jest.config.json @@ -10,7 +10,8 @@ "setupFiles": [ "core-js/fn/symbol/async-iterator", "<rootDir>/test/unit/setup-blocks.js", - "<rootDir>/test/unit/setup-wp-aliases.js" + "<rootDir>/test/unit/setup-wp-aliases.js", + "<rootDir>/test/unit/setup-mocks.js" ], "transform": { "\\.pegjs$": "<rootDir>/test/unit/pegjs-transform.js" diff --git a/test/unit/setup-mocks.js b/test/unit/setup-mocks.js new file mode 100644 index 0000000000000..9e6eaec2d1811 --- /dev/null +++ b/test/unit/setup-mocks.js @@ -0,0 +1,16 @@ +// [TEMPORARY]: Button uses React.forwardRef, added in react@16.3.0 but not yet +// supported by Enzyme as of enzyme-adapter-react-16@1.1.1 . This mock unwraps +// the ref forwarding, so any tests relying on this behavior will fail. +// +// See: https://github.com/airbnb/enzyme/issues/1604 +// See: https://github.com/airbnb/enzyme/pull/1592/files +jest.mock( '../../components/button', () => { + const { Button: RawButton } = require.requireActual( '../../components/button' ); + const { Component } = require( 'react' ); + + return class Button extends Component { + render() { + return RawButton( this.props ); + } + }; +} ); diff --git a/utils/dom.js b/utils/dom.js index 36b407bfe4106..f0dbeb5c5ae8f 100644 --- a/utils/dom.js +++ b/utils/dom.js @@ -251,6 +251,13 @@ function caretRangeFromPoint( doc, x, y ) { } const point = doc.caretPositionFromPoint( x, y ); + + // If x or y are negative, outside viewport, or there is no text entry node. + // https://developer.mozilla.org/en-US/docs/Web/API/Document/caretRangeFromPoint + if ( ! point ) { + return null; + } + const range = doc.createRange(); range.setStart( point.offsetNode, point.offset ); @@ -446,3 +453,41 @@ export function remove( node ) { export function insertAfter( newNode, referenceNode ) { referenceNode.parentNode.insertBefore( newNode, referenceNode.nextSibling ); } + +/** + * Unwrap the given node. This means any child nodes are moved to the parent. + * + * @param {Node} node The node to unwrap. + * + * @return {void} + */ +export function unwrap( node ) { + const parent = node.parentNode; + + while ( node.firstChild ) { + parent.insertBefore( node.firstChild, node ); + } + + parent.removeChild( node ); +} + +/** + * Replaces the given node with a new node with the given tag name. + * + * @param {Element} node The node to replace + * @param {string} tagName The new tag name. + * @param {Document} doc The document of the node. + * + * @return {Element} The new node. + */ +export function replaceTag( node, tagName, doc ) { + const newNode = doc.createElement( tagName ); + + while ( node.firstChild ) { + newNode.appendChild( node.firstChild ); + } + + node.parentNode.replaceChild( newNode, node ); + + return newNode; +} diff --git a/utils/keycodes.js b/utils/keycodes.js index 6dfa5c7d61120..40274e25bfa6c 100644 --- a/utils/keycodes.js +++ b/utils/keycodes.js @@ -1,3 +1,19 @@ +/** + * Note: The order of the modifier keys in many of the [foo]Shortcut() + * functions in this file are intentional and should not be changed. They're + * designed to fit with the standard menu keyboard shortcuts shown in the + * user's platform. + * + * For example, on MacOS menu shortcuts will place Shift before Command, but + * on Windows Control will usually come first. So don't provide your own + * shortcut combos directly to keyboardShortcut(). + */ + +/** + * External dependencies + */ +import { get, mapValues } from 'lodash'; + export const BACKSPACE = 8; export const TAB = 9; export const ENTER = 13; @@ -10,3 +26,67 @@ export const DOWN = 40; export const DELETE = 46; export const F10 = 121; + +export const ALT = 'alt'; +export const CTRL = 'ctrl'; +// Understood in both Mousetrap and TinyMCE. +export const COMMAND = 'meta'; +export const SHIFT = 'shift'; + +/** + * Return true if platform is MacOS. + * + * @param {Object} _window window object by default; used for DI testing. + * + * @return {boolean} True if MacOS; false otherwise. + */ +export function isMacOS( _window = window ) { + return _window.navigator.platform.indexOf( 'Mac' ) !== -1; +} + +const modifiers = { + primary: ( _isMac ) => _isMac() ? [ COMMAND ] : [ CTRL ], + primaryShift: ( _isMac ) => _isMac() ? [ SHIFT, COMMAND ] : [ CTRL, SHIFT ], + secondary: ( _isMac ) => _isMac() ? [ SHIFT, ALT, COMMAND ] : [ CTRL, SHIFT, ALT ], + access: ( _isMac ) => _isMac() ? [ CTRL, ALT ] : [ SHIFT, ALT ], +}; + +/** + * An object that contains functions to get raw shortcuts. + * E.g. rawShortcut.primary( 'm' ) will return 'meta+m' on Mac. + * These are intended for user with the KeyboardShortcuts component or TinyMCE. + * + * @type {Object} Keyed map of functions to raw shortcuts. + */ +export const rawShortcut = mapValues( modifiers, ( modifier ) => { + return ( character, _isMac = isMacOS ) => { + return [ ...modifier( _isMac ), character.toLowerCase() ].join( '+' ); + }; +} ); + +/** + * An object that contains functions to display shortcuts. + * E.g. displayShortcut.primary( 'm' ) will return '⌘M' on Mac. + * + * @type {Object} Keyed map of functions to display shortcuts. + */ +export const displayShortcut = mapValues( modifiers, ( modifier ) => { + return ( character, _isMac = isMacOS ) => { + const isMac = _isMac(); + const replacementKeyMap = { + [ ALT ]: isMac ? '⌥option' : 'Alt', + [ CTRL ]: isMac ? '⌃control' : 'Ctrl', + [ COMMAND ]: '⌘', + [ SHIFT ]: isMac ? '⇧shift' : 'Shift', + }; + const shortcut = [ + ...modifier( _isMac ).map( ( key ) => get( replacementKeyMap, key, key ) ), + character.toUpperCase(), + ].join( '+' ); + + // Because we use just the clover symbol for MacOS's "command" key, remove + // the key join character ("+") between it and the final character if that + // final character is alphanumeric. ⌘S looks nicer than ⌘+S. + return shortcut.replace( /⌘\+([A-Z0-9])$/g, '⌘$1' ); + }; +} ); diff --git a/utils/test/keycodes.js b/utils/test/keycodes.js new file mode 100644 index 0000000000000..5b7adf10debec --- /dev/null +++ b/utils/test/keycodes.js @@ -0,0 +1,133 @@ +/** + * Internal dependencies + */ +import { + isMacOS, + displayShortcut, + rawShortcut, +} from '../keycodes'; + +const isMacOSFalse = () => false; +const isMacOSTrue = () => true; + +describe( 'displayShortcut', () => { + describe( 'primary', () => { + it( 'should output Control text on Windows', () => { + const shortcut = displayShortcut.primary( 'm', isMacOSFalse ); + expect( shortcut ).toEqual( 'Ctrl+M' ); + } ); + + it( 'should output command symbol on MacOS', () => { + const shortcut = displayShortcut.primary( 'm', isMacOSTrue ); + expect( shortcut ).toEqual( '⌘M' ); + } ); + } ); + + describe( 'primaryShift', () => { + it( 'should output Ctrl+Shift text on Windows', () => { + const shortcut = displayShortcut.primaryShift( 'm', isMacOSFalse ); + expect( shortcut ).toEqual( 'Ctrl+Shift+M' ); + } ); + + it( 'should output shift+command symbols on MacOS', () => { + const shortcut = displayShortcut.primaryShift( 'm', isMacOSTrue ); + expect( shortcut ).toEqual( '⇧shift+⌘M' ); + } ); + } ); + + describe( 'secondary', () => { + it( 'should output Ctrl+Shift+Alt text on Windows', () => { + const shortcut = displayShortcut.secondary( 'm', isMacOSFalse ); + expect( shortcut ).toEqual( 'Ctrl+Shift+Alt+M' ); + } ); + + it( 'should output shift+option+command symbols on MacOS', () => { + const shortcut = displayShortcut.secondary( 'm', isMacOSTrue ); + expect( shortcut ).toEqual( '⇧shift+⌥option+⌘M' ); + } ); + } ); + + describe( 'access', () => { + it( 'should output Shift+Alt text on Windows', () => { + const shortcut = displayShortcut.access( 'm', isMacOSFalse ); + expect( shortcut ).toEqual( 'Shift+Alt+M' ); + } ); + + it( 'should output control+option symbols on MacOS', () => { + const shortcut = displayShortcut.access( 'm', isMacOSTrue ); + expect( shortcut ).toEqual( '⌃control+⌥option+M' ); + } ); + } ); +} ); + +describe( 'rawShortcut', () => { + describe( 'primary', () => { + it( 'should output ctrl on Windows', () => { + const shortcut = rawShortcut.primary( 'm', isMacOSFalse ); + expect( shortcut ).toEqual( 'ctrl+m' ); + } ); + + it( 'should output meta on MacOS', () => { + const shortcut = rawShortcut.primary( 'm', isMacOSTrue ); + expect( shortcut ).toEqual( 'meta+m' ); + } ); + } ); + + describe( 'primaryShift', () => { + it( 'should output ctrl+shift on Windows', () => { + const shortcut = rawShortcut.primaryShift( 'm', isMacOSFalse ); + expect( shortcut ).toEqual( 'ctrl+shift+m' ); + } ); + + it( 'should output shift+meta on MacOS', () => { + const shortcut = rawShortcut.primaryShift( 'm', isMacOSTrue ); + expect( shortcut ).toEqual( 'shift+meta+m' ); + } ); + } ); + + describe( 'secondary', () => { + it( 'should output ctrl+shift+alt on Windows', () => { + const shortcut = rawShortcut.secondary( 'm', isMacOSFalse ); + expect( shortcut ).toEqual( 'ctrl+shift+alt+m' ); + } ); + + it( 'should output shift+alt+meta on MacOS', () => { + const shortcut = rawShortcut.secondary( 'm', isMacOSTrue ); + expect( shortcut ).toEqual( 'shift+alt+meta+m' ); + } ); + } ); + + describe( 'access', () => { + it( 'should output shift+alt on Windows', () => { + const shortcut = rawShortcut.access( 'm', isMacOSFalse ); + expect( shortcut ).toEqual( 'shift+alt+m' ); + } ); + + it( 'should output ctrl+alt on MacOS', () => { + const shortcut = rawShortcut.access( 'm', isMacOSTrue ); + expect( shortcut ).toEqual( 'ctrl+alt+m' ); + } ); + } ); +} ); + +describe( 'isMacOS helper', () => { + it( 'should identify anything with "Mac" in it as MacOS', () => { + expect( isMacOS( { navigator: { platform: 'Mac' } } ) ).toEqual( true ); + expect( isMacOS( { navigator: { platform: 'MacIntel' } } ) ).toEqual( true ); + } ); + + it( 'should not identify Windows as MacOS', () => { + expect( isMacOS( { navigator: { platform: 'Windows' } } ) ).toEqual( false ); + expect( isMacOS( { navigator: { platform: 'Win' } } ) ).toEqual( false ); + } ); + + it( 'should not identify *NIX as MacOS', () => { + expect( isMacOS( { navigator: { platform: 'Linux' } } ) ).toEqual( false ); + expect( isMacOS( { navigator: { platform: 'Unix' } } ) ).toEqual( false ); + } ); + + it( 'should not identify other cases as MacOS', () => { + expect( isMacOS( { navigator: { platform: 'MAC' } } ) ).toEqual( false ); + expect( isMacOS( { navigator: { platform: 'mac' } } ) ).toEqual( false ); + } ); +} ); diff --git a/webpack.config.js b/webpack.config.js index 993d50723359e..1ac67256f9c39 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -13,17 +13,17 @@ const CustomTemplatedPathPlugin = require( '@wordpress/custom-templated-path-web // Main CSS loader for everything but blocks.. const mainCSSExtractTextPlugin = new ExtractTextPlugin( { - filename: './[basename]/build/style.css', + filename: './build/[basename]/style.css', } ); // CSS loader for styles specific to block editing. const editBlocksCSSPlugin = new ExtractTextPlugin( { - filename: './core-blocks/build/edit-blocks.css', + filename: './build/core-blocks/edit-blocks.css', } ); // CSS loader for styles specific to blocks in general. const blocksCSSPlugin = new ExtractTextPlugin( { - filename: './core-blocks/build/style.css', + filename: './build/core-blocks/style.css', } ); // Configuration for the ExtractTextPlugin. @@ -62,7 +62,7 @@ const extractConfig = { */ function camelCaseDash( string ) { return string.replace( - /-([a-z])/, + /-([a-z])/g, ( match, letter ) => letter.toUpperCase() ); } @@ -83,8 +83,11 @@ const entryPointNames = [ ]; const packageNames = [ + 'a11y', + 'dom-ready', 'hooks', 'i18n', + 'is-shallow-equal', ]; const coreGlobals = [ @@ -121,12 +124,13 @@ const config = { return memo; }, {} ), packageNames.reduce( ( memo, packageName ) => { - memo[ packageName ] = `./node_modules/@wordpress/${ packageName }`; + const name = camelCaseDash( packageName ); + memo[ name ] = `./node_modules/@wordpress/${ packageName }`; return memo; }, {} ) ), output: { - filename: '[basename]/build/index.js', + filename: './build/[basename]/index.js', path: __dirname, library: [ 'wp', '[name]' ], libraryTarget: 'this',