From 94b2a30118de3df553d8f81be4675aa0c39758f7 Mon Sep 17 00:00:00 2001 From: Paul Le Cam Date: Tue, 29 Sep 2015 08:38:48 +0100 Subject: [PATCH] v0.8.0-rc.3 --- .npmignore | 6 +- CHANGELOG.md | 5 + README.md | 8 +- example/app.js | 9 +- example/build/app.js | 102 +- example/build/dependencies.js | 30110 +++++++++++++------------- example/build/lib.js | 202 +- example/other-layers.js | 38 + example/vector-layers.js | 2 +- lib/{BoundsMap.js => LayerGroup.js} | 86 +- lib/Map.js | 6 +- lib/MapLayer.js | 4 +- lib/index.js | 12 +- package.json | 2 +- src/LayerGroup.js | 30 + src/Map.js | 7 +- src/MapLayer.js | 6 +- src/index.js | 2 +- 18 files changed, 15385 insertions(+), 15252 deletions(-) create mode 100644 example/other-layers.js rename lib/{BoundsMap.js => LayerGroup.js} (55%) create mode 100644 src/LayerGroup.js diff --git a/.npmignore b/.npmignore index 26223f7f..d5c5686e 100644 --- a/.npmignore +++ b/.npmignore @@ -1,6 +1,8 @@ +coverage example -src __tests__ .editorconfig +.eslintrc .travis.yml -gulpfile.js +bower.json +gulpfile.babel.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cb5a36e..c76af93e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## v0.8.0-rc.3 (29/09/15) + +- Added `LayerGroup` component ([#58](https://github.com/PaulLeCam/react-leaflet/pull/58)). +- `Map` now supports dynamic `bounds` and `maxBounds` properties ([#72](https://github.com/PaulLeCam/react-leaflet/pull/72)). + ## v0.8.0-rc.2 (22/09/15) Check if `Popup` content node exists before trying to unmount. diff --git a/README.md b/README.md index 85508694..4344acee 100644 --- a/README.md +++ b/README.md @@ -127,11 +127,11 @@ Base class extending `PopupContainer` with the following methods: This is the top-level component that must be mounted for children ones to be rendered. Refer to Leaflet documentation for more information about the properties. **Properties** -- `bounds` (optional, dynamic): A rectangle for the map to contain. It will be centered, and the map will zoom in as close as it can while still showing the full bounds. This property is dynamic, if you change it it will be reflected on the map. +- `bounds: Bounds` (optional, dynamic): A rectangle for the map to contain. It will be centered, and the map will zoom in as close as it can while still showing the full bounds. This property is dynamic, if you change it it will be reflected on the map. - `center: LatLng` (optional, dynamic): Center of the map. This property is dynamic, if you change it it will be reflected in the map. - `className: String` (optional, dynamic): className property of the `
` container for the map. - `id: String` (optional): The ID of the `
` container for the map. If you don't provide it, a unique one will be created. -- `maxBounds: Bounds` (optional) +- `maxBounds: Bounds` (optional, dynamic) - `maxZoom: Number` (optional) - `minZoom: Number` (optional) - `style: Object` (optional, dynamic): style property of the `
` container for the map. @@ -207,6 +207,10 @@ All vector layers extend the **Path** component and therefore accept dynamic [Pa #### Other Layers +##### LayerGroup + +Use the `LayerGroup` wrapper component to group children layers together. + ##### Implemented but needing testing and documentation - FeatureGroup diff --git a/example/app.js b/example/app.js index 62ed2372..91d7bba0 100644 --- a/example/app.js +++ b/example/app.js @@ -3,8 +3,9 @@ import { render } from 'react-dom'; import SimpleExample from './simple'; import EventsExample from './events'; -import VectorLayersExample from './vector-layers'; import BoundsExample from './bounds'; +import VectorLayersExample from './vector-layers'; +import OtherLayersExample from './other-layers'; const examples = (
@@ -14,11 +15,13 @@ const examples = (

Events

Click the map to show a marker at your detected location

-

Vector layers

-

Map view by bounds

Click a rectangle to fit the map to its bounds

+

Vector layers

+ +

Other layers

+
); diff --git a/example/build/app.js b/example/build/app.js index 40fb4d4b..d3f1be5b 100755 --- a/example/build/app.js +++ b/example/build/app.js @@ -17,13 +17,17 @@ var _events = require('./events'); var _events2 = _interopRequireDefault(_events); +var _bounds = require('./bounds'); + +var _bounds2 = _interopRequireDefault(_bounds); + var _vectorLayers = require('./vector-layers'); var _vectorLayers2 = _interopRequireDefault(_vectorLayers); -var _bounds = require('./bounds'); +var _otherLayers = require('./other-layers'); -var _bounds2 = _interopRequireDefault(_bounds); +var _otherLayers2 = _interopRequireDefault(_otherLayers); var examples = _react2['default'].createElement( 'div', @@ -53,25 +57,31 @@ var examples = _react2['default'].createElement( _react2['default'].createElement( 'h2', null, - 'Vector layers' + 'Map view by bounds' ), - _react2['default'].createElement(_vectorLayers2['default'], null), + _react2['default'].createElement( + 'p', + null, + 'Click a rectangle to fit the map to its bounds' + ), + _react2['default'].createElement(_bounds2['default'], null), _react2['default'].createElement( 'h2', null, - 'Map view by bounds' + 'Vector layers' ), + _react2['default'].createElement(_vectorLayers2['default'], null), _react2['default'].createElement( - 'p', + 'h2', null, - 'Click a rectangle to fit the map to its bounds' + 'Other layers' ), - _react2['default'].createElement(_bounds2['default'], null) + _react2['default'].createElement(_otherLayers2['default'], null) ); (0, _reactDom.render)(examples, document.getElementById('app')); -},{"./bounds":2,"./events":3,"./simple":4,"./vector-layers":5,"react":"react","react-dom":"react-dom"}],2:[function(require,module,exports){ +},{"./bounds":2,"./events":3,"./other-layers":4,"./simple":5,"./vector-layers":6,"react":"react","react-dom":"react-dom"}],2:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -270,6 +280,78 @@ var _react2 = _interopRequireDefault(_react); var _reactLeaflet = require('react-leaflet'); +var OtherLayersExample = (function (_Component) { + _inherits(OtherLayersExample, _Component); + + function OtherLayersExample() { + _classCallCheck(this, OtherLayersExample); + + _get(Object.getPrototypeOf(OtherLayersExample.prototype), 'constructor', this).apply(this, arguments); + } + + _createClass(OtherLayersExample, [{ + key: 'render', + value: function render() { + var center = [51.505, -0.09]; + + var rectangle = [[51.49, -0.08], [51.5, -0.06]]; + + return _react2['default'].createElement( + _reactLeaflet.Map, + { center: center, zoom: 13 }, + _react2['default'].createElement(_reactLeaflet.TileLayer, { + attribution: '© OpenStreetMap contributors', + url: 'http://{s}.tile.osm.org/{z}/{x}/{y}.png' + }), + _react2['default'].createElement( + _reactLeaflet.LayerGroup, + null, + _react2['default'].createElement(_reactLeaflet.Circle, { center: center, fillColor: 'blue', radius: 200 }), + _react2['default'].createElement(_reactLeaflet.Circle, { center: center, fillColor: 'red', radius: 100, stroke: false }), + _react2['default'].createElement( + _reactLeaflet.LayerGroup, + null, + _react2['default'].createElement(_reactLeaflet.Circle, { center: [51.51, -0.08], color: 'green', fillColor: 'green', radius: 100 }) + ) + ), + _react2['default'].createElement( + _reactLeaflet.LayerGroup, + null, + _react2['default'].createElement(_reactLeaflet.Rectangle, { bounds: rectangle, color: 'black' }) + ) + ); + } + }]); + + return OtherLayersExample; +})(_react.Component); + +exports['default'] = OtherLayersExample; +module.exports = exports['default']; + +},{"react":"react","react-leaflet":"react-leaflet"}],5:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +var _reactLeaflet = require('react-leaflet'); + var SimpleExample = (function (_Component) { _inherits(SimpleExample, _Component); @@ -320,7 +402,7 @@ var SimpleExample = (function (_Component) { exports['default'] = SimpleExample; module.exports = exports['default']; -},{"react":"react","react-leaflet":"react-leaflet"}],5:[function(require,module,exports){ +},{"react":"react","react-leaflet":"react-leaflet"}],6:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { diff --git a/example/build/dependencies.js b/example/build/dependencies.js index d60e4f70..52cf5852 100755 --- a/example/build/dependencies.js +++ b/example/build/dependencies.js @@ -1,97 +1,91 @@ require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 1) { - for (var i = 1; i < arguments.length; i++) { - args[i - 1] = arguments[i]; + /** + * Listen to DOM events during the capture phase. + * + * @param {DOMEventTarget} target DOM element to register listener on. + * @param {string} eventType Event type, e.g. 'click' or 'mouseover'. + * @param {function} callback Callback function. + * @return {object} Object with a `remove` method. + */ + capture: function (target, eventType, callback) { + if (target.addEventListener) { + target.addEventListener(eventType, callback, true); + return { + remove: function () { + target.removeEventListener(eventType, callback, true); } + }; + } else { + if (process.env.NODE_ENV !== 'production') { + console.error('Attempted to listen to events during the capture phase on a ' + 'browser that does not support the capture phase. Your application ' + 'will not receive some events.'); + } + return { + remove: emptyFunction + }; } - queue.push(new Item(fun, args)); - if (queue.length === 1 && !draining) { - setTimeout(drainQueue, 0); - } -}; - -// v8 likes predictible objects -function Item(fun, array) { - this.fun = fun; - this.array = array; -} -Item.prototype.run = function () { - this.fun.apply(null, this.array); -}; -process.title = 'browser'; -process.browser = true; -process.env = {}; -process.argv = []; -process.version = ''; // empty string to avoid regexp issues -process.versions = {}; - -function noop() {} - -process.on = noop; -process.addListener = noop; -process.once = noop; -process.off = noop; -process.removeListener = noop; -process.removeAllListeners = noop; -process.emit = noop; - -process.binding = function (name) { - throw new Error('process.binding is not supported'); -}; + }, -process.cwd = function () { return '/' }; -process.chdir = function (dir) { - throw new Error('process.chdir is not supported'); + registerDefault: function () {} }; -process.umask = function() { return 0; }; -},{}],2:[function(require,module,exports){ +module.exports = EventListener; +}).call(this,require('_process')) +},{"./emptyFunction":8,"_process":27}],2:[function(require,module,exports){ /** * Copyright 2013-2015, Facebook, Inc. * All rights reserved. @@ -100,441 +94,338 @@ process.umask = function() { return 0; }; * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @providesModule AutoFocusUtils - * @typechecks static-only + * @providesModule ExecutionEnvironment */ 'use strict'; -var ReactMount = require('./ReactMount'); +var canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement); -var findDOMNode = require('./findDOMNode'); -var focusNode = require('fbjs/lib/focusNode'); +/** + * Simple, lightweight module assisting with the detection and context of + * Worker. Helps avoid circular dependencies and allows code to reason about + * whether or not they are in a Worker, even if they never include the main + * `ReactWorker` dependency. + */ +var ExecutionEnvironment = { -var Mixin = { - componentDidMount: function () { - if (this.props.autoFocus) { - focusNode(findDOMNode(this)); - } - } -}; + canUseDOM: canUseDOM, -var AutoFocusUtils = { - Mixin: Mixin, + canUseWorkers: typeof Worker !== 'undefined', + + canUseEventListeners: canUseDOM && !!(window.addEventListener || window.attachEvent), + + canUseViewport: canUseDOM && !!window.screen, + + isInWorker: !canUseDOM // For now, this is true - might change in the future. - focusDOMComponent: function () { - focusNode(ReactMount.getNode(this._rootNodeID)); - } }; -module.exports = AutoFocusUtils; -},{"./ReactMount":66,"./findDOMNode":108,"fbjs/lib/focusNode":139}],3:[function(require,module,exports){ +module.exports = ExecutionEnvironment; +},{}],3:[function(require,module,exports){ /** - * Copyright 2013-2015 Facebook, Inc. + * Copyright 2013-2015, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @providesModule BeforeInputEventPlugin - * @typechecks static-only + * @providesModule camelize + * @typechecks */ -'use strict'; - -var EventConstants = require('./EventConstants'); -var EventPropagators = require('./EventPropagators'); -var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment'); -var FallbackCompositionState = require('./FallbackCompositionState'); -var SyntheticCompositionEvent = require('./SyntheticCompositionEvent'); -var SyntheticInputEvent = require('./SyntheticInputEvent'); +"use strict"; -var keyOf = require('fbjs/lib/keyOf'); +var _hyphenPattern = /-(.)/g; -var END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space -var START_KEYCODE = 229; +/** + * Camelcases a hyphenated string, for example: + * + * > camelize('background-color') + * < "backgroundColor" + * + * @param {string} string + * @return {string} + */ +function camelize(string) { + return string.replace(_hyphenPattern, function (_, character) { + return character.toUpperCase(); + }); +} -var canUseCompositionEvent = ExecutionEnvironment.canUseDOM && 'CompositionEvent' in window; +module.exports = camelize; +},{}],4:[function(require,module,exports){ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule camelizeStyleName + * @typechecks + */ -var documentMode = null; -if (ExecutionEnvironment.canUseDOM && 'documentMode' in document) { - documentMode = document.documentMode; -} +'use strict'; -// Webkit offers a very useful `textInput` event that can be used to -// directly represent `beforeInput`. The IE `textinput` event is not as -// useful, so we don't use it. -var canUseTextInputEvent = ExecutionEnvironment.canUseDOM && 'TextEvent' in window && !documentMode && !isPresto(); +var camelize = require('./camelize'); -// In IE9+, we have access to composition events, but the data supplied -// by the native compositionend event may be incorrect. Japanese ideographic -// spaces, for instance (\u3000) are not recorded correctly. -var useFallbackCompositionData = ExecutionEnvironment.canUseDOM && (!canUseCompositionEvent || documentMode && documentMode > 8 && documentMode <= 11); +var msPattern = /^-ms-/; /** - * Opera <= 12 includes TextEvent in window, but does not fire - * text input events. Rely on keypress instead. - */ -function isPresto() { - var opera = window.opera; - return typeof opera === 'object' && typeof opera.version === 'function' && parseInt(opera.version(), 10) <= 12; + * Camelcases a hyphenated CSS property name, for example: + * + * > camelizeStyleName('background-color') + * < "backgroundColor" + * > camelizeStyleName('-moz-transition') + * < "MozTransition" + * > camelizeStyleName('-ms-transition') + * < "msTransition" + * + * As Andi Smith suggests + * (http://www.andismith.com/blog/2012/02/modernizr-prefixed/), an `-ms` prefix + * is converted to lowercase `ms`. + * + * @param {string} string + * @return {string} + */ +function camelizeStyleName(string) { + return camelize(string.replace(msPattern, 'ms-')); } -var SPACEBAR_CODE = 32; -var SPACEBAR_CHAR = String.fromCharCode(SPACEBAR_CODE); +module.exports = camelizeStyleName; +},{"./camelize":3}],5:[function(require,module,exports){ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule containsNode + * @typechecks + */ -var topLevelTypes = EventConstants.topLevelTypes; +'use strict'; -// Events and their corresponding property names. -var eventTypes = { - beforeInput: { - phasedRegistrationNames: { - bubbled: keyOf({ onBeforeInput: null }), - captured: keyOf({ onBeforeInputCapture: null }) - }, - dependencies: [topLevelTypes.topCompositionEnd, topLevelTypes.topKeyPress, topLevelTypes.topTextInput, topLevelTypes.topPaste] - }, - compositionEnd: { - phasedRegistrationNames: { - bubbled: keyOf({ onCompositionEnd: null }), - captured: keyOf({ onCompositionEndCapture: null }) - }, - dependencies: [topLevelTypes.topBlur, topLevelTypes.topCompositionEnd, topLevelTypes.topKeyDown, topLevelTypes.topKeyPress, topLevelTypes.topKeyUp, topLevelTypes.topMouseDown] - }, - compositionStart: { - phasedRegistrationNames: { - bubbled: keyOf({ onCompositionStart: null }), - captured: keyOf({ onCompositionStartCapture: null }) - }, - dependencies: [topLevelTypes.topBlur, topLevelTypes.topCompositionStart, topLevelTypes.topKeyDown, topLevelTypes.topKeyPress, topLevelTypes.topKeyUp, topLevelTypes.topMouseDown] - }, - compositionUpdate: { - phasedRegistrationNames: { - bubbled: keyOf({ onCompositionUpdate: null }), - captured: keyOf({ onCompositionUpdateCapture: null }) - }, - dependencies: [topLevelTypes.topBlur, topLevelTypes.topCompositionUpdate, topLevelTypes.topKeyDown, topLevelTypes.topKeyPress, topLevelTypes.topKeyUp, topLevelTypes.topMouseDown] - } -}; +var isTextNode = require('./isTextNode'); -// Track whether we've ever handled a keypress on the space key. -var hasSpaceKeypress = false; +/*eslint-disable no-bitwise */ /** - * Return whether a native keypress event is assumed to be a command. - * This is required because Firefox fires `keypress` events for key commands - * (cut, copy, select-all, etc.) even though no character is inserted. + * Checks if a given DOM node contains or is another DOM node. + * + * @param {?DOMNode} outerNode Outer DOM node. + * @param {?DOMNode} innerNode Inner DOM node. + * @return {boolean} True if `outerNode` contains or is `innerNode`. */ -function isKeypressCommand(nativeEvent) { - return (nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) && - // ctrlKey && altKey is equivalent to AltGr, and is not a command. - !(nativeEvent.ctrlKey && nativeEvent.altKey); +function containsNode(_x, _x2) { + var _again = true; + + _function: while (_again) { + var outerNode = _x, + innerNode = _x2; + _again = false; + + if (!outerNode || !innerNode) { + return false; + } else if (outerNode === innerNode) { + return true; + } else if (isTextNode(outerNode)) { + return false; + } else if (isTextNode(innerNode)) { + _x = outerNode; + _x2 = innerNode.parentNode; + _again = true; + continue _function; + } else if (outerNode.contains) { + return outerNode.contains(innerNode); + } else if (outerNode.compareDocumentPosition) { + return !!(outerNode.compareDocumentPosition(innerNode) & 16); + } else { + return false; + } + } } +module.exports = containsNode; +},{"./isTextNode":18}],6:[function(require,module,exports){ /** - * Translate native top level events into event types. + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. * - * @param {string} topLevelType - * @return {object} + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule createArrayFromMixed + * @typechecks */ -function getCompositionEventType(topLevelType) { - switch (topLevelType) { - case topLevelTypes.topCompositionStart: - return eventTypes.compositionStart; - case topLevelTypes.topCompositionEnd: - return eventTypes.compositionEnd; - case topLevelTypes.topCompositionUpdate: - return eventTypes.compositionUpdate; - } -} + +'use strict'; + +var toArray = require('./toArray'); /** - * Does our fallback best-guess model think this event signifies that - * composition has begun? + * Perform a heuristic test to determine if an object is "array-like". * - * @param {string} topLevelType - * @param {object} nativeEvent + * A monk asked Joshu, a Zen master, "Has a dog Buddha nature?" + * Joshu replied: "Mu." + * + * This function determines if its argument has "array nature": it returns + * true if the argument is an actual array, an `arguments' object, or an + * HTMLCollection (e.g. node.childNodes or node.getElementsByTagName()). + * + * It will return false for other array-like objects like Filelist. + * + * @param {*} obj * @return {boolean} */ -function isFallbackCompositionStart(topLevelType, nativeEvent) { - return topLevelType === topLevelTypes.topKeyDown && nativeEvent.keyCode === START_KEYCODE; +function hasArrayNature(obj) { + return( + // not null/false + !!obj && ( + // arrays are objects, NodeLists are functions in Safari + typeof obj == 'object' || typeof obj == 'function') && + // quacks like an array + 'length' in obj && + // not window + !('setInterval' in obj) && + // no DOM node should be considered an array-like + // a 'select' element has 'length' and 'item' properties on IE8 + typeof obj.nodeType != 'number' && ( + // a real array + Array.isArray(obj) || + // arguments + 'callee' in obj || + // HTMLCollection/NodeList + 'item' in obj) + ); } /** - * Does our fallback mode think that this event is the end of composition? + * Ensure that the argument is an array by wrapping it in an array if it is not. + * Creates a copy of the argument if it is already an array. * - * @param {string} topLevelType - * @param {object} nativeEvent - * @return {boolean} + * This is mostly useful idiomatically: + * + * var createArrayFromMixed = require('createArrayFromMixed'); + * + * function takesOneOrMoreThings(things) { + * things = createArrayFromMixed(things); + * ... + * } + * + * This allows you to treat `things' as an array, but accept scalars in the API. + * + * If you need to convert an array-like object, like `arguments`, into an array + * use toArray instead. + * + * @param {*} obj + * @return {array} */ -function isFallbackCompositionEnd(topLevelType, nativeEvent) { - switch (topLevelType) { - case topLevelTypes.topKeyUp: - // Command keys insert or clear IME input. - return END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1; - case topLevelTypes.topKeyDown: - // Expect IME keyCode on each keydown. If we get any other - // code we must have exited earlier. - return nativeEvent.keyCode !== START_KEYCODE; - case topLevelTypes.topKeyPress: - case topLevelTypes.topMouseDown: - case topLevelTypes.topBlur: - // Events are not possible without cancelling IME. - return true; - default: - return false; +function createArrayFromMixed(obj) { + if (!hasArrayNature(obj)) { + return [obj]; + } else if (Array.isArray(obj)) { + return obj.slice(); + } else { + return toArray(obj); } } +module.exports = createArrayFromMixed; +},{"./toArray":25}],7:[function(require,module,exports){ +(function (process){ /** - * Google Input Tools provides composition data via a CustomEvent, - * with the `data` property populated in the `detail` object. If this - * is available on the event object, use it. If not, this is a plain - * composition event and we have nothing special to extract. + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. * - * @param {object} nativeEvent - * @return {?string} + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule createNodesFromMarkup + * @typechecks */ -function getDataFromCustomEvent(nativeEvent) { - var detail = nativeEvent.detail; - if (typeof detail === 'object' && 'data' in detail) { - return detail.data; - } - return null; -} -// Track the current IME composition fallback object, if any. -var currentComposition = null; +/*eslint-disable fb-www/unsafe-html*/ -/** - * @param {string} topLevelType Record from `EventConstants`. - * @param {DOMEventTarget} topLevelTarget The listening component root node. - * @param {string} topLevelTargetID ID of `topLevelTarget`. - * @param {object} nativeEvent Native browser event. - * @return {?object} A SyntheticCompositionEvent. - */ -function extractCompositionEvent(topLevelType, topLevelTarget, topLevelTargetID, nativeEvent, nativeEventTarget) { - var eventType; - var fallbackData; +'use strict'; - if (canUseCompositionEvent) { - eventType = getCompositionEventType(topLevelType); - } else if (!currentComposition) { - if (isFallbackCompositionStart(topLevelType, nativeEvent)) { - eventType = eventTypes.compositionStart; - } - } else if (isFallbackCompositionEnd(topLevelType, nativeEvent)) { - eventType = eventTypes.compositionEnd; - } - - if (!eventType) { - return null; - } - - if (useFallbackCompositionData) { - // The current composition is stored statically and must not be - // overwritten while composition continues. - if (!currentComposition && eventType === eventTypes.compositionStart) { - currentComposition = FallbackCompositionState.getPooled(topLevelTarget); - } else if (eventType === eventTypes.compositionEnd) { - if (currentComposition) { - fallbackData = currentComposition.getData(); - } - } - } - - var event = SyntheticCompositionEvent.getPooled(eventType, topLevelTargetID, nativeEvent, nativeEventTarget); - - if (fallbackData) { - // Inject data generated from fallback path into the synthetic event. - // This matches the property of native CompositionEventInterface. - event.data = fallbackData; - } else { - var customData = getDataFromCustomEvent(nativeEvent); - if (customData !== null) { - event.data = customData; - } - } +var ExecutionEnvironment = require('./ExecutionEnvironment'); - EventPropagators.accumulateTwoPhaseDispatches(event); - return event; -} +var createArrayFromMixed = require('./createArrayFromMixed'); +var getMarkupWrap = require('./getMarkupWrap'); +var invariant = require('./invariant'); /** - * @param {string} topLevelType Record from `EventConstants`. - * @param {object} nativeEvent Native browser event. - * @return {?string} The string corresponding to this `beforeInput` event. + * Dummy container used to render all markup. */ -function getNativeBeforeInputChars(topLevelType, nativeEvent) { - switch (topLevelType) { - case topLevelTypes.topCompositionEnd: - return getDataFromCustomEvent(nativeEvent); - case topLevelTypes.topKeyPress: - /** - * If native `textInput` events are available, our goal is to make - * use of them. However, there is a special case: the spacebar key. - * In Webkit, preventing default on a spacebar `textInput` event - * cancels character insertion, but it *also* causes the browser - * to fall back to its default spacebar behavior of scrolling the - * page. - * - * Tracking at: - * https://code.google.com/p/chromium/issues/detail?id=355103 - * - * To avoid this issue, use the keypress event as if no `textInput` - * event is available. - */ - var which = nativeEvent.which; - if (which !== SPACEBAR_CODE) { - return null; - } - - hasSpaceKeypress = true; - return SPACEBAR_CHAR; - - case topLevelTypes.topTextInput: - // Record the characters to be added to the DOM. - var chars = nativeEvent.data; - - // If it's a spacebar character, assume that we have already handled - // it at the keypress level and bail immediately. Android Chrome - // doesn't give us keycodes, so we need to blacklist it. - if (chars === SPACEBAR_CHAR && hasSpaceKeypress) { - return null; - } - - return chars; +var dummyNode = ExecutionEnvironment.canUseDOM ? document.createElement('div') : null; - default: - // For other native event types, do nothing. - return null; - } -} +/** + * Pattern used by `getNodeName`. + */ +var nodeNamePattern = /^\s*<(\w+)/; /** - * For browsers that do not provide the `textInput` event, extract the - * appropriate string to use for SyntheticInputEvent. + * Extracts the `nodeName` of the first element in a string of markup. * - * @param {string} topLevelType Record from `EventConstants`. - * @param {object} nativeEvent Native browser event. - * @return {?string} The fallback string for this `beforeInput` event. + * @param {string} markup String of markup. + * @return {?string} Node name of the supplied markup. */ -function getFallbackBeforeInputChars(topLevelType, nativeEvent) { - // If we are currently composing (IME) and using a fallback to do so, - // try to extract the composed characters from the fallback object. - if (currentComposition) { - if (topLevelType === topLevelTypes.topCompositionEnd || isFallbackCompositionEnd(topLevelType, nativeEvent)) { - var chars = currentComposition.getData(); - FallbackCompositionState.release(currentComposition); - currentComposition = null; - return chars; - } - return null; - } - - switch (topLevelType) { - case topLevelTypes.topPaste: - // If a paste event occurs after a keypress, throw out the input - // chars. Paste events should not lead to BeforeInput events. - return null; - case topLevelTypes.topKeyPress: - /** - * As of v27, Firefox may fire keypress events even when no character - * will be inserted. A few possibilities: - * - * - `which` is `0`. Arrow keys, Esc key, etc. - * - * - `which` is the pressed key code, but no char is available. - * Ex: 'AltGr + d` in Polish. There is no modified character for - * this key combination and no character is inserted into the - * document, but FF fires the keypress for char code `100` anyway. - * No `input` event will occur. - * - * - `which` is the pressed key code, but a command combination is - * being used. Ex: `Cmd+C`. No character is inserted, and no - * `input` event will occur. - */ - if (nativeEvent.which && !isKeypressCommand(nativeEvent)) { - return String.fromCharCode(nativeEvent.which); - } - return null; - case topLevelTypes.topCompositionEnd: - return useFallbackCompositionData ? null : nativeEvent.data; - default: - return null; - } +function getNodeName(markup) { + var nodeNameMatch = markup.match(nodeNamePattern); + return nodeNameMatch && nodeNameMatch[1].toLowerCase(); } /** - * Extract a SyntheticInputEvent for `beforeInput`, based on either native - * `textInput` or fallback behavior. + * Creates an array containing the nodes rendered from the supplied markup. The + * optionally supplied `handleScript` function will be invoked once for each + *