From ef26439e77f147a3b2460e75b28baecadd54dab5 Mon Sep 17 00:00:00 2001 From: ChieveiT Date: Tue, 16 May 2017 17:12:06 +0800 Subject: [PATCH] export all babel-compiled src/modules to fix #110 --- .gitignore | 1 - lib/CSSPropertyOperations.js | 129 +++++ lib/Children.js | 124 ++++ lib/Component.js | 243 ++++++++ lib/DOM.js | 22 + lib/DOMConfig.js | 549 ++++++++++++++++++ lib/DOMPropertyOperations.js | 84 +++ lib/LinkedStateMixin.js | 35 ++ lib/PropTypes.js | 29 + lib/PureComponent.js | 30 + lib/ReactCSSTransitionGroup.js | 81 +++ lib/ReactCSSTransitionGroupChild.js | 164 ++++++ lib/ReactChildren.js | 187 ++++++ lib/ReactComponentWithPureRenderMixin.js | 45 ++ lib/ReactDOM.js | 130 +++++ lib/ReactFragment.js | 56 ++ lib/ReactLink.js | 68 +++ lib/ReactStateSetters.js | 104 ++++ lib/ReactTransitionChildMapping.js | 98 ++++ lib/ReactTransitionEvents.js | 105 ++++ lib/ReactTransitionGroup.js | 203 +++++++ lib/ReactWithAddons.js | 50 ++ lib/constant.js | 36 ++ lib/createClass.js | 119 ++++ lib/createElement.js | 116 ++++ lib/event-system.js | 187 ++++++ lib/index.js | 64 ++ lib/react-tap-event-plugin.js | 9 + lib/renderSubtreeIntoContainer.js | 15 + lib/shallowCompare.js | 23 + lib/shallowEqual.js | 34 ++ lib/update.js | 108 ++++ lib/util.js | 165 ++++++ lib/utils/CSSCore.js | 97 ++++ lib/utils/PooledClass.js | 119 ++++ lib/utils/emptyFunction.js | 38 ++ lib/utils/escapeTextContentForBrowser.js | 38 ++ lib/utils/flattenChildren.js | 43 ++ lib/utils/getIteratorFn.js | 40 ++ lib/utils/keyOf.js | 35 ++ lib/utils/quoteAttributeValueForBrowser.js | 26 + lib/utils/shallowEqual.js | 50 ++ lib/utils/traverseAllChildren.js | 169 ++++++ lib/virtual-dom.js | 643 +++++++++++++++++++++ package.json | 3 +- 45 files changed, 4712 insertions(+), 2 deletions(-) create mode 100644 lib/CSSPropertyOperations.js create mode 100644 lib/Children.js create mode 100644 lib/Component.js create mode 100644 lib/DOM.js create mode 100644 lib/DOMConfig.js create mode 100644 lib/DOMPropertyOperations.js create mode 100644 lib/LinkedStateMixin.js create mode 100644 lib/PropTypes.js create mode 100644 lib/PureComponent.js create mode 100644 lib/ReactCSSTransitionGroup.js create mode 100644 lib/ReactCSSTransitionGroupChild.js create mode 100644 lib/ReactChildren.js create mode 100644 lib/ReactComponentWithPureRenderMixin.js create mode 100644 lib/ReactDOM.js create mode 100644 lib/ReactFragment.js create mode 100644 lib/ReactLink.js create mode 100644 lib/ReactStateSetters.js create mode 100644 lib/ReactTransitionChildMapping.js create mode 100644 lib/ReactTransitionEvents.js create mode 100644 lib/ReactTransitionGroup.js create mode 100644 lib/ReactWithAddons.js create mode 100644 lib/constant.js create mode 100644 lib/createClass.js create mode 100644 lib/createElement.js create mode 100644 lib/event-system.js create mode 100644 lib/index.js create mode 100644 lib/react-tap-event-plugin.js create mode 100644 lib/renderSubtreeIntoContainer.js create mode 100644 lib/shallowCompare.js create mode 100644 lib/shallowEqual.js create mode 100644 lib/update.js create mode 100644 lib/util.js create mode 100644 lib/utils/CSSCore.js create mode 100644 lib/utils/PooledClass.js create mode 100644 lib/utils/emptyFunction.js create mode 100644 lib/utils/escapeTextContentForBrowser.js create mode 100644 lib/utils/flattenChildren.js create mode 100644 lib/utils/getIteratorFn.js create mode 100644 lib/utils/keyOf.js create mode 100644 lib/utils/quoteAttributeValueForBrowser.js create mode 100644 lib/utils/shallowEqual.js create mode 100644 lib/utils/traverseAllChildren.js create mode 100644 lib/virtual-dom.js diff --git a/.gitignore b/.gitignore index a7d12a3..de81349 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,3 @@ node_modules coverage _book -lib diff --git a/lib/CSSPropertyOperations.js b/lib/CSSPropertyOperations.js new file mode 100644 index 0000000..7cf7fdf --- /dev/null +++ b/lib/CSSPropertyOperations.js @@ -0,0 +1,129 @@ +/** + * CSS Property Operations + */ + +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.setStyle = setStyle; +exports.removeStyle = removeStyle; +exports.patchStyle = patchStyle; + +function setStyle(elemStyle, styles) { + for (var styleName in styles) { + if (styles.hasOwnProperty(styleName)) { + setStyleValue(elemStyle, styleName, styles[styleName]); + } + } +} + +function removeStyle(elemStyle, styles) { + for (var styleName in styles) { + if (styles.hasOwnProperty(styleName)) { + elemStyle[styleName] = ''; + } + } +} + +function patchStyle(elemStyle, style, newStyle) { + if (style === newStyle) { + return; + } + if (!newStyle && style) { + removeStyle(elemStyle, style); + return; + } else if (newStyle && !style) { + setStyle(elemStyle, newStyle); + return; + } + + for (var key in style) { + if (newStyle.hasOwnProperty(key)) { + if (newStyle[key] !== style[key]) { + setStyleValue(elemStyle, key, newStyle[key]); + } + } else { + elemStyle[key] = ''; + } + } + for (var key in newStyle) { + if (!style.hasOwnProperty(key)) { + setStyleValue(elemStyle, key, newStyle[key]); + } + } +} + +/** + * CSS properties which accept numbers but are not in units of "px". + */ +var isUnitlessNumber = { + animationIterationCount: 1, + borderImageOutset: 1, + borderImageSlice: 1, + borderImageWidth: 1, + boxFlex: 1, + boxFlexGroup: 1, + boxOrdinalGroup: 1, + columnCount: 1, + flex: 1, + flexGrow: 1, + flexPositive: 1, + flexShrink: 1, + flexNegative: 1, + flexOrder: 1, + gridRow: 1, + gridColumn: 1, + fontWeight: 1, + lineClamp: 1, + lineHeight: 1, + opacity: 1, + order: 1, + orphans: 1, + tabSize: 1, + widows: 1, + zIndex: 1, + zoom: 1, + + // SVG-related properties + fillOpacity: 1, + floodOpacity: 1, + stopOpacity: 1, + strokeDasharray: 1, + strokeDashoffset: 1, + strokeMiterlimit: 1, + strokeOpacity: 1, + strokeWidth: 1 +}; + +function prefixKey(prefix, key) { + return prefix + key.charAt(0).toUpperCase() + key.substring(1); +} + +var prefixes = ['Webkit', 'ms', 'Moz', 'O']; + +Object.keys(isUnitlessNumber).forEach(function (prop) { + prefixes.forEach(function (prefix) { + isUnitlessNumber[prefixKey(prefix, prop)] = 1; + }); +}); + +var RE_NUMBER = /^-?\d+(\.\d+)?$/; +function setStyleValue(elemStyle, styleName, styleValue) { + + if (!isUnitlessNumber[styleName] && RE_NUMBER.test(styleValue)) { + elemStyle[styleName] = styleValue + 'px'; + return; + } + + if (styleName === 'float') { + styleName = 'cssFloat'; + } + + if (styleValue == null || typeof styleValue === 'boolean') { + styleValue = ''; + } + + elemStyle[styleName] = styleValue; +} \ No newline at end of file diff --git a/lib/Children.js b/lib/Children.js new file mode 100644 index 0000000..374df69 --- /dev/null +++ b/lib/Children.js @@ -0,0 +1,124 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.only = only; +exports.forEach = forEach; +exports.map = map; +exports.count = count; +exports.toArray = toArray; + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } + +var _util = require('./util'); + +var _ = _interopRequireWildcard(_util); + +var _createElement = require('./createElement'); + +function only(children) { + if ((0, _createElement.isValidElement)(children)) { + return children; + } + throw new Error('expect only one child'); +} + +function forEach(children, iteratee, context) { + if (children == null) { + return children; + } + var index = 0; + if (_.isArr(children)) { + _.flatEach(children, function (child) { + // from traverseAllChildrenImpl in react + var type = typeof child; + if (type === 'undefined' || type === 'boolean') { + // All of the above are perceived as null. + child = null; + } + + iteratee.call(context, child, index++); + }); + } else { + // from traverseAllChildrenImpl in react + var type = typeof children; + if (type === 'undefined' || type === 'boolean') { + // All of the above are perceived as null. + children = null; + } + iteratee.call(context, children, index); + } +} + +function map(children, iteratee, context) { + if (children == null) { + return children; + } + var store = []; + var keyMap = {}; + forEach(children, function (child, index) { + var data = {}; + data.child = iteratee.call(context, child, index) || child; + data.isEqual = data.child === child; + var key = data.key = getKey(child, index); + if (keyMap.hasOwnProperty(key)) { + keyMap[key] += 1; + } else { + keyMap[key] = 0; + } + data.index = keyMap[key]; + _.addItem(store, data); + }); + var result = []; + store.forEach(function (_ref) { + var child = _ref.child; + var key = _ref.key; + var index = _ref.index; + var isEqual = _ref.isEqual; + + if (child == null || typeof child === 'boolean') { + return; + } + if (!(0, _createElement.isValidElement)(child) || key == null) { + _.addItem(result, child); + return; + } + if (keyMap[key] !== 0) { + key += ':' + index; + } + if (!isEqual) { + key = escapeUserProvidedKey(child.key || '') + '/' + key; + } + child = (0, _createElement.cloneElement)(child, { key: key }); + _.addItem(result, child); + }); + return result; +} + +function count(children) { + var count = 0; + forEach(children, function () { + count++; + }); + return count; +} + +function toArray(children) { + return map(children, _.identity) || []; +} + +function getKey(child, index) { + var key = undefined; + if ((0, _createElement.isValidElement)(child) && typeof child.key === 'string') { + key = '.$' + child.key; + } else { + key = '.' + index.toString(36); + } + return key; +} + +var userProvidedKeyEscapeRegex = /\/(?!\/)/g; +function escapeUserProvidedKey(text) { + return ('' + text).replace(userProvidedKeyEscapeRegex, '//'); +} \ No newline at end of file diff --git a/lib/Component.js b/lib/Component.js new file mode 100644 index 0000000..fe9a1a5 --- /dev/null +++ b/lib/Component.js @@ -0,0 +1,243 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports['default'] = Component; + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } + +var _util = require('./util'); + +var _ = _interopRequireWildcard(_util); + +var _virtualDom = require('./virtual-dom'); + +var updateQueue = { + updaters: [], + isPending: false, + add: function add(updater) { + _.addItem(this.updaters, updater); + }, + batchUpdate: function batchUpdate() { + if (this.isPending) { + return; + } + this.isPending = true; + /* + each updater.update may add new updater to updateQueue + clear them with a loop + event bubbles from bottom-level to top-level + reverse the updater order can merge some props and state and reduce the refresh times + see Updater.update method below to know why + */ + var updaters = this.updaters; + + var updater = undefined; + while (updater = updaters.pop()) { + updater.updateComponent(); + } + this.isPending = false; + } +}; + +exports.updateQueue = updateQueue; +function Updater(instance) { + this.instance = instance; + this.pendingStates = []; + this.pendingCallbacks = []; + this.isPending = false; + this.nextProps = this.nextContext = null; + this.clearCallbacks = this.clearCallbacks.bind(this); +} + +Updater.prototype = { + emitUpdate: function emitUpdate(nextProps, nextContext) { + this.nextProps = nextProps; + this.nextContext = nextContext; + // receive nextProps!! should update immediately + nextProps || !updateQueue.isPending ? this.updateComponent() : updateQueue.add(this); + }, + updateComponent: function updateComponent() { + var instance = this.instance; + var pendingStates = this.pendingStates; + var nextProps = this.nextProps; + var nextContext = this.nextContext; + + if (nextProps || pendingStates.length > 0) { + nextProps = nextProps || instance.props; + nextContext = nextContext || instance.context; + this.nextProps = this.nextContext = null; + // merge the nextProps and nextState and update by one time + shouldUpdate(instance, nextProps, this.getState(), nextContext, this.clearCallbacks); + } + }, + addState: function addState(nextState) { + if (nextState) { + _.addItem(this.pendingStates, nextState); + if (!this.isPending) { + this.emitUpdate(); + } + } + }, + replaceState: function replaceState(nextState) { + var pendingStates = this.pendingStates; + + pendingStates.pop(); + // push special params to point out should replace state + _.addItem(pendingStates, [nextState]); + }, + getState: function getState() { + var instance = this.instance; + var pendingStates = this.pendingStates; + var state = instance.state; + var props = instance.props; + + if (pendingStates.length) { + state = _.extend({}, state); + pendingStates.forEach(function (nextState) { + var isReplace = _.isArr(nextState); + if (isReplace) { + nextState = nextState[0]; + } + if (_.isFn(nextState)) { + nextState = nextState.call(instance, state, props); + } + // replace state + if (isReplace) { + state = _.extend({}, nextState); + } else { + _.extend(state, nextState); + } + }); + pendingStates.length = 0; + } + return state; + }, + clearCallbacks: function clearCallbacks() { + var pendingCallbacks = this.pendingCallbacks; + var instance = this.instance; + + if (pendingCallbacks.length > 0) { + this.pendingCallbacks = []; + pendingCallbacks.forEach(function (callback) { + return callback.call(instance); + }); + } + }, + addCallback: function addCallback(callback) { + if (_.isFn(callback)) { + _.addItem(this.pendingCallbacks, callback); + } + } +}; + +function Component(props, context) { + this.$updater = new Updater(this); + this.$cache = { isMounted: false }; + this.props = props; + this.state = {}; + this.refs = {}; + this.context = context; +} + +var ReactComponentSymbol = {}; + +Component.prototype = { + constructor: Component, + isReactComponent: ReactComponentSymbol, + // getChildContext: _.noop, + // componentWillUpdate: _.noop, + // componentDidUpdate: _.noop, + // componentWillReceiveProps: _.noop, + // componentWillMount: _.noop, + // componentDidMount: _.noop, + // componentWillUnmount: _.noop, + // shouldComponentUpdate(nextProps, nextState) { + // return true + // }, + forceUpdate: function forceUpdate(callback) { + var $updater = this.$updater; + var $cache = this.$cache; + var props = this.props; + var state = this.state; + var context = this.context; + + if (!$cache.isMounted) { + return; + } + // if updater is pending, add state to trigger nexttick update + if ($updater.isPending) { + $updater.addState(state); + return; + } + var nextProps = $cache.props || props; + var nextState = $cache.state || state; + var nextContext = $cache.context || context; + var parentContext = $cache.parentContext; + var node = $cache.node; + var vnode = $cache.vnode; + $cache.props = $cache.state = $cache.context = null; + $updater.isPending = true; + if (this.componentWillUpdate) { + this.componentWillUpdate(nextProps, nextState, nextContext); + } + this.state = nextState; + this.props = nextProps; + this.context = nextContext; + var newVnode = (0, _virtualDom.renderComponent)(this); + var newNode = (0, _virtualDom.compareTwoVnodes)(vnode, newVnode, node, (0, _virtualDom.getChildContext)(this, parentContext)); + if (newNode !== node) { + newNode.cache = newNode.cache || {}; + (0, _virtualDom.syncCache)(newNode.cache, node.cache, newNode); + } + $cache.vnode = newVnode; + $cache.node = newNode; + (0, _virtualDom.clearPending)(); + if (this.componentDidUpdate) { + this.componentDidUpdate(props, state, context); + } + if (callback) { + callback.call(this); + } + $updater.isPending = false; + $updater.emitUpdate(); + }, + setState: function setState(nextState, callback) { + var $updater = this.$updater; + + $updater.addCallback(callback); + $updater.addState(nextState); + }, + replaceState: function replaceState(nextState, callback) { + var $updater = this.$updater; + + $updater.addCallback(callback); + $updater.replaceState(nextState); + }, + getDOMNode: function getDOMNode() { + var node = this.$cache.node; + return node && node.nodeName === '#comment' ? null : node; + }, + isMounted: function isMounted() { + return this.$cache.isMounted; + } +}; + +function shouldUpdate(component, nextProps, nextState, nextContext, callback) { + var shouldComponentUpdate = true; + if (component.shouldComponentUpdate) { + shouldComponentUpdate = component.shouldComponentUpdate(nextProps, nextState, nextContext); + } + if (shouldComponentUpdate === false) { + component.props = nextProps; + component.state = nextState; + component.context = nextContext || {}; + return; + } + var cache = component.$cache; + cache.props = nextProps; + cache.state = nextState; + cache.context = nextContext || {}; + component.forceUpdate(callback); +} \ No newline at end of file diff --git a/lib/DOM.js b/lib/DOM.js new file mode 100644 index 0000000..8c94f52 --- /dev/null +++ b/lib/DOM.js @@ -0,0 +1,22 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } + +var _util = require('./util'); + +var _ = _interopRequireWildcard(_util); + +var _createElement = require('./createElement'); + +var tagNames = 'a|abbr|address|area|article|aside|audio|b|base|bdi|bdo|big|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|data|datalist|dd|del|details|dfn|dialog|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|main|map|mark|menu|menuitem|meta|meter|nav|noscript|object|ol|optgroup|option|output|p|param|picture|pre|progress|q|rp|rt|ruby|s|samp|script|section|select|small|source|span|strong|style|sub|summary|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|track|u|ul|var|video|wbr|circle|clipPath|defs|ellipse|g|image|line|linearGradient|mask|path|pattern|polygon|polyline|radialGradient|rect|stop|svg|text|tspan'; +var DOM = {}; +tagNames.split('|').forEach(function (tagName) { + DOM[tagName] = (0, _createElement.createFactory)(tagName); +}); + +exports['default'] = DOM; +module.exports = exports['default']; \ No newline at end of file diff --git a/lib/DOMConfig.js b/lib/DOMConfig.js new file mode 100644 index 0000000..d1702c4 --- /dev/null +++ b/lib/DOMConfig.js @@ -0,0 +1,549 @@ +/** + * DOM config + */ + +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +var ATTRIBUTE_NAME_START_CHAR = ':A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD'; +var ATTRIBUTE_NAME_CHAR = ATTRIBUTE_NAME_START_CHAR + '\\-.0-9\\uB7\\u0300-\\u036F\\u203F-\\u2040'; + +var VALID_ATTRIBUTE_NAME_REGEX = new RegExp('^[' + ATTRIBUTE_NAME_START_CHAR + '][' + ATTRIBUTE_NAME_CHAR + ']*$'); + +exports.VALID_ATTRIBUTE_NAME_REGEX = VALID_ATTRIBUTE_NAME_REGEX; +var isCustomAttribute = RegExp.prototype.test.bind(new RegExp('^(data|aria)-[' + ATTRIBUTE_NAME_CHAR + ']*$')); +exports.isCustomAttribute = isCustomAttribute; +// will merge some data in properties below +var properties = {}; + +exports.properties = properties; +/** + * Mapping from normalized, camelcased property names to a configuration that + * specifies how the associated DOM property should be accessed or rendered. + */ +var MUST_USE_PROPERTY = 0x1; +var HAS_BOOLEAN_VALUE = 0x4; +var HAS_NUMERIC_VALUE = 0x8; +var HAS_POSITIVE_NUMERIC_VALUE = 0x10 | 0x8; +var HAS_OVERLOADED_BOOLEAN_VALUE = 0x20; + +// html config +var HTMLDOMPropertyConfig = { + props: { + /** + * Standard Properties + */ + accept: 0, + acceptCharset: 0, + accessKey: 0, + action: 0, + allowFullScreen: HAS_BOOLEAN_VALUE, + allowTransparency: 0, + alt: 0, + async: HAS_BOOLEAN_VALUE, + autoComplete: 0, + autoFocus: HAS_BOOLEAN_VALUE, + autoPlay: HAS_BOOLEAN_VALUE, + capture: HAS_BOOLEAN_VALUE, + cellPadding: 0, + cellSpacing: 0, + charSet: 0, + challenge: 0, + checked: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, + cite: 0, + classID: 0, + className: 0, + cols: HAS_POSITIVE_NUMERIC_VALUE, + colSpan: 0, + content: 0, + contentEditable: 0, + contextMenu: 0, + controls: HAS_BOOLEAN_VALUE, + coords: 0, + crossOrigin: 0, + data: 0, // For `` acts as `src`. + dateTime: 0, + 'default': HAS_BOOLEAN_VALUE, + // not in regular react, they did it in other way + defaultValue: MUST_USE_PROPERTY, + // not in regular react, they did it in other way + defaultChecked: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, + defer: HAS_BOOLEAN_VALUE, + dir: 0, + disabled: HAS_BOOLEAN_VALUE, + download: HAS_OVERLOADED_BOOLEAN_VALUE, + draggable: 0, + encType: 0, + form: 0, + formAction: 0, + formEncType: 0, + formMethod: 0, + formNoValidate: HAS_BOOLEAN_VALUE, + formTarget: 0, + frameBorder: 0, + headers: 0, + height: 0, + hidden: HAS_BOOLEAN_VALUE, + high: 0, + href: 0, + hrefLang: 0, + htmlFor: 0, + httpEquiv: 0, + icon: 0, + id: 0, + inputMode: 0, + integrity: 0, + is: 0, + keyParams: 0, + keyType: 0, + kind: 0, + label: 0, + lang: 0, + list: 0, + loop: HAS_BOOLEAN_VALUE, + low: 0, + manifest: 0, + marginHeight: 0, + marginWidth: 0, + max: 0, + maxLength: 0, + media: 0, + mediaGroup: 0, + method: 0, + min: 0, + minLength: 0, + // Caution; `option.selected` is not updated if `select.multiple` is + // disabled with `removeAttribute`. + multiple: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, + muted: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, + name: 0, + nonce: 0, + noValidate: HAS_BOOLEAN_VALUE, + open: HAS_BOOLEAN_VALUE, + optimum: 0, + pattern: 0, + placeholder: 0, + poster: 0, + preload: 0, + profile: 0, + radioGroup: 0, + readOnly: HAS_BOOLEAN_VALUE, + referrerPolicy: 0, + rel: 0, + required: HAS_BOOLEAN_VALUE, + reversed: HAS_BOOLEAN_VALUE, + role: 0, + rows: HAS_POSITIVE_NUMERIC_VALUE, + rowSpan: HAS_NUMERIC_VALUE, + sandbox: 0, + scope: 0, + scoped: HAS_BOOLEAN_VALUE, + scrolling: 0, + seamless: HAS_BOOLEAN_VALUE, + selected: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, + shape: 0, + size: HAS_POSITIVE_NUMERIC_VALUE, + sizes: 0, + span: HAS_POSITIVE_NUMERIC_VALUE, + spellCheck: 0, + src: 0, + srcDoc: 0, + srcLang: 0, + srcSet: 0, + start: HAS_NUMERIC_VALUE, + step: 0, + style: 0, + summary: 0, + tabIndex: 0, + target: 0, + title: 0, + // Setting .type throws on non- tags + type: 0, + useMap: 0, + value: MUST_USE_PROPERTY, + width: 0, + wmode: 0, + wrap: 0, + + /** + * RDFa Properties + */ + about: 0, + datatype: 0, + inlist: 0, + prefix: 0, + // property is also supported for OpenGraph in meta tags. + property: 0, + resource: 0, + 'typeof': 0, + vocab: 0, + + /** + * Non-standard Properties + */ + // autoCapitalize and autoCorrect are supported in Mobile Safari for + // keyboard hints. + autoCapitalize: 0, + autoCorrect: 0, + // autoSave allows WebKit/Blink to persist values of input fields on page reloads + autoSave: 0, + // color is for Safari mask-icon link + color: 0, + // itemProp, itemScope, itemType are for + // Microdata support. See http://schema.org/docs/gs.html + itemProp: 0, + itemScope: HAS_BOOLEAN_VALUE, + itemType: 0, + // itemID and itemRef are for Microdata support as well but + // only specified in the WHATWG spec document. See + // https://html.spec.whatwg.org/multipage/microdata.html#microdata-dom-api + itemID: 0, + itemRef: 0, + // results show looking glass icon and recent searches on input + // search fields in WebKit/Blink + results: 0, + // IE-only attribute that specifies security restrictions on an iframe + // as an alternative to the sandbox attribute on IE<10 + security: 0, + // IE-only attribute that controls focus behavior + unselectable: 0 + }, + attrNS: {}, + domAttrs: { + acceptCharset: 'accept-charset', + className: 'class', + htmlFor: 'for', + httpEquiv: 'http-equiv' + }, + domProps: {} +}; + +// svg config +var xlink = 'http://www.w3.org/1999/xlink'; +var xml = 'http://www.w3.org/XML/1998/namespace'; + +// We use attributes for everything SVG so let's avoid some duplication and run +// code instead. +// The following are all specified in the HTML config already so we exclude here. +// - class (as className) +// - color +// - height +// - id +// - lang +// - max +// - media +// - method +// - min +// - name +// - style +// - target +// - type +// - width +var ATTRS = { + accentHeight: 'accent-height', + accumulate: 0, + additive: 0, + alignmentBaseline: 'alignment-baseline', + allowReorder: 'allowReorder', + alphabetic: 0, + amplitude: 0, + arabicForm: 'arabic-form', + ascent: 0, + attributeName: 'attributeName', + attributeType: 'attributeType', + autoReverse: 'autoReverse', + azimuth: 0, + baseFrequency: 'baseFrequency', + baseProfile: 'baseProfile', + baselineShift: 'baseline-shift', + bbox: 0, + begin: 0, + bias: 0, + by: 0, + calcMode: 'calcMode', + capHeight: 'cap-height', + clip: 0, + clipPath: 'clip-path', + clipRule: 'clip-rule', + clipPathUnits: 'clipPathUnits', + colorInterpolation: 'color-interpolation', + colorInterpolationFilters: 'color-interpolation-filters', + colorProfile: 'color-profile', + colorRendering: 'color-rendering', + contentScriptType: 'contentScriptType', + contentStyleType: 'contentStyleType', + cursor: 0, + cx: 0, + cy: 0, + d: 0, + decelerate: 0, + descent: 0, + diffuseConstant: 'diffuseConstant', + direction: 0, + display: 0, + divisor: 0, + dominantBaseline: 'dominant-baseline', + dur: 0, + dx: 0, + dy: 0, + edgeMode: 'edgeMode', + elevation: 0, + enableBackground: 'enable-background', + end: 0, + exponent: 0, + externalResourcesRequired: 'externalResourcesRequired', + fill: 0, + fillOpacity: 'fill-opacity', + fillRule: 'fill-rule', + filter: 0, + filterRes: 'filterRes', + filterUnits: 'filterUnits', + floodColor: 'flood-color', + floodOpacity: 'flood-opacity', + focusable: 0, + fontFamily: 'font-family', + fontSize: 'font-size', + fontSizeAdjust: 'font-size-adjust', + fontStretch: 'font-stretch', + fontStyle: 'font-style', + fontVariant: 'font-variant', + fontWeight: 'font-weight', + format: 0, + from: 0, + fx: 0, + fy: 0, + g1: 0, + g2: 0, + glyphName: 'glyph-name', + glyphOrientationHorizontal: 'glyph-orientation-horizontal', + glyphOrientationVertical: 'glyph-orientation-vertical', + glyphRef: 'glyphRef', + gradientTransform: 'gradientTransform', + gradientUnits: 'gradientUnits', + hanging: 0, + horizAdvX: 'horiz-adv-x', + horizOriginX: 'horiz-origin-x', + ideographic: 0, + imageRendering: 'image-rendering', + 'in': 0, + in2: 0, + intercept: 0, + k: 0, + k1: 0, + k2: 0, + k3: 0, + k4: 0, + kernelMatrix: 'kernelMatrix', + kernelUnitLength: 'kernelUnitLength', + kerning: 0, + keyPoints: 'keyPoints', + keySplines: 'keySplines', + keyTimes: 'keyTimes', + lengthAdjust: 'lengthAdjust', + letterSpacing: 'letter-spacing', + lightingColor: 'lighting-color', + limitingConeAngle: 'limitingConeAngle', + local: 0, + markerEnd: 'marker-end', + markerMid: 'marker-mid', + markerStart: 'marker-start', + markerHeight: 'markerHeight', + markerUnits: 'markerUnits', + markerWidth: 'markerWidth', + mask: 0, + maskContentUnits: 'maskContentUnits', + maskUnits: 'maskUnits', + mathematical: 0, + mode: 0, + numOctaves: 'numOctaves', + offset: 0, + opacity: 0, + operator: 0, + order: 0, + orient: 0, + orientation: 0, + origin: 0, + overflow: 0, + overlinePosition: 'overline-position', + overlineThickness: 'overline-thickness', + paintOrder: 'paint-order', + panose1: 'panose-1', + pathLength: 'pathLength', + patternContentUnits: 'patternContentUnits', + patternTransform: 'patternTransform', + patternUnits: 'patternUnits', + pointerEvents: 'pointer-events', + points: 0, + pointsAtX: 'pointsAtX', + pointsAtY: 'pointsAtY', + pointsAtZ: 'pointsAtZ', + preserveAlpha: 'preserveAlpha', + preserveAspectRatio: 'preserveAspectRatio', + primitiveUnits: 'primitiveUnits', + r: 0, + radius: 0, + refX: 'refX', + refY: 'refY', + renderingIntent: 'rendering-intent', + repeatCount: 'repeatCount', + repeatDur: 'repeatDur', + requiredExtensions: 'requiredExtensions', + requiredFeatures: 'requiredFeatures', + restart: 0, + result: 0, + rotate: 0, + rx: 0, + ry: 0, + scale: 0, + seed: 0, + shapeRendering: 'shape-rendering', + slope: 0, + spacing: 0, + specularConstant: 'specularConstant', + specularExponent: 'specularExponent', + speed: 0, + spreadMethod: 'spreadMethod', + startOffset: 'startOffset', + stdDeviation: 'stdDeviation', + stemh: 0, + stemv: 0, + stitchTiles: 'stitchTiles', + stopColor: 'stop-color', + stopOpacity: 'stop-opacity', + strikethroughPosition: 'strikethrough-position', + strikethroughThickness: 'strikethrough-thickness', + string: 0, + stroke: 0, + strokeDasharray: 'stroke-dasharray', + strokeDashoffset: 'stroke-dashoffset', + strokeLinecap: 'stroke-linecap', + strokeLinejoin: 'stroke-linejoin', + strokeMiterlimit: 'stroke-miterlimit', + strokeOpacity: 'stroke-opacity', + strokeWidth: 'stroke-width', + surfaceScale: 'surfaceScale', + systemLanguage: 'systemLanguage', + tableValues: 'tableValues', + targetX: 'targetX', + targetY: 'targetY', + textAnchor: 'text-anchor', + textDecoration: 'text-decoration', + textRendering: 'text-rendering', + textLength: 'textLength', + to: 0, + transform: 0, + u1: 0, + u2: 0, + underlinePosition: 'underline-position', + underlineThickness: 'underline-thickness', + unicode: 0, + unicodeBidi: 'unicode-bidi', + unicodeRange: 'unicode-range', + unitsPerEm: 'units-per-em', + vAlphabetic: 'v-alphabetic', + vHanging: 'v-hanging', + vIdeographic: 'v-ideographic', + vMathematical: 'v-mathematical', + values: 0, + vectorEffect: 'vector-effect', + version: 0, + vertAdvY: 'vert-adv-y', + vertOriginX: 'vert-origin-x', + vertOriginY: 'vert-origin-y', + viewBox: 'viewBox', + viewTarget: 'viewTarget', + visibility: 0, + widths: 0, + wordSpacing: 'word-spacing', + writingMode: 'writing-mode', + x: 0, + xHeight: 'x-height', + x1: 0, + x2: 0, + xChannelSelector: 'xChannelSelector', + xlinkActuate: 'xlink:actuate', + xlinkArcrole: 'xlink:arcrole', + xlinkHref: 'xlink:href', + xlinkRole: 'xlink:role', + xlinkShow: 'xlink:show', + xlinkTitle: 'xlink:title', + xlinkType: 'xlink:type', + xmlBase: 'xml:base', + xmlns: 0, + xmlnsXlink: 'xmlns:xlink', + xmlLang: 'xml:lang', + xmlSpace: 'xml:space', + y: 0, + y1: 0, + y2: 0, + yChannelSelector: 'yChannelSelector', + z: 0, + zoomAndPan: 'zoomAndPan' +}; + +var SVGDOMPropertyConfig = { + props: {}, + attrNS: { + xlinkActuate: xlink, + xlinkArcrole: xlink, + xlinkHref: xlink, + xlinkRole: xlink, + xlinkShow: xlink, + xlinkTitle: xlink, + xlinkType: xlink, + xmlBase: xml, + xmlLang: xml, + xmlSpace: xml + }, + domAttrs: {}, + domProps: {} +}; + +Object.keys(ATTRS).map(function (key) { + SVGDOMPropertyConfig.props[key] = 0; + if (ATTRS[key]) { + SVGDOMPropertyConfig.domAttrs[key] = ATTRS[key]; + } +}); + +// merge html and svg config into properties +mergeConfigToProperties(HTMLDOMPropertyConfig); +mergeConfigToProperties(SVGDOMPropertyConfig); + +function mergeConfigToProperties(config) { + var + // all react/react-lite supporting property names in here + props = config.props; + var + // attributes namespace in here + attrNS = config.attrNS; + var + // propName in props which should use to be dom-attribute in here + domAttrs = config.domAttrs; + var + // propName in props which should use to be dom-property in here + domProps = config.domProps; + + for (var propName in props) { + if (!props.hasOwnProperty(propName)) { + continue; + } + var propConfig = props[propName]; + properties[propName] = { + attributeName: domAttrs.hasOwnProperty(propName) ? domAttrs[propName] : propName.toLowerCase(), + propertyName: domProps.hasOwnProperty(propName) ? domProps[propName] : propName, + attributeNamespace: attrNS.hasOwnProperty(propName) ? attrNS[propName] : null, + mustUseProperty: checkMask(propConfig, MUST_USE_PROPERTY), + hasBooleanValue: checkMask(propConfig, HAS_BOOLEAN_VALUE), + hasNumericValue: checkMask(propConfig, HAS_NUMERIC_VALUE), + hasPositiveNumericValue: checkMask(propConfig, HAS_POSITIVE_NUMERIC_VALUE), + hasOverloadedBooleanValue: checkMask(propConfig, HAS_OVERLOADED_BOOLEAN_VALUE) + }; + } +} + +function checkMask(value, bitmask) { + return (value & bitmask) === bitmask; +} \ No newline at end of file diff --git a/lib/DOMPropertyOperations.js b/lib/DOMPropertyOperations.js new file mode 100644 index 0000000..9040925 --- /dev/null +++ b/lib/DOMPropertyOperations.js @@ -0,0 +1,84 @@ +/** + * DOM Property Operations + */ + +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.setPropValue = setPropValue; +exports.removePropValue = removePropValue; + +var _DOMConfig = require('./DOMConfig'); + +/** + * Sets the value for a property on a node. + * + * @param {DOMElement} node + * @param {string} name + * @param {*} value + */ + +function setPropValue(node, name, value) { + var propInfo = _DOMConfig.properties.hasOwnProperty(name) && _DOMConfig.properties[name]; + if (propInfo) { + // should delete value from dom + if (value == null || propInfo.hasBooleanValue && !value || propInfo.hasNumericValue && isNaN(value) || propInfo.hasPositiveNumericValue && value < 1 || propInfo.hasOverloadedBooleanValue && value === false) { + removePropValue(node, name); + } else if (propInfo.mustUseProperty) { + var propName = propInfo.propertyName; + // dom.value has side effect + if (propName !== 'value' || '' + node[propName] !== '' + value) { + node[propName] = value; + } + } else { + var attributeName = propInfo.attributeName; + var namespace = propInfo.attributeNamespace; + + // `setAttribute` with objects becomes only `[object]` in IE8/9, + // ('' + value) makes it output the correct toString()-value. + if (namespace) { + node.setAttributeNS(namespace, attributeName, '' + value); + } else if (propInfo.hasBooleanValue || propInfo.hasOverloadedBooleanValue && value === true) { + node.setAttribute(attributeName, ''); + } else { + node.setAttribute(attributeName, '' + value); + } + } + } else if ((0, _DOMConfig.isCustomAttribute)(name) && _DOMConfig.VALID_ATTRIBUTE_NAME_REGEX.test(name)) { + if (value == null) { + node.removeAttribute(name); + } else { + node.setAttribute(name, '' + value); + } + } +} + +/** + * Deletes the value for a property on a node. + * + * @param {DOMElement} node + * @param {string} name + */ + +function removePropValue(node, name) { + var propInfo = _DOMConfig.properties.hasOwnProperty(name) && _DOMConfig.properties[name]; + if (propInfo) { + if (propInfo.mustUseProperty) { + var propName = propInfo.propertyName; + if (propInfo.hasBooleanValue) { + node[propName] = false; + } else { + // dom.value accept string value has side effect + if (propName !== 'value' || '' + node[propName] !== '') { + node[propName] = ''; + } + } + } else { + node.removeAttribute(propInfo.attributeName); + } + } else if ((0, _DOMConfig.isCustomAttribute)(name)) { + node.removeAttribute(name); + } +} \ No newline at end of file diff --git a/lib/LinkedStateMixin.js b/lib/LinkedStateMixin.js new file mode 100644 index 0000000..a12e8ce --- /dev/null +++ b/lib/LinkedStateMixin.js @@ -0,0 +1,35 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule LinkedStateMixin + * @typechecks static-only + */ +'use strict'; + +var ReactLink = require('./ReactLink'); +var ReactStateSetters = require('./ReactStateSetters'); + +/** + * A simple mixin around ReactLink.forState(). + */ +var LinkedStateMixin = { + /** + * Create a ReactLink that's linked to part of this component's state. The + * ReactLink will have the current value of this.state[key] and will call + * setState() when a change is requested. + * + * @param {string} key state key to update. Note: you may want to use keyOf() + * if you're using Google Closure Compiler advanced mode. + * @return {ReactLink} ReactLink instance linking to the state. + */ + linkState: function linkState(key) { + return new ReactLink(this.state[key], ReactStateSetters.createStateKeySetter(this, key)); + } +}; + +module.exports = LinkedStateMixin; \ No newline at end of file diff --git a/lib/PropTypes.js b/lib/PropTypes.js new file mode 100644 index 0000000..16e1487 --- /dev/null +++ b/lib/PropTypes.js @@ -0,0 +1,29 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +var check = function check() { + return check; +}; +check.isRequired = check; +var PropTypes = { + "array": check, + "bool": check, + "func": check, + "number": check, + "object": check, + "string": check, + "any": check, + "arrayOf": check, + "element": check, + "instanceOf": check, + "node": check, + "objectOf": check, + "oneOf": check, + "oneOfType": check, + "shape": check +}; + +exports["default"] = PropTypes; +module.exports = exports["default"]; \ No newline at end of file diff --git a/lib/PureComponent.js b/lib/PureComponent.js new file mode 100644 index 0000000..0dbac90 --- /dev/null +++ b/lib/PureComponent.js @@ -0,0 +1,30 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports['default'] = PureComponent; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _shallowEqual = require('./shallowEqual'); + +var _shallowEqual2 = _interopRequireDefault(_shallowEqual); + +var _Component = require('./Component'); + +var _Component2 = _interopRequireDefault(_Component); + +function PureComponent(props, context) { + _Component2['default'].call(this, props, context); +} + +PureComponent.prototype = Object.create(_Component2['default'].prototype); +PureComponent.prototype.constructor = PureComponent; +PureComponent.prototype.isPureReactComponent = true; +PureComponent.prototype.shouldComponentUpdate = shallowCompare; + +function shallowCompare(nextProps, nextState) { + return !(0, _shallowEqual2['default'])(this.props, nextProps) || !(0, _shallowEqual2['default'])(this.state, nextState); +} +module.exports = exports['default']; \ No newline at end of file diff --git a/lib/ReactCSSTransitionGroup.js b/lib/ReactCSSTransitionGroup.js new file mode 100644 index 0000000..25b692f --- /dev/null +++ b/lib/ReactCSSTransitionGroup.js @@ -0,0 +1,81 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @typechecks + * @providesModule ReactCSSTransitionGroup + */ + +'use strict'; + +var React = require('../dist/react-lite.common'); +var ReactTransitionGroup = require('./ReactTransitionGroup'); +var ReactCSSTransitionGroupChild = require('./ReactCSSTransitionGroupChild'); +var assign = Object.assign; + +function createTransitionTimeoutPropValidator(transitionType) { + var timeoutPropName = 'transition' + transitionType + 'Timeout'; + var enabledPropName = 'transition' + transitionType; + + return function (props) { + // If the transition is enabled + if (props[enabledPropName]) { + // If no timeout duration is provided + if (props[timeoutPropName] == null) { + return new Error(timeoutPropName + ' wasn\'t supplied to ReactCSSTransitionGroup: ' + 'this can cause unreliable animations and won\'t be supported in ' + 'a future version of React. See ' + 'https://fb.me/react-animation-transition-group-timeout for more ' + 'information.'); + + // If the duration isn't a number + } else if (typeof props[timeoutPropName] !== 'number') { + return new Error(timeoutPropName + ' must be a number (in milliseconds)'); + } + } + }; +} + +var ReactCSSTransitionGroup = React.createClass({ + displayName: 'ReactCSSTransitionGroup', + + propTypes: { + transitionName: ReactCSSTransitionGroupChild.propTypes.name, + + transitionAppear: React.PropTypes.bool, + transitionEnter: React.PropTypes.bool, + transitionLeave: React.PropTypes.bool, + transitionAppearTimeout: createTransitionTimeoutPropValidator('Appear'), + transitionEnterTimeout: createTransitionTimeoutPropValidator('Enter'), + transitionLeaveTimeout: createTransitionTimeoutPropValidator('Leave') + }, + + getDefaultProps: function getDefaultProps() { + return { + transitionAppear: false, + transitionEnter: true, + transitionLeave: true + }; + }, + + _wrapChild: function _wrapChild(child) { + // We need to provide this childFactory so that + // ReactCSSTransitionGroupChild can receive updates to name, enter, and + // leave while it is leaving. + return React.createElement(ReactCSSTransitionGroupChild, { + name: this.props.transitionName, + appear: this.props.transitionAppear, + enter: this.props.transitionEnter, + leave: this.props.transitionLeave, + appearTimeout: this.props.transitionAppearTimeout, + enterTimeout: this.props.transitionEnterTimeout, + leaveTimeout: this.props.transitionLeaveTimeout + }, child); + }, + + render: function render() { + return React.createElement(ReactTransitionGroup, assign({}, this.props, { childFactory: this._wrapChild })); + } +}); + +module.exports = ReactCSSTransitionGroup; \ No newline at end of file diff --git a/lib/ReactCSSTransitionGroupChild.js b/lib/ReactCSSTransitionGroupChild.js new file mode 100644 index 0000000..bfeee9a --- /dev/null +++ b/lib/ReactCSSTransitionGroupChild.js @@ -0,0 +1,164 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @typechecks + * @providesModule ReactCSSTransitionGroupChild + */ +'use strict'; + +var React = require('../dist/react-lite.common'); +var ReactDOM = React; +var Children = ReactDOM.Children; +var CSSCore = require('./utils/CSSCore'); +var ReactTransitionEvents = require('./ReactTransitionEvents'); + +var onlyChild = Children.only; + +// We don't remove the element from the DOM until we receive an animationend or +// transitionend event. If the user screws up and forgets to add an animation +// their node will be stuck in the DOM forever, so we detect if an animation +// does not start and if it doesn't, we just call the end listener immediately. +var TICK = 17; + +var ReactCSSTransitionGroupChild = React.createClass({ + displayName: 'ReactCSSTransitionGroupChild', + + propTypes: { + name: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.shape({ + enter: React.PropTypes.string, + leave: React.PropTypes.string, + active: React.PropTypes.string + }), React.PropTypes.shape({ + enter: React.PropTypes.string, + enterActive: React.PropTypes.string, + leave: React.PropTypes.string, + leaveActive: React.PropTypes.string, + appear: React.PropTypes.string, + appearActive: React.PropTypes.string + })]).isRequired, + + // Once we require timeouts to be specified, we can remove the + // boolean flags (appear etc.) and just accept a number + // or a bool for the timeout flags (appearTimeout etc.) + appear: React.PropTypes.bool, + enter: React.PropTypes.bool, + leave: React.PropTypes.bool, + appearTimeout: React.PropTypes.number, + enterTimeout: React.PropTypes.number, + leaveTimeout: React.PropTypes.number + }, + + transition: function transition(animationType, finishCallback, userSpecifiedDelay) { + var node = ReactDOM.findDOMNode(this); + + if (!node) { + if (finishCallback) { + finishCallback(); + } + return; + } + + var className = this.props.name[animationType] || this.props.name + '-' + animationType; + var activeClassName = this.props.name[animationType + 'Active'] || className + '-active'; + var timeout = null; + + var endListener = function endListener(e) { + if (e && e.target !== node) { + return; + } + + clearTimeout(timeout); + + CSSCore.removeClass(node, className); + CSSCore.removeClass(node, activeClassName); + + ReactTransitionEvents.removeEndEventListener(node, endListener); + + // Usually this optional callback is used for informing an owner of + // a leave animation and telling it to remove the child. + if (finishCallback) { + finishCallback(); + } + }; + + CSSCore.addClass(node, className); + + // Need to do this to actually trigger a transition. + this.queueClass(activeClassName); + + // If the user specified a timeout delay. + if (userSpecifiedDelay) { + // Clean-up the animation after the specified delay + timeout = setTimeout(endListener, userSpecifiedDelay); + this.transitionTimeouts.push(timeout); + } else { + // DEPRECATED: this listener will be removed in a future version of react + ReactTransitionEvents.addEndEventListener(node, endListener); + } + }, + + queueClass: function queueClass(className) { + this.classNameQueue.push(className); + + if (!this.timeout) { + this.timeout = setTimeout(this.flushClassNameQueue, TICK); + } + }, + + flushClassNameQueue: function flushClassNameQueue() { + if (this.isMounted()) { + this.classNameQueue.forEach(CSSCore.addClass.bind(CSSCore, ReactDOM.findDOMNode(this))); + } + this.classNameQueue.length = 0; + this.timeout = null; + }, + + componentWillMount: function componentWillMount() { + this.classNameQueue = []; + this.transitionTimeouts = []; + }, + + componentWillUnmount: function componentWillUnmount() { + if (this.timeout) { + clearTimeout(this.timeout); + } + this.transitionTimeouts.forEach(function (timeout) { + clearTimeout(timeout); + }); + }, + + componentWillAppear: function componentWillAppear(done) { + if (this.props.appear) { + this.transition('appear', done, this.props.appearTimeout); + } else { + done(); + } + }, + + componentWillEnter: function componentWillEnter(done) { + if (this.props.enter) { + this.transition('enter', done, this.props.enterTimeout); + } else { + done(); + } + }, + + componentWillLeave: function componentWillLeave(done) { + if (this.props.leave) { + this.transition('leave', done, this.props.leaveTimeout); + } else { + done(); + } + }, + + render: function render() { + return onlyChild(this.props.children); + } +}); + +module.exports = ReactCSSTransitionGroupChild; \ No newline at end of file diff --git a/lib/ReactChildren.js b/lib/ReactChildren.js new file mode 100644 index 0000000..1aec529 --- /dev/null +++ b/lib/ReactChildren.js @@ -0,0 +1,187 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactChildren + */ +'use strict'; + +var React = require('../dist/react-lite.common'); +var PooledClass = require('./utils/PooledClass'); +var emptyFunction = require('./utils/emptyFunction'); +var traverseAllChildren = require('./utils/traverseAllChildren'); +var isValidElement = React.isValidElement; +var cloneElement = React.cloneElement; + +var twoArgumentPooler = PooledClass.twoArgumentPooler; +var fourArgumentPooler = PooledClass.fourArgumentPooler; + +var cloneAndReplaceKey = function cloneAndReplaceKey(oldElement, newKey) { + var newElement = cloneElement(oldElement, { key: newKey }); + + return newElement; +}; + +var userProvidedKeyEscapeRegex = /\/(?!\/)/g; +function escapeUserProvidedKey(text) { + return ('' + text).replace(userProvidedKeyEscapeRegex, '//'); +} + +/** + * PooledClass representing the bookkeeping associated with performing a child + * traversal. Allows avoiding binding callbacks. + * + * @constructor ForEachBookKeeping + * @param {!function} forEachFunction Function to perform traversal with. + * @param {?*} forEachContext Context to perform context with. + */ +function ForEachBookKeeping(forEachFunction, forEachContext) { + this.func = forEachFunction; + this.context = forEachContext; + this.count = 0; +} +ForEachBookKeeping.prototype.destructor = function () { + this.func = null; + this.context = null; + this.count = 0; +}; +PooledClass.addPoolingTo(ForEachBookKeeping, twoArgumentPooler); + +function forEachSingleChild(bookKeeping, child, name) { + var func = bookKeeping.func; + var context = bookKeeping.context; + func.call(context, child, bookKeeping.count++); +} + +/** + * Iterates through children that are typically specified as `props.children`. + * + * The provided forEachFunc(child, index) will be called for each + * leaf child. + * + * @param {?*} children Children tree container. + * @param {function(*, int)} forEachFunc + * @param {*} forEachContext Context for forEachContext. + */ +function forEachChildren(children, forEachFunc, forEachContext) { + if (children == null) { + return children; + } + var traverseContext = ForEachBookKeeping.getPooled(forEachFunc, forEachContext); + traverseAllChildren(children, forEachSingleChild, traverseContext); + ForEachBookKeeping.release(traverseContext); +} + +/** + * PooledClass representing the bookkeeping associated with performing a child + * mapping. Allows avoiding binding callbacks. + * + * @constructor MapBookKeeping + * @param {!*} mapResult Object containing the ordered map of results. + * @param {!function} mapFunction Function to perform mapping with. + * @param {?*} mapContext Context to perform mapping with. + */ +function MapBookKeeping(mapResult, keyPrefix, mapFunction, mapContext) { + this.result = mapResult; + this.keyPrefix = keyPrefix; + this.func = mapFunction; + this.context = mapContext; + this.count = 0; +} +MapBookKeeping.prototype.destructor = function () { + this.result = null; + this.keyPrefix = null; + this.func = null; + this.context = null; + this.count = 0; +}; +PooledClass.addPoolingTo(MapBookKeeping, fourArgumentPooler); + +function mapSingleChildIntoContext(bookKeeping, child, childKey) { + var result = bookKeeping.result; + var keyPrefix = bookKeeping.keyPrefix; + var func = bookKeeping.func; + var context = bookKeeping.context; + + var mappedChild = func.call(context, child, bookKeeping.count++); + if (Array.isArray(mappedChild)) { + mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, emptyFunction.thatReturnsArgument); + } else if (mappedChild != null) { + if (isValidElement(mappedChild)) { + mappedChild = cloneAndReplaceKey(mappedChild, + // Keep both the (mapped) and old keys if they differ, just as + // traverseAllChildren used to do for objects as children + keyPrefix + (mappedChild !== child ? escapeUserProvidedKey(mappedChild.key || '') + '/' : '') + childKey); + } + result.push(mappedChild); + } +} + +function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) { + var escapedPrefix = ''; + if (prefix != null) { + escapedPrefix = escapeUserProvidedKey(prefix) + '/'; + } + var traverseContext = MapBookKeeping.getPooled(array, escapedPrefix, func, context); + traverseAllChildren(children, mapSingleChildIntoContext, traverseContext); + MapBookKeeping.release(traverseContext); +} + +/** + * Maps children that are typically specified as `props.children`. + * + * The provided mapFunction(child, key, index) will be called for each + * leaf child. + * + * @param {?*} children Children tree container. + * @param {function(*, int)} func The map function. + * @param {*} context Context for mapFunction. + * @return {object} Object containing the ordered map of results. + */ +function mapChildren(children, func, context) { + if (children == null) { + return children; + } + var result = []; + mapIntoWithKeyPrefixInternal(children, result, null, func, context); + return result; +} + +function forEachSingleChildDummy(traverseContext, child, name) { + return null; +} + +/** + * Count the number of children that are typically specified as + * `props.children`. + * + * @param {?*} children Children tree container. + * @return {number} The number of children. + */ +function countChildren(children, context) { + return traverseAllChildren(children, forEachSingleChildDummy, null); +} + +/** + * Flatten a children object (typically specified as `props.children`) and + * return an array with appropriately re-keyed children. + */ +function toArray(children) { + var result = []; + mapIntoWithKeyPrefixInternal(children, result, null, emptyFunction.thatReturnsArgument); + return result; +} + +var ReactChildren = { + forEach: forEachChildren, + map: mapChildren, + mapIntoWithKeyPrefixInternal: mapIntoWithKeyPrefixInternal, + count: countChildren, + toArray: toArray +}; + +module.exports = ReactChildren; \ No newline at end of file diff --git a/lib/ReactComponentWithPureRenderMixin.js b/lib/ReactComponentWithPureRenderMixin.js new file mode 100644 index 0000000..857ecbc --- /dev/null +++ b/lib/ReactComponentWithPureRenderMixin.js @@ -0,0 +1,45 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactComponentWithPureRenderMixin + */ +'use strict'; + +var shallowCompare = require('./shallowCompare'); + +/** + * If your React component's render function is "pure", e.g. it will render the + * same result given the same props and state, provide this Mixin for a + * considerable performance boost. + * + * Most React components have pure render functions. + * + * Example: + * + * var ReactComponentWithPureRenderMixin = + * require('ReactComponentWithPureRenderMixin'); + * React.createClass({ + * mixins: [ReactComponentWithPureRenderMixin], + * + * render: function() { + * return
foo
; + * } + * }); + * + * Note: This only checks shallow equality for props and state. If these contain + * complex data structures this mixin may have false-negatives for deeper + * differences. Only mixin to components which have simple props and state, or + * use `forceUpdate()` when you know deep data structures have changed. + */ +var ReactComponentWithPureRenderMixin = { + shouldComponentUpdate: function shouldComponentUpdate(nextProps, nextState) { + return shallowCompare(this, nextProps, nextState); + } +}; + +module.exports = ReactComponentWithPureRenderMixin; \ No newline at end of file diff --git a/lib/ReactDOM.js b/lib/ReactDOM.js new file mode 100644 index 0000000..baf97b9 --- /dev/null +++ b/lib/ReactDOM.js @@ -0,0 +1,130 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.render = render; +exports.unstable_renderSubtreeIntoContainer = unstable_renderSubtreeIntoContainer; +exports.unmountComponentAtNode = unmountComponentAtNode; +exports.findDOMNode = findDOMNode; + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } + +var _util = require('./util'); + +var _ = _interopRequireWildcard(_util); + +var _constant = require('./constant'); + +var _virtualDom = require('./virtual-dom'); + +var _Component = require('./Component'); + +function isValidContainer(node) { + return !!(node && (node.nodeType === _constant.ELEMENT_NODE_TYPE || node.nodeType === _constant.DOC_NODE_TYPE || node.nodeType === _constant.DOCUMENT_FRAGMENT_NODE_TYPE)); +} + +var pendingRendering = {}; +var vnodeStore = {}; +function renderTreeIntoContainer(vnode, container, callback, parentContext) { + if (!vnode.vtype) { + throw new Error('cannot render ' + vnode + ' to container'); + } + if (!isValidContainer(container)) { + throw new Error('container ' + container + ' is not a DOM element'); + } + var id = container[_constant.COMPONENT_ID] || (container[_constant.COMPONENT_ID] = _.getUid()); + var argsCache = pendingRendering[id]; + + // component lify cycle method maybe call root rendering + // should bundle them and render by only one time + if (argsCache) { + if (argsCache === true) { + pendingRendering[id] = argsCache = { vnode: vnode, callback: callback, parentContext: parentContext }; + } else { + argsCache.vnode = vnode; + argsCache.parentContext = parentContext; + argsCache.callback = argsCache.callback ? _.pipe(argsCache.callback, callback) : callback; + } + return; + } + + pendingRendering[id] = true; + var oldVnode = null; + var rootNode = null; + if (oldVnode = vnodeStore[id]) { + rootNode = (0, _virtualDom.compareTwoVnodes)(oldVnode, vnode, container.firstChild, parentContext); + } else { + rootNode = (0, _virtualDom.initVnode)(vnode, parentContext, container.namespaceURI); + var childNode = null; + while (childNode = container.lastChild) { + container.removeChild(childNode); + } + container.appendChild(rootNode); + } + vnodeStore[id] = vnode; + var isPending = _Component.updateQueue.isPending; + _Component.updateQueue.isPending = true; + (0, _virtualDom.clearPending)(); + argsCache = pendingRendering[id]; + delete pendingRendering[id]; + + var result = null; + if (typeof argsCache === 'object') { + result = renderTreeIntoContainer(argsCache.vnode, container, argsCache.callback, argsCache.parentContext); + } else if (vnode.vtype === _constant.VELEMENT) { + result = rootNode; + } else if (vnode.vtype === _constant.VCOMPONENT) { + result = rootNode.cache[vnode.uid]; + } + + if (!isPending) { + _Component.updateQueue.isPending = false; + _Component.updateQueue.batchUpdate(); + } + + if (callback) { + callback.call(result); + } + + return result; +} + +function render(vnode, container, callback) { + return renderTreeIntoContainer(vnode, container, callback); +} + +function unstable_renderSubtreeIntoContainer(parentComponent, subVnode, container, callback) { + var context = parentComponent.$cache.parentContext; + return renderTreeIntoContainer(subVnode, container, callback, context); +} + +function unmountComponentAtNode(container) { + if (!container.nodeName) { + throw new Error('expect node'); + } + var id = container[_constant.COMPONENT_ID]; + var vnode = null; + if (vnode = vnodeStore[id]) { + (0, _virtualDom.destroyVnode)(vnode, container.firstChild); + container.removeChild(container.firstChild); + delete vnodeStore[id]; + return true; + } + return false; +} + +function findDOMNode(node) { + if (node == null) { + return null; + } + if (node.nodeName) { + return node; + } + var component = node; + // if component.node equal to false, component must be unmounted + if (component.getDOMNode && component.$cache.isMounted) { + return component.getDOMNode(); + } + throw new Error('findDOMNode can not find Node'); +} \ No newline at end of file diff --git a/lib/ReactFragment.js b/lib/ReactFragment.js new file mode 100644 index 0000000..831242e --- /dev/null +++ b/lib/ReactFragment.js @@ -0,0 +1,56 @@ +/** + * Copyright 2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactFragment + */ +'use strict'; + +var React = require('../dist/react-lite.common'); +var isValidElement = React.isValidElement; +var ReactChildren = require('./ReactChildren'); +var emptyFunction = require('./utils/emptyFunction'); +var invariant = function invariant() {}; +var warning = function warning() {}; +/** + * We used to allow keyed objects to serve as a collection of ReactElements, + * or nested sets. This allowed us a way to explicitly key a set a fragment of + * components. This is now being replaced with an opaque data structure. + * The upgrade path is to call React.addons.createFragment({ key: value }) to + * create a keyed fragment. The resulting data structure is an array. + */ + +var numericPropertyRegex = /^\d+$/; + +var warnedAboutNumeric = false; + +var ReactFragment = { + // Wrap a keyed object in an opaque proxy that warns you if you access any + // of its properties. + create: function create(object) { + if (typeof object !== 'object' || !object || Array.isArray(object)) { + warning(false, 'React.addons.createFragment only accepts a single object. Got: %s', object); + return object; + } + if (isValidElement(object)) { + warning(false, 'React.addons.createFragment does not accept a ReactElement ' + 'without a wrapper object.'); + return object; + } + + invariant(object.nodeType !== 1, 'React.addons.createFragment(...): Encountered an invalid child; DOM ' + 'elements are not valid children of React components.'); + + var result = []; + + for (var key in object) { + ReactChildren.mapIntoWithKeyPrefixInternal(object[key], result, key, emptyFunction.thatReturnsArgument); + } + + return result; + } +}; + +module.exports = ReactFragment; \ No newline at end of file diff --git a/lib/ReactLink.js b/lib/ReactLink.js new file mode 100644 index 0000000..432ce14 --- /dev/null +++ b/lib/ReactLink.js @@ -0,0 +1,68 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactLink + * @typechecks static-only + */ + +/** + * ReactLink encapsulates a common pattern in which a component wants to modify + * a prop received from its parent. ReactLink allows the parent to pass down a + * value coupled with a callback that, when invoked, expresses an intent to + * modify that value. For example: + * + * React.createClass({ + * getInitialState: function() { + * return {value: ''}; + * }, + * render: function() { + * var valueLink = new ReactLink(this.state.value, this._handleValueChange); + * return ; + * }, + * _handleValueChange: function(newValue) { + * this.setState({value: newValue}); + * } + * }); + * + * We have provided some sugary mixins to make the creation and + * consumption of ReactLink easier; see LinkedValueUtils and LinkedStateMixin. + */ +'use strict'; + +var React = require('../dist/react-lite.common'); + +/** + * @param {*} value current value of the link + * @param {function} requestChange callback to request a change + */ +function ReactLink(value, requestChange) { + this.value = value; + this.requestChange = requestChange; +} + +/** + * Creates a PropType that enforces the ReactLink API and optionally checks the + * type of the value being passed inside the link. Example: + * + * MyComponent.propTypes = { + * tabIndexLink: ReactLink.PropTypes.link(React.PropTypes.number) + * } + */ +function createLinkTypeChecker(linkType) { + var shapes = { + value: typeof linkType === 'undefined' ? React.PropTypes.any.isRequired : linkType.isRequired, + requestChange: React.PropTypes.func.isRequired + }; + return React.PropTypes.shape(shapes); +} + +ReactLink.PropTypes = { + link: createLinkTypeChecker +}; + +module.exports = ReactLink; \ No newline at end of file diff --git a/lib/ReactStateSetters.js b/lib/ReactStateSetters.js new file mode 100644 index 0000000..5172205 --- /dev/null +++ b/lib/ReactStateSetters.js @@ -0,0 +1,104 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactStateSetters + */ + +'use strict'; + +var ReactStateSetters = { + /** + * Returns a function that calls the provided function, and uses the result + * of that to set the component's state. + * + * @param {ReactCompositeComponent} component + * @param {function} funcReturningState Returned callback uses this to + * determine how to update state. + * @return {function} callback that when invoked uses funcReturningState to + * determined the object literal to setState. + */ + createStateSetter: function createStateSetter(component, funcReturningState) { + return function (a, b, c, d, e, f) { + var partialState = funcReturningState.call(component, a, b, c, d, e, f); + if (partialState) { + component.setState(partialState); + } + }; + }, + + /** + * Returns a single-argument callback that can be used to update a single + * key in the component's state. + * + * Note: this is memoized function, which makes it inexpensive to call. + * + * @param {ReactCompositeComponent} component + * @param {string} key The key in the state that you should update. + * @return {function} callback of 1 argument which calls setState() with + * the provided keyName and callback argument. + */ + createStateKeySetter: function createStateKeySetter(component, key) { + // Memoize the setters. + var cache = component.__keySetters || (component.__keySetters = {}); + return cache[key] || (cache[key] = _createStateKeySetter(component, key)); + } +}; + +function _createStateKeySetter(component, key) { + // Partial state is allocated outside of the function closure so it can be + // reused with every call, avoiding memory allocation when this function + // is called. + var partialState = {}; + return function stateKeySetter(value) { + partialState[key] = value; + component.setState(partialState); + }; +} + +ReactStateSetters.Mixin = { + /** + * Returns a function that calls the provided function, and uses the result + * of that to set the component's state. + * + * For example, these statements are equivalent: + * + * this.setState({x: 1}); + * this.createStateSetter(function(xValue) { + * return {x: xValue}; + * })(1); + * + * @param {function} funcReturningState Returned callback uses this to + * determine how to update state. + * @return {function} callback that when invoked uses funcReturningState to + * determined the object literal to setState. + */ + createStateSetter: function createStateSetter(funcReturningState) { + return ReactStateSetters.createStateSetter(this, funcReturningState); + }, + + /** + * Returns a single-argument callback that can be used to update a single + * key in the component's state. + * + * For example, these statements are equivalent: + * + * this.setState({x: 1}); + * this.createStateKeySetter('x')(1); + * + * Note: this is memoized function, which makes it inexpensive to call. + * + * @param {string} key The key in the state that you should update. + * @return {function} callback of 1 argument which calls setState() with + * the provided keyName and callback argument. + */ + createStateKeySetter: function createStateKeySetter(key) { + return ReactStateSetters.createStateKeySetter(this, key); + } +}; + +module.exports = ReactStateSetters; \ No newline at end of file diff --git a/lib/ReactTransitionChildMapping.js b/lib/ReactTransitionChildMapping.js new file mode 100644 index 0000000..15cc187 --- /dev/null +++ b/lib/ReactTransitionChildMapping.js @@ -0,0 +1,98 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @typechecks static-only + * @providesModule ReactTransitionChildMapping + */ + +'use strict'; + +var flattenChildren = require('./utils/flattenChildren'); + +var ReactTransitionChildMapping = { + /** + * Given `this.props.children`, return an object mapping key to child. Just + * simple syntactic sugar around flattenChildren(). + * + * @param {*} children `this.props.children` + * @return {object} Mapping of key to child + */ + getChildMapping: function getChildMapping(children) { + if (!children) { + return children; + } + return flattenChildren(children); + }, + + /** + * When you're adding or removing children some may be added or removed in the + * same render pass. We want to show *both* since we want to simultaneously + * animate elements in and out. This function takes a previous set of keys + * and a new set of keys and merges them with its best guess of the correct + * ordering. In the future we may expose some of the utilities in + * ReactMultiChild to make this easy, but for now React itself does not + * directly have this concept of the union of prevChildren and nextChildren + * so we implement it here. + * + * @param {object} prev prev children as returned from + * `ReactTransitionChildMapping.getChildMapping()`. + * @param {object} next next children as returned from + * `ReactTransitionChildMapping.getChildMapping()`. + * @return {object} a key set that contains all keys in `prev` and all keys + * in `next` in a reasonable order. + */ + mergeChildMappings: function mergeChildMappings(prev, next) { + prev = prev || {}; + next = next || {}; + + function getValueForKey(key) { + if (next.hasOwnProperty(key)) { + return next[key]; + } else { + return prev[key]; + } + } + + // For each key of `next`, the list of keys to insert before that key in + // the combined list + var nextKeysPending = {}; + + var pendingKeys = []; + for (var prevKey in prev) { + if (next.hasOwnProperty(prevKey)) { + if (pendingKeys.length) { + nextKeysPending[prevKey] = pendingKeys; + pendingKeys = []; + } + } else { + pendingKeys.push(prevKey); + } + } + + var i; + var childMapping = {}; + for (var nextKey in next) { + if (nextKeysPending.hasOwnProperty(nextKey)) { + for (i = 0; i < nextKeysPending[nextKey].length; i++) { + var pendingNextKey = nextKeysPending[nextKey][i]; + childMapping[nextKeysPending[nextKey][i]] = getValueForKey(pendingNextKey); + } + } + childMapping[nextKey] = getValueForKey(nextKey); + } + + // Finally, add the keys which didn't appear before any key in `next` + for (i = 0; i < pendingKeys.length; i++) { + childMapping[pendingKeys[i]] = getValueForKey(pendingKeys[i]); + } + + return childMapping; + } +}; + +module.exports = ReactTransitionChildMapping; \ No newline at end of file diff --git a/lib/ReactTransitionEvents.js b/lib/ReactTransitionEvents.js new file mode 100644 index 0000000..dfb5b53 --- /dev/null +++ b/lib/ReactTransitionEvents.js @@ -0,0 +1,105 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactTransitionEvents + */ + +'use strict'; + +/** + * EVENT_NAME_MAP is used to determine which event fired when a + * transition/animation ends, based on the style property used to + * define that event. + */ +var EVENT_NAME_MAP = { + transitionend: { + 'transition': 'transitionend', + 'WebkitTransition': 'webkitTransitionEnd', + 'MozTransition': 'mozTransitionEnd', + 'OTransition': 'oTransitionEnd', + 'msTransition': 'MSTransitionEnd' + }, + + animationend: { + 'animation': 'animationend', + 'WebkitAnimation': 'webkitAnimationEnd', + 'MozAnimation': 'mozAnimationEnd', + 'OAnimation': 'oAnimationEnd', + 'msAnimation': 'MSAnimationEnd' + } +}; + +var endEvents = []; + +function detectEvents() { + var testEl = document.createElement('div'); + var style = testEl.style; + + // On some platforms, in particular some releases of Android 4.x, + // the un-prefixed "animation" and "transition" properties are defined on the + // style object but the events that fire will still be prefixed, so we need + // to check if the un-prefixed events are useable, and if not remove them + // from the map + if (!('AnimationEvent' in window)) { + delete EVENT_NAME_MAP.animationend.animation; + } + + if (!('TransitionEvent' in window)) { + delete EVENT_NAME_MAP.transitionend.transition; + } + + for (var baseEventName in EVENT_NAME_MAP) { + var baseEvents = EVENT_NAME_MAP[baseEventName]; + for (var styleName in baseEvents) { + if (styleName in style) { + endEvents.push(baseEvents[styleName]); + break; + } + } + } +} + +detectEvents(); + +// We use the raw {add|remove}EventListener() call because EventListener +// does not know how to remove event listeners and we really should +// clean up. Also, these events are not triggered in older browsers +// so we should be A-OK here. + +function addEventListener(node, eventName, eventListener) { + node.addEventListener(eventName, eventListener, false); +} + +function removeEventListener(node, eventName, eventListener) { + node.removeEventListener(eventName, eventListener, false); +} + +var ReactTransitionEvents = { + addEndEventListener: function addEndEventListener(node, eventListener) { + if (endEvents.length === 0) { + // If CSS transitions are not supported, trigger an "end animation" + // event immediately. + window.setTimeout(eventListener, 0); + return; + } + endEvents.forEach(function (endEvent) { + addEventListener(node, endEvent, eventListener); + }); + }, + + removeEndEventListener: function removeEndEventListener(node, eventListener) { + if (endEvents.length === 0) { + return; + } + endEvents.forEach(function (endEvent) { + removeEventListener(node, endEvent, eventListener); + }); + } +}; + +module.exports = ReactTransitionEvents; \ No newline at end of file diff --git a/lib/ReactTransitionGroup.js b/lib/ReactTransitionGroup.js new file mode 100644 index 0000000..4359733 --- /dev/null +++ b/lib/ReactTransitionGroup.js @@ -0,0 +1,203 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactTransitionGroup + */ + +'use strict'; + +var React = require('../dist/react-lite.common'); +var ReactTransitionChildMapping = require('./ReactTransitionChildMapping'); +var emptyFunction = require('./utils/emptyFunction'); +var assign = Object.assign; +var ReactTransitionGroup = React.createClass({ + displayName: 'ReactTransitionGroup', + + propTypes: { + component: React.PropTypes.any, + childFactory: React.PropTypes.func + }, + + getDefaultProps: function getDefaultProps() { + return { + component: 'span', + childFactory: emptyFunction.thatReturnsArgument + }; + }, + + getInitialState: function getInitialState() { + return { + children: ReactTransitionChildMapping.getChildMapping(this.props.children) + }; + }, + + componentWillMount: function componentWillMount() { + this.currentlyTransitioningKeys = {}; + this.keysToEnter = []; + this.keysToLeave = []; + }, + + componentDidMount: function componentDidMount() { + var initialChildMapping = this.state.children; + for (var key in initialChildMapping) { + if (initialChildMapping[key]) { + this.performAppear(key); + } + } + }, + + componentWillReceiveProps: function componentWillReceiveProps(nextProps) { + var nextChildMapping = ReactTransitionChildMapping.getChildMapping(nextProps.children); + var prevChildMapping = this.state.children; + + this.setState({ + children: ReactTransitionChildMapping.mergeChildMappings(prevChildMapping, nextChildMapping) + }); + + var key; + + for (key in nextChildMapping) { + var hasPrev = prevChildMapping && prevChildMapping.hasOwnProperty(key); + if (nextChildMapping[key] && !hasPrev && !this.currentlyTransitioningKeys[key]) { + this.keysToEnter.push(key); + } + } + + for (key in prevChildMapping) { + var hasNext = nextChildMapping && nextChildMapping.hasOwnProperty(key); + if (prevChildMapping[key] && !hasNext && !this.currentlyTransitioningKeys[key]) { + this.keysToLeave.push(key); + } + } + + // If we want to someday check for reordering, we could do it here. + }, + + componentDidUpdate: function componentDidUpdate() { + var keysToEnter = this.keysToEnter; + this.keysToEnter = []; + keysToEnter.forEach(this.performEnter); + + var keysToLeave = this.keysToLeave; + this.keysToLeave = []; + keysToLeave.forEach(this.performLeave); + }, + + performAppear: function performAppear(key) { + this.currentlyTransitioningKeys[key] = true; + + var component = this.refs[key]; + + if (component.componentWillAppear) { + component.componentWillAppear(this._handleDoneAppearing.bind(this, key)); + } else { + this._handleDoneAppearing(key); + } + }, + + _handleDoneAppearing: function _handleDoneAppearing(key) { + var component = this.refs[key]; + if (component.componentDidAppear) { + component.componentDidAppear(); + } + + delete this.currentlyTransitioningKeys[key]; + + var currentChildMapping = ReactTransitionChildMapping.getChildMapping(this.props.children); + + if (!currentChildMapping || !currentChildMapping.hasOwnProperty(key)) { + // This was removed before it had fully appeared. Remove it. + this.performLeave(key); + } + }, + + performEnter: function performEnter(key) { + this.currentlyTransitioningKeys[key] = true; + + var component = this.refs[key]; + + if (component.componentWillEnter) { + component.componentWillEnter(this._handleDoneEntering.bind(this, key)); + } else { + this._handleDoneEntering(key); + } + }, + + _handleDoneEntering: function _handleDoneEntering(key) { + var component = this.refs[key]; + if (component.componentDidEnter) { + component.componentDidEnter(); + } + + delete this.currentlyTransitioningKeys[key]; + + var currentChildMapping = ReactTransitionChildMapping.getChildMapping(this.props.children); + + if (!currentChildMapping || !currentChildMapping.hasOwnProperty(key)) { + // This was removed before it had fully entered. Remove it. + this.performLeave(key); + } + }, + + performLeave: function performLeave(key) { + this.currentlyTransitioningKeys[key] = true; + + var component = this.refs[key]; + if (component.componentWillLeave) { + component.componentWillLeave(this._handleDoneLeaving.bind(this, key)); + } else { + // Note that this is somewhat dangerous b/c it calls setState() + // again, effectively mutating the component before all the work + // is done. + this._handleDoneLeaving(key); + } + }, + + _handleDoneLeaving: function _handleDoneLeaving(key) { + var component = this.refs[key]; + + if (component.componentDidLeave) { + component.componentDidLeave(); + } + + delete this.currentlyTransitioningKeys[key]; + + var currentChildMapping = ReactTransitionChildMapping.getChildMapping(this.props.children); + + if (currentChildMapping && currentChildMapping.hasOwnProperty(key)) { + // This entered again before it fully left. Add it again. + this.performEnter(key); + } else { + this.setState(function (state) { + var newChildren = assign({}, state.children); + delete newChildren[key]; + return { children: newChildren }; + }); + } + }, + + render: function render() { + // TODO: we could get rid of the need for the wrapper node + // by cloning a single child + var childrenToRender = []; + for (var key in this.state.children) { + var child = this.state.children[key]; + if (child) { + // You may need to apply reactive updates to a child as it is leaving. + // The normal React way to do it won't work since the child will have + // already been removed. In case you need this behavior you can provide + // a childFactory function to wrap every child, even the ones that are + // leaving. + childrenToRender.push(React.cloneElement(this.props.childFactory(child), { ref: key, key: key })); + } + } + return React.createElement(this.props.component, this.props, childrenToRender); + } +}); + +module.exports = ReactTransitionGroup; \ No newline at end of file diff --git a/lib/ReactWithAddons.js b/lib/ReactWithAddons.js new file mode 100644 index 0000000..0c1cabf --- /dev/null +++ b/lib/ReactWithAddons.js @@ -0,0 +1,50 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactWithAddons + */ + +/** + * This module exists purely in the open source project, and is meant as a way + * to create a separate standalone build of React. This build has "addons", or + * functionality we've built and think might be useful but doesn't have a good + * place to live inside React core. + */ +'use strict'; + +var React = require('../dist/react-lite.common'); +var LinkedStateMixin = require('./LinkedStateMixin'); +var ReactComponentWithPureRenderMixin = require('./ReactComponentWithPureRenderMixin'); +var ReactCSSTransitionGroup = require('./ReactCSSTransitionGroup'); +var ReactFragment = require('./ReactFragment'); +var ReactTransitionGroup = require('./ReactTransitionGroup'); +var ReactUpdates = React; +var shallowCompare = require('./shallowCompare'); +var update = require('./update'); + +var warning = function warning() {}; + +var cloneWithProps = React.cloneElement; + +var warnedAboutBatchedUpdates = false; + +React.addons = { + CSSTransitionGroup: ReactCSSTransitionGroup, + LinkedStateMixin: LinkedStateMixin, + PureRenderMixin: ReactComponentWithPureRenderMixin, + TransitionGroup: ReactTransitionGroup, + batchedUpdates: function batchedUpdates() { + return ReactUpdates.batchedUpdates.apply(this, arguments); + }, + cloneWithProps: cloneWithProps, + createFragment: ReactFragment.create, + shallowCompare: shallowCompare, + update: update +}; + +module.exports = React; \ No newline at end of file diff --git a/lib/constant.js b/lib/constant.js new file mode 100644 index 0000000..dfccb51 --- /dev/null +++ b/lib/constant.js @@ -0,0 +1,36 @@ +/* + key/value configs +*/ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +var HTML_KEY = 'dangerouslySetInnerHTML'; +exports.HTML_KEY = HTML_KEY; +var SVGNamespaceURI = 'http://www.w3.org/2000/svg'; +exports.SVGNamespaceURI = SVGNamespaceURI; +var COMPONENT_ID = 'liteid'; +exports.COMPONENT_ID = COMPONENT_ID; +var VTEXT = 1; +exports.VTEXT = VTEXT; +var VELEMENT = 2; +exports.VELEMENT = VELEMENT; +var VSTATELESS = 3; +exports.VSTATELESS = VSTATELESS; +var VCOMPONENT = 4; +exports.VCOMPONENT = VCOMPONENT; +var VCOMMENT = 5; +exports.VCOMMENT = VCOMMENT; +var CREATE = 1; +exports.CREATE = CREATE; +var REMOVE = 2; +exports.REMOVE = REMOVE; +var UPDATE = 3; +exports.UPDATE = UPDATE; +var ELEMENT_NODE_TYPE = 1; +exports.ELEMENT_NODE_TYPE = ELEMENT_NODE_TYPE; +var DOC_NODE_TYPE = 9; +exports.DOC_NODE_TYPE = DOC_NODE_TYPE; +var DOCUMENT_FRAGMENT_NODE_TYPE = 11; +exports.DOCUMENT_FRAGMENT_NODE_TYPE = DOCUMENT_FRAGMENT_NODE_TYPE; \ No newline at end of file diff --git a/lib/createClass.js b/lib/createClass.js new file mode 100644 index 0000000..4ec41dc --- /dev/null +++ b/lib/createClass.js @@ -0,0 +1,119 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports['default'] = createClass; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } + +var _util = require('./util'); + +var _ = _interopRequireWildcard(_util); + +var _Component = require('./Component'); + +var _Component2 = _interopRequireDefault(_Component); + +function eachMixin(mixins, iteratee) { + mixins.forEach(function (mixin) { + if (mixin) { + if (_.isArr(mixin.mixins)) { + eachMixin(mixin.mixins, iteratee); + } + iteratee(mixin); + } + }); +} + +function combineMixinToProto(proto, mixin) { + for (var key in mixin) { + if (!mixin.hasOwnProperty(key)) { + continue; + } + var value = mixin[key]; + if (key === 'getInitialState') { + _.addItem(proto.$getInitialStates, value); + continue; + } + var curValue = proto[key]; + if (_.isFn(curValue) && _.isFn(value)) { + proto[key] = _.pipe(curValue, value); + } else { + proto[key] = value; + } + } +} + +function combineMixinToClass(Component, mixin) { + if (mixin.propTypes) { + Component.propTypes = Component.propTypes || {}; + _.extend(Component.propTypes, mixin.propTypes); + } + if (mixin.contextTypes) { + Component.contextTypes = Component.contextTypes || {}; + _.extend(Component.contextTypes, mixin.contextTypes); + } + _.extend(Component, mixin.statics); + if (_.isFn(mixin.getDefaultProps)) { + Component.defaultProps = Component.defaultProps || {}; + _.extend(Component.defaultProps, mixin.getDefaultProps()); + } +} + +function bindContext(obj, source) { + for (var key in source) { + if (source.hasOwnProperty(key)) { + if (_.isFn(source[key])) { + obj[key] = source[key].bind(obj); + } + } + } +} + +var Facade = function Facade() {}; +Facade.prototype = _Component2['default'].prototype; + +function getInitialState() { + var _this = this; + + var state = {}; + var setState = this.setState; + this.setState = Facade; + this.$getInitialStates.forEach(function (getInitialState) { + if (_.isFn(getInitialState)) { + _.extend(state, getInitialState.call(_this)); + } + }); + this.setState = setState; + return state; +} + +function createClass(spec) { + if (!_.isFn(spec.render)) { + throw new Error('createClass: spec.render is not function'); + } + var specMixins = spec.mixins || []; + var mixins = specMixins.concat(spec); + spec.mixins = null; + function Klass(props, context) { + _Component2['default'].call(this, props, context); + this.constructor = Klass; + spec.autobind !== false && bindContext(this, Klass.prototype); + this.state = this.getInitialState() || this.state; + } + Klass.displayName = spec.displayName; + var proto = Klass.prototype = new Facade(); + proto.$getInitialStates = []; + eachMixin(mixins, function (mixin) { + combineMixinToProto(proto, mixin); + combineMixinToClass(Klass, mixin); + }); + proto.getInitialState = getInitialState; + spec.mixins = specMixins; + return Klass; +} + +module.exports = exports['default']; \ No newline at end of file diff --git a/lib/createElement.js b/lib/createElement.js new file mode 100644 index 0000000..9e392b0 --- /dev/null +++ b/lib/createElement.js @@ -0,0 +1,116 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports['default'] = createElement; +exports.isValidElement = isValidElement; +exports.cloneElement = cloneElement; +exports.createFactory = createFactory; + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } + +var _util = require('./util'); + +var _ = _interopRequireWildcard(_util); + +var _constant = require('./constant'); + +var _virtualDom = require('./virtual-dom'); + +function createElement(type, props, children) { + var vtype = null; + if (typeof type === 'string') { + vtype = _constant.VELEMENT; + } else if (typeof type === 'function') { + if (type.prototype && type.prototype.isReactComponent) { + vtype = _constant.VCOMPONENT; + } else { + vtype = _constant.VSTATELESS; + } + } else { + throw new Error('React.createElement: unexpect type [ ' + type + ' ]'); + } + + var key = null; + var ref = null; + var finalProps = {}; + if (props != null) { + for (var propKey in props) { + if (!props.hasOwnProperty(propKey)) { + continue; + } + if (propKey === 'key') { + if (props.key !== undefined) { + key = '' + props.key; + } + } else if (propKey === 'ref') { + if (props.ref !== undefined) { + ref = props.ref; + } + } else { + finalProps[propKey] = props[propKey]; + } + } + } + + var defaultProps = type.defaultProps; + + if (defaultProps) { + for (var propKey in defaultProps) { + if (finalProps[propKey] === undefined) { + finalProps[propKey] = defaultProps[propKey]; + } + } + } + + var argsLen = arguments.length; + var finalChildren = children; + + if (argsLen > 3) { + finalChildren = Array(argsLen - 2); + for (var i = 2; i < argsLen; i++) { + finalChildren[i - 2] = arguments[i]; + } + } + + if (finalChildren !== undefined) { + finalProps.children = finalChildren; + } + + return (0, _virtualDom.createVnode)(vtype, type, finalProps, key, ref); +} + +function isValidElement(obj) { + return obj != null && !!obj.vtype; +} + +function cloneElement(originElem, props) { + var type = originElem.type; + var key = originElem.key; + var ref = originElem.ref; + + var newProps = _.extend(_.extend({ key: key, ref: ref }, originElem.props), props); + + for (var _len = arguments.length, children = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { + children[_key - 2] = arguments[_key]; + } + + var vnode = createElement.apply(undefined, [type, newProps].concat(children)); + if (vnode.ref === originElem.ref) { + vnode.refs = originElem.refs; + } + return vnode; +} + +function createFactory(type) { + var factory = function factory() { + for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } + + return createElement.apply(undefined, [type].concat(args)); + }; + factory.type = type; + return factory; +} \ No newline at end of file diff --git a/lib/event-system.js b/lib/event-system.js new file mode 100644 index 0000000..24f5323 --- /dev/null +++ b/lib/event-system.js @@ -0,0 +1,187 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.getEventName = getEventName; +exports.addEvent = addEvent; +exports.removeEvent = removeEvent; + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } + +var _Component = require('./Component'); + +var _util = require('./util'); + +var _ = _interopRequireWildcard(_util); + +// event config +var unbubbleEvents = { + /** + * should not bind mousemove in document scope + * even though mousemove event can bubble + */ + onmousemove: 1, + ontouchmove: 1, + onmouseleave: 1, + onmouseenter: 1, + onload: 1, + onunload: 1, + onscroll: 1, + onfocus: 1, + onblur: 1, + onrowexit: 1, + onbeforeunload: 1, + onstop: 1, + ondragdrop: 1, + ondragenter: 1, + ondragexit: 1, + ondraggesture: 1, + ondragover: 1, + oncontextmenu: 1, + onerror: 1 +}; + +exports.unbubbleEvents = unbubbleEvents; + +function getEventName(key) { + if (key === 'onDoubleClick') { + key = 'ondblclick'; + } else if (key === 'onTouchTap') { + key = 'onclick'; + } + + return key.toLowerCase(); +} + +// Mobile Safari does not fire properly bubble click events on +// non-interactive elements, which means delegated click listeners do not +// fire. The workaround for this bug involves attaching an empty click +// listener on the target node. +var inMobile = ('ontouchstart' in document); +var emptyFunction = function emptyFunction() {}; +var ON_CLICK_KEY = 'onclick'; + +var eventTypes = {}; + +function addEvent(elem, eventType, listener) { + eventType = getEventName(eventType); + + var eventStore = elem.eventStore || (elem.eventStore = {}); + eventStore[eventType] = listener; + + if (unbubbleEvents[eventType] === 1) { + elem[eventType] = dispatchUnbubbleEvent; + return; + } else if (!eventTypes[eventType]) { + // onclick -> click + document.addEventListener(eventType.substr(2), dispatchEvent, false); + eventTypes[eventType] = true; + } + + if (inMobile && eventType === ON_CLICK_KEY) { + elem.addEventListener('click', emptyFunction, false); + return; + } + + var nodeName = elem.nodeName; + + if (eventType === 'onchange' && supportInputEvent(elem)) { + addEvent(elem, 'oninput', listener); + } +} + +function removeEvent(elem, eventType) { + eventType = getEventName(eventType); + + var eventStore = elem.eventStore || (elem.eventStore = {}); + delete eventStore[eventType]; + + if (unbubbleEvents[eventType] === 1) { + elem[eventType] = null; + return; + } else if (inMobile && eventType === ON_CLICK_KEY) { + elem.removeEventListener('click', emptyFunction, false); + return; + } + + var nodeName = elem.nodeName; + + if (eventType === 'onchange' && supportInputEvent(elem)) { + delete eventStore['oninput']; + } +} + +function dispatchEvent(event) { + var target = event.target; + var type = event.type; + + var eventType = 'on' + type; + var syntheticEvent = undefined; + + _Component.updateQueue.isPending = true; + while (target) { + var _target = target; + var eventStore = _target.eventStore; + + var listener = eventStore && eventStore[eventType]; + if (!listener) { + target = target.parentNode; + continue; + } + if (!syntheticEvent) { + syntheticEvent = createSyntheticEvent(event); + } + syntheticEvent.currentTarget = target; + listener.call(target, syntheticEvent); + if (syntheticEvent.$cancelBubble) { + break; + } + target = target.parentNode; + } + _Component.updateQueue.isPending = false; + _Component.updateQueue.batchUpdate(); +} + +function dispatchUnbubbleEvent(event) { + var target = event.currentTarget || event.target; + var eventType = 'on' + event.type; + var syntheticEvent = createSyntheticEvent(event); + + syntheticEvent.currentTarget = target; + _Component.updateQueue.isPending = true; + + var eventStore = target.eventStore; + + var listener = eventStore && eventStore[eventType]; + if (listener) { + listener.call(target, syntheticEvent); + } + + _Component.updateQueue.isPending = false; + _Component.updateQueue.batchUpdate(); +} + +function createSyntheticEvent(nativeEvent) { + var syntheticEvent = {}; + var cancelBubble = function cancelBubble() { + return syntheticEvent.$cancelBubble = true; + }; + syntheticEvent.nativeEvent = nativeEvent; + syntheticEvent.persist = _.noop; + for (var key in nativeEvent) { + if (typeof nativeEvent[key] !== 'function') { + syntheticEvent[key] = nativeEvent[key]; + } else if (key === 'stopPropagation' || key === 'stopImmediatePropagation') { + syntheticEvent[key] = cancelBubble; + } else { + syntheticEvent[key] = nativeEvent[key].bind(nativeEvent); + } + } + return syntheticEvent; +} + +function supportInputEvent(elem) { + var nodeName = elem.nodeName && elem.nodeName.toLowerCase(); + return nodeName !== 'select' && !(nodeName === 'input' && elem.type === 'file'); +} \ No newline at end of file diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..f61c7d0 --- /dev/null +++ b/lib/index.js @@ -0,0 +1,64 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _Component = require('./Component'); + +var _Component2 = _interopRequireDefault(_Component); + +var _PureComponent = require('./PureComponent'); + +var _PureComponent2 = _interopRequireDefault(_PureComponent); + +var _createClass = require('./createClass'); + +var _createClass2 = _interopRequireDefault(_createClass); + +var _createElement = require('./createElement'); + +var _createElement2 = _interopRequireDefault(_createElement); + +var _Children = require('./Children'); + +var Children = _interopRequireWildcard(_Children); + +var _ReactDOM = require('./ReactDOM'); + +var ReactDOM = _interopRequireWildcard(_ReactDOM); + +var _PropTypes = require('./PropTypes'); + +var _PropTypes2 = _interopRequireDefault(_PropTypes); + +var _DOM = require('./DOM'); + +var _DOM2 = _interopRequireDefault(_DOM); + +var _util = require('./util'); + +var _ = _interopRequireWildcard(_util); + +var React = _.extend({ + version: '0.15.1', + cloneElement: _createElement.cloneElement, + isValidElement: _createElement.isValidElement, + createElement: _createElement2['default'], + createFactory: _createElement.createFactory, + Component: _Component2['default'], + PureComponent: _PureComponent2['default'], + createClass: _createClass2['default'], + Children: Children, + PropTypes: _PropTypes2['default'], + DOM: _DOM2['default'] +}, ReactDOM); + +React.__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = ReactDOM; + +exports['default'] = React; +module.exports = exports['default']; \ No newline at end of file diff --git a/lib/react-tap-event-plugin.js b/lib/react-tap-event-plugin.js new file mode 100644 index 0000000..955636a --- /dev/null +++ b/lib/react-tap-event-plugin.js @@ -0,0 +1,9 @@ +'use strict'; + +var Fastclick = require('fastclick'); +module.exports = function injectTapEventPlugin() { + var supportTouch = ('ontouchstart' in document); + if (supportTouch) { + Fastclick.attach(document.body); + } +}; \ No newline at end of file diff --git a/lib/renderSubtreeIntoContainer.js b/lib/renderSubtreeIntoContainer.js new file mode 100644 index 0000000..87d28d8 --- /dev/null +++ b/lib/renderSubtreeIntoContainer.js @@ -0,0 +1,15 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * +* @providesModule renderSubtreeIntoContainer +*/ + +'use strict'; +var React = require('../dist/react-lite.common'); +var unstable_renderSubtreeIntoContainer = React.unstable_renderSubtreeIntoContainer; +module.exports = unstable_renderSubtreeIntoContainer; \ No newline at end of file diff --git a/lib/shallowCompare.js b/lib/shallowCompare.js new file mode 100644 index 0000000..92fd6da --- /dev/null +++ b/lib/shallowCompare.js @@ -0,0 +1,23 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * +* @providesModule shallowCompare +*/ +'use strict'; + +var shallowEqual = require('./utils/shallowEqual'); + +/** + * Does a shallow comparison for props and state. + * See ReactComponentWithPureRenderMixin + */ +function shallowCompare(instance, nextProps, nextState) { + return !shallowEqual(instance.props, nextProps) || !shallowEqual(instance.state, nextState); +} + +module.exports = shallowCompare; \ No newline at end of file diff --git a/lib/shallowEqual.js b/lib/shallowEqual.js new file mode 100644 index 0000000..bb3fb42 --- /dev/null +++ b/lib/shallowEqual.js @@ -0,0 +1,34 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports['default'] = shallowEqual; + +function shallowEqual(objA, objB) { + if (objA === objB) { + return true; + } + + if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) { + return false; + } + + var keysA = Object.keys(objA); + var keysB = Object.keys(objB); + + if (keysA.length !== keysB.length) { + return false; + } + + // Test for A's keys different from B. + for (var i = 0; i < keysA.length; i++) { + if (!objB.hasOwnProperty(keysA[i]) || objA[keysA[i]] !== objB[keysA[i]]) { + return false; + } + } + + return true; +} + +module.exports = exports['default']; \ No newline at end of file diff --git a/lib/update.js b/lib/update.js new file mode 100644 index 0000000..a2a667c --- /dev/null +++ b/lib/update.js @@ -0,0 +1,108 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule update + */ + +/* global hasOwnProperty:true */ + +'use strict'; + +var React = require('../dist/react-lite.common'); +var assign = Object.assign; +var keyOf = require('./utils/keyOf'); +var invariant = function invariant() {}; +var hasOwnProperty = ({}).hasOwnProperty; + +function shallowCopy(x) { + if (Array.isArray(x)) { + return x.concat(); + } else if (x && typeof x === 'object') { + return assign(new x.constructor(), x); + } else { + return x; + } +} + +var COMMAND_PUSH = keyOf({ $push: null }); +var COMMAND_UNSHIFT = keyOf({ $unshift: null }); +var COMMAND_SPLICE = keyOf({ $splice: null }); +var COMMAND_SET = keyOf({ $set: null }); +var COMMAND_MERGE = keyOf({ $merge: null }); +var COMMAND_APPLY = keyOf({ $apply: null }); + +var ALL_COMMANDS_LIST = [COMMAND_PUSH, COMMAND_UNSHIFT, COMMAND_SPLICE, COMMAND_SET, COMMAND_MERGE, COMMAND_APPLY]; + +var ALL_COMMANDS_SET = {}; + +ALL_COMMANDS_LIST.forEach(function (command) { + ALL_COMMANDS_SET[command] = true; +}); + +function invariantArrayCase(value, spec, command) { + invariant(Array.isArray(value), 'update(): expected target of %s to be an array; got %s.', command, value); + var specValue = spec[command]; + invariant(Array.isArray(specValue), 'update(): expected spec of %s to be an array; got %s. ' + 'Did you forget to wrap your parameter in an array?', command, specValue); +} + +function update(value, spec) { + invariant(typeof spec === 'object', 'update(): You provided a key path to update() that did not contain one ' + 'of %s. Did you forget to include {%s: ...}?', ALL_COMMANDS_LIST.join(', '), COMMAND_SET); + + if (hasOwnProperty.call(spec, COMMAND_SET)) { + invariant(Object.keys(spec).length === 1, 'Cannot have more than one key in an object with %s', COMMAND_SET); + + return spec[COMMAND_SET]; + } + + var nextValue = shallowCopy(value); + + if (hasOwnProperty.call(spec, COMMAND_MERGE)) { + var mergeObj = spec[COMMAND_MERGE]; + invariant(mergeObj && typeof mergeObj === 'object', 'update(): %s expects a spec of type \'object\'; got %s', COMMAND_MERGE, mergeObj); + invariant(nextValue && typeof nextValue === 'object', 'update(): %s expects a target of type \'object\'; got %s', COMMAND_MERGE, nextValue); + assign(nextValue, spec[COMMAND_MERGE]); + } + + if (hasOwnProperty.call(spec, COMMAND_PUSH)) { + invariantArrayCase(value, spec, COMMAND_PUSH); + spec[COMMAND_PUSH].forEach(function (item) { + nextValue.push(item); + }); + } + + if (hasOwnProperty.call(spec, COMMAND_UNSHIFT)) { + invariantArrayCase(value, spec, COMMAND_UNSHIFT); + spec[COMMAND_UNSHIFT].forEach(function (item) { + nextValue.unshift(item); + }); + } + + if (hasOwnProperty.call(spec, COMMAND_SPLICE)) { + invariant(Array.isArray(value), 'Expected %s target to be an array; got %s', COMMAND_SPLICE, value); + invariant(Array.isArray(spec[COMMAND_SPLICE]), 'update(): expected spec of %s to be an array of arrays; got %s. ' + 'Did you forget to wrap your parameters in an array?', COMMAND_SPLICE, spec[COMMAND_SPLICE]); + spec[COMMAND_SPLICE].forEach(function (args) { + invariant(Array.isArray(args), 'update(): expected spec of %s to be an array of arrays; got %s. ' + 'Did you forget to wrap your parameters in an array?', COMMAND_SPLICE, spec[COMMAND_SPLICE]); + nextValue.splice.apply(nextValue, args); + }); + } + + if (hasOwnProperty.call(spec, COMMAND_APPLY)) { + invariant(typeof spec[COMMAND_APPLY] === 'function', 'update(): expected spec of %s to be a function; got %s.', COMMAND_APPLY, spec[COMMAND_APPLY]); + nextValue = spec[COMMAND_APPLY](nextValue); + } + + for (var k in spec) { + if (!(ALL_COMMANDS_SET.hasOwnProperty(k) && ALL_COMMANDS_SET[k])) { + nextValue[k] = update(value[k], spec[k]); + } + } + + return nextValue; +} + +module.exports = update; \ No newline at end of file diff --git a/lib/util.js b/lib/util.js new file mode 100644 index 0000000..dc44083 --- /dev/null +++ b/lib/util.js @@ -0,0 +1,165 @@ +// util +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.isFn = isFn; +exports.noop = noop; +exports.identity = identity; +exports.pipe = pipe; +exports.addItem = addItem; +exports.flatEach = flatEach; +exports.extend = extend; +exports.getUid = getUid; +exports.setProps = setProps; +exports.patchProps = patchProps; + +var _eventSystem = require('./event-system'); + +var _CSSPropertyOperationsJs = require('./CSSPropertyOperations.js'); + +var _DOMPropertyOperations = require('./DOMPropertyOperations'); + +var _constant = require('./constant'); + +function isFn(obj) { + return typeof obj === 'function'; +} + +var isArr = Array.isArray; + +exports.isArr = isArr; + +function noop() {} + +function identity(obj) { + return obj; +} + +function pipe(fn1, fn2) { + return function () { + fn1.apply(this, arguments); + return fn2.apply(this, arguments); + }; +} + +function addItem(list, item) { + list[list.length] = item; +} + +function flatEach(list, iteratee, a) { + var len = list.length; + var i = -1; + + while (len--) { + var item = list[++i]; + if (isArr(item)) { + flatEach(item, iteratee, a); + } else { + iteratee(item, a); + } + } +} + +function extend(to, from) { + if (!from) { + return to; + } + var keys = Object.keys(from); + var i = keys.length; + while (i--) { + to[keys[i]] = from[keys[i]]; + } + return to; +} + +var uid = 0; + +function getUid() { + return ++uid; +} + +var EVENT_KEYS = /^on/i; + +exports.EVENT_KEYS = EVENT_KEYS; +function setProp(elem, key, value, isCustomComponent) { + if (EVENT_KEYS.test(key)) { + (0, _eventSystem.addEvent)(elem, key, value); + } else if (key === 'style') { + (0, _CSSPropertyOperationsJs.setStyle)(elem.style, value); + } else if (key === _constant.HTML_KEY) { + if (value && value.__html != null) { + elem.innerHTML = value.__html; + } + } else if (isCustomComponent) { + if (value == null) { + elem.removeAttribute(key); + } else { + elem.setAttribute(key, '' + value); + } + } else { + (0, _DOMPropertyOperations.setPropValue)(elem, key, value); + } +} + +function removeProp(elem, key, oldValue, isCustomComponent) { + if (EVENT_KEYS.test(key)) { + (0, _eventSystem.removeEvent)(elem, key); + } else if (key === 'style') { + (0, _CSSPropertyOperationsJs.removeStyle)(elem.style, oldValue); + } else if (key === _constant.HTML_KEY) { + elem.innerHTML = ''; + } else if (isCustomComponent) { + elem.removeAttribute(key); + } else { + (0, _DOMPropertyOperations.removePropValue)(elem, key); + } +} + +function patchProp(elem, key, value, oldValue, isCustomComponent) { + if (key === 'value' || key === 'checked') { + oldValue = elem[key]; + } + if (value === oldValue) { + return; + } + if (value === undefined) { + removeProp(elem, key, oldValue, isCustomComponent); + return; + } + if (key === 'style') { + (0, _CSSPropertyOperationsJs.patchStyle)(elem.style, oldValue, value); + } else { + setProp(elem, key, value, isCustomComponent); + } +} + +function setProps(elem, props, isCustomComponent) { + for (var key in props) { + if (key !== 'children') { + setProp(elem, key, props[key], isCustomComponent); + } + } +} + +function patchProps(elem, props, newProps, isCustomComponent) { + for (var key in props) { + if (key !== 'children') { + if (newProps.hasOwnProperty(key)) { + patchProp(elem, key, newProps[key], props[key], isCustomComponent); + } else { + removeProp(elem, key, props[key], isCustomComponent); + } + } + } + for (var key in newProps) { + if (key !== 'children' && !props.hasOwnProperty(key)) { + setProp(elem, key, newProps[key], isCustomComponent); + } + } +} + +if (!Object.freeze) { + Object.freeze = identity; +} \ No newline at end of file diff --git a/lib/utils/CSSCore.js b/lib/utils/CSSCore.js new file mode 100644 index 0000000..7750979 --- /dev/null +++ b/lib/utils/CSSCore.js @@ -0,0 +1,97 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule CSSCore + * @typechecks + */ + +'use strict'; + +var invariant = function invariant() {}; + +/** + * The CSSCore module specifies the API (and implements most of the methods) + * that should be used when dealing with the display of elements (via their + * CSS classes and visibility on screen. It is an API focused on mutating the + * display and not reading it as no logical state should be encoded in the + * display of elements. + */ + +var CSSCore = { + + /** + * Adds the class passed in to the element if it doesn't already have it. + * + * @param {DOMElement} element the element to set the class on + * @param {string} className the CSS className + * @return {DOMElement} the element passed in + */ + addClass: function addClass(element, className) { + !!/\s/.test(className) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'CSSCore.addClass takes only a single class name. "%s" contains ' + 'multiple classes.', className) : invariant(false) : undefined; + + if (className) { + if (element.classList) { + element.classList.add(className); + } else if (!CSSCore.hasClass(element, className)) { + element.className = element.className + ' ' + className; + } + } + return element; + }, + + /** + * Removes the class passed in from the element + * + * @param {DOMElement} element the element to set the class on + * @param {string} className the CSS className + * @return {DOMElement} the element passed in + */ + removeClass: function removeClass(element, className) { + !!/\s/.test(className) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'CSSCore.removeClass takes only a single class name. "%s" contains ' + 'multiple classes.', className) : invariant(false) : undefined; + + if (className) { + if (element.classList) { + element.classList.remove(className); + } else if (CSSCore.hasClass(element, className)) { + element.className = element.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)', 'g'), '$1').replace(/\s+/g, ' ') // multiple spaces to one + .replace(/^\s*|\s*$/g, ''); // trim the ends + } + } + return element; + }, + + /** + * Helper to add or remove a class from an element based on a condition. + * + * @param {DOMElement} element the element to set the class on + * @param {string} className the CSS className + * @param {*} bool condition to whether to add or remove the class + * @return {DOMElement} the element passed in + */ + conditionClass: function conditionClass(element, className, bool) { + return (bool ? CSSCore.addClass : CSSCore.removeClass)(element, className); + }, + + /** + * Tests whether the element has the class specified. + * + * @param {DOMNode|DOMWindow} element the element to set the class on + * @param {string} className the CSS className + * @return {boolean} true if the element has the class, false if not + */ + hasClass: function hasClass(element, className) { + !!/\s/.test(className) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'CSS.hasClass takes only a single class name.') : invariant(false) : undefined; + if (element.classList) { + return !!className && element.classList.contains(className); + } + return (' ' + element.className + ' ').indexOf(' ' + className + ' ') > -1; + } + +}; + +module.exports = CSSCore; \ No newline at end of file diff --git a/lib/utils/PooledClass.js b/lib/utils/PooledClass.js new file mode 100644 index 0000000..b2675f9 --- /dev/null +++ b/lib/utils/PooledClass.js @@ -0,0 +1,119 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule PooledClass + */ + +'use strict'; + +var invariant = function invariant() {}; + +/** + * Static poolers. Several custom versions for each potential number of + * arguments. A completely generic pooler is easy to implement, but would + * require accessing the `arguments` object. In each of these, `this` refers to + * the Class itself, not an instance. If any others are needed, simply add them + * here, or in their own files. + */ +var oneArgumentPooler = function oneArgumentPooler(copyFieldsFrom) { + var Klass = this; + if (Klass.instancePool.length) { + var instance = Klass.instancePool.pop(); + Klass.call(instance, copyFieldsFrom); + return instance; + } else { + return new Klass(copyFieldsFrom); + } +}; + +var twoArgumentPooler = function twoArgumentPooler(a1, a2) { + var Klass = this; + if (Klass.instancePool.length) { + var instance = Klass.instancePool.pop(); + Klass.call(instance, a1, a2); + return instance; + } else { + return new Klass(a1, a2); + } +}; + +var threeArgumentPooler = function threeArgumentPooler(a1, a2, a3) { + var Klass = this; + if (Klass.instancePool.length) { + var instance = Klass.instancePool.pop(); + Klass.call(instance, a1, a2, a3); + return instance; + } else { + return new Klass(a1, a2, a3); + } +}; + +var fourArgumentPooler = function fourArgumentPooler(a1, a2, a3, a4) { + var Klass = this; + if (Klass.instancePool.length) { + var instance = Klass.instancePool.pop(); + Klass.call(instance, a1, a2, a3, a4); + return instance; + } else { + return new Klass(a1, a2, a3, a4); + } +}; + +var fiveArgumentPooler = function fiveArgumentPooler(a1, a2, a3, a4, a5) { + var Klass = this; + if (Klass.instancePool.length) { + var instance = Klass.instancePool.pop(); + Klass.call(instance, a1, a2, a3, a4, a5); + return instance; + } else { + return new Klass(a1, a2, a3, a4, a5); + } +}; + +var standardReleaser = function standardReleaser(instance) { + var Klass = this; + invariant(instance instanceof Klass, 'Trying to release an instance into a pool of a different type.'); + instance.destructor(); + if (Klass.instancePool.length < Klass.poolSize) { + Klass.instancePool.push(instance); + } +}; + +var DEFAULT_POOL_SIZE = 10; +var DEFAULT_POOLER = oneArgumentPooler; + +/** + * Augments `CopyConstructor` to be a poolable class, augmenting only the class + * itself (statically) not adding any prototypical fields. Any CopyConstructor + * you give this may have a `poolSize` property, and will look for a + * prototypical `destructor` on instances (optional). + * + * @param {Function} CopyConstructor Constructor that can be used to reset. + * @param {Function} pooler Customizable pooler. + */ +var addPoolingTo = function addPoolingTo(CopyConstructor, pooler) { + var NewKlass = CopyConstructor; + NewKlass.instancePool = []; + NewKlass.getPooled = pooler || DEFAULT_POOLER; + if (!NewKlass.poolSize) { + NewKlass.poolSize = DEFAULT_POOL_SIZE; + } + NewKlass.release = standardReleaser; + return NewKlass; +}; + +var PooledClass = { + addPoolingTo: addPoolingTo, + oneArgumentPooler: oneArgumentPooler, + twoArgumentPooler: twoArgumentPooler, + threeArgumentPooler: threeArgumentPooler, + fourArgumentPooler: fourArgumentPooler, + fiveArgumentPooler: fiveArgumentPooler +}; + +module.exports = PooledClass; \ No newline at end of file diff --git a/lib/utils/emptyFunction.js b/lib/utils/emptyFunction.js new file mode 100644 index 0000000..aa042b6 --- /dev/null +++ b/lib/utils/emptyFunction.js @@ -0,0 +1,38 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule emptyFunction + */ + +"use strict"; + +function makeEmptyFunction(arg) { + return function () { + return arg; + }; +} + +/** + * This function accepts and discards inputs; it has no side effects. This is + * primarily useful idiomatically for overridable function endpoints which + * always need to be callable, since JS lacks a null-call idiom ala Cocoa. + */ +function emptyFunction() {} + +emptyFunction.thatReturns = makeEmptyFunction; +emptyFunction.thatReturnsFalse = makeEmptyFunction(false); +emptyFunction.thatReturnsTrue = makeEmptyFunction(true); +emptyFunction.thatReturnsNull = makeEmptyFunction(null); +emptyFunction.thatReturnsThis = function () { + return this; +}; +emptyFunction.thatReturnsArgument = function (arg) { + return arg; +}; + +module.exports = emptyFunction; \ No newline at end of file diff --git a/lib/utils/escapeTextContentForBrowser.js b/lib/utils/escapeTextContentForBrowser.js new file mode 100644 index 0000000..691dc29 --- /dev/null +++ b/lib/utils/escapeTextContentForBrowser.js @@ -0,0 +1,38 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule escapeTextContentForBrowser + */ + +'use strict'; + +var ESCAPE_LOOKUP = { + '&': '&', + '>': '>', + '<': '<', + '"': '"', + '\'': ''' +}; + +var ESCAPE_REGEX = /[&><"']/g; + +function escaper(match) { + return ESCAPE_LOOKUP[match]; +} + +/** + * Escapes text to prevent scripting attacks. + * + * @param {*} text Text value to escape. + * @return {string} An escaped string. + */ +function escapeTextContentForBrowser(text) { + return ('' + text).replace(ESCAPE_REGEX, escaper); +} + +module.exports = escapeTextContentForBrowser; \ No newline at end of file diff --git a/lib/utils/flattenChildren.js b/lib/utils/flattenChildren.js new file mode 100644 index 0000000..316407b --- /dev/null +++ b/lib/utils/flattenChildren.js @@ -0,0 +1,43 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule flattenChildren + */ +'use strict'; + +var traverseAllChildren = require('./traverseAllChildren'); + +/** + * @param {function} traverseContext Context passed through traversal. + * @param {?ReactComponent} child React child component. + * @param {!string} name String name of key path to child. + */ +function flattenSingleChildIntoContext(traverseContext, child, name) { + // We found a component instance. + var result = traverseContext; + var keyUnique = result[name] === undefined; + if (keyUnique && child != null) { + result[name] = child; + } +} + +/** + * Flattens children that are typically specified as `props.children`. Any null + * children will not be included in the resulting object. + * @return {!object} flattened children keyed by name. + */ +function flattenChildren(children) { + if (children == null) { + return children; + } + var result = {}; + traverseAllChildren(children, flattenSingleChildIntoContext, result); + return result; +} + +module.exports = flattenChildren; \ No newline at end of file diff --git a/lib/utils/getIteratorFn.js b/lib/utils/getIteratorFn.js new file mode 100644 index 0000000..d0f86c3 --- /dev/null +++ b/lib/utils/getIteratorFn.js @@ -0,0 +1,40 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule getIteratorFn + * @typechecks static-only + */ + +'use strict'; + +/* global Symbol */ +var ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator; +var FAUX_ITERATOR_SYMBOL = '@@iterator'; // Before Symbol spec. + +/** + * Returns the iterator method function contained on the iterable object. + * + * Be sure to invoke the function with the iterable as context: + * + * var iteratorFn = getIteratorFn(myIterable); + * if (iteratorFn) { + * var iterator = iteratorFn.call(myIterable); + * ... + * } + * + * @param {?object} maybeIterable + * @return {?function} + */ +function getIteratorFn(maybeIterable) { + var iteratorFn = maybeIterable && (ITERATOR_SYMBOL && maybeIterable[ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL]); + if (typeof iteratorFn === 'function') { + return iteratorFn; + } +} + +module.exports = getIteratorFn; \ No newline at end of file diff --git a/lib/utils/keyOf.js b/lib/utils/keyOf.js new file mode 100644 index 0000000..9673c2a --- /dev/null +++ b/lib/utils/keyOf.js @@ -0,0 +1,35 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule keyOf + */ + +/** + * Allows extraction of a minified key. Let's the build system minify keys + * without losing the ability to dynamically use key strings as values + * themselves. Pass in an object with a single key/val pair and it will return + * you the string key of that single record. Suppose you want to grab the + * value for a key 'className' inside of an object. Key/val minification may + * have aliased that key to be 'xa12'. keyOf({className: null}) will return + * 'xa12' in that case. Resolve keys you want to use once at startup time, then + * reuse those resolutions. + */ +"use strict"; + +var keyOf = function keyOf(oneKeyObj) { + var key; + for (key in oneKeyObj) { + if (!oneKeyObj.hasOwnProperty(key)) { + continue; + } + return key; + } + return null; +}; + +module.exports = keyOf; \ No newline at end of file diff --git a/lib/utils/quoteAttributeValueForBrowser.js b/lib/utils/quoteAttributeValueForBrowser.js new file mode 100644 index 0000000..1de985a --- /dev/null +++ b/lib/utils/quoteAttributeValueForBrowser.js @@ -0,0 +1,26 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule quoteAttributeValueForBrowser + */ + +'use strict'; + +var escapeTextContentForBrowser = require('./escapeTextContentForBrowser'); + +/** + * Escapes attribute value to prevent scripting attacks. + * + * @param {*} value Value to escape. + * @return {string} An escaped string. + */ +function quoteAttributeValueForBrowser(value) { + return '"' + escapeTextContentForBrowser(value) + '"'; +} + +module.exports = quoteAttributeValueForBrowser; \ No newline at end of file diff --git a/lib/utils/shallowEqual.js b/lib/utils/shallowEqual.js new file mode 100644 index 0000000..5d54e62 --- /dev/null +++ b/lib/utils/shallowEqual.js @@ -0,0 +1,50 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule shallowEqual + * @typechecks + * + */ + +'use strict'; + +var hasOwnProperty = Object.prototype.hasOwnProperty; + +/** + * Performs equality by iterating through keys on an object and returning false + * when any key has values which are not strictly equal between the arguments. + * Returns true when the values of all keys are strictly equal. + */ +function shallowEqual(objA, objB) { + if (objA === objB) { + return true; + } + + if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) { + return false; + } + + var keysA = Object.keys(objA); + var keysB = Object.keys(objB); + + if (keysA.length !== keysB.length) { + return false; + } + + // Test for A's keys different from B. + var bHasOwnProperty = hasOwnProperty.bind(objB); + for (var i = 0; i < keysA.length; i++) { + if (!bHasOwnProperty(keysA[i]) || objA[keysA[i]] !== objB[keysA[i]]) { + return false; + } + } + + return true; +} + +module.exports = shallowEqual; \ No newline at end of file diff --git a/lib/utils/traverseAllChildren.js b/lib/utils/traverseAllChildren.js new file mode 100644 index 0000000..33354e3 --- /dev/null +++ b/lib/utils/traverseAllChildren.js @@ -0,0 +1,169 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule traverseAllChildren + */ +'use strict'; + +var React = require('../../dist/react-lite.common'); +var isValidElement = React.isValidElement; +var getIteratorFn = require('./getIteratorFn'); + +var invariant = function invariant() {}; +var SEPARATOR = '.'; +var SUBSEPARATOR = ':'; + +/** + * TODO: Test that a single child and an array with one item have the same key + * pattern. + */ + +var userProvidedKeyEscaperLookup = { + '=': '=0', + '.': '=1', + ':': '=2' +}; + +var userProvidedKeyEscapeRegex = /[=.:]/g; + +var didWarnAboutMaps = false; + +function userProvidedKeyEscaper(match) { + return userProvidedKeyEscaperLookup[match]; +} + +/** + * Generate a key string that identifies a component within a set. + * + * @param {*} component A component that could contain a manual key. + * @param {number} index Index that is used if a manual key is not provided. + * @return {string} + */ +function getComponentKey(component, index) { + if (component && component.key != null) { + // Explicit key + return wrapUserProvidedKey(component.key); + } + // Implicit key determined by the index in the set + return index.toString(36); +} + +/** + * Escape a component key so that it is safe to use in a reactid. + * + * @param {*} text Component key to be escaped. + * @return {string} An escaped string. + */ +function escapeUserProvidedKey(text) { + return ('' + text).replace(userProvidedKeyEscapeRegex, userProvidedKeyEscaper); +} + +/** + * Wrap a `key` value explicitly provided by the user to distinguish it from + * implicitly-generated keys generated by a component's index in its parent. + * + * @param {string} key Value of a user-provided `key` attribute + * @return {string} + */ +function wrapUserProvidedKey(key) { + return '$' + escapeUserProvidedKey(key); +} + +/** + * @param {?*} children Children tree container. + * @param {!string} nameSoFar Name of the key path so far. + * @param {!function} callback Callback to invoke with each child found. + * @param {?*} traverseContext Used to pass information throughout the traversal + * process. + * @return {!number} The number of children in this subtree. + */ +function traverseAllChildrenImpl(children, nameSoFar, callback, traverseContext) { + var type = typeof children; + + if (type === 'undefined' || type === 'boolean') { + // All of the above are perceived as null. + children = null; + } + + if (children === null || type === 'string' || type === 'number' || isValidElement(children)) { + callback(traverseContext, children, + // If it's the only child, treat the name as if it was wrapped in an array + // so that it's consistent if the number of children grows. + nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar); + return 1; + } + + var child; + var nextName; + var subtreeCount = 0; // Count of children found in the current subtree. + var nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR; + + if (Array.isArray(children)) { + for (var i = 0; i < children.length; i++) { + child = children[i]; + nextName = nextNamePrefix + getComponentKey(child, i); + subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext); + } + } else { + var iteratorFn = getIteratorFn(children); + if (iteratorFn) { + var iterator = iteratorFn.call(children); + var step; + if (iteratorFn !== children.entries) { + var ii = 0; + while (!(step = iterator.next()).done) { + child = step.value; + nextName = nextNamePrefix + getComponentKey(child, ii++); + subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext); + } + } else { + // Iterator will provide entry [k,v] tuples rather than values. + while (!(step = iterator.next()).done) { + var entry = step.value; + if (entry) { + child = entry[1]; + nextName = nextNamePrefix + wrapUserProvidedKey(entry[0]) + SUBSEPARATOR + getComponentKey(child, 0); + subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext); + } + } + } + } else if (type === 'object') { + var addendum = ''; + var childrenString = String(children); + invariant(false, 'Objects are not valid as a React child (found: %s).%s', childrenString === '[object Object]' ? 'object with keys {' + Object.keys(children).join(', ') + '}' : childrenString, addendum); + } + } + + return subtreeCount; +} + +/** + * Traverses children that are typically specified as `props.children`, but + * might also be specified through attributes: + * + * - `traverseAllChildren(this.props.children, ...)` + * - `traverseAllChildren(this.props.leftPanelChildren, ...)` + * + * The `traverseContext` is an optional argument that is passed through the + * entire traversal. It can be used to store accumulations or anything else that + * the callback might find relevant. + * + * @param {?*} children Children tree object. + * @param {!function} callback To invoke upon traversing each child. + * @param {?*} traverseContext Context for traversal. + * @return {!number} The number of children in this subtree. + */ +function traverseAllChildren(children, callback, traverseContext) { + if (children == null) { + return 0; + } + + return traverseAllChildrenImpl(children, '', callback, traverseContext); +} + +module.exports = traverseAllChildren; \ No newline at end of file diff --git a/lib/virtual-dom.js b/lib/virtual-dom.js new file mode 100644 index 0000000..52db0d4 --- /dev/null +++ b/lib/virtual-dom.js @@ -0,0 +1,643 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +exports.createVnode = createVnode; +exports.initVnode = initVnode; +exports.destroyVnode = destroyVnode; +exports.renderComponent = renderComponent; +exports.getChildContext = getChildContext; +exports.clearPending = clearPending; +exports.compareTwoVnodes = compareTwoVnodes; +exports.syncCache = syncCache; + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } + +var _util = require('./util'); + +var _ = _interopRequireWildcard(_util); + +var _constant = require('./constant'); + +/** + * current stateful component's refs property + * will attach to every vnode created by calling component.render method + */ +var refs = null; + +function createVnode(vtype, type, props, key, ref) { + var vnode = { + vtype: vtype, + type: type, + props: props, + refs: refs, + key: key, + ref: ref + }; + if (vtype === _constant.VSTATELESS || vtype === _constant.VCOMPONENT) { + vnode.uid = _.getUid(); + } + return vnode; +} + +function initVnode(vnode, parentContext, namespaceURI) { + var vtype = vnode.vtype; + + var node = null; + if (!vtype) { + // init text + node = document.createTextNode(vnode); + } else if (vtype === _constant.VELEMENT) { + // init element + node = initVelem(vnode, parentContext, namespaceURI); + } else if (vtype === _constant.VCOMPONENT) { + // init stateful component + node = initVcomponent(vnode, parentContext, namespaceURI); + } else if (vtype === _constant.VSTATELESS) { + // init stateless component + node = initVstateless(vnode, parentContext, namespaceURI); + } else if (vtype === _constant.VCOMMENT) { + // init comment + node = document.createComment('react-text: ' + (vnode.uid || _.getUid())); + } + return node; +} + +function updateVnode(vnode, newVnode, node, parentContext) { + var vtype = vnode.vtype; + + if (vtype === _constant.VCOMPONENT) { + return updateVcomponent(vnode, newVnode, node, parentContext); + } + + if (vtype === _constant.VSTATELESS) { + return updateVstateless(vnode, newVnode, node, parentContext); + } + + // ignore VCOMMENT and other vtypes + if (vtype !== _constant.VELEMENT) { + return node; + } + + var oldHtml = vnode.props[_constant.HTML_KEY] && vnode.props[_constant.HTML_KEY].__html; + if (oldHtml != null) { + updateVelem(vnode, newVnode, node, parentContext); + initVchildren(newVnode, node, parentContext); + } else { + updateVChildren(vnode, newVnode, node, parentContext); + updateVelem(vnode, newVnode, node, parentContext); + } + return node; +} + +function updateVChildren(vnode, newVnode, node, parentContext) { + var patches = { + removes: [], + updates: [], + creates: [] + }; + diffVchildren(patches, vnode, newVnode, node, parentContext); + _.flatEach(patches.removes, applyDestroy); + _.flatEach(patches.updates, applyUpdate); + _.flatEach(patches.creates, applyCreate); +} + +function applyUpdate(data) { + if (!data) { + return; + } + var vnode = data.vnode; + var newNode = data.node; + + // update + if (!data.shouldIgnore) { + if (!vnode.vtype) { + newNode.replaceData(0, newNode.length, data.newVnode); + } else if (vnode.vtype === _constant.VELEMENT) { + updateVelem(vnode, data.newVnode, newNode, data.parentContext); + } else if (vnode.vtype === _constant.VSTATELESS) { + newNode = updateVstateless(vnode, data.newVnode, newNode, data.parentContext); + } else if (vnode.vtype === _constant.VCOMPONENT) { + newNode = updateVcomponent(vnode, data.newVnode, newNode, data.parentContext); + } + } + + // re-order + var currentNode = newNode.parentNode.childNodes[data.index]; + if (currentNode !== newNode) { + newNode.parentNode.insertBefore(newNode, currentNode); + } + return newNode; +} + +function applyDestroy(data) { + destroyVnode(data.vnode, data.node); + data.node.parentNode.removeChild(data.node); +} + +function applyCreate(data) { + var node = initVnode(data.vnode, data.parentContext, data.parentNode.namespaceURI); + data.parentNode.insertBefore(node, data.parentNode.childNodes[data.index]); +} + +/** + * Only vnode which has props.children need to call destroy function + * to check whether subTree has component that need to call lify-cycle method and release cache. + */ + +function destroyVnode(vnode, node) { + var vtype = vnode.vtype; + + if (vtype === _constant.VELEMENT) { + // destroy element + destroyVelem(vnode, node); + } else if (vtype === _constant.VCOMPONENT) { + // destroy state component + destroyVcomponent(vnode, node); + } else if (vtype === _constant.VSTATELESS) { + // destroy stateless component + destroyVstateless(vnode, node); + } +} + +function initVelem(velem, parentContext, namespaceURI) { + var type = velem.type; + var props = velem.props; + + var node = null; + + if (type === 'svg' || namespaceURI === _constant.SVGNamespaceURI) { + node = document.createElementNS(_constant.SVGNamespaceURI, type); + namespaceURI = _constant.SVGNamespaceURI; + } else { + node = document.createElement(type); + } + + initVchildren(velem, node, parentContext); + + var isCustomComponent = type.indexOf('-') >= 0 || props.is != null; + _.setProps(node, props, isCustomComponent); + + if (velem.ref != null) { + _.addItem(pendingRefs, velem); + _.addItem(pendingRefs, node); + } + + return node; +} + +function initVchildren(velem, node, parentContext) { + var vchildren = node.vchildren = getFlattenChildren(velem); + var namespaceURI = node.namespaceURI; + for (var i = 0, len = vchildren.length; i < len; i++) { + node.appendChild(initVnode(vchildren[i], parentContext, namespaceURI)); + } +} + +function getFlattenChildren(vnode) { + var children = vnode.props.children; + + var vchildren = []; + if (_.isArr(children)) { + _.flatEach(children, collectChild, vchildren); + } else { + collectChild(children, vchildren); + } + return vchildren; +} + +function collectChild(child, children) { + if (child != null && typeof child !== 'boolean') { + if (!child.vtype) { + // convert immutablejs data + if (child.toJS) { + child = child.toJS(); + if (_.isArr(child)) { + _.flatEach(child, collectChild, children); + } else { + collectChild(child, children); + } + return; + } + child = '' + child; + } + children[children.length] = child; + } +} + +function diffVchildren(patches, vnode, newVnode, node, parentContext) { + if (!node.vchildren) return; // react-lite hasn't seen this DOM node before + + var childNodes = node.childNodes; + var vchildren = node.vchildren; + + var newVchildren = node.vchildren = getFlattenChildren(newVnode); + var vchildrenLen = vchildren.length; + var newVchildrenLen = newVchildren.length; + + if (vchildrenLen === 0) { + if (newVchildrenLen > 0) { + for (var i = 0; i < newVchildrenLen; i++) { + _.addItem(patches.creates, { + vnode: newVchildren[i], + parentNode: node, + parentContext: parentContext, + index: i + }); + } + } + return; + } else if (newVchildrenLen === 0) { + for (var i = 0; i < vchildrenLen; i++) { + _.addItem(patches.removes, { + vnode: vchildren[i], + node: childNodes[i] + }); + } + return; + } + + var updates = Array(newVchildrenLen); + var removes = null; + var creates = null; + + // isEqual + for (var i = 0; i < vchildrenLen; i++) { + var _vnode = vchildren[i]; + for (var j = 0; j < newVchildrenLen; j++) { + if (updates[j]) { + continue; + } + var _newVnode = newVchildren[j]; + if (_vnode === _newVnode) { + var shouldIgnore = true; + if (parentContext) { + if (_vnode.vtype === _constant.VCOMPONENT || _vnode.vtype === _constant.VSTATELESS) { + if (_vnode.type.contextTypes) { + shouldIgnore = false; + } + } + } + updates[j] = { + shouldIgnore: shouldIgnore, + vnode: _vnode, + newVnode: _newVnode, + node: childNodes[i], + parentContext: parentContext, + index: j + }; + vchildren[i] = null; + break; + } + } + } + + // isSimilar + for (var i = 0; i < vchildrenLen; i++) { + var _vnode2 = vchildren[i]; + if (_vnode2 === null) { + continue; + } + var shouldRemove = true; + for (var j = 0; j < newVchildrenLen; j++) { + if (updates[j]) { + continue; + } + var _newVnode2 = newVchildren[j]; + if (_newVnode2.type === _vnode2.type && _newVnode2.key === _vnode2.key && _newVnode2.refs === _vnode2.refs) { + updates[j] = { + vnode: _vnode2, + newVnode: _newVnode2, + node: childNodes[i], + parentContext: parentContext, + index: j + }; + shouldRemove = false; + break; + } + } + if (shouldRemove) { + if (!removes) { + removes = []; + } + _.addItem(removes, { + vnode: _vnode2, + node: childNodes[i] + }); + } + } + + for (var i = 0; i < newVchildrenLen; i++) { + var item = updates[i]; + if (!item) { + if (!creates) { + creates = []; + } + _.addItem(creates, { + vnode: newVchildren[i], + parentNode: node, + parentContext: parentContext, + index: i + }); + } else if (item.vnode.vtype === _constant.VELEMENT) { + diffVchildren(patches, item.vnode, item.newVnode, item.node, item.parentContext); + } + } + + if (removes) { + _.addItem(patches.removes, removes); + } + if (creates) { + _.addItem(patches.creates, creates); + } + _.addItem(patches.updates, updates); +} + +function updateVelem(velem, newVelem, node) { + var isCustomComponent = velem.type.indexOf('-') >= 0 || velem.props.is != null; + _.patchProps(node, velem.props, newVelem.props, isCustomComponent); + if (velem.ref !== newVelem.ref) { + detachRef(velem.refs, velem.ref, node); + attachRef(newVelem.refs, newVelem.ref, node); + } + return node; +} + +function destroyVelem(velem, node) { + var props = velem.props; + var vchildren = node.vchildren; + var childNodes = node.childNodes; + + if (vchildren) { + for (var i = 0, len = vchildren.length; i < len; i++) { + destroyVnode(vchildren[i], childNodes[i]); + } + } + detachRef(velem.refs, velem.ref, node); + node.eventStore = node.vchildren = null; +} + +function initVstateless(vstateless, parentContext, namespaceURI) { + var vnode = renderVstateless(vstateless, parentContext); + var node = initVnode(vnode, parentContext, namespaceURI); + node.cache = node.cache || {}; + node.cache[vstateless.uid] = vnode; + return node; +} + +function updateVstateless(vstateless, newVstateless, node, parentContext) { + var uid = vstateless.uid; + var vnode = node.cache[uid]; + delete node.cache[uid]; + var newVnode = renderVstateless(newVstateless, parentContext); + var newNode = compareTwoVnodes(vnode, newVnode, node, parentContext); + newNode.cache = newNode.cache || {}; + newNode.cache[newVstateless.uid] = newVnode; + if (newNode !== node) { + syncCache(newNode.cache, node.cache, newNode); + } + return newNode; +} + +function destroyVstateless(vstateless, node) { + var uid = vstateless.uid; + var vnode = node.cache[uid]; + delete node.cache[uid]; + destroyVnode(vnode, node); +} + +function renderVstateless(vstateless, parentContext) { + var factory = vstateless.type; + var props = vstateless.props; + + var componentContext = getContextByTypes(parentContext, factory.contextTypes); + var vnode = factory(props, componentContext); + if (vnode && vnode.render) { + vnode = vnode.render(); + } + if (vnode === null || vnode === false) { + vnode = createVnode(_constant.VCOMMENT); + } else if (!vnode || !vnode.vtype) { + throw new Error('@' + factory.name + '#render:You may have returned undefined, an array or some other invalid object'); + } + return vnode; +} + +function initVcomponent(vcomponent, parentContext, namespaceURI) { + var Component = vcomponent.type; + var props = vcomponent.props; + var uid = vcomponent.uid; + + var componentContext = getContextByTypes(parentContext, Component.contextTypes); + var component = new Component(props, componentContext); + var updater = component.$updater; + var cache = component.$cache; + + cache.parentContext = parentContext; + updater.isPending = true; + component.props = component.props || props; + component.context = component.context || componentContext; + if (component.componentWillMount) { + component.componentWillMount(); + component.state = updater.getState(); + } + var vnode = renderComponent(component); + var node = initVnode(vnode, getChildContext(component, parentContext), namespaceURI); + node.cache = node.cache || {}; + node.cache[uid] = component; + cache.vnode = vnode; + cache.node = node; + cache.isMounted = true; + _.addItem(pendingComponents, component); + + if (vcomponent.ref != null) { + _.addItem(pendingRefs, vcomponent); + _.addItem(pendingRefs, component); + } + + return node; +} + +function updateVcomponent(vcomponent, newVcomponent, node, parentContext) { + var uid = vcomponent.uid; + var component = node.cache[uid]; + var updater = component.$updater; + var cache = component.$cache; + var Component = newVcomponent.type; + var nextProps = newVcomponent.props; + + var componentContext = getContextByTypes(parentContext, Component.contextTypes); + delete node.cache[uid]; + node.cache[newVcomponent.uid] = component; + cache.parentContext = parentContext; + if (component.componentWillReceiveProps) { + var needToggleIsPending = !updater.isPending; + if (needToggleIsPending) updater.isPending = true; + component.componentWillReceiveProps(nextProps, componentContext); + if (needToggleIsPending) updater.isPending = false; + } + + if (vcomponent.ref !== newVcomponent.ref) { + detachRef(vcomponent.refs, vcomponent.ref, component); + attachRef(newVcomponent.refs, newVcomponent.ref, component); + } + + updater.emitUpdate(nextProps, componentContext); + + return cache.node; +} + +function destroyVcomponent(vcomponent, node) { + var uid = vcomponent.uid; + var component = node.cache[uid]; + var cache = component.$cache; + delete node.cache[uid]; + detachRef(vcomponent.refs, vcomponent.ref, component); + component.setState = component.forceUpdate = _.noop; + if (component.componentWillUnmount) { + component.componentWillUnmount(); + } + destroyVnode(cache.vnode, node); + delete component.setState; + cache.isMounted = false; + cache.node = cache.parentContext = cache.vnode = component.refs = component.context = null; +} + +function getContextByTypes(curContext, contextTypes) { + var context = {}; + if (!contextTypes || !curContext) { + return context; + } + for (var key in contextTypes) { + if (contextTypes.hasOwnProperty(key)) { + context[key] = curContext[key]; + } + } + return context; +} + +function renderComponent(component, parentContext) { + refs = component.refs; + var vnode = component.render(); + if (vnode === null || vnode === false) { + vnode = createVnode(_constant.VCOMMENT); + } else if (!vnode || !vnode.vtype) { + throw new Error('@' + component.constructor.name + '#render:You may have returned undefined, an array or some other invalid object'); + } + refs = null; + return vnode; +} + +function getChildContext(component, parentContext) { + if (component.getChildContext) { + var curContext = component.getChildContext(); + if (curContext) { + parentContext = _.extend(_.extend({}, parentContext), curContext); + } + } + return parentContext; +} + +var pendingComponents = []; +function clearPendingComponents() { + var len = pendingComponents.length; + if (!len) { + return; + } + var components = pendingComponents; + pendingComponents = []; + var i = -1; + while (len--) { + var component = components[++i]; + var updater = component.$updater; + if (component.componentDidMount) { + component.componentDidMount(); + } + updater.isPending = false; + updater.emitUpdate(); + } +} + +var pendingRefs = []; +function clearPendingRefs() { + var len = pendingRefs.length; + if (!len) { + return; + } + var list = pendingRefs; + pendingRefs = []; + for (var i = 0; i < len; i += 2) { + var vnode = list[i]; + var refValue = list[i + 1]; + attachRef(vnode.refs, vnode.ref, refValue); + } +} + +function clearPending() { + clearPendingRefs(); + clearPendingComponents(); +} + +function compareTwoVnodes(vnode, newVnode, node, parentContext) { + var newNode = node; + if (newVnode == null) { + // remove + destroyVnode(vnode, node); + node.parentNode.removeChild(node); + } else if (vnode.type !== newVnode.type || vnode.key !== newVnode.key) { + // replace + destroyVnode(vnode, node); + newNode = initVnode(newVnode, parentContext, node.namespaceURI); + node.parentNode.replaceChild(newNode, node); + } else if (vnode !== newVnode || parentContext) { + // same type and same key -> update + newNode = updateVnode(vnode, newVnode, node, parentContext); + } + return newNode; +} + +function getDOMNode() { + return this; +} + +function attachRef(refs, refKey, refValue) { + if (refKey == null || !refValue) { + return; + } + if (refValue.nodeName && !refValue.getDOMNode) { + // support react v0.13 style: this.refs.myInput.getDOMNode() + refValue.getDOMNode = getDOMNode; + } + if (_.isFn(refKey)) { + refKey(refValue); + } else if (refs) { + refs[refKey] = refValue; + } +} + +function detachRef(refs, refKey, refValue) { + if (refKey == null) { + return; + } + if (_.isFn(refKey)) { + refKey(null); + } else if (refs && refs[refKey] === refValue) { + delete refs[refKey]; + } +} + +function syncCache(cache, oldCache, node) { + for (var key in oldCache) { + if (!oldCache.hasOwnProperty(key)) { + continue; + } + var value = oldCache[key]; + cache[key] = value; + + // is component, update component.$cache.node + if (value.forceUpdate) { + value.$cache.node = node; + } + } +} \ No newline at end of file diff --git a/package.json b/package.json index fd326cb..37d7858 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "scripts": { "test": "jest", "build:addons": "babel ./addons --out-dir ./lib", - "build": "node build.js && npm run build:addons", + "build:core": "babel ./src --out-dir ./lib", + "build": "node build.js && npm run build:core && npm run build:addons", "prepublish": "npm test && npm run build" }, "publishConfig": {