Compare commits

...

9 Commits

Author SHA1 Message Date
aleclarson
7dfd3948a9 Add 'node-haste' roots 2017-03-16 21:58:43 -04:00
aleclarson
b3ebab438b Add 'build.sh' script for easier rebuilds 2017-03-16 21:58:43 -04:00
aleclarson
0070ad353a Implement multi-gesture support
`ResponderCache`
- Track active responders
- Provide `findAncestor` for getting active ancestor

`GestureCache`
- Group touches by their current target
- Active responders automatically capture touches inside descendants
- Each gesture has:
  - `target: Number`
  - `touchMap: {Number: Touch}`
  - `changedTouches: [Touch]`
  - `touchHistory: TouchHistory`
- Call `touchesChanged(...)` for every touch event
- Call `targetChanged(...)` for every grant/release event

`ResponderEventPlugin`
- Remove `responderInst` local variable in favor of `ResponderCache`
- Replace `changeResponder(...)` with `ResponderCache` methods
- Add `extractTouchEvents(...)` and call it for each changed gesture
- Remove `noResponderTouches(...)` in favor of gesture-specific `TouchHistory`
- Ignore native events without a `touches` array
- Increment/decrement `trackedTouchCount` by the number of `changedTouches`
- Add injectable `GlobalTouchHandler` to receive 'touchEnd' events

`TouchHistory`
- Previously known as the `ResponderTouchHistoryStore`
- No longer a singleton, because each gesture needs its own

`ReactNativeEventEmitter`
- Batch touch events since we handle all `changedTouches` at once

`ReactNativeGlobalResponderHandler`
- Support multiple JS responders
2017-03-16 21:58:43 -04:00
aleclarson
39ce13e9b3 [babel-plugin] Avoid circular dependency of 'object-assign' on itself
The 'transform-object-assign-require.js' plugin replaces usage of Object.assign with require('object-assign').
But it needs to make sure the current file is not 'object-assign' itself. Otherwise a circular dependency will be introduced.
2017-03-16 21:58:42 -04:00
aleclarson
6ed8e9d788 Revert "Compile to 'lib' instead of 'build/modules'"
This reverts commit 9eb86c14b4.
2017-03-16 21:58:42 -04:00
aleclarson
9eb86c14b4 Compile to 'lib' instead of 'build/modules' 2016-09-19 21:44:51 -07:00
aleclarson
c7a4196413 Ignore 'docs' directory 2016-09-19 21:23:38 -07:00
aleclarson
4decc08e53 Revert test changes made in bb11639 2016-09-19 21:23:14 -07:00
aleclarsoniv
76a30ace67 [ResponderEventPlugin] Always dispatch 'responderTerminate' before 'responderGrant'
Since the 'grantEvent' is passed to 'executeDirectDispatch', it's currently handled before the 'terminateEvent'.
It makes more sense for the previous responder to handle its terminate event before the next responder handles its grant event.
2016-09-19 21:23:06 -07:00
68 changed files with 673 additions and 31055 deletions

5
build.sh Normal file
View File

@@ -0,0 +1,5 @@
grunt build:npm-react
cd build/packages/react
deps link
npm install
cd ../../..

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +0,0 @@
/*
HTML5 Shiv v3.6.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
*/
(function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag();
a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/\w+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x<style>article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}</style>";
c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="<xyz></xyz>";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode||
"undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup main mark meter nav output progress section summary time video",version:"3.6.2",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);if(g)return a.createDocumentFragment();
for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d<h;d++)c.createElement(e[d]);return c}};l.html5=e;q(f)})(this,document);

View File

@@ -1,746 +0,0 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
function expressionAllowed(stream, state, backUp) {
return /^(?:operator|sof|keyword c|case|new|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
(state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
}
CodeMirror.defineMode("javascript", function(config, parserConfig) {
var indentUnit = config.indentUnit;
var statementIndent = parserConfig.statementIndent;
var jsonldMode = parserConfig.jsonld;
var jsonMode = parserConfig.json || jsonldMode;
var isTS = parserConfig.typescript;
var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
// Tokenizer
var keywords = function(){
function kw(type) {return {type: type, style: "keyword"};}
var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
var operator = kw("operator"), atom = {type: "atom", style: "atom"};
var jsKeywords = {
"if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
"return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "throw": C, "debugger": C,
"var": kw("var"), "const": kw("var"), "let": kw("var"),
"function": kw("function"), "catch": kw("catch"),
"for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
"in": operator, "typeof": operator, "instanceof": operator,
"true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
"this": kw("this"), "class": kw("class"), "super": kw("atom"),
"yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
"await": C, "async": kw("async")
};
// Extend the 'normal' keywords with the TypeScript language extensions
if (isTS) {
var type = {type: "variable", style: "variable-3"};
var tsKeywords = {
// object-like things
"interface": kw("class"),
"implements": C,
"namespace": C,
"module": kw("module"),
"enum": kw("module"),
// scope modifiers
"public": kw("modifier"),
"private": kw("modifier"),
"protected": kw("modifier"),
"abstract": kw("modifier"),
// operators
"as": operator,
// types
"string": type, "number": type, "boolean": type, "any": type
};
for (var attr in tsKeywords) {
jsKeywords[attr] = tsKeywords[attr];
}
}
return jsKeywords;
}();
var isOperatorChar = /[+\-*&%=<>!?|~^]/;
var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
function readRegexp(stream) {
var escaped = false, next, inSet = false;
while ((next = stream.next()) != null) {
if (!escaped) {
if (next == "/" && !inSet) return;
if (next == "[") inSet = true;
else if (inSet && next == "]") inSet = false;
}
escaped = !escaped && next == "\\";
}
}
// Used as scratch variables to communicate multiple values without
// consing up tons of objects.
var type, content;
function ret(tp, style, cont) {
type = tp; content = cont;
return style;
}
function tokenBase(stream, state) {
var ch = stream.next();
if (ch == '"' || ch == "'") {
state.tokenize = tokenString(ch);
return state.tokenize(stream, state);
} else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
return ret("number", "number");
} else if (ch == "." && stream.match("..")) {
return ret("spread", "meta");
} else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
return ret(ch);
} else if (ch == "=" && stream.eat(">")) {
return ret("=>", "operator");
} else if (ch == "0" && stream.eat(/x/i)) {
stream.eatWhile(/[\da-f]/i);
return ret("number", "number");
} else if (ch == "0" && stream.eat(/o/i)) {
stream.eatWhile(/[0-7]/i);
return ret("number", "number");
} else if (ch == "0" && stream.eat(/b/i)) {
stream.eatWhile(/[01]/i);
return ret("number", "number");
} else if (/\d/.test(ch)) {
stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
return ret("number", "number");
} else if (ch == "/") {
if (stream.eat("*")) {
state.tokenize = tokenComment;
return tokenComment(stream, state);
} else if (stream.eat("/")) {
stream.skipToEnd();
return ret("comment", "comment");
} else if (expressionAllowed(stream, state, 1)) {
readRegexp(stream);
stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
return ret("regexp", "string-2");
} else {
stream.eatWhile(isOperatorChar);
return ret("operator", "operator", stream.current());
}
} else if (ch == "`") {
state.tokenize = tokenQuasi;
return tokenQuasi(stream, state);
} else if (ch == "#") {
stream.skipToEnd();
return ret("error", "error");
} else if (isOperatorChar.test(ch)) {
stream.eatWhile(isOperatorChar);
return ret("operator", "operator", stream.current());
} else if (wordRE.test(ch)) {
stream.eatWhile(wordRE);
var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
ret("variable", "variable", word);
}
}
function tokenString(quote) {
return function(stream, state) {
var escaped = false, next;
if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
state.tokenize = tokenBase;
return ret("jsonld-keyword", "meta");
}
while ((next = stream.next()) != null) {
if (next == quote && !escaped) break;
escaped = !escaped && next == "\\";
}
if (!escaped) state.tokenize = tokenBase;
return ret("string", "string");
};
}
function tokenComment(stream, state) {
var maybeEnd = false, ch;
while (ch = stream.next()) {
if (ch == "/" && maybeEnd) {
state.tokenize = tokenBase;
break;
}
maybeEnd = (ch == "*");
}
return ret("comment", "comment");
}
function tokenQuasi(stream, state) {
var escaped = false, next;
while ((next = stream.next()) != null) {
if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
state.tokenize = tokenBase;
break;
}
escaped = !escaped && next == "\\";
}
return ret("quasi", "string-2", stream.current());
}
var brackets = "([{}])";
// This is a crude lookahead trick to try and notice that we're
// parsing the argument patterns for a fat-arrow function before we
// actually hit the arrow token. It only works if the arrow is on
// the same line as the arguments and there's no strange noise
// (comments) in between. Fallback is to only notice when we hit the
// arrow, and not declare the arguments as locals for the arrow
// body.
function findFatArrow(stream, state) {
if (state.fatArrowAt) state.fatArrowAt = null;
var arrow = stream.string.indexOf("=>", stream.start);
if (arrow < 0) return;
var depth = 0, sawSomething = false;
for (var pos = arrow - 1; pos >= 0; --pos) {
var ch = stream.string.charAt(pos);
var bracket = brackets.indexOf(ch);
if (bracket >= 0 && bracket < 3) {
if (!depth) { ++pos; break; }
if (--depth == 0) break;
} else if (bracket >= 3 && bracket < 6) {
++depth;
} else if (wordRE.test(ch)) {
sawSomething = true;
} else if (/["'\/]/.test(ch)) {
return;
} else if (sawSomething && !depth) {
++pos;
break;
}
}
if (sawSomething && !depth) state.fatArrowAt = pos;
}
// Parser
var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
function JSLexical(indented, column, type, align, prev, info) {
this.indented = indented;
this.column = column;
this.type = type;
this.prev = prev;
this.info = info;
if (align != null) this.align = align;
}
function inScope(state, varname) {
for (var v = state.localVars; v; v = v.next)
if (v.name == varname) return true;
for (var cx = state.context; cx; cx = cx.prev) {
for (var v = cx.vars; v; v = v.next)
if (v.name == varname) return true;
}
}
function parseJS(state, style, type, content, stream) {
var cc = state.cc;
// Communicate our context to the combinators.
// (Less wasteful than consing up a hundred closures on every call.)
cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
if (!state.lexical.hasOwnProperty("align"))
state.lexical.align = true;
while(true) {
var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
if (combinator(type, content)) {
while(cc.length && cc[cc.length - 1].lex)
cc.pop()();
if (cx.marked) return cx.marked;
if (type == "variable" && inScope(state, content)) return "variable-2";
return style;
}
}
}
// Combinator utils
var cx = {state: null, column: null, marked: null, cc: null};
function pass() {
for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
}
function cont() {
pass.apply(null, arguments);
return true;
}
function register(varname) {
function inList(list) {
for (var v = list; v; v = v.next)
if (v.name == varname) return true;
return false;
}
var state = cx.state;
cx.marked = "def";
if (state.context) {
if (inList(state.localVars)) return;
state.localVars = {name: varname, next: state.localVars};
} else {
if (inList(state.globalVars)) return;
if (parserConfig.globalVars)
state.globalVars = {name: varname, next: state.globalVars};
}
}
// Combinators
var defaultVars = {name: "this", next: {name: "arguments"}};
function pushcontext() {
cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
cx.state.localVars = defaultVars;
}
function popcontext() {
cx.state.localVars = cx.state.context.vars;
cx.state.context = cx.state.context.prev;
}
function pushlex(type, info) {
var result = function() {
var state = cx.state, indent = state.indented;
if (state.lexical.type == "stat") indent = state.lexical.indented;
else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
indent = outer.indented;
state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
};
result.lex = true;
return result;
}
function poplex() {
var state = cx.state;
if (state.lexical.prev) {
if (state.lexical.type == ")")
state.indented = state.lexical.indented;
state.lexical = state.lexical.prev;
}
}
poplex.lex = true;
function expect(wanted) {
function exp(type) {
if (type == wanted) return cont();
else if (wanted == ";") return pass();
else return cont(exp);
};
return exp;
}
function statement(type, value) {
if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
if (type == "{") return cont(pushlex("}"), block, poplex);
if (type == ";") return cont();
if (type == "if") {
if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
cx.state.cc.pop()();
return cont(pushlex("form"), expression, statement, poplex, maybeelse);
}
if (type == "function") return cont(functiondef);
if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
if (type == "variable") return cont(pushlex("stat"), maybelabel);
if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
block, poplex, poplex);
if (type == "case") return cont(expression, expect(":"));
if (type == "default") return cont(expect(":"));
if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
statement, poplex, popcontext);
if (type == "class") return cont(pushlex("form"), className, poplex);
if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
if (type == "module") return cont(pushlex("form"), pattern, pushlex("}"), expect("{"), block, poplex, poplex)
if (type == "async") return cont(statement)
return pass(pushlex("stat"), expression, expect(";"), poplex);
}
function expression(type) {
return expressionInner(type, false);
}
function expressionNoComma(type) {
return expressionInner(type, true);
}
function expressionInner(type, noComma) {
if (cx.state.fatArrowAt == cx.stream.start) {
var body = noComma ? arrowBodyNoComma : arrowBody;
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext);
else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
}
var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
if (type == "function") return cont(functiondef, maybeop);
if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
if (type == "(") return cont(pushlex(")"), maybeexpression, comprehension, expect(")"), poplex, maybeop);
if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
if (type == "{") return contCommasep(objprop, "}", null, maybeop);
if (type == "quasi") return pass(quasi, maybeop);
if (type == "new") return cont(maybeTarget(noComma));
return cont();
}
function maybeexpression(type) {
if (type.match(/[;\}\)\],]/)) return pass();
return pass(expression);
}
function maybeexpressionNoComma(type) {
if (type.match(/[;\}\)\],]/)) return pass();
return pass(expressionNoComma);
}
function maybeoperatorComma(type, value) {
if (type == ",") return cont(expression);
return maybeoperatorNoComma(type, value, false);
}
function maybeoperatorNoComma(type, value, noComma) {
var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
var expr = noComma == false ? expression : expressionNoComma;
if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
if (type == "operator") {
if (/\+\+|--/.test(value)) return cont(me);
if (value == "?") return cont(expression, expect(":"), expr);
return cont(expr);
}
if (type == "quasi") { return pass(quasi, me); }
if (type == ";") return;
if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
if (type == ".") return cont(property, me);
if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
}
function quasi(type, value) {
if (type != "quasi") return pass();
if (value.slice(value.length - 2) != "${") return cont(quasi);
return cont(expression, continueQuasi);
}
function continueQuasi(type) {
if (type == "}") {
cx.marked = "string-2";
cx.state.tokenize = tokenQuasi;
return cont(quasi);
}
}
function arrowBody(type) {
findFatArrow(cx.stream, cx.state);
return pass(type == "{" ? statement : expression);
}
function arrowBodyNoComma(type) {
findFatArrow(cx.stream, cx.state);
return pass(type == "{" ? statement : expressionNoComma);
}
function maybeTarget(noComma) {
return function(type) {
if (type == ".") return cont(noComma ? targetNoComma : target);
else return pass(noComma ? expressionNoComma : expression);
};
}
function target(_, value) {
if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
}
function targetNoComma(_, value) {
if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
}
function maybelabel(type) {
if (type == ":") return cont(poplex, statement);
return pass(maybeoperatorComma, expect(";"), poplex);
}
function property(type) {
if (type == "variable") {cx.marked = "property"; return cont();}
}
function objprop(type, value) {
if (type == "variable" || cx.style == "keyword") {
cx.marked = "property";
if (value == "get" || value == "set") return cont(getterSetter);
return cont(afterprop);
} else if (type == "number" || type == "string") {
cx.marked = jsonldMode ? "property" : (cx.style + " property");
return cont(afterprop);
} else if (type == "jsonld-keyword") {
return cont(afterprop);
} else if (type == "modifier") {
return cont(objprop)
} else if (type == "[") {
return cont(expression, expect("]"), afterprop);
} else if (type == "spread") {
return cont(expression);
}
}
function getterSetter(type) {
if (type != "variable") return pass(afterprop);
cx.marked = "property";
return cont(functiondef);
}
function afterprop(type) {
if (type == ":") return cont(expressionNoComma);
if (type == "(") return pass(functiondef);
}
function commasep(what, end) {
function proceed(type, value) {
if (type == ",") {
var lex = cx.state.lexical;
if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
return cont(what, proceed);
}
if (type == end || value == end) return cont();
return cont(expect(end));
}
return function(type, value) {
if (type == end || value == end) return cont();
return pass(what, proceed);
};
}
function contCommasep(what, end, info) {
for (var i = 3; i < arguments.length; i++)
cx.cc.push(arguments[i]);
return cont(pushlex(end, info), commasep(what, end), poplex);
}
function block(type) {
if (type == "}") return cont();
return pass(statement, block);
}
function maybetype(type) {
if (isTS && type == ":") return cont(typeexpr);
}
function maybedefault(_, value) {
if (value == "=") return cont(expressionNoComma);
}
function typeexpr(type) {
if (type == "variable") {cx.marked = "variable-3"; return cont(afterType);}
}
function afterType(type, value) {
if (value == "<") return cont(commasep(typeexpr, ">"), afterType)
if (type == "[") return cont(expect("]"), afterType)
}
function vardef() {
return pass(pattern, maybetype, maybeAssign, vardefCont);
}
function pattern(type, value) {
if (type == "modifier") return cont(pattern)
if (type == "variable") { register(value); return cont(); }
if (type == "spread") return cont(pattern);
if (type == "[") return contCommasep(pattern, "]");
if (type == "{") return contCommasep(proppattern, "}");
}
function proppattern(type, value) {
if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
register(value);
return cont(maybeAssign);
}
if (type == "variable") cx.marked = "property";
if (type == "spread") return cont(pattern);
if (type == "}") return pass();
return cont(expect(":"), pattern, maybeAssign);
}
function maybeAssign(_type, value) {
if (value == "=") return cont(expressionNoComma);
}
function vardefCont(type) {
if (type == ",") return cont(vardef);
}
function maybeelse(type, value) {
if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
}
function forspec(type) {
if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
}
function forspec1(type) {
if (type == "var") return cont(vardef, expect(";"), forspec2);
if (type == ";") return cont(forspec2);
if (type == "variable") return cont(formaybeinof);
return pass(expression, expect(";"), forspec2);
}
function formaybeinof(_type, value) {
if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
return cont(maybeoperatorComma, forspec2);
}
function forspec2(type, value) {
if (type == ";") return cont(forspec3);
if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
return pass(expression, expect(";"), forspec3);
}
function forspec3(type) {
if (type != ")") cont(expression);
}
function functiondef(type, value) {
if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
if (type == "variable") {register(value); return cont(functiondef);}
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext);
}
function funarg(type) {
if (type == "spread") return cont(funarg);
return pass(pattern, maybetype, maybedefault);
}
function className(type, value) {
if (type == "variable") {register(value); return cont(classNameAfter);}
}
function classNameAfter(type, value) {
if (value == "extends") return cont(expression, classNameAfter);
if (type == "{") return cont(pushlex("}"), classBody, poplex);
}
function classBody(type, value) {
if (type == "variable" || cx.style == "keyword") {
if (value == "static") {
cx.marked = "keyword";
return cont(classBody);
}
cx.marked = "property";
if (value == "get" || value == "set") return cont(classGetterSetter, functiondef, classBody);
return cont(functiondef, classBody);
}
if (value == "*") {
cx.marked = "keyword";
return cont(classBody);
}
if (type == ";") return cont(classBody);
if (type == "}") return cont();
}
function classGetterSetter(type) {
if (type != "variable") return pass();
cx.marked = "property";
return cont();
}
function afterExport(_type, value) {
if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
return pass(statement);
}
function afterImport(type) {
if (type == "string") return cont();
return pass(importSpec, maybeFrom);
}
function importSpec(type, value) {
if (type == "{") return contCommasep(importSpec, "}");
if (type == "variable") register(value);
if (value == "*") cx.marked = "keyword";
return cont(maybeAs);
}
function maybeAs(_type, value) {
if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
}
function maybeFrom(_type, value) {
if (value == "from") { cx.marked = "keyword"; return cont(expression); }
}
function arrayLiteral(type) {
if (type == "]") return cont();
return pass(expressionNoComma, maybeArrayComprehension);
}
function maybeArrayComprehension(type) {
if (type == "for") return pass(comprehension, expect("]"));
if (type == ",") return cont(commasep(maybeexpressionNoComma, "]"));
return pass(commasep(expressionNoComma, "]"));
}
function comprehension(type) {
if (type == "for") return cont(forspec, comprehension);
if (type == "if") return cont(expression, comprehension);
}
function isContinuedStatement(state, textAfter) {
return state.lastType == "operator" || state.lastType == "," ||
isOperatorChar.test(textAfter.charAt(0)) ||
/[,.]/.test(textAfter.charAt(0));
}
// Interface
return {
startState: function(basecolumn) {
var state = {
tokenize: tokenBase,
lastType: "sof",
cc: [],
lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
localVars: parserConfig.localVars,
context: parserConfig.localVars && {vars: parserConfig.localVars},
indented: basecolumn || 0
};
if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
state.globalVars = parserConfig.globalVars;
return state;
},
token: function(stream, state) {
if (stream.sol()) {
if (!state.lexical.hasOwnProperty("align"))
state.lexical.align = false;
state.indented = stream.indentation();
findFatArrow(stream, state);
}
if (state.tokenize != tokenComment && stream.eatSpace()) return null;
var style = state.tokenize(stream, state);
if (type == "comment") return style;
state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
return parseJS(state, style, type, content, stream);
},
indent: function(state, textAfter) {
if (state.tokenize == tokenComment) return CodeMirror.Pass;
if (state.tokenize != tokenBase) return 0;
var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
// Kludge to prevent 'maybelse' from blocking lexical scope pops
if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
var c = state.cc[i];
if (c == poplex) lexical = lexical.prev;
else if (c != maybeelse) break;
}
if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
lexical = lexical.prev;
var type = lexical.type, closing = firstChar == type;
if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0);
else if (type == "form" && firstChar == "{") return lexical.indented;
else if (type == "form") return lexical.indented + indentUnit;
else if (type == "stat")
return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
else if (lexical.align) return lexical.column + (closing ? 0 : 1);
else return lexical.indented + (closing ? 0 : indentUnit);
},
electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
blockCommentStart: jsonMode ? null : "/*",
blockCommentEnd: jsonMode ? null : "*/",
lineComment: jsonMode ? null : "//",
fold: "brace",
closeBrackets: "()[]{}''\"\"``",
helperType: jsonMode ? "json" : "javascript",
jsonldMode: jsonldMode,
jsonMode: jsonMode,
expressionAllowed: expressionAllowed,
skipExpression: function(state) {
var top = state.cc[state.cc.length - 1]
if (top == expression || top == expressionNoComma) state.cc.pop()
}
};
});
CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
CodeMirror.defineMIME("text/javascript", "javascript");
CodeMirror.defineMIME("text/ecmascript", "javascript");
CodeMirror.defineMIME("application/javascript", "javascript");
CodeMirror.defineMIME("application/x-javascript", "javascript");
CodeMirror.defineMIME("application/ecmascript", "javascript");
CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
});

View File

@@ -1,10 +0,0 @@
(function() {
var tag = document.querySelector(
'script[type="application/javascript;version=1.7"]'
);
if (!tag || tag.textContent.indexOf('window.onload=function(){') !== -1) {
alert('Bad JSFiddle configuration, please fork the original React JSFiddle');
}
tag.setAttribute('type', 'text/babel');
tag.textContent = tag.textContent.replace(/^\/\/<!\[CDATA\[/, '');
})();

View File

@@ -1,147 +0,0 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"))
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript"], mod)
else // Plain browser env
mod(CodeMirror)
})(function(CodeMirror) {
"use strict"
// Depth means the amount of open braces in JS context, in XML
// context 0 means not in tag, 1 means in tag, and 2 means in tag
// and js block comment.
function Context(state, mode, depth, prev) {
this.state = state; this.mode = mode; this.depth = depth; this.prev = prev
}
function copyContext(context) {
return new Context(CodeMirror.copyState(context.mode, context.state),
context.mode,
context.depth,
context.prev && copyContext(context.prev))
}
CodeMirror.defineMode("jsx", function(config, modeConfig) {
var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true, multilineTagIndentPastTag: false})
var jsMode = CodeMirror.getMode(config, modeConfig && modeConfig.base || "javascript")
function flatXMLIndent(state) {
var tagName = state.tagName
state.tagName = null
var result = xmlMode.indent(state, "")
state.tagName = tagName
return result
}
function token(stream, state) {
if (state.context.mode == xmlMode)
return xmlToken(stream, state, state.context)
else
return jsToken(stream, state, state.context)
}
function xmlToken(stream, state, cx) {
if (cx.depth == 2) { // Inside a JS /* */ comment
if (stream.match(/^.*?\*\//)) cx.depth = 1
else stream.skipToEnd()
return "comment"
}
if (stream.peek() == "{") {
xmlMode.skipAttribute(cx.state)
var indent = flatXMLIndent(cx.state), xmlContext = cx.state.context
// If JS starts on same line as tag
if (xmlContext && stream.match(/^[^>]*>\s*$/, false)) {
while (xmlContext.prev && !xmlContext.startOfLine)
xmlContext = xmlContext.prev
// If tag starts the line, use XML indentation level
if (xmlContext.startOfLine) indent -= config.indentUnit
// Else use JS indentation level
else if (cx.prev.state.lexical) indent = cx.prev.state.lexical.indented
// Else if inside of tag
} else if (cx.depth == 1) {
indent += config.indentUnit
}
state.context = new Context(CodeMirror.startState(jsMode, indent),
jsMode, 0, state.context)
return null
}
if (cx.depth == 1) { // Inside of tag
if (stream.peek() == "<") { // Tag inside of tag
xmlMode.skipAttribute(cx.state)
state.context = new Context(CodeMirror.startState(xmlMode, flatXMLIndent(cx.state)),
xmlMode, 0, state.context)
return null
} else if (stream.match("//")) {
stream.skipToEnd()
return "comment"
} else if (stream.match("/*")) {
cx.depth = 2
return token(stream, state)
}
}
var style = xmlMode.token(stream, cx.state), cur = stream.current(), stop
if (/\btag\b/.test(style)) {
if (/>$/.test(cur)) {
if (cx.state.context) cx.depth = 0
else state.context = state.context.prev
} else if (/^</.test(cur)) {
cx.depth = 1
}
} else if (!style && (stop = cur.indexOf("{")) > -1) {
stream.backUp(cur.length - stop)
}
return style
}
function jsToken(stream, state, cx) {
if (stream.peek() == "<" && jsMode.expressionAllowed(stream, cx.state)) {
jsMode.skipExpression(cx.state)
state.context = new Context(CodeMirror.startState(xmlMode, jsMode.indent(cx.state, "")),
xmlMode, 0, state.context)
return null
}
var style = jsMode.token(stream, cx.state)
if (!style && cx.depth != null) {
var cur = stream.current()
if (cur == "{") {
cx.depth++
} else if (cur == "}") {
if (--cx.depth == 0) state.context = state.context.prev
}
}
return style
}
return {
startState: function() {
return {context: new Context(CodeMirror.startState(jsMode), jsMode)}
},
copyState: function(state) {
return {context: copyContext(state.context)}
},
token: token,
indent: function(state, textAfter, fullLine) {
return state.context.mode.indent(state.context.state, textAfter, fullLine)
},
innerMode: function(state) {
return state.context
}
}
}, "xml", "javascript")
CodeMirror.defineMIME("text/jsx", "jsx")
});

42
docs/js/react-dom.js vendored
View File

@@ -1,42 +0,0 @@
/**
* ReactDOM v15.3.1
*
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
// Based off https://github.com/ForbesLindesay/umd/blob/master/template.js
;(function(f) {
// CommonJS
if (typeof exports === "object" && typeof module !== "undefined") {
module.exports = f(require('react'));
// RequireJS
} else if (typeof define === "function" && define.amd) {
define(['react'], f);
// <script>
} else {
var g;
if (typeof window !== "undefined") {
g = window;
} else if (typeof global !== "undefined") {
g = global;
} else if (typeof self !== "undefined") {
g = self;
} else {
// works providing we're not in "use strict";
// needed for Java 8 Nashorn
// see https://github.com/facebook/react/issues/3037
g = this;
}
g.ReactDOM = f(g.React);
}
})(function(React) {
return React.__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
});

20600
docs/js/react.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -1,394 +0,0 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var htmlConfig = {
autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
'track': true, 'wbr': true, 'menuitem': true},
implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
'th': true, 'tr': true},
contextGrabbers: {
'dd': {'dd': true, 'dt': true},
'dt': {'dd': true, 'dt': true},
'li': {'li': true},
'option': {'option': true, 'optgroup': true},
'optgroup': {'optgroup': true},
'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
'rp': {'rp': true, 'rt': true},
'rt': {'rp': true, 'rt': true},
'tbody': {'tbody': true, 'tfoot': true},
'td': {'td': true, 'th': true},
'tfoot': {'tbody': true},
'th': {'td': true, 'th': true},
'thead': {'tbody': true, 'tfoot': true},
'tr': {'tr': true}
},
doNotIndent: {"pre": true},
allowUnquoted: true,
allowMissing: true,
caseFold: true
}
var xmlConfig = {
autoSelfClosers: {},
implicitlyClosed: {},
contextGrabbers: {},
doNotIndent: {},
allowUnquoted: false,
allowMissing: false,
caseFold: false
}
CodeMirror.defineMode("xml", function(editorConf, config_) {
var indentUnit = editorConf.indentUnit
var config = {}
var defaults = config_.htmlMode ? htmlConfig : xmlConfig
for (var prop in defaults) config[prop] = defaults[prop]
for (var prop in config_) config[prop] = config_[prop]
// Return variables for tokenizers
var type, setStyle;
function inText(stream, state) {
function chain(parser) {
state.tokenize = parser;
return parser(stream, state);
}
var ch = stream.next();
if (ch == "<") {
if (stream.eat("!")) {
if (stream.eat("[")) {
if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
else return null;
} else if (stream.match("--")) {
return chain(inBlock("comment", "-->"));
} else if (stream.match("DOCTYPE", true, true)) {
stream.eatWhile(/[\w\._\-]/);
return chain(doctype(1));
} else {
return null;
}
} else if (stream.eat("?")) {
stream.eatWhile(/[\w\._\-]/);
state.tokenize = inBlock("meta", "?>");
return "meta";
} else {
type = stream.eat("/") ? "closeTag" : "openTag";
state.tokenize = inTag;
return "tag bracket";
}
} else if (ch == "&") {
var ok;
if (stream.eat("#")) {
if (stream.eat("x")) {
ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
} else {
ok = stream.eatWhile(/[\d]/) && stream.eat(";");
}
} else {
ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
}
return ok ? "atom" : "error";
} else {
stream.eatWhile(/[^&<]/);
return null;
}
}
inText.isInText = true;
function inTag(stream, state) {
var ch = stream.next();
if (ch == ">" || (ch == "/" && stream.eat(">"))) {
state.tokenize = inText;
type = ch == ">" ? "endTag" : "selfcloseTag";
return "tag bracket";
} else if (ch == "=") {
type = "equals";
return null;
} else if (ch == "<") {
state.tokenize = inText;
state.state = baseState;
state.tagName = state.tagStart = null;
var next = state.tokenize(stream, state);
return next ? next + " tag error" : "tag error";
} else if (/[\'\"]/.test(ch)) {
state.tokenize = inAttribute(ch);
state.stringStartCol = stream.column();
return state.tokenize(stream, state);
} else {
stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
return "word";
}
}
function inAttribute(quote) {
var closure = function(stream, state) {
while (!stream.eol()) {
if (stream.next() == quote) {
state.tokenize = inTag;
break;
}
}
return "string";
};
closure.isInAttribute = true;
return closure;
}
function inBlock(style, terminator) {
return function(stream, state) {
while (!stream.eol()) {
if (stream.match(terminator)) {
state.tokenize = inText;
break;
}
stream.next();
}
return style;
};
}
function doctype(depth) {
return function(stream, state) {
var ch;
while ((ch = stream.next()) != null) {
if (ch == "<") {
state.tokenize = doctype(depth + 1);
return state.tokenize(stream, state);
} else if (ch == ">") {
if (depth == 1) {
state.tokenize = inText;
break;
} else {
state.tokenize = doctype(depth - 1);
return state.tokenize(stream, state);
}
}
}
return "meta";
};
}
function Context(state, tagName, startOfLine) {
this.prev = state.context;
this.tagName = tagName;
this.indent = state.indented;
this.startOfLine = startOfLine;
if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
this.noIndent = true;
}
function popContext(state) {
if (state.context) state.context = state.context.prev;
}
function maybePopContext(state, nextTagName) {
var parentTagName;
while (true) {
if (!state.context) {
return;
}
parentTagName = state.context.tagName;
if (!config.contextGrabbers.hasOwnProperty(parentTagName) ||
!config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
return;
}
popContext(state);
}
}
function baseState(type, stream, state) {
if (type == "openTag") {
state.tagStart = stream.column();
return tagNameState;
} else if (type == "closeTag") {
return closeTagNameState;
} else {
return baseState;
}
}
function tagNameState(type, stream, state) {
if (type == "word") {
state.tagName = stream.current();
setStyle = "tag";
return attrState;
} else {
setStyle = "error";
return tagNameState;
}
}
function closeTagNameState(type, stream, state) {
if (type == "word") {
var tagName = stream.current();
if (state.context && state.context.tagName != tagName &&
config.implicitlyClosed.hasOwnProperty(state.context.tagName))
popContext(state);
if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
setStyle = "tag";
return closeState;
} else {
setStyle = "tag error";
return closeStateErr;
}
} else {
setStyle = "error";
return closeStateErr;
}
}
function closeState(type, _stream, state) {
if (type != "endTag") {
setStyle = "error";
return closeState;
}
popContext(state);
return baseState;
}
function closeStateErr(type, stream, state) {
setStyle = "error";
return closeState(type, stream, state);
}
function attrState(type, _stream, state) {
if (type == "word") {
setStyle = "attribute";
return attrEqState;
} else if (type == "endTag" || type == "selfcloseTag") {
var tagName = state.tagName, tagStart = state.tagStart;
state.tagName = state.tagStart = null;
if (type == "selfcloseTag" ||
config.autoSelfClosers.hasOwnProperty(tagName)) {
maybePopContext(state, tagName);
} else {
maybePopContext(state, tagName);
state.context = new Context(state, tagName, tagStart == state.indented);
}
return baseState;
}
setStyle = "error";
return attrState;
}
function attrEqState(type, stream, state) {
if (type == "equals") return attrValueState;
if (!config.allowMissing) setStyle = "error";
return attrState(type, stream, state);
}
function attrValueState(type, stream, state) {
if (type == "string") return attrContinuedState;
if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
setStyle = "error";
return attrState(type, stream, state);
}
function attrContinuedState(type, stream, state) {
if (type == "string") return attrContinuedState;
return attrState(type, stream, state);
}
return {
startState: function(baseIndent) {
var state = {tokenize: inText,
state: baseState,
indented: baseIndent || 0,
tagName: null, tagStart: null,
context: null}
if (baseIndent != null) state.baseIndent = baseIndent
return state
},
token: function(stream, state) {
if (!state.tagName && stream.sol())
state.indented = stream.indentation();
if (stream.eatSpace()) return null;
type = null;
var style = state.tokenize(stream, state);
if ((style || type) && style != "comment") {
setStyle = null;
state.state = state.state(type || style, stream, state);
if (setStyle)
style = setStyle == "error" ? style + " error" : setStyle;
}
return style;
},
indent: function(state, textAfter, fullLine) {
var context = state.context;
// Indent multi-line strings (e.g. css).
if (state.tokenize.isInAttribute) {
if (state.tagStart == state.indented)
return state.stringStartCol + 1;
else
return state.indented + indentUnit;
}
if (context && context.noIndent) return CodeMirror.Pass;
if (state.tokenize != inTag && state.tokenize != inText)
return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
// Indent the starts of attribute names.
if (state.tagName) {
if (config.multilineTagIndentPastTag !== false)
return state.tagStart + state.tagName.length + 2;
else
return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
}
if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
if (tagAfter && tagAfter[1]) { // Closing tag spotted
while (context) {
if (context.tagName == tagAfter[2]) {
context = context.prev;
break;
} else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) {
context = context.prev;
} else {
break;
}
}
} else if (tagAfter) { // Opening tag spotted
while (context) {
var grabbers = config.contextGrabbers[context.tagName];
if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
context = context.prev;
else
break;
}
}
while (context && context.prev && !context.startOfLine)
context = context.prev;
if (context) return context.indent + indentUnit;
else return state.baseIndent || 0;
},
electricInput: /<\/[\s\w:]+>$/,
blockCommentStart: "<!--",
blockCommentEnd: "-->",
configuration: config.htmlMode ? "html" : "xml",
helperType: config.htmlMode ? "html" : "xml",
skipAttribute: function(state) {
if (state.state == attrValueState)
state.state = attrState
}
};
});
CodeMirror.defineMIME("text/xml", "xml");
CodeMirror.defineMIME("application/xml", "xml");
if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
});

View File

@@ -31,5 +31,10 @@
"transform": [
"loose-envify"
]
},
"node-haste" {
"roots": [
"lib"
]
}
}

View File

@@ -12,7 +12,12 @@
module.exports = function autoImporter(babel) {
const t = babel.types;
function getAssignIdent(path, file, state) {
function isObjectAssign(file) {
var filename = file.opts.filename;
return filename.indexOf('object-assign') >= 0;
}
function getAssignIdent(path, state) {
if (!state.id) {
state.id = path.scope.generateUidIdentifier('assign');
path.scope.getProgramParent().push({
@@ -33,17 +38,19 @@ module.exports = function autoImporter(babel) {
},
visitor: {
CallExpression: function(path, file) {
CallExpression: function(path, state) {
if (isObjectAssign(state.file)) { return }
if (path.get('callee').matchesPattern('Object.assign')) {
// generate identifier and require if it hasn't been already
var id = getAssignIdent(path, file, this);
var id = getAssignIdent(path, state);
path.node.callee = id;
}
},
MemberExpression: function(path, file) {
MemberExpression: function(path, state) {
if (isObjectAssign(state.file)) { return }
if (path.matchesPattern('Object.assign')) {
var id = getAssignIdent(path, file, this);
var id = getAssignIdent(path, state);
path.replaceWith(id);
}
},

View File

@@ -191,33 +191,17 @@ var ReactNativeEventEmitter = {
removeTouchesAtIndices(touches, changedIndices) :
touchSubsequence(touches, changedIndices);
for (var jj = 0; jj < changedTouches.length; jj++) {
var touch = changedTouches[jj];
// Touch objects can fulfill the role of `DOM` `Event` objects if we set
// the `changedTouches`/`touches`. This saves allocations.
touch.changedTouches = changedTouches;
touch.touches = touches;
var nativeEvent = touch;
var rootNodeID = null;
var target = nativeEvent.target;
if (target !== null && target !== undefined) {
if (target < ReactNativeTagHandles.tagsStartAt) {
if (__DEV__) {
warning(
false,
'A view is reporting that a touch occured on tag zero.'
);
}
} else {
rootNodeID = target;
}
}
ReactNativeEventEmitter._receiveRootNodeIDEvent(
rootNodeID,
eventTopLevelType,
nativeEvent
);
}
var nativeEvent = {
target: changedTouches[0].target,
touches,
changedTouches,
};
ReactNativeEventEmitter._receiveRootNodeIDEvent(
nativeEvent.target,
eventTopLevelType,
nativeEvent
);
},
};

View File

@@ -14,13 +14,11 @@ var UIManager = require('UIManager');
var ReactNativeGlobalResponderHandler = {
onChange: function(from, to, blockNativeResponder) {
if (from !== null) {
UIManager.clearJSResponder(from._rootNodeID);
}
if (to !== null) {
UIManager.setJSResponder(
to._rootNodeID,
blockNativeResponder
);
} else {
UIManager.clearJSResponder();
UIManager.setJSResponder(to._rootNodeID, blockNativeResponder);
}
},
};

View File

@@ -0,0 +1,154 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule GestureCache
* @flow
*/
'use strict';
import type {Touch, TouchEvent} from 'TouchHistory';
const EventPluginUtils = require('EventPluginUtils');
const ResponderCache = require('ResponderCache');
const TouchHistory = require('TouchHistory');
export type Gesture = {
target: number,
touchMap: {[key:number]: Touch},
changedTouches: Array<Touch>,
touchHistory: TouchHistory,
};
const activeGestures = [];
const gesturesByTouch = {};
const gesturesByTarget = {};
exports.targetChanged = function(topLevelType: string, oldTarget: any, newTarget: any): void {
const oldTag = EventPluginUtils.getNodeFromInstance(oldTarget);
const oldGesture = gesturesByTarget[oldTag];
if (!oldGesture) {
return console.warn(`Invalid target has no gesture: ${oldTag}`);
}
const newTag = EventPluginUtils.getNodeFromInstance(newTarget);
const newGesture = gesturesByTarget[newTag];
// Move touch data from `oldGesture` to `newGesture`.
if (newGesture) {
newGesture.touchHistory.recordTouchEvent(
topLevelType, oldGesture.changedTouches
);
oldGesture.changedTouches.forEach(touch => {
newGesture.changedTouches.push(touch);
});
for (let identifier in oldGesture.touchMap) {
gesturesByTouch[identifier] = newGesture;
newGesture.touchMap[identifier] = oldGesture.touchMap[identifier];
}
removeGesture(oldGesture);
} else {
oldGesture.target = newTag;
gesturesByTarget[newTag] = oldGesture;
delete gesturesByTarget[oldTag];
}
};
exports.touchesChanged = function(topLevelType: string, nativeEvent: TouchEvent): Array<Gesture> {
const isStartish = EventPluginUtils.isStartish(topLevelType);
const isEndish = EventPluginUtils.isEndish(topLevelType);
activeGestures.forEach(gesture => {
gesture.changedTouches.length = 0;
});
nativeEvent.changedTouches.forEach(touch => {
const gesture = isStartish ?
attachGesture(touch) :
gesturesByTouch[touch.identifier];
gesture.changedTouches.push(touch);
if (!isEndish) {
// Changed touches are entirely new objects.
gesture.touchMap[touch.identifier] = touch;
}
});
// Detach ended touches from their gestures.
if (isEndish) {
nativeEvent.changedTouches.forEach(touch => {
const {identifier} = touch;
const gesture = gesturesByTouch[identifier];
// Remove the touch from the gesture, but *not* vice versa.
delete gesture.touchMap[identifier];
delete gesturesByTouch[identifier];
});
}
// Gather all gestures with at least one changed touch.
// Ended gestures are removed from `activeGestures` while
// looping, causing a shift in `activeGestures.length`.
// Thus, we must start from the end of `activeGestures`.
const changedGestures = [];
for (let i = activeGestures.length - 1; i >= 0; i--) {
let gesture = activeGestures[i];
if (!gesture.changedTouches.length) {
continue;
}
changedGestures.push(gesture);
// Update the touch history of every gesture with changed touches.
gesture.touchHistory.recordTouchEvent(
topLevelType, gesture.changedTouches
);
// When a gesture has no touches, remove it from the cache.
if (isEndish && gesture.touchHistory.numberActiveTouches === 0) {
removeGesture(gesture);
}
}
return changedGestures;
};
// Attach a gesture to a touch. Create a gesture if necessary.
function attachGesture(touch: Touch): Gesture {
// Using `findAncestor` means child responders cannot start
// new gestures while a parent has an active gesture.
const responderInst = ResponderCache.findAncestor(touch.target);
const target = responderInst ?
EventPluginUtils.getNodeFromInstance(responderInst) :
touch.target;
// Use the same gesture between touches with the same target.
let gesture = gesturesByTarget[target];
if (!gesture) {
gesture = {
target,
touchMap: {},
changedTouches: [],
touchHistory: new TouchHistory(),
};
activeGestures.push(gesture);
gesturesByTarget[target] = gesture;
}
gesturesByTouch[touch.identifier] = gesture;
return gesture;
}
// Remove a gesture from the global cache.
function removeGesture(gesture: Gesture): void {
const index = activeGestures.indexOf(gesture);
activeGestures.splice(index, 1);
delete gesturesByTarget[gesture.target];
}

View File

@@ -0,0 +1,55 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ResponderCache
*/
'use strict';
const EventPluginUtils = require('EventPluginUtils');
const emptyFunction = require('emptyFunction');
// Each active responder (by its react tag).
const activeResponders = Object.create(null);
// Set this to handle responder grant/release/terminate events.
exports.globalHandler = {onChange: emptyFunction};
exports.hasResponder = function(responderInst) {
const nodeTag = EventPluginUtils.getNodeFromInstance(responderInst);
return activeResponders[nodeTag] != null;
};
exports.onResponderGrant = function(responderInst, blockHostResponder) {
const nodeTag = EventPluginUtils.getNodeFromInstance(responderInst);
activeResponders[nodeTag] = responderInst;
this.globalHandler.onChange(null, responderInst, blockHostResponder);
};
exports.onResponderEnd = function(responderInst) {
const nodeTag = EventPluginUtils.getNodeFromInstance(responderInst);
delete activeResponders[nodeTag];
this.globalHandler.onChange(responderInst, null);
};
exports.findAncestor = function(targetInst) {
let parentInst = typeof targetInst === 'number' ?
EventPluginUtils.getInstanceFromNode(targetInst) :
targetInst;
while (parentInst != null) {
let parentTag = EventPluginUtils.getNodeFromInstance(parentInst);
if (activeResponders[parentTag]) {
return activeResponders[parentTag];
}
parentInst = EventPluginUtils.getParentInstance(parentInst);
}
return null;
};

View File

@@ -14,8 +14,9 @@
var EventConstants = require('EventConstants');
var EventPluginUtils = require('EventPluginUtils');
var EventPropagators = require('EventPropagators');
var GestureCache = require('GestureCache');
var ResponderCache = require('ResponderCache');
var ResponderSyntheticEvent = require('ResponderSyntheticEvent');
var ResponderTouchHistoryStore = require('ResponderTouchHistoryStore');
var accumulate = require('accumulate');
var keyOf = require('keyOf');
@@ -28,12 +29,6 @@ var hasDispatches = EventPluginUtils.hasDispatches;
var executeDispatchesInOrderStopAtTrue =
EventPluginUtils.executeDispatchesInOrderStopAtTrue;
/**
* Instance of element that should respond to touch/move types of interactions,
* as indicated explicitly by relevant callbacks.
*/
var responderInst = null;
/**
* Count of current touches. A textInput should become responder iff the
* selection changes while there is a touch on the screen.
@@ -45,18 +40,6 @@ var trackedTouchCount = 0;
*/
var previousActiveTouches = 0;
var changeResponder = function(nextResponderInst, blockHostResponder) {
var oldResponderInst = responderInst;
responderInst = nextResponderInst;
if (ResponderEventPlugin.GlobalResponderHandler !== null) {
ResponderEventPlugin.GlobalResponderHandler.onChange(
oldResponderInst,
nextResponderInst,
blockHostResponder
);
}
};
var eventTypes = {
/**
* On a `touchStart`/`mouseDown`, is it desired that this element become the
@@ -330,6 +313,16 @@ function setResponderAndExtractTransfer(
eventTypes.selectionChangeShouldSetResponder :
eventTypes.scrollShouldSetResponder;
// Active responders automatically capture touches inside descendants.
var responderInst = ResponderCache.findAncestor(targetInst);
if (responderInst && responderInst !== targetInst) {
GestureCache.targetChanged(
topLevelType,
targetInst,
responderInst
);
}
// TODO: stop one short of the current responder.
var bubbleShouldSetFrom = !responderInst ?
targetInst :
@@ -346,7 +339,7 @@ function setResponderAndExtractTransfer(
nativeEvent,
nativeEventTarget
);
shouldSetEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
shouldSetEvent.touchHistory = nativeEvent.touchHistory;
if (skipOverBubbleShouldSetFrom) {
EventPropagators.accumulateTwoPhaseDispatchesSkipTarget(shouldSetEvent);
} else {
@@ -361,25 +354,15 @@ function setResponderAndExtractTransfer(
return null;
}
var extracted;
var grantEvent = ResponderSyntheticEvent.getPooled(
eventTypes.responderGrant,
wantsResponderInst,
nativeEvent,
nativeEventTarget
);
grantEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
EventPropagators.accumulateDirectDispatches(grantEvent);
var blockHostResponder = executeDirectDispatch(grantEvent) === true;
if (responderInst) {
var terminationRequestEvent = ResponderSyntheticEvent.getPooled(
eventTypes.responderTerminationRequest,
responderInst,
nativeEvent,
nativeEventTarget
);
terminationRequestEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
terminationRequestEvent.touchHistory = nativeEvent.touchHistory;
EventPropagators.accumulateDirectDispatches(terminationRequestEvent);
var shouldSwitch = !hasDispatches(terminationRequestEvent) ||
executeDirectDispatch(terminationRequestEvent);
@@ -387,32 +370,56 @@ function setResponderAndExtractTransfer(
terminationRequestEvent.constructor.release(terminationRequestEvent);
}
if (shouldSwitch) {
var terminateEvent = ResponderSyntheticEvent.getPooled(
eventTypes.responderTerminate,
responderInst,
nativeEvent,
nativeEventTarget
);
terminateEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
EventPropagators.accumulateDirectDispatches(terminateEvent);
extracted = accumulate(extracted, [grantEvent, terminateEvent]);
changeResponder(wantsResponderInst, blockHostResponder);
} else {
if (!shouldSwitch) {
var rejectEvent = ResponderSyntheticEvent.getPooled(
eventTypes.responderReject,
wantsResponderInst,
nativeEvent,
nativeEventTarget
);
rejectEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
rejectEvent.touchHistory = nativeEvent.touchHistory;
EventPropagators.accumulateDirectDispatches(rejectEvent);
extracted = accumulate(extracted, rejectEvent);
return accumulate(extracted, rejectEvent);
}
var terminateEvent = ResponderSyntheticEvent.getPooled(
eventTypes.responderTerminate,
responderInst,
nativeEvent,
nativeEventTarget
);
terminateEvent.touchHistory = nativeEvent.touchHistory;
EventPropagators.accumulateDirectDispatches(terminateEvent);
extracted = accumulate(extracted, terminateEvent);
// Always dispatch 'terminateEvent' before 'grantEvent'.
if (hasDispatches(terminateEvent)) {
executeDirectDispatch(terminateEvent);
}
} else {
extracted = accumulate(extracted, grantEvent);
changeResponder(wantsResponderInst, blockHostResponder);
}
// Transfer gesture to next responder.
if (responderInst || targetInst !== wantsResponderInst) {
GestureCache.targetChanged(
topLevelType,
responderInst || targetInst,
wantsResponderInst
);
}
var grantEvent = ResponderSyntheticEvent.getPooled(
eventTypes.responderGrant,
wantsResponderInst,
nativeEvent,
nativeEventTarget
);
grantEvent.touchHistory = nativeEvent.touchHistory;
EventPropagators.accumulateDirectDispatches(grantEvent);
extracted = accumulate(extracted, grantEvent);
var blockHostResponder = executeDirectDispatch(grantEvent) === true;
responderInst && ResponderCache.onResponderEnd(responderInst);
ResponderCache.onResponderGrant(wantsResponderInst, blockHostResponder);
return extracted;
}
@@ -438,32 +445,105 @@ function canTriggerTransfer(topLevelType, topLevelInst, nativeEvent) {
);
}
/**
* Returns whether or not this touch end event makes it such that there are no
* longer any touches that started inside of the current `responderInst`.
*
* @param {NativeEvent} nativeEvent Native touch end event.
* @return {boolean} Whether or not this touch end event ends the responder.
*/
function noResponderTouches(nativeEvent) {
var touches = nativeEvent.touches;
if (!touches || touches.length === 0) {
return true;
}
for (var i = 0; i < touches.length; i++) {
var activeTouch = touches[i];
var target = activeTouch.target;
if (target !== null && target !== undefined && target !== 0) {
// Is the original touch location inside of the current responder?
var targetInst = EventPluginUtils.getInstanceFromNode(target);
if (EventPluginUtils.isAncestor(responderInst, targetInst)) {
return false;
}
}
}
return true;
}
function extractTouchEvents(
topLevelType,
targetInst,
nativeEvent,
nativeEventTarget
) {
var extracted;
if (canTriggerTransfer(topLevelType, targetInst, nativeEvent)) {
extracted = setResponderAndExtractTransfer(
topLevelType,
targetInst,
nativeEvent,
nativeEventTarget
);
}
// Find the first ancestor that is currently responding.
var responderInst = ResponderCache.findAncestor(targetInst);
if (responderInst) {
nativeEventTarget =
EventPluginUtils.getNodeFromInstance(responderInst);
}
/**
* Responder may or may not have transferred on a new touch start/move.
* Regardless, whoever is the responder after any potential transfer, we
* direct all touch start/move/ends to them in the form of
* `onResponderMove/Start/End`. These will be called for *every* additional
* finger that move/start/end, dispatched directly to whoever is the
* current responder at that moment, until the responder is "released".
*
* These multiple individual change touch events are are always bookended
* by `onResponderGrant`, and one of
* (`onResponderRelease/onResponderTerminate`).
*/
var isResponderTouchStart = responderInst && isStartish(topLevelType);
var isResponderTouchMove = responderInst && isMoveish(topLevelType);
var isResponderTouchEnd = responderInst && isEndish(topLevelType);
var incrementalTouch =
isResponderTouchStart ? eventTypes.responderStart :
isResponderTouchMove ? eventTypes.responderMove :
isResponderTouchEnd ? eventTypes.responderEnd :
null;
var touchHandler = ResponderEventPlugin.GlobalTouchHandler;
if (touchHandler && isEndish(topLevelType)) {
var endishEvent =
ResponderSyntheticEvent.getPooled(
eventTypes.responderEnd,
responderInst || targetInst,
nativeEvent,
nativeEventTarget
);
endishEvent.touchHistory = nativeEvent.touchHistory;
touchHandler.onTouchEnd(endishEvent);
}
if (incrementalTouch) {
var touchEvent =
ResponderSyntheticEvent.getPooled(
incrementalTouch,
responderInst,
nativeEvent,
nativeEventTarget
);
touchEvent.touchHistory = nativeEvent.touchHistory;
EventPropagators.accumulateDirectDispatches(touchEvent);
extracted = accumulate(extracted, touchEvent);
}
var isResponderTerminate =
responderInst &&
topLevelType === EventConstants.topLevelTypes.topTouchCancel;
var isResponderRelease =
responderInst &&
!isResponderTerminate &&
isEndish(topLevelType) &&
nativeEvent.touchHistory.numberActiveTouches === 0;
var finalTouch =
isResponderTerminate ? eventTypes.responderTerminate :
isResponderRelease ? eventTypes.responderRelease :
null;
if (finalTouch) {
ResponderCache.onResponderEnd(responderInst);
var finalEvent =
ResponderSyntheticEvent.getPooled(
finalTouch,
responderInst,
nativeEvent,
nativeEventTarget
);
finalEvent.touchHistory = nativeEvent.touchHistory;
EventPropagators.accumulateDirectDispatches(finalEvent);
extracted = accumulate(extracted, finalEvent);
}
return extracted;
}
var ResponderEventPlugin = {
@@ -485,12 +565,15 @@ var ResponderEventPlugin = {
nativeEvent,
nativeEventTarget
) {
if (!nativeEvent.touches) {
return null;
}
if (isStartish(topLevelType)) {
trackedTouchCount += 1;
trackedTouchCount += nativeEvent.changedTouches.length;
} else if (isEndish(topLevelType)) {
if (trackedTouchCount >= 0) {
trackedTouchCount -= 1;
} else {
trackedTouchCount -= nativeEvent.changedTouches.length;
if (trackedTouchCount < 0) {
console.error(
'Ended a touch event which was not counted in `trackedTouchCount`.'
);
@@ -498,93 +581,52 @@ var ResponderEventPlugin = {
}
}
ResponderTouchHistoryStore.recordTouchTrack(topLevelType, nativeEvent);
// Touches are grouped by active target.
var changedGestures = GestureCache.touchesChanged(topLevelType, nativeEvent);
var extracted = canTriggerTransfer(topLevelType, targetInst, nativeEvent) ?
setResponderAndExtractTransfer(
var extracted;
changedGestures.forEach(gesture => {
gesture.touches = Object.values(gesture.touchMap);
var targetInst = EventPluginUtils.getInstanceFromNode(gesture.target);
var touchEvents = extractTouchEvents(
topLevelType,
targetInst,
nativeEvent,
nativeEventTarget) :
null;
// Responder may or may not have transferred on a new touch start/move.
// Regardless, whoever is the responder after any potential transfer, we
// direct all touch start/move/ends to them in the form of
// `onResponderMove/Start/End`. These will be called for *every* additional
// finger that move/start/end, dispatched directly to whoever is the
// current responder at that moment, until the responder is "released".
//
// These multiple individual change touch events are are always bookended
// by `onResponderGrant`, and one of
// (`onResponderRelease/onResponderTerminate`).
var isResponderTouchStart = responderInst && isStartish(topLevelType);
var isResponderTouchMove = responderInst && isMoveish(topLevelType);
var isResponderTouchEnd = responderInst && isEndish(topLevelType);
var incrementalTouch =
isResponderTouchStart ? eventTypes.responderStart :
isResponderTouchMove ? eventTypes.responderMove :
isResponderTouchEnd ? eventTypes.responderEnd :
null;
if (incrementalTouch) {
var gesture =
ResponderSyntheticEvent.getPooled(
incrementalTouch,
responderInst,
nativeEvent,
nativeEventTarget
);
gesture.touchHistory = ResponderTouchHistoryStore.touchHistory;
EventPropagators.accumulateDirectDispatches(gesture);
extracted = accumulate(extracted, gesture);
}
var isResponderTerminate =
responderInst &&
topLevelType === EventConstants.topLevelTypes.topTouchCancel;
var isResponderRelease =
responderInst &&
!isResponderTerminate &&
isEndish(topLevelType) &&
noResponderTouches(nativeEvent);
var finalTouch =
isResponderTerminate ? eventTypes.responderTerminate :
isResponderRelease ? eventTypes.responderRelease :
null;
if (finalTouch) {
var finalEvent = ResponderSyntheticEvent.getPooled(
finalTouch, responderInst, nativeEvent, nativeEventTarget
gesture,
gesture.target
);
finalEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
EventPropagators.accumulateDirectDispatches(finalEvent);
extracted = accumulate(extracted, finalEvent);
changeResponder(null);
}
if (touchEvents) {
extracted = accumulate(extracted, touchEvents);
}
});
var numberActiveTouches =
ResponderTouchHistoryStore.touchHistory.numberActiveTouches;
if (ResponderEventPlugin.GlobalInteractionHandler &&
numberActiveTouches !== previousActiveTouches) {
ResponderEventPlugin.GlobalInteractionHandler.onChange(
numberActiveTouches
);
var interactionHandler = ResponderEventPlugin.GlobalInteractionHandler;
if (interactionHandler && trackedTouchCount !== previousActiveTouches) {
interactionHandler.onChange(trackedTouchCount);
}
previousActiveTouches = numberActiveTouches;
previousActiveTouches = trackedTouchCount;
return extracted;
},
GlobalResponderHandler: null,
GlobalTouchHandler: null,
GlobalInteractionHandler: null,
injection: {
/**
* @param {onTouchEnd: (TouchEvent) => void} GlobalTouchHandler
* Object that handles all touch events.
*/
injectGlobalTouchHandler:function(GlobalTouchHandler) {
ResponderEventPlugin.GlobalTouchHandler = GlobalTouchHandler;
},
/**
* @param {{onChange: (ReactID, ReactID) => void} GlobalResponderHandler
* Object that handles any change in responder. Use this to inject
* integration with an existing touch handling system etc.
*/
injectGlobalResponderHandler: function(GlobalResponderHandler) {
ResponderEventPlugin.GlobalResponderHandler = GlobalResponderHandler;
ResponderCache.globalHandler = GlobalResponderHandler;
},
/**

View File

@@ -0,0 +1,228 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule TouchHistory
* @flow
*/
'use strict';
const EventPluginUtils = require('EventPluginUtils');
const invariant = require('invariant');
const warning = require('warning');
const {
isEndish,
isMoveish,
isStartish,
} = EventPluginUtils;
export type Touch = {
identifier: ?number,
pageX: number,
pageY: number,
timestamp: number,
};
export type TouchEvent = {
changedTouches: Array<Touch>,
touches: Array<Touch>,
};
/**
* Tracks the position and time of each active touch by `touch.identifier`. We
* should typically only see IDs in the range of 1-20 because IDs get recycled
* when touches end and start again.
*/
type TouchRecord = {
touchActive: boolean,
startPageX: number,
startPageY: number,
startTimeStamp: number,
currentPageX: number,
currentPageY: number,
currentTimeStamp: number,
previousPageX: number,
previousPageY: number,
previousTimeStamp: number,
};
type TouchBank = Array<TouchRecord>;
const MAX_TOUCH_BANK = 20;
class TouchHistory {
constructor() {
this.touchBank = [];
this.numberActiveTouches = 0;
// If there is only one active touch, we remember its location. This prevents
// us having to loop through all of the touches all the time in the most
// common case.
this.indexOfSingleActiveTouch = -1;
this.mostRecentTimeStamp = 0;
}
recordTouchEvent(topLevelType: string, changedTouches: Array<Touch>): void {
if (isMoveish(topLevelType)) {
changedTouches.forEach(touch => this._recordTouchMove(touch));
} else if (isStartish(topLevelType)) {
changedTouches.forEach(touch => this._recordTouchStart(touch));
if (this.numberActiveTouches === 1) {
this.indexOfSingleActiveTouch =
changedTouches[0].identifier;
}
} else if (isEndish(topLevelType)) {
changedTouches.forEach(touch => this._recordTouchEnd(touch));
if (this.numberActiveTouches === 1) {
for (let i = 0; i < this.touchBank.length; i++) {
const touchTrackToCheck = this.touchBank[i];
if (touchTrackToCheck != null && touchTrackToCheck.touchActive) {
this.indexOfSingleActiveTouch = i;
break;
}
}
if (__DEV__) {
const activeRecord = this.touchBank[this.indexOfSingleActiveTouch];
warning(
activeRecord != null &&
activeRecord.touchActive,
'Cannot find single active touch.'
);
}
}
}
}
_recordTouchStart(touch: Touch): void {
const identifier = getTouchIdentifier(touch);
const touchRecord = this.touchBank[identifier];
if (touchRecord) {
resetTouchRecord(touchRecord, touch);
} else {
this.touchBank[identifier] = createTouchRecord(touch);
}
this.mostRecentTimeStamp = timestampForTouch(touch);
this.numberActiveTouches += 1;
}
_recordTouchMove(touch: Touch): void {
const touchRecord = this.touchBank[getTouchIdentifier(touch)];
if (touchRecord) {
touchRecord.touchActive = true;
touchRecord.previousPageX = touchRecord.currentPageX;
touchRecord.previousPageY = touchRecord.currentPageY;
touchRecord.previousTimeStamp = touchRecord.currentTimeStamp;
touchRecord.currentPageX = touch.pageX;
touchRecord.currentPageY = touch.pageY;
touchRecord.currentTimeStamp = timestampForTouch(touch);
this.mostRecentTimeStamp = timestampForTouch(touch);
} else {
console.error(
'Cannot record touch move without a touch start.\n' +
'Touch Move: %s\n',
'Touch Bank: %s',
printTouch(touch),
printTouchBank(this.touchBank)
);
}
}
_recordTouchEnd(touch: Touch): void {
const touchRecord = this.touchBank[getTouchIdentifier(touch)];
if (touchRecord) {
this.numberActiveTouches -= 1;
touchRecord.touchActive = false;
touchRecord.previousPageX = touchRecord.currentPageX;
touchRecord.previousPageY = touchRecord.currentPageY;
touchRecord.previousTimeStamp = touchRecord.currentTimeStamp;
touchRecord.currentPageX = touch.pageX;
touchRecord.currentPageY = touch.pageY;
touchRecord.currentTimeStamp = timestampForTouch(touch);
this.mostRecentTimeStamp = timestampForTouch(touch);
} else {
console.error(
'Cannot record touch end without a touch start.\n' +
'Touch End: %s\n',
'Touch Bank: %s',
printTouch(touch),
printTouchBank(this.touchBank)
);
}
}
}
module.exports = TouchHistory;
function timestampForTouch(touch: Touch): number {
// The legacy internal implementation provides "timeStamp", which has been
// renamed to "timestamp". Let both work for now while we iron it out
// TODO (evv): rename timeStamp to timestamp in internal code
return (touch: any).timeStamp || touch.timestamp;
}
function getTouchIdentifier({identifier}: Touch): number {
invariant(identifier != null, 'Touch object is missing identifier.');
warning(
identifier <= MAX_TOUCH_BANK,
'Touch identifier %s is greater than maximum supported %s which causes ' +
'performance issues backfilling array locations for all of the indices.',
identifier,
MAX_TOUCH_BANK
);
return identifier;
}
function printTouch(touch: Touch): string {
return JSON.stringify({
identifier: touch.identifier,
pageX: touch.pageX,
pageY: touch.pageY,
timestamp: timestampForTouch(touch),
});
}
function printTouchBank(touchBank: TouchBank): string {
let printed = JSON.stringify(touchBank.slice(0, MAX_TOUCH_BANK));
if (touchBank.length > MAX_TOUCH_BANK) {
printed += ' (original size: ' + touchBank.length + ')';
}
return printed;
}
/**
* TODO: Instead of making gestures recompute filtered velocity, we could
* include a built in velocity computation that can be reused globally.
*/
function createTouchRecord(touch: Touch): TouchRecord {
return {
touchActive: true,
startPageX: touch.pageX,
startPageY: touch.pageY,
startTimeStamp: timestampForTouch(touch),
currentPageX: touch.pageX,
currentPageY: touch.pageY,
currentTimeStamp: timestampForTouch(touch),
previousPageX: touch.pageX,
previousPageY: touch.pageY,
previousTimeStamp: timestampForTouch(touch),
};
}
function resetTouchRecord(touchRecord: TouchRecord, touch: Touch): void {
touchRecord.touchActive = true;
touchRecord.startPageX = touch.pageX;
touchRecord.startPageY = touch.pageY;
touchRecord.startTimeStamp = timestampForTouch(touch);
touchRecord.currentPageX = touch.pageX;
touchRecord.currentPageY = touch.pageY;
touchRecord.currentTimeStamp = timestampForTouch(touch);
touchRecord.previousPageX = touch.pageX;
touchRecord.previousPageY = touch.pageY;
touchRecord.previousTimeStamp = timestampForTouch(touch);
}

View File

@@ -730,8 +730,8 @@ describe('ResponderEventPlugin', function() {
// Parent is responder, and responder is transferred by a second touch start
config.startShouldSetResponder.captured.grandParent = {order: 0, returnVal: true};
config.responderGrant.grandParent = {order: 1};
config.responderTerminationRequest.parent = {order: 2, returnVal: true};
config.responderTerminationRequest.parent = {order: 1, returnVal: true};
config.responderGrant.grandParent = {order: 2};
config.responderTerminate.parent = {order: 3};
config.responderStart.grandParent = {order: 4};
run(config, three, startConfig(three.child, [three.child, three.child], [1]));
@@ -899,11 +899,10 @@ describe('ResponderEventPlugin', function() {
config.moveShouldSetResponder.captured.grandParent = {order: 0, returnVal: false};
config.moveShouldSetResponder.captured.parent = {order: 1, returnVal: false};
config.moveShouldSetResponder.bubbled.parent = {order: 2, returnVal: true};
config.responderGrant.parent = {order: 3};
config.responderTerminationRequest.child = {order: 4, returnVal: false};
config.responderReject.parent = {order: 5};
config.responderTerminationRequest.child = {order: 3, returnVal: false};
config.responderReject.parent = {order: 4};
// The start/move should occur on the original responder if new one is rejected
config.responderMove.child = {order: 6};
config.responderMove.child = {order: 5};
var touchConfig =
moveConfig(three.child, [three.child], [0]);
@@ -914,11 +913,10 @@ describe('ResponderEventPlugin', function() {
config.startShouldSetResponder.captured.grandParent = {order: 0, returnVal: false};
config.startShouldSetResponder.captured.parent = {order: 1, returnVal: false};
config.startShouldSetResponder.bubbled.parent = {order: 2, returnVal: true};
config.responderGrant.parent = {order: 3};
config.responderTerminationRequest.child = {order: 4, returnVal: false};
config.responderReject.parent = {order: 5};
config.responderTerminationRequest.child = {order: 3, returnVal: false};
config.responderReject.parent = {order: 4};
// The start/move should occur on the original responder if new one is rejected
config.responderStart.child = {order: 6};
config.responderStart.child = {order: 5};
touchConfig =
startConfig(three.child, [three.child, three.child], [1]);
@@ -948,9 +946,8 @@ describe('ResponderEventPlugin', function() {
config.scrollShouldSetResponder.captured.grandParent = {order: 0, returnVal: false};
config.scrollShouldSetResponder.captured.parent = {order: 1, returnVal: false};
config.scrollShouldSetResponder.bubbled.parent = {order: 2, returnVal: true};
config.responderGrant.parent = {order: 3};
config.responderTerminationRequest.child = {order: 4, returnVal: false};
config.responderReject.parent = {order: 5};
config.responderTerminationRequest.child = {order: 3, returnVal: false};
config.responderReject.parent = {order: 4};
run(config, three, {
topLevelType: topLevelTypes.topScroll,
@@ -965,8 +962,8 @@ describe('ResponderEventPlugin', function() {
config.scrollShouldSetResponder.captured.grandParent = {order: 0, returnVal: false};
config.scrollShouldSetResponder.captured.parent = {order: 1, returnVal: false};
config.scrollShouldSetResponder.bubbled.parent = {order: 2, returnVal: true};
config.responderGrant.parent = {order: 3};
config.responderTerminationRequest.child = {order: 4, returnVal: true};
config.responderTerminationRequest.child = {order: 3, returnVal: true};
config.responderGrant.parent = {order: 4};
config.responderTerminate.child = {order: 5};
run(config, three, {