/** * Copyright 2013-present, 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 ReactDOMComponent */ /* global hasOwnProperty:true */ 'use strict'; var AutoFocusUtils = require('AutoFocusUtils'); var CSSPropertyOperations = require('CSSPropertyOperations'); var DOMLazyTree = require('DOMLazyTree'); var DOMNamespaces = require('DOMNamespaces'); var DOMProperty = require('DOMProperty'); var DOMPropertyOperations = require('DOMPropertyOperations'); var EventPluginRegistry = require('EventPluginRegistry'); var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter'); var ReactDOMComponentFlags = require('ReactDOMComponentFlags'); var ReactDOMComponentTree = require('ReactDOMComponentTree'); var ReactDOMInput = require('ReactDOMInput'); var ReactDOMOption = require('ReactDOMOption'); var ReactDOMSelect = require('ReactDOMSelect'); var ReactDOMTextarea = require('ReactDOMTextarea'); var ReactInstrumentation = require('ReactInstrumentation'); var ReactMultiChild = require('ReactMultiChild'); var ReactServerRenderingTransaction = require('ReactServerRenderingTransaction'); var emptyFunction = require('emptyFunction'); var escapeTextContentForBrowser = require('escapeTextContentForBrowser'); var invariant = require('invariant'); var isEventSupported = require('isEventSupported'); var inputValueTracking = require('inputValueTracking'); var validateDOMNesting = require('validateDOMNesting'); var warning = require('warning'); var didWarnShadyDOM = false; var Flags = ReactDOMComponentFlags; var getNode = ReactDOMComponentTree.getNodeFromInstance; var listenTo = ReactBrowserEventEmitter.listenTo; var registrationNameModules = EventPluginRegistry.registrationNameModules; // For quickly matching children type, to test if can be treated as content. var CONTENT_TYPES = {'string': true, 'number': true}; var STYLE = 'style'; var HTML = '__html'; var RESERVED_PROPS = { children: null, dangerouslySetInnerHTML: null, suppressContentEditableWarning: null, }; // Node type for document fragments (Node.DOCUMENT_FRAGMENT_NODE). var DOC_FRAGMENT_TYPE = 11; function getDeclarationErrorAddendum(internalInstance) { if (internalInstance) { var owner = internalInstance._currentElement._owner || null; if (owner) { var name = owner.getName(); if (name) { return ' This DOM node was rendered by `' + name + '`.'; } } } return ''; } /** * @param {object} component * @param {?object} props */ function assertValidProps(component, props) { if (!props) { return; } // Note the use of `==` which checks for null or undefined. if (voidElementTags[component._tag]) { invariant( props.children == null && props.dangerouslySetInnerHTML == null, '%s is a void element tag and must neither have `children` nor ' + 'use `dangerouslySetInnerHTML`.%s', component._tag, component._currentElement._owner ? ' Check the render method of ' + component._currentElement._owner.getName() + '.' : '' ); } if (props.dangerouslySetInnerHTML != null) { invariant( props.children == null, 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.' ); invariant( typeof props.dangerouslySetInnerHTML === 'object' && HTML in props.dangerouslySetInnerHTML, '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' + 'Please visit https://fb.me/react-invariant-dangerously-set-inner-html ' + 'for more information.' ); } if (__DEV__) { warning( props.innerHTML == null, 'Directly setting property `innerHTML` is not permitted. ' + 'For more information, lookup documentation on `dangerouslySetInnerHTML`.' ); warning( props.suppressContentEditableWarning || !props.contentEditable || props.children == null, '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.' ); warning( props.onFocusIn == null && props.onFocusOut == null, '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.' ); } invariant( props.style == null || typeof props.style === 'object', 'The `style` prop expects a mapping from style properties to values, ' + 'not a string. For example, style={{marginRight: spacing + \'em\'}} when ' + 'using JSX.%s', getDeclarationErrorAddendum(component) ); } function ensureListeningTo(inst, registrationName, transaction) { if (transaction instanceof ReactServerRenderingTransaction) { return; } if (__DEV__) { // IE8 has no API for event capturing and the `onScroll` event doesn't // bubble. warning( registrationName !== 'onScroll' || isEventSupported('scroll', true), 'This browser doesn\'t support the `onScroll` event' ); } var containerInfo = inst._hostContainerInfo; var isDocumentFragment = containerInfo._node && containerInfo._node.nodeType === DOC_FRAGMENT_TYPE; var doc = isDocumentFragment ? containerInfo._node : containerInfo._ownerDocument; listenTo(registrationName, doc); } function inputPostMount() { var inst = this; ReactDOMInput.postMountWrapper(inst); } function textareaPostMount() { var inst = this; ReactDOMTextarea.postMountWrapper(inst); } function optionPostMount() { var inst = this; ReactDOMOption.postMountWrapper(inst); } var setAndValidateContentChildDev = emptyFunction; if (__DEV__) { setAndValidateContentChildDev = function(content) { var hasExistingContent = this._contentDebugID != null; var debugID = this._debugID; // This ID represents the inlined child that has no backing instance: var contentDebugID = -debugID; if (content == null) { if (hasExistingContent) { ReactInstrumentation.debugTool.onUnmountComponent(this._contentDebugID); } this._contentDebugID = null; return; } validateDOMNesting(null, String(content), this, this._ancestorInfo); this._contentDebugID = contentDebugID; if (hasExistingContent) { ReactInstrumentation.debugTool.onBeforeUpdateComponent(contentDebugID, content); ReactInstrumentation.debugTool.onUpdateComponent(contentDebugID); } else { ReactInstrumentation.debugTool.onBeforeMountComponent(contentDebugID, content, debugID); ReactInstrumentation.debugTool.onMountComponent(contentDebugID); ReactInstrumentation.debugTool.onSetChildren(debugID, [contentDebugID]); } }; } // There are so many media events, it makes sense to just // maintain a list rather than create a `trapBubbledEvent` for each var mediaEvents = { 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', }; function trackInputValue() { inputValueTracking.track(this); } function trapClickOnNonInteractiveElement() { // Mobile Safari does not fire properly bubble click events on // non-interactive elements, which means delegated click listeners do not // fire. The workaround for this bug involves attaching an empty click // listener on the target node. // http://www.quirksmode.org/blog/archives/2010/09/click_event_del.html // Just set it using the onclick property so that we don't have to manage any // bookkeeping for it. Not sure if we need to clear it when the listener is // removed. // TODO: Only do this for the relevant Safaris maybe? var node = getNode(this); node.onclick = emptyFunction; } function trapBubbledEventsLocal() { var inst = this; // If a component renders to null or if another component fatals and causes // the state of the tree to be corrupted, `node` here can be null. invariant(inst._rootNodeID, 'Must be mounted to trap events'); var node = getNode(inst); invariant( node, 'trapBubbledEvent(...): Requires node to be rendered.' ); switch (inst._tag) { case 'iframe': case 'object': inst._wrapperState.listeners = [ ReactBrowserEventEmitter.trapBubbledEvent( 'topLoad', 'load', node ), ]; break; case 'video': case 'audio': inst._wrapperState.listeners = []; // Create listener for each media event for (var event in mediaEvents) { if (mediaEvents.hasOwnProperty(event)) { inst._wrapperState.listeners.push( ReactBrowserEventEmitter.trapBubbledEvent( event, mediaEvents[event], node ) ); } } break; case 'source': inst._wrapperState.listeners = [ ReactBrowserEventEmitter.trapBubbledEvent( 'topError', 'error', node ), ]; break; case 'img': inst._wrapperState.listeners = [ ReactBrowserEventEmitter.trapBubbledEvent( 'topError', 'error', node ), ReactBrowserEventEmitter.trapBubbledEvent( 'topLoad', 'load', node ), ]; break; case 'form': inst._wrapperState.listeners = [ ReactBrowserEventEmitter.trapBubbledEvent( 'topReset', 'reset', node ), ReactBrowserEventEmitter.trapBubbledEvent( 'topSubmit', 'submit', node ), ]; break; case 'input': case 'select': case 'textarea': inst._wrapperState.listeners = [ ReactBrowserEventEmitter.trapBubbledEvent( 'topInvalid', 'invalid', node ), ]; break; } } function postUpdateSelectWrapper() { ReactDOMSelect.postUpdateWrapper(this); } // For HTML, certain tags should omit their close tag. We keep a whitelist for // those special-case tags. var omittedCloseTags = { 'area': true, 'base': true, 'br': true, 'col': true, 'embed': true, 'hr': true, 'img': true, 'input': true, 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true, 'track': true, 'wbr': true, // NOTE: menuitem's close tag should be omitted, but that causes problems. }; var newlineEatingTags = { 'listing': true, 'pre': true, 'textarea': true, }; // For HTML, certain tags cannot have children. This has the same purpose as // `omittedCloseTags` except that `menuitem` should still have its closing tag. var voidElementTags = Object.assign({ 'menuitem': true, }, omittedCloseTags); // We accept any tag to be rendered but since this gets injected into arbitrary // HTML, we want to make sure that it's a safe tag. // http://www.w3.org/TR/REC-xml/#NT-Name var VALID_TAG_REGEX = /^[a-zA-Z][a-zA-Z:_\.\-\d]*$/; // Simplified subset var validatedTagCache = {}; var hasOwnProperty = {}.hasOwnProperty; function validateDangerousTag(tag) { if (!hasOwnProperty.call(validatedTagCache, tag)) { invariant(VALID_TAG_REGEX.test(tag), 'Invalid tag: %s', tag); validatedTagCache[tag] = true; } } function isCustomComponent(tagName, props) { return tagName.indexOf('-') >= 0 || props.is != null; } var globalIdCounter = 1; /** * Creates a new React class that is idempotent and capable of containing other * React components. It accepts event listeners and DOM properties that are * valid according to `DOMProperty`. * * - Event listeners: `onClick`, `onMouseDown`, etc. * - DOM properties: `className`, `name`, `title`, etc. * * The `style` property functions differently from the DOM API. It accepts an * object mapping of style properties to values. * * @constructor ReactDOMComponent * @extends ReactMultiChild */ function ReactDOMComponent(element) { var tag = element.type; this._currentElement = element; this._tag = tag.toLowerCase(); this._namespaceURI = null; this._renderedChildren = 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; if (__DEV__) { this._ancestorInfo = null; setAndValidateContentChildDev.call(this, null); } } ReactDOMComponent.displayName = 'ReactDOMComponent'; ReactDOMComponent.Mixin = { /** * Generates root tag markup then recurses. This method has side effects and * is not idempotent. * * @internal * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction * @param {?ReactDOMComponent} the parent component instance * @param {?object} info about the host container * @param {object} context * @return {string} The computed markup. */ mountComponent: function( transaction, hostParent, hostContainerInfo, context ) { this._rootNodeID = globalIdCounter++; this._domID = hostContainerInfo._idCounter++; this._hostParent = hostParent; this._hostContainerInfo = hostContainerInfo; var props = 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, }; transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this); break; case 'input': ReactDOMInput.mountWrapper(this, props, hostParent); props = ReactDOMInput.getHostProps(this, props); transaction.getReactMountReady().enqueue(trackInputValue, this); transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this); // For controlled components we always need to ensure we're listening // to onChange. Even if there is no listener. ensureListeningTo(this, 'onChange', transaction); break; case 'option': ReactDOMOption.mountWrapper(this, props, hostParent); props = ReactDOMOption.getHostProps(this, props); break; case 'select': ReactDOMSelect.mountWrapper(this, props, hostParent); props = ReactDOMSelect.getHostProps(this, props); transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this); // For controlled components we always need to ensure we're listening // to onChange. Even if there is no listener. ensureListeningTo(this, 'onChange', transaction); break; case 'textarea': ReactDOMTextarea.mountWrapper(this, props, hostParent); props = ReactDOMTextarea.getHostProps(this, props); transaction.getReactMountReady().enqueue(trackInputValue, this); transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this); // For controlled components we always need to ensure we're listening // to onChange. Even if there is no listener. ensureListeningTo(this, 'onChange', transaction); break; } assertValidProps(this, props); // We create tags in the namespace of their parent container, except HTML // tags get no namespace. var namespaceURI; var parentTag; if (hostParent != null) { namespaceURI = hostParent._namespaceURI; parentTag = hostParent._tag; } else if (hostContainerInfo._tag) { namespaceURI = hostContainerInfo._namespaceURI; parentTag = hostContainerInfo._tag; } if (namespaceURI == null || namespaceURI === DOMNamespaces.svg && parentTag === 'foreignobject') { namespaceURI = DOMNamespaces.html; } if (namespaceURI === DOMNamespaces.html) { if (__DEV__) { warning( isCustomComponent(this._tag, props) || this._tag === this._currentElement.type, '<%s /> is using uppercase HTML. Always use lowercase HTML tags ' + 'in React.', this._currentElement.type ); } if (this._tag === 'svg') { namespaceURI = DOMNamespaces.svg; } else if (this._tag === 'math') { namespaceURI = DOMNamespaces.mathml; } } this._namespaceURI = namespaceURI; if (__DEV__) { var parentInfo; if (hostParent != null) { parentInfo = hostParent._ancestorInfo; } else if (hostContainerInfo._tag) { parentInfo = hostContainerInfo._ancestorInfo; } if (parentInfo) { // parentInfo should always be present except for the top-level // component when server rendering validateDOMNesting(this._tag, null, this, parentInfo); } this._ancestorInfo = validateDOMNesting.updatedAncestorInfo(parentInfo, this._tag, this); } var mountImage; var type = this._currentElement.type; if (transaction.useCreateElement) { var ownerDocument = hostContainerInfo._ownerDocument; var el; if (namespaceURI === DOMNamespaces.html) { if (this._tag === 'script') { // Create the script via .innerHTML so its "parser-inserted" flag is // set to true and it does not execute var div = ownerDocument.createElement('div'); div.innerHTML = `<${type}>`; el = div.removeChild(div.firstChild); } else if (props.is) { el = ownerDocument.createElement(type, props.is); } else { // Separate else branch instead of using `props.is || undefined` above becuase of a Firefox bug. // See discussion in https://github.com/facebook/react/pull/6896 // and discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=1276240 el = ownerDocument.createElement(type); } } else { el = ownerDocument.createElementNS( namespaceURI, type ); } var isCustomComponentTag = isCustomComponent(this._tag, props); if (__DEV__ && isCustomComponentTag && !didWarnShadyDOM && el.shadyRoot) { var owner = this._currentElement._owner; var name = owner && owner.getName() || 'A component'; warning( false, '%s is using shady DOM. Using shady DOM with React can ' + 'cause things to break subtly.', name ); didWarnShadyDOM = true; } ReactDOMComponentTree.precacheNode(this, el); this._flags |= Flags.hasCachedChildNodes; if (!this._hostParent) { DOMPropertyOperations.setAttributeForRoot(el); } this._updateDOMProperties(null, props, transaction, isCustomComponentTag); var lazyTree = DOMLazyTree(el); this._createInitialChildren(transaction, props, context, lazyTree); mountImage = lazyTree; } else { validateDangerousTag(this._tag); var tagOpen = this._createOpenTagMarkupAndPutListeners(transaction, props); var tagContent = this._createContentMarkup(transaction, props, context); if (!tagContent && omittedCloseTags[this._tag]) { mountImage = tagOpen + '/>'; } else { mountImage = tagOpen + '>' + tagContent + ''; } } switch (this._tag) { case 'input': transaction.getReactMountReady().enqueue( inputPostMount, this ); if (props.autoFocus) { transaction.getReactMountReady().enqueue( AutoFocusUtils.focusDOMComponent, this ); } break; case 'textarea': transaction.getReactMountReady().enqueue( textareaPostMount, this ); if (props.autoFocus) { transaction.getReactMountReady().enqueue( AutoFocusUtils.focusDOMComponent, this ); } break; case 'select': if (props.autoFocus) { transaction.getReactMountReady().enqueue( AutoFocusUtils.focusDOMComponent, this ); } break; case 'button': if (props.autoFocus) { transaction.getReactMountReady().enqueue( AutoFocusUtils.focusDOMComponent, this ); } break; case 'option': transaction.getReactMountReady().enqueue( optionPostMount, this ); break; default: if (typeof props.onClick === 'function') { transaction.getReactMountReady().enqueue( trapClickOnNonInteractiveElement, this ); } break; } return mountImage; }, /** * Creates markup for the open tag and all attributes. * * This method has side effects because events get registered. * * Iterating over object properties is faster than iterating over arrays. * @see http://jsperf.com/obj-vs-arr-iteration * * @private * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction * @param {object} props * @return {string} Markup of opening tag. */ _createOpenTagMarkupAndPutListeners: function(transaction, props) { var ret = '<' + this._currentElement.type; for (var propKey in props) { if (!props.hasOwnProperty(propKey)) { continue; } var propValue = props[propKey]; if (propValue == null) { continue; } if (registrationNameModules.hasOwnProperty(propKey)) { if (propValue) { ensureListeningTo(this, propKey, transaction); } } else { if (propKey === STYLE) { if (propValue) { if (__DEV__) { Object.freeze(propValue); } } propValue = CSSPropertyOperations.createMarkupForStyles(propValue, this); } var markup = null; if (this._tag != null && isCustomComponent(this._tag, props)) { if (!RESERVED_PROPS.hasOwnProperty(propKey)) { markup = DOMPropertyOperations.createMarkupForCustomAttribute(propKey, propValue); } } else { markup = DOMPropertyOperations.createMarkupForProperty(propKey, propValue); } if (markup) { ret += ' ' + markup; } } } // For static pages, no need to put React ID and checksum. Saves lots of // bytes. if (transaction.renderToStaticMarkup) { return ret; } if (!this._hostParent) { ret += ' ' + DOMPropertyOperations.createMarkupForRoot(); } ret += ' ' + DOMPropertyOperations.createMarkupForID(this._domID); return ret; }, /** * Creates markup for the content between the tags. * * @private * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction * @param {object} props * @param {object} context * @return {string} Content markup. */ _createContentMarkup: function(transaction, props, context) { var ret = ''; // Intentional use of != to avoid catching zero/false. var innerHTML = props.dangerouslySetInnerHTML; if (innerHTML != null) { if (innerHTML.__html != null) { ret = innerHTML.__html; } } else { var contentToUse = CONTENT_TYPES[typeof props.children] ? props.children : null; var childrenToUse = contentToUse != null ? null : props.children; if (contentToUse != null) { // TODO: Validate that text is allowed as a child of this node ret = escapeTextContentForBrowser(contentToUse); if (__DEV__) { setAndValidateContentChildDev.call(this, contentToUse); } } else if (childrenToUse != null) { var mountImages = this.mountChildren( childrenToUse, transaction, context ); ret = mountImages.join(''); } } if (newlineEatingTags[this._tag] && ret.charAt(0) === '\n') { // text/html ignores the first character in these tags if it's a newline // Prefer to break application/xml over text/html (for now) by adding // a newline specifically to get eaten by the parser. (Alternately for // textareas, replacing "^\n" with "\r\n" doesn't get eaten, and the first // \r is normalized out by HTMLTextAreaElement#value.) // See: // See: // See: // See: Parsing of "textarea" "listing" and "pre" elements // from return '\n' + ret; } else { return ret; } }, _createInitialChildren: function(transaction, props, context, lazyTree) { // Intentional use of != to avoid catching zero/false. var innerHTML = props.dangerouslySetInnerHTML; if (innerHTML != null) { const innerHTMLContent = innerHTML.__html; if (innerHTMLContent != null && innerHTMLContent !== '') { DOMLazyTree.queueHTML(lazyTree, innerHTMLContent); } } else { var contentToUse = CONTENT_TYPES[typeof props.children] ? props.children : null; var childrenToUse = contentToUse != null ? null : props.children; // TODO: Validate that text is allowed as a child of this node if (contentToUse != null) { // Avoid setting textContent when the text is empty. In IE11 setting // textContent on a text area will cause the placeholder to not // show within the textarea until it has been focused and blurred again. // https://github.com/facebook/react/issues/6731#issuecomment-254874553 if (contentToUse !== '') { if (__DEV__) { setAndValidateContentChildDev.call(this, contentToUse); } DOMLazyTree.queueText(lazyTree, contentToUse); } } else if (childrenToUse != null) { var mountImages = this.mountChildren( childrenToUse, transaction, context ); for (var i = 0; i < mountImages.length; i++) { DOMLazyTree.queueChild(lazyTree, mountImages[i]); } } } }, /** * Receives a next element and updates the component. * * @internal * @param {ReactElement} nextElement * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction * @param {object} context */ receiveComponent: function(nextElement, transaction, context) { var prevElement = this._currentElement; this._currentElement = nextElement; this.updateComponent(transaction, prevElement, nextElement, context); }, /** * Updates a DOM component after it has already been allocated and * attached to the DOM. Reconciles the root DOM node, then recurses. * * @param {ReactReconcileTransaction} transaction * @param {ReactElement} prevElement * @param {ReactElement} nextElement * @internal * @overridable */ updateComponent: function(transaction, prevElement, nextElement, context) { var lastProps = prevElement.props; var nextProps = this._currentElement.props; switch (this._tag) { case 'input': lastProps = ReactDOMInput.getHostProps(this, lastProps); nextProps = ReactDOMInput.getHostProps(this, nextProps); break; case 'option': lastProps = ReactDOMOption.getHostProps(this, lastProps); nextProps = ReactDOMOption.getHostProps(this, nextProps); break; case 'select': lastProps = ReactDOMSelect.getHostProps(this, lastProps); nextProps = ReactDOMSelect.getHostProps(this, nextProps); break; case 'textarea': lastProps = ReactDOMTextarea.getHostProps(this, lastProps); nextProps = ReactDOMTextarea.getHostProps(this, nextProps); break; default: if (typeof lastProps.onClick !== 'function' && typeof nextProps.onClick === 'function') { transaction.getReactMountReady().enqueue( trapClickOnNonInteractiveElement, this ); } break; } assertValidProps(this, nextProps); var isCustomComponentTag = isCustomComponent(this._tag, nextProps); this._updateDOMProperties(lastProps, nextProps, transaction, isCustomComponentTag); this._updateDOMChildren( lastProps, nextProps, transaction, context ); switch (this._tag) { case 'input': // Update the wrapper around inputs *after* updating props. This has to // happen after `_updateDOMProperties`. Otherwise HTML5 input validations // raise warnings and prevent the new value from being assigned. ReactDOMInput.updateWrapper(this); break; case 'textarea': ReactDOMTextarea.updateWrapper(this); break; case 'select': //