├── .gitignore ├── README.md ├── gulpfile.js ├── index.js ├── package.json ├── react ├── AbstractTextFieldMixin.react.js ├── AbstractTextInput.react.js ├── Grid.react.js ├── InlineBlock.react.js ├── PopoverMenu.react.js ├── ScrollableArea.react.js ├── SearchableTextInput.react.js ├── StarsInput.react.js ├── Timestamp.react.js ├── TooltipLink.react.js └── TooltipMixin.js └── src ├── $.js ├── .jshintrc ├── AjaxLoader.js ├── AjaxRequest.js ├── Arbiter.js ├── ArbiterMixin.js ├── AsyncUploadBase.js ├── AsyncUploadRequest.js ├── BaseAsyncLoader.js ├── BasicVector.js ├── BehaviorsMixin.js ├── BigPipe.js ├── BitMap.js ├── Bootloader.js ├── CSS.js ├── CSSCore.js ├── CallbackDependencyManager.js ├── CallbackManagerController.js ├── ChannelConstants.js ├── CircularBuffer.js ├── CurrentUser.js ├── DOM.js ├── DOMEvent.js ├── DOMEventListener.js ├── DOMQuery.js ├── DOMVector.js ├── DataStore.js ├── Deferred.js ├── EmitterSubscription.js ├── Env.js ├── ErrorUtils.js ├── Event.js ├── EventEmitter.js ├── EventEmitterWithHolding.js ├── EventEmitterWithValidation.js ├── EventHolder.js ├── EventSubscription.js ├── EventSubscriptionVendor.js ├── ExecutionEnvironment.js ├── FBAjaxRequest.js ├── FileForm.js ├── Focus.js ├── Form.js ├── HTML.js ├── ImmediateImplementation.js ├── Input.js ├── InputSelection.js ├── JSCC.js ├── JSONPTransport.js ├── KeyStatus.js ├── KeyedCallbackManager.js ├── Keys.js ├── LiveTimer.js ├── LogBuffer.js ├── NotificationConstants.js ├── NotificationSeenState.js ├── NotificationStore.js ├── NotificationTokens.js ├── NotificationUpdates.js ├── OnVisible.js ├── OnloadEvent.js ├── PHPQuerySerializer.js ├── PageletSet.js ├── Parent.js ├── Poller.js ├── PopoverMenu.js ├── Primer.js ├── QueryString.js ├── Run.js ├── Scrollable.js ├── ScrollableArea.js ├── ScrollingPager.js ├── ServerJSDefine.js ├── ServerTime.js ├── SimpleDrag.js ├── Stopwatch.js ├── StopwatchPool.js ├── Style-upstream.js ├── Style.js ├── TimeSlice.js ├── URI.js ├── URIBase.js ├── URIRFC3986.js ├── UserActivity.js ├── UserAgent_DEPRECATED.js ├── Vector.js ├── areEqual.js ├── arraySort.js ├── bind.js ├── camelize.js ├── camelizeStyleName.js ├── clickRefAction.js ├── coalesce.js ├── containsNode.js ├── copyProperties.js ├── createArrayFrom.js ├── createArrayFromMixed.js ├── createNodesFromMarkup.js ├── createObjectFrom.js ├── debounce.js ├── debounceAcrossTransitions.js ├── debounceCore.js ├── emptyFunction.js ├── enforceMaxLength.js ├── eprintf.js ├── erx.js ├── escapeJSQuotes.js ├── escapeTextForBrowser.js ├── evalGlobal.js ├── event-form-bubbling.js ├── ex.js ├── fbt.js ├── fillArray.js ├── filterObject.js ├── flattenArray.js ├── forEachObject.js ├── ge.js ├── getActiveElement.js ├── getDocumentScrollElement.js ├── getElementPosition.js ├── getElementRect.js ├── getElementText.js ├── getMarkupWrap.js ├── getObjectValues.js ├── getOpacityStyleName.js ├── getOrCreateDOMID.js ├── getSameOriginTransport.js ├── getStyleProperty.js ├── getUnboundedScrollPosition.js ├── getViewportDimensions.js ├── guid.js ├── hyphenate.js ├── invariant.js ├── invokeCallbacks.js ├── isElementNode.js ├── isEmpty.js ├── isInIframe.js ├── isNode.js ├── isScalar.js ├── isTextNode.js ├── ix.js ├── joinClasses.js ├── loadImage.js ├── mapObject.js ├── memoize.js ├── memoizeStringOnly.js ├── mergeObjects.js ├── mixin.js ├── performance.js ├── performanceAbsoluteNow.js ├── performanceNow.js ├── queryThenMutateDOM.js ├── removeFromArray.js ├── repeatString.js ├── replaceTransportMarkers.js ├── setImmediatePolyfill.js ├── setIntervalAcrossTransitions.js ├── setTimeoutAcrossTransitions.js ├── shield.js ├── substituteTokens.js ├── throttle.js ├── toArray.js ├── uniqueID.js ├── unqualifyURI.js └── wrapFunction.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidxi/js-code-study/9b990abbf038e0195567b30c8da216498c23691b/README.md -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | var gulp = require('gulp'); 4 | var browserify = require('browserify'), 5 | jshint = require('gulp-jshint'), 6 | source = require('vinyl-source-stream'), 7 | rename = require('gulp-rename'), 8 | runSequence = require('run-sequence'), 9 | uglify = require('gulp-uglify'); 10 | 11 | gulp.task('browserify', [], function() { 12 | var options = { 13 | entries: path.join(__dirname, './index.js') 14 | }; 15 | return browserify(options) 16 | .bundle() 17 | .pipe(source('bundle.js')) 18 | .pipe(gulp.dest('./build')) 19 | }); 20 | 21 | gulp.task('lint', [], function() { 22 | return gulp.src(['src/**/*.js', 'react/**/*.js']) 23 | .pipe(jshint()) 24 | .pipe(jshint.reporter('jshint-stylish')); 25 | }); 26 | 27 | gulp.task('compress', function() { 28 | gulp.src('build/*.js') 29 | .pipe(uglify()) 30 | .pipe(rename(function (path) { 31 | path.basename += "-min"; 32 | })) 33 | .pipe(gulp.dest('build')); 34 | }); 35 | 36 | gulp.task('default', [], function() { 37 | runSequence(['lint'], ['browserify'], ['compress']); 38 | }); 39 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = require('./src/Arbiter.js'); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-code-study", 3 | "devDependencies": { 4 | "browserify": "^5.9.1", 5 | "vinyl-source-stream": "^0.1.1", 6 | "jshint-stylish": "1.0.0", 7 | 8 | "gulp": "~3.5.2", 9 | "gulp-concat": "~2.2.0", 10 | "gulp-if": "~1.2.4", 11 | "gulp-jshint": "1.8.5", 12 | "gulp-rimraf": "0.0.9", 13 | "gulp-rename": "1.2.0", 14 | "gulp-tap": "0.1.1", 15 | "gulp-util": "~2.2.14", 16 | "gulp-uglify": "~1.0.1", 17 | "run-sequence": "^0.3.6", 18 | "streamqueue": "0.0.4", 19 | "through": "2.3.4" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /react/AbstractTextInput.react.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule AbstractTextInput.react 3 | */ 4 | var AbstractTextFieldMixin = require('./AbstractTextFieldMixin.react'); 5 | var React = require('./React'); 6 | 7 | var $AbstractTextInput = React.createClass({ 8 | displayName: "AbstractTextInput", 9 | mixins: [AbstractTextFieldMixin], 10 | renderTextField: function() { 11 | return this.setTextFieldPropsOn(React.createElement("input", { 12 | type: "text", 13 | /* 14 | ._58al { 15 | background: transparent; 16 | border: 0; 17 | margin: 0; 18 | outline: 0; 19 | padding: 0; 20 | width: 100% 21 | } 22 | */ 23 | className: "_58al", 24 | size: this.props.size, 25 | tabIndex: this.props.tabIndex, 26 | onClick: this.props.onClick, 27 | onKeyUp: this.props.onKeyUp, 28 | onPaste: this.props.onPaste 29 | })); 30 | } 31 | }); 32 | module.exports = $AbstractTextInput; -------------------------------------------------------------------------------- /react/InlineBlock.react.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule InlineBlock.react 3 | */ 4 | /* jshint eqnull: true */ 5 | var React = require('./React'); 6 | var joinClasses = require('./joinClasses'); 7 | 8 | var alignvClassNames = { 9 | baseline: null, 10 | bottom: "_6d", 11 | middle: "_6b", 12 | top: "_6e" 13 | }; 14 | /* 15 | ._6d { 16 | vertical-align: bottom 17 | } 18 | ._6b { 19 | vertical-align: middle 20 | } 21 | ._6e { 22 | vertical-align: top 23 | } 24 | */ 25 | 26 | var $InlineBlock = React.createClass({ 27 | displayName: "InlineBlock", 28 | propTypes: { 29 | alignv: React.PropTypes.oneOf(['baseline', 'bottom', 'middle', 'top']), 30 | height: React.PropTypes.number, 31 | fullWidth: React.PropTypes.bool 32 | }, 33 | getDefaultProps: function() { 34 | return { 35 | alignv: 'baseline', 36 | fullWidth: false 37 | }; 38 | }, 39 | render: function() { 40 | var alignvClassName = alignvClassNames[this.props.alignv]; 41 | /* 42 | ._6a { 43 | display: inline-block 44 | } 45 | */ 46 | var inlineClassName = "_6a"; 47 | if (this.props.fullWidth) { 48 | /* 49 | ._5u5j { 50 | width: 100% 51 | } 52 | */ 53 | inlineClassName = joinClasses(inlineClassName, "_5u5j"); 54 | } 55 | var contentClassName = joinClasses(inlineClassName, alignvClassName); 56 | var children = null; 57 | 58 | if (this.props.height != null) { 59 | var fixedHeightDiv = React.createElement("div", { 60 | className: joinClasses("_6a", alignvClassName), 61 | style: { 62 | height: this.props.height + 'px' 63 | } 64 | }); 65 | 66 | children = [ 67 | fixedHeightDiv, 68 | React.createElement("div", { 69 | className: contentClassName 70 | }, this.props.children) 71 | ]; 72 | 73 | return React.createElement("div", React.__spread({}, this.props, { 74 | className: joinClasses(this.props.className, inlineClassName), 75 | height: null 76 | }), children); 77 | 78 | } else { 79 | 80 | children = this.props.children; 81 | return React.createElement("div", React.__spread({}, this.props, { 82 | className: joinClasses(this.props.className, contentClassName) 83 | }), children); 84 | } 85 | } 86 | }); 87 | module.exports = $InlineBlock; -------------------------------------------------------------------------------- /react/ScrollableArea.react.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule ScrollableArea.react 3 | */ 4 | var Bootloader = require('./Bootloader'); 5 | var React = require('./React'); 6 | var Style = require('./Style'); 7 | var joinClasses = require('./joinClasses'); 8 | 9 | require('./Scrollable'); 10 | 11 | var $ScrollableArea = React.createClass({ 12 | displayName: "ReactScrollableArea", 13 | propTypes: { 14 | width: React.PropTypes.number, 15 | height: React.PropTypes.oneOfType([React.PropTypes.number, React.PropTypes.string]), 16 | onScroll: React.PropTypes.func, 17 | shadow: React.PropTypes.bool, 18 | fade: React.PropTypes.bool, 19 | persistent: React.PropTypes.bool 20 | }, 21 | render: function() { 22 | var containerStyle = { 23 | height: this.props.height 24 | }; 25 | return (React.createElement("div", React.__spread({}, this.props, { 26 | className: joinClasses(this.props.className, 'uiScrollableArea native'), 27 | ref: "root", 28 | style: Object.assign({}, (this.props.style || {}), containerStyle) 29 | }), React.createElement("div", { 30 | className: "uiScrollableAreaWrap scrollable", 31 | ref: "wrap" 32 | }, React.createElement("div", { 33 | className: 'uiScrollableAreaBody', 34 | ref: "body" 35 | }, React.createElement("div", { 36 | className: 'uiScrollableAreaContent' 37 | }, this.props.children))))); 38 | }, 39 | getArea: function() { 40 | return this._area; 41 | }, 42 | componentDidMount: function() { 43 | Bootloader.loadModules(["ScrollableArea"], this._loadScrollableArea); 44 | }, 45 | componentDidUpdate: function(prevProps) { 46 | if (prevProps.width !== this.props.width) { 47 | this._setWidthFromProps(); 48 | var scrollAreaInstance = this.getArea(); 49 | scrollAreaInstance.setScrollTop(scrollAreaInstance.getScrollHeight(), false); 50 | } 51 | }, 52 | _loadScrollableArea: function(scrollAreaInstance) { 53 | this._area = scrollAreaInstance.fromNative(this.refs.root.getDOMNode(), { 54 | fade: this.props.fade, 55 | persistent: this.props.persistent, 56 | shadow: this.props.shadow === undefined ? true : this.props.shadow 57 | }); 58 | this._setWidthFromProps(); 59 | if (this.props.onScroll) { 60 | this._area.subscribe('scroll', this.props.onScroll); 61 | } 62 | }, 63 | _setWidthFromProps: function() { 64 | var scrollAreaBodyNode = this.refs.body.getDOMNode(); 65 | Style.set(scrollAreaBodyNode, 'width', this.props.width + 'px'); 66 | } 67 | }); 68 | module.exports = $ScrollableArea; -------------------------------------------------------------------------------- /react/SearchableTextInput.react.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule SearchableTextInput.react 3 | */ 4 | var EventListener = require('./EventListener'); 5 | var React = require('./React'); 6 | var AbstractTextFieldMixin = require('./AbstractTextFieldMixin.react'); 7 | var $AbstractTextInput = require('./AbstractTextInput.react'); 8 | var getActiveElement = require('./getActiveElement'); 9 | var merge = require('./merge'); 10 | 11 | var $SearchableTextInput = React.createClass({ 12 | displayName: "SearchableTextInput", 13 | propTypes: merge(AbstractTextFieldMixin.propTypes, { 14 | queryString: React.PropTypes.string, 15 | searchSource: React.PropTypes.object, 16 | searchSourceOptions: React.PropTypes.object, 17 | onEntriesFound: React.PropTypes.func.isRequired, 18 | searchOnFocus: React.PropTypes.bool, 19 | searchOnUpdate: React.PropTypes.bool, 20 | onPaste: React.PropTypes.func, 21 | onFocus: React.PropTypes.func, 22 | onChange: React.PropTypes.func 23 | }), 24 | componentDidMount: function() { 25 | if (this.props.onPaste) { 26 | this._listener = EventListener.listen(this.refs.input.getTextFieldDOM(), 'paste', this.props.onPaste); 27 | } 28 | }, 29 | componentWillReceiveProps: function() {}, 30 | componentDidUpdate: function(prevProps) { 31 | if (this.props.searchOnUpdate) { 32 | if (prevProps.queryString !== this.props.queryString) { 33 | this.search(this.props.queryString); 34 | } 35 | } 36 | }, 37 | componentWillUnmount: function() { 38 | if (this._listener) { 39 | this._listener.remove(); 40 | this._listener = null; 41 | } 42 | }, 43 | _onInputFocus: function() { 44 | this.props.searchSource.bootstrap(function() { 45 | if (this.props.searchOnFocus) { 46 | this.search(this.props.queryString); 47 | } 48 | }.bind(this)); 49 | /*jshint -W030 */ 50 | this.props.onFocus && this.props.onFocus(); 51 | /*jshint +W030 */ 52 | }, 53 | _onSearchCallback: function(matched, queryString) { 54 | if (this.props.queryString === queryString) { // async 55 | this.props.onEntriesFound(matched); 56 | } 57 | }, 58 | _onChange: function(event) { 59 | /*jshint -W030 */ 60 | this.props.onChange && this.props.onChange(event); 61 | /*jshint +W030 */ 62 | var queryValue = event.target.value; 63 | setTimeout(function() { 64 | this.search(queryValue); 65 | }.bind(this)); 66 | }, 67 | search: function(queryString) { 68 | this.props.searchSource.search(queryString, this._onSearchCallback, this.props.searchSourceOptions); 69 | }, 70 | focusInput: function() { 71 | var textFieldNode = this.getTextFieldDOM(); 72 | if (getActiveElement() === textFieldNode) { 73 | this._onInputFocus(); 74 | } else { 75 | // 'offsetHeight' = 0 when it is in hidden display 76 | /*jshint -W030 */ 77 | textFieldNode.offsetHeight && textFieldNode.focus(); 78 | /*jshint +W030 */ 79 | } 80 | }, 81 | blurInput: function() { 82 | var textFieldNode = this.getTextFieldDOM(); 83 | // 'offsetHeight' = 0 when it is in hidden display 84 | /*jshint -W030 */ 85 | textFieldNode.offsetHeight && textFieldNode.blur(); 86 | /*jshint +W030 */ 87 | }, 88 | getTextFieldDOM: function() { 89 | return this.refs.input.getTextFieldDOM(); 90 | }, 91 | render: function() { 92 | var queryString = this.props.queryString || ''; 93 | return (React.createElement($AbstractTextInput, React.__spread({}, this.props, { 94 | "aria-label": queryString, 95 | onChange: this._onChange, 96 | onFocus: this._onInputFocus, 97 | ref: "input", 98 | role: "combobox", 99 | value: queryString 100 | }))); 101 | } 102 | }); 103 | module.exports = $SearchableTextInput; -------------------------------------------------------------------------------- /react/StarsInput.react.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule StarsInput.react 3 | */ 4 | var React = require('./React'); 5 | var $TooltipLink = require('./TooltipLink.react'); 6 | 7 | var $StarsInput = React.createClass({ 8 | displayName: "StarsInput", 9 | propTypes: { 10 | allowMultipleSubmissions: React.PropTypes.bool, 11 | onClick: React.PropTypes.func.isRequired, 12 | starLabels: React.PropTypes.array 13 | }, 14 | getDefaultProps: function() { 15 | return { 16 | allowMultipleSubmissions: false, 17 | starLabels: ["Poor", "Fair", "Good", "Very Good", "Excellent"] 18 | }; 19 | }, 20 | getInitialState: function() { 21 | return { 22 | starRating: 0, 23 | starsShown: 0, 24 | canUpdate: true 25 | }; 26 | }, 27 | _getStarRating: function(l) { 28 | return parseInt(l.split('.').pop(), 10) + 1; 29 | }, 30 | onMouseEnter: function(event) { 31 | if (this.state.canUpdate) { 32 | this.setState({ 33 | // @todo: in which module is 'dispatchMarker' added to event ? 34 | starsShown: this._getStarRating(event.dispatchMarker) 35 | }); 36 | } 37 | }, 38 | onMouseLeave: function() { 39 | if (this.state.canUpdate) { 40 | var starRating = this.state.starRating; 41 | this.setState({ 42 | starsShown: starRating 43 | }); 44 | } 45 | }, 46 | onClick: function(event) { 47 | if (this.state.canUpdate) { 48 | var starRating = this._getStarRating(event.dispatchMarker); 49 | this.setState({ 50 | starRating: starRating, 51 | starsShown: starRating, 52 | canUpdate: this.props.allowMultipleSubmissions 53 | }); 54 | this.props.onClick(starRating); 55 | } 56 | }, 57 | getStars: function() { 58 | var labelsLen = this.props.starLabels.length, 59 | starElms = []; 60 | for (var n = 0; n < labelsLen; n++) { 61 | starElms.push(React.createElement($TooltipLink, { 62 | /* 63 | .mls { 64 | margin-left: 5px 65 | } 66 | .mlm { 67 | margin-left: 10px 68 | } 69 | .mll { 70 | margin-left: 20px 71 | } 72 | ._22mm { 73 | cursor: pointer; 74 | display: inline-block; 75 | height: 13px; 76 | width: 13px 77 | } 78 | */ 79 | className: (("mls") + (' ' + "_22mm") + (n >= this.state.starsShown ? ' ' + "_22mn" : '') + (n < this.state.starsShown ? ' ' + "_22mo" : '') + (!this.state.canUpdate ? ' ' + "_1g87" : '')), 80 | // @todo: 'n >= starsShown' and 'n < starsShown' both ? 81 | tooltip: this.props.starLabels[n], 82 | onMouseEnter: this.onMouseEnter, 83 | onMouseLeave: this.onMouseLeave, 84 | onClick: this.onClick, 85 | position: "above", 86 | alignH: "center" 87 | })); 88 | } 89 | return starElms; 90 | }, 91 | render: function() { 92 | return (React.createElement("div", null, this.getStars())); 93 | } 94 | }); 95 | module.exports = $StarsInput; -------------------------------------------------------------------------------- /react/Timestamp.react.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule Timestamp.react 3 | */ 4 | var LiveTimer = require('./LiveTimer'); 5 | var joinClasses = require('./joinClasses'); 6 | var React = require('./React'); 7 | 8 | var $Timestamp = React.createClass({ 9 | displayName: "Timestamp", 10 | propTypes: { 11 | autoUpdate: React.PropTypes.bool 12 | }, 13 | getDefaultProps: function() { 14 | return { 15 | autoUpdate: false 16 | }; 17 | }, 18 | componentDidMount: function() { 19 | if (this.props.autoUpdate) { 20 | LiveTimer.addTimeStamps(this.getDOMNode()); 21 | } 22 | }, 23 | componentDidUpdate: function(prevProps) { 24 | if (this.props.autoUpdate && this.props.time !== prevProps.time) { 25 | LiveTimer.loop(); 26 | } 27 | }, 28 | render: function() { 29 | var label = LiveTimer.renderRelativeTimeToServer(this.props.time, this.props.shorten); 30 | var props = React.__spread({}, this.props, { 31 | className: joinClasses(this.props.className, "livetimestamp"), 32 | title: this.props.verbose, 33 | "data-utime": this.props.time, 34 | "data-shorten": this.props.shorten ? true : null 35 | }); 36 | return React.createElement("abbr", props, label.text || this.props.text); 37 | } 38 | }); 39 | module.exports = $Timestamp; -------------------------------------------------------------------------------- /react/TooltipLink.react.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule TooltipLink.react 3 | */ 4 | var React = require('./React'); 5 | var TooltipMixin = require('./TooltipMixin'); 6 | 7 | var $TooltipLink = React.createClass({ 8 | displayName: "TooltipLink", 9 | mixins: [TooltipMixin], 10 | render: function() { 11 | return React.createElement("a", React.__spread({}, this.props), this.props.children); 12 | } 13 | }); 14 | module.exports = $TooltipLink; -------------------------------------------------------------------------------- /react/TooltipMixin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule TooltipMixin 3 | */ 4 | /*jshint eqnull:true */ 5 | var React = require('./React'); 6 | var Tooltip = require('./Tooltip'); 7 | var DOM = require('./DOM'); 8 | 9 | function isUseTooltip(props) { 10 | var m = props.tooltip; 11 | return m != null && typeof m !== 'string'; 12 | } 13 | var TooltipMixin = { 14 | propTypes: { 15 | tooltip: React.PropTypes.oneOfType([React.PropTypes.element, React.PropTypes.string]), 16 | position: React.PropTypes.oneOf(['above', 'below', 'left', 'right']), 17 | alignH: React.PropTypes.oneOf(['left', 'center', 'right']) 18 | }, 19 | getInitialState: function() { 20 | return { 21 | tooltipContainer: isUseTooltip(this.props) ? DOM.create('div') : null 22 | }; 23 | }, 24 | componentWillReceiveProps: function(nextProps) { 25 | var useTooltip = isUseTooltip(nextProps); 26 | var tooltipContainer = this.state.tooltipContainer; 27 | if (tooltipContainer && !useTooltip) { 28 | this.setState({ 29 | tooltipContainer: null 30 | }); 31 | } else if (!tooltipContainer && useTooltip) { 32 | this.setState({ 33 | tooltipContainer: DOM.create('div') 34 | }); 35 | } 36 | }, 37 | componentDidMount: function() { 38 | this._updateTooltip(); 39 | }, 40 | componentDidUpdate: function(prevProps, prevState) { 41 | if (prevState.tooltipContainer && !this.state.tooltipContainer) { 42 | this._cleanupContainer(prevState.tooltipContainer); 43 | } 44 | this._updateTooltip(); 45 | }, 46 | _updateTooltip: function() { 47 | var tooltipContainer; 48 | if (isUseTooltip(this.props)) { 49 | tooltipContainer = this.state.tooltipContainer; 50 | React.render(this.props.tooltip, tooltipContainer); 51 | } else { 52 | tooltipContainer = this.props.tooltip; 53 | } 54 | if (tooltipContainer != null) { 55 | Tooltip.set(this.getDOMNode(), tooltipContainer, this.props.position, this.props.alignH); 56 | } else { 57 | Tooltip.remove(this.getDOMNode()); 58 | } 59 | }, 60 | componentWillUnmount: function() { 61 | if (this.state.tooltipContainer) { 62 | this._cleanupContainer(this.state.tooltipContainer); 63 | } 64 | Tooltip.remove(this.getDOMNode()); 65 | }, 66 | _cleanupContainer: function(tooltipContainer) { 67 | React.unmountComponentAtNode(tooltipContainer); 68 | } 69 | }; 70 | module.exports = TooltipMixin; -------------------------------------------------------------------------------- /src/$.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule $ 3 | */ 4 | var ex = require('./ex.js'); 5 | 6 | function $(id) { 7 | var node = typeof id === 'string' ? 8 | document.getElementById(id) : 9 | id; 10 | if (!node) { 11 | throw new Error(ex('Tried to get element with id of "%s" but it is not present on the page.', id)); 12 | } 13 | return node; 14 | } 15 | 16 | function wrapper(id) { 17 | return $(id); 18 | } 19 | 20 | wrapper.unsafe = $; 21 | module.exports = wrapper; -------------------------------------------------------------------------------- /src/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "browser": true, 3 | "esnext": true, 4 | 5 | "bitwise": false, 6 | "boss": true, 7 | "devel": true, 8 | "eqnull": true, 9 | "expr": true, 10 | "evil": true, 11 | "funcscope": true, 12 | "globalstrict": false, 13 | "loopfunc": true, 14 | "newcap": false, 15 | "noempty": true, 16 | "nonstandard": true, 17 | "onecase": true, 18 | "regexdash": true, 19 | "scripturl": true, 20 | "sub": false, 21 | "trailing": true, 22 | "undef": true, 23 | "unused": "vars", 24 | 25 | "globals": { 26 | "__DEV__": false, 27 | "Event": true, 28 | "history": true, 29 | 30 | "global": true, 31 | "exports": false, 32 | "module": false, 33 | "require": false, 34 | 35 | "afterEach": false, 36 | "beforeEach": false, 37 | "describe": false, 38 | "expect": false, 39 | "it": false, 40 | "spyOn": false 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/AjaxLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule AjaxLoader 3 | */ 4 | var copyProperties = require('copyProperties'); 5 | var FBAjaxRequest = require('FBAjaxRequest'); 6 | var BaseAsyncLoader = require('BaseAsyncLoader'); 7 | 8 | function AjaxLoader(endpont, type) { 9 | // props used by BaseAsyncLoader::getLoader() 10 | this._endpoint = endpont; 11 | this._type = type; 12 | } 13 | 14 | copyProperties(AjaxLoader.prototype, BaseAsyncLoader.prototype); 15 | 16 | AjaxLoader.prototype.send = function(uri, ids, _, cbOnJSON, cbOnError) { 17 | var ajaxRequest = new FBAjaxRequest('GET', uri, { 18 | ids: ids 19 | }); 20 | ajaxRequest.onJSON = function(response) { 21 | cbOnJSON({ 22 | payload: response.json 23 | }); 24 | }; 25 | ajaxRequest.onError = cbOnError; 26 | ajaxRequest.send(); 27 | }; 28 | 29 | module.exports = AjaxLoader; -------------------------------------------------------------------------------- /src/ArbiterMixin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule ArbiterMixin 3 | */ 4 | var Arbiter = require('./Arbiter.js'); 5 | var guid = require('./guid.js'); 6 | 7 | var uid = "arbiter$" + guid(); 8 | var owns = Object.prototype.hasOwnProperty; 9 | 10 | var ArbiterMixin = { 11 | _getArbiterInstance: function() { 12 | return owns.call(this, uid) ? 13 | this[uid] : 14 | this[uid] = new Arbiter(); 15 | }, 16 | inform: function(l, m, n) { 17 | return this._getArbiterInstance().inform(l, m, n); 18 | }, 19 | subscribe: function(l, m, n) { 20 | return this._getArbiterInstance().subscribe(l, m, n); 21 | }, 22 | subscribeOnce: function(l, m, n) { 23 | return this._getArbiterInstance().subscribeOnce(l, m, n); 24 | }, 25 | unsubscribe: function(l) { 26 | this._getArbiterInstance().unsubscribe(l); 27 | }, 28 | unsubscribeCurrentSubscription: function() { 29 | this._getArbiterInstance().unsubscribeCurrentSubscription(); 30 | }, 31 | releaseCurrentPersistentEvent: function() { 32 | this._getArbiterInstance().releaseCurrentPersistentEvent(); 33 | }, 34 | registerCallback: function(l, m) { 35 | return this._getArbiterInstance().registerCallback(l, m); 36 | }, 37 | query: function(l) { 38 | return this._getArbiterInstance().query(l); 39 | } 40 | }; 41 | 42 | module.exports = ArbiterMixin; -------------------------------------------------------------------------------- /src/AsyncUploadRequest.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule AsyncUploadRequest 3 | */ 4 | var AsyncUploadBase = require('./AsyncUploadBase'); 5 | 6 | for (var h in AsyncUploadBase) { 7 | if (AsyncUploadBase.hasOwnProperty(h)) { 8 | AsyncUploadRequest[h] = AsyncUploadBase[h]; 9 | } 10 | } 11 | var protoAsyncUploadBase = AsyncUploadBase === null ? null : AsyncUploadBase.prototype; 12 | AsyncUploadRequest.prototype = Object.create(protoAsyncUploadBase); 13 | AsyncUploadRequest.prototype.constructor = AsyncUploadRequest; 14 | AsyncUploadRequest.__superConstructor__ = AsyncUploadBase; 15 | 16 | function AsyncUploadRequest() { 17 | if (AsyncUploadBase !== null) { 18 | AsyncUploadBase.apply(this, arguments); 19 | } 20 | } 21 | AsyncUploadRequest.prototype.setFiles = function(files) { 22 | this._files = AsyncUploadBase.parseFiles(files); 23 | return this; 24 | }; 25 | AsyncUploadRequest.prototype.abort = function() { 26 | /** 27 | * this._uploads[] --> array of AsyncUploadBase::FileUpload instances 28 | */ 29 | this._uploads.forEach(function(fileUpload) { 30 | return this._abort(fileUpload); 31 | }.bind(this), this); 32 | }; 33 | AsyncUploadRequest.prototype.send = function() { 34 | if (this._inFlight) { 35 | return; 36 | } 37 | this._inFlight = true; 38 | this._uploads = []; 39 | for (var name in this._files) { 40 | this._files[name].forEach(function(file) { 41 | this._uploads.push(this._createFileUpload(name, file)); 42 | }.bind(this)); 43 | } 44 | /** 45 | * this._waiting[] --> array of AsyncUploadBase::FileUpload instances 46 | */ 47 | this._waiting = this._uploads.slice(0); 48 | this._pending = []; 49 | if (this._uploads.length) { 50 | this._processQueue(); 51 | } else { 52 | this._processUpload(this._createFileUpload(null, null)); 53 | } 54 | }; 55 | AsyncUploadRequest.prototype._processQueue = function() { 56 | protoAsyncUploadBase._processQueue.call(this); 57 | if (!this._pending.length && !this._waiting.length) { 58 | this.inform('complete', this._uploads); 59 | } 60 | }; 61 | AsyncUploadRequest.isSupported = function() { 62 | return AsyncUploadBase.isSupported(); 63 | }; 64 | module.exports = AsyncUploadRequest; -------------------------------------------------------------------------------- /src/BaseAsyncLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule BaseAsyncLoader 3 | */ 4 | var KeyedCallbackManager = require('KeyedCallbackManager'); 5 | var copyProperties = require('copyProperties'); 6 | 7 | var cache = {}; 8 | 9 | // @param(sendFn) is mixed-in from another module AjaxRequest 10 | function getLoader(endpoint, type, sendFn) { 11 | var callbackController = new KeyedCallbackManager(); 12 | var hasUnavaiable = false; 13 | var unavaiableCallback = []; // due to unavaiable resources 14 | function nextCycle() { 15 | if (!unavaiableCallback.length || hasUnavaiable) { 16 | return; 17 | } 18 | hasUnavaiable = true; 19 | setTimeout(send, 0); 20 | } 21 | 22 | function complete(callbackIds) { 23 | hasUnavaiable = false; 24 | callbackIds.forEach(callbackController.unsubscribe.bind(callbackController)); 25 | nextCycle(); 26 | } 27 | 28 | function send() { 29 | var w = {}; 30 | var callbackIdsHasUnavaiable = []; 31 | 32 | unavaiableCallback = unavaiableCallback.filter(function(callbackId) { 33 | var unavaiable = callbackController.getUnavailableResources(callbackId); 34 | // unavaiable -> Array[request1, request2, ...] 35 | if (unavaiable.length) { 36 | unavaiable.forEach(function(request) { 37 | w[request] = true; 38 | }); 39 | callbackIdsHasUnavaiable.push(callbackId); 40 | return true; 41 | } 42 | return false; 43 | }); 44 | var unavaiableResources = Object.keys(w); 45 | if (unavaiableResources.length) { 46 | sendFn( 47 | endpoint, 48 | unavaiableResources, 49 | callbackIdsHasUnavaiable, 50 | done.bind(null, callbackIdsHasUnavaiable), 51 | fail.bind(null, callbackIdsHasUnavaiable) 52 | ); 53 | } else { 54 | hasUnavaiable = false; 55 | } 56 | } 57 | 58 | function done(callbackIds, x) { 59 | var resources = x.payload[type] || x.payload; 60 | callbackController.addResourcesAndExecute(resources); 61 | complete(callbackIds); 62 | } 63 | 64 | function fail(callbackIds) { // not sure of this function purpose 65 | complete(callbackIds); 66 | } 67 | return { 68 | get: function(requests, fn) { 69 | var callbackId = callbackController.executeOrEnqueue(requests, fn); 70 | var unavaiable = callbackController.getUnavailableResources(callbackId); 71 | if (unavaiable.length) { 72 | unavaiableCallback.push(callbackId); 73 | nextCycle(); 74 | } 75 | }, 76 | getCachedKeys: function() { 77 | return Object.keys(callbackController.getAllResources()); 78 | }, 79 | getNow: function(request /*string*/ ) { 80 | return callbackController.getResource(request) || null; 81 | }, 82 | set: function(resources /*object*/ ) { 83 | callbackController.addResourcesAndExecute(resources); 84 | } 85 | }; 86 | } 87 | 88 | function BaseAsyncLoader(l, m) { 89 | throw ('BaseAsyncLoader can\'t be instantiated'); 90 | } 91 | copyProperties(BaseAsyncLoader.prototype, { 92 | _getLoader: function() { 93 | cache[this._endpoint] || (cache[this._endpoint] = getLoader(this._endpoint, this._type, this.send)); 94 | return cache[this._endpoint]; 95 | }, 96 | get: function(requests, fn) { 97 | return this._getLoader().get(requests, fn); 98 | }, 99 | getCachedKeys: function() { 100 | return this._getLoader().getCachedKeys(); 101 | }, 102 | getNow: function(request /*string*/ ) { 103 | return this._getLoader().getNow(request); 104 | }, 105 | reset: function() { 106 | cache[this._endpoint] = null; 107 | }, 108 | set: function(resources /*object*/ ) { 109 | this._getLoader().set(resources); 110 | } 111 | }); 112 | module.exports = BaseAsyncLoader; -------------------------------------------------------------------------------- /src/BasicVector.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule BasicVector 3 | */ 4 | function BasicVector(x, y) { 5 | this.x = x; 6 | this.y = y; 7 | } 8 | BasicVector.prototype.derive = function(x, y) { 9 | return new BasicVector(x, y); 10 | }; 11 | BasicVector.prototype.toString = function() { 12 | return '(' + this.x + ', ' + this.y + ')'; 13 | }; 14 | BasicVector.prototype.add = function(x, y) { 15 | if (x instanceof BasicVector) { 16 | y = x.y; 17 | x = x.x; 18 | } 19 | return this.derive( 20 | this.x + parseFloat(x), 21 | this.y + parseFloat(y) 22 | ); 23 | }; 24 | BasicVector.prototype.mul = function(x, y) { 25 | if (y === undefined) { 26 | y = x; 27 | } 28 | return this.derive(this.x * x, this.y * y); 29 | }; 30 | BasicVector.prototype.div = function(x, y) { 31 | if (y === undefined) { 32 | y = x; 33 | } 34 | return this.derive(this.x * 1 / x, this.y * 1 / y); 35 | }; 36 | BasicVector.prototype.sub = function(x, y) { 37 | if (arguments.length === 1) { // @todo: should use instanceof, in case 'x.mul' is undefined ? 38 | return this.add(x.mul(-1)); 39 | } else { 40 | return this.add(-x, -y); 41 | } 42 | }; 43 | BasicVector.prototype.distanceTo = function(vector) { 44 | return this.sub(vector).magnitude(); 45 | }; 46 | BasicVector.prototype.magnitude = function() { 47 | return Math.sqrt((this.x * this.x) + (this.y * this.y)); 48 | }; 49 | BasicVector.prototype.rotate = function(angle) { 50 | return this.derive(this.x * Math.cos(angle) - this.y * Math.sin(angle), this.x * Math.sin(angle) + this.y * Math.cos(angle)); 51 | }; 52 | module.exports = BasicVector; -------------------------------------------------------------------------------- /src/BehaviorsMixin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule BehaviorsMixin 3 | */ 4 | var copyProperties = require('./copyProperties'); 5 | 6 | function BehaviorSubscription(behaviorInstance) { 7 | this._behavior = behaviorInstance; 8 | this._enabled = false; 9 | } 10 | copyProperties(BehaviorSubscription.prototype, { 11 | enable: function() { 12 | if (!this._enabled) { 13 | this._enabled = true; 14 | this._behavior.enable(); 15 | } 16 | }, 17 | disable: function() { 18 | if (this._enabled) { 19 | this._enabled = false; 20 | this._behavior.disable(); 21 | } 22 | } 23 | }); 24 | 25 | var uid = 1; 26 | function markBehaviorId(l) { 27 | if (!l.__BEHAVIOR_ID) { 28 | l.__BEHAVIOR_ID = uid++; 29 | } 30 | return l.__BEHAVIOR_ID; 31 | } 32 | 33 | var BehaviorsMixin = { 34 | enableBehavior: function(BehaviorConstructor) { 35 | if (!this._behaviors) { 36 | this._behaviors = {}; 37 | } 38 | var id = markBehaviorId(BehaviorConstructor); 39 | if (!this._behaviors[id]) { 40 | this._behaviors[id] = new BehaviorSubscription(new BehaviorConstructor(this)); 41 | } 42 | this._behaviors[id].enable(); 43 | return this; 44 | }, 45 | disableBehavior: function(BehaviorConstructor) { 46 | if (this._behaviors) { 47 | var id = markBehaviorId(BehaviorConstructor); 48 | if (this._behaviors[id]) { 49 | this._behaviors[id].disable(); 50 | } 51 | } 52 | return this; 53 | }, 54 | enableBehaviors: function(BehaviorConstructors) { 55 | BehaviorConstructors.forEach(this.enableBehavior.bind(this)); 56 | return this; 57 | }, 58 | destroyBehaviors: function() { 59 | if (this._behaviors) { 60 | for (var id in this._behaviors) { 61 | this._behaviors[id].disable(); 62 | } 63 | this._behaviors = {}; 64 | } 65 | }, 66 | hasBehavior: function(BehaviorConstructor) { 67 | return this._behaviors && (markBehaviorId(BehaviorConstructor) in this._behaviors); 68 | } 69 | }; 70 | module.exports = BehaviorsMixin; -------------------------------------------------------------------------------- /src/BitMap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule BitMap 3 | */ 4 | var repeatString = require('repeatString'); 5 | 6 | var CHAR_MAP_64 = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_'; 7 | 8 | function BitMap() { 9 | this._bits = []; 10 | } 11 | BitMap.prototype.set = function(index) { 12 | this._bits[index] = 1; 13 | return this; 14 | }; 15 | BitMap.prototype.toString = function() { 16 | var binary = []; 17 | for (var m = 0; m < this._bits.length; m++) { 18 | binary.push(this._bits[m] ? 1 : 0); 19 | } 20 | return binary.length ? 21 | convertTo64Char(binary.join('')) : 22 | ''; 23 | }; 24 | BitMap.prototype.toCompressedString = function() { 25 | if (this._bits.length === 0) { 26 | return ''; 27 | } 28 | var binary = []; 29 | var lenConsecutiveSame = 1; 30 | var lastDigit = this._bits[0] || 0; 31 | var leadingDigit = lastDigit.toString(2); 32 | for (var p = 1; p < this._bits.length; p++) { 33 | var currentDigit = this._bits[p] || 0; 34 | if (currentDigit === lastDigit) { 35 | lenConsecutiveSame++; 36 | } else { 37 | binary.push(genUniqStr(lenConsecutiveSame)); 38 | lastDigit = currentDigit; 39 | lenConsecutiveSame = 1; 40 | } 41 | } 42 | if (lenConsecutiveSame) { 43 | binary.push(genUniqStr(lenConsecutiveSame)); 44 | } 45 | return convertTo64Char(leadingDigit + binary.join('')); 46 | }; 47 | 48 | function genUniqStr(num) { // @todo: not sure this is for generating unique str purpose ? 49 | var m = num.toString(2); 50 | var n = repeatString('0', m.length - 1); 51 | return n + m; 52 | } 53 | 54 | function convertTo64Char(binary) { 55 | // 2^6 --> 64 --> CHAR_MAP_64.length 56 | var everySixDigits = (binary + '00000').match(/[01]{6}/g); 57 | var ret = ''; 58 | for (var o = 0; o < everySixDigits.length; o++) 59 | ret += CHAR_MAP_64[parseInt(everySixDigits[o], 2)]; 60 | return ret; 61 | } 62 | module.exports = BitMap; -------------------------------------------------------------------------------- /src/CSS.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule CSS 3 | */ 4 | var CSSCore = require('./CSSCore.js'); 5 | var $ = require('./$.js').unsafe; 6 | 7 | // .hidden_elem {display: none !important;} 8 | var hiddenClassName = 'hidden_elem'; 9 | 10 | var CSS = { 11 | setClass: function(nodeId, className) { 12 | $(nodeId).className = className || ''; 13 | return nodeId; 14 | }, 15 | hasClass: function(nodeId, className) { 16 | return CSSCore.hasClass($(nodeId), className); 17 | }, 18 | addClass: function(nodeId, className) { 19 | return CSSCore.addClass($(nodeId), className); 20 | }, 21 | removeClass: function(nodeId, className) { 22 | return CSSCore.removeClass($(nodeId), className); 23 | }, 24 | conditionClass: function(nodeId, className, bool) { 25 | return CSSCore.conditionClass($(nodeId), className, bool); 26 | }, 27 | toggleClass: function(nodeId, className) { 28 | return CSS.conditionClass(nodeId, className, !CSS.hasClass(nodeId, className)); 29 | }, 30 | shown: function(nodeId) { 31 | return !CSS.hasClass(nodeId, hiddenClassName); 32 | }, 33 | hide: function(nodeId) { 34 | return CSS.addClass(nodeId, hiddenClassName); 35 | }, 36 | show: function(nodeId) { 37 | return CSS.removeClass(nodeId, hiddenClassName); 38 | }, 39 | toggle: function(nodeId) { 40 | return CSS.toggleClass(nodeId, hiddenClassName); 41 | }, 42 | conditionShow: function(nodeId, bool) { 43 | return CSS.conditionClass(nodeId, hiddenClassName, !bool); 44 | } 45 | }; 46 | 47 | module.exports = CSS; -------------------------------------------------------------------------------- /src/CSSCore.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule CSSCore 3 | */ 4 | var invariant = require('invariant'); 5 | 6 | /** 7 | * The CSSCore module specifies the API (and implements most of the methods) 8 | * that should be used when dealing with the display of elements (via their 9 | * CSS classes and visibility on screen. It is an API focused on mutating the 10 | * display and not reading it as no logical state should be encoded in the 11 | * display of elements. 12 | */ 13 | 14 | var CSSCore = { 15 | 16 | /** 17 | * Adds the class passed in to the element if it doesn't already have it. 18 | * 19 | * @param {DOMElement} element the element to set the class on 20 | * @param {string} className the CSS className 21 | * @return {DOMElement} the element passed in 22 | */ 23 | addClass: function(element, className) { 24 | invariant(!/\s/.test(className), 25 | 'CSSCore.addClass takes only a single class name. "%s" contains ' + 26 | 'multiple classes.', className 27 | ); 28 | 29 | if (className) { 30 | if (element.classList) { 31 | element.classList.add(className); 32 | } else if (!CSSCore.hasClass(element, className)) { 33 | element.className = element.className + ' ' + className; 34 | } 35 | } 36 | return element; 37 | }, 38 | 39 | /** 40 | * Removes the class passed in from the element 41 | * 42 | * @param {DOMElement} element the element to set the class on 43 | * @param {string} className the CSS className 44 | * @return {DOMElement} the element passed in 45 | */ 46 | removeClass: function(element, className) { 47 | invariant(!/\s/.test(className), 48 | 'CSSCore.removeClass takes only a single class name. "%s" contains ' + 49 | 'multiple classes.', className 50 | ); 51 | 52 | if (className) { 53 | if (element.classList) { 54 | element.classList.remove(className); 55 | } else if (CSSCore.hasClass(element, className)) { 56 | element.className = element.className 57 | .replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)', 'g'), '$1') 58 | .replace(/\s+/g, ' ') // multiple spaces to one 59 | .replace(/^\s*|\s*$/g, ''); // trim the ends 60 | } 61 | } 62 | return element; 63 | }, 64 | 65 | /** 66 | * Helper to add or remove a class from an element based on a condition. 67 | * 68 | * @param {DOMElement} element the element to set the class on 69 | * @param {string} className the CSS className 70 | * @param {*} bool condition to whether to add or remove the class 71 | * @return {DOMElement} the element passed in 72 | */ 73 | conditionClass: function(element, className, bool) { 74 | return (bool ? CSSCore.addClass : CSSCore.removeClass)(element, className); 75 | }, 76 | 77 | /** 78 | * Tests whether the element has the class specified. 79 | * 80 | * @param {DOMNode|DOMWindow} element the element to set the class on 81 | * @param {string} className the CSS className 82 | * @return {boolean} true if the element has the class, false if not 83 | */ 84 | hasClass: function(element, className) { 85 | invariant(!/\s/.test(className), 86 | 'CSS.hasClass takes only a single class name.' 87 | ); 88 | if (element.classList) { 89 | return !!className && element.classList.contains(className); 90 | } 91 | return (' ' + element.className + ' ').indexOf(' ' + className + ' ') > -1; 92 | } 93 | 94 | }; 95 | 96 | module.exports = CSSCore; -------------------------------------------------------------------------------- /src/CallbackDependencyManager.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule CallbackDependencyManager 3 | */ 4 | var ErrorUtils = require('./ErrorUtils'); 5 | 6 | function CallbackDependencyManager() { 7 | this.dependencyId = 1; 8 | this.storedDeps = {}; 9 | this.countMap = {}; 10 | this.persistendDepsSatisfied = {}; 11 | } 12 | 13 | CallbackDependencyManager.prototype._markDependencyRelationship = function(depId, names) { 14 | var affected = 0, 15 | involved = {}, 16 | i = names.length; 17 | while (i--) { 18 | involved[names[i]] = true; 19 | } 20 | Object.keys(involved).forEach(function(name) { 21 | if (this.persistendDepsSatisfied[name]) return; 22 | affected++; 23 | this.countMap[name] || (this.countMap[name] = {}); 24 | this.countMap[name][depId] = (this.countMap[name][depId] || 0) + 1; 25 | }.bind(this)); 26 | return affected; 27 | }; 28 | 29 | CallbackDependencyManager.prototype.addDependencyToExistingCallback = function(depId, eventNames) { 30 | if (!this.storedDeps[depId]) return; 31 | var refCount = this._markDependencyRelationship(depId, eventNames); 32 | this.storedDeps[depId].remaingCount += refCount; 33 | }; 34 | 35 | CallbackDependencyManager.prototype.registerCallback = function(callback, eventNames) { 36 | var token = this.dependencyId++; 37 | var refCount = this._markDependencyRelationship(token, eventNames); 38 | if (refCount <= 0) { 39 | ErrorUtils.applyWithGuard(callback); 40 | return null; 41 | } 42 | this.storedDeps[token] = new CallbackDependencyTask(callback, refCount); 43 | return token; 44 | }; 45 | 46 | CallbackDependencyManager.prototype._satisfyDependency = function(name) { 47 | if (!this.countMap[name]) return; // event not registered to track 48 | for (var depId in this.countMap[name]) { 49 | 50 | this.countMap[name][depId] --; // ref-- 51 | if (this.countMap[name][depId] <= 0) { 52 | delete this.countMap[name][depId]; 53 | } 54 | 55 | this.storedDeps[depId].remaingCount--; 56 | if (this.storedDeps[depId].remaingCount <= 0) { 57 | var callback = this.storedDeps[depId].callback; 58 | delete this.storedDeps[depId]; // avoid multiple invoke callback 59 | ErrorUtils.applyWithGuard(callback); 60 | } 61 | } 62 | }; 63 | 64 | CallbackDependencyManager.prototype.isPersistentDependencySatisfied = function(name) { 65 | return !!this.persistendDepsSatisfied[name]; 66 | }; 67 | 68 | CallbackDependencyManager.prototype.satisfyPersistentDependency = function(name) { 69 | this.persistendDepsSatisfied[name] = true; 70 | this._satisfyDependency(name); 71 | }; 72 | 73 | CallbackDependencyManager.prototype.unsatisfyPersistentDependency = function(name) { 74 | delete this.persistendDepsSatisfied[name]; 75 | }; 76 | 77 | CallbackDependencyManager.prototype.satisfyNonPersistentDependency = function(name) { 78 | var isPersistentDependencySatisfied = this.persistendDepsSatisfied[name]; 79 | isPersistentDependencySatisfied || (this.persistendDepsSatisfied[name] = true); 80 | this._satisfyDependency(name); 81 | isPersistentDependencySatisfied || (delete this.persistendDepsSatisfied[name]); 82 | }; 83 | 84 | function CallbackDependencyTask(callback, count) { 85 | this.callback = callback; 86 | this.remaingCount = count || 0; 87 | } 88 | 89 | module.exports = CallbackDependencyManager; -------------------------------------------------------------------------------- /src/CallbackManagerController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule CallbackManagerController 3 | */ 4 | var ErrorUtils = require('./ErrorUtils.js'); 5 | var copyProperties = require('./copyProperties.js'); 6 | 7 | function CallbackManagerController(callbackArgHandler) { 8 | this._pendingIDs = []; 9 | this._allRequests = [undefined]; 10 | // if callbackArgHandler(...) returns false, 11 | // callback would not be invoked. 12 | this._callbackArgHandler = callbackArgHandler; 13 | } 14 | 15 | copyProperties(CallbackManagerController.prototype, { 16 | executeOrEnqueue: function(requests/*Array*/, fn, opts) /*callbackId(int)*/ { 17 | opts = opts || {}; 18 | if (this._attemptCallback(fn, requests, opts)) { 19 | // fn is already being invoked 20 | return 0; 21 | } 22 | this._allRequests.push({ 23 | fn: fn, 24 | request: requests, 25 | options: opts 26 | }); 27 | var callbackId = this._allRequests.length - 1; 28 | this._pendingIDs.push(callbackId); 29 | return callbackId; 30 | }, 31 | unsubscribe: function(callbackId) { 32 | delete this._allRequests[callbackId]; 33 | }, 34 | reset: function() { 35 | this._allRequests = []; 36 | }, 37 | getRequest: function(callbackId) { 38 | return this._allRequests[callbackId]; 39 | }, 40 | runPossibleCallbacks: function() { 41 | var pendingIds = this._pendingIDs; 42 | this._pendingIDs = []; 43 | var toInvoke = []; 44 | pendingIds.forEach(function(pendingId) { 45 | var callback = this._allRequests[pendingId]; 46 | if (!callback) { 47 | // cause the following block uses 'delete', 48 | // so there are holes 49 | return; 50 | } 51 | if (this._callbackArgHandler(callback.request, callback.options)) { 52 | toInvoke.push(pendingId); 53 | } else { 54 | // send back to pending state 55 | this._pendingIDs.push(pendingId); 56 | } 57 | }.bind(this)); 58 | toInvoke.forEach(function(pendingId) { 59 | var callback = this._allRequests[pendingId]; 60 | delete this._allRequests[pendingId]; 61 | this._attemptCallback(callback.fn, callback.request, callback.options); 62 | }.bind(this)); 63 | }, 64 | _attemptCallback: function(fn, request, opts) { 65 | if (this._callbackArgHandler(request, opts)) { 66 | var n = { 67 | ids: request 68 | }; 69 | ErrorUtils.applyWithGuard(fn, n, opts); 70 | return true; 71 | } 72 | return false; 73 | } 74 | }); 75 | 76 | module.exports = CallbackManagerController; -------------------------------------------------------------------------------- /src/ChannelConstants.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule ChannelConstants 3 | */ 4 | var prefix = 'channel/'; 5 | 6 | module.exports = { 7 | ON_SHUTDOWN: prefix + 'shutdown', 8 | ON_INVALID_HISTORY: prefix + 'invalid_history', 9 | ON_CONFIG: prefix + 'config', 10 | ON_ENTER_STATE: prefix + 'enter_state', 11 | ON_EXIT_STATE: prefix + 'exit_state', 12 | ATTEMPT_RECONNECT: prefix + 'attempt_reconnect', 13 | RTI_SESSION: prefix + 'new_rti_address', 14 | SKYWALKER: prefix + 'skywalker', 15 | OK: 'ok', 16 | ERROR: 'error', 17 | ERROR_MAX: 'error_max', 18 | ERROR_MISSING: 'error_missing', 19 | ERROR_MSG_TYPE: 'error_msg_type', 20 | ERROR_SHUTDOWN: 'error_shutdown', 21 | ERROR_STALE: 'error_stale', 22 | SYS_OWNER: 'sys_owner', 23 | SYS_NONOWNER: 'sys_nonowner', 24 | SYS_ONLINE: 'sys_online', 25 | SYS_OFFLINE: 'sys_offline', 26 | SYS_TIMETRAVEL: 'sys_timetravel', 27 | HINT_AUTH: 'shutdown auth', 28 | HINT_CONN: 'shutdown conn', 29 | HINT_DISABLED: 'shutdown disabled', 30 | HINT_INVALID_STATE: 'shutdown invalid state', 31 | HINT_MAINT: 'shutdown maint', 32 | HINT_UNSUPPORTED: 'shutdown unsupported', 33 | reason_Unknown: 0, 34 | reason_AsyncError: 1, 35 | reason_TooLong: 2, 36 | reason_Refresh: 3, 37 | reason_RefreshDelay: 4, 38 | reason_UIRestart: 5, 39 | reason_NeedSeq: 6, 40 | reason_PrevFailed: 7, 41 | reason_IFrameLoadGiveUp: 8, 42 | reason_IFrameLoadRetry: 9, 43 | reason_IFrameLoadRetryWorked: 10, 44 | reason_PageTransitionRetry: 11, 45 | reason_IFrameLoadMaxSubdomain: 12, 46 | reason_NoChannelInfo: 13, 47 | reason_NoChannelHost: 14, 48 | CAPABILITY_VOIP_INTEROP: 8, 49 | CAPABILITY_VIDEO: 32, 50 | FANTAIL_DEBUG: 'DEBUG', 51 | FANTAIL_WARN: 'WARN', 52 | FANTAIL_INFO: 'INFO', 53 | FANTAIL_ERROR: 'ERROR', 54 | SUBSCRIBE: 'subscribe', 55 | UNSUBSCRIBE: 'unsubscribe', 56 | getArbiterType: function(type) { 57 | return prefix + 'message:' + type; 58 | }, 59 | getSkywalkerArbiterType: function(type) { 60 | return prefix + 'skywalker:' + type; 61 | } 62 | }; -------------------------------------------------------------------------------- /src/CircularBuffer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule CircularBuffer 3 | */ 4 | var invariant = require('./invariant'); 5 | 6 | function CircularBuffer(maxLen) { 7 | invariant(maxLen > 0); 8 | this._maxLen = maxLen; 9 | this._pointer = 0; 10 | this._buffer = []; 11 | } 12 | CircularBuffer.prototype.write = function(data) { 13 | if (this._buffer.length < this._maxLen) { 14 | this._buffer.push(data); 15 | } else { 16 | this._buffer[this._pointer] = data; 17 | this._pointer++; 18 | this._pointer %= this._maxLen; 19 | } 20 | return this; 21 | }; 22 | CircularBuffer.prototype.read = function() { 23 | return this._buffer. 24 | // circular -> [pointer - n] + [0 - pointer] 25 | slice(this._pointer). 26 | concat(this._buffer.slice(0, this._pointer)); 27 | }; 28 | CircularBuffer.prototype.clear = function() { 29 | this._pointer = 0; 30 | this._buffer = []; 31 | return this; 32 | }; 33 | module.exports = CircularBuffer; -------------------------------------------------------------------------------- /src/CurrentUser.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule CurrentUser 3 | */ 4 | var Cookie = require('./Cookie'); 5 | var CurrentUserInitialData = require('./CurrentUserInitialData'); 6 | 7 | var CurrentUser = { 8 | getID: function() { 9 | return CurrentUserInitialData.USER_ID; 10 | }, 11 | getAccountID: function() { 12 | return CurrentUserInitialData.ACCOUNT_ID; 13 | }, 14 | isLoggedIn: function() { 15 | return CurrentUserInitialData.USER_ID && 16 | CurrentUserInitialData.USER_ID !== '0'; 17 | }, 18 | isLoggedInNow: function() { 19 | if (!CurrentUser.isLoggedIn()) { 20 | return false; 21 | } 22 | if (CurrentUserInitialData.IS_INTERN_SITE) { 23 | return true; 24 | } 25 | if (CurrentUserInitialData.ORIGINAL_USER_ID) { 26 | return CurrentUserInitialData.ORIGINAL_USER_ID === Cookie.get('c_user'); 27 | } 28 | return CurrentUserInitialData.USER_ID === Cookie.get('c_user'); 29 | }, 30 | isEmployee: function() { 31 | return !!CurrentUserInitialData.IS_EMPLOYEE; 32 | }, 33 | isGray: function() { 34 | return !!CurrentUserInitialData.IS_GRAY; 35 | } 36 | }; 37 | module.exports = CurrentUser; -------------------------------------------------------------------------------- /src/DOMEvent.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule DOMEvent 3 | */ 4 | var invariant = require('invariant'); 5 | 6 | function DOMEvent(event) { 7 | this.event = event || window.event; 8 | invariant(typeof(this.event.srcElement) != 'unknown'); 9 | this.target = this.event.target || this.event.srcElement; 10 | } 11 | DOMEvent.prototype.preventDefault = function() { 12 | var event = this.event; 13 | if (event.preventDefault) { 14 | event.preventDefault(); 15 | if (!('defaultPrevented' in event)) { 16 | event.defaultPrevented = true; 17 | } 18 | } else { 19 | event.returnValue = false; 20 | } 21 | return this; 22 | }; 23 | DOMEvent.prototype.isDefaultPrevented = function() { 24 | var event = this.event; 25 | return ('defaultPrevented' in event) ? 26 | event.defaultPrevented : 27 | event.returnValue === false; 28 | }; 29 | DOMEvent.prototype.stopPropagation = function() { 30 | var event = this.event; 31 | event.stopPropagation ? 32 | event.stopPropagation() : 33 | event.cancelBubble = true; 34 | return this; 35 | }; 36 | DOMEvent.prototype.kill = function() { 37 | this.stopPropagation().preventDefault(); 38 | return this; 39 | }; 40 | DOMEvent.killThenCall = function(fn) { 41 | return function(event) { 42 | new DOMEvent(event).kill(); 43 | return fn(); 44 | }; 45 | }; 46 | module.exports = DOMEvent; -------------------------------------------------------------------------------- /src/DOMEventListener.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule DOMEventListener 3 | */ 4 | var wrapFunction = require('wrapFunction'); 5 | 6 | var add_, remove_; 7 | 8 | if (window.addEventListener) { 9 | add_ = function(elem, eventType, handler) { 10 | handler.wrapper = wrapFunction(handler, 'entry', 'DOMEventListener.add ' + eventType); 11 | elem.addEventListener(eventType, handler.wrapper, false); 12 | }; 13 | remove_ = function(elem, eventType, handler) { 14 | elem.removeEventListener(eventType, handler.wrapper, false); 15 | }; 16 | } else if (window.attachEvent) { 17 | add_ = function(elem, eventType, handler) { 18 | handler.wrapper = wrapFunction(handler, 'entry', 'DOMEventListener.add ' + eventType); 19 | elem.attachEvent('on' + eventType, handler.wrapper); 20 | }; 21 | remove_ = function(elem, eventType, handler) { 22 | elem.detachEvent('on' + eventType, handler.wrapper); 23 | }; 24 | } else { 25 | remove_ = add_ = function() {/* no-op */}; 26 | } 27 | 28 | var DOMEventListener = { 29 | add: function(elem, eventType, handler) { 30 | add_(elem, eventType, handler); 31 | return { 32 | remove: function() { 33 | remove_(elem, eventType, handler); 34 | // erase DOM reference in closure 35 | elem = null; 36 | } 37 | }; 38 | }, 39 | remove: remove_ 40 | }; 41 | module.exports = DOMEventListener; -------------------------------------------------------------------------------- /src/DOMVector.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule DOMVector 3 | */ 4 | var BasicVector = require('./BasicVector'); 5 | var getDocumentScrollElement = require('./getDocumentScrollElement'); 6 | var getElementPosition = require('./getElementPosition'); 7 | var getUnboundedScrollPosition = require('./getUnboundedScrollPosition'); 8 | var getViewportDimensions = require('./getViewportDimensions'); 9 | 10 | for (var l in BasicVector) { 11 | if (BasicVector.hasOwnProperty(l)) { 12 | DOMVector[l] = BasicVector[l]; 13 | } 14 | } 15 | var protoBasicVector = BasicVector === null ? 16 | null : 17 | BasicVector.prototype; 18 | DOMVector.prototype = Object.create(protoBasicVector); 19 | DOMVector.prototype.constructor = DOMVector; 20 | DOMVector.__superConstructor__ = BasicVector; 21 | 22 | 23 | function DOMVector(width, height, domain) { 24 | BasicVector.call(this, width, height); 25 | this.domain = domain || 'pure'; 26 | } 27 | DOMVector.prototype.derive = function(width, height, domain) { 28 | return new DOMVector(width, height, domain || this.domain); 29 | }; 30 | DOMVector.prototype.add = function(width, height) { 31 | if (width instanceof DOMVector && width.getDomain() !== 'pure') { 32 | width = width.convertTo(this.domain); 33 | } 34 | return protoBasicVector.add.call(this, width, height); 35 | }; 36 | DOMVector.prototype.convertTo = function(domain) { 37 | if (domain != 'pure' && domain != 'viewport' && domain != 'document') { 38 | return this.derive(0, 0); 39 | } 40 | if (domain == this.domain) { 41 | return this.derive(this.x, this.y, this.domain); 42 | } 43 | if (domain == 'pure') { 44 | return this.derive(this.x, this.y); 45 | } 46 | if (this.domain == 'pure') { 47 | return this.derive(0, 0); 48 | } 49 | var offset = DOMVector.getScrollPosition('document'); 50 | var x = this.x; 51 | var y = this.y; 52 | if (this.domain == 'document') { 53 | x -= offset.x; 54 | y -= offset.y; 55 | } else { 56 | x += offset.x; 57 | y += offset.y; 58 | } 59 | return this.derive(x, y, domain); 60 | }; 61 | DOMVector.prototype.getDomain = function() { 62 | return this.domain; 63 | }; 64 | DOMVector.from = function(width, height, domain) { 65 | return new DOMVector(width, height, domain); 66 | }; 67 | DOMVector.getScrollPosition = function(domain) { 68 | domain = domain || 'document'; 69 | var offset = getUnboundedScrollPosition(window); 70 | return this.from(offset.x, offset.y, 'document').convertTo(domain); 71 | }; 72 | DOMVector.getElementPosition = function(elem, domain) { 73 | domain = domain || 'document'; 74 | var offset = getElementPosition(elem); 75 | return this.from(offset.x, offset.y, 'viewport').convertTo(domain); 76 | }; 77 | DOMVector.getElementDimensions = function(elem) { 78 | return this.from(elem.offsetWidth, elem.offsetHeight); 79 | }; 80 | DOMVector.getViewportDimensions = function() { 81 | var dim = getViewportDimensions(); 82 | return this.from(dim.width, dim.height, 'viewport'); 83 | }; 84 | DOMVector.getViewportWithoutScrollbarDimensions = function() { 85 | var dim = getViewportDimensions.withoutScrollbars(); 86 | return this.from(dim.width, dim.height, 'viewport'); 87 | }; 88 | DOMVector.getDocumentDimensions = function(docRoot) { 89 | var dim = getDocumentScrollElement(docRoot); 90 | return this.from(dim.scrollWidth, dim.scrollHeight, 'document'); 91 | }; 92 | 93 | module.exports = DOMVector; -------------------------------------------------------------------------------- /src/DataStore.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule DataStore 3 | */ 4 | var isEmpty = require('./isEmpty.js'); 5 | 6 | var cache = {}, 7 | id = 1; 8 | 9 | function generateKey(m) { 10 | if (typeof m == 'string') { 11 | return 'str_' + m; 12 | } else { 13 | return 'elem_' + (m.__FB_TOKEN || (m.__FB_TOKEN = [id++]))[0]; 14 | } 15 | } 16 | 17 | function getStorage(namespace) { 18 | var keyWithPrefix = generateKey(namespace); 19 | return cache[keyWithPrefix] || (cache[keyWithPrefix] = {}); 20 | } 21 | 22 | var DataStore = { 23 | set: function(namespace /*{string|element_node}*/ , key, val) { 24 | if (!namespace) { 25 | throw new TypeError('DataStore.set: namespace is required, got ' + (typeof namespace)); 26 | } 27 | var storage = getStorage(namespace); 28 | storage[key] = val; 29 | return namespace; 30 | }, 31 | get: function(namespace /*{string|element_node}*/ , key, defaultValue) { 32 | if (!namespace) { 33 | throw new TypeError('DataStore.get: namespace is required, got ' + (typeof namespace)); 34 | } 35 | var storage = getStorage(namespace); 36 | var val = storage[key]; 37 | if (typeof val === 'undefined' && namespace.getAttribute) 38 | if (namespace.hasAttribute && !namespace.hasAttribute('data-' + key)) { 39 | val = undefined; 40 | } else { 41 | var r = namespace.getAttribute('data-' + key); 42 | val = (null === r) ? undefined : r; 43 | } 44 | if ((defaultValue !== undefined) && (val === undefined)) { 45 | val = storage[key] = defaultValue; 46 | } 47 | return val; 48 | }, 49 | remove: function(namespace, key) { 50 | if (!namespace) { 51 | throw new TypeError('DataStore.remove: namespace is required, got ' + (typeof namespace)); 52 | } 53 | var storage = getStorage(namespace); 54 | var val = storage[key]; 55 | delete storage[key]; 56 | if (isEmpty(storage)) { 57 | DataStore.purge(namespace); 58 | } 59 | return val; 60 | }, 61 | purge: function(namespace /*{string|element_node}*/ ) { 62 | delete cache[generateKey(namespace)]; 63 | }, 64 | _storage: cache 65 | }; 66 | 67 | module.exports = DataStore; -------------------------------------------------------------------------------- /src/EmitterSubscription.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule EmitterSubscription 3 | */ 4 | var EventSubscription = require('./EventSubscription'); 5 | 6 | for (var h in EventSubscription) { 7 | if (EventSubscription.hasOwnProperty(h)) { 8 | EmitterSubscription[h] = EventSubscription[h]; 9 | } 10 | } 11 | var protoEventSubscription = EventSubscription === null ? null : EventSubscription.prototype; 12 | EmitterSubscription.prototype = Object.create(protoEventSubscription); 13 | EmitterSubscription.prototype.constructor = EmitterSubscription; 14 | EmitterSubscription.__superConstructor__ = EventSubscription; 15 | 16 | function EmitterSubscription(subscriber, listener, context) { 17 | EventSubscription.call(this, subscriber); 18 | this.listener = listener; 19 | this.context = context; 20 | } 21 | module.exports = EmitterSubscription; -------------------------------------------------------------------------------- /src/Env.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule Env 3 | */ 4 | var copyProperties = require('./copyProperties.js'); 5 | 6 | var Env = { start: Date.now() }; 7 | 8 | if (global.Env) { 9 | copyProperties(Env, global.Env); 10 | global.Env = undefined; 11 | } 12 | 13 | module.exports = Env; -------------------------------------------------------------------------------- /src/EventEmitter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule EventEmitter 3 | */ 4 | var EmitterSubscription = require('./EmitterSubscription'); 5 | var ErrorUtils = require('./ErrorUtils'); 6 | var EventSubscriptionVendor = require('./EventSubscriptionVendor'); 7 | var emptyFunction = require('./emptyFunction'); 8 | var invariant = require('./invariant'); 9 | var StopwatchPool = require('./StopwatchPool'); 10 | var LogBuffer = require('./LogBuffer'); 11 | 12 | function EventEmitter() { 13 | this._eventSubscription = new EventSubscriptionVendor(); 14 | this._currentSubscription = null; 15 | } 16 | EventEmitter.prototype.addListener = function(eventType, listener, context) { 17 | var subscription = new EmitterSubscription(this._eventSubscription, listener, context); 18 | return this._eventSubscription.addSubscription(eventType, subscription); 19 | }; 20 | EventEmitter.prototype.once = function(eventType, listener, context) { 21 | var emitter = this; 22 | return this.addListener(eventType, function() { 23 | emitter.removeCurrentListener(); 24 | listener.apply(context, arguments); 25 | }); 26 | }; 27 | EventEmitter.prototype.removeAllListeners = function(eventType) { 28 | // 'eventType' can be undefined, in which case, to remove all subscrptions 29 | this._eventSubscription.removeAllSubscriptions(eventType); 30 | }; 31 | EventEmitter.prototype.removeCurrentListener = function() { 32 | invariant(!!this._currentSubscription); 33 | this._eventSubscription.removeSubscription(this._currentSubscription); 34 | }; 35 | EventEmitter.prototype.listeners = function(eventType) { 36 | var subscriptionsOfType = this._eventSubscription.getSubscriptionsForType(eventType); 37 | if (subscriptionsOfType) { 38 | // @todo: purpose of .filter(emptyFunction.thatReturnsTrue) ? 39 | return subscriptionsOfType.filter(emptyFunction.thatReturnsTrue).map(function(subscription) { 40 | return subscription.listener; 41 | }); 42 | } else { 43 | return []; 44 | } 45 | }; 46 | EventEmitter.prototype.emit = function(eventType) { 47 | var subscriptionsOfType = this._eventSubscription.getSubscriptionsForType(eventType); 48 | if (subscriptionsOfType) { 49 | var q = Object.keys(subscriptionsOfType); 50 | var stopWatch = StopwatchPool.acquire(); 51 | for (var s = 0; s < q.length; s++) { 52 | // @todo: 53 | // since subscriptionsOfType is an array, what's the purpose of doing 54 | // Object.keys(foo) + foo[Object.keys(foo)[i]] ? 55 | var t = q[s]; 56 | var subscription = subscriptionsOfType[t]; 57 | if (subscription) { 58 | this._currentSubscription = subscription; 59 | var listernerMeta = subscription.listener.__SMmeta || { 60 | name: subscription.listener.name || '' 61 | }; 62 | stopWatch.reset(); 63 | ErrorUtils.applyWithGuard( 64 | subscription.listener, // fn 65 | subscription.context, // context 66 | Array.prototype.slice.call(arguments, 1), // fn args 67 | null, 68 | 'EventEmitter:' + eventType // guart tag 69 | ); 70 | var timeElapsed = stopWatch.read(); 71 | LogBuffer.write('event_handler_performance', { 72 | functionMeta: listernerMeta, 73 | time: timeElapsed, 74 | context: eventType 75 | }); 76 | } 77 | } 78 | this._currentSubscription = null; 79 | } 80 | }; 81 | module.exports = EventEmitter; -------------------------------------------------------------------------------- /src/EventEmitterWithHolding.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule EventEmitterWithHolding 3 | */ 4 | function EventEmitterWithHolding(emitter, holder) { 5 | this._emitter = emitter; 6 | this._eventHolder = holder; 7 | this._currentEventToken = null; 8 | this._emittingHeldEvents = false; 9 | } 10 | 11 | EventEmitterWithHolding.prototype.addListener = function(eventType, listener, context) { 12 | return this._emitter.addListener(eventType, listener, context); 13 | }; 14 | 15 | EventEmitterWithHolding.prototype.once = function(eventType, listener, context) { 16 | return this._emitter.once(eventType, listener, context); 17 | }; 18 | 19 | EventEmitterWithHolding.prototype.addRetroactiveListener = function(eventType, listener, context) { 20 | var subscription = this._emitter.addListener(eventType, listener, context); 21 | 22 | this._emittingHeldEvents = true; 23 | this._eventHolder.emitToListener(eventType, listener, context); 24 | this._emittingHeldEvents = false; 25 | 26 | return subscription; 27 | }; 28 | 29 | EventEmitterWithHolding.prototype.removeAllListeners = function(eventType) { 30 | this._emitter.removeAllListeners(eventType); 31 | }; 32 | 33 | EventEmitterWithHolding.prototype.removeCurrentListener = function() { 34 | this._emitter.removeCurrentListener(); 35 | }; 36 | 37 | EventEmitterWithHolding.prototype.removeSubscription = function(subscription) { 38 | this._emitter.removeSubscription(subscription); 39 | }; 40 | 41 | EventEmitterWithHolding.prototype.listeners = function(eventType) { 42 | return this._emitter.listeners(eventType); 43 | }; 44 | 45 | EventEmitterWithHolding.prototype.emit = function(eventType, a, b, c, d, e, _) { 46 | this._emitter.emit(eventType, a, b, c, d, e, _); 47 | }; 48 | 49 | EventEmitterWithHolding.prototype.emitAndHold = function(eventType, a, b, c, d, e, _) { 50 | this._currentEventToken = this._eventHolder.holdEvent( 51 | eventType, 52 | a, b, c, d, e, _ 53 | ); 54 | this._emitter.emit(eventType, a, b, c, d, e, _); 55 | this._currentEventToken = null; 56 | }; 57 | 58 | EventEmitterWithHolding.prototype.releaseCurrentEvent = function() { 59 | if (this._currentEventToken !== null) { 60 | this._eventHolder.releaseEvent(this._currentEventToken); 61 | } else if (this._emittingHeldEvents) { 62 | this._eventHolder.releaseCurrentEvent(); 63 | } 64 | }; 65 | 66 | module.exports = EventEmitterWithHolding; -------------------------------------------------------------------------------- /src/EventEmitterWithValidation.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule EventEmitterWithValidation 3 | */ 4 | var EventEmitter = require('./EventEmitter.js'); 5 | 6 | for (var h in EventEmitter) { 7 | if (EventEmitter.hasOwnProperty(h)) EventEmitterWithValidation[h] = EventEmitter[h]; 8 | } 9 | 10 | var protoEmitter = EventEmitter === null ? null : EventEmitter.prototype; 11 | EventEmitterWithValidation.prototype = Object.create(protoEmitter); 12 | EventEmitterWithValidation.prototype.constructor = EventEmitterWithValidation; 13 | EventEmitterWithValidation.__superConstructor__ = EventEmitter; 14 | 15 | function EventEmitterWithValidation(m) { 16 | EventEmitter.call(this); 17 | this._allowedEventTypes = Object.keys(m); 18 | } 19 | EventEmitterWithValidation.prototype.emit = function(eventType) { 20 | validateEventType(eventType, this._allowedEventTypes); 21 | return protoEmitter.emit.apply(this, arguments); 22 | }; 23 | 24 | function validateEventType(eventType, allowedEventTypes) { 25 | if (allowedEventTypes.indexOf(eventType) === -1) { 26 | throw new TypeError(validationError(eventType, allowedEventTypes)); 27 | } 28 | } 29 | 30 | function validationError(m, n) { 31 | var o = 'Unknown event type "' + m + '". '; 32 | o += 'Known event types: ' + n.join(', ') + '.'; 33 | return o; 34 | } 35 | module.exports = EventEmitterWithValidation; 36 | -------------------------------------------------------------------------------- /src/EventHolder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule EventHolder 3 | */ 4 | var invariant = require('./invariant.js'); 5 | 6 | function EventHolder() { 7 | this._eventsByType = {}; 8 | this._currentEventTokens = []; 9 | } 10 | 11 | EventHolder.prototype.holdEvent = function(eventType, a, b, c, d, e, _) { 12 | this._eventsByType[eventType] = this._eventsByType[eventType] || []; 13 | var eventsOfType = this._eventsByType[eventType]; 14 | var token = { 15 | eventType: eventType, 16 | index: eventsOfType.length 17 | }; 18 | eventsOfType.push([a, b, c, d, e, _]); 19 | return token; 20 | }; 21 | 22 | EventHolder.prototype.emitToListener = function(eventType, listener, context) { 23 | var eventsOfType = this._eventsByType[eventType]; 24 | if (!eventsOfType) return; 25 | 26 | eventsOfType.forEach(function(eventObj, _) { 27 | if (!eventObj) return; 28 | this._currentEventTokens.push({ 29 | eventType: eventType, 30 | index: _ 31 | }); 32 | // eventObj = [a, b, c, d, e, _] 33 | listener.apply(context, eventObj); 34 | this._currentEventTokens.pop(); 35 | }.bind(this)); 36 | }; 37 | 38 | EventHolder.prototype.releaseCurrentEvent = function() { 39 | invariant(this._currentEventTokens.length); 40 | this.releaseEvent(this._currentEventTokens[this._currentEventTokens.length - 1]); 41 | }; 42 | 43 | EventHolder.prototype.releaseEvent = function(token) { 44 | // @todo: add validation in case undefiend null reference ? 45 | delete this._eventsByType[token.eventType][token.index]; 46 | }; 47 | 48 | EventHolder.prototype.releaseEventType = function(eventType) { 49 | this._eventsByType[eventType] = []; 50 | }; 51 | 52 | module.exports = EventHolder; -------------------------------------------------------------------------------- /src/EventSubscription.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule EventSubscription 3 | */ 4 | 5 | function EventSubscription(subscriber) { 6 | this.subscriber = subscriber; 7 | } 8 | EventSubscription.prototype.remove = function() { 9 | this.subscriber.removeSubscription(this); 10 | }; 11 | 12 | module.exports = EventSubscription; -------------------------------------------------------------------------------- /src/EventSubscriptionVendor.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule EventSubscriptionVendor 3 | */ 4 | var invariant = require('./invariant'); 5 | 6 | function EventSubscriptionVendor() { 7 | this._subscriptionByType = {}; 8 | } 9 | EventSubscriptionVendor.prototype.addSubscription = function(eventType, subscription) { 10 | // 'subscription' is an instance of EmitterSubscription {subscriber, listener, context} 11 | invariant(subscription.subscriber === this); 12 | if (!this._subscriptionByType[eventType]) { 13 | this._subscriptionByType[eventType] = []; 14 | } 15 | var key = this._subscriptionByType[eventType].length; 16 | this._subscriptionByType[eventType].push(subscription); 17 | subscription.eventType = eventType; 18 | subscription.key = key; 19 | return subscription; 20 | }; 21 | EventSubscriptionVendor.prototype.removeAllSubscriptions = function(eventType) { 22 | if (eventType === undefined) { 23 | this._subscriptionByType = {}; 24 | } else { 25 | delete this._subscriptionByType[eventType]; 26 | } 27 | }; 28 | EventSubscriptionVendor.prototype.removeSubscription = function(subscription) { 29 | // 'subscription' is an instance of EmitterSubscription {subscriber, listener, context} 30 | var eventType = subscription.evenType; 31 | var key = subscription.key; 32 | var subscriptionsOfType = this._subscriptionByType[eventType]; 33 | if (subscriptionsOfType) { 34 | delete subscriptionsOfType[key]; 35 | } 36 | }; 37 | EventSubscriptionVendor.prototype.getSubscriptionsForType = function(eventType) { 38 | return this._subscriptionByType[eventType]; 39 | }; 40 | 41 | module.exports = EventSubscriptionVendor; -------------------------------------------------------------------------------- /src/ExecutionEnvironment.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule ExecutionEnvironment 3 | */ 4 | var canUseDOM = !!( 5 | typeof window !== 'undefined' && 6 | window.document && 7 | window.document.createElement 8 | ); 9 | 10 | /** 11 | * Simple, lightweight module assisting with the detection and context of 12 | * Worker. Helps avoid circular dependencies and allows code to reason about 13 | * whether or not they are in a Worker, even if they never include the main 14 | * `ReactWorker` dependency. 15 | */ 16 | var ExecutionEnvironment = { 17 | 18 | canUseDOM: canUseDOM, 19 | 20 | canUseWorkers: typeof Worker !== 'undefined', 21 | 22 | canUseEventListeners: canUseDOM && !!(window.addEventListener || window.attachEvent), 23 | 24 | canUseViewport: canUseDOM && !!window.screen, 25 | 26 | isInWorker: !canUseDOM // For now, this is true - might change in the future. 27 | 28 | }; 29 | 30 | module.exports = ExecutionEnvironment; -------------------------------------------------------------------------------- /src/FBAjaxRequest.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule FBAjaxRequest 3 | */ 4 | var AjaxRequest = require('AjaxRequest'); 5 | var copyProperties = require('copyProperties'); 6 | var getAsyncParams = require('getAsyncParams'); 7 | 8 | function FBAjaxRequest(httpMethod, uri, params) { 9 | // copy global user data (token/id) to ajax params 10 | params = copyProperties(getAsyncParams(httpMethod), params); 11 | // intialize from base constructor 12 | var ajaxRequest = new AjaxRequest(httpMethod, uri, params); 13 | // do not use 'process on partial received data' mode 14 | ajaxRequest.streamMode = false; 15 | 16 | // overwrite _call() 17 | var _call_orig = ajaxRequest._call; 18 | ajaxRequest._call = function(method) { 19 | if (method == 'onJSON' && this.json) { 20 | if (this.json.error) { 21 | this.errorType = AjaxRequest.SERVER_ERROR; 22 | this.errorText = 'AsyncResponse error: ' + this.json.error; 23 | } 24 | this.json = this.json.payload; 25 | } 26 | _call_orig.apply(this, arguments); 27 | }; 28 | 29 | // overwrite send() 30 | ajaxRequest.ajaxReqSend = ajaxRequest.send; 31 | ajaxRequest.send = function(data) { 32 | this.ajaxReqSend(copyProperties(data, params)); 33 | }; 34 | 35 | return ajaxRequest; 36 | } 37 | module.exports = FBAjaxRequest; -------------------------------------------------------------------------------- /src/Focus.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule Focus 3 | */ 4 | var CSS = require('CSS'); 5 | var DOM = require('DOM'); 6 | var Event = require('Event'); 7 | var Run = require('Run'); 8 | var ge = require('ge'); 9 | 10 | var eventHandlers = {}, 11 | hasSetFocusEventRootListen; 12 | var Focus = { 13 | set: function(inputElem) { 14 | try { 15 | inputElem.tabIndex = inputElem.tabIndex; // @todo why ? 16 | inputElem.focus(); 17 | } catch (t) {} 18 | }, 19 | setWithoutOutline: function(inputElem) { 20 | // ._5f0v { 21 | // outline: none 22 | // } 23 | CSS.addClass(inputElem, "_5f0v"); 24 | var handlerSubscription = Event.listen(inputElem, 'blur', function() { 25 | CSS.removeClass(inputElem, "_5f0v"); 26 | handlerSubscription.remove(); // once 27 | }); 28 | Focus.set(inputElem); 29 | }, 30 | relocate: function(inputElem, relocateElem) { 31 | // ._3oxt { 32 | // outline: 1px dotted #3b5998; 33 | // outline-color: invert 34 | // } 35 | Focus.listen(inputElem, function(isFocusInEvent) { 36 | CSS.conditionClass(relocateElem, "_3oxt", isFocusInEvent); 37 | }); 38 | CSS.addClass(inputElem, "_5f0v"); 39 | }, 40 | listen: function(elem, listener) { 41 | setFocusEventRootListener(); 42 | var elemId = DOM.getID(elem); 43 | eventHandlers[elemId] = listener; 44 | Run.onLeave(removeEmptyElementHandler.bind(null, elemId)); 45 | } 46 | }; 47 | 48 | function setFocusEventRootListener() { 49 | if (hasSetFocusEventRootListen) { 50 | return; 51 | } 52 | Event.listen(document.documentElement, 'focusout', dispatchFocusEvent); 53 | Event.listen(document.documentElement, 'focusin', dispatchFocusEvent); 54 | hasSetFocusEventRootListen = true; 55 | } 56 | 57 | function dispatchFocusEvent(event) { 58 | var targetElem = event.getTarget(); 59 | if (typeof eventHandlers[targetElem.id] === 'function') { 60 | var isFocusInEvent = event.type === 'focusin' || // not supported in FireFox 61 | event.type === 'focus'; // 'focus' event can not bubble 62 | eventHandlers[targetElem.id](isFocusInEvent); 63 | } 64 | } 65 | 66 | function removeEmptyElementHandler(handlerBoundElementId) { 67 | if (eventHandlers[handlerBoundElementId] && !ge(handlerBoundElementId)) { 68 | delete eventHandlers[handlerBoundElementId]; 69 | } 70 | } 71 | module.exports = Focus; -------------------------------------------------------------------------------- /src/HTML.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @providesModule HTML 3 | */ 4 | var Bootloader = require('./Bootloader.js'); 5 | var createNodesFromMarkup = require('./createNodesFromMarkup.js'); 6 | var emptyFunction = require('./emptyFunction.js'); 7 | var evalGlobal = require('./evalGlobal.js'); 8 | var invariant = require('./invariant.js'); 9 | 10 | var reSingleTag = /(<(\w+)[^>]*?)\/>/g, 11 | hasSingleTag = { 12 | abbr: true, 13 | area: true, 14 | br: true, 15 | col: true, 16 | embed: true, 17 | hr: true, 18 | img: true, 19 | input: true, 20 | link: true, 21 | meta: true, 22 | param: true 23 | }; 24 | 25 | function HTML(o) { 26 | if (o && typeof o.__html === 'string') o = o.__html; 27 | if (!(this instanceof HTML)) { 28 | if (o instanceof HTML) return o; 29 | return new HTML(o); 30 | } 31 | if (o) { 32 | invariant(typeof o === 'string'); 33 | } 34 | this._markup = o || ''; 35 | this._defer = false; 36 | this._extraAction = ''; 37 | this._nodes = null; 38 | this._inlineJS = emptyFunction; 39 | this._rootNode = null; 40 | } 41 | 42 | HTML.prototype.toString = function() { 43 | var o = this._markup; 44 | if (this._extraAction) { 45 | o += '