From ab946df12aefa1474ca70c5a666c4c8691d5eba9 Mon Sep 17 00:00:00 2001 From: Boopesh Mahendran Date: Fri, 11 Aug 2017 14:47:52 +0530 Subject: [PATCH 01/11] Add Preact library --- src/main.js | 7 + src/thirdparty/preact/preact-compat.js | 627 +++++++++++++++++++++++++ src/thirdparty/preact/preact.js | 409 ++++++++++++++++ test/SpecRunner.js | 7 + 4 files changed, 1050 insertions(+) create mode 100644 src/thirdparty/preact/preact-compat.js create mode 100644 src/thirdparty/preact/preact.js diff --git a/src/main.js b/src/main.js index 38f0e36c5a1..a732b37d0fd 100644 --- a/src/main.js +++ b/src/main.js @@ -35,6 +35,13 @@ require.config({ // implementations (e.g. cloud-based storage). "fileSystemImpl" : "filesystem/impls/appshell/AppshellFileSystem" }, + packages: [ + { + name: "thirdparty/preact", + location: "thirdparty/preact", + main: "preact-compat" + } + ], map: { "*": { "thirdparty/CodeMirror2": "thirdparty/CodeMirror", diff --git a/src/thirdparty/preact/preact-compat.js b/src/thirdparty/preact/preact-compat.js new file mode 100644 index 00000000000..9f0c565ec13 --- /dev/null +++ b/src/thirdparty/preact/preact-compat.js @@ -0,0 +1,627 @@ +//Preact compat version 3.17.0 +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./preact')) : + typeof define === 'function' && define.amd ? define(['./preact'], factory) : + (global.preactCompat = factory(global.preact)); +}(this, (function (preact) { + + +var version = '15.1.0'; // trick libraries to think we are react + +var ELEMENTS = 'a abbr address area article aside audio b base bdi bdo big blockquote body br button canvas caption cite code col colgroup data datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 head header hgroup hr html i iframe img input ins kbd keygen label legend li link main map mark menu menuitem meta meter nav noscript object ol optgroup option output p param picture pre progress q rp rt ruby s samp script section select small source span strong style sub summary sup table tbody td textarea tfoot th thead time title tr track u ul var video wbr circle clipPath defs ellipse g image line linearGradient mask path pattern polygon polyline radialGradient rect stop svg text tspan'.split(' '); + +var REACT_ELEMENT_TYPE = (typeof Symbol!=='undefined' && Symbol.for && Symbol.for('react.element')) || 0xeac7; + +var COMPONENT_WRAPPER_KEY = typeof Symbol!=='undefined' ? Symbol.for('__preactCompatWrapper') : '__preactCompatWrapper'; + +// don't autobind these methods since they already have guaranteed context. +var AUTOBIND_BLACKLIST = { + constructor: 1, + render: 1, + shouldComponentUpdate: 1, + componentWillReceiveProps: 1, + componentWillUpdate: 1, + componentDidUpdate: 1, + componentWillMount: 1, + componentDidMount: 1, + componentWillUnmount: 1, + componentDidUnmount: 1 +}; + + +var CAMEL_PROPS = /^(?:accent|alignment|arabic|baseline|cap|clip|color|fill|flood|font|glyph|horiz|marker|overline|paint|stop|strikethrough|stroke|text|underline|unicode|units|v|vector|vert|word|writing|x)[A-Z]/; + + +var BYPASS_HOOK = {}; + +/*global process*/ +var DEV = typeof process==='undefined' || !process.env || process.env.NODE_ENV!=='production'; + +// a component that renders nothing. Used to replace components for unmountComponentAtNode. +function EmptyComponent() { return null; } + + + +// make react think we're react. +var VNode = preact.h('a', null).constructor; +VNode.prototype.$$typeof = REACT_ELEMENT_TYPE; +VNode.prototype.preactCompatUpgraded = false; +VNode.prototype.preactCompatNormalized = false; + +Object.defineProperty(VNode.prototype, 'type', { + get: function() { return this.nodeName; }, + set: function(v) { this.nodeName = v; }, + configurable:true +}); + +Object.defineProperty(VNode.prototype, 'props', { + get: function() { return this.attributes; }, + set: function(v) { this.attributes = v; }, + configurable:true +}); + + + +var oldEventHook = preact.options.event; +preact.options.event = function (e) { + if (oldEventHook) { e = oldEventHook(e); } + e.persist = Object; + e.nativeEvent = e; + return e; +}; + + +var oldVnodeHook = preact.options.vnode; +preact.options.vnode = function (vnode) { + if (!vnode.preactCompatUpgraded) { + vnode.preactCompatUpgraded = true; + + var tag = vnode.nodeName, + attrs = vnode.attributes = extend({}, vnode.attributes); + + if (typeof tag==='function') { + if (tag[COMPONENT_WRAPPER_KEY]===true || (tag.prototype && 'isReactComponent' in tag.prototype)) { + if (vnode.children && String(vnode.children)==='') { vnode.children = undefined; } + if (vnode.children) { attrs.children = vnode.children; } + + if (!vnode.preactCompatNormalized) { + normalizeVNode(vnode); + } + handleComponentVNode(vnode); + } + } + else { + if (vnode.children && String(vnode.children)==='') { vnode.children = undefined; } + if (vnode.children) { attrs.children = vnode.children; } + + if (attrs.defaultValue) { + if (!attrs.value && attrs.value!==0) { + attrs.value = attrs.defaultValue; + } + delete attrs.defaultValue; + } + + handleElementVNode(vnode, attrs); + } + } + + if (oldVnodeHook) { oldVnodeHook(vnode); } +}; + +function handleComponentVNode(vnode) { + var tag = vnode.nodeName, + a = vnode.attributes; + + vnode.attributes = {}; + if (tag.defaultProps) { extend(vnode.attributes, tag.defaultProps); } + if (a) { extend(vnode.attributes, a); } +} + +function handleElementVNode(vnode, a) { + var shouldSanitize, attrs, i; + if (a) { + for (i in a) { if ((shouldSanitize = CAMEL_PROPS.test(i))) { break; } } + if (shouldSanitize) { + attrs = vnode.attributes = {}; + for (i in a) { + if (a.hasOwnProperty(i)) { + attrs[ CAMEL_PROPS.test(i) ? i.replace(/([A-Z0-9])/, '-$1').toLowerCase() : i ] = a[i]; + } + } + } + } +} + + + +// proxy render() since React returns a Component reference. +function render$1(vnode, parent, callback) { + var prev = parent && parent._preactCompatRendered && parent._preactCompatRendered.base; + + // ignore impossible previous renders + if (prev && prev.parentNode!==parent) { prev = null; } + + // default to first Element child + if (!prev && parent) { prev = parent.firstElementChild; } + + // remove unaffected siblings + for (var i=parent.childNodes.length; i--; ) { + if (parent.childNodes[i]!==prev) { + parent.removeChild(parent.childNodes[i]); + } + } + + var out = preact.render(vnode, parent, prev); + if (parent) { parent._preactCompatRendered = out && (out._component || { base: out }); } + if (typeof callback==='function') { callback(); } + return out && out._component || out; +} + + +var ContextProvider = function () {}; + +ContextProvider.prototype.getChildContext = function () { + return this.props.context; +}; +ContextProvider.prototype.render = function (props) { + return props.children[0]; +}; + +function renderSubtreeIntoContainer(parentComponent, vnode, container, callback) { + var wrap = preact.h(ContextProvider, { context: parentComponent.context }, vnode); + var renderContainer = render$1(wrap, container); + var component = renderContainer._component || renderContainer.base; + if (callback) { callback.call(component, renderContainer); } + return component; +} + + +function unmountComponentAtNode(container) { + var existing = container._preactCompatRendered && container._preactCompatRendered.base; + if (existing && existing.parentNode===container) { + preact.render(preact.h(EmptyComponent), container, existing); + return true; + } + return false; +} + + + +var ARR = []; + +// This API is completely unnecessary for Preact, so it's basically passthrough. +var Children = { + map: function(children, fn, ctx) { + if (children == null) { return null; } + children = Children.toArray(children); + if (ctx && ctx!==children) { fn = fn.bind(ctx); } + return children.map(fn); + }, + forEach: function(children, fn, ctx) { + if (children == null) { return null; } + children = Children.toArray(children); + if (ctx && ctx!==children) { fn = fn.bind(ctx); } + children.forEach(fn); + }, + count: function(children) { + return children && children.length || 0; + }, + only: function(children) { + children = Children.toArray(children); + if (children.length!==1) { throw new Error('Children.only() expects only one child.'); } + return children[0]; + }, + toArray: function(children) { + if (children == null) { return []; } + return ARR.concat(children); + } +}; + + +/** Track current render() component for ref assignment */ +var currentComponent; + + +function createFactory(type) { + return createElement.bind(null, type); +} + + +var DOM = {}; +for (var i=ELEMENTS.length; i--; ) { + DOM[ELEMENTS[i]] = createFactory(ELEMENTS[i]); +} + +function upgradeToVNodes(arr, offset) { + for (var i=offset || 0; i 0 ) children[ len ] = arguments[ len + 2 ]; + + if (!isValidElement(element)) { return element; } + var elementProps = element.attributes || element.props; + var node = preact.h( + element.nodeName || element.type, + elementProps, + element.children || elementProps && elementProps.children + ); + // Only provide the 3rd argument if needed. + // Arguments 3+ overwrite element.children in preactCloneElement + var cloneArgs = [node, props]; + if (children && children.length) { + cloneArgs.push(children); + } + else if (props && props.children) { + cloneArgs.push(props.children); + } + return normalizeVNode(preact.cloneElement.apply(void 0, cloneArgs)); +} + + +function isValidElement(element) { + return element && ((element instanceof VNode) || element.$$typeof===REACT_ELEMENT_TYPE); +} + + +function createStringRefProxy(name, component) { + return component._refProxies[name] || (component._refProxies[name] = function (resolved) { + if (component && component.refs) { + component.refs[name] = resolved; + if (resolved===null) { + delete component._refProxies[name]; + component = null; + } + } + }); +} + + +function applyEventNormalization(ref) { + var nodeName = ref.nodeName; + var attributes = ref.attributes; + + if (!attributes || typeof nodeName!=='string') { return; } + var props = {}; + for (var i in attributes) { + props[i.toLowerCase()] = i; + } + if (props.ondoubleclick) { + attributes.ondblclick = attributes[props.ondoubleclick]; + delete attributes[props.ondoubleclick]; + } + // for *textual inputs* (incl textarea), normalize `onChange` -> `onInput`: + if (props.onchange && (nodeName==='textarea' || (nodeName.toLowerCase()==='input' && !/^fil|che|rad/i.test(attributes.type)))) { + var normalized = props.oninput || 'oninput'; + if (!attributes[normalized]) { + attributes[normalized] = multihook([attributes[normalized], attributes[props.onchange]]); + delete attributes[props.onchange]; + } + } +} + + +function applyClassName(vnode) { + var a = vnode.attributes || (vnode.attributes = {}); + classNameDescriptor.enumerable = 'className' in a; + if (a.className) { a.class = a.className; } + Object.defineProperty(a, 'className', classNameDescriptor); +} + + +var classNameDescriptor = { + configurable: true, + get: function() { return this.class; }, + set: function(v) { this.class = v; } +}; + +function extend(base, props) { + var arguments$1 = arguments; + + for (var i=1, obj = (void 0); i 2; ) stack.push(arguments[i]); + if (attributes && null != attributes.children) { + if (!stack.length) stack.push(attributes.children); + delete attributes.children; + } + while (stack.length) if ((child = stack.pop()) && void 0 !== child.pop) for (i = child.length; i--; ) stack.push(child[i]); else { + if ('boolean' == typeof child) child = null; + if (simple = 'function' != typeof nodeName) if (null == child) child = ''; else if ('number' == typeof child) child = String(child); else if ('string' != typeof child) simple = !1; + if (simple && lastSimple) children[children.length - 1] += child; else if (children === EMPTY_CHILDREN) children = [ child ]; else children.push(child); + lastSimple = simple; + } + var p = new VNode(); + p.nodeName = nodeName; + p.children = children; + p.attributes = null == attributes ? void 0 : attributes; + p.key = null == attributes ? void 0 : attributes.key; + if (void 0 !== options.vnode) options.vnode(p); + return p; + } + function extend(obj, props) { + for (var i in props) obj[i] = props[i]; + return obj; + } + function cloneElement(vnode, props) { + return h(vnode.nodeName, extend(extend({}, vnode.attributes), props), arguments.length > 2 ? [].slice.call(arguments, 2) : vnode.children); + } + function enqueueRender(component) { + if (!component.__d && (component.__d = !0) && 1 == items.push(component)) (options.debounceRendering || defer)(rerender); + } + function rerender() { + var p, list = items; + items = []; + while (p = list.pop()) if (p.__d) renderComponent(p); + } + function isSameNodeType(node, vnode, hydrating) { + if ('string' == typeof vnode || 'number' == typeof vnode) return void 0 !== node.splitText; + if ('string' == typeof vnode.nodeName) return !node._componentConstructor && isNamedNode(node, vnode.nodeName); else return hydrating || node._componentConstructor === vnode.nodeName; + } + function isNamedNode(node, nodeName) { + return node.__n === nodeName || node.nodeName.toLowerCase() === nodeName.toLowerCase(); + } + function getNodeProps(vnode) { + var props = extend({}, vnode.attributes); + props.children = vnode.children; + var defaultProps = vnode.nodeName.defaultProps; + if (void 0 !== defaultProps) for (var i in defaultProps) if (void 0 === props[i]) props[i] = defaultProps[i]; + return props; + } + function createNode(nodeName, isSvg) { + var node = isSvg ? document.createElementNS('http://www.w3.org/2000/svg', nodeName) : document.createElement(nodeName); + node.__n = nodeName; + return node; + } + function removeNode(node) { + var parentNode = node.parentNode; + if (parentNode) parentNode.removeChild(node); + } + function setAccessor(node, name, old, value, isSvg) { + if ('className' === name) name = 'class'; + if ('key' === name) ; else if ('ref' === name) { + if (old) old(null); + if (value) value(node); + } else if ('class' === name && !isSvg) node.className = value || ''; else if ('style' === name) { + if (!value || 'string' == typeof value || 'string' == typeof old) node.style.cssText = value || ''; + if (value && 'object' == typeof value) { + if ('string' != typeof old) for (var i in old) if (!(i in value)) node.style[i] = ''; + for (var i in value) node.style[i] = 'number' == typeof value[i] && !1 === IS_NON_DIMENSIONAL.test(i) ? value[i] + 'px' : value[i]; + } + } else if ('dangerouslySetInnerHTML' === name) { + if (value) node.innerHTML = value.__html || ''; + } else if ('o' == name[0] && 'n' == name[1]) { + var useCapture = name !== (name = name.replace(/Capture$/, '')); + name = name.toLowerCase().substring(2); + if (value) { + if (!old) node.addEventListener(name, eventProxy, useCapture); + } else node.removeEventListener(name, eventProxy, useCapture); + (node.__l || (node.__l = {}))[name] = value; + } else if ('list' !== name && 'type' !== name && !isSvg && name in node) { + setProperty(node, name, null == value ? '' : value); + if (null == value || !1 === value) node.removeAttribute(name); + } else { + var ns = isSvg && name !== (name = name.replace(/^xlink\:?/, '')); + if (null == value || !1 === value) if (ns) node.removeAttributeNS('http://www.w3.org/1999/xlink', name.toLowerCase()); else node.removeAttribute(name); else if ('function' != typeof value) if (ns) node.setAttributeNS('http://www.w3.org/1999/xlink', name.toLowerCase(), value); else node.setAttribute(name, value); + } + } + function setProperty(node, name, value) { + try { + node[name] = value; + } catch (e) {} + } + function eventProxy(e) { + return this.__l[e.type](options.event && options.event(e) || e); + } + function flushMounts() { + var c; + while (c = mounts.pop()) { + if (options.afterMount) options.afterMount(c); + if (c.componentDidMount) c.componentDidMount(); + } + } + function diff(dom, vnode, context, mountAll, parent, componentRoot) { + if (!diffLevel++) { + isSvgMode = null != parent && void 0 !== parent.ownerSVGElement; + hydrating = null != dom && !('__preactattr_' in dom); + } + var ret = idiff(dom, vnode, context, mountAll, componentRoot); + if (parent && ret.parentNode !== parent) parent.appendChild(ret); + if (!--diffLevel) { + hydrating = !1; + if (!componentRoot) flushMounts(); + } + return ret; + } + function idiff(dom, vnode, context, mountAll, componentRoot) { + var out = dom, prevSvgMode = isSvgMode; + if (null == vnode || 'boolean' == typeof vnode) vnode = ''; + if ('string' == typeof vnode || 'number' == typeof vnode) { + if (dom && void 0 !== dom.splitText && dom.parentNode && (!dom._component || componentRoot)) { + if (dom.nodeValue != vnode) dom.nodeValue = vnode; + } else { + out = document.createTextNode(vnode); + if (dom) { + if (dom.parentNode) dom.parentNode.replaceChild(out, dom); + recollectNodeTree(dom, !0); + } + } + out.__preactattr_ = !0; + return out; + } + var vnodeName = vnode.nodeName; + if ('function' == typeof vnodeName) return buildComponentFromVNode(dom, vnode, context, mountAll); + isSvgMode = 'svg' === vnodeName ? !0 : 'foreignObject' === vnodeName ? !1 : isSvgMode; + vnodeName = String(vnodeName); + if (!dom || !isNamedNode(dom, vnodeName)) { + out = createNode(vnodeName, isSvgMode); + if (dom) { + while (dom.firstChild) out.appendChild(dom.firstChild); + if (dom.parentNode) dom.parentNode.replaceChild(out, dom); + recollectNodeTree(dom, !0); + } + } + var fc = out.firstChild, props = out.__preactattr_, vchildren = vnode.children; + if (null == props) { + props = out.__preactattr_ = {}; + for (var a = out.attributes, i = a.length; i--; ) props[a[i].name] = a[i].value; + } + if (!hydrating && vchildren && 1 === vchildren.length && 'string' == typeof vchildren[0] && null != fc && void 0 !== fc.splitText && null == fc.nextSibling) { + if (fc.nodeValue != vchildren[0]) fc.nodeValue = vchildren[0]; + } else if (vchildren && vchildren.length || null != fc) innerDiffNode(out, vchildren, context, mountAll, hydrating || null != props.dangerouslySetInnerHTML); + diffAttributes(out, vnode.attributes, props); + isSvgMode = prevSvgMode; + return out; + } + function innerDiffNode(dom, vchildren, context, mountAll, isHydrating) { + var j, c, f, vchild, child, originalChildren = dom.childNodes, children = [], keyed = {}, keyedLen = 0, min = 0, len = originalChildren.length, childrenLen = 0, vlen = vchildren ? vchildren.length : 0; + if (0 !== len) for (var i = 0; i < len; i++) { + var _child = originalChildren[i], props = _child.__preactattr_, key = vlen && props ? _child._component ? _child._component.__k : props.key : null; + if (null != key) { + keyedLen++; + keyed[key] = _child; + } else if (props || (void 0 !== _child.splitText ? isHydrating ? _child.nodeValue.trim() : !0 : isHydrating)) children[childrenLen++] = _child; + } + if (0 !== vlen) for (var i = 0; i < vlen; i++) { + vchild = vchildren[i]; + child = null; + var key = vchild.key; + if (null != key) { + if (keyedLen && void 0 !== keyed[key]) { + child = keyed[key]; + keyed[key] = void 0; + keyedLen--; + } + } else if (!child && min < childrenLen) for (j = min; j < childrenLen; j++) if (void 0 !== children[j] && isSameNodeType(c = children[j], vchild, isHydrating)) { + child = c; + children[j] = void 0; + if (j === childrenLen - 1) childrenLen--; + if (j === min) min++; + break; + } + child = idiff(child, vchild, context, mountAll); + f = originalChildren[i]; + if (child && child !== dom && child !== f) if (null == f) dom.appendChild(child); else if (child === f.nextSibling) removeNode(f); else dom.insertBefore(child, f); + } + if (keyedLen) for (var i in keyed) if (void 0 !== keyed[i]) recollectNodeTree(keyed[i], !1); + while (min <= childrenLen) if (void 0 !== (child = children[childrenLen--])) recollectNodeTree(child, !1); + } + function recollectNodeTree(node, unmountOnly) { + var component = node._component; + if (component) unmountComponent(component); else { + if (null != node.__preactattr_ && node.__preactattr_.ref) node.__preactattr_.ref(null); + if (!1 === unmountOnly || null == node.__preactattr_) removeNode(node); + removeChildren(node); + } + } + function removeChildren(node) { + node = node.lastChild; + while (node) { + var next = node.previousSibling; + recollectNodeTree(node, !0); + node = next; + } + } + function diffAttributes(dom, attrs, old) { + var name; + for (name in old) if ((!attrs || null == attrs[name]) && null != old[name]) setAccessor(dom, name, old[name], old[name] = void 0, isSvgMode); + for (name in attrs) if (!('children' === name || 'innerHTML' === name || name in old && attrs[name] === ('value' === name || 'checked' === name ? dom[name] : old[name]))) setAccessor(dom, name, old[name], old[name] = attrs[name], isSvgMode); + } + function collectComponent(component) { + var name = component.constructor.name; + (components[name] || (components[name] = [])).push(component); + } + function createComponent(Ctor, props, context) { + var inst, list = components[Ctor.name]; + if (Ctor.prototype && Ctor.prototype.render) { + inst = new Ctor(props, context); + Component.call(inst, props, context); + } else { + inst = new Component(props, context); + inst.constructor = Ctor; + inst.render = doRender; + } + if (list) for (var i = list.length; i--; ) if (list[i].constructor === Ctor) { + inst.__b = list[i].__b; + list.splice(i, 1); + break; + } + return inst; + } + function doRender(props, state, context) { + return this.constructor(props, context); + } + function setComponentProps(component, props, opts, context, mountAll) { + if (!component.__x) { + component.__x = !0; + if (component.__r = props.ref) delete props.ref; + if (component.__k = props.key) delete props.key; + if (!component.base || mountAll) { + if (component.componentWillMount) component.componentWillMount(); + } else if (component.componentWillReceiveProps) component.componentWillReceiveProps(props, context); + if (context && context !== component.context) { + if (!component.__c) component.__c = component.context; + component.context = context; + } + if (!component.__p) component.__p = component.props; + component.props = props; + component.__x = !1; + if (0 !== opts) if (1 === opts || !1 !== options.syncComponentUpdates || !component.base) renderComponent(component, 1, mountAll); else enqueueRender(component); + if (component.__r) component.__r(component); + } + } + function renderComponent(component, opts, mountAll, isChild) { + if (!component.__x) { + var rendered, inst, cbase, props = component.props, state = component.state, context = component.context, previousProps = component.__p || props, previousState = component.__s || state, previousContext = component.__c || context, isUpdate = component.base, nextBase = component.__b, initialBase = isUpdate || nextBase, initialChildComponent = component._component, skip = !1; + if (isUpdate) { + component.props = previousProps; + component.state = previousState; + component.context = previousContext; + if (2 !== opts && component.shouldComponentUpdate && !1 === component.shouldComponentUpdate(props, state, context)) skip = !0; else if (component.componentWillUpdate) component.componentWillUpdate(props, state, context); + component.props = props; + component.state = state; + component.context = context; + } + component.__p = component.__s = component.__c = component.__b = null; + component.__d = !1; + if (!skip) { + rendered = component.render(props, state, context); + if (component.getChildContext) context = extend(extend({}, context), component.getChildContext()); + var toUnmount, base, childComponent = rendered && rendered.nodeName; + if ('function' == typeof childComponent) { + var childProps = getNodeProps(rendered); + inst = initialChildComponent; + if (inst && inst.constructor === childComponent && childProps.key == inst.__k) setComponentProps(inst, childProps, 1, context, !1); else { + toUnmount = inst; + component._component = inst = createComponent(childComponent, childProps, context); + inst.__b = inst.__b || nextBase; + inst.__u = component; + setComponentProps(inst, childProps, 0, context, !1); + renderComponent(inst, 1, mountAll, !0); + } + base = inst.base; + } else { + cbase = initialBase; + toUnmount = initialChildComponent; + if (toUnmount) cbase = component._component = null; + if (initialBase || 1 === opts) { + if (cbase) cbase._component = null; + base = diff(cbase, rendered, context, mountAll || !isUpdate, initialBase && initialBase.parentNode, !0); + } + } + if (initialBase && base !== initialBase && inst !== initialChildComponent) { + var baseParent = initialBase.parentNode; + if (baseParent && base !== baseParent) { + baseParent.replaceChild(base, initialBase); + if (!toUnmount) { + initialBase._component = null; + recollectNodeTree(initialBase, !1); + } + } + } + if (toUnmount) unmountComponent(toUnmount); + component.base = base; + if (base && !isChild) { + var componentRef = component, t = component; + while (t = t.__u) (componentRef = t).base = base; + base._component = componentRef; + base._componentConstructor = componentRef.constructor; + } + } + if (!isUpdate || mountAll) mounts.unshift(component); else if (!skip) { + if (component.componentDidUpdate) component.componentDidUpdate(previousProps, previousState, previousContext); + if (options.afterUpdate) options.afterUpdate(component); + } + if (null != component.__h) while (component.__h.length) component.__h.pop().call(component); + if (!diffLevel && !isChild) flushMounts(); + } + } + function buildComponentFromVNode(dom, vnode, context, mountAll) { + var c = dom && dom._component, originalComponent = c, oldDom = dom, isDirectOwner = c && dom._componentConstructor === vnode.nodeName, isOwner = isDirectOwner, props = getNodeProps(vnode); + while (c && !isOwner && (c = c.__u)) isOwner = c.constructor === vnode.nodeName; + if (c && isOwner && (!mountAll || c._component)) { + setComponentProps(c, props, 3, context, mountAll); + dom = c.base; + } else { + if (originalComponent && !isDirectOwner) { + unmountComponent(originalComponent); + dom = oldDom = null; + } + c = createComponent(vnode.nodeName, props, context); + if (dom && !c.__b) { + c.__b = dom; + oldDom = null; + } + setComponentProps(c, props, 1, context, mountAll); + dom = c.base; + if (oldDom && dom !== oldDom) { + oldDom._component = null; + recollectNodeTree(oldDom, !1); + } + } + return dom; + } + function unmountComponent(component) { + if (options.beforeUnmount) options.beforeUnmount(component); + var base = component.base; + component.__x = !0; + if (component.componentWillUnmount) component.componentWillUnmount(); + component.base = null; + var inner = component._component; + if (inner) unmountComponent(inner); else if (base) { + if (base.__preactattr_ && base.__preactattr_.ref) base.__preactattr_.ref(null); + component.__b = base; + removeNode(base); + collectComponent(component); + removeChildren(base); + } + if (component.__r) component.__r(null); + } + function Component(props, context) { + this.__d = !0; + this.context = context; + this.props = props; + this.state = this.state || {}; + } + function render(vnode, parent, merge) { + return diff(merge, vnode, {}, !1, parent, !1); + } + var options = {}; + var stack = []; + var EMPTY_CHILDREN = []; + var defer = 'function' == typeof Promise ? Promise.resolve().then.bind(Promise.resolve()) : setTimeout; + var IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i; + var items = []; + var mounts = []; + var diffLevel = 0; + var isSvgMode = !1; + var hydrating = !1; + var components = {}; + extend(Component.prototype, { + setState: function(state, callback) { + var s = this.state; + if (!this.__s) this.__s = extend({}, s); + extend(s, 'function' == typeof state ? state(s, this.props) : state); + if (callback) (this.__h = this.__h || []).push(callback); + enqueueRender(this); + }, + forceUpdate: function(callback) { + if (callback) (this.__h = this.__h || []).push(callback); + renderComponent(this, 2); + }, + render: function() {} + }); + var preact = { + h: h, + createElement: h, + cloneElement: cloneElement, + Component: Component, + render: render, + rerender: rerender, + options: options + }; + if ('undefined' != typeof module) module.exports = preact; else self.preact = preact; +}); +//# sourceMappingURL=preact.js.map diff --git a/test/SpecRunner.js b/test/SpecRunner.js index 599c93f5ba8..3d9038269ac 100644 --- a/test/SpecRunner.js +++ b/test/SpecRunner.js @@ -36,6 +36,13 @@ require.config({ "fileSystemImpl" : "filesystem/impls/appshell/AppshellFileSystem", "preferences/PreferencesImpl" : "../test/TestPreferencesImpl" }, + packages: [ + { + name: "thirdparty/preact", + location: "thirdparty/preact", + main: "preact-compat" + } + ], map: { "*": { "thirdparty/react": "react" From 99c130e0a3f497ef8a0ab5eccd3a7491cfc42ff1 Mon Sep 17 00:00:00 2001 From: Boopesh Mahendran Date: Fri, 11 Aug 2017 14:49:38 +0530 Subject: [PATCH 02/11] Change react to preact --- src/project/FileTreeView.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/project/FileTreeView.js b/src/project/FileTreeView.js index 3ea667c145e..4f46a214621 100644 --- a/src/project/FileTreeView.js +++ b/src/project/FileTreeView.js @@ -31,8 +31,8 @@ define(function (require, exports, module) { "use strict"; - var React = require("thirdparty/react"), - ReactDOM = require("thirdparty/react-dom"), + var React = require("thirdparty/preact"), + ReactDOM = require("thirdparty/preact"), Classnames = require("thirdparty/classnames"), Immutable = require("thirdparty/immutable"), _ = require("thirdparty/lodash"), From 504cdf468ebe82b209ac3018e470d16439c6daee Mon Sep 17 00:00:00 2001 From: Boopesh Mahendran Date: Fri, 11 Aug 2017 14:58:48 +0530 Subject: [PATCH 03/11] Fix problems caused by replacing react with preact --- src/project/FileTreeView.js | 18 +++++++++++++++--- src/styles/jsTreeTheme.less | 2 +- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/project/FileTreeView.js b/src/project/FileTreeView.js index 4f46a214621..21b2359b90e 100644 --- a/src/project/FileTreeView.js +++ b/src/project/FileTreeView.js @@ -219,6 +219,7 @@ define(function (require, exports, module) { var node = this.refs.name; node.setSelectionRange(0, _getName(fullname, extension).length); + node.focus(); // set focus on the rename input ViewUtils.scrollElementIntoView($("#project-files-container"), $(node), true); }, @@ -480,6 +481,11 @@ define(function (require, exports, module) { extension = LanguageManager.getCompoundFileExtension(fullname), name = _getName(fullname, extension); + // React automatically wraps content in a span element whereas preact doesn't, so do it manually + if (name) { + name = DOM.span({}, name); + } + if (extension) { extension = DOM.span({ className: "extension" @@ -601,6 +607,7 @@ define(function (require, exports, module) { var node = this.refs.name; node.setSelectionRange(0, fullname.length); + node.focus(); // set focus on the rename input ViewUtils.scrollElementIntoView($("#project-files-container"), $(node), true); }, @@ -758,11 +765,16 @@ define(function (require, exports, module) { parentPath: this.props.parentPath }); } else { + // React automatically wraps content in a span element whereas preact doesn't, so do it manually + if (this.props.name) { + var name = DOM.span({}, this.props.name); + } + // Need to flatten the arguments because getIcons returns an array var aArgs = _.flatten([{ href: "#", className: directoryClasses - }, thickness, this.getIcons(), this.props.name]); + }, thickness, this.getIcons(), name]); nameDisplay = DOM.a.apply(DOM.a, aArgs); } @@ -1035,11 +1047,11 @@ define(function (require, exports, module) { return DOM.div( null, + contents, selectionBackground, contextBackground, extensionForSelection, - extensionForContext, - contents + extensionForContext ); } })); diff --git a/src/styles/jsTreeTheme.less b/src/styles/jsTreeTheme.less index a2eae59cfb7..1dc74579e1b 100644 --- a/src/styles/jsTreeTheme.less +++ b/src/styles/jsTreeTheme.less @@ -26,7 +26,7 @@ /* (these are based on jsTree's default theme .css file, so they are not very LESS-like) */ @li-min-height: 23px; -.jstree ul, .jstree li { display:block; margin:0 0 0 0; padding:0 0 0 0; list-style-type:none; } +.jstree ul, .jstree li { display:block; margin:0 0 0 0; padding:0 0 0 0; list-style-type:none; z-index:1; } .jstree li { display:block; min-height:@li-min-height; line-height:16px; white-space:nowrap; min-width:18px; } .jstree-rtl li { margin-left:0; margin-right:18px; } .jstree > ul > li { margin-left:0; } From 6c5c5b8f335ed0223d99eb435efd03c71a333569 Mon Sep 17 00:00:00 2001 From: Boopesh Mahendran Date: Fri, 11 Aug 2017 15:43:44 +0530 Subject: [PATCH 04/11] Add Preact test utils and dependencies --- src/thirdparty/preact/events.js | 8 + src/thirdparty/preact/preact-test-utils.js | 124 ++++++ src/thirdparty/preact/simulate-event.js | 431 +++++++++++++++++++++ src/thirdparty/preact/xtend/mutable.js | 19 + 4 files changed, 582 insertions(+) create mode 100644 src/thirdparty/preact/events.js create mode 100644 src/thirdparty/preact/preact-test-utils.js create mode 100644 src/thirdparty/preact/simulate-event.js create mode 100644 src/thirdparty/preact/xtend/mutable.js diff --git a/src/thirdparty/preact/events.js b/src/thirdparty/preact/events.js new file mode 100644 index 00000000000..1cf43e73875 --- /dev/null +++ b/src/thirdparty/preact/events.js @@ -0,0 +1,8 @@ +define (function(require, exports, module){ + 'use strict'; + + module.exports = `onKeyDown onKeyPress onKeyUp onFocus onBlur onClick onContextMenu onDoubleClick onDrag onDragEnd onDragEnter onDragExit onDragLeave onDragOver onDragStart onDrop onMouseDown onMouseEnter onMouseLeave onMouseMove onMouseOut onMouseOver onMouseUp onChange onInput onSubmit onTouchCancel onTouchEnd onTouchMove onTouchStart onLoad onError onAnimationStart onAnimationEnd onAnimationIteration onTransitionEnd`.split(' ').map(item => { + const event = item.replace('on', ''); + return event.charAt(0).toLowerCase() + event.substr(1); + }); +}); diff --git a/src/thirdparty/preact/preact-test-utils.js b/src/thirdparty/preact/preact-test-utils.js new file mode 100644 index 00000000000..a2b18c8a762 --- /dev/null +++ b/src/thirdparty/preact/preact-test-utils.js @@ -0,0 +1,124 @@ +// Preact test utils version 0.1.3 +define(function(require, exports, module) { + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _preactCompat = require('./preact-compat'); + + var _preactCompat2 = _interopRequireDefault(_preactCompat); + + var _simulateEvent = require('./simulate-event'); + + var _simulateEvent2 = _interopRequireDefault(_simulateEvent); + + var _events = require('./events'); + + var _events2 = _interopRequireDefault(_events); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + //TODO + var ReactShallowRenderer = function () { + function ReactShallowRenderer() { + _classCallCheck(this, ReactShallowRenderer); + } + + ReactShallowRenderer.prototype.render = function render(node, context) {}; + + ReactShallowRenderer.prototype.getRenderOutput = function getRenderOutput() {}; + + return ReactShallowRenderer; + }(); + + var ReactTestUtils = { + renderIntoDocument: function renderIntoDocument(element) { + var div = document.createElement('div'); + return _preactCompat2['default'].render(element, div); + }, + isElement: function isElement(element) { + return _preactCompat2['default'].isValidElement(element); + }, + isElementOfType: function isElementOfType(inst, convenienceConstructor) { + return _preactCompat2['default'].isValidElement(inst) && inst.type === convenienceConstructor; + }, + isDOMComponent: function isDOMComponent(inst) { + return !!(inst && inst.nodeType === 1 && inst.tagName); + }, + isCompositeComponent: function isCompositeComponent(inst) { + if (ReactTestUtils.isDOMComponent(inst)) { + return false; + } + return inst != null && typeof inst.render === 'function' && typeof inst.setState === 'function'; + }, + isCompositeComponentWithType: function isCompositeComponentWithType(inst, type) { + if (!ReactTestUtils.isCompositeComponent(inst)) { + return false; + } + var constructor = inst.type; + return constructor === type; + }, + isCompositeComponentElement: function isCompositeComponentElement(inst) { + if (!_preactCompat2['default'].isValidElement(inst)) { + return false; + } + // We check the prototype of the type that will get mounted, not the + // instance itself. This is a future proof way of duck typing. + var prototype = inst.type.prototype; + return typeof prototype.render === 'function' && typeof prototype.setState === 'function'; + }, + findAllInRenderedTree: function findAllInRenderedTree(inst, test) { + if (!inst) { + return []; + } + var findTreeFromDOM = function findTreeFromDOM(dom, test) { + var ret = []; + var inc = dom._component; + if (inc && test(inc)) { + ret.push(inc, dom); + for (var i = 0; i < dom.childNodes.length; i++) { + var childNode = dom.childNodes[i]; + ret = ret.concat(findTreeFromDOM(childNode, test)); + } + } else if (_preactCompat2['default'].isDOMComponent(dom)) { + ret.push(dom); + } + return ret; + }; + return findTreeFromDOM(inst.base, test); + }, + mockComponent: function mockComponent(module, mockTagName) { + mockTagName = mockTagName || module.mockTagName || 'div'; + + module.prototype.render.mockImplementation(function () { + return _preactCompat2['default'].createElement(mockTagName, null, this.props.children); + }); + return this; + }, + batchedUpdates: function batchedUpdates(callback) { + callback(); + }, + createRenderer: function createRenderer() { + return new ReactShallowRenderer(); + }, + + Simulate: {} + }; + + function buildSimulate() { + _events2['default'].forEach(function (event) { + ReactTestUtils.Simulate[event] = function (node, mock) { + _simulateEvent2['default'].simulate(node, event.toLowerCase(), mock); + }; + }); + } + + buildSimulate(); + + exports['default'] = ReactTestUtils; + module.exports = exports['default']; +}); diff --git a/src/thirdparty/preact/simulate-event.js b/src/thirdparty/preact/simulate-event.js new file mode 100644 index 00000000000..1b2350c0d0f --- /dev/null +++ b/src/thirdparty/preact/simulate-event.js @@ -0,0 +1,431 @@ +// Simulate event version 1.4.0 +define(function(require, exports, module) { + var extend = require('./xtend/mutable') + + /** + * Set some default options. + * + * @type {Object} + */ + var eventOptions = { + UIEvent: function () { + return { + view: document.defaultView + } + }, + FocusEvent: function () { + return eventOptions.UIEvent.apply(this, arguments) + }, + MouseEvent: function (type) { + return { + button: 0, + bubbles: (type !== 'mouseenter' && type !== 'mouseleave'), + cancelable: (type !== 'mouseenter' && type !== 'mouseleave'), + ctrlKey: false, + altKey: false, + shiftKey: false, + metaKey: false, + clientX: 1, + clientY: 1, + screenX: 0, + screenY: 0, + view: document.defaultView, + relatedTarget: document.documentElement + } + }, + WheelEvent: function (type) { + return eventOptions.MouseEvent.apply(this, arguments) + }, + KeyboardEvent: function () { + return { + view: document.defaultView, + ctrlKey: false, + altKey: false, + shiftKey: false, + metaKey: false, + keyCode: 0 + } + } + } + + /** + * Map event names to constructor names. + * + * @type {Object} + */ + var eventTypes = { + beforeprint: 'Event', + afterprint: 'Event', + beforeunload: 'Event', + abort: 'Event', + error: 'Event', + change: 'Event', + submit: 'Event', + reset: 'Event', + cached: 'Event', + canplay: 'Event', + canplaythrough: 'Event', + chargingchange: 'Event', + chargingtimechange: 'Event', + checking: 'Event', + close: 'Event', + downloading: 'Event', + durationchange: 'Event', + emptied: 'Event', + ended: 'Event', + fullscreenchange: 'Event', + fullscreenerror: 'Event', + invalid: 'Event', + levelchange: 'Event', + loadeddata: 'Event', + loadedmetadata: 'Event', + noupdate: 'Event', + obsolete: 'Event', + offline: 'Event', + online: 'Event', + open: 'Event', + orientationchange: 'Event', + pause: 'Event', + pointerlockchange: 'Event', + pointerlockerror: 'Event', + copy: 'Event', + cut: 'Event', + paste: 'Event', + play: 'Event', + playing: 'Event', + ratechange: 'Event', + readystatechange: 'Event', + seeked: 'Event', + seeking: 'Event', + stalled: 'Event', + success: 'Event', + suspend: 'Event', + timeupdate: 'Event', + updateready: 'Event', + visibilitychange: 'Event', + volumechange: 'Event', + waiting: 'Event', + load: 'UIEvent', + unload: 'UIEvent', + resize: 'UIEvent', + scroll: 'UIEvent', + select: 'UIEvent', + drag: 'MouseEvent', + dragenter: 'MouseEvent', + dragleave: 'MouseEvent', + dragover: 'MouseEvent', + dragstart: 'MouseEvent', + dragend: 'MouseEvent', + drop: 'MouseEvent', + touchcancel: 'UIEvent', + touchend: 'UIEvent', + touchenter: 'UIEvent', + touchleave: 'UIEvent', + touchmove: 'UIEvent', + touchstart: 'UIEvent', + blur: 'UIEvent', + focus: 'UIEvent', + focusin: 'UIEvent', + focusout: 'UIEvent', + input: 'UIEvent', + show: 'MouseEvent', + click: 'MouseEvent', + dblclick: 'MouseEvent', + mouseenter: 'MouseEvent', + mouseleave: 'MouseEvent', + mousedown: 'MouseEvent', + mouseup: 'MouseEvent', + mouseover: 'MouseEvent', + mousemove: 'MouseEvent', + mouseout: 'MouseEvent', + contextmenu: 'MouseEvent', + wheel: 'WheelEvent', + message: 'MessageEvent', + storage: 'StorageEvent', + timeout: 'StorageEvent', + keydown: 'KeyboardEvent', + keypress: 'KeyboardEvent', + keyup: 'KeyboardEvent', + progress: 'ProgressEvent', + loadend: 'ProgressEvent', + loadstart: 'ProgressEvent', + popstate: 'PopStateEvent', + hashchange: 'HashChangeEvent', + transitionend: 'TransitionEvent', + compositionend: 'CompositionEvent', + compositionstart: 'CompositionEvent', + compositionupdate: 'CompositionEvent', + pagehide: 'PageTransitionEvent', + pageshow: 'PageTransitionEvent' + } + + /** + * Map the event type constructor to the initialization method. + * + * @type {Object} + */ + var eventInit = { + Event: 'initEvent', + UIEvent: 'initUIEvent', + FocusEvent: 'initUIEvent', + MouseEvent: 'initMouseEvent', + WheelEvent: 'initMouseEvent', + MessageEvent: 'initMessageEvent', + StorageEvent: 'initStorageEvent', + KeyboardEvent: 'initKeyboardEvent', + ProgressEvent: 'initEvent', + PopStateEvent: 'initEvent', + TransitionEvent: 'initEvent', + HashChangeEvent: 'initHashChangeEvent', + CompositionEvent: 'initCompositionEvent', + DeviceMotionEvent: 'initDeviceMotionEvent', + PageTransitionEvent: 'initEvent', + DeviceOrientationEvent: 'initDeviceOrientationEvent' + } + + /** + * Map the options object to initialization parameters. + * + * @type {Object} + */ + var eventParameters = { + initEvent: [], + initUIEvent: [ + 'view', + 'detail' + ], + initKeyboardEvent: [ + 'view', + 'char', + 'key', + 'location', + 'modifiersList', + 'repeat', + 'locale' + ], + initKeyEvent: [ + 'view', + 'ctrlKey', + 'altKey', + 'shiftKey', + 'metaKey', + 'keyCode', + 'charCode' + ], + initMouseEvent: [ + 'view', + 'detail', + 'screenX', + 'screenY', + 'clientX', + 'clientY', + 'ctrlKey', + 'altKey', + 'shiftKey', + 'metaKey', + 'button', + 'relatedTarget' + ], + initHashChangeEvent: [ + 'oldURL', + 'newURL' + ], + initCompositionEvent: [ + 'view', + 'data', + 'locale' + ], + initDeviceMotionEvent: [ + 'acceleration', + 'accelerationIncludingGravity', + 'rotationRate', + 'interval' + ], + initDeviceOrientationEvent: [ + 'alpha', + 'beta', + 'gamma', + 'absolute' + ], + initMessageEvent: [ + 'data', + 'origin', + 'lastEventId', + 'source' + ], + initStorageEvent: [ + 'key', + 'oldValue', + 'newValue', + 'url', + 'storageArea' + ] + } + + /** + * Map the event types to constructors. + * + * @type {Object} + */ + var eventConstructors = { + UIEvent: window.UIEvent, + FocusEvent: window.FocusEvent, + MouseEvent: window.MouseEvent, + WheelEvent: window.MouseEvent, + KeyboardEvent: window.KeyboardEvent + } + + /** + * Get attributes which must be overriden manually. + * + * @param {String} eventType + * @param {Object} options. + */ + function getOverrides (eventType, options) { + if (eventType === 'KeyboardEvent' && options) { + return { + keyCode: options.keyCode || 0, + key: options.key || 0, + which: options.which || options.keyCode || 0 + } + } + } + + /** + * Generate an event. + * + * @param {String} type + * @param {Object} options + * @return {Event} + */ + exports.generate = function (type, options) { + // Immediately throw an error when the event name does not translate. + if (!eventTypes.hasOwnProperty(type)) { + throw new SyntaxError('Unsupported event type') + } + + var eventType = eventTypes[type] + var event + var key + + // Handle parameters which must be manually overridden using + // `Object.defineProperty`. + var overrides = getOverrides(eventType, options) + + // Extend a new object with the default and passed in options. + // Existing events already have all of their defaults set. + if (!(options instanceof window.Event)) { + // Check for extra defaults to pass in. + if (eventType in eventOptions) { + options = extend({ + bubbles: true, + cancelable: true + }, eventOptions[eventType](type, options), options) + } else { + options = extend({ + bubbles: true, + cancelable: true + }, options) + } + } + + // Attempt the Event Constructors DOM API. + var Constructor = eventConstructors[eventType] || window.Event + + try { + event = new Constructor(type, options) + + // Add the override properties. + for (key in overrides) { + Object.defineProperty(event, key, { + value: overrides[key] + }) + } + + return event + } catch (e) { + // Continue. + } + + // In IE11, the Keyboard event does not allow setting the + // keyCode property, even with Object.defineProperty, + // so we have to use UIEvent. + var ua = window.navigator.userAgent.toLowerCase() + var msie = Math.max(ua.indexOf('msie'), ua.indexOf('trident')) + + if (msie >= 0 && eventType === 'KeyboardEvent') { + eventType = 'UIEvent' + } + + var initEvent = eventInit[eventType] + + // In < IE9, the `createEvent` function is not available and we have to + // resort to using `fireEvent`. + if (!document.createEvent) { + event = extend(document.createEventObject(), options) + + // Add the override properties. + for (key in overrides) { + Object.defineProperty(event, key, { + value: overrides[key] + }) + } + + return event + } + + event = extend(document.createEvent(eventType), options) + + // Handle differences between `initKeyboardEvent` and `initKeyEvent`. + if (initEvent === 'initKeyboardEvent') { + if (event[initEvent] === void 0) { + initEvent = 'initKeyEvent' + } else if (!('modifiersList' in options)) { + var mods = [] + if (options.metaKey) mods.push('Meta') + if (options.altKey) mods.push('Alt') + if (options.shiftKey) mods.push('Shift') + if (options.ctrlKey) mods.push('Control') + options['modifiersList'] = mods.join(' ') + } + } + + // Map argument names to the option values. + var args = eventParameters[initEvent].map(function (parameter) { + return options[parameter] + }) + + // Initialize the event using the built-in method. + event[initEvent].apply( + event, [type, options.bubbles, options.cancelable].concat(args) + ) + + // Add the override properties. + for (key in overrides) { + Object.defineProperty(event, key, { + value: overrides[key] + }) + } + + return event + } + + /** + * Simulate an event which is dispatched on the given element. + * + * @param {Element} element + * @param {String} type + * @param {Object} options + * @return {Boolean} + */ + exports.simulate = function (element, type, options) { + var event = exports.generate(type, options) + + // In < IE9, the `createEvent` function is not available and we have to + // resort to using `fireEvent`. + if (!document.createEvent) { + return element.fireEvent('on' + type, event) + } + return element.dispatchEvent(event) + } +}); diff --git a/src/thirdparty/preact/xtend/mutable.js b/src/thirdparty/preact/xtend/mutable.js new file mode 100644 index 00000000000..0b7106e0c0e --- /dev/null +++ b/src/thirdparty/preact/xtend/mutable.js @@ -0,0 +1,19 @@ +define(function(require, exports, module){ + module.exports = extend + + var hasOwnProperty = Object.prototype.hasOwnProperty; + + function extend(target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i] + + for (var key in source) { + if (hasOwnProperty.call(source, key)) { + target[key] = source[key] + } + } + } + + return target + } +}); From a7d197ede97abd28eebe938d9e563a8d7a49c396 Mon Sep 17 00:00:00 2001 From: Boopesh Mahendran Date: Fri, 11 Aug 2017 15:55:33 +0530 Subject: [PATCH 05/11] Change requires from react to preact in FileTreeView-test --- test/spec/FileTreeView-test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/spec/FileTreeView-test.js b/test/spec/FileTreeView-test.js index 1a41cd77751..80ff8e3eaae 100644 --- a/test/spec/FileTreeView-test.js +++ b/test/spec/FileTreeView-test.js @@ -29,10 +29,10 @@ define(function (require, exports, module) { var FileTreeView = require("project/FileTreeView"), FileTreeViewModel = require("project/FileTreeViewModel"), - React = require("thirdparty/react"), - ReactDOM = require("thirdparty/react-dom"), + React = require("thirdparty/preact"), + ReactDOM = require("thirdparty/preact"), Immutable = require("thirdparty/immutable"), - RTU = React.addons.TestUtils, + RTU = require("thirdparty/preact/preact-test-utils"), _ = require("thirdparty/lodash"); describe("FileTreeView", function () { From 9e8ec14f4376a8485802bba2832020c20c364169 Mon Sep 17 00:00:00 2001 From: Boopesh Mahendran Date: Fri, 11 Aug 2017 15:57:22 +0530 Subject: [PATCH 06/11] Fix problems caused by replacing react test utils with preact test utils --- test/spec/FileTreeView-test.js | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/test/spec/FileTreeView-test.js b/test/spec/FileTreeView-test.js index 80ff8e3eaae..996bf26fc79 100644 --- a/test/spec/FileTreeView-test.js +++ b/test/spec/FileTreeView-test.js @@ -35,6 +35,25 @@ define(function (require, exports, module) { RTU = require("thirdparty/preact/preact-test-utils"), _ = require("thirdparty/lodash"); + // Preact Test Utils doesn't have findRenderedDOMComponentWithTag method + // So create it + RTU.findRenderedDOMComponentWithTag = function(root, tagName) { + var nodes = []; + if (root.base.tagName.toUpperCase() === tagName.toUpperCase()){ + nodes.push(root.base); + } else { + for (var i = 0; i < root.base.childNodes.length; ++i) { + if (root.base.childNodes[i].tagName === tagName.toUpperCase()) { + nodes.push(root.base.childNodes[i]); + } + } + } + if (nodes.length !== 1) { + throw new Error('Did not find exactly one match for tag:' + tagName); + } + return nodes[0]; + }; + describe("FileTreeView", function () { describe("_fileNode", function () { @@ -112,7 +131,7 @@ define(function (require, exports, module) { parentPath: "/foo/" })); var node = ReactDOM.findDOMNode(rendered); - React.addons.TestUtils.Simulate.mouseDown(node, { + RTU.Simulate.mouseDown(node, { button: 2 }); expect(actions.setContext).toHaveBeenCalledWith("/foo/afile.js"); @@ -128,7 +147,7 @@ define(function (require, exports, module) { platform: "mac" })); var node = ReactDOM.findDOMNode(rendered); - React.addons.TestUtils.Simulate.mouseDown(node, { + RTU.Simulate.mouseDown(node, { button: 0, ctrlKey: true }); @@ -145,7 +164,7 @@ define(function (require, exports, module) { platform: "win" })); var node = ReactDOM.findDOMNode(rendered); - React.addons.TestUtils.Simulate.mouseDown(node, { + RTU.Simulate.mouseDown(node, { button: 0, ctrlKey: true }); From 398abb2721f7fed464c0b8963e96898dac0eac26 Mon Sep 17 00:00:00 2001 From: Boopesh Mahendran Date: Fri, 11 Aug 2017 16:07:31 +0530 Subject: [PATCH 07/11] Remove react --- src/main.js | 4 +- src/thirdparty/react-dom.js | 42 - src/thirdparty/react.js | 20771 ---------------------------------- test/SpecRunner.js | 8 +- 4 files changed, 2 insertions(+), 20823 deletions(-) delete mode 100644 src/thirdparty/react-dom.js delete mode 100644 src/thirdparty/react.js diff --git a/src/main.js b/src/main.js index a732b37d0fd..c31e77068ad 100644 --- a/src/main.js +++ b/src/main.js @@ -29,7 +29,6 @@ require.config({ paths: { "text" : "thirdparty/text/text", "i18n" : "thirdparty/i18n/i18n", - "react" : "thirdparty/react", // The file system implementation. Change this value to use different // implementations (e.g. cloud-based storage). @@ -44,8 +43,7 @@ require.config({ ], map: { "*": { - "thirdparty/CodeMirror2": "thirdparty/CodeMirror", - "thirdparty/react": "react" + "thirdparty/CodeMirror2": "thirdparty/CodeMirror" } } }); diff --git a/src/thirdparty/react-dom.js b/src/thirdparty/react-dom.js deleted file mode 100644 index 117e1393c64..00000000000 --- a/src/thirdparty/react-dom.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * ReactDOM v0.14.7 - * - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ -// 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); - - //