diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000000..667a507d56e975 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = tab +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[package.json] +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000000000..1b46617a163d24 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,52 @@ +{ + "root": true, + "extends": "wordpress", + "env": { + "browser": true + }, + "rules": { + "array-bracket-spacing": [ "error", "always" ], + "brace-style": [ "error", "1tbs" ], + "comma-spacing": "error", + "comma-style": "error", + "computed-property-spacing": [ "error", "always" ], + "dot-notation": "error", + "eol-last": "error", + "func-call-spacing": "error", + "indent": [ "error", "tab", { "SwitchCase": 1 } ], + "key-spacing": "error", + "keyword-spacing": "error", + "no-console": "error", + "no-debugger": "error", + "no-dupe-args": "error", + "no-dupe-keys": "error", + "no-duplicate-case": "error", + "no-else-return": "error", + "no-extra-semi": "error", + "no-lonely-if": "error", + "no-mixed-spaces-and-tabs": "error", + "no-multiple-empty-lines": [ "error", { "max": 1 } ], + "no-multi-spaces": "error", + "no-negated-in-lhs": "error", + "no-nested-ternary": "error", + "no-redeclare": "error", + "no-shadow": "error", + "no-unreachable": "error", + "no-use-before-define": [ "error", "nofunc" ], + "object-curly-spacing": [ "error", "always" ], + "padded-blocks": [ "error", "never" ], + "quote-props": [ "error", "as-needed", { "keywords": true } ], + "semi": "error", + "semi-spacing": "error", + "space-before-blocks": [ "error", "always" ], + "space-before-function-paren": [ "error", "never" ], + "space-in-parens": [ "error", "always" ], + "space-infix-ops": [ "error", { "int32Hint": false } ], + "space-unary-ops": [ "error", { + "overrides": { + "!": true + } + } ], + "valid-jsdoc": [ "error", { "requireReturn": false } ] + } +} diff --git a/README.md b/README.md index e8c31b87ddc8d3..81f89746f62e87 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,16 @@ Prototyping since 1440. This is the development and prototyping hub for the editor focus in core. +Gutenberg is the project name. Conversations and discussions take place in #core-editor in Slack. > 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. — Matt Mullenweg +WordPress already supports a large amount of "blocks", but doesn't surface them very well, nor give them many rich layout options. By embracing the blocky nature, we can hopefully surface blocks that already exist, as well as attach more advanced layout options to each of them, allowing you to easily write richer posts. + ## Overview - What are little blocks made of? - Editor Technical Overview -- Mockups: https://cloudup.com/c9pKpaoDpQ4 ## Prototypes @@ -22,6 +24,181 @@ This is the development and prototyping hub for the editor focus in core. - WP Post grammar parser. ----- +## How Designers Can Contribute + +The editor we're building means to make the editing experience better for every WordPress user, by creating an interface 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", to quote the kickoff goal. + +That is difficult. So your designer eyes and help is appreciated, in what capacity you'd like to contribute. + +A good place to start is having a look at the current mockups and the UI prototype. We also have a GitHub repository, where anything labelled "Design" could use thoughtful replies, mockups, animatics, sketches, doodles. + +With regards to specific changes to the design, the details & execution (like colors, borders, shadows), those are best done as minimal and specific iterations on the work that precedes it, so we can ideally compare. That doesn't preclude wild ideas, but should be considered for precise tasks like "give the pressed buttons more contrast", things in that vein. + +Grab the Sketch file so you don't have to start from scratch: + +**Download, Updated Mar. 15th.** + +## Mockups + +These mockups are all subject to change and feedback. + +**Basic Blocks** + +_Text_ + +![Text, Neutral](mockups/Text,%20Neutral.png) + +_Text, Hover_ + +![Text, Hover](mockups/Text,%20Hover.png) + +_Text, Selected_ + +![Text, Selected](mockups/Text,%20Selected.png) + +--- + +_Empty Image_ + +![Empty Image](mockups/Image,%20Empty.png) + +_Empty Image, Hover_ + +![Empty Image, Hover](mockups/Image,%20Empty,%20Hover.png) + +_Image_ + +![Image, Neutral](mockups/Image,%20Neutral.png) + +_Image, Hover_ + +![Image, Hover](mockups/Image,%20Hover.png) + +_Image, Selected_ + +![Image, Selected](mockups/Image,%20Selected.png) + +_Image, Caption_ + +![Image, Caption](mockups/Image,%20Caption.png) + +--- + +_Empty Quote_ + +![Empty Quote](mockups/Quote,%20Empty.png) + +_Empty Quote, Hover_ + +![Empty Quote, Hover](mockups/Quote,%20Empty,%20Hover.png) + +_Quote_ + +![Quote, Neutral](mockups/Quote,%20Neutral.png) + +_Quote, Hover_ + +![Quote, Hover](mockups/Quote,%20Hover.png) + +_Quote, Selected_ + +![Quote, Selected](mockups/Quote,%20Selected.png) + +_Quote, Citation_ + +![Quote, Citation](mockups/Quote,%20Citation.png) + +_Quote 2_ + +![Quote 2, Neutral](mockups/Quote%202,%20Neutral.png) + +_Quote 2, Hover_ + +![Quote 2, Hover](mockups/Quote%202,%20Hover.png) + +_Quote 2, Selected_ + +![Quote 2, Selected](mockups/Quote%202,%20Selected.png) + +--- + +_Heading_ + +![Heading, Neutral](mockups/Heading,%20Neutral.png) + +_Heading, Hover_ + +![Heading, Hover](mockups/Heading,%20Hover.png) + +_Heading, Selected_ + +![Heading, Selected](mockups/Heading,%20Selected.png) + +--- + +_Empty Embed_ + +![Empty Embed, Neutral](mockups/Empty%20Embed,%20Neutral.png) + +_Empty Embed, Hover_ + +![Empty Embed, Hover](mockups/Empty%20Embed,%20Hover.png) + +_Embed, Neutral_ + +![Embed, Neutral](mockups/Embed,%20Neutral.png) + +_Embed, Hover_ + +![Embed, Hover](mockups/Embed,%20Hover.png) + +_Embed, Selected_ + +![Embed, Selected](mockups/Embed,%20Selected.png) + +_Embed, Caption_ + +![Embed, Caption](mockups/Embed,%20Caption.png) + +--- + +_Gallery_ + +![Gallery, Neutral](mockups/Gallery,%20Neutral.png) + +_Gallery, Hover_ + +![Gallery, Hover](mockups/Gallery,%20Hover.png) + +_Gallery, Selected_ + +![Gallery, Selected](mockups/Gallery,%20Selected.png) + +_Gallery, Selected Image_ + +![Gallery, Selected Image](mockups/Gallery,%20Selected%20Image.png) + +_Gallery, Caption_ + +![Gallery, Caption](mockups/Gallery,%20Caption.png) + +--- + +_Basic UI controls_ + +![Drag and drop](mockups/Drag%20and%20drop.png) +![Insert](mockups/Insert.png) +![Newlines](mockups/Newlines.png) +![Type Switcher](mockups/Type%20Switcher.png) + +**Early Admin UI Concept** + +**Note:** This is how it _could_ look. + +![Admin UI](mockups/Admin%20UI.png) + +![Admin UI, Sidebar Open](mockups/Admin%20UI,%20Sidebar%20Open.png) + +**Early Mobile UI Concept** -Conversations and discussions take place in #core-editor in Slack. +![Mobile](mockups/Mobile.png) diff --git a/blocks.js b/blocks.js index 8873400246e0d2..e5bb7451da1bac 100644 --- a/blocks.js +++ b/blocks.js @@ -40,50 +40,181 @@ var config = { 'default': [] }, blockCategories: [ - { id: 'frequent', label: 'Frequently Used' }, - { id: 'media', label: 'Media' } + { id: 'common', label: 'Common' }, + { id: 'media', label: 'Media' }, + { id: 'embeds', label: 'Embeds' }, + { id: 'other', label: 'Other' }, + { id: 'layout', label: 'Layout' } ], blocks: [ - { - label: 'Paragraph', - icon: '', - categories: [ 'frequent' ] - }, - { - label: 'Heading', - icon: 'Heading', - categories: [ 'frequent' ] - }, - { - label: 'Image', - icon: 'Image', - categories: [ 'frequent' ] - }, - { - label: 'Quote', - icon: '', - categories: [ 'frequent' ] - }, - { - label: 'Gallery', - icon: '', - categories: [ 'media' ] - }, - { - label: 'Unordered List', - icon: '', - categories: [ 'frequent' ] - }, - { - label: 'Ordered List', - icon: '', - categories: [ 'frequent' ] - }, - { - label: 'Embed', - icon: '', - categories: [ 'media' ] - } + { + id: 'paragraph', + label: 'Paragraph', + icon: '', + category: 'common' + }, + { + id: 'heading', + label: 'Heading', + icon: 'Heading', + category: 'common' + }, + { + id: 'image', + label: 'Image', + icon: '', + category: 'common' + }, + { + id: 'quote', + label: 'Quote', + icon: '', + category: 'common' + }, + { + id: 'gallery', + label: 'Gallery', + icon: '', + category: 'media' + }, + { + id: 'unordered-list', + label: 'Unordered List', + icon: '', + category: 'common' + }, + { + id: 'ordered-list', + label: 'Ordered List', + icon: '', + category: 'common' + }, + { + id: 'embed', + label: 'Embed', + icon: '', + category: 'embeds' + }, + { + id: 'separator', + label: 'Separator', + icon: 'Minus Small', + category: 'common' + }, + { + id: 'map', + label: 'Map', + icon: '', + category: 'embeds' + }, + { + id: 'google-map', + label: 'Google Map', + icon: '', + category: 'other' + }, + { + id: 'openstreet-map', + label: 'OpenStreet Map', + icon: '', + category: 'other' + }, + { + id: 'tweet', + label: 'Tweet', + icon: '', + category: 'other' + }, + { + id: 'video', + label: 'Video', + icon: '', + category: 'media' + }, + { + id: 'youtube', + label: 'YouTube', + icon: '', + category: 'media' + }, + { + id: 'vimeo', + label: 'Vimeo', + icon: '', + category: 'media' + }, + { + id: 'audio', + label: 'Audio', + icon: 'Audio', + category: 'media' + }, + { + id: 'form', + label: 'Form', + icon: '', + category: 'other' + }, + { + id: 'survey', + label: 'Survey', + icon: 'Custom Post Type', + category: 'other' + }, + { + id: 'toc', + label: 'Table of Contents', + icon: '', + category: 'layout' + }, + { + id: 'wordpress-post', + label: 'WordPress Post', + icon: 'My Sites', + category: 'other' + }, + { + id: 'facebook-post', + label: 'Facebook Post', + icon: '', + category: 'other' + }, + { + id: 'opengraph-link', + label: 'OpenGraph Link', + icon: '', + category: 'other' + }, + { + id: 'playlist', + label: 'Playlist', + icon: 'Audio', + category: 'media' + }, + { + id: 'spotify-playlist', + label: 'Spotify Playlist', + icon: '', + category: 'media' + }, + { + id: 'poet', + label: 'Poet', + icon: 'Pencil', + category: 'layout' + }, + { + id: 'custom-field', + label: 'Custom Field', + icon: 'Layout Blocks', + category: 'layout' + }, + { + id: 'gist', + label: 'Gist', + icon: 'Code', + category: 'other' + } ] }; @@ -91,8 +222,7 @@ var editor = queryFirst( '.editor' ); var switcher = queryFirst( '.block-switcher' ); var switcherButtons = query( '.block-switcher .type svg' ); var switcherMenu = queryFirst( '.switch-block__menu' ); -var blockControls = queryFirst( '.block-controls' ); -var inlineControls = queryFirst( '.inline-controls' ); +var dockedControls = queryFirst( '.docked-controls' ); var insertBlockButton = queryFirst( '.insert-block__button' ); var insertBlockMenu = queryFirst( '.insert-block__menu' ); var insertBlockMenuSearchInput = queryFirst( '.insert-block__search' ); @@ -105,8 +235,30 @@ var imageAlignNone = queryFirst( '.block-image__no-align' ); var imageAlignLeft = queryFirst( '.block-image__align-left' ); var imageAlignRight = queryFirst( '.block-image__align-right' ); +// Contants +var KEY_ENTER = 13; +var KEY_ARROW_LEFT = 37; +var KEY_ARROW_UP = 38; +var KEY_ARROW_RIGHT = 39; +var KEY_ARROW_DOWN = 40; + +// Editor Variables var selectedBlock = null; + +// Block Menu Variables +var previouslyFocusedBlock = null; var searchBlockFilter = ''; +var blockMenuOpened = false; +var menuSelectedBlock = null; + +// Helper variables +var orderedBlocks = config.blockCategories.reduce( function( memo, category ) { + var categoryBlocks = config.blocks.filter( function( block ) { + return block.category === category.id; + } ); + + return memo.concat( categoryBlocks ); +}, [] ); var supportedBlockTags = Object.keys( config.tagTypes ) .slice( 0, -1 ) // remove 'default' option @@ -122,12 +274,12 @@ insertBlockButton.addEventListener( 'click', openBlockMenu, false ); insertBlockMenu.addEventListener( 'click', function( event ) { event.stopPropagation(); }, false ); -window.addEventListener( 'mouseup', onSelectText, false ); attachBlockHandlers(); attachControlActions(); attachTypeSwitcherActions(); attachBlockMenuSearch(); +attachKeyboardShortcuts(); /** * Core logic @@ -144,6 +296,14 @@ function getBlocks() { supportedBlockTags.map( query ) ); } +function getFocusedBlock() { + var focusedBlocks = getBlocks().filter( function( block ) { + return block.contains( window.getSelection().anchorNode ); + } ); + + return focusedBlocks.length ? focusedBlocks[ 0 ] : null; +} + function selectBlock( event ) { clearBlocks(); event.stopPropagation(); @@ -168,6 +328,7 @@ function showControls( node ) { switcherButtons.forEach( function( element ) { element.style.display = 'none'; } ); + var blockType = getTagType( node.nodeName ); var switcherQuery = '.type-icon-' + blockType; queryFirst( switcherQuery ).style.display = 'block'; @@ -178,52 +339,48 @@ function showControls( node ) { switcher.style.top = ( position.top + 18 + window.scrollY ) + 'px'; // show/hide block-specific block controls - blockControls.className = 'block-controls'; + dockedControls.className = 'docked-controls'; getTypeKinds( blockType ).forEach( function( kind ) { - blockControls.classList.add( 'is-' + kind ); + dockedControls.classList.add( 'is-' + kind ); } ); - blockControls.style.display = 'block'; + dockedControls.style.display = 'block'; // reposition block-specific block controls - blockControls.style.top = ( position.top - 36 + window.scrollY ) + 'px'; - blockControls.style.maxHeight = 'none'; + updateDockedControlsPosition(); } -function hideControls() { - switcher.style.opacity = 0; - switcherMenu.style.display = 'none'; - blockControls.style.display = 'none'; -} +function updateDockedControlsPosition( newClassName ) { + var isImage = selectedBlock.tagName === 'IMG'; + var className = selectedBlock.className; + var position = selectedBlock.getBoundingClientRect(); + var alignedRight = className.match( /align-right/ ); + var alignedLeft = className.match( /align-left/ ); + var fullBleed = className.match( /full-bleed/ ); + + var topPosition = position.top - 34 + window.scrollY; + var leftPosition = null; + + if ( isImage && alignedRight ) { + leftPosition = position.left; + topPosition = newClassName ? topPosition - 15 : topPosition; + } else if ( isImage && alignedLeft && newClassName ) { + topPosition = topPosition - 15; + } else if ( isImage && className === 'is-selected' && dockedControls.style.left ) { + leftPosition = null; + topPosition = topPosition + 15; + } else if ( fullBleed ) { + leftPosition = ( window.innerWidth / 2 ) - ( dockedControls.clientWidth / 2 ); + } -function hideInlineControls() { - inlineControls.style.display = 'none'; + dockedControls.style.maxHeight = 'none'; + dockedControls.style.top = topPosition + 'px'; + dockedControls.style.left = leftPosition ? leftPosition + 'px' : null; } -// Show popup on text selection -function onSelectText( event ) { - event.stopPropagation(); - var txt = ""; - - if ( window.getSelection ) { - txt = window.getSelection(); - } else if ( document.getSelection ) { - txt = document.getSelection(); - } else if ( document.selection ) { - txt = document.selection.createRange().text; - } - - // Show formatting bar - if ( txt != '' ) { - inlineControls.style.display = 'block'; - var range = txt.getRangeAt(0); - var pos = range.getBoundingClientRect(); - var selectCenter = pos.width / 2; - var controlsCenter = inlineControls.offsetWidth / 2; - inlineControls.style.left = ( pos.left + selectCenter - controlsCenter ) + 'px'; - inlineControls.style.top = ( pos.top - 48 + window.scrollY ) + 'px'; - } else { - inlineControls.style.display = 'none'; - } +function hideControls() { + switcher.style.opacity = 0; + switcherMenu.style.display = 'none'; + dockedControls.style.display = 'none'; } function attachControlActions() { @@ -241,9 +398,11 @@ function attachControlActions() { if ( getter ) { node.addEventListener( 'click', function( event ) { event.stopPropagation(); + var previousOffset = selectedBlock.offsetTop; swapNodes( selectedBlock, getter( selectedBlock ) ); attachBlockHandlers(); reselect(); + window.scrollTo( window.scrollX, window.scrollY + selectedBlock.offsetTop - previousOffset ); }, false ); } } ); @@ -276,12 +435,9 @@ function attachTypeSwitcherActions() { } ); Object.keys( typeToTag ).forEach( function( type ) { - var selector = '.switch-block__block .type-icon-' + type; - var button = queryFirst( selector ); - var label = queryFirst( selector + ' + label' ); - + var iconSelector = '.switch-block__block .type-icon-' + type; + var button = queryFirst( iconSelector ).parentNode; button.addEventListener( 'click', switchBlockType, false ); - label.addEventListener( 'click', switchBlockType, false ); function switchBlockType( event ) { if ( ! selectedBlock ) { @@ -300,7 +456,7 @@ function attachTypeSwitcherActions() { } ); } -function fillBlockMenu() { +function renderBlockMenu() { insertBlockMenuContent.innerHTML = ''; config.blockCategories.forEach( function ( category ) { var node = document.createElement( 'div' ); @@ -314,13 +470,13 @@ function fillBlockMenu() { node.appendChild( nodeBlocks ); var categoryBlocks = config.blocks .filter( function( block ) { - return block.categories.indexOf( category.id ) !== -1 + return block.category === category.id && block.label.toLowerCase().indexOf( searchBlockFilter.toLowerCase() ) !== -1; } ); categoryBlocks .forEach( function( block ) { var node = document.createElement( 'div' ); - node.className = 'insert-block__block'; + node.className = 'insert-block__block block-' + block.id + ( menuSelectedBlock === block ? ' is-active' : '' ); node.innerHTML = block.icon + ' ' + block.label; nodeBlocks.appendChild(node); } ); @@ -329,21 +485,128 @@ function fillBlockMenu() { insertBlockMenuContent.appendChild( node ); } } ); - - var placeholder = document.createElement('div'); - placeholder.className = 'insert-block__separator'; - placeholder.textContent = 'These don\'t work yet.'; - insertBlockMenuContent.appendChild( placeholder ); } function attachBlockMenuSearch() { insertBlockMenuSearchInput.addEventListener( 'keyup', filterBlockMenu, false ); insertBlockMenuSearchInput.addEventListener( 'input', filterBlockMenu, false ); - fillBlockMenu(); + insertBlockMenuContent.addEventListener( 'scroll', handleBlockMenuScroll, false ); + selectBlockInMenu(); + renderBlockMenu(); function filterBlockMenu( event ) { searchBlockFilter = event.target.value; - fillBlockMenu(); + selectBlockInMenu(); + renderBlockMenu(); + } + + function handleBlockMenuScroll( event ) { + if ( insertBlockMenuContent.scrollHeight - insertBlockMenuContent.scrollTop <= insertBlockMenuContent.clientHeight ) { + insertBlockMenuContent.className = 'insert-block__content is-bottom'; + } else { + insertBlockMenuContent.className = 'insert-block__content'; + } + } +} + +/** + * Select a block in the block menu + * @param direction direction from the current position (up/down/left/right) + */ +function selectBlockInMenu( direction ) { + var filteredBlocks = orderedBlocks.filter( function( block ) { + return block.label.toLowerCase().indexOf( searchBlockFilter.toLowerCase() ) !== -1; + } ); + var countBlocksByCategories = filteredBlocks.reduce( function( memo, block ) { + if ( ! memo[ block.category ] ) { + memo[ block.category ] = 0; + } + memo[ block.category ]++; + return memo; + }, {} ); + + var selectedBlockIndex = filteredBlocks.indexOf( menuSelectedBlock ); + selectedBlockIndex = selectedBlockIndex === -1 ? 0 : selectedBlockIndex; + var currentBlock = filteredBlocks[ selectedBlockIndex ]; + var previousBlock = filteredBlocks[ selectedBlockIndex - 1 ]; + var nextBlock = filteredBlocks[ selectedBlockIndex + 1 ]; + var offset = 0; + switch ( direction ) { + case KEY_ARROW_UP: + offset = ( + currentBlock + && filteredBlocks[ selectedBlockIndex - 2 ] + && ( + filteredBlocks[ selectedBlockIndex - 2 ].category === currentBlock.category + || countBlocksByCategories[ previousBlock.category ] % 2 === 0 + ) + ) ? -2 : -1; + break; + case KEY_ARROW_DOWN: + offset = ( + currentBlock + && filteredBlocks[ selectedBlockIndex + 2 ] + && ( + currentBlock.category === filteredBlocks[ selectedBlockIndex + 2 ].category + || filteredBlocks[ selectedBlockIndex + 2 ].category === nextBlock.category + || nextBlock.category === currentBlock.category + ) + ) ? 2 : 1; + break; + case KEY_ARROW_RIGHT: + offset = 1; + break; + case KEY_ARROW_LEFT: + offset = -1; + break; + } + + menuSelectedBlock = filteredBlocks[ selectedBlockIndex + offset ] || menuSelectedBlock; + + // Hack to wait for the rerender before scrolling + setTimeout( function() { + var blockElement = queryFirst( '.insert-block__block.block-' + menuSelectedBlock.id ); + if ( + blockElement && ( + blockElement.offsetTop + blockElement.offsetHeight > insertBlockMenuContent.clientHeight + insertBlockMenuContent.scrollTop + || blockElement.offsetTop < insertBlockMenuContent.scrollTop + ) + ) { + insertBlockMenuContent.scrollTop = blockElement.offsetTop - 23; + } + } ); +} + +function attachKeyboardShortcuts() { + document.addEventListener( 'keypress', handleKeyPress, false ); + document.addEventListener( 'keydown', handleKeyDown, false ); + + function handleKeyPress( event ) { + if ( '/' === String.fromCharCode( event.keyCode ) && ! blockMenuOpened ) { + var focusedBlock = getFocusedBlock(); + if ( document.activeElement !== editor || ( focusedBlock && ! focusedBlock.textContent ) ) { + event.preventDefault(); + openBlockMenu(); + } + } + } + + function handleKeyDown( event ) { + if ( ! blockMenuOpened ) return; + switch ( event.keyCode ) { + case KEY_ENTER: + event.preventDefault(); + hideMenu(); + break; + case KEY_ARROW_DOWN: + case KEY_ARROW_UP: + case KEY_ARROW_LEFT: + case KEY_ARROW_RIGHT: + event.preventDefault(); + selectBlockInMenu( event.keyCode ); + renderBlockMenu(); + break; + } } } @@ -392,15 +655,26 @@ function siblingGetter( direction ) { } function openBlockMenu( event ) { - hideInlineControls(); clearBlocks(); - event.stopPropagation(); + event && event.stopPropagation(); insertBlockMenu.style.display = 'block'; + blockMenuOpened = true; + searchBlockFilter = ''; + insertBlockMenuSearchInput.value = ''; + menuSelectedBlock = false; + previouslyFocusedBlock = getFocusedBlock(); insertBlockMenuSearchInput.focus(); + selectBlockInMenu(); + renderBlockMenu(); } function hideMenu() { + if ( ! blockMenuOpened ) return; insertBlockMenu.style.display = 'none'; + blockMenuOpened = false; + if ( previouslyFocusedBlock ) { + setCaret( previouslyFocusedBlock ); + } } function showSwitcherMenu( event ) { @@ -436,6 +710,16 @@ function setElementState( className, event ) { if ( className ) { selectedBlock.classList.add( className ); } + updateDockedControlsPosition( className ); +} + +function setCaret( element ) { + var range = document.createRange(); + range.setStart( element.childNodes[0] ,0 ); + range.collapse( true ); + var selection = window.getSelection(); + selection.removeAllRanges(); + selection.addRange( range ); } function l( data ) { diff --git a/index.html b/index.html index 59c07a4891173b..9c01eefb3dbe12 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ Editor Blocks - + @@ -18,36 +18,38 @@ Quote -
- - - - - - - -
-
- - - - - - +
+
+ + + + + + + +
+
+ + + + + + +
@@ -85,5 +87,6 @@

1.0 Is The Loneliest Number

+ diff --git a/mockups/Admin UI, Sidebar Open.png b/mockups/Admin UI, Sidebar Open.png new file mode 100644 index 00000000000000..e683e21f8cf6ac Binary files /dev/null and b/mockups/Admin UI, Sidebar Open.png differ diff --git a/mockups/Admin UI.png b/mockups/Admin UI.png new file mode 100644 index 00000000000000..81b739d6ed6fd7 Binary files /dev/null and b/mockups/Admin UI.png differ diff --git a/mockups/Drag and drop.png b/mockups/Drag and drop.png new file mode 100644 index 00000000000000..983226b8a619ab Binary files /dev/null and b/mockups/Drag and drop.png differ diff --git a/mockups/Embed, Caption.png b/mockups/Embed, Caption.png new file mode 100644 index 00000000000000..ae57a550a4fb8b Binary files /dev/null and b/mockups/Embed, Caption.png differ diff --git a/mockups/Embed, Hover.png b/mockups/Embed, Hover.png new file mode 100644 index 00000000000000..0497d940428006 Binary files /dev/null and b/mockups/Embed, Hover.png differ diff --git a/mockups/Embed, Neutral.png b/mockups/Embed, Neutral.png new file mode 100644 index 00000000000000..cead43bde8476d Binary files /dev/null and b/mockups/Embed, Neutral.png differ diff --git a/mockups/Embed, Selected.png b/mockups/Embed, Selected.png new file mode 100644 index 00000000000000..c9516519296402 Binary files /dev/null and b/mockups/Embed, Selected.png differ diff --git a/mockups/Empty Embed, Hover.png b/mockups/Empty Embed, Hover.png new file mode 100644 index 00000000000000..6f1aa7ac5d2f99 Binary files /dev/null and b/mockups/Empty Embed, Hover.png differ diff --git a/mockups/Empty Embed, Neutral.png b/mockups/Empty Embed, Neutral.png new file mode 100644 index 00000000000000..c4456c62a36a23 Binary files /dev/null and b/mockups/Empty Embed, Neutral.png differ diff --git a/mockups/Gallery, Caption.png b/mockups/Gallery, Caption.png new file mode 100644 index 00000000000000..f5e39d42cfc4da Binary files /dev/null and b/mockups/Gallery, Caption.png differ diff --git a/mockups/Gallery, Hover.png b/mockups/Gallery, Hover.png new file mode 100644 index 00000000000000..f955eaeafc80ba Binary files /dev/null and b/mockups/Gallery, Hover.png differ diff --git a/mockups/Gallery, Neutral.png b/mockups/Gallery, Neutral.png new file mode 100644 index 00000000000000..9a124eca176bb9 Binary files /dev/null and b/mockups/Gallery, Neutral.png differ diff --git a/mockups/Gallery, Selected Image.png b/mockups/Gallery, Selected Image.png new file mode 100644 index 00000000000000..5ad767b07e85ad Binary files /dev/null and b/mockups/Gallery, Selected Image.png differ diff --git a/mockups/Gallery, Selected.png b/mockups/Gallery, Selected.png new file mode 100644 index 00000000000000..502ad41bcc7bf5 Binary files /dev/null and b/mockups/Gallery, Selected.png differ diff --git a/mockups/Heading, Hover.png b/mockups/Heading, Hover.png new file mode 100644 index 00000000000000..e849d030e4b5c6 Binary files /dev/null and b/mockups/Heading, Hover.png differ diff --git a/mockups/Heading, Neutral.png b/mockups/Heading, Neutral.png new file mode 100644 index 00000000000000..62d399afe12da8 Binary files /dev/null and b/mockups/Heading, Neutral.png differ diff --git a/mockups/Heading, Selected.png b/mockups/Heading, Selected.png new file mode 100644 index 00000000000000..9c5b3901589c6f Binary files /dev/null and b/mockups/Heading, Selected.png differ diff --git a/mockups/Image, Caption.png b/mockups/Image, Caption.png new file mode 100644 index 00000000000000..332fd6ca8640a9 Binary files /dev/null and b/mockups/Image, Caption.png differ diff --git a/mockups/Image, Empty, Hover.png b/mockups/Image, Empty, Hover.png new file mode 100644 index 00000000000000..92ffe46f243180 Binary files /dev/null and b/mockups/Image, Empty, Hover.png differ diff --git a/mockups/Image, Empty.png b/mockups/Image, Empty.png new file mode 100644 index 00000000000000..b37d56d1a3f1a7 Binary files /dev/null and b/mockups/Image, Empty.png differ diff --git a/mockups/Image, Hover.png b/mockups/Image, Hover.png new file mode 100644 index 00000000000000..fe2ecce9ecf5f3 Binary files /dev/null and b/mockups/Image, Hover.png differ diff --git a/mockups/Image, Neutral.png b/mockups/Image, Neutral.png new file mode 100644 index 00000000000000..bd86b524ffd8c9 Binary files /dev/null and b/mockups/Image, Neutral.png differ diff --git a/mockups/Image, Selected.png b/mockups/Image, Selected.png new file mode 100644 index 00000000000000..176b6dfc3648b1 Binary files /dev/null and b/mockups/Image, Selected.png differ diff --git a/mockups/Insert.png b/mockups/Insert.png new file mode 100644 index 00000000000000..ff81cbcd560a66 Binary files /dev/null and b/mockups/Insert.png differ diff --git a/mockups/Mobile.png b/mockups/Mobile.png new file mode 100644 index 00000000000000..85234070170a81 Binary files /dev/null and b/mockups/Mobile.png differ diff --git a/mockups/Newlines.png b/mockups/Newlines.png new file mode 100644 index 00000000000000..201ca63eef82fb Binary files /dev/null and b/mockups/Newlines.png differ diff --git a/mockups/Quote 2, Hover.png b/mockups/Quote 2, Hover.png new file mode 100644 index 00000000000000..28de11e64264d7 Binary files /dev/null and b/mockups/Quote 2, Hover.png differ diff --git a/mockups/Quote 2, Neutral.png b/mockups/Quote 2, Neutral.png new file mode 100644 index 00000000000000..8aa1a4d1aa51f4 Binary files /dev/null and b/mockups/Quote 2, Neutral.png differ diff --git a/mockups/Quote 2, Selected.png b/mockups/Quote 2, Selected.png new file mode 100644 index 00000000000000..ada92a4cb4acb7 Binary files /dev/null and b/mockups/Quote 2, Selected.png differ diff --git a/mockups/Quote, Citation.png b/mockups/Quote, Citation.png new file mode 100644 index 00000000000000..c6d3aeb3103252 Binary files /dev/null and b/mockups/Quote, Citation.png differ diff --git a/mockups/Quote, Empty, Hover.png b/mockups/Quote, Empty, Hover.png new file mode 100644 index 00000000000000..65980f5f5d7003 Binary files /dev/null and b/mockups/Quote, Empty, Hover.png differ diff --git a/mockups/Quote, Empty.png b/mockups/Quote, Empty.png new file mode 100644 index 00000000000000..d19fe11d1c755f Binary files /dev/null and b/mockups/Quote, Empty.png differ diff --git a/mockups/Quote, Hover.png b/mockups/Quote, Hover.png new file mode 100644 index 00000000000000..4fcb923e3371c1 Binary files /dev/null and b/mockups/Quote, Hover.png differ diff --git a/mockups/Quote, Neutral.png b/mockups/Quote, Neutral.png new file mode 100644 index 00000000000000..7947bb1d6ca313 Binary files /dev/null and b/mockups/Quote, Neutral.png differ diff --git a/mockups/Quote, Selected.png b/mockups/Quote, Selected.png new file mode 100644 index 00000000000000..504e66f842f4a1 Binary files /dev/null and b/mockups/Quote, Selected.png differ diff --git a/mockups/Text, Hover.png b/mockups/Text, Hover.png new file mode 100644 index 00000000000000..c4a4f8a4c7c25c Binary files /dev/null and b/mockups/Text, Hover.png differ diff --git a/mockups/Text, Neutral.png b/mockups/Text, Neutral.png new file mode 100644 index 00000000000000..0236620867555b Binary files /dev/null and b/mockups/Text, Neutral.png differ diff --git a/mockups/Text, Selected.png b/mockups/Text, Selected.png new file mode 100644 index 00000000000000..1314bb4524e7aa Binary files /dev/null and b/mockups/Text, Selected.png differ diff --git a/mockups/Type Switcher.png b/mockups/Type Switcher.png new file mode 100644 index 00000000000000..d1134588b92d50 Binary files /dev/null and b/mockups/Type Switcher.png differ diff --git a/package.json b/package.json index 157ef3e5da322e..81b2459805faa1 100644 --- a/package.json +++ b/package.json @@ -9,20 +9,22 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/Automattic/gutenberg.git" + "url": "git+https://github.com/WordPress/gutenberg.git" }, "keywords": [ "WordPress", "editor", "prototype" ], - "author": "Automattic, Inc.", + "author": "The WordPress Contributors", "license": "GPL-2.0+", "bugs": { - "url": "https://github.com/Automattic/gutenberg/issues" + "url": "https://github.com/WordPress/gutenberg/issues" }, - "homepage": "https://github.com/Automattic/gutenberg#readme", + "homepage": "https://github.com/WordPress/gutenberg#readme", "devDependencies": { + "eslint": "^3.16.1", + "eslint-config-wordpress": "^1.1.0", "http-server": "0.9.0" } } diff --git a/shared/gridicons.svg b/shared/gridicons.svg new file mode 100644 index 00000000000000..8437578d4af7b2 --- /dev/null +++ b/shared/gridicons.svg @@ -0,0 +1,260 @@ +gridicons-add-image gridicons-add-outline gridicons-add gridicons-align-center gridicons-align-image-center gridicons-align-image-left gridicons-align-image-none gridicons-align-image-right gridicons-align-justify gridicons-align-left gridicons-align-right gridicons-arrow-down gridicons-arrow-left gridicons-arrow-right gridicons-arrow-up gridicons-aside gridicons-attachment gridicons-audio gridicons-bell gridicons-block gridicons-bold gridicons-book gridicons-bookmark-outline gridicons-bookmark gridicons-briefcase gridicons-calendar gridicons-camera gridicons-caption gridicons-cart gridicons-chat gridicons-checkmark-circle gridicons-checkmark gridicons-chevron-down gridicons-chevron-left gridicons-chevron-right gridicons-chevron-up gridicons-clear-formatting gridicons-clipboard gridicons-cloud-download gridicons-cloud-outline gridicons-cloud-upload gridicons-cloud gridicons-code gridicons-cog gridicons-comment gridicons-computer gridicons-coupon gridicons-create gridicons-credit-card gridicons-crop gridicons-cross-circle gridicons-cross-small gridicons-crossgridicons-custom-post-type gridicons-customize gridicons-domains gridicons-dropdown gridicons-ellipsis-circle gridicons-ellipsis gridicons-external gridicons-filter gridicons-flag gridicons-flip-horizontal gridicons-flip-vertical gridicons-folder-multiple gridicons-folder gridicons-fullscreen-exitgridicons-fullscreengridicons-globe gridicons-grid gridicons-heading gridicons-heart-outline gridicons-heart gridicons-help-outline gridicons-help gridicons-history gridicons-house gridicons-image-multiple gridicons-image gridicons-indent-left gridicons-indent-right gridicons-info-outline gridicons-info gridicons-ink gridicons-institution gridicons-italic gridicons-layout-blocks gridicons-layout gridicons-link-break gridicons-link gridicons-list-checkmark gridicons-list-ordered gridicons-list-unordered gridicons-location gridicons-lock gridicons-mail gridicons-mentiongridicons-menu gridicons-menus gridicons-microphonegridicons-minus-small gridicons-minus gridicons-money gridicons-my-sites-horizon gridicons-my-sites gridicons-not-visible gridicons-notice-outline gridicons-notice gridicons-offlinegridicons-pages gridicons-pause gridicons-pencil gridicons-phone gridicons-plugins gridicons-plus-small gridicons-plusgridicons-popout gridicons-posts gridicons-print gridicons-product-downloadable gridicons-product-external gridicons-product-virtual gridicons-product gridicons-quote gridicons-read-more gridicons-reader-follow gridicons-reader-following gridicons-reader gridicons-reblog gridicons-redo gridicons-refresh gridicons-refund gridicons-reply gridicons-resizegridicons-rotate gridicons-scheduled gridicons-search gridicons-share-ios gridicons-share gridicons-shipping gridicons-sign-out gridicons-spam gridicons-speaker gridicons-special-character gridicons-star-outline gridicons-star gridicons-stats-alt gridicons-stats gridicons-status gridicons-strikethrough gridicons-sync gridicons-tablet gridicons-tag gridicons-text-color gridicons-themes gridicons-thumbs-up gridicons-time gridicons-trash gridicons-trophy gridicons-types gridicons-underline gridicons-undo gridicons-user-addgridicons-user-circle gridicons-user gridicons-video-camera gridicons-video gridicons-visible + + + + + + + + + diff --git a/shared/index.css b/shared/index.css new file mode 100644 index 00000000000000..c609798ccf1973 --- /dev/null +++ b/shared/index.css @@ -0,0 +1,219 @@ +/** + * Basic + */ + +html, +body { + margin: 0; + padding: 0; + height: 100%; +} + +* { + box-sizing: border-box; +} + +body { + font: 13px/1.8 -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen-Sans", "Ubuntu", "Cantarell", "Helvetica Neue", sans-serif; + max-width: 820px; + margin: 60px auto; + color: #12181e; +} + +*[contenteditable] { + outline: none; +} + +/** + * Editor Basics + */ + +#editor { + font-family: "Noto Serif", serif; + font-size: 16px; + padding: 50px; +} + +#editor .text-align-left { + text-align: left; +} + +#editor .text-align-center { + text-align: center; +} + +#editor .text-align-right { + text-align: right; +} + +#editor a { + color: inherit; +} + +#editor iframe { + max-width: 100%; + border: 0; +} + +#editor img { + max-width: 100%; + height: auto; +} + +#editor svg { + fill: currentColor; +} + + +/** + * Editor Basic Blocks + */ + +#editor p, +#editor blockquote, +#editor h1, +#editor h2, +#editor h3, +#editor h4, +#editor h5, +#editor h6, +#editor img { + margin: 1em 0; + box-shadow: inset 0px 0px 0px 0px #e0e5e9; + transition: all .2s ease; +} + +#editor h2 { + font-weight: 900; + font-size: 28px; +} + +#editor blockquote { + font-size: 20px; + border-left: 4px solid black; + padding-left: 1em; + font-style: italic; +} + +#editor section:focus { + outline: none; +} + +#editor figure { + clear: both; + float: none; + margin-left: auto; + margin-right: auto; + width: 100%; + /*transition: margin 0.5s, width 0.5s;*/ +} + +#editor figure.aligncenter { + margin-left: auto; + margin-right: auto; +} + +#editor figure.alignleft { + float: left; + margin: 0.5em 1em 0.5em 0; + width: 50%; +} + +#editor figure.alignright { + float: right; + margin: 0.5em 0 0.5em 1em; + width: 50%; +} + +#editor figure.alignfull { + margin-left: -100px; + margin-right: -100px; + width: calc( 100% + 200px ); +} + +#editor figure.alignfull figcaption { + margin-left: 0.5em; + margin-right: 0.5em; +} + +#editor figure.alignfull > figcaption { + margin-left: 100px; + margin-right: 100px; +} + +#editor figure.alignleft, +#editor figure.alignright { + clear: none; +} + +#editor figcaption { + margin-top: 0.5em; + color: #86909b; + font-size: 0.9em; + font-style: italic; +} + +#editor figure img, +#editor figure iframe { + display: block; + margin: 0 auto; +} + +#editor pre { + overflow: auto; +} + +#editor hr { + border: none; + font-size: 36px; + margin: 1em 0; +} + +#editor hr:before { + content: '· · ·'; + display: block; + text-align: center; +} + +#editor blockquote { + margin: 2em; +} + +#editor blockquote footer { + color: #86909b; + font-size: 0.9em; + font-style: normal; + margin-left: 1.3em; + position: relative; +} + +#editor blockquote footer:after { + content: '— '; + position: absolute; + left: -1.3em; + top: 0; +} + +#editor table { + border-collapse: collapse; + table-layout: fixed; + width: 100%; +} + +#editor table { + width: 100%; +} + +#editor td, +#editor th { + padding: 0.5em; + border: 1px solid currentColor; +} + +#editor:after { + content: "."; + visibility: hidden; + display: block; + height: 0; + clear: both; +} diff --git a/shared/navigation.js b/shared/navigation.js new file mode 100644 index 00000000000000..86877fc052d684 --- /dev/null +++ b/shared/navigation.js @@ -0,0 +1,73 @@ +( function( window, document ) { + var PROTOTYPES, paths, p, pl, path, navigation, path, label, link, style; + + /** + * Set of all prototypes, keyed by path with label value. + * + * @type {Object} + */ + PROTOTYPES = { + '/': 'UI Prototype', + '/tinymce-per-block/': 'TinyMCE per block prototype', + '/tinymce-single/': 'Single TinyMCE instance prototype' + }; + + // Generate Navigation DOM + + navigation = document.createElement( 'div' ); + navigation.className = 'prototype-navigation'; + + paths = Object.keys( PROTOTYPES ); + + for ( p = 0, pl = paths.length; p < pl; p++ ) { + path = paths[ p ]; + label = PROTOTYPES[ path ]; + + link = document.createElement( 'a' ); + link.href = '/gutenberg' + path; + link.setAttribute( 'title', label ); + link.textContent = ( p + 1 ); + + if ( '/gutenberg' + path === window.location.pathname ) { + link.className = 'is-current'; + } + + navigation.appendChild( link ); + } + + // Generate Stylesheet DOM + + style = document.createElement( 'style' ); + style.innerHTML = [ + '.prototype-navigation {', + 'font: 13px/1.8 -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen-Sans", "Ubuntu", "Cantarell", "Helvetica Neue", sans-serif;', + 'display: flex;', + 'position: absolute;', + 'top: 24px;', + 'left: 24px;', + '}', + '.prototype-navigation a {', + 'display: inline-block;', + 'width: 28px;', + 'height: 28px;', + 'font-size: 12px;', + 'border: 1px solid #b4b9be;', + 'line-height: 28px;', + 'border-radius: 50%;', + 'text-decoration: none;', + 'text-align: center;', + 'margin-right: 8px;', + '}', + '.prototype-navigation a.is-current {', + 'background: #008ec2;', + 'border-color: #008ec2;', + 'color: #fff;', + '}' + ].join( '\n' ); + + // Append to body + + document.body.appendChild( navigation ); + document.body.appendChild( style ); + +} )( this, this.document ); diff --git a/shared/post-content.js b/shared/post-content.js new file mode 100644 index 00000000000000..296c19f49444a9 --- /dev/null +++ b/shared/post-content.js @@ -0,0 +1,33 @@ +window.content = [ + '', + '

1.0 Is The Loneliest Number

', + '', + + '', + '

I imagine prior to the launch of the iPod, or the iPhone, there were teams saying the same thing: the copy + paste guys are so close to being ready and we know Walt Mossberg is going to ding us for this so let\'s just not ship to the manufacturers in China for just a few more weeks… The Apple teams were probably embarrassed. But if you\'re not embarrassed when you ship your first version you waited too long.

', + '', + + '', + '
', + '', + + '', + '

A beautiful thing about Apple is how quickly they obsolete their own products. I imagine this also makes the discipline of getting things out there easier. Like I mentioned before, the longer it’s been since the last release the more pressure there is, but if you know that if your bit of code doesn’t make this version but there’s the +0.1 coming out in 6 weeks, then it’s not that bad. It’s like flights from San Francisco to LA, if you miss one you know there’s another one an hour later so it’s not a big deal. Amazon has done a fantastic job of this with the Kindle as well, with a new model every year.

', + '', + + '', + '

Real artists ship.

', + '', + + '', + '
Beautiful landscape
', + '', + + '', + '

By shipping early and often you have the unique competitive advantage of hearing from real people what they think of your work, which in best case helps you anticipate market direction, and in worst case gives you a few people rooting for you that you can email when your team pivots to a new idea. Nothing can recreate the crucible of real usage.

', + '', + + '', + '', + '' +].join( '' ); diff --git a/shared/tinymce/clean-paste.js b/shared/tinymce/clean-paste.js new file mode 100644 index 00000000000000..d3763edfdaaec9 --- /dev/null +++ b/shared/tinymce/clean-paste.js @@ -0,0 +1,19 @@ +( function( tinymce ) { + tinymce.PluginManager.add( 'clean-paste', function( editor ) { + // To do: remove pasted classes but keep when internally pasted. + + editor.on( 'BeforePastePreProcess', function( event ) { + var content = event.content; + + // Remove all external styles + content = content.replace( /(<[^>]+) style="[^"]*"([^>]*>)/gi, '$1$2' ); + + // Keep internal styles + content = content.replace( /(<[^>]+) data-mce-style="([^"]+)"([^>]*>)/gi, function( all, before, value, after ) { + return before + ' style="' + value + '"' + after; + } ); + + event.content = content; + } ); + } ); +} )( window.tinymce ); diff --git a/shared/tinymce/logger.js b/shared/tinymce/logger.js new file mode 100644 index 00000000000000..c2bfa6fde1c7df --- /dev/null +++ b/shared/tinymce/logger.js @@ -0,0 +1,22 @@ +// For testing, learning and debugging. Feel free to add. +// See https://www.tinymce.com/docs/advanced/events/ +window.tinymce.PluginManager.add( 'logger', function( editor ) { + var types = { + selectionChange: 'fires when the selection changes.', + nodeChange: 'fires when the selection and the node changes. Use `event.element`.', + beforePastePreProcess: 'gives raw paste content before processing.', + pastePostProcess: 'gives paste content after processing. Use `event.node.innerHTML`.', + beforeSetContent: 'fires before content is set in the editor. Can be used for manipulation.', + setContent: 'fires after content is set in the editor.', + beforeExecCommand: 'fires before commands are executed.', + execCommand: 'fires after commands are executed.', + change: 'fires when a new undo level is added.', + dirty: 'fires when the editor is considered to be in a dirty (unsaved) state.' + }; + + window.tinymce.each( types, function( info, type ) { + editor.on( type, function( event ) { + window.console.log( type, info, event ); + } ); + } ); +} ); diff --git a/shared/tinymce/toolbar.js b/shared/tinymce/toolbar.js new file mode 100644 index 00000000000000..558cf1a4746dc9 --- /dev/null +++ b/shared/tinymce/toolbar.js @@ -0,0 +1,397 @@ +( function( tinymce ) { + tinymce.ui.Factory.add( 'svgbutton', tinymce.ui.Button.extend( { + renderHtml: function() { + var id = this._id; + var prefix = this.classPrefix; + var icon = this.state.get( 'icon' ); + var text = this.state.get( 'text' ); + var html = ''; + + if ( icon && icon.indexOf( 'gridicons-' ) === 0 ) { + html += ( + '' + + '' + + '' + ); + } else if ( icon ) { + html += ''; + } + + if ( text ) { + this.classes.add( 'btn-has-text' ); + html += '' + this.encode( text ) + ''; + } + + return ( + '
' + + '' + + '
' + ); + }, + bindStates: function() { + var $el = this.$( this.getEl() ); + + this._super(); + + this.state.on( 'change:icon', function( event ) { + var icon = event.value; + var $i = $el.find( 'i' ); + var $svg = $el.find( 'svg' ); + + if ( icon && icon.indexOf( 'gridicons-' ) === 0 ) { + $i.remove(); + $svg.find( 'use' ).attr( 'xlink:href', '../shared/gridicons.svg#' + icon ); + } else { + $svg.remove(); + } + } ); + + return this; + }, + active: function( isActive ) { + this.$( this.getEl() ).toggleClass( 'is-active', isActive ); + } + } ) ); + + tinymce.ui.Factory.add( 'svglistbox', tinymce.ui.ListBox.extend( { + renderHtml: function() { + var id = this._id; + var prefix = this.classPrefix; + var icon = this.state.get( 'icon' ); + var text = this.state.get( 'text' ); + var html = ''; + + if ( icon && icon.indexOf( 'gridicons-' ) === 0 ) { + html += ( + '' + + '' + + '' + ); + } else if ( icon ) { + html += ''; + } + + if ( text ) { + this.classes.add( 'btn-has-text' ); + html += '' + this.encode( text ) + ''; + } + + html += ( + '' + + '' + + '' + ); + + this.aria( 'role', 'button' ); + + return ( + '
' + + '' + + '
' + ); + } + } ) ); + + tinymce.ui.Factory.add( 'menuitem', tinymce.ui.MenuItem.extend( { + renderHtml: function () { + var self = this, id = self._id, settings = self.settings, prefix = self.classPrefix; + + var icon = this.settings.icon; + var text = this.settings.text; + var html = ''; + + if ( icon ) { + html += ( + '' + + '' + + '' + ); + } + + if ( text ) { + html += '' + this.encode( text ) + ''; + } + + return ( + '
' + + html + + '
' + ); + } + } ) ); + + tinymce.PluginManager.add( 'toolbar', function( editor ) { + var each = tinymce.each; + var DOM = tinymce.DOM; + + editor.on( 'preinit', function() { + var Factory = tinymce.ui.Factory, + settings = editor.settings, + activeToolbar, + currentSelection, + timeout, + container = editor.getContainer(); + + function create( buttons, bottom ) { + var toolbar, + toolbarItems = [], + buttonGroup; + + each( buttons, function( item ) { + var itemName; + + function onClick( callback ) { + return function( event ) { + editor.undoManager.transact( function() { + callback( window.wp.blocks.getSelectedBlock(), editor, event ); + } ); + } + } + + function onPostRender( callback ) { + return function() { + var button = this; + + editor.on( 'nodechange', function() { + button.active( callback( window.wp.blocks.getSelectedBlock() ) ); + } ); + } + } + + function bindSelectorChanged() { + var selection = editor.selection; + + if ( itemName === 'bullist' ) { + selection.selectorChanged( 'ul > li', function( state, args ) { + var i = args.parents.length, + nodeName; + + while ( i-- ) { + nodeName = args.parents[ i ].nodeName; + + if ( nodeName === 'OL' || nodeName == 'UL' ) { + break; + } + } + + item.active( state && nodeName === 'UL' ); + } ); + } + + if ( itemName === 'numlist' ) { + selection.selectorChanged( 'ol > li', function( state, args ) { + var i = args.parents.length, + nodeName; + + while ( i-- ) { + nodeName = args.parents[ i ].nodeName; + + if ( nodeName === 'OL' || nodeName === 'UL' ) { + break; + } + } + + item.active( state && nodeName === 'OL' ); + } ); + } + + if ( item.settings.stateSelector ) { + selection.selectorChanged( item.settings.stateSelector, function( state ) { + item.active( state ); + }, true ); + } + + if ( item.settings.disabledStateSelector ) { + selection.selectorChanged( item.settings.disabledStateSelector, function( state ) { + item.disabled( state ); + } ); + } + } + + if ( item === '|' ) { + buttonGroup = null; + } else { + if ( typeof item === 'string' && Factory.has( item ) ) { + item = { + type: item + }; + + if ( settings.toolbar_items_size ) { + item.size = settings.toolbar_items_size; + } + + toolbarItems.push( item ); + + buttonGroup = null; + } else { + if ( ! buttonGroup ) { + buttonGroup = { + type: 'buttongroup', + items: [] + }; + + toolbarItems.push( buttonGroup ); + } + + if ( editor.buttons[ item ] ) { + item = editor.buttons[ item ]; + } else { + item.onClick = onClick( item.onClick ); + + if ( item.isActive ) { + item.onPostRender = onPostRender( item.isActive ); + } + } + + if ( typeof item === 'function' ) { + item = item(); + } + + if ( item ) { + item.type = item.type || 'svgbutton'; + + if ( settings.toolbar_items_size ) { + item.size = settings.toolbar_items_size; + } + + item = Factory.create( item ); + + buttonGroup.items.push( item ); + + if ( editor.initialized ) { + bindSelectorChanged(); + } else { + editor.on( 'init', bindSelectorChanged ); + } + } + } + } + } ); + + toolbar = Factory.create( { + type: 'panel', + layout: 'stack', + classes: 'toolbar-grp inline-toolbar-grp', + ariaRoot: true, + ariaRemember: true, + items: [ { + type: 'toolbar', + layout: 'flow', + items: toolbarItems + } ] + } ); + + toolbar.bottom = bottom; + + function reposition() { + if ( ! currentSelection ) { + return; + } + + var toolbar = this.getEl(); + var toolbarRect = toolbar.getBoundingClientRect(); + var elementRect = currentSelection.getBoundingClientRect(); + + DOM.setStyles( toolbar, { + position: 'absolute', + left: elementRect.left + 'px', + top: elementRect.bottom + window.pageYOffset + 'px' + } ); + + this.show(); + } + + toolbar.on( 'show', function() { + this.reposition(); + } ); + + toolbar.on( 'keydown', function( event ) { + if ( event.keyCode === 27 ) { + this.hide(); + editor.focus(); + } + } ); + + editor.on( 'remove', function() { + toolbar.remove(); + } ); + + toolbar.reposition = reposition; + toolbar.hide().renderTo( document.body ); + + return toolbar; + } + + editor.on( 'nodechange', function( event ) { + var args = { + element: event.element, + parents: event.parents + }; + + editor.fire( 'wptoolbar', args ); + + currentSelection = args.selection || args.element; + + if ( activeToolbar && activeToolbar !== args.toolbar ) { + activeToolbar.hide(); + } + + if ( args.toolbar ) { + if ( activeToolbar !== args.toolbar ) { + activeToolbar = args.toolbar; + activeToolbar.show(); + } else { + activeToolbar.reposition(); + } + } else { + activeToolbar = false; + } + } ); + + function hide( event ) { + // if ( activeToolbar ) { + // if ( activeToolbar.tempHide || event.type === 'hide' ) { + // activeToolbar.hide(); + // activeToolbar = false; + // } else if ( ( + // event.type === 'resizewindow' || + // event.type === 'scrollwindow' || + // event.type === 'resize' || + // event.type === 'scroll' + // ) && ! activeToolbar.blockHide ) { + // clearTimeout( timeout ); + + // timeout = setTimeout( function() { + // if ( activeToolbar && typeof activeToolbar.show === 'function' ) { + // activeToolbar.scrolling = false; + // activeToolbar.show(); + // } + // }, 250 ); + + // activeToolbar.scrolling = true; + // activeToolbar.hide(); + // } + // } + } + + // For full height editor. + editor.on( 'resizewindow scrollwindow', hide ); + // For scrollable editor. + editor.dom.bind( editor.getWin(), 'resize scroll', hide ); + + editor.on( 'remove', function() { + editor.off( 'resizewindow scrollwindow', hide ); + editor.dom.unbind( editor.getWin(), 'resize scroll', hide ); + } ); + + editor.on( 'blur hide', hide ); + + editor.wp = editor.wp || {}; + editor.wp._createToolbar = create; + }, true ); + }); +} )( window.tinymce ); diff --git a/shared/tinymce/wplink.js b/shared/tinymce/wplink.js new file mode 100644 index 00000000000000..4bec22f575fa22 --- /dev/null +++ b/shared/tinymce/wplink.js @@ -0,0 +1,613 @@ +( function( tinymce ) { + tinymce.ui.Factory.add( 'WPLinkPreview', tinymce.ui.Control.extend( { + url: '#', + renderHtml: function() { + return ( + '' + ); + }, + setURL: function( url ) { + var index, lastIndex; + + if ( this.url !== url ) { + this.url = url; + + url = window.decodeURIComponent( url ); + + url = url.replace( /^(?:https?:)?\/\/(?:www\.)?/, '' ); + + if ( ( index = url.indexOf( '?' ) ) !== -1 ) { + url = url.slice( 0, index ); + } + + if ( ( index = url.indexOf( '#' ) ) !== -1 ) { + url = url.slice( 0, index ); + } + + url = url.replace( /(?:index)?\.html$/, '' ); + + if ( url.charAt( url.length - 1 ) === '/' ) { + url = url.slice( 0, -1 ); + } + + // If nothing's left (maybe the URL was just a fragment), use the whole URL. + if ( url === '' ) { + url = this.url; + } + + // If the URL is longer that 40 chars, concatenate the beginning (after the domain) and ending with ... + if ( url.length > 40 && ( index = url.indexOf( '/' ) ) !== -1 && ( lastIndex = url.lastIndexOf( '/' ) ) !== -1 && lastIndex !== index ) { + // If the beginning + ending are shorter that 40 chars, show more of the ending + if ( index + url.length - lastIndex < 40 ) { + lastIndex = -( 40 - ( index + 1 ) ); + } + + url = url.slice( 0, index + 1 ) + '\u2026' + url.slice( lastIndex ); + } + + tinymce.$( this.getEl().firstChild ).attr( 'href', this.url ).text( url ); + } + } + } ) ); + + tinymce.ui.Factory.add( 'WPLinkInput', tinymce.ui.Control.extend( { + renderHtml: function() { + return ( + '' + ); + }, + setURL: function( url ) { + this.getEl().firstChild.value = url; + }, + getURL: function() { + return tinymce.trim( this.getEl().firstChild.value ); + }, + getLinkText: function() { + var text = this.getEl().firstChild.nextSibling.value; + + if ( ! tinymce.trim( text ) ) { + return ''; + } + + return text.replace( /[\r\n\t ]+/g, ' ' ); + }, + reset: function() { + var urlInput = this.getEl().firstChild; + + urlInput.value = ''; + urlInput.nextSibling.value = ''; + } + } ) ); + + tinymce.PluginManager.add( 'wplink', function( editor ) { + var toolbar; + var editToolbar; + var previewInstance; + var inputInstance; + var linkNode; + var doingUndoRedo; + var doingUndoRedoTimer; + var $ = window.jQuery; + var emailRegex = /^(mailto:)?[a-z0-9._%+-]+@[a-z0-9][a-z0-9.-]*\.[a-z]{2,63}$/i; + var urlRegex1 = /^https?:\/\/([^\s/?.#-][^\s\/?.#]*\.?)+(\/[^\s"]*)?$/i; + var urlRegex2 = /^https?:\/\/[^\/]+\.[^\/]+($|\/)/i; + var speak = ( typeof window.wp !== 'undefined' && window.wp.a11y && window.wp.a11y.speak ) ? window.wp.a11y.speak : function() {}; + var hasLinkError = false; + + function getSelectedLink() { + var href, html, + node = editor.selection.getNode(), + link = editor.dom.getParent( node, 'a[href]' ); + + if ( ! link ) { + html = editor.selection.getContent({ format: 'raw' }); + + if ( html && html.indexOf( '' ) !== -1 ) { + href = html.match( /href="([^">]+)"/ ); + + if ( href && href[1] ) { + link = editor.$( 'a[href="' + href[1] + '"]', node )[0]; + } + + if ( link ) { + editor.selection.select( link ); + } + } + } + + return link; + } + + function removePlaceholders() { + editor.$( 'a' ).each( function( i, element ) { + var $element = editor.$( element ); + + if ( $element.attr( 'href' ) === '_wp_link_placeholder' ) { + editor.dom.remove( element, true ); + } else if ( $element.attr( 'data-wplink-edit' ) ) { + $element.attr( 'data-wplink-edit', null ); + } + }); + } + + function removePlaceholderStrings( content, dataAttr ) { + return content.replace( /(]+>)([\s\S]*?)<\/a>/g, function( all, tag, text ) { + if ( tag.indexOf( ' href="_wp_link_placeholder"' ) > -1 ) { + return text; + } + + if ( dataAttr ) { + tag = tag.replace( / data-wplink-edit="true"/g, '' ); + } + + tag = tag.replace( / data-wplink-url-error="true"/g, '' ); + + return tag + text + ''; + }); + } + + function checkLink( node ) { + var $link = editor.$( node ); + var href = $link.attr( 'href' ); + + if ( ! href || typeof $ === 'undefined' ) { + return; + } + + hasLinkError = false; + + if ( /^http/i.test( href ) && ( ! urlRegex1.test( href ) || ! urlRegex2.test( href ) ) ) { + hasLinkError = true; + $link.attr( 'data-wplink-url-error', 'true' ); + speak( editor.translate( 'Warning: the link has been inserted but may have errors. Please test it.' ), 'assertive' ); + } else { + $link.removeAttr( 'data-wplink-url-error' ); + } + } + + editor.on( 'preinit', function() { + if ( editor.wp && editor.wp._createToolbar ) { + toolbar = editor.wp._createToolbar( [ + 'wp_link_preview', + 'wp_link_edit', + 'wp_link_remove' + ], true ); + + var editButtons = [ + 'wp_link_input', + 'wp_link_apply' + ]; + + if ( typeof window.wpLink !== 'undefined' ) { + editButtons.push( 'wp_link_advanced' ); + } + + editToolbar = editor.wp._createToolbar( editButtons, true ); + + editToolbar.on( 'show', function() { + if ( typeof window.wpLink === 'undefined' || ! window.wpLink.modalOpen ) { + window.setTimeout( function() { + var element = editToolbar.$el.find( 'input' )[0], + selection = linkNode && ( linkNode.textContent || linkNode.innerText ); + + if ( element ) { + if ( ! element.value && selection && typeof window.wpLink !== 'undefined' ) { + element.value = window.wpLink.getUrlFromSelection( selection ); + } + + if ( ! doingUndoRedo ) { + element.focus(); + element.select(); + } + } + } ); + } + } ); + + editToolbar.on( 'hide', function() { + if ( ! editToolbar.scrolling ) { + editor.execCommand( 'wp_link_cancel' ); + } + } ); + } + } ); + + editor.addCommand( 'WP_Link', function() { + if ( tinymce.Env.ie && tinymce.Env.ie < 10 && typeof window.wpLink !== 'undefined' ) { + window.wpLink.open( editor.id ); + return; + } + + linkNode = getSelectedLink(); + editToolbar.tempHide = false; + + if ( linkNode ) { + editor.dom.setAttribs( linkNode, { 'data-wplink-edit': true } ); + } else { + removePlaceholders(); + editor.execCommand( 'mceInsertLink', false, { href: '_wp_link_placeholder' } ); + + linkNode = editor.$( 'a[href="_wp_link_placeholder"]' )[0]; + editor.nodeChanged(); + } + } ); + + editor.addCommand( 'wp_link_apply', function() { + if ( editToolbar.scrolling ) { + return; + } + + var href, text; + + if ( linkNode ) { + href = inputInstance.getURL(); + text = inputInstance.getLinkText(); + editor.focus(); + + if ( ! href ) { + editor.dom.remove( linkNode, true ); + return; + } + + if ( ! /^(?:[a-z]+:|#|\?|\.|\/)/.test( href ) && ! emailRegex.test( href ) ) { + href = 'http://' + href; + } + + editor.dom.setAttribs( linkNode, { href: href, 'data-wplink-edit': null } ); + + if ( ! tinymce.trim( linkNode.innerHTML ) ) { + editor.$( linkNode ).text( text || href ); + } + + checkLink( linkNode ); + } + + inputInstance.reset(); + editor.nodeChanged(); + + // Audible confirmation message when a link has been inserted in the Editor. + if ( typeof window.wpLinkL10n !== 'undefined' && ! hasLinkError ) { + speak( window.wpLinkL10n.linkInserted ); + } + } ); + + editor.addCommand( 'wp_link_cancel', function() { + if ( ! editToolbar.tempHide ) { + inputInstance.reset(); + removePlaceholders(); + } + } ); + + editor.addCommand( 'wp_unlink', function() { + editor.execCommand( 'unlink' ); + editToolbar.tempHide = false; + editor.execCommand( 'wp_link_cancel' ); + } ); + + // WP default shortcuts + editor.addShortcut( 'access+a', '', 'WP_Link' ); + editor.addShortcut( 'access+s', '', 'wp_unlink' ); + // The "de-facto standard" shortcut, see #27305 + editor.addShortcut( 'meta+k', '', 'WP_Link' ); + + editor.addButton( 'link', { + icon: 'link', + tooltip: 'Insert/edit link', + cmd: 'WP_Link', + stateSelector: 'a[href]' + }); + + editor.addButton( 'unlink', { + icon: 'unlink', + tooltip: 'Remove link', + cmd: 'unlink' + }); + + editor.addMenuItem( 'link', { + icon: 'link', + text: 'Insert/edit link', + cmd: 'WP_Link', + stateSelector: 'a[href]', + context: 'insert', + prependToContext: true + }); + + editor.on( 'pastepreprocess', function( event ) { + var pastedStr = event.content, + regExp = /^(?:https?:)?\/\/\S+$/i; + + if ( ! editor.selection.isCollapsed() && ! regExp.test( editor.selection.getContent() ) ) { + pastedStr = pastedStr.replace( /<[^>]+>/g, '' ); + pastedStr = tinymce.trim( pastedStr ); + + if ( regExp.test( pastedStr ) ) { + editor.execCommand( 'mceInsertLink', false, { + href: editor.dom.decode( pastedStr ) + } ); + + event.preventDefault(); + } + } + } ); + + // Remove any remaining placeholders on saving. + editor.on( 'savecontent', function( event ) { + event.content = removePlaceholderStrings( event.content, true ); + }); + + // Prevent adding undo levels on inserting link placeholder. + editor.on( 'BeforeAddUndo', function( event ) { + if ( event.lastLevel && event.lastLevel.content && event.level.content && + event.lastLevel.content === removePlaceholderStrings( event.level.content ) ) { + + event.preventDefault(); + } + }); + + // When doing undo and redo with keyboard shortcuts (Ctrl|Cmd+Z, Ctrl|Cmd+Shift+Z, Ctrl|Cmd+Y), + // set a flag to not focus the inline dialog. The editor has to remain focused so the users can do consecutive undo/redo. + editor.on( 'keydown', function( event ) { + if ( event.keyCode === 27 ) { // Esc + editor.execCommand( 'wp_link_cancel' ); + } + + if ( event.altKey || ( tinymce.Env.mac && ( ! event.metaKey || event.ctrlKey ) ) || + ( ! tinymce.Env.mac && ! event.ctrlKey ) ) { + + return; + } + + if ( event.keyCode === 89 || event.keyCode === 90 ) { // Y or Z + doingUndoRedo = true; + + window.clearTimeout( doingUndoRedoTimer ); + doingUndoRedoTimer = window.setTimeout( function() { + doingUndoRedo = false; + }, 500 ); + } + } ); + + editor.addButton( 'wp_link_preview', { + type: 'WPLinkPreview', + onPostRender: function() { + previewInstance = this; + } + } ); + + editor.addButton( 'wp_link_input', { + type: 'WPLinkInput', + onPostRender: function() { + var element = this.getEl(), + input = element.firstChild, + $input, cache, last; + + inputInstance = this; + + if ( $ && $.ui && $.ui.autocomplete ) { + $input = $( input ); + + $input.on( 'keydown', function() { + $input.removeAttr( 'aria-activedescendant' ); + } ) + .autocomplete( { + source: function( request, response ) { + if ( last === request.term ) { + response( cache ); + return; + } + + if ( /^https?:/.test( request.term ) || request.term.indexOf( '.' ) !== -1 ) { + return response(); + } + + $.post( window.ajaxurl, { + action: 'wp-link-ajax', + page: 1, + search: request.term, + _ajax_linking_nonce: $( '#_ajax_linking_nonce' ).val() + }, function( data ) { + cache = data; + response( data ); + }, 'json' ); + + last = request.term; + }, + focus: function( event, ui ) { + $input.attr( 'aria-activedescendant', 'mce-wp-autocomplete-' + ui.item.ID ); + /* + * Don't empty the URL input field, when using the arrow keys to + * highlight items. See api.jqueryui.com/autocomplete/#event-focus + */ + event.preventDefault(); + }, + select: function( event, ui ) { + $input.val( ui.item.permalink ); + $( element.firstChild.nextSibling ).val( ui.item.title ); + + if ( 9 === event.keyCode && typeof window.wpLinkL10n !== 'undefined' ) { + // Audible confirmation message when a link has been selected. + speak( window.wpLinkL10n.linkSelected ); + } + + return false; + }, + open: function() { + $input.attr( 'aria-expanded', 'true' ); + editToolbar.blockHide = true; + }, + close: function() { + $input.attr( 'aria-expanded', 'false' ); + editToolbar.blockHide = false; + }, + minLength: 2, + position: { + my: 'left top+2' + }, + messages: { + noResults: ( typeof window.uiAutocompleteL10n !== 'undefined' ) ? window.uiAutocompleteL10n.noResults : '', + results: function( number ) { + if ( typeof window.uiAutocompleteL10n !== 'undefined' ) { + if ( number > 1 ) { + return window.uiAutocompleteL10n.manyResults.replace( '%d', number ); + } + + return window.uiAutocompleteL10n.oneResult; + } + } + } + } ).autocomplete( 'instance' )._renderItem = function( ul, item ) { + return $( '
  • ' ) + .append( '' + item.title + ' ' + item.info + '' ) + .appendTo( ul ); + }; + + $input.attr( { + 'role': 'combobox', + 'aria-autocomplete': 'list', + 'aria-expanded': 'false', + 'aria-owns': $input.autocomplete( 'widget' ).attr( 'id' ) + } ) + .on( 'focus', function() { + var inputValue = $input.val(); + /* + * Don't trigger a search if the URL field already has a link or is empty. + * Also, avoids screen readers announce `No search results`. + */ + if ( inputValue && ! /^https?:/.test( inputValue ) ) { + $input.autocomplete( 'search' ); + } + } ) + // Returns a jQuery object containing the menu element. + .autocomplete( 'widget' ) + .addClass( 'wplink-autocomplete' ) + .attr( 'role', 'listbox' ) + .removeAttr( 'tabindex' ) // Remove the `tabindex=0` attribute added by jQuery UI. + /* + * Looks like Safari and VoiceOver need an `aria-selected` attribute. See ticket #33301. + * The `menufocus` and `menublur` events are the same events used to add and remove + * the `ui-state-focus` CSS class on the menu items. See jQuery UI Menu Widget. + */ + .on( 'menufocus', function( event, ui ) { + ui.item.attr( 'aria-selected', 'true' ); + }) + .on( 'menublur', function() { + /* + * The `menublur` event returns an object where the item is `null` + * so we need to find the active item with other means. + */ + $( this ).find( '[aria-selected="true"]' ).removeAttr( 'aria-selected' ); + }); + } + + tinymce.$( input ).on( 'keydown', function( event ) { + if ( event.keyCode === 13 ) { + editor.execCommand( 'wp_link_apply' ); + event.preventDefault(); + } + } ); + } + } ); + + editor.on( 'wptoolbar', function( event ) { + var linkNode = editor.dom.getParent( event.element, 'a' ), + $linkNode, href, edit; + + if ( typeof window.wpLink !== 'undefined' && window.wpLink.modalOpen ) { + editToolbar.tempHide = true; + return; + } + + editToolbar.tempHide = false; + + if ( linkNode ) { + $linkNode = editor.$( linkNode ); + href = $linkNode.attr( 'href' ); + edit = $linkNode.attr( 'data-wplink-edit' ); + + if ( href === '_wp_link_placeholder' || edit ) { + if ( href !== '_wp_link_placeholder' && ! inputInstance.getURL() ) { + inputInstance.setURL( href ); + } + + event.element = linkNode; + event.toolbar = editToolbar; + } else if ( href && ! $linkNode.find( 'img' ).length ) { + previewInstance.setURL( href ); + event.element = linkNode; + event.toolbar = toolbar; + + if ( $linkNode.attr( 'data-wplink-url-error' ) === 'true' ) { + toolbar.$el.find( '.wp-link-preview a' ).addClass( 'wplink-url-error' ); + } else { + toolbar.$el.find( '.wp-link-preview a' ).removeClass( 'wplink-url-error' ); + hasLinkError = false; + } + } + } else if ( editToolbar.visible() ) { + editor.execCommand( 'wp_link_cancel' ); + } + } ); + + editor.addButton( 'wp_link_edit', { + tooltip: 'Edit ', // trailing space is needed, used for context + icon: 'gridicons-pencil', + cmd: 'WP_Link' + } ); + + editor.addButton( 'wp_link_remove', { + tooltip: 'Remove link', + icon: 'gridicons-link-break', + cmd: 'wp_unlink' + } ); + + editor.addButton( 'wp_link_advanced', { + tooltip: 'Link options', + icon: 'dashicon dashicons-admin-generic', + onclick: function() { + if ( typeof window.wpLink !== 'undefined' ) { + var url = inputInstance.getURL() || null, + text = inputInstance.getLinkText() || null; + + /* + * Accessibility note: moving focus back to the editor confuses + * screen readers. They will announce again the Editor ARIA role + * `application` and the iframe `title` attribute. + * + * Unfortunately IE looses the selection when the editor iframe + * looses focus, so without returning focus to the editor, the code + * in the modal will not be able to get the selection, place the caret + * at the same location, etc. + */ + if ( tinymce.Env.ie ) { + editor.focus(); // Needed for IE + } + + window.wpLink.open( editor.id, url, text, linkNode ); + + editToolbar.tempHide = true; + inputInstance.reset(); + } + } + } ); + + editor.addButton( 'wp_link_apply', { + tooltip: 'Apply', + icon: 'gridicons-checkmark', + cmd: 'wp_link_apply', + classes: 'widget btn primary' + } ); + + return { + close: function() { + editToolbar.tempHide = false; + editor.execCommand( 'wp_link_cancel' ); + }, + checkLink: checkLink + }; + } ); +} )( window.tinymce ); diff --git a/shared/tinymce/wptextpattern.js b/shared/tinymce/wptextpattern.js new file mode 100644 index 00000000000000..236775034cce38 --- /dev/null +++ b/shared/tinymce/wptextpattern.js @@ -0,0 +1,348 @@ +/** + * Text pattern plugin for TinyMCE + * + * @since 4.3.0 + * + * This plugin can automatically format text patterns as you type. It includes several groups of patterns. + * + * Start of line patterns: + * As-you-type: + * - Unordered list (`* ` and `- `). + * - Ordered list (`1. ` and `1) `). + * + * On enter: + * - h2 (## ). + * - h3 (### ). + * - h4 (#### ). + * - h5 (##### ). + * - h6 (###### ). + * - blockquote (> ). + * - hr (---). + * + * Inline patterns: + * - (`) (backtick). + * + * If the transformation in unwanted, the user can undo the change by pressing backspace, + * using the undo shortcut, or the undo button in the toolbar. + * + * Setting for the patterns can be overridden by plugins by using the `tiny_mce_before_init` PHP filter. + * The setting name is `wptextpattern` and the value is an object containing override arrays for each + * patterns group. There are three groups: "space", "enter", and "inline". Example (PHP): + * + * add_filter( 'tiny_mce_before_init', 'my_mce_init_wptextpattern' ); + * function my_mce_init_wptextpattern( $init ) { + * $init['wptextpattern'] = wp_json_encode( array( + * 'inline' => array( + * array( 'delimiter' => '**', 'format' => 'bold' ), + * array( 'delimiter' => '__', 'format' => 'italic' ), + * ), + * ) ); + * + * return $init; + * } + * + * Note that setting this will override the default text patterns. You will need to include them + * in your settings array if you want to keep them working. + */ +( function( tinymce, setTimeout ) { + if ( tinymce.Env.ie && tinymce.Env.ie < 9 ) { + return; + } + + /** + * Escapes characters for use in a Regular Expression. + * + * @param {String} string Characters to escape + * + * @return {String} Escaped characters + */ + function escapeRegExp( string ) { + return string.replace( /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&' ); + } + + tinymce.PluginManager.add( 'wptextpattern', function( editor ) { + var VK = tinymce.util.VK; + var settings = editor.settings.wptextpattern || {}; + + var spacePatterns = settings.space || [ + { regExp: /^[*-]\s/, cmd: 'InsertUnorderedList' }, + { regExp: /^1[.)]\s/, cmd: 'InsertOrderedList' } + ]; + + var enterPatterns = settings.enter || [ + { start: '##', format: 'h2' }, + { start: '###', format: 'h3' }, + { start: '####', format: 'h4' }, + { start: '#####', format: 'h5' }, + { start: '######', format: 'h6' }, + { start: '>', format: 'blockquote' }, + { regExp: /^(-){3,}$/, element: 'hr' } + ]; + + var inlinePatterns = settings.inline || [ + { delimiter: '`', format: 'code' } + ]; + + var canUndo; + + editor.on( 'selectionchange', function() { + canUndo = null; + } ); + + editor.on( 'keydown', function( event ) { + if ( ( canUndo && event.keyCode === 27 /* ESCAPE */ ) || ( canUndo === 'space' && event.keyCode === VK.BACKSPACE ) ) { + editor.undoManager.undo(); + event.preventDefault(); + event.stopImmediatePropagation(); + } + + if ( VK.metaKeyPressed( event ) ) { + return; + } + + if ( event.keyCode === VK.ENTER ) { + enter(); + // Wait for the browser to insert the character. + } else if ( event.keyCode === VK.SPACEBAR ) { + setTimeout( space ); + } else if ( event.keyCode > 47 && ! ( event.keyCode >= 91 && event.keyCode <= 93 ) ) { + setTimeout( inline ); + } + }, true ); + + function inline() { + var rng = editor.selection.getRng(); + var node = rng.startContainer; + var offset = rng.startOffset; + var startOffset; + var endOffset; + var pattern; + var format; + var zero; + + // We need a non empty text node with an offset greater than zero. + if ( ! node || node.nodeType !== 3 || ! node.data.length || ! offset ) { + return; + } + + var string = node.data.slice( 0, offset ); + var lastChar = node.data.charAt( offset - 1 ); + + tinymce.each( inlinePatterns, function( p ) { + // Character before selection should be delimiter. + if ( lastChar !== p.delimiter.slice( -1 ) ) { + return; + } + + var escDelimiter = escapeRegExp( p.delimiter ); + var delimiterFirstChar = p.delimiter.charAt( 0 ); + var regExp = new RegExp( '(.*)' + escDelimiter + '.+' + escDelimiter + '$' ); + var match = string.match( regExp ); + + if ( ! match ) { + return; + } + + startOffset = match[1].length; + endOffset = offset - p.delimiter.length; + + var before = string.charAt( startOffset - 1 ); + var after = string.charAt( startOffset + p.delimiter.length ); + + // test*test* => format applied + // test *test* => applied + // test* test* => not applied + if ( startOffset && /\S/.test( before ) ) { + if ( /\s/.test( after ) || before === delimiterFirstChar ) { + return; + } + } + + // Do not replace when only whitespace and delimiter characters. + if ( ( new RegExp( '^[\\s' + escapeRegExp( delimiterFirstChar ) + ']+$' ) ).test( string.slice( startOffset, endOffset ) ) ) { + return; + } + + pattern = p; + + return false; + } ); + + if ( ! pattern ) { + return; + } + + format = editor.formatter.get( pattern.format ); + + if ( format && format[0].inline ) { + editor.undoManager.add(); + + editor.undoManager.transact( function() { + node.insertData( offset, '\uFEFF' ); + + node = node.splitText( startOffset ); + zero = node.splitText( offset - startOffset ); + + node.deleteData( 0, pattern.delimiter.length ); + node.deleteData( node.data.length - pattern.delimiter.length, pattern.delimiter.length ); + + editor.formatter.apply( pattern.format, {}, node ); + + editor.selection.setCursorLocation( zero, 1 ); + } ); + + // We need to wait for native events to be triggered. + setTimeout( function() { + canUndo = 'space'; + + editor.once( 'selectionchange', function() { + var offset; + + if ( zero ) { + offset = zero.data.indexOf( '\uFEFF' ); + + if ( offset !== -1 ) { + zero.deleteData( offset, offset + 1 ); + } + } + } ); + } ); + } + } + + function firstTextNode( node ) { + var parent = editor.dom.getParent( node, 'p' ), + child; + + if ( ! parent ) { + return; + } + + while ( child = parent.firstChild ) { + if ( child.nodeType !== 3 ) { + parent = child; + } else { + break; + } + } + + if ( ! child ) { + return; + } + + if ( ! child.data ) { + if ( child.nextSibling && child.nextSibling.nodeType === 3 ) { + child = child.nextSibling; + } else { + child = null; + } + } + + return child; + } + + function space() { + var rng = editor.selection.getRng(), + node = rng.startContainer, + parent, + text; + + if ( ! node || firstTextNode( node ) !== node ) { + return; + } + + parent = node.parentNode; + text = node.data; + + tinymce.each( spacePatterns, function( pattern ) { + var match = text.match( pattern.regExp ); + + if ( ! match || rng.startOffset !== match[0].length ) { + return; + } + + editor.undoManager.add(); + + editor.undoManager.transact( function() { + node.deleteData( 0, match[0].length ); + + if ( ! parent.innerHTML ) { + parent.appendChild( document.createElement( 'br' ) ); + } + + editor.selection.setCursorLocation( parent ); + editor.execCommand( pattern.cmd ); + } ); + + // We need to wait for native events to be triggered. + setTimeout( function() { + canUndo = 'space'; + } ); + + return false; + } ); + } + + function enter() { + var rng = editor.selection.getRng(), + start = rng.startContainer, + node = firstTextNode( start ), + i = enterPatterns.length, + text, pattern, parent; + + if ( ! node ) { + return; + } + + text = node.data; + + while ( i-- ) { + if ( enterPatterns[ i ].start ) { + if ( text.indexOf( enterPatterns[ i ].start ) === 0 ) { + pattern = enterPatterns[ i ]; + break; + } + } else if ( enterPatterns[ i ].regExp ) { + if ( enterPatterns[ i ].regExp.test( text ) ) { + pattern = enterPatterns[ i ]; + break; + } + } + } + + if ( ! pattern ) { + return; + } + + if ( node === start && tinymce.trim( text ) === pattern.start ) { + return; + } + + editor.once( 'keyup', function() { + editor.undoManager.add(); + + editor.undoManager.transact( function() { + if ( pattern.format ) { + editor.formatter.apply( pattern.format, {}, node ); + node.replaceData( 0, node.data.length, ltrim( node.data.slice( pattern.start.length ) ) ); + } else if ( pattern.element ) { + parent = node.parentNode && node.parentNode.parentNode; + + if ( parent ) { + parent.replaceChild( document.createElement( pattern.element ), node.parentNode ); + } + } + } ); + + // We need to wait for native events to be triggered. + setTimeout( function() { + canUndo = 'enter'; + } ); + } ); + } + + function ltrim( text ) { + return text ? text.replace( /^\s+/, '' ) : ''; + } + } ); +} )( window.tinymce, window.setTimeout ); diff --git a/style.css b/style.css index 772d289b0436ff..721ae0ad8904b7 100644 --- a/style.css +++ b/style.css @@ -38,7 +38,7 @@ h4, h5, h6, img { - font-family: "Merriweather", serif; + font-family: "Noto Serif", serif; margin: 15px 0; /* Uses paddings instead */ } @@ -93,7 +93,7 @@ h6:hover, p:hover, blockquote:hover, img:hover { - box-shadow: inset 0px 0px 0px 2px #e0e5e9; + box-shadow: inset 9px 0px 0px -7px #e0e5e9; } h1.is-selected, @@ -105,7 +105,7 @@ h6.is-selected, p.is-selected, blockquote.is-selected, img.is-selected { - box-shadow: inset 0px 0px 0px 2px #6d7882; + box-shadow: inset 0px 0px 0px 2px #e1e6ea; position: relative; z-index: 1; } @@ -163,25 +163,38 @@ img.is-selected { display: none; } - /** - * Inline controls + * Controls */ +.docked-controls { + position: absolute; + display: none; + z-index: 10; + transform: translateZ( 0 ); +} + +.docked-controls.is-image .block-image, +.docked-controls.is-text .block-text { + display: block; +} -.inline-controls { +.inline-controls, +.block-controls { background: #fff; - border: 1px solid #e1e6ea; + border: 1px solid #d8dbdf; box-shadow: 0px 3px 20px rgba( 18, 24, 30, .1 ), 0px 1px 3px rgba( 18, 24, 30, .1 ); color: #12181e; display: inline-block; height: 38px; - overflow: hidden; - position: absolute; - z-index: 10; + margin-left: 14px; +} + +.docked-controls.is-image .inline-controls { display: none; } -.inline-controls button { +.inline-controls button, +.block-controls button { background: #fff; border: none; width: 36px; @@ -190,31 +203,46 @@ img.is-selected { cursor: pointer; display: block; float: left; + color: #6d7882; } -.inline-controls button.do-not-work { +.inline-controls button.do-not-work, +.block-controls button.do-not-work { margin: 0 8px; width: auto; } -.inline-controls button:hover { - background: #f0f2f4; +.inline-controls button:hover, +.block-controls button:hover { + background: #f8f9f9; + outline: 1px solid #6d7882; + position: relative; } -.inline-controls button:focus { +.inline-controls button:focus, +.block-controls button:focus { outline: none; } -.control-group { +.control-group, { display: inline-block; margin-left: 20px; } -.inline-controls button.heading-dropdown { +.inline-controls button.heading-dropdown, +.block-controls button.heading-dropdown { width: 54px; position: relative; } +.inline-controls button.is-active, +.block-controls button.is-active { + background: #eef0f0; + outline: 1px solid #6d7882; + position: relative; + color: #3e444c; +} + .heading-dropdown .heading { position: absolute; left: 0; @@ -239,47 +267,10 @@ img.is-selected { bottom: 8px; } - -/** - * Block Controls - */ - -.block-controls { - background: #191e23; - display: inline-block; - max-height: 36px; - overflow: hidden; - position: absolute; - z-index: 10; - display: none; - transform: translateZ( 0 ); - } - -.block-controls button { - background: #191e23; - color: #fff; - border: none; - width: 36px; - height: 36px; - padding: 6px; - cursor: pointer; - display: block; - float: left; - } - -.block-controls button.is-active { - background: #6d7882; -} - .block-controls button { display: none; } -.block-controls.is-image .block-image, -.block-controls.is-text .block-text { - display: block; -} - /** * Block inserter and switcher */ @@ -301,18 +292,13 @@ img.is-selected { border: none; margin: 0; padding: 0; -} - -.insert-block svg, -.switch-block__block svg, -.switch-block__block label { + color: #87919d; cursor: pointer; - fill: #87919d; } -.insert-block__button:hover svg, -.switch-block__button:hover svg { - fill: #12181e; +.insert-block__button:hover, +.switch-block__button:hover { + color: #19a2d4; } .insert-block__button:focus, @@ -426,6 +412,15 @@ img.is-selected { .insert-block__content { max-height: 180px; overflow: auto; + box-shadow: inset 0px -4px 6px -2px rgba(224,229,233,1); +} + +.insert-block__content.is-bottom { + box-shadow: none; +} + +.insert-block__content:focus { + outline: none; } .insert_block__category-blocks { @@ -442,6 +437,7 @@ img.is-selected { align-items: center; cursor: pointer; } +.insert-block__block.is-active, .insert-block__block:hover, .switch-block__block:hover { background: #f0f2f4; @@ -456,6 +452,11 @@ img.is-selected { fill: #191e23; } +.insert-block__block svg { + widows: 24px; + height: 24px; +} + /* * Text actions */ diff --git a/tinymce-per-block/.babelrc b/tinymce-per-block/.babelrc new file mode 100644 index 00000000000000..2293ad41d1a26d --- /dev/null +++ b/tinymce-per-block/.babelrc @@ -0,0 +1,14 @@ +{ + "presets": [ + [ "es2015", { + "modules": false + } ], + "stage-2" + ], + "plugins": [ + [ "transform-react-jsx", { + "pragma": "createElement" + } ], + "lodash" + ] +} diff --git a/tinymce-per-block/.editorconfig b/tinymce-per-block/.editorconfig new file mode 100644 index 00000000000000..667a507d56e975 --- /dev/null +++ b/tinymce-per-block/.editorconfig @@ -0,0 +1,16 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = tab +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[package.json] +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/tinymce-per-block/.eslintignore b/tinymce-per-block/.eslintignore new file mode 100644 index 00000000000000..0b6bacfd9731bf --- /dev/null +++ b/tinymce-per-block/.eslintignore @@ -0,0 +1 @@ +src/parsers/block/grammar.js diff --git a/tinymce-per-block/.eslintrc.json b/tinymce-per-block/.eslintrc.json new file mode 100644 index 00000000000000..4a800f0d5d04e2 --- /dev/null +++ b/tinymce-per-block/.eslintrc.json @@ -0,0 +1,19 @@ +{ + "root": true, + "extends": "wpcalypso/react", + "parser": "babel-eslint", + "env": { + "browser": true, + "node": true + }, + "settings": { + "react": { + "pragma": "createElement" + } + }, + "rules": { + "camelcase": 0, + "max-len": [ 2, { "code": 120 } ], + "react/jsx-no-bind": 0 + } +} diff --git a/tinymce-per-block/README.md b/tinymce-per-block/README.md new file mode 100644 index 00000000000000..e3c44a54606440 --- /dev/null +++ b/tinymce-per-block/README.md @@ -0,0 +1,119 @@ +"TinyMCE Per Block" Prototype +============================= + +This is a WordPress editor technical prototype to explore the feasability of decorating blocks as editable, initializing TinyMCE on only those blocks for which it is required. + +## Demo + +https://wordpress.github.io/gutenberg/tinymce-per-block/ + +## Background + +This prototype implements ideas explored in the Make WordPress Core's [Editor Technical Overview](https://make.wordpress.org/core/2017/01/17/editor-technical-overview/) blog post. Instead of expecting TinyMCE to manage the entire markup of a post, this prototype instead uses a [formal post grammar](https://github.com/Automattic/wp-post-grammar) to parse a post's content in the browser client. The JavaScript data structure of the post content is then used to render a set of controls which form the visual representation of the editor. The look and feel of these controls can be extended through an included block registration API, with backwards compatibility for posts preserved by either a generic block type or automated migration (a solution is yet to be decided). + +Examples: + +- [Text Block](./src/blocks/text-block) +- [Image Block](./src/blocks/image-block) + +## Data Flow + +![TinyMCE per block Data Flow](./doc/data-flow.png) + +## Block API + +### Registering a block +We do so by calling `wpblocks.registerBlock` with a name and an object defining the block to be registered. This object has the following attributes: + + * title: A string representing the label of the block + * icon: The wpblocks component to display the icon of the block + * form: The wpblocks component to display the block form (controls and contenteditables) + * parse: A function that takes a raw grammar block as argument and returns the parsed block (if this function returns `false` we fallback to an HTML block) + * serialize: The opposite of `parse`, transforms the current block to a raw grammar block + * create: A function that returns an empty block object + * transformations: An array of possible transformations. A transformation is the ability for a block to be switched to another block (for example, transforming a heading block to a text block) + * merge: When the user hits backspace at the beginning of the blocks, the current block needs to be merged with the previous blocks. This attribute is an array of possible merge functions depending on the blockType. + + +### The Block Form component + +It’s a wpblocks component (Abstraction of React for now, could be any vdom library) responsible of rendering the block form to edit the content of the blocks. + +For example, a text block can show a TinyMCE instance. A quote block can render a TinyMCE instance for the content and a simple textarea for the cite etc… +Editor commands + +The form component can trigger commands in response to any user event. For now the editor can handle these type of commands: change, append, remove, mergeWithPrevious, focus, moveCurosrUp, moveCurosrDown, moveBlockUp, moveBlockDown, select, unselect, hover, unhover. + +#### Commands + +The form receives an `api` object as a prop containing the different commands callbacks. + +*change:* This command is triggered when the block changes, for example when the user type into the tinymce instance of a text block, or any attribute change. + +*appendBlock:* This command should be triggered to append a new block right after the current block, for example when hitting “Enter” on a paragraph block + +*remove:* This command should be triggered to remove the current block, for example when typing “backspace” on an empty paragraph block + +*mergeWithPrevious:* This command should be triggered to ask for a merging the current block with the previous one. For example when we hit “backspace” and we’re focusing the beginning of a block paragraph + +*focus:* This command should be triggered to focus the current block, the focus config is a free object passed as an argument to this helper, it contains all the data necessary to focus the current block at the right position. + +*moveCursorUp:* This command should be triggered to ask for moving the cursor to the previous block for example when we hit the “up” arrow and we’re focusing the beginning of a block paragraph. + +*moveCursorDown:* This command should be triggered to ask for moving the cursor to the next block for example when we hit the “down” arrow and we’re focusing the end of a block paragraph. +Block form interface +A block form should also implement some functions in response to other block commands: + +*moveBlockUp:* Moves the block up + +*moveBlockDown:* Moves the block down + +*select:* Selects the current block + +*unselect:* Unselects the current block + +*hover:* to be called when the current block is hovered + +*unhover:* to be called when the current block is "unhovered" + +#### Form Props + +The form also receives the following props: + +*isFocused and focusConfig:* A block form should watch changes to these props and focus in consequence. +Utils + +*isSelected:* Whether the block is selected + +*isHovevered:* Whether the block is hovered + +To ease writing block forms in the context of this prototype, we could reuse the two generic components: + +#### Helpers + +**EditableComponent:** which is an enhanced TinyMCE wrapper to catch different events (moveUp, moveDown, remove, mergeWithPrevious …) + +**EnhancedInputComponent:** which is an autogrowing textarea wrapper. It also catches the different events (moveUp, moveDown, removePrevious, ….) + + +## Development + +You must first [download and install Node.js](https://nodejs.org/en/download/) before you can build or develop this prototype. Next, in your terminal, navigate to the project directory and install dependencies: + +``` +npm install +``` + +**To develop with automatic recompilation:** + +``` +npm run dev +``` + +You can now visit [http://localhost:8081](http://localhost:8081) in your browser. + +**To compile changes for commit:** + +``` +npm run build +``` diff --git a/tinymce-per-block/build/app.js b/tinymce-per-block/build/app.js new file mode 100644 index 00000000000000..02c64addc6462e --- /dev/null +++ b/tinymce-per-block/build/app.js @@ -0,0 +1,35 @@ +!function(e){function t(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,t),r.l=!0,r.exports}var n={};return t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,o){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:o})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=436)}([function(e,t){function n(){throw new Error("setTimeout has not been defined")}function o(){throw new Error("clearTimeout has not been defined")}function r(e){if(l===setTimeout)return setTimeout(e,0);if((l===n||!l)&&setTimeout)return l=setTimeout,setTimeout(e,0);try{return l(e,0)}catch(t){try{return l.call(null,e,0)}catch(t){return l.call(this,e,0)}}}function i(e){if(p===clearTimeout)return clearTimeout(e);if((p===o||!p)&&clearTimeout)return p=clearTimeout,clearTimeout(e);try{return p(e)}catch(t){try{return p.call(null,e)}catch(t){return p.call(this,e)}}}function a(){v&&f&&(v=!1,f.length?h=f.concat(h):m=-1,h.length&&u())}function u(){if(!v){var e=r(a);v=!0;for(var t=h.length;t;){for(f=h,h=[];++m1)for(var n=1;n1?t-1:0),o=1;o2?o-2:0),i=2;i1){for(var _=Array(b),E=0;E1){for(var b=Array(g),_=0;_-1?void 0:"production"!==t.env.NODE_ENV?u(!1,"EventPluginRegistry: Cannot inject event plugins that do not exist in the plugin ordering, `%s`.",e):a("96",e),!l.plugins[o]){n.extractEvents?void 0:"production"!==t.env.NODE_ENV?u(!1,"EventPluginRegistry: Event plugins must implement an `extractEvents` method, but `%s` does not.",e):a("97",e),l.plugins[o]=n;var i=n.eventTypes;for(var p in i)r(i[p],n,p)?void 0:"production"!==t.env.NODE_ENV?u(!1,"EventPluginRegistry: Failed to publish event `%s` for plugin `%s`.",p,e):a("98",p,e)}}}function r(e,n,o){l.eventNameDispatchConfigs.hasOwnProperty(o)?"production"!==t.env.NODE_ENV?u(!1,"EventPluginHub: More than one plugin attempted to publish the same event name, `%s`.",o):a("99",o):void 0,l.eventNameDispatchConfigs[o]=e;var r=e.phasedRegistrationNames;if(r){for(var c in r)if(r.hasOwnProperty(c)){var s=r[c];i(s,n,o)}return!0}return!!e.registrationName&&(i(e.registrationName,n,o),!0)}function i(e,n,o){if(l.registrationNameModules[e]?"production"!==t.env.NODE_ENV?u(!1,"EventPluginHub: More than one plugin attempted to publish the same registration name, `%s`.",e):a("100",e):void 0,l.registrationNameModules[e]=n,l.registrationNameDependencies[e]=n.eventTypes[o].dependencies,"production"!==t.env.NODE_ENV){var r=e.toLowerCase();l.possibleRegistrationNames[r]=e,"onDoubleClick"===e&&(l.possibleRegistrationNames.ondblclick=e)}}var a=n(4),u=n(2),c=null,s={},l={plugins:[],eventNameDispatchConfigs:{},registrationNameModules:{},registrationNameDependencies:{},possibleRegistrationNames:"production"!==t.env.NODE_ENV?{}:null,injectEventPluginOrder:function(e){c?"production"!==t.env.NODE_ENV?u(!1,"EventPluginRegistry: Cannot inject event plugin ordering more than once. You are likely trying to load more than one copy of React."):a("101"):void 0,c=Array.prototype.slice.call(e),o()},injectEventPluginsByName:function(e){var n=!1;for(var r in e)if(e.hasOwnProperty(r)){var i=e[r];s.hasOwnProperty(r)&&s[r]===i||(s[r]?"production"!==t.env.NODE_ENV?u(!1,"EventPluginRegistry: Cannot inject two different event plugins using the same name, `%s`.",r):a("102",r):void 0,s[r]=i,n=!0)}n&&o()},getPluginModuleForEvent:function(e){var t=e.dispatchConfig;if(t.registrationName)return l.registrationNameModules[t.registrationName]||null;if(void 0!==t.phasedRegistrationNames){var n=t.phasedRegistrationNames;for(var o in n)if(n.hasOwnProperty(o)){var r=l.registrationNameModules[n[o]];if(r)return r}}return null},_resetEventPlugins:function(){c=null;for(var e in s)s.hasOwnProperty(e)&&delete s[e];l.plugins.length=0;var n=l.eventNameDispatchConfigs;for(var o in n)n.hasOwnProperty(o)&&delete n[o];var r=l.registrationNameModules;for(var i in r)r.hasOwnProperty(i)&&delete r[i];if("production"!==t.env.NODE_ENV){var a=l.possibleRegistrationNames;for(var u in a)a.hasOwnProperty(u)&&delete a[u]}}};e.exports=l}).call(t,n(0))},function(e,t,n){"use strict";function o(e){return Object.prototype.hasOwnProperty.call(e,v)||(e[v]=f++,p[e[v]]={}),p[e[v]]}var r,i=n(5),a=n(50),u=n(342),c=n(147),s=n(378),l=n(90),p={},d=!1,f=0,h={topAbort:"abort",topAnimationEnd:s("animationend")||"animationend",topAnimationIteration:s("animationiteration")||"animationiteration",topAnimationStart:s("animationstart")||"animationstart",topBlur:"blur",topCanPlay:"canplay",topCanPlayThrough:"canplaythrough",topChange:"change",topClick:"click",topCompositionEnd:"compositionend",topCompositionStart:"compositionstart",topCompositionUpdate:"compositionupdate",topContextMenu:"contextmenu",topCopy:"copy",topCut:"cut",topDoubleClick:"dblclick",topDrag:"drag",topDragEnd:"dragend",topDragEnter:"dragenter",topDragExit:"dragexit",topDragLeave:"dragleave",topDragOver:"dragover",topDragStart:"dragstart",topDrop:"drop",topDurationChange:"durationchange",topEmptied:"emptied",topEncrypted:"encrypted",topEnded:"ended",topError:"error",topFocus:"focus",topInput:"input",topKeyDown:"keydown",topKeyPress:"keypress",topKeyUp:"keyup",topLoadedData:"loadeddata",topLoadedMetadata:"loadedmetadata",topLoadStart:"loadstart",topMouseDown:"mousedown",topMouseMove:"mousemove",topMouseOut:"mouseout",topMouseOver:"mouseover",topMouseUp:"mouseup",topPaste:"paste",topPause:"pause",topPlay:"play",topPlaying:"playing",topProgress:"progress",topRateChange:"ratechange",topScroll:"scroll",topSeeked:"seeked",topSeeking:"seeking",topSelectionChange:"selectionchange",topStalled:"stalled",topSuspend:"suspend",topTextInput:"textInput",topTimeUpdate:"timeupdate",topTouchCancel:"touchcancel",topTouchEnd:"touchend",topTouchMove:"touchmove",topTouchStart:"touchstart",topTransitionEnd:s("transitionend")||"transitionend",topVolumeChange:"volumechange",topWaiting:"waiting",topWheel:"wheel"},v="_reactListenersID"+String(Math.random()).slice(2),m=i({},u,{ReactEventListener:null,injection:{injectReactEventListener:function(e){e.setHandleTopLevel(m.handleTopLevel),m.ReactEventListener=e}},setEnabled:function(e){m.ReactEventListener&&m.ReactEventListener.setEnabled(e)},isEnabled:function(){return!(!m.ReactEventListener||!m.ReactEventListener.isEnabled())},listenTo:function(e,t){for(var n=t,r=o(n),i=a.registrationNameDependencies[e],u=0;u]/;e.exports=r},function(e,t,n){"use strict";var o,r=n(8),i=n(79),a=/^[ \r\n\t\f]/,u=/<(!--|link|noscript|meta|script|style)[ \r\n\t\f\/>]/,c=n(86),s=c(function(e,t){if(e.namespaceURI!==i.svg||"innerHTML"in e)e.innerHTML=t;else{o=o||document.createElement("div"),o.innerHTML=""+t+"";for(var n=o.firstChild;n.firstChild;)e.appendChild(n.firstChild)}});if(r.canUseDOM){var l=document.createElement("div");l.innerHTML=" ",""===l.innerHTML&&(s=function(e,t){if(e.parentNode&&e.parentNode.replaceChild(e,e),a.test(t)||"<"===t[0]&&u.test(t)){e.innerHTML=String.fromCharCode(65279)+t;var n=e.firstChild;1===n.data.length?e.removeChild(n):n.deleteData(0,1)}else e.innerHTML=t}),l=null}e.exports=s},function(e,t,n){"use strict";function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function a(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}var u=n(1),c=n(18),s=n.n(c),l=n(10),p=function(){function e(e,t){for(var n=0;n-1&&e%1==0&&e-1&&e%1==0&&e<=o}var o=9007199254740991;e.exports=n},function(e,t,n){function o(e){return a(e)?r(e,!0):i(e)}var r=n(106),i=n(218),a=n(37);e.exports=o},function(e,t,n){"use strict";(function(t){function o(e,t){return Array.isArray(t)&&(t=t[1]),t?t.nextSibling:e.firstChild}function r(e,t,n){l.insertTreeBefore(e,t,n)}function i(e,t,n){Array.isArray(t)?u(e,t[0],t[1],n):y(e,t,n)}function a(e,t){if(Array.isArray(t)){var n=t[1];t=t[0],c(e,t,n),e.removeChild(n)}e.removeChild(t)}function u(e,t,n,o){for(var r=t;;){var i=r.nextSibling;if(y(e,r,o),r===n)break;r=i}}function c(e,t,n){for(;;){var o=t.nextSibling;if(o===n)break;e.removeChild(o)}}function s(e,n,o){var r=e.parentNode,i=e.nextSibling;i===n?o&&y(r,document.createTextNode(o),i):o?(m(i,o),c(r,i,n)):c(r,e,n),"production"!==t.env.NODE_ENV&&f.debugTool.onHostOperation({instanceID:d.getInstanceFromNode(e)._debugID,type:"replace text",payload:o})}var l=n(30),p=n(315),d=n(6),f=n(13),h=n(86),v=n(55),m=n(154),y=h(function(e,t,n){e.insertBefore(t,n)}),g=p.dangerouslyReplaceNodeWithMarkup;"production"!==t.env.NODE_ENV&&(g=function(e,t,n){if(p.dangerouslyReplaceNodeWithMarkup(e,t),0!==n._debugID)f.debugTool.onHostOperation({instanceID:n._debugID,type:"replace with",payload:t.toString()});else{var o=d.getInstanceFromNode(t.node);0!==o._debugID&&f.debugTool.onHostOperation({instanceID:o._debugID,type:"mount",payload:t.toString()})}});var b={dangerouslyReplaceNodeWithMarkup:g,replaceDelimitedText:s,processUpdates:function(e,n){if("production"!==t.env.NODE_ENV)var u=d.getInstanceFromNode(e)._debugID;for(var c=0;c0&&o.length<20?n+" (keys: "+o.join(", ")+")":n}function i(e,n){var o=s.get(e);if(!o){if("production"!==t.env.NODE_ENV){var r=e.constructor;"production"!==t.env.NODE_ENV?f(!n,"%s(...): Can only update a mounted or mounting component. This usually means you called %s() on an unmounted component. This is a no-op. Please check the code for the %s component.",n,n,r&&(r.displayName||r.name)||"ReactClass"):void 0}return null}return"production"!==t.env.NODE_ENV&&("production"!==t.env.NODE_ENV?f(null==c.current,"%s(...): Cannot update during an existing state transition (such as within `render` or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to `componentWillMount`.",n):void 0),o}var a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},u=n(4),c=n(17),s=n(41),l=n(13),p=n(16),d=n(2),f=n(3),h={isMounted:function(e){if("production"!==t.env.NODE_ENV){var n=c.current;null!==n&&("production"!==t.env.NODE_ENV?f(n._warnedAboutRefsInRender,"%s is accessing isMounted inside its render() function. render() should be a pure function of props and state. It should never access something that requires stale data from the previous render, such as refs. Move this logic to componentDidMount and componentDidUpdate instead.",n.getName()||"A component"):void 0,n._warnedAboutRefsInRender=!0)}var o=s.get(e);return!!o&&!!o._renderedComponent},enqueueCallback:function(e,t,n){h.validateCallback(t,n);var r=i(e);return r?(r._pendingCallbacks?r._pendingCallbacks.push(t):r._pendingCallbacks=[t],void o(r)):null},enqueueCallbackInternal:function(e,t){e._pendingCallbacks?e._pendingCallbacks.push(t):e._pendingCallbacks=[t],o(e)},enqueueForceUpdate:function(e){var t=i(e,"forceUpdate");t&&(t._pendingForceUpdate=!0,o(t))},enqueueReplaceState:function(e,t){var n=i(e,"replaceState");n&&(n._pendingStateQueue=[t],n._pendingReplaceState=!0,o(n))},enqueueSetState:function(e,n){"production"!==t.env.NODE_ENV&&(l.debugTool.onSetState(),"production"!==t.env.NODE_ENV?f(null!=n,"setState(...): You passed an undefined or null state object; instead, use forceUpdate()."):void 0);var r=i(e,"setState");if(r){var a=r._pendingStateQueue||(r._pendingStateQueue=[]);a.push(n),o(r)}},enqueueElementInternal:function(e,t,n){e._pendingElement=t,e._context=n,o(e)},validateCallback:function(e,n){e&&"function"!=typeof e?"production"!==t.env.NODE_ENV?d(!1,"%s(...): Expected the last optional `callback` argument to be a function. Instead received: %s.",n,r(e)):u("122",n,r(e)):void 0}};e.exports=h}).call(t,n(0))},function(e,t,n){"use strict";var o=function(e){return"undefined"!=typeof MSApp&&MSApp.execUnsafeLocalFunction?function(t,n,o,r){MSApp.execUnsafeLocalFunction(function(){return e(t,n,o,r)})}:e};e.exports=o},function(e,t,n){"use strict";function o(e){var t,n=e.keyCode;return"charCode"in e?(t=e.charCode,0===t&&13===n&&(t=13)):t=n,t>=32||13===t?t:0}e.exports=o},function(e,t,n){"use strict";function o(e){var t=this,n=t.nativeEvent;if(n.getModifierState)return n.getModifierState(e);var o=i[e];return!!o&&!!n[o]}function r(e){return o}var i={Alt:"altKey",Control:"ctrlKey",Meta:"metaKey",Shift:"shiftKey"};e.exports=r},function(e,t,n){"use strict";function o(e){var t=e.target||e.srcElement||window;return t.correspondingUseElement&&(t=t.correspondingUseElement),3===t.nodeType?t.parentNode:t}e.exports=o},function(e,t,n){"use strict";/** + * Checks if an event is supported in the current execution environment. + * + * NOTE: This will not work correctly for non-generic events such as `change`, + * `reset`, `load`, `error`, and `select`. + * + * Borrows from Modernizr. + * + * @param {string} eventNameSuffix Event name, e.g. "click". + * @param {?boolean} capture Check if the capture phase is supported. + * @return {boolean} True if the event is supported. + * @internal + * @license Modernizr 3.0.0pre (Custom Build) | MIT + */ +function o(e,t){if(!i.canUseDOM||t&&!("addEventListener"in document))return!1;var n="on"+e,o=n in document;if(!o){var a=document.createElement("div");a.setAttribute(n,"return;"),o="function"==typeof a[n]}return!o&&r&&"wheel"===e&&(o=document.implementation.hasFeature("Events.wheel","3.0")),o}var r,i=n(8);i.canUseDOM&&(r=document.implementation&&document.implementation.hasFeature&&document.implementation.hasFeature("","")!==!0),e.exports=o},function(e,t,n){"use strict";function o(e,t){var n=null===e||e===!1,o=null===t||t===!1;if(n||o)return n===o;var i="undefined"==typeof e?"undefined":r(e),a="undefined"==typeof t?"undefined":r(t);return"string"===i||"number"===i?"string"===a||"number"===a:"object"===a&&e.type===t.type&&e.key===t.key}var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};e.exports=o},function(e,t,n){"use strict";(function(t){var o=n(5),r=n(14),i=n(3),a=r;if("production"!==t.env.NODE_ENV){var u=["address","applet","area","article","aside","base","basefont","bgsound","blockquote","body","br","button","caption","center","col","colgroup","dd","details","dir","div","dl","dt","embed","fieldset","figcaption","figure","footer","form","frame","frameset","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","iframe","img","input","isindex","li","link","listing","main","marquee","menu","menuitem","meta","nav","noembed","noframes","noscript","object","ol","p","param","plaintext","pre","script","section","select","source","style","summary","table","tbody","td","template","textarea","tfoot","th","thead","title","tr","track","ul","wbr","xmp"],c=["applet","caption","html","table","td","th","marquee","object","template","foreignObject","desc","title"],s=c.concat(["button"]),l=["dd","dt","li","option","optgroup","p","rp","rt"],p={current:null,formTag:null,aTagInScope:null,buttonTagInScope:null,nobrTagInScope:null,pTagInButtonScope:null,listItemTagAutoclosing:null,dlItemTagAutoclosing:null},d=function(e,t,n){var r=o({},e||p),i={tag:t,instance:n};return c.indexOf(t)!==-1&&(r.aTagInScope=null,r.buttonTagInScope=null,r.nobrTagInScope=null),s.indexOf(t)!==-1&&(r.pTagInButtonScope=null),u.indexOf(t)!==-1&&"address"!==t&&"div"!==t&&"p"!==t&&(r.listItemTagAutoclosing=null,r.dlItemTagAutoclosing=null),r.current=i,"form"===t&&(r.formTag=i),"a"===t&&(r.aTagInScope=i),"button"===t&&(r.buttonTagInScope=i),"nobr"===t&&(r.nobrTagInScope=i),"p"===t&&(r.pTagInButtonScope=i),"li"===t&&(r.listItemTagAutoclosing=i),"dd"!==t&&"dt"!==t||(r.dlItemTagAutoclosing=i),r},f=function(e,t){switch(t){case"select":return"option"===e||"optgroup"===e||"#text"===e;case"optgroup":return"option"===e||"#text"===e;case"option":return"#text"===e;case"tr":return"th"===e||"td"===e||"style"===e||"script"===e||"template"===e;case"tbody":case"thead":case"tfoot":return"tr"===e||"style"===e||"script"===e||"template"===e;case"colgroup":return"col"===e||"template"===e;case"table":return"caption"===e||"colgroup"===e||"tbody"===e||"tfoot"===e||"thead"===e||"style"===e||"script"===e||"template"===e;case"head":return"base"===e||"basefont"===e||"bgsound"===e||"link"===e||"meta"===e||"title"===e||"noscript"===e||"noframes"===e||"style"===e||"script"===e||"template"===e;case"html":return"head"===e||"body"===e;case"#document":return"html"===e}switch(e){case"h1":case"h2":case"h3":case"h4":case"h5":case"h6":return"h1"!==t&&"h2"!==t&&"h3"!==t&&"h4"!==t&&"h5"!==t&&"h6"!==t;case"rp":case"rt":return l.indexOf(t)===-1;case"body":case"caption":case"col":case"colgroup":case"frame":case"head":case"html":case"tbody":case"td":case"tfoot":case"th":case"thead":case"tr":return null==t}return!0},h=function(e,t){switch(e){case"address":case"article":case"aside":case"blockquote":case"center":case"details":case"dialog":case"dir":case"div":case"dl":case"fieldset":case"figcaption":case"figure":case"footer":case"header":case"hgroup":case"main":case"menu":case"nav":case"ol":case"p":case"section":case"summary":case"ul":case"pre":case"listing":case"table":case"hr":case"xmp":case"h1":case"h2":case"h3":case"h4":case"h5":case"h6":return t.pTagInButtonScope;case"form":return t.formTag||t.pTagInButtonScope;case"li":return t.listItemTagAutoclosing;case"dd":case"dt":return t.dlItemTagAutoclosing;case"button":return t.buttonTagInScope;case"a":return t.aTagInScope;case"nobr":return t.nobrTagInScope}return null},v=function(e){if(!e)return[];var t=[];do t.push(e);while(e=e._currentElement._owner);return t.reverse(),t},m={};a=function(e,n,o,r){r=r||p;var a=r.current,u=a&&a.tag;null!=n&&("production"!==t.env.NODE_ENV?i(null==e,"validateDOMNesting: when childText is passed, childTag should be null"):void 0,e="#text");var c=f(e,u)?null:a,s=c?null:h(e,r),l=c||s;if(l){var d,y=l.tag,g=l.instance,b=o&&o._currentElement._owner,_=g&&g._currentElement._owner,E=v(b),N=v(_),w=Math.min(E.length,N.length),k=-1;for(d=0;d "),S=!!c+"|"+e+"|"+y+"|"+D;if(m[S])return;m[S]=!0;var T=e,P="";if("#text"===e?/\S/.test(n)?T="Text nodes":(T="Whitespace text nodes",P=" Make sure you don't have any extra whitespace between tags on each line of your source code."):T="<"+e+">",c){var M="";"table"===y&&"tr"===e&&(M+=" Add a to your code to match the DOM tree generated by the browser."),"production"!==t.env.NODE_ENV?i(!1,"validateDOMNesting(...): %s cannot appear as a child of <%s>.%s See %s.%s",T,y,P,D,M):void 0}else"production"!==t.env.NODE_ENV?i(!1,"validateDOMNesting(...): %s cannot appear as a descendant of <%s>. See %s.",T,y,D):void 0}},a.updatedAncestorInfo=d,a.isTagValidInContext=function(e,t){t=t||p;var n=t.current,o=n&&n.tag;return f(e,o)&&!h(e,t)}}e.exports=a}).call(t,n(0))},function(e,t,n){"use strict";(function(t){function o(e,t,n){this.props=e,this.context=t,this.refs=c,this.updater=n||a}var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},i=n(25),a=n(94),u=n(96),c=n(34),s=n(2),l=n(3);if(o.prototype.isReactComponent={},o.prototype.setState=function(e,n){"object"!==("undefined"==typeof e?"undefined":r(e))&&"function"!=typeof e&&null!=e?"production"!==t.env.NODE_ENV?s(!1,"setState(...): takes an object of state variables to update or a function which returns an object of state variables."):i("85"):void 0,this.updater.enqueueSetState(this,e),n&&this.updater.enqueueCallback(this,n,"setState")},o.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this),e&&this.updater.enqueueCallback(this,e,"forceUpdate")},"production"!==t.env.NODE_ENV){var p={isMounted:["isMounted","Instead, make sure to clean up subscriptions and pending requests in componentWillUnmount to prevent memory leaks."],replaceState:["replaceState","Refactor your code to use setState instead (see https://github.com/facebook/react/issues/3236)."]},d=function(e,n){u&&Object.defineProperty(o.prototype,e,{get:function(){"production"!==t.env.NODE_ENV?l(!1,"%s(...) is deprecated in plain JavaScript React classes. %s",n[0],n[1]):void 0}})};for(var f in p)p.hasOwnProperty(f)&&d(f,p[f])}e.exports=o}).call(t,n(0))},function(e,t,n){"use strict";(function(t){function o(e,n){if("production"!==t.env.NODE_ENV){var o=e.constructor;"production"!==t.env.NODE_ENV?r(!1,"%s(...): Can only update a mounted or mounting component. This usually means you called %s() on an unmounted component. This is a no-op. Please check the code for the %s component.",n,n,o&&(o.displayName||o.name)||"ReactClass"):void 0}}var r=n(3),i={isMounted:function(e){return!1},enqueueCallback:function(e,t){},enqueueForceUpdate:function(e){o(e,"forceUpdate")},enqueueReplaceState:function(e,t){o(e,"replaceState")},enqueueSetState:function(e,t){o(e,"setState")}};e.exports=i}).call(t,n(0))},function(e,t,n){"use strict";(function(t){var n={};"production"!==t.env.NODE_ENV&&(n={prop:"prop",context:"context",childContext:"child context"}),e.exports=n}).call(t,n(0))},function(e,t,n){"use strict";(function(t){var n=!1;if("production"!==t.env.NODE_ENV)try{Object.defineProperty({},"x",{get:function(){}}),n=!0}catch(e){}e.exports=n}).call(t,n(0))},function(e,t,n){"use strict";function o(e){var t=e&&(r&&e[r]||e[i]);if("function"==typeof t)return t}var r="function"==typeof Symbol&&Symbol.iterator,i="@@iterator";e.exports=o},function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,"loaded",{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,"id",{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}},function(e,t,n){"use strict";function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function r(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}var a=n(306),u=n.n(a),c=n(1),s=n(7),l=function(){function e(e,t){for(var n=0;nd))return!1;var h=l.get(e);if(h&&l.get(t))return h==t;var v=-1,m=!0,y=n&c?new r:void 0;for(l.set(e,t),l.set(t,e);++v must be an array if `multiple` is true.%s",a,r(o)):void 0:!n.multiple&&u&&("production"!==t.env.NODE_ENV?d(!1,"The `%s` prop supplied to ',""],s=[1,"","
    "],l=[3,"","
    "],p=[1,'',""],d={"*":[1,"?
    ","
    "],area:[1,"",""],col:[2,"","
    "],legend:[1,"
    ","
    "],param:[1,"",""],tr:[2,"","
    "],optgroup:c,option:c,caption:s,colgroup:s,tbody:s,tfoot:s,thead:s,td:l,th:l},f=["circle","clipPath","defs","ellipse","g","image","line","linearGradient","mask","path","pattern","polygon","polyline","radialGradient","rect","stop","text","tspan"];f.forEach(function(e){d[e]=p,u[e]=!0}),e.exports=o}).call(t,n(0))},function(e,t,n){"use strict";function o(e){return e===window?{x:window.pageXOffset||document.documentElement.scrollLeft, +y:window.pageYOffset||document.documentElement.scrollTop}:{x:e.scrollLeft,y:e.scrollTop}}e.exports=o},function(e,t,n){"use strict";function o(e){return e.replace(r,"-$1").toLowerCase()}var r=/([A-Z])/g;e.exports=o},function(e,t,n){"use strict";function o(e){return r(e).replace(i,"-ms-")}var r=n(185),i=/^ms-/;e.exports=o},function(e,t,n){"use strict";function o(e){return!(!e||!("function"==typeof Node?e instanceof Node:"object"===("undefined"==typeof e?"undefined":r(e))&&"number"==typeof e.nodeType&&"string"==typeof e.nodeName))}var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};e.exports=o},function(e,t,n){"use strict";function o(e){return r(e)&&3==e.nodeType}var r=n(187);e.exports=o},function(e,t,n){"use strict";function o(e){var t={};return function(n){return t.hasOwnProperty(n)||(t[n]=e.call(this,n)),t[n]}}e.exports=o},function(e,t,n){"use strict";var o,r=n(8);r.canUseDOM&&(o=window.performance||window.msPerformance||window.webkitPerformance),e.exports=o||{}},function(e,t,n){"use strict";var o,r=n(190);o=r.now?function(){return r.now()}:function(){return Date.now()},e.exports=o},function(e,t,n){var o=n(22),r=n(15),i=o(r,"DataView");e.exports=i},function(e,t,n){function o(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t0&&n(l)?t>1?o(l,t-1,n,a,u):r(u,l):a||(u[u.length]=l)}return u}var r=n(62),i=n(263);e.exports=o},function(e,t,n){var o=n(245),r=o();e.exports=r},function(e,t,n){function o(e,t){return e&&r(e,t,i)}var r=n(209),i=n(38);e.exports=o},function(e,t){function n(e,t){return null!=e&&t in Object(e)}e.exports=n},function(e,t,n){function o(e){return i(e)&&r(e)==a}var r=n(27),i=n(29),a="[object Arguments]";e.exports=o},function(e,t,n){function o(e,t,n,o,m,g){var b=s(e),_=s(t),E=b?h:c(e),N=_?h:c(t);E=E==f?v:E,N=N==f?v:N;var w=E==v,k=N==v,C=E==N;if(C&&l(e)){if(!l(t))return!1;b=!0,w=!1}if(C&&!w)return g||(g=new r),b||p(e)?i(e,t,n,o,m,g):a(e,t,E,n,o,m,g);if(!(n&d)){var x=w&&y.call(e,"__wrapped__"),O=k&&y.call(t,"__wrapped__");if(x||O){var D=x?e.value():e,S=O?t.value():t;return g||(g=new r),m(D,S,n,o,g)}}return!!C&&(g||(g=new r),u(e,t,n,o,m,g))}var r=n(60),i=n(113),a=n(248),u=n(249),c=n(118),s=n(11),l=n(75),p=n(128),d=1,f="[object Arguments]",h="[object Array]",v="[object Object]",m=Object.prototype,y=m.hasOwnProperty;e.exports=o},function(e,t,n){function o(e,t,n,o){var c=n.length,s=c,l=!o;if(null==e)return!s;for(e=Object(e);c--;){var p=n[c];if(l&&p[2]?p[1]!==e[p[0]]:!(p[0]in e))return!1}for(;++cr?0:r+t),n=n>r?r:n,n<0&&(n+=r),r=t>n?0:n-t>>>0,t>>>=0;for(var i=Array(r);++o1?n[r-1]:void 0,u=r>2?n[2]:void 0;for(a=e.length>3&&"function"==typeof a?(r--,a):void 0,u&&i(n[0],n[1],u)&&(a=r<3?void 0:a,r=1),t=Object(t);++o-1}var r=n(45);e.exports=o},function(e,t,n){function o(e,t){var n=this.__data__,o=r(n,e);return o<0?(++this.size,n.push([e,t])):n[o][1]=t,this}var r=n(45);e.exports=o},function(e,t,n){function o(){this.size=0,this.__data__={hash:new r,map:new(a||i),string:new r}}var r=n(193),i=n(44),a=n(58);e.exports=o},function(e,t,n){function o(e){var t=r(this,e).delete(e);return this.size-=t?1:0,t}var r=n(47);e.exports=o},function(e,t,n){function o(e){return r(this,e).get(e)}var r=n(47);e.exports=o},function(e,t,n){function o(e){return r(this,e).has(e)}var r=n(47);e.exports=o},function(e,t,n){function o(e,t){var n=r(this,e),o=n.size;return n.set(e,t),this.size+=n.size==o?0:1,this}var r=n(47);e.exports=o},function(e,t,n){function o(e){var t=r(e,function(e){return n.size===i&&n.clear(),e}),n=t.cache;return t}var r=n(302),i=500;e.exports=o},function(e,t,n){var o=n(122),r=o(Object.keys,Object);e.exports=r},function(e,t){function n(e){var t=[];if(null!=e)for(var n in Object(e))t.push(n);return t}e.exports=n},function(e,t,n){(function(e){var o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},r=n(114),i="object"==o(t)&&t&&!t.nodeType&&t,a=i&&"object"==o(e)&&e&&!e.nodeType&&e,u=a&&a.exports===i,c=u&&r.process,s=function(){try{return c&&c.binding&&c.binding("util")}catch(e){}}();e.exports=s}).call(t,n(98)(e))},function(e,t){function n(e){return r.call(e)}var o=Object.prototype,r=o.toString;e.exports=n},function(e,t,n){function o(e,t){return t.length<2?e:r(e,i(t,0,-1))}var r=n(64),i=n(226);e.exports=o},function(e,t){function n(e){return this.__data__.set(e,o),this}var o="__lodash_hash_undefined__";e.exports=n},function(e,t){function n(e){return this.__data__.has(e)}e.exports=n},function(e,t){function n(e){var t=0,n=0;return function(){var a=i(),u=r-(a-n);if(n=a,u>0){if(++t>=o)return arguments[0]}else t=0;return e.apply(void 0,arguments)}}var o=800,r=16,i=Date.now;e.exports=n},function(e,t,n){function o(){this.__data__=new r,this.size=0}var r=n(44);e.exports=o},function(e,t){function n(e){var t=this.__data__,n=t.delete(e);return this.size=t.size,n}e.exports=n},function(e,t){function n(e){return this.__data__.get(e)}e.exports=n},function(e,t){function n(e){return this.__data__.has(e)}e.exports=n},function(e,t,n){function o(e,t){var n=this.__data__;if(n instanceof r){var o=n.__data__;if(!i||o.length=t||n<0||x&&o>=_}function f(){var e=i();return d(e)?h(e):void(N=setTimeout(f,p(e)))}function h(e){return N=void 0,O&&g?o(e):(g=b=void 0,E)}function v(){void 0!==N&&clearTimeout(N),k=0,g=w=b=N=void 0}function m(){return void 0===N?E:h(i())}function y(){var e=i(),n=d(e);if(g=arguments,b=this,w=e,n){if(void 0===N)return l(w);if(x)return N=setTimeout(f,t),o(w)}return void 0===N&&(N=setTimeout(f,t)),E}var g,b,_,E,N,w,k=0,C=!1,x=!1,O=!0;if("function"!=typeof e)throw new TypeError(u);return t=a(t)||0,r(n)&&(C=!!n.leading,x="maxWait"in n,_=x?c(a(n.maxWait)||0,t):_,O="trailing"in n?!!n.trailing:O),y.cancel=v,y.flush=m,y}var r=n(19),i=n(303),a=n(132),u="Expected a function",c=Math.max,s=Math.min;e.exports=o},function(e,t,n){var o=n(105),r=n(292),i=n(111),a=n(246),u=i(function(e){return e.push(void 0,a),o(r,void 0,e)});e.exports=u},function(e,t,n){function o(e,t,n){var o=null==e?0:e.length;if(!o)return-1;var c=null==n?0:a(n);return c<0&&(c=u(o+c,0)),r(e,i(t,3),c)}var r=n(207),i=n(66),a=n(309),u=Math.max;e.exports=o},function(e,t,n){function o(e){var t=null==e?0:e.length;return t?r(e,1):[]}var r=n(208);e.exports=o},function(e,t,n){function o(e,t,n){var o=null==e?void 0:r(e,t);return void 0===o?n:o}var r=n(64);e.exports=o},function(e,t,n){function o(e,t){return null!=e&&i(e,t,r)}var r=n(211),i=n(254);e.exports=o},function(e,t,n){function o(e,t){return r(e,t)}var r=n(65);e.exports=o},function(e,t,n){function o(e){if(!a(e)||r(e)!=u)return!1;var t=i(e);if(null===t)return!0;var n=p.call(t,"constructor")&&t.constructor;return"function"==typeof n&&n instanceof n&&l.call(n)==d}var r=n(27),i=n(68),a=n(29),u="[object Object]",c=Function.prototype,s=Object.prototype,l=c.toString,p=s.hasOwnProperty,d=l.call(Object);e.exports=o},function(e,t,n){function o(e,t){if("function"!=typeof e||null!=t&&"function"!=typeof t)throw new TypeError(i);var n=function n(){var o=arguments,r=t?t.apply(this,o):o[0],i=n.cache;if(i.has(r))return i.get(r);var a=e.apply(this,o);return n.cache=i.set(r,a)||i,a};return n.cache=new(o.Cache||r),n}var r=n(59),i="Expected a function";o.Cache=r,e.exports=o},function(e,t,n){var o=n(15),r=function(){return o.Date.now()};e.exports=r},function(e,t,n){var o=n(61),r=n(205),i=n(230),a=n(46),u=n(28),c=n(247),s=n(250),l=n(116),p=1,d=2,f=4,h=s(function(e,t){var n={};if(null==e)return n;var s=!1;t=o(t,function(t){return t=a(t,e),s||(s=t.length>1),t}),u(e,l(e),n),s&&(n=r(n,p|d|f,c));for(var h=t.length;h--;)i(n,t[h]);return n});e.exports=h},function(e,t,n){function o(e){return a(e)?r(u(e)):i(e)}var r=n(222),i=n(223),a=n(71),u=n(35);e.exports=o},function(e,t,n){function o(e,t,n){var o=c(e)?r:u,s=arguments.length<3;return o(e,a(t,4),n,s,i)}var r=n(63),i=n(109),a=n(66),u=n(224),c=n(11);e.exports=o},function(e,t){function n(){return!1}e.exports=n},function(e,t,n){function o(e){if(!e)return 0===e?e:0;if(e=r(e),e===i||e===-i){var t=e<0?-1:1;return t*a}return e===e?e:0}var r=n(132),i=1/0,a=1.7976931348623157e308;e.exports=o},function(e,t,n){function o(e){var t=r(e),n=t%1;return t===t?n?t-n:t:0}var r=n(308);e.exports=o},function(e,t,n){"use strict";var o={Properties:{"aria-current":0,"aria-details":0,"aria-disabled":0,"aria-hidden":0,"aria-invalid":0,"aria-keyshortcuts":0,"aria-label":0,"aria-roledescription":0,"aria-autocomplete":0,"aria-checked":0,"aria-expanded":0,"aria-haspopup":0,"aria-level":0,"aria-modal":0,"aria-multiline":0,"aria-multiselectable":0,"aria-orientation":0,"aria-placeholder":0,"aria-pressed":0,"aria-readonly":0,"aria-required":0,"aria-selected":0,"aria-sort":0,"aria-valuemax":0,"aria-valuemin":0,"aria-valuenow":0,"aria-valuetext":0,"aria-atomic":0,"aria-busy":0,"aria-live":0,"aria-relevant":0,"aria-dropeffect":0,"aria-grabbed":0,"aria-activedescendant":0,"aria-colcount":0,"aria-colindex":0,"aria-colspan":0,"aria-controls":0,"aria-describedby":0,"aria-errormessage":0,"aria-flowto":0,"aria-labelledby":0,"aria-owns":0,"aria-posinset":0,"aria-rowcount":0,"aria-rowindex":0,"aria-rowspan":0,"aria-setsize":0},DOMAttributeNames:{},DOMPropertyNames:{}};e.exports=o},function(e,t,n){"use strict";var o=n(6),r=n(102),i={focusDOMComponent:function(){r(o.getNodeFromInstance(this))}};e.exports=i},function(e,t,n){"use strict";function o(){var e=window.opera;return"object"===("undefined"==typeof e?"undefined":f(e))&&"function"==typeof e.version&&parseInt(e.version(),10)<=12}function r(e){return(e.ctrlKey||e.altKey||e.metaKey)&&!(e.ctrlKey&&e.altKey)}function i(e){switch(e){case"topCompositionStart":return O.compositionStart;case"topCompositionEnd":return O.compositionEnd;case"topCompositionUpdate":return O.compositionUpdate}}function a(e,t){return"topKeyDown"===e&&t.keyCode===_}function u(e,t){switch(e){case"topKeyUp":return b.indexOf(t.keyCode)!==-1;case"topKeyDown":return t.keyCode!==_;case"topKeyPress":case"topMouseDown":case"topBlur":return!0;default:return!1}}function c(e){var t=e.detail;return"object"===("undefined"==typeof t?"undefined":f(t))&&"data"in t?t.data:null}function s(e,t,n,o){var r,s;if(E?r=i(e):S?u(e,n)&&(r=O.compositionEnd):a(e,n)&&(r=O.compositionStart),!r)return null;k&&(S||r!==O.compositionStart?r===O.compositionEnd&&S&&(s=S.getData()):S=m.getPooled(o));var l=y.getPooled(r,t,n,o);if(s)l.data=s;else{var p=c(n);null!==p&&(l.data=p)}return h.accumulateTwoPhaseDispatches(l),l}function l(e,t){switch(e){case"topCompositionEnd":return c(t);case"topKeyPress":var n=t.which;return n!==C?null:(D=!0,x);case"topTextInput":var o=t.data;return o===x&&D?null:o;default:return null}}function p(e,t){if(S){if("topCompositionEnd"===e||!E&&u(e,t)){var n=S.getData();return m.release(S),S=null,n}return null}switch(e){case"topPaste":return null;case"topKeyPress":return t.which&&!r(t)?String.fromCharCode(t.which):null;case"topCompositionEnd":return k?null:t.data;default:return null}}function d(e,t,n,o){var r;if(r=w?l(e,n):p(e,n),!r)return null;var i=g.getPooled(O.beforeInput,t,n,o);return i.data=r,h.accumulateTwoPhaseDispatches(i),i}var f="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},h=n(40),v=n(8),m=n(318),y=n(361),g=n(364),b=[9,13,27,32],_=229,E=v.canUseDOM&&"CompositionEvent"in window,N=null;v.canUseDOM&&"documentMode"in document&&(N=document.documentMode);var w=v.canUseDOM&&"TextEvent"in window&&!N&&!o(),k=v.canUseDOM&&(!E||N&&N>8&&N<=11),C=32,x=String.fromCharCode(C),O={beforeInput:{phasedRegistrationNames:{bubbled:"onBeforeInput",captured:"onBeforeInputCapture"},dependencies:["topCompositionEnd","topKeyPress","topTextInput","topPaste"]},compositionEnd:{phasedRegistrationNames:{bubbled:"onCompositionEnd",captured:"onCompositionEndCapture"},dependencies:["topBlur","topCompositionEnd","topKeyDown","topKeyPress","topKeyUp","topMouseDown"]},compositionStart:{phasedRegistrationNames:{bubbled:"onCompositionStart",captured:"onCompositionStartCapture"},dependencies:["topBlur","topCompositionStart","topKeyDown","topKeyPress","topKeyUp","topMouseDown"]},compositionUpdate:{phasedRegistrationNames:{bubbled:"onCompositionUpdate",captured:"onCompositionUpdateCapture"},dependencies:["topBlur","topCompositionUpdate","topKeyDown","topKeyPress","topKeyUp","topMouseDown"]}},D=!1,S=null,T={eventTypes:O,extractEvents:function(e,t,n,o){return[s(e,t,n,o),d(e,t,n,o)]}};e.exports=T},function(e,t,n){"use strict";(function(t){var o=n(135),r=n(8),i=n(13),a=n(179),u=n(371),c=n(186),s=n(189),l=n(3),p=s(function(e){return c(e)}),d=!1,f="cssFloat";if(r.canUseDOM){var h=document.createElement("div").style;try{h.font=""}catch(e){d=!0}void 0===document.documentElement.style.cssFloat&&(f="styleFloat")}if("production"!==t.env.NODE_ENV)var v=/^(?:webkit|moz|o)[A-Z]/,m=/;\s*$/,y={},g={},b=!1,_=function(e,n){y.hasOwnProperty(e)&&y[e]||(y[e]=!0,"production"!==t.env.NODE_ENV?l(!1,"Unsupported style property %s. Did you mean %s?%s",e,a(e),k(n)):void 0)},E=function(e,n){y.hasOwnProperty(e)&&y[e]||(y[e]=!0,"production"!==t.env.NODE_ENV?l(!1,"Unsupported vendor-prefixed style property %s. Did you mean %s?%s",e,e.charAt(0).toUpperCase()+e.slice(1),k(n)):void 0)},N=function(e,n,o){g.hasOwnProperty(n)&&g[n]||(g[n]=!0,"production"!==t.env.NODE_ENV?l(!1,'Style property values shouldn\'t contain a semicolon.%s Try "%s: %s" instead.',k(o),e,n.replace(m,"")):void 0)},w=function(e,n,o){b||(b=!0,"production"!==t.env.NODE_ENV?l(!1,"`NaN` is an invalid value for the `%s` css style property.%s",e,k(o)):void 0)},k=function(e){if(e){var t=e.getName();if(t)return" Check the render method of `"+t+"`."}return""},C=function(e,t,n){var o;n&&(o=n._currentElement._owner),e.indexOf("-")>-1?_(e,o):v.test(e)?E(e,o):m.test(t)&&N(e,t,o),"number"==typeof t&&isNaN(t)&&w(e,t,o)};var x={createMarkupForStyles:function(e,n){var o="";for(var r in e)if(e.hasOwnProperty(r)){var i=e[r];"production"!==t.env.NODE_ENV&&C(r,i,n),null!=i&&(o+=p(r)+":",o+=u(r,i,n)+";")}return o||null},setValueForStyles:function(e,n,r){"production"!==t.env.NODE_ENV&&i.debugTool.onHostOperation({instanceID:r._debugID,type:"update styles",payload:n});var a=e.style;for(var c in n)if(n.hasOwnProperty(c)){"production"!==t.env.NODE_ENV&&C(c,n[c],r);var s=u(c,n[c],r);if("float"!==c&&"cssFloat"!==c||(c=f),s)a[c]=s;else{var l=d&&o.shorthandPropertyExpansions[c];if(l)for(var p in l)a[p]="";else a[c]=""}}}};e.exports=x}).call(t,n(0))},function(e,t,n){"use strict";function o(e){var t=e.nodeName&&e.nodeName.toLowerCase();return"select"===t||"input"===t&&"file"===e.type}function r(e){var t=w.getPooled(O.change,S,e,k(e));b.accumulateTwoPhaseDispatches(t),N.batchedUpdates(i,t)}function i(e){g.enqueueEvents(e),g.processEventQueue(!1)}function a(e,t){D=e,S=t,D.attachEvent("onchange",r)}function u(){D&&(D.detachEvent("onchange",r),D=null,S=null)}function c(e,t){if("topChange"===e)return t}function s(e,t,n){"topFocus"===e?(u(),a(t,n)):"topBlur"===e&&u()}function l(e,t){D=e,S=t,T=e.value,P=Object.getOwnPropertyDescriptor(e.constructor.prototype,"value"),Object.defineProperty(D,"value",A),D.attachEvent?D.attachEvent("onpropertychange",d):D.addEventListener("propertychange",d,!1)}function p(){D&&(delete D.value,D.detachEvent?D.detachEvent("onpropertychange",d):D.removeEventListener("propertychange",d,!1),D=null,S=null,T=null,P=null)}function d(e){if("value"===e.propertyName){var t=e.srcElement.value;t!==T&&(T=t,r(e))}}function f(e,t){if("topInput"===e)return t}function h(e,t,n){"topFocus"===e?(p(),l(t,n)):"topBlur"===e&&p()}function v(e,t){if(("topSelectionChange"===e||"topKeyUp"===e||"topKeyDown"===e)&&D&&D.value!==T)return T=D.value,S}function m(e){return e.nodeName&&"input"===e.nodeName.toLowerCase()&&("checkbox"===e.type||"radio"===e.type)}function y(e,t){if("topClick"===e)return t}var g=n(39),b=n(40),_=n(8),E=n(6),N=n(16),w=n(20),k=n(89),C=n(90),x=n(153),O={change:{phasedRegistrationNames:{bubbled:"onChange",captured:"onChangeCapture"},dependencies:["topBlur","topChange","topClick","topFocus","topInput","topKeyDown","topKeyUp","topSelectionChange"]}},D=null,S=null,T=null,P=null,M=!1; +_.canUseDOM&&(M=C("change")&&(!document.documentMode||document.documentMode>8));var I=!1;_.canUseDOM&&(I=C("input")&&(!document.documentMode||document.documentMode>11));var A={get:function(){return P.get.call(this)},set:function(e){T=""+e,P.set.call(this,e)}},j={eventTypes:O,extractEvents:function(e,t,n,r){var i,a,u=t?E.getNodeFromInstance(t):window;if(o(u)?M?i=c:a=s:x(u)?I?i=f:(i=v,a=h):m(u)&&(i=y),i){var l=i(e,t);if(l){var p=w.getPooled(O.change,l,n,r);return p.type="change",b.accumulateTwoPhaseDispatches(p),p}}a&&a(e,u,t)}};e.exports=j},function(e,t,n){"use strict";(function(t){var o=n(4),r=n(30),i=n(8),a=n(182),u=n(14),c=n(2),s={dangerouslyReplaceNodeWithMarkup:function(e,n){if(i.canUseDOM?void 0:"production"!==t.env.NODE_ENV?c(!1,"dangerouslyReplaceNodeWithMarkup(...): Cannot render markup in a worker thread. Make sure `window` and `document` are available globally before requiring React when unit testing or use ReactDOMServer.renderToString() for server rendering."):o("56"),n?void 0:"production"!==t.env.NODE_ENV?c(!1,"dangerouslyReplaceNodeWithMarkup(...): Missing markup."):o("57"),"HTML"===e.nodeName?"production"!==t.env.NODE_ENV?c(!1,"dangerouslyReplaceNodeWithMarkup(...): Cannot replace markup of the node. This is because browser quirks make this unreliable and/or slow. If you want to render to the root you must use server rendering. See ReactDOMServer.renderToString()."):o("58"):void 0,"string"==typeof n){var s=a(n,u)[0];e.parentNode.replaceChild(s,e)}else r.replaceChildWithTree(e,n)}};e.exports=s}).call(t,n(0))},function(e,t,n){"use strict";var o=["ResponderEventPlugin","SimpleEventPlugin","TapEventPlugin","EnterLeaveEventPlugin","ChangeEventPlugin","SelectEventPlugin","BeforeInputEventPlugin"];e.exports=o},function(e,t,n){"use strict";var o=n(40),r=n(6),i=n(52),a={mouseEnter:{registrationName:"onMouseEnter",dependencies:["topMouseOut","topMouseOver"]},mouseLeave:{registrationName:"onMouseLeave",dependencies:["topMouseOut","topMouseOver"]}},u={eventTypes:a,extractEvents:function(e,t,n,u){if("topMouseOver"===e&&(n.relatedTarget||n.fromElement))return null;if("topMouseOut"!==e&&"topMouseOver"!==e)return null;var c;if(u.window===u)c=u;else{var s=u.ownerDocument;c=s?s.defaultView||s.parentWindow:window}var l,p;if("topMouseOut"===e){l=t;var d=n.relatedTarget||n.toElement;p=d?r.getClosestInstanceFromNode(d):null}else l=null,p=t;if(l===p)return null;var f=null==l?c:r.getNodeFromInstance(l),h=null==p?c:r.getNodeFromInstance(p),v=i.getPooled(a.mouseLeave,l,n,u);v.type="mouseleave",v.target=f,v.relatedTarget=h;var m=i.getPooled(a.mouseEnter,p,n,u);return m.type="mouseenter",m.target=h,m.relatedTarget=f,o.accumulateEnterLeaveDispatches(v,m,l,p),[v,m]}};e.exports=u},function(e,t,n){"use strict";function o(e){this._root=e,this._startText=this.getText(),this._fallbackText=null}var r=n(5),i=n(23),a=n(151);r(o.prototype,{destructor:function(){this._root=null,this._startText=null,this._fallbackText=null},getText:function(){return"value"in this._root?this._root.value:this._root[a()]},getData:function(){if(this._fallbackText)return this._fallbackText;var e,t,n=this._startText,o=n.length,r=this.getText(),i=r.length;for(e=0;e1?1-t:void 0;return this._fallbackText=r.slice(e,u),this._fallbackText}}),i.addPoolingTo(o),e.exports=o},function(e,t,n){"use strict";var o=n(21),r=o.injection.MUST_USE_PROPERTY,i=o.injection.HAS_BOOLEAN_VALUE,a=o.injection.HAS_NUMERIC_VALUE,u=o.injection.HAS_POSITIVE_NUMERIC_VALUE,c=o.injection.HAS_OVERLOADED_BOOLEAN_VALUE,s={isCustomAttribute:RegExp.prototype.test.bind(new RegExp("^(data|aria)-["+o.ATTRIBUTE_NAME_CHAR+"]*$")),Properties:{accept:0,acceptCharset:0,accessKey:0,action:0,allowFullScreen:i,allowTransparency:0,alt:0,as:0,async:i,autoComplete:0,autoPlay:i,capture:i,cellPadding:0,cellSpacing:0,charSet:0,challenge:0,checked:r|i,cite:0,classID:0,className:0,cols:u,colSpan:0,content:0,contentEditable:0,contextMenu:0,controls:i,coords:0,crossOrigin:0,data:0,dateTime:0,default:i,defer:i,dir:0,disabled:i,download:c,draggable:0,encType:0,form:0,formAction:0,formEncType:0,formMethod:0,formNoValidate:i,formTarget:0,frameBorder:0,headers:0,height:0,hidden:i,high:0,href:0,hrefLang:0,htmlFor:0,httpEquiv:0,icon:0,id:0,inputMode:0,integrity:0,is:0,keyParams:0,keyType:0,kind:0,label:0,lang:0,list:0,loop:i,low:0,manifest:0,marginHeight:0,marginWidth:0,max:0,maxLength:0,media:0,mediaGroup:0,method:0,min:0,minLength:0,multiple:r|i,muted:r|i,name:0,nonce:0,noValidate:i,open:i,optimum:0,pattern:0,placeholder:0,playsInline:i,poster:0,preload:0,profile:0,radioGroup:0,readOnly:i,referrerPolicy:0,rel:0,required:i,reversed:i,role:0,rows:u,rowSpan:a,sandbox:0,scope:0,scoped:i,scrolling:0,seamless:i,selected:r|i,shape:0,size:u,sizes:0,span:u,spellCheck:0,src:0,srcDoc:0,srcLang:0,srcSet:0,start:a,step:0,style:0,summary:0,tabIndex:0,target:0,title:0,type:0,useMap:0,value:0,width:0,wmode:0,wrap:0,about:0,datatype:0,inlist:0,prefix:0,property:0,resource:0,typeof:0,vocab:0,autoCapitalize:0,autoCorrect:0,autoSave:0,color:0,itemProp:0,itemScope:i,itemType:0,itemID:0,itemRef:0,results:0,security:0,unselectable:0},DOMAttributeNames:{acceptCharset:"accept-charset",className:"class",htmlFor:"for",httpEquiv:"http-equiv"},DOMPropertyNames:{}};e.exports=s},function(e,t,n){"use strict";(function(t){function o(e,o,i,c){var s=void 0===e[i];"production"!==t.env.NODE_ENV&&(r||(r=n(12)),s||("production"!==t.env.NODE_ENV?l(!1,"flattenChildren(...): Encountered two children with the same key, `%s`. Child keys must be unique; when two children share a key, only the first child will be used.%s",u.unescape(i),r.getStackAddendumByID(c)):void 0)),null!=o&&s&&(e[i]=a(o,!0))}var r,i=n(31),a=n(152),u=n(81),c=n(91),s=n(155),l=n(3);"undefined"!=typeof t&&t.env&&"test"===t.env.NODE_ENV&&(r=n(12));var p={instantiateChildren:function(e,n,r,i){if(null==e)return null;var a={};return"production"!==t.env.NODE_ENV?s(e,function(e,t,n){return o(e,t,n,i)},a):s(e,o,a),a},updateChildren:function(e,t,n,o,r,u,s,l,p){if(t||e){var d,f;for(d in t)if(t.hasOwnProperty(d)){f=e&&e[d];var h=f&&f._currentElement,v=t[d];if(null!=f&&c(h,v))i.receiveComponent(f,v,r,l),t[d]=f;else{f&&(o[d]=i.getHostNode(f),i.unmountComponent(f,!1));var m=a(v,!0);t[d]=m;var y=i.mountComponent(m,r,u,s,l,p);n.push(y)}}for(d in e)!e.hasOwnProperty(d)||t&&t.hasOwnProperty(d)||(f=e[d],o[d]=i.getHostNode(f),i.unmountComponent(f,!1))}},unmountChildren:function(e,t){for(var n in e)if(e.hasOwnProperty(n)){var o=e[n];i.unmountComponent(o,t)}}};e.exports=p}).call(t,n(0))},function(e,t,n){"use strict";var o=n(78),r=n(328),i={processChildrenUpdates:r.dangerouslyProcessChildrenUpdates,replaceNodeWithMarkup:o.dangerouslyReplaceNodeWithMarkup};e.exports=i},function(e,t,n){"use strict";(function(t){function o(e){}function r(e,n){"production"!==t.env.NODE_ENV&&("production"!==t.env.NODE_ENV?k(null===n||n===!1||p.isValidElement(n),"%s(...): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.",e.displayName||e.name||"Component"):void 0,"production"!==t.env.NODE_ENV?k(!e.childContextTypes,"%s(...): childContextTypes cannot be defined on a functional component.",e.displayName||e.name||"Component"):void 0)}function i(e){return!(!e.prototype||!e.prototype.isReactComponent)}function a(e){return!(!e.prototype||!e.prototype.isPureReactComponent)}function u(e,t,n){if(0===t)return e();m.debugTool.onBeginLifeCycleTimer(t,n);try{return e()}finally{m.debugTool.onEndLifeCycleTimer(t,n)}}var c="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},s=n(4),l=n(5),p=n(32),d=n(83),f=n(17),h=n(84),v=n(41),m=n(13),y=n(145),g=n(31);if("production"!==t.env.NODE_ENV)var b=n(370);var _=n(34),E=n(2),N=n(57),w=n(91),k=n(3),C={ImpureClass:0,PureClass:1,StatelessFunctional:2};o.prototype.render=function(){var e=v.get(this)._currentElement.type,t=e(this.props,this.context,this.updater);return r(e,t),t};var x=1,O={construct:function(e){this._currentElement=e,this._rootNodeID=0,this._compositeType=null,this._instance=null,this._hostParent=null,this._hostContainerInfo=null,this._updateBatchNumber=null,this._pendingElement=null,this._pendingStateQueue=null,this._pendingReplaceState=!1,this._pendingForceUpdate=!1,this._renderedNodeType=null,this._renderedComponent=null,this._context=null,this._mountOrder=0,this._topLevelWrapper=null,this._pendingCallbacks=null,this._calledComponentWillUnmount=!1,"production"!==t.env.NODE_ENV&&(this._warnedAboutRefsInRender=!1)},mountComponent:function(e,n,l,d){var f=this;this._context=d,this._mountOrder=x++,this._hostParent=n,this._hostContainerInfo=l;var h,m=this._currentElement.props,y=this._processContext(d),g=this._currentElement.type,b=e.getUpdateQueue(),N=i(g),w=this._constructComponent(N,m,y,b);if(N||null!=w&&null!=w.render?a(g)?this._compositeType=C.PureClass:this._compositeType=C.ImpureClass:(h=w,r(g,h),null===w||w===!1||p.isValidElement(w)?void 0:"production"!==t.env.NODE_ENV?E(!1,"%s(...): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.",g.displayName||g.name||"Component"):s("105",g.displayName||g.name||"Component"),w=new o(g),this._compositeType=C.StatelessFunctional),"production"!==t.env.NODE_ENV){null==w.render&&("production"!==t.env.NODE_ENV?k(!1,"%s(...): No `render` method found on the returned component instance: you may have forgotten to define `render`.",g.displayName||g.name||"Component"):void 0);var O=w.props!==m,D=g.displayName||g.name||"Component";"production"!==t.env.NODE_ENV?k(void 0===w.props||!O,"%s(...): When calling super() in `%s`, make sure to pass up the same props that your component's constructor was passed.",D,D):void 0}w.props=m,w.context=y,w.refs=_,w.updater=b,this._instance=w,v.set(w,this),"production"!==t.env.NODE_ENV&&("production"!==t.env.NODE_ENV?k(!w.getInitialState||w.getInitialState.isReactClassApproved||w.state,"getInitialState was defined on %s, a plain JavaScript class. This is only supported for classes created using React.createClass. Did you mean to define a state property instead?",this.getName()||"a component"):void 0,"production"!==t.env.NODE_ENV?k(!w.getDefaultProps||w.getDefaultProps.isReactClassApproved,"getDefaultProps was defined on %s, a plain JavaScript class. This is only supported for classes created using React.createClass. Use a static property to define defaultProps instead.",this.getName()||"a component"):void 0,"production"!==t.env.NODE_ENV?k(!w.propTypes,"propTypes was defined as an instance property on %s. Use a static property to define propTypes instead.",this.getName()||"a component"):void 0,"production"!==t.env.NODE_ENV?k(!w.contextTypes,"contextTypes was defined as an instance property on %s. Use a static property to define contextTypes instead.",this.getName()||"a component"):void 0,"production"!==t.env.NODE_ENV?k("function"!=typeof w.componentShouldUpdate,"%s has a method called componentShouldUpdate(). Did you mean shouldComponentUpdate()? The name is phrased as a question because the function is expected to return a value.",this.getName()||"A component"):void 0,"production"!==t.env.NODE_ENV?k("function"!=typeof w.componentDidUnmount,"%s has a method called componentDidUnmount(). But there is no such lifecycle method. Did you mean componentWillUnmount()?",this.getName()||"A component"):void 0,"production"!==t.env.NODE_ENV?k("function"!=typeof w.componentWillRecieveProps,"%s has a method called componentWillRecieveProps(). Did you mean componentWillReceiveProps()?",this.getName()||"A component"):void 0);var S=w.state;void 0===S&&(w.state=S=null),"object"!==("undefined"==typeof S?"undefined":c(S))||Array.isArray(S)?"production"!==t.env.NODE_ENV?E(!1,"%s.state: must be set to an object or null",this.getName()||"ReactCompositeComponent"):s("106",this.getName()||"ReactCompositeComponent"):void 0,this._pendingStateQueue=null,this._pendingReplaceState=!1,this._pendingForceUpdate=!1;var T;return T=w.unstable_handleError?this.performInitialMountWithErrorHandling(h,n,l,e,d):this.performInitialMount(h,n,l,e,d),w.componentDidMount&&("production"!==t.env.NODE_ENV?e.getReactMountReady().enqueue(function(){u(function(){return w.componentDidMount()},f._debugID,"componentDidMount")}):e.getReactMountReady().enqueue(w.componentDidMount,w)),T},_constructComponent:function(e,n,o,r){if("production"===t.env.NODE_ENV)return this._constructComponentWithoutOwner(e,n,o,r);f.current=this;try{return this._constructComponentWithoutOwner(e,n,o,r)}finally{f.current=null}},_constructComponentWithoutOwner:function(e,n,o,r){var i=this._currentElement.type;return e?"production"!==t.env.NODE_ENV?u(function(){return new i(n,o,r)},this._debugID,"ctor"):new i(n,o,r):"production"!==t.env.NODE_ENV?u(function(){return i(n,o,r)},this._debugID,"render"):i(n,o,r)},performInitialMountWithErrorHandling:function(e,t,n,o,r){var i,a=o.checkpoint();try{i=this.performInitialMount(e,t,n,o,r)}catch(u){o.rollback(a),this._instance.unstable_handleError(u),this._pendingStateQueue&&(this._instance.state=this._processPendingState(this._instance.props,this._instance.context)),a=o.checkpoint(),this._renderedComponent.unmountComponent(!0),o.rollback(a),i=this.performInitialMount(e,t,n,o,r)}return i},performInitialMount:function(e,n,o,r,i){var a=this._instance,c=0;"production"!==t.env.NODE_ENV&&(c=this._debugID),a.componentWillMount&&("production"!==t.env.NODE_ENV?u(function(){return a.componentWillMount()},c,"componentWillMount"):a.componentWillMount(),this._pendingStateQueue&&(a.state=this._processPendingState(a.props,a.context))),void 0===e&&(e=this._renderValidatedComponent());var s=y.getType(e);this._renderedNodeType=s;var l=this._instantiateReactComponent(e,s!==y.EMPTY);this._renderedComponent=l;var p=g.mountComponent(l,r,n,o,this._processChildContext(i),c);if("production"!==t.env.NODE_ENV&&0!==c){var d=0!==l._debugID?[l._debugID]:[];m.debugTool.onSetChildren(c,d)}return p},getHostNode:function(){return g.getHostNode(this._renderedComponent)},unmountComponent:function(e){if(this._renderedComponent){var n=this._instance;if(n.componentWillUnmount&&!n._calledComponentWillUnmount)if(n._calledComponentWillUnmount=!0,e){var o=this.getName()+".componentWillUnmount()";h.invokeGuardedCallback(o,n.componentWillUnmount.bind(n))}else"production"!==t.env.NODE_ENV?u(function(){return n.componentWillUnmount()},this._debugID,"componentWillUnmount"):n.componentWillUnmount();this._renderedComponent&&(g.unmountComponent(this._renderedComponent,e),this._renderedNodeType=null,this._renderedComponent=null,this._instance=null),this._pendingStateQueue=null,this._pendingReplaceState=!1,this._pendingForceUpdate=!1,this._pendingCallbacks=null,this._pendingElement=null,this._context=null,this._rootNodeID=0,this._topLevelWrapper=null,v.remove(n)}},_maskContext:function(e){var t=this._currentElement.type,n=t.contextTypes;if(!n)return _;var o={};for(var r in n)o[r]=e[r];return o},_processContext:function(e){var n=this._maskContext(e);if("production"!==t.env.NODE_ENV){var o=this._currentElement.type;o.contextTypes&&this._checkContextTypes(o.contextTypes,n,"context")}return n},_processChildContext:function(e){var n,o=this._currentElement.type,r=this._instance;if(r.getChildContext)if("production"!==t.env.NODE_ENV){m.debugTool.onBeginProcessingChildContext();try{n=r.getChildContext()}finally{m.debugTool.onEndProcessingChildContext()}}else n=r.getChildContext();if(n){"object"!==c(o.childContextTypes)?"production"!==t.env.NODE_ENV?E(!1,"%s.getChildContext(): childContextTypes must be defined in order to use getChildContext().",this.getName()||"ReactCompositeComponent"):s("107",this.getName()||"ReactCompositeComponent"):void 0,"production"!==t.env.NODE_ENV&&this._checkContextTypes(o.childContextTypes,n,"childContext");for(var i in n)i in o.childContextTypes?void 0:"production"!==t.env.NODE_ENV?E(!1,'%s.getChildContext(): key "%s" is not defined in childContextTypes.',this.getName()||"ReactCompositeComponent",i):s("108",this.getName()||"ReactCompositeComponent",i);return l({},e,n)}return e},_checkContextTypes:function(e,n,o){"production"!==t.env.NODE_ENV&&b(e,n,o,this.getName(),null,this._debugID)},receiveComponent:function(e,t,n){var o=this._currentElement,r=this._context;this._pendingElement=null,this.updateComponent(t,o,e,r,n)},performUpdateIfNecessary:function(e){null!=this._pendingElement?g.receiveComponent(this,this._pendingElement,e,this._context):null!==this._pendingStateQueue||this._pendingForceUpdate?this.updateComponent(e,this._currentElement,this._currentElement,this._context,this._context):this._updateBatchNumber=null},updateComponent:function(e,n,o,r,i){var a=this._instance;null==a?"production"!==t.env.NODE_ENV?E(!1,"Attempted to update component `%s` that has already been unmounted (or failed to mount).",this.getName()||"ReactCompositeComponent"):s("136",this.getName()||"ReactCompositeComponent"):void 0;var c,l=!1;this._context===i?c=a.context:(c=this._processContext(i),l=!0);var p=n.props,d=o.props;n!==o&&(l=!0),l&&a.componentWillReceiveProps&&("production"!==t.env.NODE_ENV?u(function(){return a.componentWillReceiveProps(d,c)},this._debugID,"componentWillReceiveProps"):a.componentWillReceiveProps(d,c));var f=this._processPendingState(d,c),h=!0;this._pendingForceUpdate||(a.shouldComponentUpdate?h="production"!==t.env.NODE_ENV?u(function(){return a.shouldComponentUpdate(d,f,c)},this._debugID,"shouldComponentUpdate"):a.shouldComponentUpdate(d,f,c):this._compositeType===C.PureClass&&(h=!N(p,d)||!N(a.state,f))),"production"!==t.env.NODE_ENV&&("production"!==t.env.NODE_ENV?k(void 0!==h,"%s.shouldComponentUpdate(): Returned undefined instead of a boolean value. Make sure to return true or false.",this.getName()||"ReactCompositeComponent"):void 0),this._updateBatchNumber=null,h?(this._pendingForceUpdate=!1,this._performComponentUpdate(o,d,f,c,e,i)):(this._currentElement=o,this._context=i,a.props=d,a.state=f,a.context=c)},_processPendingState:function(e,t){var n=this._instance,o=this._pendingStateQueue,r=this._pendingReplaceState;if(this._pendingReplaceState=!1,this._pendingStateQueue=null,!o)return n.state;if(r&&1===o.length)return o[0];for(var i=l({},r?o[0]:n.state),a=r?1:0;a-1&&navigator.userAgent.indexOf("Edge")===-1||navigator.userAgent.indexOf("Firefox")>-1)){var v=window.location.protocol.indexOf("http")===-1&&navigator.userAgent.indexOf("Firefox")===-1;console.debug("Download the React DevTools "+(v?"and use an HTTP server (instead of a file: URL) ":"")+"for a better development experience: https://fb.me/react-devtools")}var m=function(){};"production"!==t.env.NODE_ENV?d((m.name||m.toString()).indexOf("testFn")!==-1,"It looks like you're using a minified copy of the development build of React. When deploying React apps to production, make sure to use the production build which skips development warnings and is faster. See https://fb.me/react-minification for more details."):void 0;var y=document.documentMode&&document.documentMode<8;"production"!==t.env.NODE_ENV?d(!y,'Internet Explorer is running in compatibility mode; please add the following tag to your HTML to prevent this from happening: '):void 0;for(var g=[Array.isArray,Array.prototype.every,Array.prototype.forEach,Array.prototype.indexOf,Array.prototype.map,Date.now,Function.prototype.bind,Object.keys,String.prototype.trim],b=0;b",r(e),r(n)):void 0)}}function a(e,n){n&&(ae[e._tag]&&(null!=n.children||null!=n.dangerouslySetInnerHTML?"production"!==t.env.NODE_ENV?F(!1,"%s is a void element tag and must neither have `children` nor use `dangerouslySetInnerHTML`.%s",e._tag,e._currentElement._owner?" Check the render method of "+e._currentElement._owner.getName()+".":""):g("137",e._tag,e._currentElement._owner?" Check the render method of "+e._currentElement._owner.getName()+".":""):void 0),null!=n.dangerouslySetInnerHTML&&(null!=n.children?"production"!==t.env.NODE_ENV?F(!1,"Can only set one of `children` or `props.dangerouslySetInnerHTML`."):g("60"):void 0,"object"===y(n.dangerouslySetInnerHTML)&&J in n.dangerouslySetInnerHTML?void 0:"production"!==t.env.NODE_ENV?F(!1,"`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. Please visit https://fb.me/react-invariant-dangerously-set-inner-html for more information."):g("61")),"production"!==t.env.NODE_ENV&&("production"!==t.env.NODE_ENV?W(null==n.innerHTML,"Directly setting property `innerHTML` is not permitted. For more information, lookup documentation on `dangerouslySetInnerHTML`."):void 0,"production"!==t.env.NODE_ENV?W(n.suppressContentEditableWarning||!n.contentEditable||null==n.children,"A component is `contentEditable` and contains `children` managed by React. It is now your responsibility to guarantee that none of those nodes are unexpectedly modified or duplicated. This is probably not intentional."):void 0,"production"!==t.env.NODE_ENV?W(null==n.onFocusIn&&null==n.onFocusOut,"React uses onFocus and onBlur instead of onFocusIn and onFocusOut. All React events are normalized to bubble, so onFocusIn and onFocusOut are not needed/supported by React."):void 0),null!=n.style&&"object"!==y(n.style)?"production"!==t.env.NODE_ENV?F(!1,"The `style` prop expects a mapping from style properties to values, not a string. For example, style={{marginRight: spacing + 'em'}} when using JSX.%s",o(e)):g("62",o(e)):void 0)}function u(e,n,o,r){if(!(r instanceof R)){"production"!==t.env.NODE_ENV&&("production"!==t.env.NODE_ENV?W("onScroll"!==n||B("scroll",!0),"This browser doesn't support the `onScroll` event"):void 0);var i=e._hostContainerInfo,a=i._node&&i._node.nodeType===ee,u=a?i._node:i._ownerDocument;$(n,u),r.getReactMountReady().enqueue(c,{inst:e,registrationName:n,listener:o})}}function c(){var e=this;x.putListener(e.inst,e.registrationName,e.listener)}function s(){var e=this;P.postMountWrapper(e)}function l(){var e=this;A.postMountWrapper(e)}function p(){var e=this;M.postMountWrapper(e)}function d(){var e=this;e._rootNodeID?void 0:"production"!==t.env.NODE_ENV?F(!1,"Must be mounted to trap events"):g("63");var n=K(e);switch(n?void 0:"production"!==t.env.NODE_ENV?F(!1,"trapBubbledEvent(...): Requires node to be rendered."):g("64"),e._tag){case"iframe":case"object":e._wrapperState.listeners=[D.trapBubbledEvent("topLoad","load",n)];break;case"video":case"audio":e._wrapperState.listeners=[];for(var o in oe)oe.hasOwnProperty(o)&&e._wrapperState.listeners.push(D.trapBubbledEvent(o,oe[o],n));break;case"source":e._wrapperState.listeners=[D.trapBubbledEvent("topError","error",n)];break;case"img":e._wrapperState.listeners=[D.trapBubbledEvent("topError","error",n),D.trapBubbledEvent("topLoad","load",n)];break;case"form":e._wrapperState.listeners=[D.trapBubbledEvent("topReset","reset",n),D.trapBubbledEvent("topSubmit","submit",n)];break;case"input":case"select":case"textarea":e._wrapperState.listeners=[D.trapBubbledEvent("topInvalid","invalid",n)]}}function f(){I.postUpdateWrapper(this)}function h(e){se.call(ce,e)||(ue.test(e)?void 0:"production"!==t.env.NODE_ENV?F(!1,"Invalid tag: %s",e):g("65",e),ce[e]=!0)}function v(e,t){return e.indexOf("-")>=0||null!=t.is}function m(e){var n=e.type;h(n),this._currentElement=e,this._tag=n.toLowerCase(),this._namespaceURI=null,this._renderedChildren=null,this._previousStyle=null,this._previousStyleCopy=null,this._hostNode=null,this._hostParent=null,this._rootNodeID=0,this._domID=0,this._hostContainerInfo=null,this._wrapperState=null,this._topLevelWrapper=null,this._flags=0,"production"!==t.env.NODE_ENV&&(this._ancestorInfo=null,ne.call(this,null))}var y="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},g=n(4),b=n(5),_=n(311),E=n(313),N=n(30),w=n(79),k=n(21),C=n(137),x=n(39),O=n(50),D=n(51),S=n(138),T=n(6),P=n(329),M=n(332),I=n(139),A=n(335),j=n(13),V=n(348),R=n(353),U=n(14),L=n(54),F=n(2),B=n(90),z=n(57),H=n(92),W=n(3),q=S,Y=x.deleteListener,K=T.getNodeFromInstance,$=D.listenTo,G=O.registrationNameModules,X={string:!0,number:!0},Q="style",J="__html",Z={children:null,dangerouslySetInnerHTML:null,suppressContentEditableWarning:null},ee=11,te={},ne=U;"production"!==t.env.NODE_ENV&&(ne=function(e){var t=null!=this._contentDebugID,n=this._debugID,o=-n;return null==e?(t&&j.debugTool.onUnmountComponent(this._contentDebugID),void(this._contentDebugID=null)):(H(null,String(e),this,this._ancestorInfo),this._contentDebugID=o,void(t?(j.debugTool.onBeforeUpdateComponent(o,e),j.debugTool.onUpdateComponent(o)):(j.debugTool.onBeforeMountComponent(o,e,n),j.debugTool.onMountComponent(o),j.debugTool.onSetChildren(n,[o]))))});var oe={topAbort:"abort",topCanPlay:"canplay",topCanPlayThrough:"canplaythrough",topDurationChange:"durationchange",topEmptied:"emptied",topEncrypted:"encrypted",topEnded:"ended",topError:"error",topLoadedData:"loadeddata",topLoadedMetadata:"loadedmetadata",topLoadStart:"loadstart",topPause:"pause",topPlay:"play",topPlaying:"playing",topProgress:"progress",topRateChange:"ratechange",topSeeked:"seeked",topSeeking:"seeking",topStalled:"stalled",topSuspend:"suspend",topTimeUpdate:"timeupdate",topVolumeChange:"volumechange",topWaiting:"waiting"},re={area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0},ie={listing:!0,pre:!0,textarea:!0},ae=b({menuitem:!0},re),ue=/^[a-zA-Z][a-zA-Z:_\.\-\d]*$/,ce={},se={}.hasOwnProperty,le=1;m.displayName="ReactDOMComponent",m.Mixin={mountComponent:function(e,n,o,r){this._rootNodeID=le++,this._domID=o._idCounter++,this._hostParent=n,this._hostContainerInfo=o;var i=this._currentElement.props;switch(this._tag){case"audio":case"form":case"iframe":case"img":case"link":case"object":case"source":case"video":this._wrapperState={listeners:null},e.getReactMountReady().enqueue(d,this);break;case"input":P.mountWrapper(this,i,n),i=P.getHostProps(this,i),e.getReactMountReady().enqueue(d,this);break;case"option":M.mountWrapper(this,i,n),i=M.getHostProps(this,i);break;case"select":I.mountWrapper(this,i,n),i=I.getHostProps(this,i),e.getReactMountReady().enqueue(d,this);break;case"textarea":A.mountWrapper(this,i,n),i=A.getHostProps(this,i),e.getReactMountReady().enqueue(d,this)}a(this,i);var u,c;if(null!=n?(u=n._namespaceURI,c=n._tag):o._tag&&(u=o._namespaceURI, +c=o._tag),(null==u||u===w.svg&&"foreignobject"===c)&&(u=w.html),u===w.html&&("svg"===this._tag?u=w.svg:"math"===this._tag&&(u=w.mathml)),this._namespaceURI=u,"production"!==t.env.NODE_ENV){var f;null!=n?f=n._ancestorInfo:o._tag&&(f=o._ancestorInfo),f&&H(this._tag,null,this,f),this._ancestorInfo=H.updatedAncestorInfo(f,this._tag,this)}var h;if(e.useCreateElement){var v,m=o._ownerDocument;if(u===w.html)if("script"===this._tag){var y=m.createElement("div"),g=this._currentElement.type;y.innerHTML="<"+g+">",v=y.removeChild(y.firstChild)}else v=i.is?m.createElement(this._currentElement.type,i.is):m.createElement(this._currentElement.type);else v=m.createElementNS(u,this._currentElement.type);T.precacheNode(this,v),this._flags|=q.hasCachedChildNodes,this._hostParent||C.setAttributeForRoot(v),this._updateDOMProperties(null,i,e);var b=N(v);this._createInitialChildren(e,i,r,b),h=b}else{var E=this._createOpenTagMarkupAndPutListeners(e,i),k=this._createContentMarkup(e,i,r);h=!k&&re[this._tag]?E+"/>":E+">"+k+""}switch(this._tag){case"input":e.getReactMountReady().enqueue(s,this),i.autoFocus&&e.getReactMountReady().enqueue(_.focusDOMComponent,this);break;case"textarea":e.getReactMountReady().enqueue(l,this),i.autoFocus&&e.getReactMountReady().enqueue(_.focusDOMComponent,this);break;case"select":i.autoFocus&&e.getReactMountReady().enqueue(_.focusDOMComponent,this);break;case"button":i.autoFocus&&e.getReactMountReady().enqueue(_.focusDOMComponent,this);break;case"option":e.getReactMountReady().enqueue(p,this)}return h},_createOpenTagMarkupAndPutListeners:function(e,n){var o="<"+this._currentElement.type;for(var r in n)if(n.hasOwnProperty(r)){var i=n[r];if(null!=i)if(G.hasOwnProperty(r))i&&u(this,r,i,e);else{r===Q&&(i&&("production"!==t.env.NODE_ENV&&(this._previousStyle=i),i=this._previousStyleCopy=b({},n.style)),i=E.createMarkupForStyles(i,this));var a=null;null!=this._tag&&v(this._tag,n)?Z.hasOwnProperty(r)||(a=C.createMarkupForCustomAttribute(r,i)):a=C.createMarkupForProperty(r,i),a&&(o+=" "+a)}}return e.renderToStaticMarkup?o:(this._hostParent||(o+=" "+C.createMarkupForRoot()),o+=" "+C.createMarkupForID(this._domID))},_createContentMarkup:function(e,n,o){var r="",i=n.dangerouslySetInnerHTML;if(null!=i)null!=i.__html&&(r=i.__html);else{var a=X[y(n.children)]?n.children:null,u=null!=a?null:n.children;if(null!=a)r=L(a),"production"!==t.env.NODE_ENV&&ne.call(this,a);else if(null!=u){var c=this.mountChildren(u,e,o);r=c.join("")}}return ie[this._tag]&&"\n"===r.charAt(0)?"\n"+r:r},_createInitialChildren:function(e,n,o,r){var i=n.dangerouslySetInnerHTML;if(null!=i)null!=i.__html&&N.queueHTML(r,i.__html);else{var a=X[y(n.children)]?n.children:null,u=null!=a?null:n.children;if(null!=a)""!==a&&("production"!==t.env.NODE_ENV&&ne.call(this,a),N.queueText(r,a));else if(null!=u)for(var c=this.mountChildren(u,e,o),s=0;s tried to unmount. Because of cross-browser quirks it is impossible to unmount some top-level components (eg , , and ) reliably and efficiently. To fix this, have a single top-level component that never unmounts render these elements.",this._tag):g("66",this._tag)}this.unmountChildren(e),T.uncacheNode(this),x.deleteAllListeners(this),this._rootNodeID=0,this._domID=0,this._wrapperState=null,"production"!==t.env.NODE_ENV&&ne.call(this,null)},getPublicInstance:function(){return K(this)}},b(m.prototype,m.Mixin,V.Mixin),e.exports=m}).call(t,n(0))},function(e,t,n){"use strict";(function(t){function o(e,n){var o={_topLevelWrapper:e,_idCounter:1,_ownerDocument:n?n.nodeType===i?n:n.ownerDocument:null,_node:n,_tag:n?n.nodeName.toLowerCase():null,_namespaceURI:n?n.namespaceURI:null};return"production"!==t.env.NODE_ENV&&(o._ancestorInfo=n?r.updatedAncestorInfo(null,o._tag,null):null),o}var r=n(92),i=9;e.exports=o}).call(t,n(0))},function(e,t,n){"use strict";var o=n(5),r=n(30),i=n(6),a=function(e){this._currentElement=null,this._hostNode=null,this._hostParent=null,this._hostContainerInfo=null,this._domID=0};o(a.prototype,{mountComponent:function(e,t,n,o){var a=n._idCounter++;this._domID=a,this._hostParent=t,this._hostContainerInfo=n;var u=" react-empty: "+this._domID+" ";if(e.useCreateElement){var c=n._ownerDocument,s=c.createComment(u);return i.precacheNode(this,s),r(s)}return e.renderToStaticMarkup?"":""},receiveComponent:function(){},getHostNode:function(){return i.getNodeFromInstance(this)},unmountComponent:function(){i.uncacheNode(this)}}),e.exports=a},function(e,t,n){"use strict";var o={useCreateElement:!0,useFiber:!1};e.exports=o},function(e,t,n){"use strict";var o=n(78),r=n(6),i={dangerouslyProcessChildrenUpdates:function(e,t){var n=r.getNodeFromInstance(e);o.processUpdates(n,t)}};e.exports=i},function(e,t,n){"use strict";(function(t){function o(){this._rootNodeID&&_.updateWrapper(this)}function r(e){var t="checkbox"===e.type||"radio"===e.type;return t?null!=e.checked:null!=e.value}function i(e){var n=this._currentElement.props,r=s.executeOnChange(n,e);p.asap(o,this);var i=n.name;if("radio"===n.type&&null!=i){for(var u=l.getNodeFromInstance(this),c=u;c.parentNode;)c=c.parentNode;for(var f=c.querySelectorAll("input[name="+JSON.stringify(""+i)+'][type="radio"]'),h=0;h tag. For details, see https://fb.me/invalid-aria-prop%s",s,n.type,u.getStackAddendumByID(e)):void 0:r.length>1&&("production"!==t.env.NODE_ENV?c(!1,"Invalid aria props %s on <%s> tag. For details, see https://fb.me/invalid-aria-prop%s",s,n.type,u.getStackAddendumByID(e)):void 0)}function i(e,t){null!=t&&"string"==typeof t.type&&(t.type.indexOf("-")>=0||t.props.is||r(e,t))}var a=n(21),u=n(12),c=n(3),s={},l=new RegExp("^(aria)-["+a.ATTRIBUTE_NAME_CHAR+"]*$"),p={onBeforeMountComponent:function(e,n){"production"!==t.env.NODE_ENV&&i(e,n)},onBeforeUpdateComponent:function(e,n){"production"!==t.env.NODE_ENV&&i(e,n)}};e.exports=p}).call(t,n(0))},function(e,t,n){"use strict";(function(t){function o(e,n){null!=n&&("input"!==n.type&&"textarea"!==n.type&&"select"!==n.type||null==n.props||null!==n.props.value||a||("production"!==t.env.NODE_ENV?i(!1,"`value` prop on `%s` should not be null. Consider using the empty string to clear the component or `undefined` for uncontrolled components.%s",n.type,r.getStackAddendumByID(e)):void 0,a=!0))}var r=n(12),i=n(3),a=!1,u={onBeforeMountComponent:function(e,t){o(e,t)},onBeforeUpdateComponent:function(e,t){o(e,t)}};e.exports=u}).call(t,n(0))},function(e,t,n){"use strict";(function(t){function o(e){var n="";return i.Children.forEach(e,function(e){null!=e&&("string"==typeof e||"number"==typeof e?n+=e:s||(s=!0,"production"!==t.env.NODE_ENV?c(!1,"Only strings and numbers are supported as