jam-cloud/web/app/assets/javascripts/react_ujs.js

234 lines
6.7 KiB
JavaScript

// ES5-compatible local override for react-rails UJS.
// This shadows the gem asset so legacy Qt5WebKit can parse the page.
(function(root) {
"use strict";
var React = root.React;
var ReactDOM = root.ReactDOM;
var ReactDOMServer = root.ReactDOMServer;
if (!React || !ReactDOM) {
return;
}
var ReactRailsUJS = {
CLASS_NAME_ATTR: "data-react-class",
PROPS_ATTR: "data-react-props",
RENDER_ATTR: "data-hydrate",
CACHE_ID_ATTR: "data-react-cache-id",
TURBOLINKS_PERMANENT_ATTR: "data-turbolinks-permanent",
jQuery: (typeof root.jQuery !== "undefined" && root.jQuery) ? root.jQuery : null,
components: {},
roots: [],
findDOMNodes: function(searchSelector) {
var selector;
var parent;
var attr = this.CLASS_NAME_ATTR;
switch (typeof searchSelector) {
case "undefined":
selector = "[" + attr + "]";
parent = document;
break;
case "object":
selector = "[" + attr + "]";
parent = searchSelector;
break;
case "string":
selector = searchSelector + "[" + attr + "], " + searchSelector + " [" + attr + "]";
parent = document;
break;
default:
selector = "[" + attr + "]";
parent = document;
}
if (this.jQuery) {
return this.jQuery(selector, parent);
}
return parent.querySelectorAll(selector);
},
constructorFromGlobal: function(className) {
var topLevel = (typeof window === "undefined") ? this : window;
var constructor = topLevel[className];
if (!constructor) {
constructor = eval(className); // legacy behavior from react-rails
}
if (constructor && constructor["default"]) {
constructor = constructor["default"];
}
return constructor;
},
getConstructor: function(className) {
return this.constructorFromGlobal(className);
},
useContext: function() {
return this;
},
useContexts: function() {
return this;
},
serverRender: function(renderFunction, className, props) {
if (!ReactDOMServer || typeof ReactDOMServer[renderFunction] !== "function") {
throw new Error("ReactDOMServer." + renderFunction + " is not available");
}
var Constructor = this.getConstructor(className);
var element = React.createElement(Constructor, props);
return ReactDOMServer[renderFunction](element);
},
findRoot: function(node) {
var i;
for (i = 0; i < this.roots.length; i += 1) {
if (this.roots[i].node === node) {
return this.roots[i].root;
}
}
return null;
},
findOrCreateRoot: function(node) {
var existing = this.findRoot(node);
var rootRecord;
if (existing) {
return existing;
}
rootRecord = {
render: function(component) {
if (typeof ReactDOM.render === "function") {
return ReactDOM.render(component, node);
}
throw new Error("ReactDOM.render is not available");
},
unmount: function() {
if (typeof ReactDOM.unmountComponentAtNode === "function") {
return ReactDOM.unmountComponentAtNode(node);
}
return false;
}
};
this.roots.push({ node: node, root: rootRecord });
return rootRecord;
},
unmountRoot: function(node) {
var rootObj = this.findRoot(node);
var i;
if (rootObj && typeof rootObj.unmount === "function") {
rootObj.unmount();
}
for (i = this.roots.length - 1; i >= 0; i -= 1) {
if (this.roots[i].node === node) {
this.roots.splice(i, 1);
}
}
},
mountComponents: function(searchSelector) {
var nodes = this.findDOMNodes(searchSelector);
var i;
for (i = 0; i < nodes.length; i += 1) {
var node = nodes[i];
var className = node.getAttribute(this.CLASS_NAME_ATTR);
var Constructor = this.getConstructor(className);
var propsJson = node.getAttribute(this.PROPS_ATTR);
var props = propsJson && JSON.parse(propsJson);
var shouldHydrate = node.getAttribute(this.RENDER_ATTR);
var cacheId = node.getAttribute(this.CACHE_ID_ATTR);
var isPermanent = node.hasAttribute(this.TURBOLINKS_PERMANENT_ATTR);
var element;
if (!Constructor) {
throw new Error("Cannot find component: '" + className + "'");
}
element = this.components[cacheId];
if (typeof element === "undefined") {
element = React.createElement(Constructor, props);
if (isPermanent) {
this.components[cacheId] = element;
}
}
if (shouldHydrate && typeof ReactDOM.hydrate === "function") {
ReactDOM.hydrate(element, node);
} else {
this.findOrCreateRoot(node).render(element);
}
}
},
unmountComponents: function(searchSelector) {
var nodes = this.findDOMNodes(searchSelector);
var i;
for (i = 0; i < nodes.length; i += 1) {
this.unmountRoot(nodes[i]);
}
},
handleMount: function(e) {
var target;
if (e && e.target) {
target = e.target;
}
this.mountComponents(target);
},
handleUnmount: function(e) {
var target;
if (e && e.target) {
target = e.target;
}
this.unmountComponents(target);
},
_bind: function(eventName, handler) {
if (document.addEventListener) {
document.addEventListener(eventName, handler, false);
} else if (document.attachEvent) {
document.attachEvent("on" + eventName, handler);
}
},
detectEvents: function() {
var self = this;
var mountHandler = function(e) { self.handleMount(e); };
var unmountHandler = function(e) { self.handleUnmount(e); };
this._bind("DOMContentLoaded", mountHandler);
if (root.addEventListener) {
root.addEventListener("load", mountHandler, false);
} else if (root.attachEvent) {
root.attachEvent("onload", mountHandler);
}
// Legacy Turbolinks / pjax hooks (best-effort)
if (document.addEventListener) {
document.addEventListener("turbolinks:load", mountHandler, false);
document.addEventListener("page:change", mountHandler, false);
document.addEventListener("page:receive", unmountHandler, false);
document.addEventListener("pjax:end", mountHandler, false);
document.addEventListener("pjax:beforeReplace", unmountHandler, false);
}
if (this.jQuery) {
this.jQuery(mountHandler);
}
}
};
ReactRailsUJS.detectEvents();
root.ReactRailsUJS = ReactRailsUJS;
})(this);