From 4bfdaa792908334bd314e20aa82bb164690f2fc0 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sat, 6 Sep 2014 22:51:52 -0700 Subject: [PATCH 1/2] Move htmltojsx.js to React-Magic project. The HTMLtoJSX file is really a part of the React-Magic project, and should belong there rather than here: - It doesn't really follow the release cycle of React at all - It is totally standalone with no dependency on React - The only usage of this script is in React-Magic and on the HTML to JSX page on the React website (http://facebook.github.io/react/html-jsx.html) I've hotlinked it from the Github site for that project since this was the easiest way to pull it in. Due to the fact that Github Pages lacks redirects (among other basic web hosting functionality), I've replaced the `html-jsx-lib.js` file with a comment explaining where the file has moved to, just in case anyone was hotlinking it from the React site. --- _js/html-jsx-lib.js | 483 +------------------------------------------- html-jsx.md | 2 +- 2 files changed, 2 insertions(+), 483 deletions(-) diff --git a/_js/html-jsx-lib.js b/_js/html-jsx-lib.js index 02b36bcf..1ce79ecb 100644 --- a/_js/html-jsx-lib.js +++ b/_js/html-jsx-lib.js @@ -1,482 +1 @@ -/** - * Copyright 2013-2014 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * This is a very simple HTML to JSX converter. It turns out that browsers - * have good HTML parsers (who would have thought?) so we utilise this by - * inserting the HTML into a temporary DOM node, and then do a breadth-first - * traversal of the resulting DOM tree. - */ -;(function(global) { - 'use strict'; - - // https://developer.mozilla.org/en-US/docs/Web/API/Node.nodeType - var NODE_TYPE = { - ELEMENT: 1, - TEXT: 3, - COMMENT: 8 - }; - var ATTRIBUTE_MAPPING = { - 'for': 'htmlFor', - 'class': 'className' - }; - - /** - * Repeats a string a certain number of times. - * Also: the future is bright and consists of native string repetition: - * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat - * - * @param {string} string String to repeat - * @param {number} times Number of times to repeat string. Integer. - * @see http://jsperf.com/string-repeater/2 - */ - function repeatString(string, times) { - if (times === 1) { - return string; - } - if (times < 0) { throw new Error(); } - var repeated = ''; - while (times) { - if (times & 1) { - repeated += string; - } - if (times >>= 1) { - string += string; - } - } - return repeated; - } - - /** - * Determine if the string ends with the specified substring. - * - * @param {string} haystack String to search in - * @param {string} needle String to search for - * @return {boolean} - */ - function endsWith(haystack, needle) { - return haystack.slice(-needle.length) === needle; - } - - /** - * Trim the specified substring off the string. If the string does not end - * with the specified substring, this is a no-op. - * - * @param {string} haystack String to search in - * @param {string} needle String to search for - * @return {string} - */ - function trimEnd(haystack, needle) { - return endsWith(haystack, needle) - ? haystack.slice(0, -needle.length) - : haystack; - } - - /** - * Convert a hyphenated string to camelCase. - */ - function hyphenToCamelCase(string) { - return string.replace(/-(.)/g, function(match, chr) { - return chr.toUpperCase(); - }); - } - - /** - * Determines if the specified string consists entirely of whitespace. - */ - function isEmpty(string) { - return !/[^\s]/.test(string); - } - - /** - * Determines if the specified string consists entirely of numeric characters. - */ - function isNumeric(input) { - return input !== undefined - && input !== null - && (typeof input === 'number' || parseInt(input, 10) == input); - } - - var HTMLtoJSX = function(config) { - this.config = config || {}; - - if (this.config.createClass === undefined) { - this.config.createClass = true; - } - if (!this.config.indent) { - this.config.indent = ' '; - } - if (!this.config.outputClassName) { - this.config.outputClassName = 'NewComponent'; - } - }; - HTMLtoJSX.prototype = { - /** - * Reset the internal state of the converter - */ - reset: function() { - this.output = ''; - this.level = 0; - }, - /** - * Main entry point to the converter. Given the specified HTML, returns a - * JSX object representing it. - * @param {string} html HTML to convert - * @return {string} JSX - */ - convert: function(html) { - this.reset(); - - // It turns out browsers have good HTML parsers (imagine that). - // Let's take advantage of it. - var containerEl = document.createElement('div'); - containerEl.innerHTML = '\n' + this._cleanInput(html) + '\n'; - - if (this.config.createClass) { - if (this.config.outputClassName) { - this.output = 'var ' + this.config.outputClassName + ' = React.createClass({\n'; - } else { - this.output = 'React.createClass({\n'; - } - this.output += this.config.indent + 'render: function() {' + "\n"; - this.output += this.config.indent + this.config.indent + 'return (\n'; - } - - if (this._onlyOneTopLevel(containerEl)) { - // Only one top-level element, the component can return it directly - // No need to actually visit the container element - this._traverse(containerEl); - } else { - // More than one top-level element, need to wrap the whole thing in a - // container. - this.output += this.config.indent + this.config.indent + this.config.indent; - this.level++; - this._visit(containerEl); - } - this.output = this.output.trim() + '\n'; - if (this.config.createClass) { - this.output += this.config.indent + this.config.indent + ');\n'; - this.output += this.config.indent + '}\n'; - this.output += '});'; - } - return this.output; - }, - - /** - * Cleans up the specified HTML so it's in a format acceptable for - * converting. - * - * @param {string} html HTML to clean - * @return {string} Cleaned HTML - */ - _cleanInput: function(html) { - // Remove unnecessary whitespace - html = html.trim(); - // Ugly method to strip script tags. They can wreak havoc on the DOM nodes - // so let's not even put them in the DOM. - html = html.replace(//g, ''); - return html; - }, - - /** - * Determines if there's only one top-level node in the DOM tree. That is, - * all the HTML is wrapped by a single HTML tag. - * - * @param {DOMElement} containerEl Container element - * @return {boolean} - */ - _onlyOneTopLevel: function(containerEl) { - // Only a single child element - if ( - containerEl.childNodes.length === 1 - && containerEl.childNodes[0].nodeType === NODE_TYPE.ELEMENT - ) { - return true; - } - // Only one element, and all other children are whitespace - var foundElement = false; - for (var i = 0, count = containerEl.childNodes.length; i < count; i++) { - var child = containerEl.childNodes[i]; - if (child.nodeType === NODE_TYPE.ELEMENT) { - if (foundElement) { - // Encountered an element after already encountering another one - // Therefore, more than one element at root level - return false; - } else { - foundElement = true; - } - } else if (child.nodeType === NODE_TYPE.TEXT && !isEmpty(child.textContent)) { - // Contains text content - return false; - } - } - return true; - }, - - /** - * Gets a newline followed by the correct indentation for the current - * nesting level - * - * @return {string} - */ - _getIndentedNewline: function() { - return '\n' + repeatString(this.config.indent, this.level + 2); - }, - - /** - * Handles processing the specified node - * - * @param {Node} node - */ - _visit: function(node) { - this._beginVisit(node); - this._traverse(node); - this._endVisit(node); - }, - - /** - * Traverses all the children of the specified node - * - * @param {Node} node - */ - _traverse: function(node) { - this.level++; - for (var i = 0, count = node.childNodes.length; i < count; i++) { - this._visit(node.childNodes[i]); - } - this.level--; - }, - - /** - * Handle pre-visit behaviour for the specified node. - * - * @param {Node} node - */ - _beginVisit: function(node) { - switch (node.nodeType) { - case NODE_TYPE.ELEMENT: - this._beginVisitElement(node); - break; - - case NODE_TYPE.TEXT: - this._visitText(node); - break; - - case NODE_TYPE.COMMENT: - this._visitComment(node); - break; - - default: - console.warn('Unrecognised node type: ' + node.nodeType); - } - }, - - /** - * Handles post-visit behaviour for the specified node. - * - * @param {Node} node - */ - _endVisit: function(node) { - switch (node.nodeType) { - case NODE_TYPE.ELEMENT: - this._endVisitElement(node); - break; - // No ending tags required for these types - case NODE_TYPE.TEXT: - case NODE_TYPE.COMMENT: - break; - } - }, - - /** - * Handles pre-visit behaviour for the specified element node - * - * @param {DOMElement} node - */ - _beginVisitElement: function(node) { - var tagName = node.tagName.toLowerCase(); - var attributes = []; - for (var i = 0, count = node.attributes.length; i < count; i++) { - attributes.push(this._getElementAttribute(node, node.attributes[i])); - } - - this.output += '<' + tagName; - if (attributes.length > 0) { - this.output += ' ' + attributes.join(' '); - } - if (node.firstChild) { - this.output += '>'; - } - }, - - /** - * Handles post-visit behaviour for the specified element node - * - * @param {Node} node - */ - _endVisitElement: function(node) { - // De-indent a bit - // TODO: It's inefficient to do it this way :/ - this.output = trimEnd(this.output, this.config.indent); - if (node.firstChild) { - this.output += ''; - } else { - this.output += ' />'; - } - }, - - /** - * Handles processing of the specified text node - * - * @param {TextNode} node - */ - _visitText: function(node) { - var text = node.textContent; - // If there's a newline in the text, adjust the indent level - if (text.indexOf('\n') > -1) { - text = node.textContent.replace(/\n\s*/g, this._getIndentedNewline()); - } - this.output += text; - }, - - /** - * Handles processing of the specified text node - * - * @param {Text} node - */ - _visitComment: function(node) { - // Do not render the comment - // Since we remove comments, we also need to remove the next line break so we - // don't end up with extra whitespace after every comment - //if (node.nextSibling && node.nextSibling.nodeType === NODE_TYPE.TEXT) { - // node.nextSibling.textContent = node.nextSibling.textContent.replace(/\n\s*/, ''); - //} - this.output += '{/*' + node.textContent.replace('*/', '* /') + '*/}'; - }, - - /** - * Gets a JSX formatted version of the specified attribute from the node - * - * @param {DOMElement} node - * @param {object} attribute - * @return {string} - */ - _getElementAttribute: function(node, attribute) { - switch (attribute.name) { - case 'style': - return this._getStyleAttribute(attribute.value); - default: - var name = ATTRIBUTE_MAPPING[attribute.name] || attribute.name; - var result = name + '='; - // Numeric values should be output as {123} not "123" - if (isNumeric(attribute.value)) { - result += '{' + attribute.value + '}'; - } else { - result += '"' + attribute.value.replace('"', '"') + '"'; - } - return result; - } - }, - - /** - * Gets a JSX formatted version of the specified element styles - * - * @param {string} styles - * @return {string} - */ - _getStyleAttribute: function(styles) { - var jsxStyles = new StyleParser(styles).toJSXString(); - return 'style={{' + jsxStyles + '}}'; - } - }; - - /** - * Handles parsing of inline styles - * - * @param {string} rawStyle Raw style attribute - * @constructor - */ - var StyleParser = function(rawStyle) { - this.parse(rawStyle); - }; - StyleParser.prototype = { - /** - * Parse the specified inline style attribute value - * @param {string} rawStyle Raw style attribute - */ - parse: function(rawStyle) { - this.styles = {}; - rawStyle.split(';').forEach(function(style) { - style = style.trim(); - var firstColon = style.indexOf(':'); - var key = style.substr(0, firstColon); - var value = style.substr(firstColon + 1).trim(); - if (key !== '') { - this.styles[key] = value; - } - }, this); - }, - - /** - * Convert the style information represented by this parser into a JSX - * string - * - * @return {string} - */ - toJSXString: function() { - var output = []; - for (var key in this.styles) { - if (!this.styles.hasOwnProperty(key)) { - continue; - } - output.push(this.toJSXKey(key) + ': ' + this.toJSXValue(this.styles[key])); - } - return output.join(', '); - }, - - /** - * Convert the CSS style key to a JSX style key - * - * @param {string} key CSS style key - * @return {string} JSX style key - */ - toJSXKey: function(key) { - return hyphenToCamelCase(key); - }, - - /** - * Convert the CSS style value to a JSX style value - * - * @param {string} value CSS style value - * @return {string} JSX style value - */ - toJSXValue: function(value) { - if (isNumeric(value)) { - // If numeric, no quotes - return value; - } else if (endsWith(value, 'px')) { - // "500px" -> 500 - return trimEnd(value, 'px'); - } else { - // Proably a string, wrap it in quotes - return '\'' + value.replace(/'/g, '"') + '\''; - } - } - }; - - // Expose public API - global.HTMLtoJSX = HTMLtoJSX; -}(window)); \ No newline at end of file +// This file has moved to http://reactjs.github.io/react-magic/htmltojsx.min.js diff --git a/html-jsx.md b/html-jsx.md index b013b545..0d16c5a5 100644 --- a/html-jsx.md +++ b/html-jsx.md @@ -6,6 +6,6 @@ id: html-jsx

HTML to JSX Compiler

- +
From 1d0fc1188b6481e7792d935aeb7da6ff2ca16b7e Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 7 Sep 2014 15:20:42 -0700 Subject: [PATCH 2/2] `console.warn` when html-jsx-lib.js is loaded --- _js/html-jsx-lib.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/_js/html-jsx-lib.js b/_js/html-jsx-lib.js index 1ce79ecb..2e94a4db 100644 --- a/_js/html-jsx-lib.js +++ b/_js/html-jsx-lib.js @@ -1 +1,7 @@ -// This file has moved to http://reactjs.github.io/react-magic/htmltojsx.min.js +// Ideally it would be nice to just redirect, but Github Pages is very basic and +// lacks that functionality. +console.warn( + 'html-jsx-lib.js has moved to http://reactjs.github.io/react-magic/' + + 'htmltojsx.min.js. If using React-Magic, you are no longer required to ' + + 'link to this file. Please delete its