Repository: ramitos/react-tvml
Branch: master
Commit: 6c003d2715c4
Files: 16
Total size: 98.3 KB
Directory structure:
gitextract_asnex2vt/
├── .gitignore
├── .tern-project
├── package.json
├── readme.md
└── src/
├── Component.js
├── EventEmitter.js
├── EventListener.js
├── EventPlugin.js
├── IDOperations.js
├── Mount.js
├── ReconcileTransaction.js
├── TextComponent.js
├── globals.js
├── instantiateReactComponent.js
├── menuBar.js
└── react-tvml.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Created by https://www.gitignore.io
### Node ###
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
### Windows ###
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
### Linux ###
*~
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
### OSX ###
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### SublimeText ###
# cache files for sublime text
*.tmlanguage.cache
*.tmPreferences.cache
*.stTheme.cache
# workspace files are user-specific
*.sublime-workspace
# project files should be checked into the repository, unless a significant
# proportion of contributors will probably not be using SublimeText
# *.sublime-project
# sftp configuration file
sftp-config.json
### TextMate ###
*.tmproj
*.tmproject
tmtags
### Vim ###
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
*.un~
Session.vim
.netrwhist
*~
================================================
FILE: .tern-project
================================================
{
"libs": ["ecma6"],
"plugins": {
"doc_comment": true,
"node": {}
}
}
================================================
FILE: package.json
================================================
{
"name": "react-tvml",
"version": "1.0.4",
"license": "BSD",
"description": "react bindings to Apple's TVJS & TVML",
"author": "Sérgio Ramos <mail@sergioramos.me>",
"main": "src/react-tvml.js",
"repository": {
"type": "git",
"url": "https://github.com/ramitos/react-tvml"
},
"bugs": {
"url": "https://github.com/ramitos/react-tvml/issues"
},
"keywords": [
"react",
"tvjs",
"tvml",
"tvos",
"apple"
],
"dependencies": {
"fbjs": "0.7.0",
"has-own-prop": "1.0.0",
"lodash.clone": "^3.0.3",
"react": "^0.14.6"
},
"devDependencies": {
"imports-loader": "0.6.5"
}
}
================================================
FILE: readme.md
================================================
# react-tvml
**this is a very alpha release**
React bindings to Apple's [TVJS and TVML](https://developer.apple.com/library/prerelease/tvos/navigation/)
[](https://cldup.com/u6sOUJLLE9.mp4)
(it's not this slow, click on the gif to see a video)
## install
```bash
$ npm install --save react-tvml
```
## example
[sprice/tvOS-hello-world-example](https://github.com/sprice/tvOS-hello-world-example)
## usage
```js
var React = require('react');
var TVML = require('react-tvml');
var App = React.createClass({
render: function() {
return (<loadingTemplate>
<activityIndicator>
<text>Loading...</text>
</activityIndicator>
</loadingTemplate>);
}
});
TVML.render(<App />);
```
## todo (PRs are welcome)
* Most of the code is copied from the react dom renderer. A lot of it needs to be removed and cleaned according to TVML use case
* push vs replace document
* some events
* A **lot** of polish
* Validations: e.g. some components can only be children of some specific components
* consistent code style and linting
* tests
## license
BSD
================================================
FILE: src/Component.js
================================================
'use strict';
// TODO: validate children elements (or parents?). Ex: <badge> can only be a child of
// - buttonLockup
// - header
// - lockup
// - overlay
// - row
// - text
// - title
// TODO: validate CSS props. Only some are valid
var __DEV__ = process.env.NODE_ENV !== 'production';
var CSSPropertyOperations = require('react/lib/CSSPropertyOperations');
var DOMProperty = require('react/lib/DOMProperty');
var DOMPropertyOperations = require('react/lib/DOMPropertyOperations');
// var EventConstants = require('react/lib/EventConstants');
var EventEmitter = require('./EventEmitter');
var IDOperations = require('./IDOperations');
var MultiChild = require('react/lib/ReactMultiChild');
var Perf = require('react/lib/ReactPerf');
var UpdateQueue = require('react/lib/ReactUpdateQueue');
var assign = require('react/lib/Object.assign');
var escapeTextContentForBrowser = require('react/lib/escapeTextContentForBrowser');
var invariant = require('fbjs/lib/invariant');
var isEventSupported = require('react/lib/isEventSupported');
var keyOf = require('fbjs/lib/keyOf');
var setInnerHTML = require('react/lib/setInnerHTML');
var setTextContent = require('react/lib/setTextContent');
var shallowEqual = require('fbjs/lib/shallowEqual');
var validateDOMNesting = require('react/lib/validateDOMNesting');
var warning = require('fbjs/lib/warning');
var hasOwnProperty = require('has-own-prop');
var Mount = require('./Mount');
var ELEMENT_NODE_TYPE = 1;
// For quickly matching children type, to test if can be treated as content.
var CONTENT_TYPES = {
'string': true,
'number': true
};
var STYLE = keyOf({
'style': null
});
var COMPONENT_CLASSES = {
menubar: require('./menuBar')
};
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 '';
}
function legacyGetDOMNode() {
if (__DEV__) {
var component = this._reactInternalComponent;
warning(
false,
'ReactTVMLComponent: Do not access .getDOMNode() of a DOM node; ' +
'instead, use the node directly.%s',
getDeclarationErrorAddendum(component)
);
}
return this;
}
function legacyIsMounted() {
var component = this._reactInternalComponent;
if (__DEV__) {
warning(
false,
'ReactTVMLComponent: Do not access .isMounted() of a DOM node.%s',
getDeclarationErrorAddendum(component)
);
}
return !!component;
}
function legacySetStateEtc() {
if (__DEV__) {
var component = this._reactInternalComponent;
warning(
false,
'ReactTVMLComponent: Do not access .setState(), .replaceState(), or ' +
'.forceUpdate() of a DOM node. This is a no-op.%s',
getDeclarationErrorAddendum(component)
);
}
}
function legacySetProps(partialProps, callback) {
var component = this._reactInternalComponent;
if (__DEV__) {
warning(
false,
'ReactTVMLComponent: Do not access .setProps() of a DOM node. ' +
'Instead, call ReactDOM.render again at the top level.%s',
getDeclarationErrorAddendum(component)
);
}
if (!component) {
return;
}
UpdateQueue.enqueueSetPropsInternal(component, partialProps);
if (callback) {
UpdateQueue.enqueueCallbackInternal(component, callback);
}
}
function legacyReplaceProps(partialProps, callback) {
var component = this._reactInternalComponent;
if (__DEV__) {
warning(
false,
'ReactTVMLComponent: Do not access .replaceProps() of a DOM node. ' +
'Instead, call ReactDOM.render again at the top level.%s',
getDeclarationErrorAddendum(component)
);
}
if (!component) {
return;
}
UpdateQueue.enqueueReplacePropsInternal(component, partialProps);
if (callback) {
UpdateQueue.enqueueCallbackInternal(component, callback);
}
}
var styleMutationWarning = {};
function checkAndWarnForMutatedStyle(style1, style2, component) {
if (style1 == null || style2 == null) {
return;
}
if (shallowEqual(style1, style2)) {
return;
}
var componentName = component._tag;
var owner = component._currentElement._owner;
var ownerName;
if (owner) {
ownerName = owner.getName();
}
var hash = ownerName + '|' + componentName;
if (hasOwnProperty(styleMutationWarning, hash)) {
return;
}
styleMutationWarning[hash] = true;
warning(
false,
'`%s` was passed a style object that has previously been mutated. ' +
'Mutating `style` is deprecated. Consider cloning it beforehand. Check ' +
'the `render` %s. Previous style: %s. Mutated style: %s.',
componentName,
owner ? 'of `' + ownerName + '`' : 'using <' + componentName + '>',
JSON.stringify(style1),
JSON.stringify(style2)
);
}
/**
* @param {object} component
* @param {?object} props
*/
function assertValidProps(component, props) {
if (!props) {
return;
}
// Note the use of `==` which checks for null or undefined.
if (__DEV__) {
if (voidElementTags[component._tag]) {
warning(
props.children == null && props.dangerouslySetInnerHTML == null,
'%s is a void element tag and must not have `children` or ' +
'use `props.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.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.'
);
}
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 enqueuePutListener(id, registrationName, listener, transaction) {
var container = Mount.findReactContainerForID(id);
if (container) {
var doc = container.nodeType === ELEMENT_NODE_TYPE ? container.ownerDocument : container;
EventEmitter.listenTo(registrationName, doc);
}
transaction.getReactMountReady().enqueue(putListener, {
id: id,
registrationName: registrationName,
listener: listener
});
}
function putListener(id, registrationName, listener, transaction) {
// TODO check if tvml registers events the same way as listenTo
// TODO I'm not sure we can register events in `document` and delegate
EventEmitter.EventEmitter.listenTo(registrationName, Mount.findReactContainerForID(id));
transaction.getReactMountReady().enqueue(putListener, {
id: id,
registrationName: registrationName,
listener: listener,
});
// if (container) {
// var doc = container.nodeType === ELEMENT_NODE_TYPE ?
// container.ownerDocument :
// container;
// EventEmitter.listenTo(registrationName, doc);
// }
}
function putListener() {
var listenerToPut = this;
EventEmitter.putListener(
listenerToPut.id,
listenerToPut.registrationName,
listenerToPut.listener
);
}
// 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 postUpdateSelectWrapper() {
DOMSelect.postUpdateWrapper(this);
}
// For HTML, certain tags should omit their close tag. We keep a whitelist for
// those special cased tags.
var omittedCloseTags = {
'badge': true,
'decorationImage': true,
'fullscreenImg': true,
'heroImg': true,
'img': true,
'ratingBadge': true,
'asset': true,
'monogram': true
};
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 = assign({
}, 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 = {};
function validateDangerousTag(tag) {
if (!hasOwnProperty(validatedTagCache, tag)) {
invariant(VALID_TAG_REGEX.test(tag), 'Invalid tag: %s', tag);
validatedTagCache[tag] = true;
}
}
function processChildContext(context, inst) {
if (__DEV__) {
// Pass down our tag name to child components for validation purposes
context = assign({}, context);
var info = context[validateDOMNesting.ancestorInfoContextKey];
context[validateDOMNesting.ancestorInfoContextKey] =
validateDOMNesting.updatedAncestorInfo(info, inst._tag, inst);
}
return context;
}
function isCustomComponent(tagName, props) {
return tagName.indexOf('-') >= 0 || props.is != null;
}
/**
* 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 ReactTVMLComponent
* @extends ReactMultiChild
*/
function ReactTVMLComponent(tag) {
validateDangerousTag(tag);
this._tag = tag.toLowerCase();
this._renderedChildren = null;
this._previousStyle = null;
this._previousStyleCopy = null;
this._rootNodeID = null;
this._wrapperState = null;
this._topLevelWrapper = null;
this._nodeWithLegacyProperties = null;
}
ReactTVMLComponent.displayName = 'ReactTVMLComponent';
ReactTVMLComponent.Mixin = {
construct: function(element) {
this._currentElement = element;
},
/**
* Generates root tag markup then recurses. This method has side effects and
* is not idempotent.
*
* @internal
* @param {string} rootID The root DOM ID for this node.
* @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
* @param {object} context
* @return {string} The computed markup.
*/
mountComponent: function(rootID, transaction, context) {
this._rootNodeID = rootID;
var props = this._currentElement.props;
if (hasOwnProperty(COMPONENT_CLASSES, this._tag)) {
if (hasOwnProperty(COMPONENT_CLASSES[this._tag], 'getNativeProps')) {
props = COMPONENT_CLASSES[this._tag].getNativeProps(this, props, context);
}
}
assertValidProps(this, props);
if (__DEV__) {
if (context[validateDOMNesting.ancestorInfoContextKey]) {
validateDOMNesting(
this._tag,
this,
context[validateDOMNesting.ancestorInfoContextKey]
);
}
}
var mountImage;
// isn't useCreateElement always false?
if (transaction.useCreateElement) {
var ownerDocument = context[Mount.ownerDocumentContextKey];
var el = ownerDocument.createElement(this._currentElement.type);
DOMPropertyOperations.setAttributeForID(el, this._rootNodeID);
// Populate node cache
Mount.getID(el);
this._updateDOMProperties({}, props, transaction, el);
this._createInitialChildren(transaction, props, context, el);
mountImage = el;
} else {
var tagOpen = this._createOpenTagMarkupAndPutListeners(transaction, props);
var tagContent = this._createContentMarkup(transaction, props, context);
if (!tagContent && omittedCloseTags[this._tag]) {
mountImage = tagOpen + '/>';
} else {
mountImage =
tagOpen + '>' + tagContent + '</' + this._currentElement.type + '>';
}
}
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 (!hasOwnProperty(props, propKey)) {
continue;
}
var propValue = props[propKey];
if (propValue == null) {
continue;
}
if (hasOwnProperty(EventEmitter.registrationNameModules, propKey)) {
enqueuePutListener(this._rootNodeID, propKey, propValue, transaction);
} else {
if (propKey === STYLE) {
if (propValue) {
if (__DEV__) {
// See `_updateDOMProperties`. style block
this._previousStyle = propValue;
}
propValue = this._previousStyleCopy = assign({}, props.style);
}
propValue = CSSPropertyOperations.createMarkupForStyles(propValue);
}
var markup = null;
if (this._tag != null && isCustomComponent(this._tag, props)) {
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;
}
var markupForID = DOMPropertyOperations.createMarkupForID(this._rootNodeID);
return ret + ' ' + markupForID;
},
/**
* 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);
} else if (childrenToUse != null) {
var mountImages = this.mountChildren(
childrenToUse,
transaction,
processChildContext(context, this)
);
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: <http://www.w3.org/TR/html-polyglot/#newlines-in-textarea-and-pre>
// See: <http://www.w3.org/TR/html5/syntax.html#element-restrictions>
// See: <http://www.w3.org/TR/html5/syntax.html#newlines>
// See: Parsing of "textarea" "listing" and "pre" elements
// from <http://www.w3.org/TR/html5/syntax.html#parsing-main-inbody>
return '\n' + ret;
} else {
return ret;
}
},
_createInitialChildren: function(transaction, props, context, el) {
// Intentional use of != to avoid catching zero/false.
var innerHTML = props.dangerouslySetInnerHTML;
if (innerHTML != null) {
if (innerHTML.__html != null) {
setInnerHTML(el, 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
setTextContent(el, contentToUse);
} else if (childrenToUse != null) {
var mountImages = this.mountChildren(
childrenToUse,
transaction,
processChildContext(context, this)
);
for (var i = 0; i < mountImages.length; i++) {
el.appendChild(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 native 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;
if (hasOwnProperty(COMPONENT_CLASSES, this._tag)) {
if (hasOwnProperty(COMPONENT_CLASSES[this._tag], 'getNativeProps')) {
lastProps = COMPONENT_CLASSES[this._tag].getNativeProps(this, lastProps);
nextProps = COMPONENT_CLASSES[this._tag].getNativeProps(this, nextProps);
}
}
assertValidProps(this, nextProps);
this._updateDOMProperties(lastProps, nextProps, transaction, null);
this._updateDOMChildren(
lastProps,
nextProps,
transaction,
processChildContext(context, this)
);
},
/**
* Reconciles the properties by detecting differences in property values and
* updating the DOM as necessary. This function is probably the single most
* critical path for performance optimization.
*
* TODO: Benchmark whether checking for changed values in memory actually
* improves performance (especially statically positioned elements).
* TODO: Benchmark the effects of putting this at the top since 99% of props
* do not change for a given reconciliation.
* TODO: Benchmark areas that can be improved with caching.
*
* @private
* @param {object} lastProps
* @param {object} nextProps
* @param {ReactReconcileTransaction} transaction
* @param {?DOMElement} node
*/
_updateDOMProperties: function(lastProps, nextProps, transaction, node) {
var propKey;
var styleName;
var styleUpdates;
for (propKey in lastProps) {
if (hasOwnProperty(nextProps, propKey) || !hasOwnProperty(lastProps, propKey)) {
continue;
}
if (propKey === STYLE) {
var lastStyle = this._previousStyleCopy;
for (styleName in lastStyle) {
if (hasOwnProperty(lastStyle, styleName)) {
styleUpdates = styleUpdates || {};
styleUpdates[styleName] = '';
}
}
this._previousStyleCopy = null;
} else if (hasOwnProperty(EventEmitter.registrationNameModules, propKey)) {
if (lastProps[propKey]) {
// Only call deleteListener if there was a listener previously or
// else willDeleteListener gets called when there wasn't actually a
// listener (e.g., onClick={null})
EventEmitter.deleteListener(this._rootNodeID, propKey);
}
} else if (
DOMProperty.properties[propKey] ||
DOMProperty.isCustomAttribute(propKey)) {
if (!node) {
node = Mount.getNode(this._rootNodeID);
}
DOMPropertyOperations.deleteValueForProperty(node, propKey);
}
}
for (propKey in nextProps) {
var nextProp = nextProps[propKey];
var lastProp = propKey === STYLE ?
this._previousStyleCopy :
lastProps[propKey];
if (!hasOwnProperty(nextProps, propKey) || nextProp === lastProp) {
continue;
}
if (propKey === STYLE) {
if (nextProp) {
if (__DEV__) {
checkAndWarnForMutatedStyle(
this._previousStyleCopy,
this._previousStyle,
this
);
this._previousStyle = nextProp;
}
nextProp = this._previousStyleCopy = assign({}, nextProp);
} else {
this._previousStyleCopy = null;
}
if (lastProp) {
// Unset styles on `lastProp` but not on `nextProp`.
for (styleName in lastProp) {
if (hasOwnProperty(lastProp, styleName) &&
(!nextProp || !hasOwnProperty(nextProp, styleName))) {
styleUpdates = styleUpdates || {};
styleUpdates[styleName] = '';
}
}
// Update styles that changed since `lastProp`.
for (styleName in nextProp) {
if (hasOwnProperty(nextProp, styleName) &&
lastProp[styleName] !== nextProp[styleName]) {
styleUpdates = styleUpdates || {};
styleUpdates[styleName] = nextProp[styleName];
}
}
} else {
// Relies on `updateStylesByID` not mutating `styleUpdates`.
styleUpdates = nextProp;
}
} else if (hasOwnProperty(propKey)) {
if (nextProp) {
enqueuePutListener(this._rootNodeID, propKey, nextProp, transaction);
} else if (lastProp) {
EventEmitter.deleteListener(this._rootNodeID, propKey);
}
} else if (isCustomComponent(this._tag, nextProps)) {
if (!node) {
node = Mount.getNode(this._rootNodeID);
}
DOMPropertyOperations.setValueForAttribute(
node,
propKey,
nextProp
);
} else if (
DOMProperty.properties[propKey] ||
DOMProperty.isCustomAttribute(propKey)) {
if (!node) {
node = Mount.getNode(this._rootNodeID);
}
// If we're updating to null or undefined, we should remove the property
// from the DOM node instead of inadvertantly setting to a string. This
// brings us in line with the same behavior we have on initial render.
if (nextProp != null) {
DOMPropertyOperations.setValueForProperty(node, propKey, nextProp);
} else {
DOMPropertyOperations.deleteValueForProperty(node, propKey);
}
}
}
if (styleUpdates) {
if (!node) {
node = Mount.getNode(this._rootNodeID);
}
CSSPropertyOperations.setValueForStyles(node, styleUpdates);
}
},
/**
* Reconciles the children with the various properties that affect the
* children content.
*
* @param {object} lastProps
* @param {object} nextProps
* @param {ReactReconcileTransaction} transaction
* @param {object} context
*/
_updateDOMChildren: function(lastProps, nextProps, transaction, context) {
var lastContent =
CONTENT_TYPES[typeof lastProps.children] ? lastProps.children : null;
var nextContent =
CONTENT_TYPES[typeof nextProps.children] ? nextProps.children : null;
var lastHtml =
lastProps.dangerouslySetInnerHTML &&
lastProps.dangerouslySetInnerHTML.__html;
var nextHtml =
nextProps.dangerouslySetInnerHTML &&
nextProps.dangerouslySetInnerHTML.__html;
// Note the use of `!=` which checks for null or undefined.
var lastChildren = lastContent != null ? null : lastProps.children;
var nextChildren = nextContent != null ? null : nextProps.children;
// If we're switching from children to content/html or vice versa, remove
// the old content
var lastHasContentOrHtml = lastContent != null || lastHtml != null;
var nextHasContentOrHtml = nextContent != null || nextHtml != null;
if (lastChildren != null && nextChildren == null) {
this.updateChildren(null, transaction, context);
} else if (lastHasContentOrHtml && !nextHasContentOrHtml) {
this.updateTextContent('');
}
if (nextContent != null) {
if (lastContent !== nextContent) {
this.updateTextContent('' + nextContent);
}
} else if (nextHtml != null) {
if (lastHtml !== nextHtml) {
this.updateMarkup('' + nextHtml);
}
} else if (nextChildren != null) {
this.updateChildren(nextChildren, transaction, context);
}
},
/**
* Destroys all event registrations for this instance. Does not remove from
* the DOM. That must be done by the parent.
*
* @internal
*/
unmountComponent: function() {
this.unmountChildren();
EventEmitter.deleteAllListeners(this._rootNodeID);
IDOperations.unmountIDFromEnvironment(this._rootNodeID);
this._rootNodeID = null;
this._wrapperState = null;
if (this._nodeWithLegacyProperties) {
var node = this._nodeWithLegacyProperties;
node._reactInternalComponent = null;
this._nodeWithLegacyProperties = null;
}
},
getPublicInstance: function() {
if (this._nodeWithLegacyProperties) {
return this._nodeWithLegacyProperties;
}
var node = Mount.getNode(this._rootNodeID);
node._reactInternalComponent = this;
node.getDOMNode = legacyGetDOMNode;
node.isMounted = legacyIsMounted;
node.setState = legacySetStateEtc;
node.replaceState = legacySetStateEtc;
node.forceUpdate = legacySetStateEtc;
node.setProps = legacySetProps;
node.replaceProps = legacyReplaceProps;
// updateComponent will update this property on subsequent renders
node.props = this._currentElement.props;
this._nodeWithLegacyProperties = node;
}
};
Perf.measureMethods(ReactTVMLComponent, 'ReactTVMLComponent', {
mountComponent: 'mountComponent',
updateComponent: 'updateComponent'
});
assign(
ReactTVMLComponent.prototype,
ReactTVMLComponent.Mixin,
MultiChild.Mixin
);
module.exports = ReactTVMLComponent;
================================================
FILE: src/EventEmitter.js
================================================
// For components like lockup, listItemLockup you can expect select, play, highlight, holdselect. For Buttons only select and play events are fired. For text fields change event is sent.
'use strict';
var EventPluginHub = require('react/lib//EventPluginHub');
var EventPluginRegistry = require('react/lib//EventPluginRegistry');
var ReactEventEmitterMixin = require('react/lib//ReactEventEmitterMixin');
var EventListener = require('./EventListener');
var assign = require('react/lib/Object.assign');
var hasOwnProperty = require('has-own-prop');
/**
* Summary of `EventEmitter` event handling:
*
* - Top-level delegation is used to trap most native browser events. This
* may only occur in the main thread and is the responsibility of
* EventListener, which is injected and can therefore support pluggable
* event sources. This is the only work that occurs in the main thread.
*
* - We normalize and de-duplicate events to account for browser quirks. This
* may be done in the worker thread.
*
* - Forward these native events (with the associated top-level type used to
* trap it) to `EventPluginHub`, which in turn will ask plugins if they want
* to extract any synthetic events.
*
* - The `EventPluginHub` will then process each event by annotating them with
* "dispatches", a sequence of listeners and IDs that care about that event.
*
* - The `EventPluginHub` then dispatches the events.
*
* Overview of React and the event system:
*
* +------------+ .
* | DOM | .
* +------------+ .
* | .
* v .
* +------------+ .
* | ReactEvent | .
* | Listener | .
* +------------+ . +-----------+
* | . +--------+|SimpleEvent|
* | . | |Plugin |
* +-----|------+ . v +-----------+
* | | | . +--------------+ +------------+
* | +-----------.--->|EventPluginHub| | Event |
* | | . | | +-----------+ | Propagators|
* | ReactEvent | . | | |TapEvent | |------------|
* | Emitter | . | |<---+|Plugin | |other plugin|
* | | . | | +-----------+ | utilities |
* | +-----------.--->| | +------------+
* | | | . +--------------+
* +-----|------+ . ^ +-----------+
* | . | |Enter/Leave|
* + . +-------+|Plugin |
* +-------------+ . +-----------+
* | application | .
* |-------------| .
* | | .
* | | .
* +-------------+ .
* .
* React Core . General Purpose Event Plugin System
*/
var alreadyListeningTo = {};
var reactTopListenersCounter = 0;
// For events like 'submit' which don't consistently bubble (which we trap at a
// lower node than `document`), binding at `document` would cause duplicate
// events so we don't include them here
var topEventMapping = {
topSelect: 'select',
topPlay: 'play',
topHighlight: 'highlight',
topHoldselect: 'holdselect'
};
/**
* To ensure no conflicts with other potential React instances on the page
*/
var topListenersIDKey = '_reactListenersID' + String(Math.random()).slice(2);
function getListeningForDocument(mountAt) {
if (!hasOwnProperty(mountAt, topListenersIDKey)) {
mountAt[topListenersIDKey] = reactTopListenersCounter + 1;
alreadyListeningTo[mountAt[topListenersIDKey]] = {};
}
return alreadyListeningTo[mountAt[topListenersIDKey]];
}
var getDependencies = function(name) {
return EventPluginRegistry.registrationNameDependencies[name];
};
/**
* `EventEmitter` is used to attach top-level event listeners. For
* example:
*
* EventEmitter.putListener('myID', 'onClick', myFunction);
*
* This would allocate a "registration" of `('onClick', myFunction)` on 'myID'.
*
* @internal
*/
var EventEmitter = assign({}, ReactEventEmitterMixin, {
/**
* Sets whether or not any created callbacks should be enabled.
*
* @param {boolean} enabled True if callbacks should be enabled.
*/
setEnabled: function(enabled) {
EventListener.setEnabled(enabled);
},
/**
* @return {boolean} True if callbacks are enabled.
*/
isEnabled: function() {
return EventListener.isEnabled();
},
/**
* We listen for bubbled touch events on the document object.
*
*
* @param {string} registrationName Name of listener (e.g. `onSelect`).
* @param {object} mountAt Document which owns the container
*/
listenTo: function(registrationName, mountAt) {
var isListening = getListeningForDocument(mountAt);
var dependencies = getDependencies(registrationName);
dependencies.forEach(function(dependency) {
if ((hasOwnProperty(isListening, dependency) && isListening[dependency])) {
return;
}
if (hasOwnProperty(topEventMapping, dependency)) {
EventEmitter.trapBubbledEvent(dependency, topEventMapping[dependency], mountAt);
}
isListening[dependency] = true;
});
},
trapBubbledEvent: function(topLevelType, handlerBaseName, handle) {
return EventListener.trapBubbledEvent(topLevelType, handlerBaseName, handle);
},
trapCapturedEvent: function(topLevelType, handlerBaseName, handle) {
return EventListener.trapCapturedEvent(topLevelType, handlerBaseName, handle);
},
eventNameDispatchConfigs: EventPluginHub.eventNameDispatchConfigs,
registrationNameModules: EventPluginHub.registrationNameModules,
putListener: EventPluginHub.putListener,
getListener: EventPluginHub.getListener,
deleteListener: EventPluginHub.deleteListener,
deleteAllListeners: EventPluginHub.deleteAllListeners
});
module.exports = EventEmitter;
================================================
FILE: src/EventListener.js
================================================
'use strict';
var EventListener = require('fbjs/lib/EventListener');
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
var PooledClass = require('react/lib/PooledClass');
var ReactInstanceHandles = require('react/lib/ReactInstanceHandles');
var ReactMount = require('./Mount');
var ReactUpdates = require('react/lib/ReactUpdates');
var assign = require('react/lib/Object.assign');
var getEventTarget = require('react/lib/getEventTarget');
var getUnboundedScrollPosition = require('fbjs/lib/getUnboundedScrollPosition');
var DOCUMENT_FRAGMENT_NODE_TYPE = 11;
/**
* Finds the parent React component of `node`.
*
* @param {*} node
* @return {?DOMEventTarget} Parent container, or `null` if the specified node
* is not nested.
*/
function findParent(node) {
// TODO: It may be a good idea to cache this to prevent unnecessary DOM
// traversal, but caching is difficult to do correctly without using a
// mutation observer to listen for all DOM changes.
var nodeID = ReactMount.getID(node);
var rootID = ReactInstanceHandles.getReactRootIDFromNodeID(nodeID);
var container = ReactMount.findReactContainerForID(rootID);
var parent = ReactMount.getFirstReactDOM(container);
return parent;
}
// Used to store ancestor hierarchy in top level callback
function TopLevelCallbackBookKeeping(topLevelType, nativeEvent) {
this.topLevelType = topLevelType;
this.nativeEvent = nativeEvent;
this.ancestors = [];
}
assign(TopLevelCallbackBookKeeping.prototype, {
destructor: function () {
this.topLevelType = null;
this.nativeEvent = null;
this.ancestors.length = 0;
}
});
PooledClass.addPoolingTo(TopLevelCallbackBookKeeping, PooledClass.twoArgumentPooler);
function handleTopLevelImpl(bookKeeping) {
// TODO: Re-enable event.path handling
//
// if (bookKeeping.nativeEvent.path && bookKeeping.nativeEvent.path.length > 1) {
// // New browsers have a path attribute on native events
// handleTopLevelWithPath(bookKeeping);
// } else {
// // Legacy browsers don't have a path attribute on native events
// handleTopLevelWithoutPath(bookKeeping);
// }
void handleTopLevelWithPath; // temporarily unused
handleTopLevelWithoutPath(bookKeeping);
}
// Legacy browsers don't have a path attribute on native events
function handleTopLevelWithoutPath(bookKeeping) {
var topLevelTarget = ReactMount.getFirstReactDOM(getEventTarget(bookKeeping.nativeEvent)) || window;
// Loop through the hierarchy, in case there's any nested components.
// It's important that we build the array of ancestors before calling any
// event handlers, because event handlers can modify the DOM, leading to
// inconsistencies with ReactMount's node cache. See #1105.
var ancestor = topLevelTarget;
while (ancestor) {
bookKeeping.ancestors.push(ancestor);
ancestor = findParent(ancestor);
}
for (var i = 0; i < bookKeeping.ancestors.length; i++) {
topLevelTarget = bookKeeping.ancestors[i];
var topLevelTargetID = ReactMount.getID(topLevelTarget) || '';
ReactEventListener._handleTopLevel(bookKeeping.topLevelType, topLevelTarget, topLevelTargetID, bookKeeping.nativeEvent, getEventTarget(bookKeeping.nativeEvent));
}
}
// New browsers have a path attribute on native events
function handleTopLevelWithPath(bookKeeping) {
var path = bookKeeping.nativeEvent.path;
var currentNativeTarget = path[0];
var eventsFired = 0;
for (var i = 0; i < path.length; i++) {
var currentPathElement = path[i];
if (currentPathElement.nodeType === DOCUMENT_FRAGMENT_NODE_TYPE) {
currentNativeTarget = path[i + 1];
}
// TODO: slow
var reactParent = ReactMount.getFirstReactDOM(currentPathElement);
if (reactParent === currentPathElement) {
var currentPathElementID = ReactMount.getID(currentPathElement);
var newRootID = ReactInstanceHandles.getReactRootIDFromNodeID(currentPathElementID);
bookKeeping.ancestors.push(currentPathElement);
var topLevelTargetID = ReactMount.getID(currentPathElement) || '';
eventsFired++;
ReactEventListener._handleTopLevel(bookKeeping.topLevelType, currentPathElement, topLevelTargetID, bookKeeping.nativeEvent, currentNativeTarget);
// Jump to the root of this React render tree
while (currentPathElementID !== newRootID) {
i++;
currentPathElement = path[i];
currentPathElementID = ReactMount.getID(currentPathElement);
}
}
}
if (eventsFired === 0) {
ReactEventListener._handleTopLevel(bookKeeping.topLevelType, window, '', bookKeeping.nativeEvent, getEventTarget(bookKeeping.nativeEvent));
}
}
function scrollValueMonitor(cb) {
var scrollPosition = getUnboundedScrollPosition(window);
cb(scrollPosition);
}
var ReactEventListener = {
_enabled: true,
_handleTopLevel: null,
WINDOW_HANDLE: ExecutionEnvironment.canUseDOM ? window : null,
setHandleTopLevel: function (handleTopLevel) {
ReactEventListener._handleTopLevel = handleTopLevel;
},
setEnabled: function (enabled) {
ReactEventListener._enabled = !!enabled;
},
isEnabled: function () {
return ReactEventListener._enabled;
},
/**
* Traps top-level events by using event bubbling.
*
* @param {string} topLevelType Record from `EventConstants`.
* @param {string} handlerBaseName Event name (e.g. "click").
* @param {object} handle Element on which to attach listener.
* @return {?object} An object with a remove function which will forcefully
* remove the listener.
* @internal
*/
trapBubbledEvent: function (topLevelType, handlerBaseName, handle) {
var element = handle;
if (!element) {
return null;
}
return EventListener.listen(element, handlerBaseName, ReactEventListener.dispatchEvent.bind(null, topLevelType));
},
/**
* Traps a top-level event by using event capturing.
*
* @param {string} topLevelType Record from `EventConstants`.
* @param {string} handlerBaseName Event name (e.g. "click").
* @param {object} handle Element on which to attach listener.
* @return {?object} An object with a remove function which will forcefully
* remove the listener.
* @internal
*/
trapCapturedEvent: function (topLevelType, handlerBaseName, handle) {
var element = handle;
if (!element) {
return null;
}
return EventListener.capture(element, handlerBaseName, ReactEventListener.dispatchEvent.bind(null, topLevelType));
},
dispatchEvent: function (topLevelType, nativeEvent) {
if (!ReactEventListener._enabled) {
return;
}
var bookKeeping = TopLevelCallbackBookKeeping.getPooled(topLevelType, nativeEvent);
try {
// Event queue being processed in the same cycle allows
// `preventDefault`.
ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);
} finally {
TopLevelCallbackBookKeeping.release(bookKeeping);
}
}
};
module.exports = ReactEventListener;
================================================
FILE: src/EventPlugin.js
================================================
'use strict';
var EventPropagators = require('react/lib/EventPropagators');
var SyntheticEvent = require('react/lib/SyntheticEvent');
var keyOf = require('fbjs/lib/keyOf');
var eventTypes = {
select: {
phasedRegistrationNames: {
bubbled: keyOf({
onSelect: true
}),
captured: keyOf({
onSelectCapture: true
})
}
},
play: {
phasedRegistrationNames: {
bubbled: keyOf({
onPlay: true
}),
captured: keyOf({
onPlayCapture: true
})
}
},
highlight: {
phasedRegistrationNames: {
bubbled: keyOf({
onHighlight: true
}),
captured: keyOf({
onHighlightCapture: true
})
}
},
holdSelect: {
phasedRegistrationNames: {
bubbled: keyOf({
onHoldSelect: true
}),
captured: keyOf({
onHoldSelectCapture: true
})
}
}
};
var topLevelEventsToDispatchConfig = {
topSelect: eventTypes.select,
topPlay: eventTypes.play,
topHighlight: eventTypes.highlight,
topHoldSelect: eventTypes.holdSelect
};
for (var type in topLevelEventsToDispatchConfig) {
topLevelEventsToDispatchConfig[type].dependencies = [type];
}
var SimpleEventPlugin = {
eventTypes: eventTypes,
/**
* @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 {*} An accumulation of synthetic events.
* @see {EventPluginHub.extractEvents}
*/
extractEvents: function (topLevelType, topLevelTarget, topLevelTargetID, nativeEvent, nativeEventTarget) {
var dispatchConfig = topLevelEventsToDispatchConfig[topLevelType];
if (!dispatchConfig) {
return null;
}
var ev = SyntheticEvent.getPooled(dispatchConfig, topLevelTargetID, nativeEvent, nativeEventTarget);
EventPropagators.accumulateTwoPhaseDispatches(ev);
return ev;
}
};
module.exports = SimpleEventPlugin;
================================================
FILE: src/IDOperations.js
================================================
'use strict';
var DOMChildrenOperations = require('react/lib/DOMChildrenOperations');
var DOMPropertyOperations = require('react/lib/DOMPropertyOperations');
var ReactPerf = require('react/lib/ReactPerf');
var Mount = require('./Mount');
var invariant = require('fbjs/lib/invariant');
/**
* Operations used to process updates to DOM nodes.
*/
var ReactTVMLIDOperations = {
/**
* Replaces a DOM node that exists in the document with markup.
*
* @param {string} id ID of child to be replaced.
* @param {string} markup Dangerous markup to inject in place of child.
* @internal
* @see {Danger.dangerouslyReplaceNodeWithMarkup}
*/
replaceNodeWithMarkupByID: function(id, markup) {
var node = Mount.getNode(id);
DOMChildrenOperations.dangerouslyReplaceNodeWithMarkup(node, markup);
},
/**
* Updates a component's children by processing a series of updates.
*
* @param {array<object>} updates List of update configurations.
* @param {array<string>} markup List of markup strings.
* @internal
*/
processChildrenUpdates: function(updates, markup) {
DOMChildrenOperations.processUpdates(updates.map(function(update) {
update.parentNode = Mount.getNode(update.parentID);
return update;
}), markup);
},
/**
* If a particular environment requires that some resources be cleaned up,
* specify this in the injected Mixin. In the DOM, we would likely want to
* purge any cached node ID lookups.
*
* @private
*/
unmountIDFromEnvironment: function(rootNodeID) {
Mount.purgeID(rootNodeID);
}
};
ReactPerf.measureMethods(ReactTVMLIDOperations, 'ReactTVMLIDOperations', {
updatePropertyByID: 'updatePropertyByID',
replaceNodeWithMarkupByID: 'replaceNodeWithMarkupByID',
processChildrenUpdates: 'processChildrenUpdates'
});
module.exports = ReactTVMLIDOperations;
================================================
FILE: src/Mount.js
================================================
'use strict';
var __DEV__ = false; // process.env.NODE_ENV !== 'production';
var DOMProperty = require('react/lib/DOMProperty');
var ReactCurrentOwner = require('react/lib/ReactCurrentOwner');
var ReactDOMFeatureFlags = require('react/lib/ReactDOMFeatureFlags');
var ReactElement = require('react/lib/ReactElement');
var ReactEmptyComponentRegistry = require('react/lib/ReactEmptyComponentRegistry');
var ReactInstanceHandles = require('react/lib/ReactInstanceHandles');
var ReactInstanceMap = require('react/lib/ReactInstanceMap');
var ReactMarkupChecksum = require('react/lib/ReactMarkupChecksum');
var ReactPerf = require('react/lib/ReactPerf');
var ReactReconciler = require('react/lib/ReactReconciler');
var ReactUpdateQueue = require('react/lib/ReactUpdateQueue');
var ReactUpdates = require('react/lib/ReactUpdates');
var assign = require('react/lib/Object.assign');
var emptyObject = require('fbjs/lib/emptyObject');
var containsNode = require('fbjs/lib/containsNode');
var instantiateReactComponent = require('./instantiateReactComponent');
var invariant = require('fbjs/lib/invariant');
var setInnerHTML = require('react/lib/setInnerHTML');
var shouldUpdateReactComponent = require('react/lib/shouldUpdateReactComponent');
var validateDOMNesting = require('react/lib/validateDOMNesting');
var warning = require('fbjs/lib/warning');
var parser = new DOMParser();
var ATTR_NAME = DOMProperty.ID_ATTRIBUTE_NAME;
var nodeCache = {};
var ELEMENT_NODE_TYPE = 1;
var DOC_NODE_TYPE = 9;
var DOCUMENT_FRAGMENT_NODE_TYPE = 11;
var ownerDocumentContextKey =
'__TVMLMount_ownerDocument$' + Math.random().toString(36).slice(2);
/** Mapping from reactRootID to React component instance. */
var instancesByReactRootID = {};
/** Mapping from reactRootID to `container` nodes. */
var containersByReactRootID = {};
if (__DEV__) {
/** __DEV__-only mapping from reactRootID to root elements. */
var rootElementsByReactRootID = {};
}
// Used to store breadth-first search state in findComponentRoot.
var findComponentRootReusableArray = [];
/**
* Finds the index of the first character
* that's not common between the two given strings.
*
* @return {number} the index of the character where the strings diverge
*/
function firstDifferenceIndex(string1, string2) {
var minLen = Math.min(string1.length, string2.length);
for (var i = 0; i < minLen; i++) {
if (string1.charAt(i) !== string2.charAt(i)) {
return i;
}
}
return string1.length === string2.length ? -1 : minLen;
}
/**
* @param {DOMElement|DOMDocument} container DOM element that may contain
* a React component
* @return {?*} DOM element that may have the reactRoot ID, or null.
*/
function getReactRootElementInContainer(container) {
if (!container) {
return null;
}
return container.documentElement;
}
/**
* @param {DOMElement} container DOM element that may contain a React component.
* @return {?string} A "reactRoot" ID, if a React component is rendered.
*/
function getReactRootID(container) {
var rootElement = getReactRootElementInContainer(container);
return rootElement && TVMLMount.getID(rootElement);
}
/**
* Accessing node[ATTR_NAME] or calling getAttribute(ATTR_NAME) on a form
* element can return its control whose name or ID equals ATTR_NAME. All
* DOM nodes support `getAttributeNode` but this can also get called on
* other objects so just return '' if we're given something other than a
* DOM node (such as window).
*
* @param {?DOMElement|DOMWindow|DOMDocument|DOMTextNode} node DOM node.
* @return {string} ID of the supplied `domNode`.
*/
function getID(node) {
var id = internalGetID(node);
if (id) {
if (nodeCache.hasOwnProperty(id)) {
var cached = nodeCache[id];
if (cached !== node) {
invariant(
!isValid(cached, id),
'TVMLMount: Two valid but unequal nodes with the same `%s`: %s',
ATTR_NAME, id
);
nodeCache[id] = node;
}
} else {
nodeCache[id] = node;
}
}
return id;
}
function internalGetID(node) {
// If node is something like a window, document, or text node, none of
// which support attributes or a .getAttribute method, gracefully return
// the empty string, as if the attribute were missing.
return node && node.getAttribute && node.getAttribute(ATTR_NAME) || '';
}
/**
* Sets the React-specific ID of the given node.
*
* @param {DOMElement} node The DOM node whose ID will be set.
* @param {string} id The value of the ID attribute.
*/
function setID(node, id) {
var oldID = internalGetID(node);
if (oldID !== id) {
delete nodeCache[oldID];
}
node.setAttribute(ATTR_NAME, id);
nodeCache[id] = node;
}
/**
* Finds the node with the supplied React-generated DOM ID.
*
* @param {string} id A React-generated DOM ID.
* @return {DOMElement} DOM node with the suppled `id`.
* @internal
*/
function getNode(id) {
if (!nodeCache.hasOwnProperty(id) || !isValid(nodeCache[id], id)) {
nodeCache[id] = TVMLMount.findReactNodeByID(id);
}
return nodeCache[id];
}
/**
* Finds the node with the supplied public React instance.
*
* @param {*} instance A public React instance.
* @return {?DOMElement} DOM node with the suppled `id`.
* @internal
*/
function getNodeFromInstance(instance) {
var id = ReactInstanceMap.get(instance)._rootNodeID;
if (ReactEmptyComponentRegistry.isNullComponentID(id)) {
return null;
}
if (!nodeCache.hasOwnProperty(id) || !isValid(nodeCache[id], id)) {
nodeCache[id] = TVMLMount.findReactNodeByID(id);
}
return nodeCache[id];
}
/**
* A node is "valid" if it is contained by a currently mounted container.
*
* This means that the node does not have to be contained by a document in
* order to be considered valid.
*
* @param {?DOMElement} node The candidate DOM node.
* @param {string} id The expected ID of the node.
* @return {boolean} Whether the node is contained by a mounted container.
*/
function isValid(node, id) {
if (node) {
invariant(
internalGetID(node) === id,
'TVMLMount: Unexpected modification of `%s`',
ATTR_NAME
);
var container = TVMLMount.findReactContainerForID(id);
if (container && containsNode(container, node)) {
return true;
}
}
return false;
}
/**
* Causes the cache to forget about one React-specific ID.
*
* @param {string} id The ID to forget.
*/
function purgeID(id) {
delete nodeCache[id];
}
var deepestNodeSoFar = null;
function findDeepestCachedAncestorImpl(ancestorID) {
var ancestor = nodeCache[ancestorID];
if (ancestor && isValid(ancestor, ancestorID)) {
deepestNodeSoFar = ancestor;
} else {
// This node isn't populated in the cache, so presumably none of its
// descendants are. Break out of the loop.
return false;
}
}
/**
* Return the deepest cached node whose ID is a prefix of `targetID`.
*/
function findDeepestCachedAncestor(targetID) {
deepestNodeSoFar = null;
ReactInstanceHandles.traverseAncestors(
targetID,
findDeepestCachedAncestorImpl
);
var foundNode = deepestNodeSoFar;
deepestNodeSoFar = null;
return foundNode;
}
/**
* Mounts this component and inserts it into the DOM.
*
* @param {ReactComponent} componentInstance The instance to mount.
* @param {string} rootID DOM ID of the root node.
* @param {DOMElement} container DOM element to mount into.
* @param {ReactReconcileTransaction} transaction
* @param {boolean} shouldReuseMarkup If true, do not insert markup
*/
function mountComponentIntoNode(
componentInstance,
rootID,
container,
transaction,
shouldReuseMarkup,
context
) {
if (ReactDOMFeatureFlags.useCreateElement) {
context = assign({}, context);
if (container.nodeType === DOC_NODE_TYPE) {
context[ownerDocumentContextKey] = container;
} else {
context[ownerDocumentContextKey] = container.ownerDocument;
}
}
if (__DEV__) {
if (context === emptyObject) {
context = {};
}
var tag = container.nodeName.toLowerCase();
context[validateDOMNesting.ancestorInfoContextKey] =
validateDOMNesting.updatedAncestorInfo(null, tag, null);
}
var markup = ReactReconciler.mountComponent(
componentInstance, rootID, transaction, context
);
componentInstance._renderedComponent._topLevelWrapper = componentInstance;
TVMLMount._mountImageIntoNode(
markup,
container,
shouldReuseMarkup,
transaction
);
}
/**
* Batched mount.
*
* @param {ReactComponent} componentInstance The instance to mount.
* @param {string} rootID DOM ID of the root node.
* @param {DOMElement} container DOM element to mount into.
* @param {boolean} shouldReuseMarkup If true, do not insert markup
*/
function batchedMountComponentIntoNode(
componentInstance,
rootID,
container,
shouldReuseMarkup,
context
) {
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(
/* forceHTML */ shouldReuseMarkup
);
transaction.perform(
mountComponentIntoNode,
null,
componentInstance,
rootID,
container,
transaction,
shouldReuseMarkup,
context
);
ReactUpdates.ReactReconcileTransaction.release(transaction);
}
/**
* Unmounts a component and removes it from the DOM.
*
* @param {ReactComponent} instance React component instance.
* @param {DOMElement} container DOM element to unmount from.
* @final
* @internal
* @see {TVMLMount.unmountComponentAtNode}
*/
function unmountComponentFromNode(instance, container) {
ReactReconciler.unponent(instance);
if (container.nodeType === DOC_NODE_TYPE) {
container = container.documentElement;
}
// http://jsperf.com/emptying-a-node
while (container.lastChild) {
container.removeChild(container.lastChild);
}
}
/**
* True if the supplied DOM node has a direct React-rendered child that is
* not a React root element. Useful for warning in `render`,
* `unmountComponentAtNode`, etc.
*
* @param {?DOMElement} node The candidate DOM node.
* @return {boolean} True if the DOM element contains a direct child that was
* rendered by React but is not a root element.
* @internal
*/
function hasNonRootReactChild(node) {
var reactRootID = getReactRootID(node);
return reactRootID ? reactRootID !==
ReactInstanceHandles.getReactRootIDFromNodeID(reactRootID) : false;
}
/**
* Returns the first (deepest) ancestor of a node which is rendered by this copy
* of React.
*/
function findFirstReactDOMImpl(node) {
// This node might be from another React instance, so we make sure not to
// examine the node cache here
for (; node && node.parentNode !== node; node = node.parentNode) {
var nodeID = internalGetID(node);
if (!nodeID) {
continue;
}
var reactRootID = ReactInstanceHandles.getReactRootIDFromNodeID(nodeID);
// If containersByReactRootID contains the container we find by crawling up
// the tree, we know that this instance of React rendered the node.
// nb. isValid's strategy (with containsNode) does not work because render
// trees may be nested and we don't want a false positive in that case.
var current = node;
var lastID;
do {
lastID = internalGetID(current);
current = current.parentNode;
if (current == null) {
// The passed-in node has been detached from the container it was
// originally rendered into.
return null;
}
} while (lastID !== reactRootID);
var expected = containersByReactRootID[reactRootID];
if ((
expected.nodeType === DOC_NODE_TYPE &&
current.nodeType === ELEMENT_NODE_TYPE &&
current.ownerDocument === expected
)) {
return node;
}
if (current === expected) {
return node;
}
}
return null;
}
/**
* Temporary (?) hack so that we can store all top-level pending updates on
* composites instead of having to worry about different types of components
* here.
*/
var TopLevelWrapper = function() {};
TopLevelWrapper.isReactClass = {};
if (__DEV__) {
TopLevelWrapper.displayName = 'TopLevelWrapper';
}
TopLevelWrapper.prototype.render = function() {
// this.props is actually a ReactElement
return this.props;
};
/**
* Mounting is the process of initializing a React component by creating its
* representative DOM elements and inserting them into a supplied `container`.
* Any prior content inside `container` is destroyed in the process.
*
* TVMLMount.render(
* component,
* document.getElementById('container')
* );
*
* <div id="container"> <-- Supplied `container`.
* <div data-reactid=".3"> <-- Rendered reactRoot of React
* // ... component.
* </div>
* </div>
*
* Inside of `container`, the first element rendered is the "reactRoot".
*/
var TVMLMount = {
/** Exposed for debugging purposes **/
_instancesByReactRootID: instancesByReactRootID,
generateEmptyContainer: function () {
return parser.parseFromString('<document></document>', 'text/xml');
},
/**
* Take a component that's already mounted into the DOM and replace its props
* @param {ReactComponent} prevComponent component instance already in the DOM
* @param {ReactElement} nextElement component instance to render
* @param {DOMElement} container container to render into
* @param {?function} callback function triggered on completion
*/
_updateRootComponent: function(
prevComponent,
nextElement,
container,
callback) {
TVMLMount.scrollMonitor(container, function() {
ReactUpdateQueue.enqueueElementInternal(prevComponent, nextElement);
if (callback) {
ReactUpdateQueue.enqueueCallbackInternal(prevComponent, callback);
}
});
if (__DEV__) {
// Record the root element in case it later gets transplanted.
rootElementsByReactRootID[getReactRootID(container)] =
getReactRootElementInContainer(container);
}
return prevComponent;
},
/**
* Register a component into the instance map and starts scroll value
* monitoring
* @param {ReactComponent} nextComponent component instance to render
* @param {DOMElement} container container to render into
* @return {string} reactRoot ID prefix
*/
_registerComponent: function(nextComponent, container) {
invariant(
container && (
container.nodeType === ELEMENT_NODE_TYPE ||
container.nodeType === DOC_NODE_TYPE ||
container.nodeType === DOCUMENT_FRAGMENT_NODE_TYPE
),
'_registerComponent(...): Target container is not a DOM element.'
);
var reactRootID = TVMLMount.registerContainer(container);
instancesByReactRootID[reactRootID] = nextComponent;
return reactRootID;
},
/**
* Render a new component into the DOM.
* @param {ReactElement} nextElement element to render
* @param {DOMElement} container container to render into
* @param {boolean} shouldReuseMarkup if we should skip the markup insertion
* @return {ReactComponent} nextComponent
*/
_renderNewRootComponent: function(
nextElement,
container,
shouldReuseMarkup,
context
) {
// Various parts of our code (such as ReactCompositeComponent's
// _renderValidatedComponent) assume that calls to render aren't nested;
// verify that that's the case.
warning(
ReactCurrentOwner.current == null,
'_renderNewRootComponent(): Render methods should be a pure function ' +
'of props and state; triggering nested component updates from ' +
'render is not allowed. If necessary, trigger nested updates in ' +
'componentDidUpdate. Check the render method of %s.',
ReactCurrentOwner.current && ReactCurrentOwner.current.getName() ||
'ReactCompositeComponent'
);
var componentInstance = instantiateReactComponent(nextElement, null);
var reactRootID = TVMLMount._registerComponent(
componentInstance,
container
);
// The initial render is synchronous but any updates that happen during
// rendering, in componentWillMount or componentDidMount, will be batched
// according to the current batching strategy.
ReactUpdates.batchedUpdates(
batchedMountComponentIntoNode,
componentInstance,
reactRootID,
container,
shouldReuseMarkup,
context
);
if (__DEV__) {
// Record the root element in case it later gets transplanted.
rootElementsByReactRootID[reactRootID] =
getReactRootElementInContainer(container);
}
return componentInstance;
},
/**
* Renders a React component into the DOM in the supplied `container`.
*
* If the React component was previously rendered into `container`, this will
* perform an update on it and only mutate the DOM as necessary to reflect the
* latest React component.
*
* @param {ReactComponent} parentComponent The conceptual parent of this render tree.
* @param {ReactElement} nextElement Component element to render.
* @param {DOMElement} container DOM element to render into.
* @param {?function} callback function triggered on completion
* @return {ReactComponent} Component instance rendered in `container`.
*/
renderSubtreeIntoContainer: function(parentComponent, nextElement, container, callback) {
invariant(
parentComponent != null && parentComponent._reactInternalInstance != null,
'parentComponent must be a valid React Component'
);
return TVMLMount._renderSubtreeIntoContainer(
parentComponent,
nextElement,
container,
callback
);
},
_renderSubtreeIntoContainer: function(parentComponent, nextElement, container, callback) {
invariant(
ReactElement.isValidElement(nextElement),
'ReactDOM.render(): Invalid component element.%s',
(
typeof nextElement === 'string' ?
' Instead of passing an element string, make sure to instantiate ' +
'it by passing it to React.createElement.' :
typeof nextElement === 'function' ?
' Instead of passing a component class, make sure to instantiate ' +
'it by passing it to React.createElement.' :
// Check if it quacks like an element
nextElement != null && nextElement.props !== undefined ?
' This may be caused by unintentionally loading two independent ' +
'copies of React.' :
''
)
);
warning(
!container || !container.tagName ||
container.tagName.toUpperCase() !== 'BODY',
'render(): Rendering components directly into document.body is ' +
'discouraged, since its children are often manipulated by third-party ' +
'scripts and browser extensions. This may lead to subtle ' +
'reconciliation issues. Try rendering into a container element created ' +
'for your app.'
);
var nextWrappedElement = new ReactElement(
TopLevelWrapper,
null,
null,
null,
null,
null,
nextElement
);
var prevComponent = instancesByReactRootID[getReactRootID(container)];
if (prevComponent) {
var prevWrappedElement = prevComponent._currentElement;
var prevElement = prevWrappedElement.props;
if (shouldUpdateReactComponent(prevElement, nextElement)) {
return TVMLMount._updateRootComponent(
prevComponent,
nextWrappedElement,
container,
callback
)._renderedComponent.getPublicInstance();
} else {
TVMLMount.unmountComponentAtNode(container);
}
}
var reactRootElement = getReactRootElementInContainer(container);
var containerHasReactMarkup =
reactRootElement && !!internalGetID(reactRootElement);
var containerHasNonRootReactChild = hasNonRootReactChild(container);
if (__DEV__) {
warning(
!containerHasNonRootReactChild,
'render(...): Replacing React-rendered children with a new root ' +
'component. If you intended to update the children of this node, ' +
'you should instead have the existing children update their state ' +
'and render the new components instead of calling ReactDOM.render.'
);
if (!containerHasReactMarkup || reactRootElement.nextSibling) {
var rootElementSibling = reactRootElement;
while (rootElementSibling) {
if (internalGetID(rootElementSibling)) {
warning(
false,
'render(): Target node has markup rendered by React, but there ' +
'are unrelated nodes as well. This is most commonly caused by ' +
'white-space inserted around server-rendered markup.'
);
break;
}
rootElementSibling = rootElementSibling.nextSibling;
}
}
}
var shouldReuseMarkup =
containerHasReactMarkup &&
!prevComponent &&
!containerHasNonRootReactChild;
var component = TVMLMount._renderNewRootComponent(
nextWrappedElement,
container,
shouldReuseMarkup,
parentComponent != null ?
parentComponent._reactInternalInstance._processChildContext(
parentComponent._reactInternalInstance._context
) :
emptyObject
)._renderedComponent.getPublicInstance();
if (callback) {
callback.call(component);
}
return component;
},
/**
* Renders a React component into the DOM in the supplied `container`.
*
* If the React component was previously rendered into `container`, this will
* perform an update on it and only mutate the DOM as necessary to reflect the
* latest React component.
*
* @param {ReactElement} nextElement Component element to render.
* @param {DOMElement} container DOM element to render into.
* @param {?function} callback function triggered on completion
* @return {ReactComponent} Component instance rendered in `container`.
*/
render: function(nextElement, callback) {
var container = TVMLMount.generateEmptyContainer();
var component = TVMLMount._renderSubtreeIntoContainer(null, nextElement, container, callback);
navigationDocument.pushDocument(container);
return component;
},
/**
* Registers a container node into which React components will be rendered.
* This also creates the "reactRoot" ID that will be assigned to the element
* rendered within.
*
* @param {DOMElement} container DOM element to register as a container.
* @return {string} The "reactRoot" ID of elements rendered within.
*/
registerContainer: function(container) {
var reactRootID = getReactRootID(container);
if (reactRootID) {
// If one exists, make sure it is a valid "reactRoot" ID.
reactRootID = ReactInstanceHandles.getReactRootIDFromNodeID(reactRootID);
}
if (!reactRootID) {
// No valid "reactRoot" ID found, create one.
reactRootID = ReactInstanceHandles.createReactRootID();
}
containersByReactRootID[reactRootID] = container;
return reactRootID;
},
/**
* Unmounts and destroys the React component rendered in the `container`.
*
* @param {DOMElement} container DOM element containing a React component.
* @return {boolean} True if a component was found in and unmounted from
* `container`
*/
unmountComponentAtNode: function(container) {
// Various parts of our code (such as ReactCompositeComponent's
// _renderValidatedComponent) assume that calls to render aren't nested;
// verify that that's the case. (Strictly speaking, unmounting won't cause a
// render but we still don't expect to be in a render call here.)
warning(
ReactCurrentOwner.current == null,
'unmountComponentAtNode(): Render methods should be a pure function ' +
'of props and state; triggering nested component updates from render ' +
'is not allowed. If necessary, trigger nested updates in ' +
'componentDidUpdate. Check the render method of %s.',
ReactCurrentOwner.current && ReactCurrentOwner.current.getName() ||
'ReactCompositeComponent'
);
invariant(
container && (
container.nodeType === ELEMENT_NODE_TYPE ||
container.nodeType === DOC_NODE_TYPE ||
container.nodeType === DOCUMENT_FRAGMENT_NODE_TYPE
),
'unmountComponentAtNode(...): Target container is not a DOM element.'
);
var reactRootID = getReactRootID(container);
var component = instancesByReactRootID[reactRootID];
if (!component) {
// Check if the node being unmounted was rendered by React, but isn't a
// root node.
var containerHasNonRootReactChild = hasNonRootReactChild(container);
// Check if the container itself is a React root node.
var containerID = internalGetID(container);
var isContainerReactRoot =
containerID &&
containerID ===
ReactInstanceHandles.getReactRootIDFromNodeID(containerID);
if (__DEV__) {
warning(
!containerHasNonRootReactChild,
'unmountComponentAtNode(): The node you\'re attempting to unmount ' +
'was rendered by React and is not a top-level container. %s',
(
isContainerReactRoot ?
'You may have accidentally passed in a React root node instead ' +
'of its container.' :
'Instead, have the parent component update its state and ' +
'rerender in order to remove this component.'
)
);
}
return false;
}
ReactUpdates.batchedUpdates(
unmountComponentFromNode,
component,
container
);
delete instancesByReactRootID[reactRootID];
delete containersByReactRootID[reactRootID];
if (__DEV__) {
delete rootElementsByReactRootID[reactRootID];
}
return true;
},
/**
* Finds the container DOM element that contains React component to which the
* supplied DOM `id` belongs.
*
* @param {string} id The ID of an element rendered by a React component.
* @return {?DOMElement} DOM element that contains the `id`.
*/
findReactContainerForID: function(id) {
var reactRootID = ReactInstanceHandles.getReactRootIDFromNodeID(id);
var container = containersByReactRootID[reactRootID];
if (__DEV__) {
var rootElement = rootElementsByReactRootID[reactRootID];
if (rootElement && rootElement.parentNode !== container) {
warning(
// Call internalGetID here because getID calls isValid which calls
// findReactContainerForID (this function).
internalGetID(rootElement) === reactRootID,
'TVMLMount: Root element ID differed from reactRootID.'
);
var containerChild = container.firstChild;
if (containerChild &&
reactRootID === internalGetID(containerChild)) {
// If the container has a new child with the same ID as the old
// root element, then rootElementsByReactRootID[reactRootID] is
// just stale and needs to be updated. The case that deserves a
// warning is when the container is empty.
rootElementsByReactRootID[reactRootID] = containerChild;
} else {
warning(
false,
'TVMLMount: Root element has been removed from its original ' +
'container. New container: %s',
rootElement.parentNode
);
}
}
}
return container;
},
/**
* Finds an element rendered by React with the supplied ID.
*
* @param {string} id ID of a DOM node in the React component.
* @return {DOMElement} Root DOM node of the React component.
*/
findReactNodeByID: function(id) {
var reactRoot = TVMLMount.findReactContainerForID(id);
return TVMLMount.findComponentRoot(reactRoot, id);
},
/**
* Traverses up the ancestors of the supplied node to find a node that is a
* DOM representation of a React component rendered by this copy of React.
*
* @param {*} node
* @return {?DOMEventTarget}
* @internal
*/
getFirstReactDOM: function(node) {
return findFirstReactDOMImpl(node);
},
/**
* Finds a node with the supplied `targetID` inside of the supplied
* `ancestorNode`. Exploits the ID naming scheme to perform the search
* quickly.
*
* @param {DOMEventTarget} ancestorNode Search from this root.
* @pararm {string} targetID ID of the DOM representation of the component.
* @return {DOMEventTarget} DOM node with the supplied `targetID`.
* @internal
*/
findComponentRoot: function(ancestorNode, targetID) {
var firstChildren = findComponentRootReusableArray;
var childIndex = 0;
var deepestAncestor = findDeepestCachedAncestor(targetID) || ancestorNode;
if (__DEV__) {
// This will throw on the next line; give an early warning
warning(
deepestAncestor != null,
'React can\'t find the root component node for data-reactid value ' +
'`%s`. If you\'re seeing this message, it probably means that ' +
'you\'ve loaded two copies of React on the page. At this time, only ' +
'a single copy of React can be loaded at a time.',
targetID
);
}
firstChildren[0] = deepestAncestor.firstChild;
firstChildren.length = 1;
while (childIndex < firstChildren.length) {
var child = firstChildren[childIndex++];
var targetChild;
while (child) {
var childID = TVMLMount.getID(child);
if (childID) {
// Even if we find the node we're looking for, we finish looping
// through its siblings to ensure they're cached so that we don't have
// to revisit this node again. Otherwise, we make n^2 calls to getID
// when visiting the many children of a single node in order.
if (targetID === childID) {
targetChild = child;
} else if (ReactInstanceHandles.isAncestorIDOf(childID, targetID)) {
// If we find a child whose ID is an ancestor of the given ID,
// then we can be sure that we only want to search the subtree
// rooted at this child, so we can throw out the rest of the
// search state.
firstChildren.length = childIndex = 0;
firstChildren.push(child.firstChild);
}
} else {
// If this child had no ID, then there's a chance that it was
// injected automatically by the browser, as when a `<table>`
// element sprouts an extra `<tbody>` child as a side effect of
// `.innerHTML` parsing. Optimistically continue down this
// branch, but not before examining the other siblings.
firstChildren.push(child.firstChild);
}
child = child.nextSibling;
}
if (targetChild) {
// Emptying firstChildren/findComponentRootReusableArray is
// not necessary for correctness, but it helps the GC reclaim
// any nodes that were left at the end of the search.
firstChildren.length = 0;
return targetChild;
}
}
firstChildren.length = 0;
invariant(
false,
'findComponentRoot(..., %s): Unable to find element. This probably ' +
'means the DOM was unexpectedly mutated (e.g., by the browser), ' +
'usually due to forgetting a <tbody> when using tables, nesting tags ' +
'like <form>, <p>, or <a>, or using non-SVG elements in an <svg> ' +
'parent. ' +
'Try inspecting the child nodes of the element with React ID `%s`.',
targetID,
TVMLMount.getID(ancestorNode)
);
},
_mountImageIntoNode: function(
markup,
container,
shouldReuseMarkup,
transaction
) {
invariant(
container && (
container.nodeType === ELEMENT_NODE_TYPE ||
container.nodeType === DOC_NODE_TYPE ||
container.nodeType === DOCUMENT_FRAGMENT_NODE_TYPE
),
'mountComponentIntoNode(...): Target container is not valid.'
);
if (shouldReuseMarkup) {
var rootElement = getReactRootElementInContainer(container);
if (ReactMarkupChecksum.canReuseMarkup(markup, rootElement)) {
return;
} else {
var checksum = rootElement.getAttribute(
ReactMarkupChecksum.CHECKSUM_ATTR_NAME
);
rootElement.removeAttribute(ReactMarkupChecksum.CHECKSUM_ATTR_NAME);
var rootMarkup = rootElement.outerHTML;
rootElement.setAttribute(
ReactMarkupChecksum.CHECKSUM_ATTR_NAME,
checksum
);
var normalizedMarkup = markup;
if (__DEV__) {
// because rootMarkup is retrieved from the DOM, various normalizations
// will have occurred which will not be present in `markup`. Here,
// insert markup into a <div> or <iframe> depending on the container
// type to perform the same normalizations before comparing.
var normalizer;
if (container.nodeType === ELEMENT_NODE_TYPE) {
normalizer = document.createElement('div');
normalizer.innerHTML = markup;
normalizedMarkup = normalizer.innerHTML;
} else {
normalizer = document.createElement('iframe');
document.body.appendChild(normalizer);
normalizer.contentDocument.write(markup);
normalizedMarkup = normalizer.contentDocument.documentElement.outerHTML;
document.body.removeChild(normalizer);
}
}
var diffIndex = firstDifferenceIndex(normalizedMarkup, rootMarkup);
var difference = ' (client) ' +
normalizedMarkup.substring(diffIndex - 20, diffIndex + 20) +
'\n (server) ' + rootMarkup.substring(diffIndex - 20, diffIndex + 20);
invariant(
container.nodeType !== DOC_NODE_TYPE,
'You\'re trying to render a component to the document using ' +
'server rendering but the checksum was invalid. This usually ' +
'means you rendered a different component type or props on ' +
'the client from the one on the server, or your render() ' +
'methods are impure. React cannot handle this case due to ' +
'cross-browser quirks by rendering at the document root. You ' +
'should look for environment dependent code in your components ' +
'and ensure the props are the same client and server side:\n%s',
difference
);
if (__DEV__) {
warning(
false,
'React attempted to reuse markup in a container but the ' +
'checksum was invalid. This generally means that you are ' +
'using server rendering and the markup generated on the ' +
'server was not what the client was expecting. React injected ' +
'new markup to compensate which works but you have lost many ' +
'of the benefits of server rendering. Instead, figure out ' +
'why the markup being generated is different on the client ' +
'or server:\n%s',
difference
);
}
}
}
var getElement = function(el) {
return el.nodeType === DOC_NODE_TYPE ? el.documentElement : el;
};
var containerElement = getElement(container);
if (transaction.useCreateElement) {
while (containerElement.lastChild) {
containerElement.removeChild(container.lastChild);
}
var node = parser.parseFromString(markup, 'application/xml');
containerElement.appendChild(getElement(node));
} else {
setInnerHTML(containerElement, markup);
}
},
getComponentInstanceFromRootID: function (rootID) {
return instancesByReactRootID[rootID];
},
ownerDocumentContextKey: ownerDocumentContextKey,
/**
* React ID utilities.
*/
getReactRootID: getReactRootID,
getID: getID,
setID: setID,
getNode: getNode,
getNodeFromInstance: getNodeFromInstance,
isValid: isValid,
purgeID: purgeID,
};
ReactPerf.measureMethods(TVMLMount, 'TVMLMount', {
_renderNewRootComponent: '_renderNewRootComponent',
_mountImageIntoNode: '_mountImageIntoNode',
});
module.exports = TVMLMount;
================================================
FILE: src/ReconcileTransaction.js
================================================
'use strict';
var CallbackQueue = require('react/lib/CallbackQueue');
var PooledClass = require('react/lib/PooledClass');
var ReactDOMFeatureFlags = require('react/lib/ReactDOMFeatureFlags');
var Transaction = require('react/lib/Transaction');
var assign = require('react/lib/Object.assign');
/**
* Provides a queue for collecting `componentDidMount` and
* `componentDidUpdate` callbacks during the the transaction.
*/
var ON_DOM_READY_QUEUEING = {
initialize: function() {
this.reactMountReady.reset();
},
close: function() {
this.reactMountReady.notifyAll();
}
};
function ReactTVMLReconcileTransaction(forceHTML) {
this.reinitializeTransaction();
// Only server-side rendering really needs this option
this.renderToStaticMarkup = false;
this.reactMountReady = CallbackQueue.getPooled(null);
this.useCreateElement = !forceHTML && ReactDOMFeatureFlags.useCreateElement;
}
var Mixin = {
/**
* @see Transaction
* @abstract
* @final
* @return {array<object>} List of operation wrap procedures.
* TODO: convert to array<TransactionWrapper>
*/
getTransactionWrappers: function() {
return [ON_DOM_READY_QUEUEING];
},
/**
* @return {object} The queue to collect `onDOMReady` callbacks with.
*/
getReactMountReady: function() {
return this.reactMountReady;
},
/**
* `PooledClass` looks for this, and will invoke this before allowing this
* instance to be reused.
*/
destructor: function() {
CallbackQueue.release(this.reactMountReady);
this.reactMountReady = null;
}
};
assign(ReactTVMLReconcileTransaction.prototype, Transaction.Mixin, Mixin);
PooledClass.addPoolingTo(ReactTVMLReconcileTransaction);
module.exports = ReactTVMLReconcileTransaction;
================================================
FILE: src/TextComponent.js
================================================
'use strict';
var DOMChildrenOperations = require('react/lib/DOMChildrenOperations');
var DOMPropertyOperations = require('react/lib/DOMPropertyOperations');
var IDOperations = require('./IDOperations');
var ReactMount = require('./Mount');
var assign = require('react/lib/Object.assign');
var escapeTextContentForBrowser = require('react/lib/escapeTextContentForBrowser');
var setTextContent = require('react/lib/setTextContent');
var validateDOMNesting = require('react/lib/validateDOMNesting');
/**
* Text nodes violate a couple assumptions that React makes about components:
*
* - When mounting text into the DOM, adjacent text nodes are merged.
* - Text nodes cannot be assigned a React root ID.
*
* This component is used to wrap strings in elements so that they can undergo
* the same reconciliation that is applied to elements.
*
* TODO: Investigate representing React components in the DOM with text nodes.
*
* @class ReactDOMTextComponent
* @extends ReactComponent
* @internal
*/
var ReactDOMTextComponent = function (props) {
// This constructor and its argument is currently used by mocks.
};
assign(ReactDOMTextComponent.prototype, {
/**
* @param {ReactText} text
* @internal
*/
construct: function (text) {
// TODO: This is really a ReactText (ReactNode), not a ReactElement
this._currentElement = text;
this._stringText = '' + text;
// Properties
this._rootNodeID = null;
this._mountIndex = 0;
},
/**
* Creates the markup for this text node. This node is not intended to have
* any features besides containing text content.
*
* @param {string} rootID DOM ID of the root node.
* @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
* @return {string} Markup for this text node.
* @internal
*/
mountComponent: function (rootID, transaction, context) {
if (process.env.NODE_ENV !== 'production') {
if (context[validateDOMNesting.ancestorInfoContextKey]) {
validateDOMNesting('span', null, context[validateDOMNesting.ancestorInfoContextKey]);
}
}
this._rootNodeID = rootID;
if (transaction.useCreateElement) {
var ownerDocument = context[ReactMount.ownerDocumentContextKey];
var el = ownerDocument.createElement('span');
DOMPropertyOperations.setAttributeForID(el, rootID);
// Populate node cache
ReactMount.getID(el);
setTextContent(el, this._stringText);
return el;
} else {
var escapedText = escapeTextContentForBrowser(this._stringText);
if (transaction.renderToStaticMarkup) {
// Normally we'd wrap this in a `span` for the reasons stated above, but
// since this is a situation where React won't take over (static pages),
// we can simply return the text as it is.
return escapedText;
}
return '<span ' + DOMPropertyOperations.createMarkupForID(rootID) + '>' + escapedText + '</span>';
}
},
/**
* Updates this component by updating the text content.
*
* @param {ReactText} nextText The next text content
* @param {ReactReconcileTransaction} transaction
* @internal
*/
receiveComponent: function (nextText, transaction) {
if (nextText !== this._currentElement) {
this._currentElement = nextText;
var nextStringText = '' + nextText;
if (nextStringText !== this._stringText) {
// TODO: Save this as pending props and use performUpdateIfNecessary
// and/or updateComponent to do the actual update for consistency with
// other component types?
this._stringText = nextStringText;
var node = ReactMount.getNode(this._rootNodeID);
DOMChildrenOperations.updateTextContent(node, nextStringText);
}
}
},
unmountComponent: function () {
IDOperations.unmountIDFromEnvironment(this._rootNodeID);
}
});
module.exports = ReactDOMTextComponent;
================================================
FILE: src/globals.js
================================================
if (!global.window) {
global.window = Object.keys(global).map(function(name) {
return {
name: name,
value: global[name]
};
}).reduce(function(win, prop) {
win[prop.name] = prop.value;
return win;
}, {});
}
================================================
FILE: src/instantiateReactComponent.js
================================================
'use strict';
var ReactCompositeComponent = require('react/lib/ReactCompositeComponent');
var ReactEmptyComponent = require('react/lib/ReactEmptyComponent');
var ReactNativeComponent = require('react/lib/ReactNativeComponent');
var assign = require('react/lib/Object.assign');
var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');
// To avoid a cyclic dependency, we create the final class in this module
var ReactCompositeComponentWrapper = function () {};
assign(ReactCompositeComponentWrapper.prototype, ReactCompositeComponent.Mixin, {
_instantiateReactComponent: instantiateReactComponent
});
function getDeclarationErrorAddendum(owner) {
if (owner) {
var name = owner.getName();
if (name) {
return ' Check the render method of `' + name + '`.';
}
}
return '';
}
/**
* Check if the type reference is a known internal type. I.e. not a user
* provided composite type.
*
* @param {function} type
* @return {boolean} Returns true if this is a valid internal type.
*/
function isInternalComponentType(type) {
return typeof type === 'function' && typeof type.prototype !== 'undefined' && typeof type.prototype.mountComponent === 'function' && typeof type.prototype.receiveComponent === 'function';
}
/**
* Given a ReactNode, create an instance that will actually be mounted.
*
* @param {ReactNode} node
* @return {object} A new instance of the element's constructor.
* @protected
*/
function instantiateReactComponent(node) {
var instance;
if (node === null || node === false) {
instance = new ReactEmptyComponent(instantiateReactComponent);
} else if (typeof node === 'object') {
var element = node;
!(element && (typeof element.type === 'function' || typeof element.type === 'string')) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Element type is invalid: expected a string (for built-in components) ' + 'or a class/function (for composite components) but got: %s.%s', element.type == null ? element.type : typeof element.type, getDeclarationErrorAddendum(element._owner)) : invariant(false) : undefined;
// Special case string values
if (typeof element.type === 'string') {
instance = ReactNativeComponent.createInternalComponent(element);
} else if (isInternalComponentType(element.type)) {
// This is temporarily available for custom components that are not string
// representations. I.e. ART. Once those are updated to use the string
// representation, we can drop this code path.
instance = new element.type(element);
} else {
instance = new ReactCompositeComponentWrapper();
}
} else if (typeof node === 'string' || typeof node === 'number') {
instance = ReactNativeComponent.createInstanceForText(node);
} else {
!false ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Encountered invalid React node of type %s', typeof node) : invariant(false) : undefined;
}
if (process.env.NODE_ENV !== 'production') {
process.env.NODE_ENV !== 'production' ? warning(typeof instance.construct === 'function' && typeof instance.mountComponent === 'function' && typeof instance.receiveComponent === 'function' && typeof instance.unmountComponent === 'function', 'Only React Components can be mounted.') : undefined;
}
// Sets up the instance. This can probably just move into the constructor now.
instance.construct(node);
// These two fields are used by the DOM and ART diffing algorithms
// respectively. Instead of using expandos on components, we should be
// storing the state needed by the diffing algorithms elsewhere.
instance._mountIndex = 0;
instance._mountImage = null;
if (process.env.NODE_ENV !== 'production') {
instance._isOwnerNecessary = false;
instance._warnedAboutRefsInRender = false;
}
// Internal instances should fully constructed at this point, so they should
// not get any new fields added to them at this point.
if (process.env.NODE_ENV !== 'production') {
if (Object.preventExtensions) {
Object.preventExtensions(instance);
}
}
return instance;
}
module.exports = instantiateReactComponent;
================================================
FILE: src/menuBar.js
================================================
'use strict';
var hasOwnProperty = require('has-own-prop');
var assign = require('react/lib/Object.assign');
var clone = require('lodash.clone');
var Mount = require('./Mount');
var valueContextKey = '__ReactTVMLMenuBar_value$' + Math.random().toString(36).slice(2);
var itemsToDocuments = {};
var menuBarsToItems = {};
var unMountItems = function(ids) {
ids.forEach(function(id) {
Mount.unmountComponentAtNode(Mount.findReactContainerForID(id));
delete itemsToDocuments[id];
});
};
var updateItemsIndex = function(ev, inst, props) {
var prevChildIds = (function() {
if (!hasOwnProperty(menuBarsToItems, inst._rootNodeID)) {
return [];
}
return clone(menuBarsToItems[inst._rootNodeID]);
})();
var childKeys = Object.keys(inst._renderedChildren);
var nextChildIds = childKeys.map(function (name) {
return inst._renderedChildren[name]._rootNodeID;
});
unMountItems(prevChildIds.filter(function(id) {
return nextChildIds.indexOf(id) < 0;
}));
menuBarsToItems[inst._rootNodeID] = nextChildIds;
};
var getChildrenNodes = function(inst) {
return Object.keys(inst._renderedChildren).map(function (name) {
return inst._renderedChildren[name];
});
};
var getChildrenNodeById = function(inst, id) {
return getChildrenNodes(inst).filter(function(child) {
return child._rootNodeID === id;
}).pop();
};
var render = function(ev, item, doc, feature) {
var el = item._currentElement.props.render(ev);
Mount._renderSubtreeIntoContainer(null, el, doc);
feature.setDocument(doc, ev.target);
}
var mountDocument = function(ev, item, feature) {
var doc = Mount.generateEmptyContainer();
render(ev, item, doc, feature);
itemsToDocuments[item._rootNodeID] = doc;
}
var updateDocument = function(ev, item, feature) {
var doc = itemsToDocuments[item._rootNodeID];
render(ev, item, doc, feature);
};
var onSelect = function(inst, props, context) {
return function(ev) {
updateItemsIndex(ev, inst, props);
var item = getChildrenNodeById(inst, ev.dispatchMarker);
if (hasOwnProperty(item._currentElement.props, 'onSelect')) {
item._currentElement.props.onSelect(ev);
}
var feature = ev.target.parentNode.getFeature('MenuBarDocument');
if (!hasOwnProperty(itemsToDocuments, item._rootNodeID)) {
mountDocument(ev, item, feature);
} else {
updateDocument(ev, item, feature);
}
};
};
var menuBar = {
valueContextKey: valueContextKey,
getNativeProps: function (inst, props, context) {
return assign({}, props, {
onSelect: onSelect(inst, props, context),
value: undefined
});
}
};
module.exports = menuBar;
================================================
FILE: src/react-tvml.js
================================================
try {
require('imports?this=>global!./globals.js');
} catch (err) {}
var Injection = require('react/lib/ReactInjection');
var EventPluginRegistry = require('react/lib/EventPluginRegistry');
var ComponentEnvironment = require('react/lib/ReactComponentEnvironment');
var ReactIsomorphic = require('react/lib/ReactIsomorphic');
var ReactDOMServer = require('react/lib/ReactDOMServer');
var EventPlugin = require('./EventPlugin');
var EventListener = require('./EventListener');
var Component = require('./Component');
var TextComponent = require('./TextComponent');
var ReconcileTransaction = require('./ReconcileTransaction');
var IDOperations = require('./IDOperations');
var Mount = require('./Mount');
var assign = require('react/lib/Object.assign')
EventPluginRegistry._resetEventPlugins();
EventPluginRegistry.registrationNameDependencies = {};
Injection.EventEmitter.injectReactEventListener(EventListener);
/**
* Inject modules for resolving DOM hierarchy and plugin ordering.
*/
Injection.EventPluginHub.injectEventPluginOrder(['EventPlugin']);
// Injection.EventPluginHub.injectInstanceHandle(ReactInstanceHandles);
Injection.EventPluginHub.injectMount(Mount);
/**
* Some important event plugins included by default (without having to require
* them).
*/
Injection.EventPluginHub.injectEventPluginsByName({
EventPlugin: EventPlugin
});
Injection.NativeComponent.injectGenericComponentClass(Component);
Injection.NativeComponent.injectTextComponentClass(TextComponent);
// Injection.Class.injectMixin(ReactBrowserComponentMixin);
// Injection.DOMProperty.injectDOMPropertyConfig(HTMLDOMPropertyConfig);
// Injection.DOMProperty.injectDOMPropertyConfig(SVGDOMPropertyConfig);
//
// Injection.EmptyComponent.injectEmptyComponent('noscript');
Injection.Updates.injectReconcileTransaction(ReconcileTransaction);
// Injection.Updates.injectBatchingStrategy(ReactDefaultBatchingStrategy);
// Injection.RootIndex.injectCreateReactRootIndex(
// ExecutionEnvironment.canUseDOM ? ClientReactRootIndex.createReactRootIndex : ServerReactRootIndex.createReactRootIndex
// );
ComponentEnvironment.unmountIDFromEnvironment = IDOperations.unmountIDFromEnvironment;
ComponentEnvironment.replaceNodeWithMarkupByID = IDOperations.replaceNodeWithMarkupByID;
ComponentEnvironment.processChildrenUpdates = IDOperations.processChildrenUpdates;
Injection.EventEmitter.injectReactEventListener(EventListener);
module.exports = assign(assign({}, ReactIsomorphic), {
findDOMNode: require('react/lib/findDOMNode'),
render: Mount.render,
unmountComponentAtNode: Mount.unmountComponentAtNode,
// Server
renderToString: ReactDOMServer.renderToString,
renderToStaticMarkup: ReactDOMServer.renderToStaticMarkup
});
gitextract_asnex2vt/
├── .gitignore
├── .tern-project
├── package.json
├── readme.md
└── src/
├── Component.js
├── EventEmitter.js
├── EventListener.js
├── EventPlugin.js
├── IDOperations.js
├── Mount.js
├── ReconcileTransaction.js
├── TextComponent.js
├── globals.js
├── instantiateReactComponent.js
├── menuBar.js
└── react-tvml.js
SYMBOL INDEX (44 symbols across 6 files)
FILE: src/Component.js
function getDeclarationErrorAddendum (line 57) | function getDeclarationErrorAddendum(internalInstance) {
function legacyGetDOMNode (line 71) | function legacyGetDOMNode() {
function legacyIsMounted (line 84) | function legacyIsMounted() {
function legacySetStateEtc (line 96) | function legacySetStateEtc() {
function legacySetProps (line 108) | function legacySetProps(partialProps, callback) {
function legacyReplaceProps (line 127) | function legacyReplaceProps(partialProps, callback) {
function checkAndWarnForMutatedStyle (line 148) | function checkAndWarnForMutatedStyle(style1, style2, component) {
function assertValidProps (line 187) | function assertValidProps(component, props) {
function enqueuePutListener (line 242) | function enqueuePutListener(id, registrationName, listener, transaction) {
function putListener (line 255) | function putListener(id, registrationName, listener, transaction) {
function putListener (line 274) | function putListener() {
function postUpdateSelectWrapper (line 311) | function postUpdateSelectWrapper() {
function validateDangerousTag (line 346) | function validateDangerousTag(tag) {
function processChildContext (line 353) | function processChildContext(context, inst) {
function isCustomComponent (line 364) | function isCustomComponent(tagName, props) {
function ReactTVMLComponent (line 382) | function ReactTVMLComponent(tag) {
FILE: src/EventEmitter.js
function getListeningForDocument (line 86) | function getListeningForDocument(mountAt) {
FILE: src/EventListener.js
function findParent (line 24) | function findParent(node) {
function TopLevelCallbackBookKeeping (line 36) | function TopLevelCallbackBookKeeping(topLevelType, nativeEvent) {
function handleTopLevelImpl (line 50) | function handleTopLevelImpl(bookKeeping) {
function handleTopLevelWithoutPath (line 66) | function handleTopLevelWithoutPath(bookKeeping) {
function handleTopLevelWithPath (line 87) | function handleTopLevelWithPath(bookKeeping) {
function scrollValueMonitor (line 120) | function scrollValueMonitor(cb) {
FILE: src/Mount.js
function firstDifferenceIndex (line 60) | function firstDifferenceIndex(string1, string2) {
function getReactRootElementInContainer (line 75) | function getReactRootElementInContainer(container) {
function getReactRootID (line 87) | function getReactRootID(container) {
function getID (line 102) | function getID(node) {
function internalGetID (line 124) | function internalGetID(node) {
function setID (line 137) | function setID(node, id) {
function getNode (line 153) | function getNode(id) {
function getNodeFromInstance (line 167) | function getNodeFromInstance(instance) {
function isValid (line 188) | function isValid(node, id) {
function purgeID (line 210) | function purgeID(id) {
function findDeepestCachedAncestorImpl (line 215) | function findDeepestCachedAncestorImpl(ancestorID) {
function findDeepestCachedAncestor (line 229) | function findDeepestCachedAncestor(targetID) {
function mountComponentIntoNode (line 250) | function mountComponentIntoNode(
function batchedMountComponentIntoNode (line 294) | function batchedMountComponentIntoNode(
function unmountComponentFromNode (line 326) | function unmountComponentFromNode(instance, container) {
function hasNonRootReactChild (line 349) | function hasNonRootReactChild(node) {
function findFirstReactDOMImpl (line 359) | function findFirstReactDOMImpl(node) {
FILE: src/ReconcileTransaction.js
function ReactTVMLReconcileTransaction (line 24) | function ReactTVMLReconcileTransaction(forceHTML) {
FILE: src/instantiateReactComponent.js
function getDeclarationErrorAddendum (line 17) | function getDeclarationErrorAddendum(owner) {
function isInternalComponentType (line 34) | function isInternalComponentType(type) {
function instantiateReactComponent (line 45) | function instantiateReactComponent(node) {
Condensed preview — 16 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (105K chars).
[
{
"path": ".gitignore",
"chars": 1812,
"preview": "# Created by https://www.gitignore.io\n\n### Node ###\n# Logs\nlogs\n*.log\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for"
},
{
"path": ".tern-project",
"chars": 83,
"preview": "{\n \"libs\": [\"ecma6\"],\n \"plugins\": {\n \"doc_comment\": true,\n \"node\": {}\n }\n}"
},
{
"path": "package.json",
"chars": 644,
"preview": "{\n \"name\": \"react-tvml\",\n \"version\": \"1.0.4\",\n \"license\": \"BSD\",\n \"description\": \"react bindings to Apple's TVJS & T"
},
{
"path": "readme.md",
"chars": 1124,
"preview": "# react-tvml\n\n**this is a very alpha release**\n\nReact bindings to Apple's [TVJS and TVML](https://developer.apple.com/li"
},
{
"path": "src/Component.js",
"chars": 28282,
"preview": "'use strict';\n\n// TODO: validate children elements (or parents?). Ex: <badge> can only be a child of\n// - buttonLockup\n"
},
{
"path": "src/EventEmitter.js",
"chars": 5991,
"preview": "// For components like lockup, listItemLockup you can expect select, play, highlight, holdselect. For Buttons only selec"
},
{
"path": "src/EventListener.js",
"chars": 7017,
"preview": "'use strict';\n\nvar EventListener = require('fbjs/lib/EventListener');\nvar ExecutionEnvironment = require('fbjs/lib/Execu"
},
{
"path": "src/EventPlugin.js",
"chars": 2075,
"preview": "'use strict';\n\nvar EventPropagators = require('react/lib/EventPropagators');\nvar SyntheticEvent = require('react/lib/Syn"
},
{
"path": "src/IDOperations.js",
"chars": 1858,
"preview": "'use strict';\n\nvar DOMChildrenOperations = require('react/lib/DOMChildrenOperations');\nvar DOMPropertyOperations = requi"
},
{
"path": "src/Mount.js",
"chars": 36281,
"preview": "'use strict';\n\nvar __DEV__ = false; // process.env.NODE_ENV !== 'production';\n\nvar DOMProperty = require('react/lib/DOMP"
},
{
"path": "src/ReconcileTransaction.js",
"chars": 1752,
"preview": "'use strict';\n\nvar CallbackQueue = require('react/lib/CallbackQueue');\nvar PooledClass = require('react/lib/PooledClass'"
},
{
"path": "src/TextComponent.js",
"chars": 3927,
"preview": "'use strict';\n\nvar DOMChildrenOperations = require('react/lib/DOMChildrenOperations');\nvar DOMPropertyOperations = requi"
},
{
"path": "src/globals.js",
"chars": 239,
"preview": "if (!global.window) {\n global.window = Object.keys(global).map(function(name) {\n return {\n name: name,\n va"
},
{
"path": "src/instantiateReactComponent.js",
"chars": 4175,
"preview": "'use strict';\n\nvar ReactCompositeComponent = require('react/lib/ReactCompositeComponent');\nvar ReactEmptyComponent = req"
},
{
"path": "src/menuBar.js",
"chars": 2664,
"preview": "'use strict';\n\nvar hasOwnProperty = require('has-own-prop');\nvar assign = require('react/lib/Object.assign');\nvar clone "
},
{
"path": "src/react-tvml.js",
"chars": 2728,
"preview": "try {\n require('imports?this=>global!./globals.js');\n} catch (err) {}\n\nvar Injection = require('react/lib/ReactInjectio"
}
]
About this extraction
This page contains the full source code of the ramitos/react-tvml GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 16 files (98.3 KB), approximately 23.9k tokens, and a symbol index with 44 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.