Question:
The last two assertions in this new test don't pass because of the duck-typing check in . Not quite sure what you had in mind here. Not every object whose is a component class is a valid descriptor… right?
This is an intermediate step. Currently the duck typing is flawed regardless. Descriptors will probably get an inheritance chain so that instanceof ReactDescriptor works as a safer check. Only descriptors should pass. We could probably check for props too. It is expected that component classes themselves fail the test. Therefore it should also be renamed. On Feb 24, 2014, at 7:42 AM, Ben Alpert wrote:
Right -- with the current code, returns true when passed a component class, which is wrong.
That's right. We should add that unit test. Out of curiosity, how did you find this? On Feb 24, 2014, at 1:51 PM, Ben Alpert wrote:
I was adding a warning for passing a component class to renderComponent because of this Stack Overflow question:
I think this is fixed. Not sure if we have a solid unit test to cover this.

<ins> /** * 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. * * @jsx React.DOM * @emails react-core */ "use strict"; var React; var ReactDescriptor; describe('ReactDescriptor', function() { beforeEach(function() { React = require('React'); ReactDescriptor = require('ReactDescriptor'); }); it('should identify valid descriptors correctly', function() { var Component = React.createClass({ render: function() { return <div />; } }); expect(ReactDescriptor.isValidDescriptor(<div />)).toEqual(true); expect(ReactDescriptor.isValidDescriptor(<Component />)).toEqual(true); expect(ReactDescriptor.isValidDescriptor(null)).toEqual(false); expect(ReactDescriptor.isValidDescriptor(true)).toEqual(false); expect(ReactDescriptor.isValidDescriptor({})).toEqual(false); expect(ReactDescriptor.isValidDescriptor("string")).toEqual(false); expect(ReactDescriptor.isValidDescriptor(React.DOM.div)).toEqual(false); expect(ReactDescriptor.isValidDescriptor(Component)).toEqual(false); }); }); </ins>


Question:
React adds a scroll listener that tracks and caches the scroll position (I assume for some undocumented internal purpose). Additionally, the scroll listener forces a synchronous layout (see screenshot). This seems wasteful, especially considering that the values tracked are never exposed in the API. What is the scroll listener used for? Can it be removed? !
ViewportMetrics is used to normalize pageX and pageY on mouse events: . Perhaps ironically, it was created to prevent synchronous layouts during mouse event handlers.
I wish these values (as well as the event) were somehow exposed.
:+1:
BTW you can obviously have this if you use a Webpack-like bundler:
Well, is only used for IE8 and possibly other very old browsers (), so we should be able to just disable it for all other browsers and everyone is happy right? (Could still be optimized slightly for IE8... if anyone cares) Also, That makes no sense at all to me, does not take an argument, yet we get the scroll position and provide it as an argument. Instead, gets the scroll position internally and uses that? So we call twice, but only use the result once (also, this should be broken for events inside iframes, for IE8). Also2, I'm pretty sure any browsers that supports touch, also supports . cc
I agree that we shouldn't be tracking it in other browsers if we don't need it. I don't know if there's a way to predict whether we'll have .pageX and .pageY before mouse events actually come in.
:)
Another idea being just let resize/scroll set a flag, when a mouse event is created, if the flag is set, update viewport metrics. That way we only update it when needed, but could potentially cause a reflow if there are other events before it (very unlikely though I imagine).
This issue causes React based longer lists unusable on mobile.
Is there any progress on this issue? I’m not sure if this is the root-cause for pages rendered with react scrolling extremely slow with firefox mobile on android (e.g. this react website ).
Is that your site? If so, you can try commenting out the logic in refreshScrollValues to see if it makes a difference. If it does, I'm happy to prioritize and try to get it in.
No, that is not my site, it is an example react & fluxible page (see: ), but we have exactly the same issues with scrolling on firefox mobile. I’ve applied the changes in manually and it doesn’t solves the problem, so this issue might be unrelated to my problem. I will investigate further. Thanks for considering to prioritize this. Much appreciated.

*    React Core     .  General Purpose Event Plugin System */ <ins> var hasEventPageXY; </ins> var alreadyListeningTo = {}; var isMonitoringScrollValue = false; var reactTopListenersCounter = 0;


Question:
React adds a scroll listener that tracks and caches the scroll position (I assume for some undocumented internal purpose). Additionally, the scroll listener forces a synchronous layout (see screenshot). This seems wasteful, especially considering that the values tracked are never exposed in the API. What is the scroll listener used for? Can it be removed? !
ViewportMetrics is used to normalize pageX and pageY on mouse events: . Perhaps ironically, it was created to prevent synchronous layouts during mouse event handlers.
I wish these values (as well as the event) were somehow exposed.
:+1:
BTW you can obviously have this if you use a Webpack-like bundler:
Well, is only used for IE8 and possibly other very old browsers (), so we should be able to just disable it for all other browsers and everyone is happy right? (Could still be optimized slightly for IE8... if anyone cares) Also, That makes no sense at all to me, does not take an argument, yet we get the scroll position and provide it as an argument. Instead, gets the scroll position internally and uses that? So we call twice, but only use the result once (also, this should be broken for events inside iframes, for IE8). Also2, I'm pretty sure any browsers that supports touch, also supports . cc
I agree that we shouldn't be tracking it in other browsers if we don't need it. I don't know if there's a way to predict whether we'll have .pageX and .pageY before mouse events actually come in.
:)
Another idea being just let resize/scroll set a flag, when a mouse event is created, if the flag is set, update viewport metrics. That way we only update it when needed, but could potentially cause a reflow if there are other events before it (very unlikely though I imagine).
This issue causes React based longer lists unusable on mobile.
Is there any progress on this issue? I’m not sure if this is the root-cause for pages rendered with react scrolling extremely slow with firefox mobile on android (e.g. this react website ).
Is that your site? If so, you can try commenting out the logic in refreshScrollValues to see if it makes a difference. If it does, I'm happy to prioritize and try to get it in.
No, that is not my site, it is an example react & fluxible page (see: ), but we have exactly the same issues with scrolling on firefox mobile. I’ve applied the changes in manually and it doesn’t solves the problem, so this issue might be unrelated to my problem. I will investigate further. Thanks for considering to prioritize this. Much appreciated.

* Listens to window scroll and resize events. We cache scroll values so that * application code can access them without triggering reflows. * <ins> * ViewportMetrics is only used by SyntheticMouse/TouchEvent and only when * pageX/pageY isn't supported (legacy browsers). * </ins> * NOTE: Scroll events do not bubble. * * @see http://www.quirksmode.org/dom/events/scroll.html */ <del> ensureScrollValueMonitoring: function() { if (!isMonitoringScrollValue) { </del> <ins> ensureScrollValueMonitoring: function(){ if (hasEventPageXY === undefined) { hasEventPageXY = document.createEvent && 'pageX' in document.createEvent('MouseEvent'); } if (!hasEventPageXY && !isMonitoringScrollValue) { </ins> var refresh = ViewportMetrics.refreshScrollValues; ReactBrowserEventEmitter.ReactEventListener.monitorScrollValue(refresh); isMonitoringScrollValue = true;


Question:
I am working on a project that has strict accessibility requirements. The table I am putting some data in has to implement headers and ids attributes to meet accessibility. See W3 techniques here Unfortunately the "headers" attribute is being stripped out because it's not supported by React (it's not on the supported attributes list). Can you please add support for the missing standard attribute called "headers"?
+1
As a workaround I believe on a you can grab the DOMNode with a ref and add what you need.

form: MUST_USE_ATTRIBUTE, formNoValidate: HAS_BOOLEAN_VALUE, frameBorder: MUST_USE_ATTRIBUTE, <ins> headers: null, </ins> height: MUST_USE_ATTRIBUTE, hidden: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE, href: null,


Question: If we claim to support IE8, and users are testing their code locally in IE8, they might get thrown into compatibility mode by default. This will cause their webapp to blow up, and they will be confused because IE8 is a supported browser. For background, check out this thread: Users can get out of compatibility mode by adding this tag: Since the current failure is cryptic, and it's easy to detect when IE8 is in compatibility mode (), we should probably add a warning to give a hint to the user that even though they're using IE8, they're actually in compatibility mode (effectively IE7) and should add the meta tag or use one of the other various workarounds.

var assign = require('Object.assign'); var findDOMNode = require('findDOMNode'); var onlyChild = require('onlyChild'); <ins> var warning = require('warning'); </ins> ReactDefaultInjection.inject();


Question: If we claim to support IE8, and users are testing their code locally in IE8, they might get thrown into compatibility mode by default. This will cause their webapp to blow up, and they will be confused because IE8 is a supported browser. For background, check out this thread: Users can get out of compatibility mode by adding this tag: Since the current failure is cryptic, and it's easy to detect when IE8 is in compatibility mode (), we should probably add a warning to give a hint to the user that even though they're using IE8, they're actually in compatibility mode (effectively IE7) and should add the meta tag or use one of the other various workarounds.

} } <ins> // If we're in IE8, check to see if we are in combatibility mode and provide // information on preventing compatibility mode var ieCompatibilityMode = document.documentMode && document.documentMode < 8; warning( !ieCompatibilityMode, 'Internet Explorer is running in compatibility mode, please add the following ' + 'tag to your HTML to prevent this from happening: ' + '<meta http-equiv="X-UA-Compatible" content="IE=edge" />' ); </ins> var expectedFeatures = [ // shims Array.isArray,


Question:
Since copy+pasting our code makes it too easy for people to XSS themselves, lets just add a comment in there about it.
PR at - let me know if that's what you had in mind!
My intuition is that it makes more sense to fix the example (call a sanitization library) or to change the example completely (demonstrate something else that is safer). It looks really bad to have a security warning on the homepage of the react site. Makes it seem like our framework encourages unsafe operations, rather than a safe-by-default way of doing things.
Should switch the demo to use a different library like markdown-js that escapes everything and doesn't support HTML:
markdown-js looks good. First attempt: ,output Code licensed if someone wants to turn this into a coherent example; I've signed the CLA.
That's a good suggestion too :) - what are your thoughts? I'm happy to change the example to use like people have suggested above. nice usage example :+1:
Uh yea, that sounds fine to me. This behavior is definitely non-standard markdown but it's an example so not a big deal. We should probably do the same for the tutorial while we're here. And it's on cdnjs so that's all pretty easy:
Just going to use marked instead, which I'm already familiar with and has an option to sanitize input.

var MARKDOWN_COMPONENT = ` <del> var converter = new Showdown.converter(); </del> var MarkdownEditor = React.createClass({ getInitialState: function() { return {value: 'Type some *markdown* here!'};


Question:
Since copy+pasting our code makes it too easy for people to XSS themselves, lets just add a comment in there about it.
PR at - let me know if that's what you had in mind!
My intuition is that it makes more sense to fix the example (call a sanitization library) or to change the example completely (demonstrate something else that is safer). It looks really bad to have a security warning on the homepage of the react site. Makes it seem like our framework encourages unsafe operations, rather than a safe-by-default way of doing things.
Should switch the demo to use a different library like markdown-js that escapes everything and doesn't support HTML:
markdown-js looks good. First attempt: ,output Code licensed if someone wants to turn this into a coherent example; I've signed the CLA.
That's a good suggestion too :) - what are your thoughts? I'm happy to change the example to use like people have suggested above. nice usage example :+1:
Uh yea, that sounds fine to me. This behavior is definitely non-standard markdown but it's an example so not a big deal. We should probably do the same for the tutorial while we're here. And it's on cdnjs so that's all pretty easy:
Just going to use marked instead, which I'm already familiar with and has an option to sanitize input.

<div className="content" dangerouslySetInnerHTML={{  __html: converter.makeHtml(this.state.value)   __html: marked(this.state.value, {sanitize: true})  }} />


Question:
Since copy+pasting our code makes it too easy for people to XSS themselves, lets just add a comment in there about it.
PR at - let me know if that's what you had in mind!
My intuition is that it makes more sense to fix the example (call a sanitization library) or to change the example completely (demonstrate something else that is safer). It looks really bad to have a security warning on the homepage of the react site. Makes it seem like our framework encourages unsafe operations, rather than a safe-by-default way of doing things.
Should switch the demo to use a different library like markdown-js that escapes everything and doesn't support HTML:
markdown-js looks good. First attempt: ,output Code licensed if someone wants to turn this into a coherent example; I've signed the CLA.
That's a good suggestion too :) - what are your thoughts? I'm happy to change the example to use like people have suggested above. nice usage example :+1:
Uh yea, that sounds fine to me. This behavior is definitely non-standard markdown but it's an example so not a big deal. We should probably do the same for the tutorial while we're here. And it's on cdnjs so that's all pretty easy:
Just going to use marked instead, which I'm already familiar with and has an option to sanitize input.

<script type="text/javascript" src="/react/js/react.js"></script> <script type="text/javascript" src="/react/js/JSXTransformer.js"></script> <script type="text/javascript" src="/react/js/live_editor.js"></script> <del> <script type="text/javascript" src="/react/js/showdown.js"></script> </del> </head> <body>


Question:
Since copy+pasting our code makes it too easy for people to XSS themselves, lets just add a comment in there about it.
PR at - let me know if that's what you had in mind!
My intuition is that it makes more sense to fix the example (call a sanitization library) or to change the example completely (demonstrate something else that is safer). It looks really bad to have a security warning on the homepage of the react site. Makes it seem like our framework encourages unsafe operations, rather than a safe-by-default way of doing things.
Should switch the demo to use a different library like markdown-js that escapes everything and doesn't support HTML:
markdown-js looks good. First attempt: ,output Code licensed if someone wants to turn this into a coherent example; I've signed the CLA.
That's a good suggestion too :) - what are your thoughts? I'm happy to change the example to use like people have suggested above. nice usage example :+1:
Uh yea, that sounds fine to me. This behavior is definitely non-standard markdown but it's an example so not a big deal. We should probably do the same for the tutorial while we're here. And it's on cdnjs so that's all pretty easy:
Just going to use marked instead, which I'm already familiar with and has an option to sanitize input.

Markdown is a simple way to format your text inline. For example, surrounding text with asterisks will make it emphasized. <del> First, add the third-party **Showdown** library to your application. This is a JavaScript library which takes Markdown text and converts it to raw HTML. This requires a script tag in your head (which we have already included in the React playground): </del> <ins> First, add the third-party library **marked** to your application. This is a JavaScript library which takes Markdown text and converts it to raw HTML. This requires a script tag in your head (which we have already included in the React playground): </ins> ```html{7} <!-- index.html -->


Question:
Since copy+pasting our code makes it too easy for people to XSS themselves, lets just add a comment in there about it.
PR at - let me know if that's what you had in mind!
My intuition is that it makes more sense to fix the example (call a sanitization library) or to change the example completely (demonstrate something else that is safer). It looks really bad to have a security warning on the homepage of the react site. Makes it seem like our framework encourages unsafe operations, rather than a safe-by-default way of doing things.
Should switch the demo to use a different library like markdown-js that escapes everything and doesn't support HTML:
markdown-js looks good. First attempt: ,output Code licensed if someone wants to turn this into a coherent example; I've signed the CLA.
That's a good suggestion too :) - what are your thoughts? I'm happy to change the example to use like people have suggested above. nice usage example :+1:
Uh yea, that sounds fine to me. This behavior is definitely non-standard markdown but it's an example so not a big deal. We should probably do the same for the tutorial while we're here. And it's on cdnjs so that's all pretty easy:
Just going to use marked instead, which I'm already familiar with and has an option to sanitize input.

<script src="https://fb.me/react-{{site.react_version}}.js"></script> <script src="https://fb.me/JSXTransformer-{{site.react_version}}.js"></script> <script src="https://code.jquery.com/jquery-1.10.0.min.js"></script> <del> <script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/0.3.1/showdown.min.js"></script> </del> <ins> <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/0.3.2/marked.min.js"></script> </ins> </head> ``` Next, let's convert the comment text to Markdown and output it: <del> ```javascript{2,10} </del> <ins> ```javascript{9} </ins> // tutorial6.js <del> var converter = new Showdown.converter(); </del> var Comment = React.createClass({ render: function() { return (


Question:
Since copy+pasting our code makes it too easy for people to XSS themselves, lets just add a comment in there about it.
PR at - let me know if that's what you had in mind!
My intuition is that it makes more sense to fix the example (call a sanitization library) or to change the example completely (demonstrate something else that is safer). It looks really bad to have a security warning on the homepage of the react site. Makes it seem like our framework encourages unsafe operations, rather than a safe-by-default way of doing things.
Should switch the demo to use a different library like markdown-js that escapes everything and doesn't support HTML:
markdown-js looks good. First attempt: ,output Code licensed if someone wants to turn this into a coherent example; I've signed the CLA.
That's a good suggestion too :) - what are your thoughts? I'm happy to change the example to use like people have suggested above. nice usage example :+1:
Uh yea, that sounds fine to me. This behavior is definitely non-standard markdown but it's an example so not a big deal. We should probably do the same for the tutorial while we're here. And it's on cdnjs so that's all pretty easy:
Just going to use marked instead, which I'm already familiar with and has an option to sanitize input.

 {this.props.author}
  {converter.makeHtml(this.props.children.toString())}   {marked(this.props.children.toString())}
 ); } }); ``` <del> All we're doing here is calling the Showdown library. We need to convert `this.props.children` from React's wrapped text to a raw string that Showdown will understand so we explicitly call `toString()`. </del> <ins> All we're doing here is calling the marked library. We need to convert `this.props.children` from React's wrapped text to a raw string that marked will understand so we explicitly call `toString()`. </ins> But there's a problem! Our rendered comments look like this in the browser: "`<p>`This is `<em>`another`</em>` comment`</p>`". We want those tags to actually render as HTML. That's React protecting you from an XSS attack. There's a way to get around it but the framework warns you not to use it: <del> ```javascript{5,11}   ```javascript{4,10}  // tutorial7.js  var converter = new Showdown.converter();  var Comment = React.createClass({ render: function() {  var rawMarkup = converter.makeHtml(this.props.children.toString());   var rawMarkup = marked(this.props.children.toString(), {sanitize: true});  return (


Question:
Since copy+pasting our code makes it too easy for people to XSS themselves, lets just add a comment in there about it.
PR at - let me know if that's what you had in mind!
My intuition is that it makes more sense to fix the example (call a sanitization library) or to change the example completely (demonstrate something else that is safer). It looks really bad to have a security warning on the homepage of the react site. Makes it seem like our framework encourages unsafe operations, rather than a safe-by-default way of doing things.
Should switch the demo to use a different library like markdown-js that escapes everything and doesn't support HTML:
markdown-js looks good. First attempt: ,output Code licensed if someone wants to turn this into a coherent example; I've signed the CLA.
That's a good suggestion too :) - what are your thoughts? I'm happy to change the example to use like people have suggested above. nice usage example :+1:
Uh yea, that sounds fine to me. This behavior is definitely non-standard markdown but it's an example so not a big deal. We should probably do the same for the tutorial while we're here. And it's on cdnjs so that's all pretty easy:
Just going to use marked instead, which I'm already familiar with and has an option to sanitize input.

}); ``` <del> This is a special API that intentionally makes it difficult to insert raw HTML, but for Showdown we'll take advantage of this backdoor. </del> <ins> This is a special API that intentionally makes it difficult to insert raw HTML, but for marked we'll take advantage of this backdoor. </ins> <del> **Remember:** by using this feature you're relying on Showdown to be secure. </del> <ins> **Remember:** by using this feature you're relying on marked to be secure. In this case, we pass `sanitize: true` which tells marked to escape any HTML markup in the source instead of passing it through unchanged. </ins> ### Hook up the data model


Question:
Since copy+pasting our code makes it too easy for people to XSS themselves, lets just add a comment in there about it.
PR at - let me know if that's what you had in mind!
My intuition is that it makes more sense to fix the example (call a sanitization library) or to change the example completely (demonstrate something else that is safer). It looks really bad to have a security warning on the homepage of the react site. Makes it seem like our framework encourages unsafe operations, rather than a safe-by-default way of doing things.
Should switch the demo to use a different library like markdown-js that escapes everything and doesn't support HTML:
markdown-js looks good. First attempt: ,output Code licensed if someone wants to turn this into a coherent example; I've signed the CLA.
That's a good suggestion too :) - what are your thoughts? I'm happy to change the example to use like people have suggested above. nice usage example :+1:
Uh yea, that sounds fine to me. This behavior is definitely non-standard markdown but it's an example so not a big deal. We should probably do the same for the tutorial while we're here. And it's on cdnjs so that's all pretty easy:
Just going to use marked instead, which I'm already familiar with and has an option to sanitize input.

A Component Using External Plugins

 React is flexible and provides hooks that allow you to interface with  other libraries and frameworks. This example uses Showdown, an external   other libraries and frameworks. This example uses **marked**, an external  Markdown library, to convert the textarea's value in real-time.


Question:
Since copy+pasting our code makes it too easy for people to XSS themselves, lets just add a comment in there about it.
PR at - let me know if that's what you had in mind!
My intuition is that it makes more sense to fix the example (call a sanitization library) or to change the example completely (demonstrate something else that is safer). It looks really bad to have a security warning on the homepage of the react site. Makes it seem like our framework encourages unsafe operations, rather than a safe-by-default way of doing things.
Should switch the demo to use a different library like markdown-js that escapes everything and doesn't support HTML:
markdown-js looks good. First attempt: ,output Code licensed if someone wants to turn this into a coherent example; I've signed the CLA.
That's a good suggestion too :) - what are your thoughts? I'm happy to change the example to use like people have suggested above. nice usage example :+1:
Uh yea, that sounds fine to me. This behavior is definitely non-standard markdown but it's an example so not a big deal. We should probably do the same for the tutorial while we're here. And it's on cdnjs so that's all pretty easy:
Just going to use marked instead, which I'm already familiar with and has an option to sanitize input.

 /** * marked - a markdown parser * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed) * https://github.com/chjj/marked */ (function(){var block={newline:/^n+/,code:/^( {4}[^n]+n*)+/,fences:noop,hr:/^( *[-*_]){3,} *(?:n+|$)/,heading:/^ *(#{1,6}) *([^n]+?) *#* *(?:n+|$)/,nptable:noop,lheading:/^([^n]+)n *(=|-){2,} *(?:n+|$)/,blockquote:/^( *>[^n]+(n(?!def)[^n]+)*n*)+/,list:/^( *)(bull) [sS]+?(?:hr|def|n{2,}(?! )(?!1bull )n*|s*$)/,html:/^ *(?:comment|closed|closing) *(?:n{2,}|s*$)/,def:/^ *[([^]]+)]: *<?([^s>]+)>?(?: +["(]([^n]+)[")])? *(?:n+|$)/,table:noop,paragraph:/^((?:[^n]+n?(?!hr|heading|lheading|blockquote|tag|def))+)n*/,text:/^[^n]+/};block.bullet=/(?:[*+-]|d+.)/;block.item=/^( *)(bull) [^n]*(?:n(?!1bull )[^n]*)*/;block.item=replace(block.item,"gm")(/bull/g,block.bullet)();block.list=replace(block.list)(/bull/g,block.bullet)("hr","n+(?=1?(?:[-*_] *){3,}(?:n+|$))")("def","n+(?="+block.def.source+")")();block.blockquote=replace(block.blockquote)("def",block.def)();block._tag="(?!(?:"+"a|em|strong|small|s|cite|q|dfn|abbr|data|time|code"+"|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo"+"|span|br|wbr|ins|del|img)b)w+(?!:/|[^ws@]*@)b";block.html=replace(block.html)("comment",//)("closed",/<(tag)[sS]+?</1>/)("closing",/<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)(/tag/g,block._tag)();block.paragraph=replace(block.paragraph)("hr",block.hr)("heading",block.heading)("lheading",block.lheading)("blockquote",block.blockquote)("tag","<"+block._tag)("def",block.def)();block.normal=merge({},block);block.gfm=merge({},block.normal,{fences:/^ *(`{3,}|~{3,}) *(S+)? *n([sS]+?)s*1 *(?:n+|$)/,paragraph:/^/});block.gfm.paragraph=replace(block.paragraph)("(?!","(?!"+block.gfm.fences.source.replace("1","2")+"|"+block.list.source.replace("1","3")+"|")();block.tables=merge({},block.gfm,{nptable:/^ *(S.*|.*)n *([-:]+ *|[-| :]*)n((?:.*|.*(?:n|$))*)n*/,table:/^ *|(.+)n *|( *[-:]+[-| :]*)n((?: *|.*(?:n|$))*)n*/});function Lexer(options){this.tokens=[];this.tokens.links={};this.options=options||marked.defaults;this.rules=block.normal;if(this.options.gfm){if(this.options.tables){this.rules=block.tables}else{this.rules=block.gfm}}}Lexer.rules=block;Lexer.lex=function(src,options){var lexer=new Lexer(options);return lexer.lex(src)};Lexer.prototype.lex=function(src){src=src.replace(/rn|r/g,"n").replace(/t/g,"    ").replace(/u00a0/g," ").replace(/u2424/g,"n");return this.token(src,true)};Lexer.prototype.token=function(src,top,bq){var src=src.replace(/^ +$/gm,""),next,loose,cap,bull,b,item,space,i,l;while(src){if(cap=this.rules.newline.exec(src)){src=src.substring(cap[0].length);if(cap[0].length>1){this.tokens.push({type:"space"})}}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);cap=cap[0].replace(/^ {4}/gm,"");this.tokens.push({type:"code",text:!this.options.pedantic?cap.replace(/n+$/,""):cap});continue}if(cap=this.rules.fences.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"code",lang:cap[2],text:cap[3]});continue}if(cap=this.rules.heading.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"heading",depth:cap[1].length,text:cap[2]});continue}if(top&&(cap=this.rules.nptable.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *| *$/g,"").split(/ *| */),align:cap[2].replace(/^ *|| *$/g,"").split(/ *| */),cells:cap[3].replace(/n$/,"").split("n")};for(i=0;i<item.align.length;i++){if(/^ *-+: *$/.test(item.align[i])){item.align[i]="right"}else if(/^ *:-+: *$/.test(item.align[i])){item.align[i]="center"}else if(/^ *:-+ *$/.test(item.align[i])){item.align[i]="left"}else{item.align[i]=null}}for(i=0;i<item.cells.length;i++){item.cells[i]=item.cells[i].split(/ *| */)}this.tokens.push(item);continue}if(cap=this.rules.lheading.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"heading",depth:cap[2]==="="?1:2,text:cap[1]});continue}if(cap=this.rules.hr.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"hr"});continue}if(cap=this.rules.blockquote.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"blockquote_start"});cap=cap[0].replace(/^ *> ?/gm,"");this.token(cap,top,true);this.tokens.push({type:"blockquote_end"});continue}if(cap=this.rules.list.exec(src)){src=src.substring(cap[0].length);bull=cap[2];this.tokens.push({type:"list_start",ordered:bull.length>1});cap=cap[0].match(this.rules.item);next=false;l=cap.length;i=0;for(;i<l;i++){item=cap[i];space=item.length;item=item.replace(/^ *([*+-]|d+.) +/,"");if(~item.indexOf("n ")){space-=item.length;item=!this.options.pedantic?item.replace(new RegExp("^ {1,"+space+"}","gm"),""):item.replace(/^ {1,4}/gm,"")}if(this.options.smartLists&&i!==l-1){b=block.bullet.exec(cap[i+1])[0];if(bull!==b&&!(bull.length>1&&b.length>1)){src=cap.slice(i+1).join("n")+src;i=l-1}}loose=next||/nn(?!s*$)/.test(item);if(i!==l-1){next=item.charAt(item.length-1)==="n";if(!loose)loose=next}this.tokens.push({type:loose?"loose_item_start":"list_item_start"});this.token(item,false,bq);this.tokens.push({type:"list_item_end"})}this.tokens.push({type:"list_end"});continue}if(cap=this.rules.html.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:this.options.sanitize?"paragraph":"html",pre:cap[1]==="pre"||cap[1]==="script"||cap[1]==="style",text:cap[0]});continue}if(!bq&&top&&(cap=this.rules.def.exec(src))){src=src.substring(cap[0].length);this.tokens.links[cap[1].toLowerCase()]={href:cap[2],title:cap[3]};continue}if(top&&(cap=this.rules.table.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *| *$/g,"").split(/ *| */),align:cap[2].replace(/^ *|| *$/g,"").split(/ *| */),cells:cap[3].replace(/(?: *| *)?n$/,"").split("n")};for(i=0;i<item.align.length;i++){if(/^ *-+: *$/.test(item.align[i])){item.align[i]="right"}else if(/^ *:-+: *$/.test(item.align[i])){item.align[i]="center"}else if(/^ *:-+ *$/.test(item.align[i])){item.align[i]="left"}else{item.align[i]=null}}for(i=0;i<item.cells.length;i++){item.cells[i]=item.cells[i].replace(/^ *| *| *| *$/g,"").split(/ *| */)}this.tokens.push(item);continue}if(top&&(cap=this.rules.paragraph.exec(src))){src=src.substring(cap[0].length);this.tokens.push({type:"paragraph",text:cap[1].charAt(cap[1].length-1)==="n"?cap[1].slice(0,-1):cap[1]});continue}if(cap=this.rules.text.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"text",text:cap[0]});continue}if(src){throw new Error("Infinite loop on byte: "+src.charCodeAt(0))}}return this.tokens};var inline={escape:/^([`*{}[]()#+-.!_>])/,autolink:/^<([^ >]+(@|:/)[^ >]+)>/,url:noop,tag:/^|^</?w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,link:/^!?[(inside)](href)/,reflink:/^!?[(inside)]s*[([^]]*)]/,nolink:/^!?[((?:[[^]]*]|[^[]])*)]/,strong:/^__([sS]+?)__(?!_)|^**([sS]+?)**(?!*)/,em:/^b_((?:__|[sS])+?)_b|^*((?:**|[sS])+?)*(?!*)/,code:/^(`+)s*([sS]*?[^`])s*1(?!`)/,br:/^ {2,}n(?!s*$)/,del:noop,text:/^[sS]+?(?=[<![_*`]| {2,}n|$)/};inline._inside=/(?:[[^]]*]|[^[]]|](?=[^[]*]))*/;inline._href=/s*<?([sS]*?)>?(?:s+['"]([sS]*?)['"])?s*/;inline.link=replace(inline.link)("inside",inline._inside)("href",inline._href)();inline.reflink=replace(inline.reflink)("inside",inline._inside)();inline.normal=merge({},inline);inline.pedantic=merge({},inline.normal,{strong:/^__(?=S)([sS]*?S)__(?!_)|^**(?=S)([sS]*?S)**(?!*)/,em:/^_(?=S)([sS]*?S)_(?!_)|^*(?=S)([sS]*?S)*(?!*)/});inline.gfm=merge({},inline.normal,{escape:replace(inline.escape)("])","~|])")(),url:/^(https?://[^s<]+[^<.,:;"')]s])/,del:/^~~(?=S)([sS]*?S)~~/,text:replace(inline.text)("]|","~]|")("|","|https?://|")()});inline.breaks=merge({},inline.gfm,{br:replace(inline.br)("{2,}","*")(),text:replace(inline.gfm.text)("{2,}","*")()});function InlineLexer(links,options){this.options=options||marked.defaults;this.links=links;this.rules=inline.normal;this.renderer=this.options.renderer||new Renderer;this.renderer.options=this.options;if(!this.links){throw new Error("Tokens array requires a `links` property.")}if(this.options.gfm){if(this.options.breaks){this.rules=inline.breaks}else{this.rules=inline.gfm}}else if(this.options.pedantic){this.rules=inline.pedantic}}InlineLexer.rules=inline;InlineLexer.output=function(src,links,options){var inline=new InlineLexer(links,options);return inline.output(src)};InlineLexer.prototype.output=function(src){var out="",link,text,href,cap;while(src){if(cap=this.rules.escape.exec(src)){src=src.substring(cap[0].length);out+=cap[1];continue}if(cap=this.rules.autolink.exec(src)){src=src.substring(cap[0].length);if(cap[2]==="@"){text=cap[1].charAt(6)===":"?this.mangle(cap[1].substring(7)):this.mangle(cap[1]);href=this.mangle("mailto:")+text}else{text=escape(cap[1]);href=text}out+=this.renderer.link(href,null,text);continue}if(!this.inLink&&(cap=this.rules.url.exec(src))){src=src.substring(cap[0].length);text=escape(cap[1]);href=text;out+=this.renderer.link(href,null,text);continue}if(cap=this.rules.tag.exec(src)){if(!this.inLink&&/^<a /i.test(cap[0])){this.inLink=true}else if(this.inLink&&/^/i.test(cap[0])){this.inLink=false}src=src.substring(cap[0].length);out+=this.options.sanitize?escape(cap[0]):cap[0];continue}if(cap=this.rules.link.exec(src)){src=src.substring(cap[0].length);this.inLink=true;out+=this.outputLink(cap,{href:cap[2],title:cap[3]});this.inLink=false;continue}if((cap=this.rules.reflink.exec(src))||(cap=this.rules.nolink.exec(src))){src=src.substring(cap[0].length);link=(cap[2]||cap[1]).replace(/s+/g," ");link=this.links[link.toLowerCase()];if(!link||!link.href){out+=cap[0].charAt(0);src=cap[0].substring(1)+src;continue}this.inLink=true;out+=this.outputLink(cap,link);this.inLink=false;continue}if(cap=this.rules.strong.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.strong(this.output(cap[2]||cap[1]));continue}if(cap=this.rules.em.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.em(this.output(cap[2]||cap[1]));continue}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.codespan(escape(cap[2],true));continue}if(cap=this.rules.br.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.br();continue}if(cap=this.rules.del.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.del(this.output(cap[1]));continue}if(cap=this.rules.text.exec(src)){src=src.substring(cap[0].length);out+=escape(this.smartypants(cap[0]));continue}if(src){throw new Error("Infinite loop on byte: "+src.charCodeAt(0))}}return out};InlineLexer.prototype.outputLink=function(cap,link){var href=escape(link.href),title=link.title?escape(link.title):null;return cap[0].charAt(0)!=="!"?this.renderer.link(href,title,this.output(cap[1])):this.renderer.image(href,title,escape(cap[1]))};InlineLexer.prototype.smartypants=function(text){if(!this.options.smartypants)return text;return text.replace(/--/g,"—").replace(/(^|[-u2014/([{"s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-u2014/([{u2018s])"/g,"$1“").replace(/"/g,"”").replace(/.{3}/g,"…")};InlineLexer.prototype.mangle=function(text){var out="",l=text.length,i=0,ch;for(;i<l;i++){ch=text.charCodeAt(i);if(Math.random()>.5){ch="x"+ch.toString(16)}out+="&#"+ch+";"}return out};function Renderer(options){this.options=options||{}}Renderer.prototype.code=function(code,lang,escaped){if(this.options.highlight){var out=this.options.highlight(code,lang);if(out!=null&&out!==code){escaped=true;code=out}}if(!lang){return"
"+(escaped?code:escape(code,true))+"n
"}return'
'+(escaped?code:escape(code,true))+"n
n"};Renderer.prototype.blockquote=function(quote){return"
n"+quote+"
n"};Renderer.prototype.html=function(html){return html};Renderer.prototype.heading=function(text,level,raw){return"<h"+level+' id="'+this.options.headerPrefix+raw.toLowerCase().replace(/[^w]+/g,"-")+'">'+text+"</h"+level+">n"};Renderer.prototype.hr=function(){return this.options.xhtml?"
n":"
n"};Renderer.prototype.list=function(body,ordered){var type=ordered?"ol":"ul";return"<"+type+">n"+body+"</"+type+">n"};Renderer.prototype.listitem=function(text){return"
"+text+"
n"};Renderer.prototype.paragraph=function(text){return"
"+text+"
n"};Renderer.prototype.table=function(header,body){return"
n"+"
n"+header+"
n"+"
n"+body+"
n"+"
n"};Renderer.prototype.tablerow=function(content){return"
n"+content+"
n"};Renderer.prototype.tablecell=function(content,flags){var type=flags.header?"th":"td";var tag=flags.align?"<"+type+' style="text-align:'+flags.align+'">':"<"+type+">";return tag+content+"</"+type+">n"};Renderer.prototype.strong=function(text){return""+text+""};Renderer.prototype.em=function(text){return""+text+""};Renderer.prototype.codespan=function(text){return""+text+""};Renderer.prototype.br=function(){return this.options.xhtml?"
":"
"};Renderer.prototype.del=function(text){return""+text+""};Renderer.prototype.link=function(href,title,text){if(this.options.sanitize){try{var prot=decodeURIComponent(unescape(href)).replace(/[^w:]/g,"").toLowerCase()}catch(e){return""}if(prot.indexOf("javascript:")===0){return""}}var out='"+text+"";return out};Renderer.prototype.image=function(href,title,text){var out='":">";return out};function Parser(options){this.tokens=[];this.token=null;this.options=options||marked.defaults;this.options.renderer=this.options.renderer||new Renderer;this.renderer=this.options.renderer;this.renderer.options=this.options}Parser.parse=function(src,options,renderer){var parser=new Parser(options,renderer);return parser.parse(src)};Parser.prototype.parse=function(src){this.inline=new InlineLexer(src.links,this.options,this.renderer);this.tokens=src.reverse();var out="";while(this.next()){out+=this.tok()}return out};Parser.prototype.next=function(){return this.token=this.tokens.pop()};Parser.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0};Parser.prototype.parseText=function(){var body=this.token.text;while(this.peek().type==="text"){body+="n"+this.next().text}return this.inline.output(body)};Parser.prototype.tok=function(){switch(this.token.type){case"space":{return""}case"hr":{return this.renderer.hr()}case"heading":{return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,this.token.text)}case"code":{return this.renderer.code(this.token.text,this.token.lang,this.token.escaped)}case"table":{var header="",body="",i,row,cell,flags,j;cell="";for(i=0;i<this.token.header.length;i++){flags={header:true,align:this.token.align[i]};cell+=this.renderer.tablecell(this.inline.output(this.token.header[i]),{header:true,align:this.token.align[i]})}header+=this.renderer.tablerow(cell);for(i=0;i<this.token.cells.length;i++){row=this.token.cells[i];cell="";for(j=0;j<row.length;j++){cell+=this.renderer.tablecell(this.inline.output(row[j]),{header:false,align:this.token.align[j]})}body+=this.renderer.tablerow(cell)}return this.renderer.table(header,body)}case"blockquote_start":{var body="";while(this.next().type!=="blockquote_end"){body+=this.tok()}return this.renderer.blockquote(body)}case"list_start":{var body="",ordered=this.token.ordered;while(this.next().type!=="list_end"){body+=this.tok()}return this.renderer.list(body,ordered)}case"list_item_start":{var body="";while(this.next().type!=="list_item_end"){body+=this.token.type==="text"?this.parseText():this.tok()}return this.renderer.listitem(body)}case"loose_item_start":{var body="";while(this.next().type!=="list_item_end"){body+=this.tok()}return this.renderer.listitem(body)}case"html":{var html=!this.token.pre&&!this.options.pedantic?this.inline.output(this.token.text):this.token.text;return this.renderer.html(html)}case"paragraph":{return this.renderer.paragraph(this.inline.output(this.token.text))}case"text":{return this.renderer.paragraph(this.parseText())}}};function escape(html,encode){return html.replace(!encode?/&(?!#?w+;)/g:/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function unescape(html){return html.replace(/&([#w]+);/g,function(_,n){n=n.toLowerCase();if(n==="colon")return":";if(n.charAt(0)==="#"){return n.charAt(1)==="x"?String.fromCharCode(parseInt(n.substring(2),16)):String.fromCharCode(+n.substring(1))}return""})}function replace(regex,opt){regex=regex.source;opt=opt||"";return function self(name,val){if(!name)return new RegExp(regex,opt);val=val.source||val;val=val.replace(/(^|[^[])^/g,"$1");regex=regex.replace(name,val);return self}}function noop(){}noop.exec=noop;function merge(obj){var i=1,target,key;for(;i<arguments.length;i++){target=arguments[i];for(key in target){if(Object.prototype.hasOwnProperty.call(target,key)){obj[key]=target[key]}}}return obj}function marked(src,opt,callback){if(callback||typeof opt==="function"){if(!callback){callback=opt;opt=null}opt=merge({},marked.defaults,opt||{});var highlight=opt.highlight,tokens,pending,i=0;try{tokens=Lexer.lex(src,opt)}catch(e){return callback(e)}pending=tokens.length;var done=function(){var out,err;try{out=Parser.parse(tokens,opt)}catch(e){err=e}opt.highlight=highlight;return err?callback(err):callback(null,out)};if(!highlight||highlight.length<3){return done()}delete opt.highlight;if(!pending)return done();for(;i<tokens.length;i++){(function(token){if(token.type!=="code"){return--pending||done()}return highlight(token.text,token.lang,function(err,code){if(code==null||code===token.text){return--pending||done()}token.text=code;token.escaped=true;--pending||done()})})(tokens[i])}return}try{if(opt)opt=merge({},marked.defaults,opt);return Parser.parse(Lexer.lex(src,opt),opt)}catch(e){e.message+="nPlease report this to https://github.com/chjj/marked.";if((opt||marked.defaults).silent){return"
An error occured:

"+escape(e.message+"",true)+"
"}throw e}}marked.options=marked.setOptions=function(opt){merge(marked.defaults,opt);return marked};marked.defaults={gfm:true,tables:true,breaks:false,pedantic:false,sanitize:false,smartLists:false,silent:false,highlight:null,langPrefix:"lang-",smartypants:false,headerPrefix:"",renderer:new Renderer,xhtml:false};marked.Parser=Parser;marked.parser=Parser.parse;marked.Renderer=Renderer;marked.Lexer=Lexer;marked.lexer=Lexer.lex;marked.InlineLexer=InlineLexer;marked.inlineLexer=InlineLexer.output;marked.parse=marked;if(typeof exports==="object"){module.exports=marked}else if(typeof define==="function"&&define.amd){define(function(){return marked})}else{this.marked=marked}}).call(function(){return this||(typeof window!=="undefined"?window:global)}());   No newline at end of file


Question:
Since copy+pasting our code makes it too easy for people to XSS themselves, lets just add a comment in there about it.
PR at - let me know if that's what you had in mind!
My intuition is that it makes more sense to fix the example (call a sanitization library) or to change the example completely (demonstrate something else that is safer). It looks really bad to have a security warning on the homepage of the react site. Makes it seem like our framework encourages unsafe operations, rather than a safe-by-default way of doing things.
Should switch the demo to use a different library like markdown-js that escapes everything and doesn't support HTML:
markdown-js looks good. First attempt: ,output Code licensed if someone wants to turn this into a coherent example; I've signed the CLA.
That's a good suggestion too :) - what are your thoughts? I'm happy to change the example to use like people have suggested above. nice usage example :+1:
Uh yea, that sounds fine to me. This behavior is definitely non-standard markdown but it's an example so not a big deal. We should probably do the same for the tutorial while we're here. And it's on cdnjs so that's all pretty easy:
Just going to use marked instead, which I'm already familiar with and has an option to sanitize input.

 // // showdown.js -- A javascript port of Markdown. // // Copyright (c) 2007 John Fraser. // // Original Markdown Copyright (c) 2004-2005 John Gruber //   <http://daringfireball.net/projects/markdown/> // // Redistributable under a BSD-style open source license. // See license.txt for more information. // // The full source distribution is at: // //				A A L //				T C A //				T K B // //   <http://www.attacklab.net/> // // // Wherever possible, Showdown is a straight, line-by-line port // of the Perl version of Markdown. // // This is not a normal parser design; it's basically just a // series of string substitutions.  It's hard to read and // maintain this way,  but keeping Showdown close to the original // design makes it easier to port new features. // // More importantly, Showdown behaves like markdown.pl in most // edge cases.  So web applications can do client-side preview // in Javascript, and then build identical HTML on the server. // // This port needs the new RegExp functionality of ECMA 262, // 3rd Edition (i.e. Javascript 1.5).  Most modern web browsers // should do fine.  Even with the new regular expression features, // We do a lot of work to emulate Perl's regex functionality. // The tricky changes in this file mostly have the "attacklab:" // label.  Major or self-explanatory changes don't. // // Smart diff tools like Araxis Merge will be able to match up // this file with markdown.pl in a useful way.  A little tweaking // helps: in a copy of markdown.pl, replace "#" with "//" and // replace "$text" with "text".  Be sure to ignore whitespace // and line endings. // // // Showdown usage: // //   var text = "Markdown *rocks*."; // //   var converter = new Showdown.converter(); //   var html = converter.makeHtml(text); // //   alert(html); // // Note: move the sample code to the bottom of this // file before uncommenting it. // // // Showdown namespace // var Showdown = {}; // // converter // // Wraps all "globals" so that the only thing // exposed is makeHtml(). // Showdown.converter = function() { // // Globals: // // Global hashes, used by various utility routines var g_urls; var g_titles; var g_html_blocks; // Used to track when we're inside an ordered or unordered list // (see _ProcessListItems() for details): var g_list_level = 0; this.makeHtml = function(text) { // // Main function. The order in which other subs are called here is // essential. Link and image substitutions need to happen before // _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the  // and  tags get encoded. // // Clear the global hashes. If we don't clear these, you get conflicts // from other articles when generating a page which contains more than // one article (e.g. an index page that shows the N most recent // articles): g_urls = new Array(); g_titles = new Array(); g_html_blocks = new Array(); // attacklab: Replace ~ with ~T // This lets us use tilde as an escape char to avoid md5 hashes // The choice of character is arbitray; anything that isn't // magic in Markdown will work. text = text.replace(/~/g,"~T"); // attacklab: Replace $ with ~D // RegExp interprets $ as a special character // when it's in a replacement string text = text.replace(/$/g,"~D"); // Standardize line endings text = text.replace(/rn/g,"n"); // DOS to Unix text = text.replace(/r/g,"n"); // Mac to Unix // Make sure text begins and ends with a couple of newlines: text = "nn" + text + "nn"; // Convert all tabs to spaces. text = _Detab(text); // Strip any lines consisting only of spaces and tabs. // This makes subsequent regexen easier to write, because we can // match consecutive blank lines with /n+/ instead of something // contorted like /[ t]*n+/ . text = text.replace(/^[ t]+$/mg,""); // Turn block-level HTML blocks into hash entries text = _HashHTMLBlocks(text); // Strip link definitions, store in hashes. text = _StripLinkDefinitions(text); text = _RunBlockGamut(text); text = _UnescapeSpecialChars(text); // attacklab: Restore dollar signs text = text.replace(/~D/g,"$$"); // attacklab: Restore tildes text = text.replace(/~T/g,"~"); return text; } var _StripLinkDefinitions = function(text) { // // Strips link definitions from text, stores the URLs and titles in // hash references. // // Link defs are in the form: ^[id]: url "optional title" /* var text = text.replace(/ ^[ ]{0,3}[(.+)]:  // id = $1  attacklab: g_tab_width - 1 [ t]* n?				// maybe *one* newline [ t]* <?(S+?)>?			// url = $2 [ t]* n?				// maybe one newline [ t]* (?: (n*)				// any lines skipped = $3 attacklab: lookbehind removed ["(] (.+?)				// title = $4 [")] [ t]* )?					// title is optional (?:n+|$) /gm, function(){...}); */ var text = text.replace(/^[ ]{0,3}[(.+)]:[ t]*n?[ t]*<?(S+?)>?[ t]*n?[ t]*(?:(n*)["(](.+?)[")][ t]*)?(?:n+|Z)/gm, function (wholeMatch,m1,m2,m3,m4) { m1 = m1.toLowerCase(); g_urls[m1] = _EncodeAmpsAndAngles(m2);  // Link IDs are case-insensitive if (m3) { // Oops, found blank lines, so it's not a title. // Put back the parenthetical statement we stole. return m3+m4; } else if (m4) { g_titles[m1] = m4.replace(/"/g,"""); } // Completely remove the definition from the text return ""; } ); return text; } var _HashHTMLBlocks = function(text) { // attacklab: Double up blank lines to reduce lookaround text = text.replace(/n/g,"nn"); // Hashify HTML blocks: // We only want to do this for block-level HTML tags, such as headers, // lists, and tables. That's because we still want to wrap
s around // "paragraphs" that are wrapped in non-block-level tags, such as anchors, // phrase emphasis, and spans. The list of tags we're looking for is // hard-coded: var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del" var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math" // First, look for nested blocks, e.g.: //
 //
 //     tags for inner block must be indented. //
 //
 // // The outermost tags must start at the left margin for this to match, and // the inner nested divs must be indented. // We need to do this before the next, more liberal match, because the next // match will start at the first `<div>` and stop at the first `</div>`. // attacklab: This regex can be expensive when it fails. /* var text = text.replace(/ (						// save in $1 ^					// start of line  (with /m) <($block_tags_a)	// start tag = $2 b					// word break // attacklab: hack around khtml/pcre bug... [^r]*?n			// any number of lines, minimally matching </2>				// the matching end tag [ t]*				// trailing spaces/tabs (?=n+)				// followed by a newline )						// attacklab: there are sentinel newlines at end of document /gm,function(){...}}; */ text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)b[^r]*?n</2>[ t]*(?=n+))/gm,hashElement); // // Now match more liberally, simply from `n<tag>` to `</tag>n` // /* var text = text.replace(/ (						// save in $1 ^					// start of line  (with /m) <($block_tags_b)	// start tag = $2 b					// word break // attacklab: hack around khtml/pcre bug... [^r]*?				// any number of lines, minimally matching .*</2>				// the matching end tag [ t]*				// trailing spaces/tabs (?=n+)				// followed by a newline )						// attacklab: there are sentinel newlines at end of document /gm,function(){...}}; */ text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math)b[^r]*?.*</2>[ t]*(?=n+)n)/gm,hashElement); // Special case just for
. It was easier to make a special case than // to make the other regex more complicated. /* text = text.replace(/ (						// save in $1 nn				// Starting after a blank line [ ]{0,3} (<(hr)				// start tag = $2 b					// word break ([^<>])*?			// /?>)				// the matching end tag [ t]* (?=n{2,})			// followed by a blank line ) /g,hashElement); */ text = text.replace(/(n[ ]{0,3}(<(hr)b([^<>])*?/?>)[ t]*(?=n{2,}))/g,hashElement); // Special case for standalone HTML comments: /* text = text.replace(/ (						// save in $1 nn				// Starting after a blank line [ ]{0,3}			// attacklab: g_tab_width - 1 <! (--[^r]*?--s*)+ > [ t]* (?=n{2,})			// followed by a blank line ) /g,hashElement); */ text = text.replace(/(nn[ ]{0,3}<!(--[^r]*?--s*)+>[ t]*(?=n{2,}))/g,hashElement); // PHP and ASP-style processor instructions (<?...?> and <%...%>) /* text = text.replace(/ (?: nn				// Starting after a blank line ) (						// save in $1 [ ]{0,3}			// attacklab: g_tab_width - 1 (?: <([?%])			// $2 [^r]*? 2> ) [ t]* (?=n{2,})			// followed by a blank line ) /g,hashElement); */ text = text.replace(/(?:nn)([ ]{0,3}(?:<([?%])[^r]*?2>)[ t]*(?=n{2,}))/g,hashElement); // attacklab: Undo double lines (see comment at top of this function) text = text.replace(/nn/g,"n"); return text; } var hashElement = function(wholeMatch,m1) { var blockText = m1; // Undo double lines blockText = blockText.replace(/nn/g,"n"); blockText = blockText.replace(/^n/,""); // strip trailing blank lines blockText = blockText.replace(/n+$/g,""); // Replace the element text with a marker ("~KxK" where x is its key) blockText = "nn~K" + (g_html_blocks.push(blockText)-1) + "Knn"; return blockText; }; var _RunBlockGamut = function(text) { // // These are all the transformations that form block-level // tags like paragraphs, headers, and list items. // text = _DoHeaders(text); // Do Horizontal Rules: var key = hashBlock("
"); text = text.replace(/^[ ]{0,2}([ ]?*[ ]?){3,}[ t]*$/gm,key); text = text.replace(/^[ ]{0,2}([ ]?-[ ]?){3,}[ t]*$/gm,key); text = text.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ t]*$/gm,key); text = _DoLists(text); text = _DoCodeBlocks(text); text = _DoBlockQuotes(text); // We already ran _HashHTMLBlocks() before, in Markdown(), but that // was to escape raw HTML in the original Markdown source. This time, // we're escaping the markup we've just created, so that we don't wrap //
 tags around block-level tags. text = _HashHTMLBlocks(text); text = _FormParagraphs(text); return text; } var _RunSpanGamut = function(text) { // // These are all the transformations that occur *within* block-level // tags like paragraphs, headers, and list items. // text = _DoCodeSpans(text); text = _EscapeSpecialCharsWithinTagAttributes(text); text = _EncodeBackslashEscapes(text); // Process anchor and image tags. Images must come first, // because ![foo][f] looks like an anchor. text = _DoImages(text); text = _DoAnchors(text); // Make links out of things like `<http://example.com/>` // Must come after _DoAnchors(), because you can use < and > // delimiters in inline links like [this](<url>). text = _DoAutoLinks(text); text = _EncodeAmpsAndAngles(text); text = _DoItalicsAndBold(text); // Do hard breaks: text = text.replace(/  +n/g,"
n"); return text; } var _EscapeSpecialCharsWithinTagAttributes = function(text) { // // Within tags -- meaning between < and > -- encode [ ` * _] so they // don't conflict with their use in Markdown for code, italics and strong. // // Build a regex to find HTML tags and comments.  See Friedl's // "Mastering Regular Expressions", 2nd Ed., pp. 200-201. var regex = /(<[a-z/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--s*)+>)/gi; text = text.replace(regex, function(wholeMatch) { var tag = wholeMatch.replace(/(.)</?code>(?=.)/g,"$1`"); tag = escapeCharacters(tag,"`*_"); return tag; }); return text; } var _DoAnchors = function(text) { // // Turn Markdown link shortcuts into XHTML  tags. // // // First, handle reference-style links: [link text] [id] // /* text = text.replace(/ (							// wrap whole match in $1 [ ( (?: [[^]]*]		// allow brackets nested one level | [^[]			// or anything else )* ) ] [ ]?					// one optional space (?:n[ ]*)?				// one optional newline followed by spaces [ (.*?)					// id = $3 ] )()()()()					// pad remaining backreferences /g,_DoAnchors_callback); */ text = text.replace(/([((?:[[^]]*]|[^[]])*)][ ]?(?:n[ ]*)?[(.*?)])()()()()/g,writeAnchorTag); // // Next, inline-style links: [link text](url "optional title") // /* text = text.replace(/ (						// wrap whole match in $1 [ ( (?: [[^]]*]	// allow brackets nested one level | [^[]]			// or anything else ) ) ] (						// literal paren [ t]* ()						// no id, so leave $3 empty <?(.*?)>?				// href = $4 [ t]* (						// $5 (['"])				// quote char = $6 (.*?)				// Title = $7 6					// matching quote [ t]*				// ignore any spaces/tabs between closing quote and ) )?						// title is optional ) ) /g,writeAnchorTag); */ text = text.replace(/([((?:[[^]]*]|[^[]])*)]([ t]*()<?(.*?)>?[ t]*((['"])(.*?)6[ t]*)?))/g,writeAnchorTag); // // Last, handle reference-style shortcuts: [link text] // These must come last in case you've also got [link test][1] // or [link test](/foo) // /* text = text.replace(/ (		 					// wrap whole match in $1 [ ([^[]]+)				// link text = $2; can't contain '[' or ']' ] )()()()()()					// pad rest of backreferences /g, writeAnchorTag); */ text = text.replace(/([([^[]]+)])()()()()()/g, writeAnchorTag); return text; } var writeAnchorTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) { if (m7 == undefined) m7 = ""; var whole_match = m1; var link_text   = m2; var link_id	 = m3.toLowerCase(); var url		= m4; var title	= m7; if (url == "") { if (link_id == "") { // lower-case and turn embedded newlines into spaces link_id = link_text.toLowerCase().replace(/ ?n/g," "); } url = "#"+link_id; if (g_urls[link_id] != undefined) { url = g_urls[link_id]; if (g_titles[link_id] != undefined) { title = g_titles[link_id]; } } else { if (whole_match.search(/(s*)$/m)>-1) { // Special case for explicit empty url url = ""; } else { return whole_match; } } } url = escapeCharacters(url,"*_"); var result = "" + link_text + ""; return result; } var _DoImages = function(text) { // // Turn Markdown image shortcuts into  tags. // // // First, handle reference-style labeled images: ![alt text][id] // /* text = text.replace(/ (						// wrap whole match in $1 ![ (.*?)				// alt text = $2 ] [ ]?				// one optional space (?:n[ ]*)?			// one optional newline followed by spaces [ (.*?)				// id = $3 ] )()()()()				// pad rest of backreferences /g,writeImageTag); */ text = text.replace(/(![(.*?)][ ]?(?:n[ ]*)?[(.*?)])()()()()/g,writeImageTag); // // Next, handle inline images:  ![alt text](url "optional title") // Don't forget: encode * and _ /* text = text.replace(/ (						// wrap whole match in $1 ![ (.*?)				// alt text = $2 ] s?					// One optional whitespace character (					// literal paren [ t]* ()					// no id, so leave $3 empty <?(S+?)>?			// src url = $4 [ t]* (					// $5 (['"])			// quote char = $6 (.*?)			// title = $7 6				// matching quote [ t]* )?					// title is optional ) ) /g,writeImageTag); */ text = text.replace(/(![(.*?)]s?([ t]*()<?(S+?)>?[ t]*((['"])(.*?)6[ t]*)?))/g,writeImageTag); return text; } var writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) { var whole_match = m1; var alt_text   = m2; var link_id	 = m3.toLowerCase(); var url		= m4; var title	= m7; if (!title) title = ""; if (url == "") { if (link_id == "") { // lower-case and turn embedded newlines into spaces link_id = alt_text.toLowerCase().replace(/ ?n/g," "); } url = "#"+link_id; if (g_urls[link_id] != undefined) { url = g_urls[link_id]; if (g_titles[link_id] != undefined) { title = g_titles[link_id]; } } else { return whole_match; } } alt_text = alt_text.replace(/"/g,"""); url = escapeCharacters(url,"*_"); var result = ""; return result; } var _DoHeaders = function(text) { // Setext-style headers: //	Header 1 //	======== // //	Header 2 //	-------- // text = text.replace(/^(.+)[ t]*n=+[ t]*n+/gm, function(wholeMatch,m1){return hashBlock('
' + _RunSpanGamut(m1) + "
");}); text = text.replace(/^(.+)[ t]*n-+[ t]*n+/gm, function(matchFound,m1){return hashBlock('
' + _RunSpanGamut(m1) + "
");}); // atx-style headers: //  # Header 1 //  ## Header 2 //  ## Header 2 with closing hashes ## //  ... //  ###### Header 6 // /* text = text.replace(/ ^(#{1,6})				// $1 = string of #'s [ t]* (.+?)					// $2 = Header text [ t]* #*						// optional closing #'s (not counted) n+ /gm, function() {...}); */ text = text.replace(/^(#{1,6})[ t]*(.+?)[ t]*#*n+/gm, function(wholeMatch,m1,m2) { var h_level = m1.length; return hashBlock("<h" + h_level + ' id="' + headerId(m2) + '">' + _RunSpanGamut(m2) + "</h" + h_level + ">"); }); function headerId(m) { return m.replace(/[^w]/g, '').toLowerCase(); } return text; } // This declaration keeps Dojo compressor from outputting garbage: var _ProcessListItems; var _DoLists = function(text) { // // Form HTML ordered (numbered) and unordered (bulleted) lists. // // attacklab: add sentinel to hack around khtml/safari bug: // http://bugs.webkit.org/show_bug.cgi?id=11231 text += "~0"; // Re-usable pattern to match any entirel ul or ol list: /* var whole_list = / (									// $1 = whole list (								// $2 [ ]{0,3}					// attacklab: g_tab_width - 1 ([*+-]|d+[.])				// $3 = first list item marker [ t]+ ) [^r]+? (								// $4 ~0							// sentinel for workaround; should be $ | n{2,} (?=S) (?!							// Negative lookahead for another list item marker [ t]* (?:[*+-]|d+[.])[ t]+ ) ) )/g */ var whole_list = /^(([ ]{0,3}([*+-]|d+[.])[ t]+)[^r]+?(~0|n{2,}(?=S)(?![ t]*(?:[*+-]|d+[.])[ t]+)))/gm; if (g_list_level) { text = text.replace(whole_list,function(wholeMatch,m1,m2) { var list = m1; var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol"; // Turn double returns into triple returns, so that we can make a // paragraph for the last item in a list, if necessary: list = list.replace(/n{2,}/g,"nnn");; var result = _ProcessListItems(list); // Trim any trailing whitespace, to put the closing `</$list_type>` // up on the preceding line, to get it past the current stupid // HTML block parser. This is a hack to work around the terrible // hack that is the HTML block parser. result = result.replace(/s+$/,""); result = "<"+list_type+">" + result + "</"+list_type+">n"; return result; }); } else { whole_list = /(nn|^n?)(([ ]{0,3}([*+-]|d+[.])[ t]+)[^r]+?(~0|n{2,}(?=S)(?![ t]*(?:[*+-]|d+[.])[ t]+)))/g; text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) { var runup = m1; var list = m2; var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol"; // Turn double returns into triple returns, so that we can make a // paragraph for the last item in a list, if necessary: var list = list.replace(/n{2,}/g,"nnn");; var result = _ProcessListItems(list); result = runup + "<"+list_type+">n" + result + "</"+list_type+">n"; return result; }); } // attacklab: strip sentinel text = text.replace(/~0/,""); return text; } _ProcessListItems = function(list_str) { // //  Process the contents of a single ordered or unordered list, splitting it //  into individual list items. // // The $g_list_level global keeps track of when we're inside a list. // Each time we enter a list, we increment it; when we leave a list, // we decrement. If it's zero, we're not in a list anymore. // // We do this because when we're not inside a list, we want to treat // something like this: // //    I recommend upgrading to version //    8. Oops, now this line is treated //    as a sub-list. // // As a single paragraph, despite the fact that the second line starts // with a digit-period-space sequence. // // Whereas when we're inside a list (or sub-list), that line will be // treated as the start of a sub-list. What a kludge, huh? This is // an aspect of Markdown's syntax that's hard to parse perfectly // without resorting to mind-reading. Perhaps the solution is to // change the syntax rules such that sub-lists must start with a // starting cardinal number; e.g. "1." or "a.". g_list_level++; // trim trailing blank lines: list_str = list_str.replace(/n{2,}$/,"n"); // attacklab: add sentinel to emulate z list_str += "~0"; /* list_str = list_str.replace(/ (n)?							// leading line = $1 (^[ t]*)						// leading whitespace = $2 ([*+-]|d+[.]) [ t]+			// list marker = $3 ([^r]+?						// list item text   = $4 (n{1,2})) (?= n* (~0 | 2 ([*+-]|d+[.]) [ t]+)) /gm, function(){...}); */ list_str = list_str.replace(/(n)?(^[ t]*)([*+-]|d+[.])[ t]+([^r]+?(n{1,2}))(?=n*(~0|2([*+-]|d+[.])[ t]+))/gm, function(wholeMatch,m1,m2,m3,m4){ var item = m4; var leading_line = m1; var leading_space = m2; if (leading_line || (item.search(/n{2,}/)>-1)) { item = _RunBlockGamut(_Outdent(item)); } else { // Recursion for sub-lists: item = _DoLists(_Outdent(item)); item = item.replace(/n$/,""); // chomp(item) item = _RunSpanGamut(item); } return  "
" + item + "
n"; } ); // attacklab: strip sentinel list_str = list_str.replace(/~0/g,""); g_list_level--; return list_str; } var _DoCodeBlocks = function(text) { // //  Process Markdown `<pre><code>` blocks. // /* text = text.replace(text, /(?:nn|^) (								// $1 = the code block -- one or more lines, starting with a space/tab (?: (?:[ ]{4}|t)			// Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width .*n+ )+ ) (n*[ ]{0,3}[^ tn]|(?=~0))	// attacklab: g_tab_width /g,function(){...}); */ // attacklab: sentinel workarounds for lack of A and Z, safarikhtml bug text += "~0"; text = text.replace(/(?:nn|^)((?:(?:[ ]{4}|t).*n+)+)(n*[ ]{0,3}[^ tn]|(?=~0))/g, function(wholeMatch,m1,m2) { var codeblock = m1; var nextChar = m2; codeblock = _EncodeCode( _Outdent(codeblock)); codeblock = _Detab(codeblock); codeblock = codeblock.replace(/^n+/g,""); // trim leading newlines codeblock = codeblock.replace(/n+$/g,""); // trim trailing whitespace codeblock = "
" + codeblock + "n
"; return hashBlock(codeblock) + nextChar; } ); // attacklab: strip sentinel text = text.replace(/~0/,""); return text; } var hashBlock = function(text) { text = text.replace(/(^n+|n+$)/g,""); return "nn~K" + (g_html_blocks.push(text)-1) + "Knn"; } var _DoCodeSpans = function(text) { // //   *  Backtick quotes are used for  spans. // //   *  You can use multiple backticks as the delimiters if you want to //	 include literal backticks in the code span. So, this input: // //		 Just type ``foo `bar` baz`` at the prompt. // //	   Will translate to: // //
Just type foo `bar` baz at the prompt.
 // //	There's no arbitrary limit to the number of backticks you //	can use as delimters. If you need three consecutive backticks //	in your code, use four for delimiters, etc. // //  *  You can use spaces to get literal backticks at the edges: // //		 ... type `` `bar` `` ... // //	   Turns to: // //		 ... type `bar` ... // /* text = text.replace(/ (^|[^])					// Character before opening ` can't be a backslash (`+)						// $2 = Opening run of ` (							// $3 = The code block [^r]*? [^`]					// attacklab: work around lack of lookbehind ) 2							// Matching closer (?!`) /gm, function(){...}); */ text = text.replace(/(^|[^])(`+)([^r]*?[^`])2(?!`)/gm, function(wholeMatch,m1,m2,m3,m4) { var c = m3; c = c.replace(/^([ t]*)/g,"");	// leading whitespace c = c.replace(/[ t]*$/g,"");	// trailing whitespace c = _EncodeCode(c); return m1+""+c+""; }); return text; } var _EncodeCode = function(text) { // // Encode/escape certain characters inside Markdown code runs. // The point is that in code, these characters are literals, // and lose their special Markdown meanings. // // Encode all ampersands; HTML entities are not // entities within a Markdown code span. text = text.replace(/&/g,"&"); // Do the angle bracket song and dance: text = text.replace(/</g,"<"); text = text.replace(/>/g,">"); // Now, escape characters that are magic in Markdown: text = escapeCharacters(text,"*_{}[]",false); // jj the line above breaks this: //--- //* Item //   1. Subitem //            special char: * //--- return text; } var _DoItalicsAndBold = function(text) { //  must go first: text = text.replace(/(**|__)(?=S)([^r]*?S[*_]*)1/g, "$2"); text = text.replace(/(*|_)(?=S)([^r]*?S)1/g, "$2"); return text; } var _DoBlockQuotes = function(text) { /* text = text.replace(/ (								// Wrap whole match in $1 ( ^[ t]*>[ t]?			// '>' at the start of a line .+n					// rest of the first line (.+n)*					// subsequent consecutive lines n*						// blanks )+ ) /gm, function(){...}); */ text = text.replace(/((^[ t]*>[ t]?.+n(.+n)*n*)+)/gm, function(wholeMatch,m1) { var bq = m1; // attacklab: hack around Konqueror 3.5.4 bug: // "----------bug".replace(/^-/g,"") == "bug" bq = bq.replace(/^[ t]*>[ t]?/gm,"~0");	// trim one level of quoting // attacklab: clean up hack bq = bq.replace(/~0/g,""); bq = bq.replace(/^[ t]+$/gm,"");		// trim whitespace-only lines bq = _RunBlockGamut(bq);				// recurse bq = bq.replace(/(^|n)/g,"$1  "); // These leading spaces screw with
 content, so we need to fix that: bq = bq.replace( /(s*
[^r]+?
)/gm, function(wholeMatch,m1) { var pre = m1; // attacklab: hack around Konqueror 3.5.4 bug: pre = pre.replace(/^  /mg,"~0"); pre = pre.replace(/~0/g,""); return pre; }); return hashBlock("
n" + bq + "n
"); }); return text; } var _FormParagraphs = function(text) { // //  Params: //    $text - string to process with html
 tags // // Strip leading and trailing lines: text = text.replace(/^n+/g,""); text = text.replace(/n+$/g,""); var grafs = text.split(/n{2,}/g); var grafsOut = new Array(); // // Wrap
 tags. // var end = grafs.length; for (var i=0; i<end; i++) { var str = grafs[i]; // if this is an HTML marker, copy it if (str.search(/~K(d+)K/g) >= 0) { grafsOut.push(str); } else if (str.search(/S/) >= 0) { str = _RunSpanGamut(str); str = str.replace(/^([ t]*)/g,"
"); str += "
" grafsOut.push(str); } } // // Unhashify HTML blocks // end = grafsOut.length; for (var i=0; i<end; i++) { // if this is a marker for an html block... while (grafsOut[i].search(/~K(d+)K/) >= 0) { var blockText = g_html_blocks[RegExp.$1]; blockText = blockText.replace(/$/g,"$$$$"); // Escape any dollar signs grafsOut[i] = grafsOut[i].replace(/~Kd+K/,blockText); } } return grafsOut.join("nn"); } var _EncodeAmpsAndAngles = function(text) { // Smart processing for ampersands and angle brackets that need to be encoded. // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: //   http://bumppo.net/projects/amputator/ text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|w+);)/g,"&"); // Encode naked <'s text = text.replace(/<(?![a-z/?$!])/gi,"<"); return text; } var _EncodeBackslashEscapes = function(text) { // //   Parameter:  String. //   Returns:	The string, with after processing the following backslash //			   escape sequences. // // attacklab: The polite way to do this is with the new // escapeCharacters() function: // // 	text = escapeCharacters(text,"",true); // 	text = escapeCharacters(text,"`*_{}[]()>#+-.!",true); // // ...but we're sidestepping its use of the (slow) RegExp constructor // as an optimization for Firefox.  This function gets called a LOT. text = text.replace(/()/g,escapeCharacters_callback); text = text.replace(/([`*_{}[]()>#+-.!])/g,escapeCharacters_callback); return text; } var _DoAutoLinks = function(text) { text = text.replace(/<((https?|ftp|dict):[^'">s]+)>/gi,"$1"); // Email addresses: <address@domain.foo> /* text = text.replace(/ < (?:mailto:)? ( [-.w]+ @ [-a-z0-9]+(.[-a-z0-9]+)*.[a-z]+ ) > /gi, _DoAutoLinks_callback()); */ text = text.replace(/<(?:mailto:)?([-.w]+@[-a-z0-9]+(.[-a-z0-9]+)*.[a-z]+)>/gi, function(wholeMatch,m1) { return _EncodeEmailAddress( _UnescapeSpecialChars(m1) ); } ); return text; } var _EncodeEmailAddress = function(addr) { // //  Input: an email address, e.g. "foo@example.com" // //  Output: the email address as a mailto link, with each character //	of the address encoded as either a decimal or hex entity, in //	the hopes of foiling most address harvesting spam bots. E.g.: // //	foo //	   @example.com // //  Based on a filter by Matthew Wickline, posted to the BBEdit-Talk //  mailing list: <http://tinyurl.com/yu7ue> // // attacklab: why can't javascript speak hex? function char2hex(ch) { var hexDigits = '0123456789ABCDEF'; var dec = ch.charCodeAt(0); return(hexDigits.charAt(dec>>4) + hexDigits.charAt(dec&15)); } var encode = [ function(ch){return "&#"+ch.charCodeAt(0)+";";}, function(ch){return "&#x"+char2hex(ch)+";";}, function(ch){return ch;} ]; addr = "mailto:" + addr; addr = addr.replace(/./g, function(ch) { if (ch == "@") { // this *must* be encoded. I insist. ch = encode[Math.floor(Math.random()*2)](ch); } else if (ch !=":") { // leave ':' alone (to spot mailto: later) var r = Math.random(); // roughly 10% raw, 45% hex, 45% dec ch =  ( r > .9  ?	encode[2](ch)   : r > .45 ?	encode[1](ch)   : encode[0](ch) ); } return ch; }); addr = "" + addr + ""; addr = addr.replace(/">.+:/g,"">"); // strip the mailto: from the visible part return addr; } var _UnescapeSpecialChars = function(text) { // // Swap back in all the special characters we've hidden. // text = text.replace(/~E(d+)E/g, function(wholeMatch,m1) { var charCodeToReplace = parseInt(m1); return String.fromCharCode(charCodeToReplace); } ); return text; } var _Outdent = function(text) { // // Remove one level of line-leading tabs or spaces // // attacklab: hack around Konqueror 3.5.4 bug: // "----------bug".replace(/^-/g,"") == "bug" text = text.replace(/^(t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width // attacklab: clean up hack text = text.replace(/~0/g,"") return text; } var _Detab = function(text) { // attacklab: Detab's completely rewritten for speed. // In perl we could fix it by anchoring the regexp with G. // In javascript we're less fortunate. // expand first n-1 tabs text = text.replace(/t(?=t)/g,"    "); // attacklab: g_tab_width // replace the nth with two sentinels text = text.replace(/t/g,"~A~B"); // use the sentinel to anchor our regex so it doesn't explode text = text.replace(/~B(.+?)~A/g, function(wholeMatch,m1,m2) { var leadingText = m1; var numSpaces = 4 - leadingText.length % 4;  // attacklab: g_tab_width // there *must* be a better way to do this: for (var i=0; i<numSpaces; i++) leadingText+=" "; return leadingText; } ); // clean up sentinels text = text.replace(/~A/g,"    ");  // attacklab: g_tab_width text = text.replace(/~B/g,""); return text; } // //  attacklab: Utility functions // var escapeCharacters = function(text, charsToEscape, afterBackslash) { // First we have to escape the escape characters so that // we can build a character class out of them var regexString = "([" + charsToEscape.replace(/([[]])/g,"$1") + "])"; if (afterBackslash) { regexString = "" + regexString; } var regex = new RegExp(regexString,"g"); text = text.replace(regex,escapeCharacters_callback); return text; } var escapeCharacters_callback = function(wholeMatch,m1) { var charCodeToEscape = m1.charCodeAt(0); return "~E"+charCodeToEscape+"E"; } } // end of Showdown.converter // export if (typeof exports != 'undefined') exports.Showdown = Showdown;   No newline at end of file


Question:
Calling on in a component being Shallow Rendered will result in the error being thrown. I set up a test case:
Who would be best to answer this one?
Seems like a valid bug report — thanks and the test case is rad. I may not be able to look at this for a while, unfortunately.
no worries let me know if you need any more info from me. This was on React 14 beta btw (not sure if that matters)
I have the same issue. A simpler example that throws the error:
Any way of getting around this issue? I am currently running into it.
Sorry about the slow response from us (FB) here. I'm the principal author of the shallow render feature, but React core isn't my main gig and I've been swamped with product work. I don't have an offhand answer for you, but I've just put time on my calendar to look into this next week. Jim, Ben, Paul — if by chance one of you happened to take care of this before then, I wouldn't be mad :)
An update on my end. I think that my problem actually went away when I deduped my npm dependencies to remove an extra react version that was causing issues. If this error pops up again I'll let you know. Thanks for the response! On Fri, Aug 28, 2015, 9:38 PM Scott Feeney wrote:
Hmm I am still able to reproduce this issue in my test case after running npm dedupe
Just to clarify, my previous comment wasn't supposed to mean that the issue has gone away. I think I was seeing a separate issue caused by multiple react versions that I misidentified as this issue (I hope that makes sense).
I am getting this error, too. I'm using react v0.14.0-rc1. Looking at the code : Apparently .
Note that shallow rendered components override to return a instead of whatever it normally returns. That still shouldn't be undefined and should have a attribute, though.
I'm experiencing this issue too - your suggestion gets me past the initial error & on to in
Pretty sure this breaks many other things outside of , but I noticed moving to be directly above in fixed this for me.
We are also affected by this issue. I think shallow rendering is a really cool feature for testing, but unfortunately I can't use it for some of our components due to this bug.
I created pull request with a fix that works for me. Can someone else verify?
Any workaround for this issue?
:+1: Having same issue
Just ran into this too :/
Any update on this? I see that 0.14.3 was released, but these kind of tests are still failing.
I think I have to add a test to my pull request before it can be merged, but I did not get around to do it yet.
Closing in favor of which should merge soon.

if (!context) { context = emptyObject; } <del> var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(true); this._render(element, transaction, context); ReactUpdates.ReactReconcileTransaction.release(transaction); </del> <ins> ReactUpdates.batchedUpdates(_batchedRender, this, element, context); </ins> return this.getRenderOutput(); }; <ins> function _batchedRender(renderer, element, context) { var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(true); renderer._render(element, transaction, context); ReactUpdates.ReactReconcileTransaction.release(transaction); } </ins> ReactShallowRenderer.prototype.getRenderOutput = function() { return ( (this._instance && this._instance._renderedComponent &&


Question:
Calling on in a component being Shallow Rendered will result in the error being thrown. I set up a test case:
Who would be best to answer this one?
Seems like a valid bug report — thanks and the test case is rad. I may not be able to look at this for a while, unfortunately.
no worries let me know if you need any more info from me. This was on React 14 beta btw (not sure if that matters)
I have the same issue. A simpler example that throws the error:
Any way of getting around this issue? I am currently running into it.
Sorry about the slow response from us (FB) here. I'm the principal author of the shallow render feature, but React core isn't my main gig and I've been swamped with product work. I don't have an offhand answer for you, but I've just put time on my calendar to look into this next week. Jim, Ben, Paul — if by chance one of you happened to take care of this before then, I wouldn't be mad :)
An update on my end. I think that my problem actually went away when I deduped my npm dependencies to remove an extra react version that was causing issues. If this error pops up again I'll let you know. Thanks for the response! On Fri, Aug 28, 2015, 9:38 PM Scott Feeney wrote:
Hmm I am still able to reproduce this issue in my test case after running npm dedupe
Just to clarify, my previous comment wasn't supposed to mean that the issue has gone away. I think I was seeing a separate issue caused by multiple react versions that I misidentified as this issue (I hope that makes sense).
I am getting this error, too. I'm using react v0.14.0-rc1. Looking at the code : Apparently .
Note that shallow rendered components override to return a instead of whatever it normally returns. That still shouldn't be undefined and should have a attribute, though.
I'm experiencing this issue too - your suggestion gets me past the initial error & on to in
Pretty sure this breaks many other things outside of , but I noticed moving to be directly above in fixed this for me.
We are also affected by this issue. I think shallow rendering is a really cool feature for testing, but unfortunately I can't use it for some of our components due to this bug.
I created pull request with a fix that works for me. Can someone else verify?
Any workaround for this issue?
:+1: Having same issue
Just ran into this too :/
Any update on this? I see that 0.14.3 was released, but these kind of tests are still failing.
I think I have to add a test to my pull request before it can be merged, but I did not get around to do it yet.
Closing in favor of which should merge soon.

expect(result.props.className).toEqual('clicked'); });  it('can setState in componentWillMount when shallow rendering', function() { var SimpleComponent = React.createClass({ componentWillMount() { this.setState({groovy: 'doovy'}); }, render() { return
{this.state.groovy}
; }, }); var shallowRenderer = ReactTestUtils.createRenderer(); var result = shallowRenderer.render(<SimpleComponent />); expect(result).toEqual(
doovy
); });  it('can pass context when shallowly rendering', function() { var SimpleComponent = React.createClass({ contextTypes: {


Question:
I'm trying to use the attribute as described here: Unfortunately, sometimes returns , when is called multiple times. MWE:
This is intended – when the component is unmounted or the ref changes, the old ref gets called with null before it's called with the new value (or the same component instance again). If you store the instance on your component, this effect should be unobservable as long as your component is mounted, but it prevents memory leaks. If you want to do something with in the ref handler, you should check if it's null.
Thanks for the information! I think this might be worth mentioning in the docs.

1. Assign a `ref` attribute to anything returned from `render` such as: <del> ```html <input ref="myInput" /> ``` </del> <ins> ```html <input ref="myInput" /> ``` </ins> 2. In some other code (typically event handler code), access the **backing instance** via `this.refs` as in: <del> ```javascript this.refs.myInput ``` </del> <ins> ```javascript this.refs.myInput ``` </ins> You can access the component's DOM node directly by calling `React.findDOMNode(this.refs.myInput)`.


Question:
I'm trying to use the attribute as described here: Unfortunately, sometimes returns , when is called multiple times. MWE:
This is intended – when the component is unmounted or the ref changes, the old ref gets called with null before it's called with the new value (or the same component instance again). If you store the instance on your component, this effect should be unobservable as long as your component is mounted, but it prevents memory leaks. If you want to do something with in the ref handler, you should check if it's null.
Thanks for the information! I think this might be worth mentioning in the docs.

It's as simple as assigning a `ref` attribute to anything returned from `render` such as: <del> ```html <input ref={ function(component){ React.findDOMNode(component).focus();} } /> ``` </del> <ins> ```html render: function() { return <TextInput ref={(c) => this._input = c} } />; }, componentDidMount: function() { this._input.focus(); }, ``` or ```html render: function() { return ( <TextInput ref={function(input) { if (input != null) { input.focus(); } }} /> ); }, ``` Note that when the referenced component is unmounted and whenever the ref changes, the old ref will be called with `null` as an argument. This prevents memory leaks in the case that the instance is stored, as in the first example. Note that when writing refs with inline function expressions as in the examples here, React sees a different function object each time so on every update, ref will be called with `null` immediately before it's called with the component instance. </ins> ## Completing the Example


Question:
Issue with the reproducible example . Full build contains non-standard iterator. Theoretical, it can be to ES in the future, it can be (or already ) to another libraries. should take into account possibility iterable numbers.
Well, the good news is that if this is already on enough pages, then introducing iterable numbers may not be web compatible anyway. I think that this is only in DEV only code. Unfortunately a lot of pages incorrectly uses the DEV build of React.
"good news"

* @param {*} parentType node's parent's type. */ function validateChildKeys(node, parentType) { <ins> if (typeof node !== 'object') { return; } </ins> if (Array.isArray(node)) { for (var i = 0; i < node.length; i++) { var child = node[i];


Question:
Issue with the reproducible example . Full build contains non-standard iterator. Theoretical, it can be to ES in the future, it can be (or already ) to another libraries. should take into account possibility iterable numbers.
Well, the good news is that if this is already on enough pages, then introducing iterable numbers may not be web compatible anyway. I think that this is only in DEV only code. Unfortunately a lot of pages incorrectly uses the DEV build of React.
"good news"

expect(console.error.argsForCall.length).toBe(0); }); <ins> it('should not enumerate enumerable numbers (#4776)', function() { /*eslint-disable no-extend-native */ Number.prototype['@@iterator'] = function() { throw new Error('number iterator called'); }; /*eslint-enable no-extend-native */ try { void ( <div> {5} {12} {13} </div> ); } finally { delete Number.prototype['@@iterator']; } }); </ins> });


Question:
Issue with the reproducible example . Full build contains non-standard iterator. Theoretical, it can be to ES in the future, it can be (or already ) to another libraries. should take into account possibility iterable numbers.
Well, the good news is that if this is already on enough pages, then introducing iterable numbers may not be web compatible anyway. I think that this is only in DEV only code. Unfortunately a lot of pages incorrectly uses the DEV build of React.
"good news"

); }); <ins> it('should not enumerate enumerable numbers (#4776)', function() { /*eslint-disable no-extend-native */ Number.prototype['@@iterator'] = function() { throw new Error('number iterator called'); }; /*eslint-enable no-extend-native */ try { var instance = ( <div> {5} {12} {13} </div> ); var traverseFn = jasmine.createSpy(); traverseAllChildren(instance.props.children, traverseFn, null); expect(traverseFn.calls.length).toBe(3); expect(traverseFn).toHaveBeenCalledWith( null, 5, '.0' ); expect(traverseFn).toHaveBeenCalledWith( null, 12, '.1' ); expect(traverseFn).toHaveBeenCalledWith( null, 13, '.2' ); } finally { delete Number.prototype['@@iterator']; } }); </ins> });


Question:
I'm still quite new to React, but I guess this is a bug in the React framework introduced in 0.14. Seems like all listeners attached with JSX are broken in native Android browser for Android LTE 4.3, when compiling code with NODEENV development. Everything works fine with NODEENV production compilation. I've tracked it down to being related to ReactErrorUtils, that in development mode do guards on events. The source code: If I simply remove that part of code after compiling the React app, the listeners starts working again. I guess this is not intentional, but instead a bug? To reproduce problem in Android LTE 4.3 (code that works in e.g. Chrome Browser): Using the pre-compiled Getting Started lib
Hm. That's unfortunate. I guess we'll try to find a feature test for this and patch it.

* real browser event. */ if (typeof window !== 'undefined' && <del> typeof window.dispatchEvent === 'function' && typeof Event === 'function') { </del> <ins> typeof window.dispatchEvent === 'function') { </ins> var fakeNode = document.createElement('react'); ReactErrorUtils.invokeGuardedCallback = function(name, func, a, b) { var boundFunc = func.bind(null, a, b); fakeNode.addEventListener(name, boundFunc, false); <del> fakeNode.dispatchEvent(new Event(name)); </del> <ins> var evt = document.createEvent('Event'); evt.initEvent(name, false, false); fakeNode.dispatchEvent(evt); </ins> fakeNode.removeEventListener(name, boundFunc, false); }; }


Question:
Oddly enough, the two examples below are not equal as I thought they would be. The error is as follows: I'm assuming that is because the function as assuming an optional callback. But I would expect your code to do a typeof check, and not just a defined check. Can we change it to do a check? I can submit a PR.
Well, the typecheck happens with that exact typecheck, just not directly in forceupdate. Here's forceUpdate: and where the invariant happens: FWIW, is not the same as due to an important distinction. The former makes a new function that is bound but it will pass along any arguments. The latter also is a new function but it explicitly doesn't pass any arguments. So the former gets the event passed along while the latter just drops it. The event isn't callable, thus the invariant. We need to do the typecheck so I guess we could discuss whether we keep it as an invariant or a warning. But it is a misuse of the API.
To follow up on that, the more "equal" thing would be: Which should throw the same error that you see with your first bind example.
Correct. Yes, I understand all of that. It wasn't toooo hard for me to understand what is happening. And it is a misuse of the API. So I guess, I would consider adding the argument type to the invariant to help make it clearer? Something more like: It's kind of moot. But It confused me for a second. Might confuse others also.

invariant( typeof callback === 'function', 'enqueueCallback(...): You called `setProps`, `replaceProps`, ' + <del> '`setState`, `replaceState`, or `forceUpdate` with a callback that ' + 'isn't callable.' </del> <ins> '`setState`, `replaceState`, or `forceUpdate` with a callback of type ' + '%s. A function is expected', typeof callback === 'object' && Object.keys(callback).length && Object.keys(callback).length < 20 ? typeof callback + ' (keys: ' + Object.keys(callback) + ')' : typeof callback </ins> ); var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);


Question:
Oddly enough, the two examples below are not equal as I thought they would be. The error is as follows: I'm assuming that is because the function as assuming an optional callback. But I would expect your code to do a typeof check, and not just a defined check. Can we change it to do a check? I can submit a PR.
Well, the typecheck happens with that exact typecheck, just not directly in forceupdate. Here's forceUpdate: and where the invariant happens: FWIW, is not the same as due to an important distinction. The former makes a new function that is bound but it will pass along any arguments. The latter also is a new function but it explicitly doesn't pass any arguments. So the former gets the event passed along while the latter just drops it. The event isn't callable, thus the invariant. We need to do the typecheck so I guess we could discuss whether we keep it as an invariant or a warning. But it is a misuse of the API.
To follow up on that, the more "equal" thing would be: Which should throw the same error that you see with your first bind example.
Correct. Yes, I understand all of that. It wasn't toooo hard for me to understand what is happening. And it is a misuse of the API. So I guess, I would consider adding the argument type to the invariant to help make it clearer? Something more like: It's kind of moot. But It confused me for a second. Might confuse others also.

invariant( typeof callback === 'function', 'enqueueCallback(...): You called `setProps`, `replaceProps`, ' + <del> '`setState`, `replaceState`, or `forceUpdate` with a callback that ' + 'isn't callable.' </del> <ins> '`setState`, `replaceState`, or `forceUpdate` with a callback of type ' + '%s. A function is expected', typeof callback === 'object' && Object.keys(callback).length && Object.keys(callback).length < 20 ? typeof callback + ' (keys: ' + Object.keys(callback) + ')' : typeof callback </ins> ); if (internalInstance._pendingCallbacks) { internalInstance._pendingCallbacks.push(callback);


Question:
Introduction I found a bug on resolving default props when component props have property with HTMLAllCollection. React ignores value and uses default value (it should take that collection). isIE9OrLower (look at the end of topic) is simple function to test Internet Explorer in version 9 or lower. Simple use case: Chrome/Firefox/modern IE (yeah, i know....) returns HTMLAllColection (as ), but old IE returns true. HTMLAllCollection is weird collection: Source of bug and fix release: 0.14.7 file: line: 9149 code (bug): if (typeof props[propName] === 'undefined') { code (fix): if (props[propName] === undefined) { Btw, === undefined is faster than typeof === 'undefined' on Chrome and IE. Reproduction Open in Firefox / Chrome / IE10 or higher. Fiddle: Raw: Workaround Change to
Haha, browsers suck. Want to submit a PR? It looks like we do that thing unnecessarily in a few places in the React codebase. Note that we do need to use typeof if the binding might not exist, so we need to look at each case individually to make sure the change is valid there.
I went through the react repo configuration (build, tests execution). Fix didn't break anything, but so far I didn't find solution to make good test (phantomjs doesn't have property). (HTMLAllCollection) is probably the only exception to the typeof and is not a html standard. Any suggestions to the unit test for this fix/improvement?
As long as you tested it manually and it worked in IE, I think we can accept this change without a unit test.
Fixed in
lol what even
I can't reproduce being in IE 8 or IE 9 on Windows 7, either in standards or quirks mode. What am I missing?
IE < 10 is fine. Problem occurs when using modern browser like Chrome, Firefox, IE = 10 or Edge. They returns . component prop as in Chrome. will compare your prop with default prop (which is ). on Chrome is so React treats it as missing value. Default prop () will be selected. the end, prop will be always set to on modern browser in this case.
Oh! Sorry I totally misread. Yes I can repro this.

*/ function createLinkTypeChecker(linkType) { var shapes = { <del> value: typeof linkType === 'undefined' ? </del> <ins> value: linkType === undefined ? </ins> React.PropTypes.any.isRequired : linkType.isRequired, requestChange: React.PropTypes.func.isRequired,


Question:
Introduction I found a bug on resolving default props when component props have property with HTMLAllCollection. React ignores value and uses default value (it should take that collection). isIE9OrLower (look at the end of topic) is simple function to test Internet Explorer in version 9 or lower. Simple use case: Chrome/Firefox/modern IE (yeah, i know....) returns HTMLAllColection (as ), but old IE returns true. HTMLAllCollection is weird collection: Source of bug and fix release: 0.14.7 file: line: 9149 code (bug): if (typeof props[propName] === 'undefined') { code (fix): if (props[propName] === undefined) { Btw, === undefined is faster than typeof === 'undefined' on Chrome and IE. Reproduction Open in Firefox / Chrome / IE10 or higher. Fiddle: Raw: Workaround Change to
Haha, browsers suck. Want to submit a PR? It looks like we do that thing unnecessarily in a few places in the React codebase. Note that we do need to use typeof if the binding might not exist, so we need to look at each case individually to make sure the change is valid there.
I went through the react repo configuration (build, tests execution). Fix didn't break anything, but so far I didn't find solution to make good test (phantomjs doesn't have property). (HTMLAllCollection) is probably the only exception to the typeof and is not a html standard. Any suggestions to the unit test for this fix/improvement?
As long as you tested it manually and it worked in IE, I think we can accept this change without a unit test.
Fixed in
lol what even
I can't reproduce being in IE 8 or IE 9 on Windows 7, either in standards or quirks mode. What am I missing?
IE < 10 is fine. Problem occurs when using modern browser like Chrome, Firefox, IE = 10 or Edge. They returns . component prop as in Chrome. will compare your prop with default prop (which is ). on Chrome is so React treats it as missing value. Default prop () will be selected. the end, prop will be always set to on modern browser in this case.
Oh! Sorry I totally misread. Yes I can repro this.

var initialState = this.getInitialState ? this.getInitialState() : null; if (__DEV__) { // We allow auto-mocks to proceed as if they're returning null. <del> if (typeof initialState === 'undefined' && </del> <ins> if (initialState === undefined && </ins> this.getInitialState._isMockFunction) { // This is probably bad practice. Consider warning here and // deprecating this convenience.