Pen Settings

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

You're using npm packages, so we've auto-selected Babel for you here, which we require to process imports and make it all work. If you need to use a different JavaScript preprocessor, remove the packages in the npm tab.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Use npm Packages

We can make npm packages available for you to use in your JavaScript. We use webpack to prepare them and make them available to import. We'll also process your JavaScript with Babel.

⚠️ This feature can only be used by logged in users.

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

HTML Settings

Here you can Sed posuere consectetur est at lobortis. Donec ullamcorper nulla non metus auctor fringilla. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec id elit non mi porta gravida at eget metus. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.

            
              $red: #F44336;
$green: #4CAF50;
$yellow: #FFC107;

html {
  box-sizing: border-box;
}

*, *:before, *:after {
  box-sizing: inherit;
}

body {
  color: #212121;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  background: #E0F7FA;
  line-height: 1.65;
}

code {
  font-family: SFMono-Regular,Consolas,"Liberation Mono",Menlo,Courier,monospace;
  background: #efefef;
  display: inline-block;
  padding: 1px 3px;
  border-radius: 2px;
}

a {
  color: #03A9F4;
  text-decoration: none;
  &:hover {
    text-decoration: underline;
  }
}

h1 {
  margin: 0;
}

input {
  width: 100%;
  padding: 5px;
  height: 30px;
  border-radius: 4px;
  border: 1px solid #ccc;
}

.fake-labels-lol-ship-it {
  font-weight: bold;
  text-transform: uppercase;
  font-size: .8em;
  margin-bottom: 5px;
}

.notes {
  border-top: 1px solid #eaeaea;
  font-size: .9em;
  margin-top: 30px;
}

.user-form-wrap {
  display: flex;
  width: 100vw;
  min-height: 100vh;
}

.user-form {
  border-radius: 4px;
  padding: 30px;
  background: white;
  max-width: 700px;
  margin: auto;
  box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
}

.pens-list {
  padding: 0;
  box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
  max-width: 700px;
  margin: 40px auto;
}

.pen {
  border-left: 10px solid;
  display: block;
  &--safe {
    border-left-color: $green;
    background: lighten($green, 30%);
    &:hover {
      background: lighten($green, 20%);
    }
  }
  &--unsafe {
    border-left-color: $red;
    background: lighten($red, 25%);
    &:hover {
      background: lighten($red, 15%);
    }
  }
  &--error, &--warn {
    border-left-color: $yellow;
    background:lighten($yellow, 35%);
    &:hover {
      background:lighten($yellow, 25%);
    }
  }
}

.pen-info-breakdown {
  display: flex;
  justify-content: space-between;
  cursor: pointer;
}

.pen-title {
  font-size: 1.2em;
  padding: 0 20px;
  margin: auto 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-weight: normal;
}

.pen-status-chunk {
  background: #F5F5F5;
  display: flex;
}

.pen-link {
  display: inline-block;
  margin-bottom: 10px;
}

.pen-details {
  background: white;
  flex-basis: 100%;
  padding: 20px;
  overflow: auto;
  h3 {
    text-transform: uppercase;
    font-size: 1em;
    margin-top: 2em;
  }
}

.pen-message-details {
  overflow-wrap: break-word;
  word-wrap: break-word;
  font-family: SFMono-Regular,Consolas,"Liberation Mono",Menlo,Courier,monospace;  
}

.status {
  padding: 10px;
  &--safe {
    color: $green;
  }
  &--error, &--warn {
    color: $yellow;
  }
  &--unsafe {
    color: $red;
  }
}

.status__title {
  font-weight: bold;
  font-size: .6em;
  margin-bottom: 7.5px;
  text-transform: uppercase;
}

.status__state {
  text-align: center;
}

.input--invalid {
  border: 3px solid red;
}
            
          
!
            
              /*
Hi friends!

1. Read the description for a breakdown of things this can/can't catch

2. If there's something I've missed that can be handled trivially lemme know

3. CodePen will upgrade insecure requests if possible, this doesn't so it'll throw
   false positives.

4. This is just a quick tool aka hacks on hacks on hacks so don't look too hard
   at the code thaaanks. MVP with a capital M (and also V and P but whatevs).

5. Big thanks (again) to @natewiley (https://codepen.io/natewiley/) for the API.
   Everyone go follow him and send him nice tweets. API link here: http://cpv2api.com/
*/

// these helpers are set up in an external Pen 'cause they're real ugly:
// https://codepen.io/alexzaworski/pen/2aaaedbc00a5e061e526485348f9b830
const cpv2 = new CPV2(); // world's tiniest wrapper for cpv2api
const safetyChecker = new SafetyChecker(); // the thing that handles actual analysis

// the rest is just front-endy stuff
class UserForm extends React.Component {
  constructor () {
    super();
    this.state = {
      val: ''
    };
  }

  updateVal (e) {
    this.setState({
      val: e.target.value
    });
  }

  handleChange (e) {
    this.updateVal(e);
    if (this.props.onChange) this.props.onChange();
  }

  handleSubmit (e) {
    e.preventDefault();
    this.props.onSubmit(this.state.val);
  }

  render () {
    return (
      <div className='user-form-wrap'>
        <div className='user-form'>
          <h1>SSL Checker Thingo</h1>
          <p><a target='_blank' href='https://blog.codepen.io/2017/03/31/codepen-going-https/'>CodePen is going HTTPS</a> on June 1, 2017. This'll pull your Pens and (try to) determine if they're loading any insecure resources so you can fix 'em.</p>
          <p>The checks are pretty naive but this should at least get you off to a decent start.</p>
          <div className='fake-labels-lol-ship-it'>Username:</div>
          <form onSubmit={e => this.handleSubmit(e)}>
            <input
              required
              className='input'
              type='text'
              value={this.state.val}
              onChange={e => this.handleChange(e)}
              />
          </form>
          {this.props.invalid ? <div className='form-error'>no Pens found :(</div> : null}
          <div className='notes'>
            <h4>Important Notes:</h4>
            <ul>
              <li>This makes no attempts to upgrade <i>any</i> resources from http to https (too tough for a number of reasons). When the switch happens, CodePen will upgrade resources when possible (so expect some false positives).</li>
              <li>Read the description for a better breakdown of what this can/can't catch.</li>
              <li>If you have a ton of public Pens this might take a sec.</li>
              <li>Powered by Nate Wiley's <a target='_blank' href='http://cpv2api.com/'>cpv2api &lt;3</a></li>
            </ul>
          </div>
        </div>
      </div>
    );
  }
}

class PenDetail extends React.Component {
  render () {
    const { title, details } = this.props;
    const { status } = details;
    const classes = classNames(
      'status',
      {'status--error': status === 'error'},
      {'status--safe': status === 'safe'},
      {'status--warn': status === 'warn'},
      {'status--unsafe': status === 'unsafe'}
    );

    let icon;
    switch (status) {
      case 'warn':
        icon = '😕';
        break;
      case 'safe':
        icon = '👌';
        break;
      case 'unsafe':
        icon = '👎';
        break;
      case 'error':
        icon = '⚠️';
        break;
    }

    return (
      <div className={classes}>
        <div className='status__title'>{title}</div>
        <div className='status__state'>{icon}</div>
      </div>
    );
  }
}

class PenMessage extends React.Component {
  render () {
    const { title, message, data = false } = this.props;
    return (
      <div>
        <h3>{title}</h3>
        <p dangerouslySetInnerHTML={{__html: message}} />
        { data ? <PenMessageData data={data} /> : null }
      </div>
    );
  }
}

class PenMessageData extends React.Component {
  render () {
    let { data } = this.props;
    if (Array.isArray(data)) {
      data = <ul>{data.map(datum => <li>{datum}</li>)}</ul>;
    }
    return (
      <div>
        <h4>Info:</h4>
        <div className='pen-message-details'>{data}</div>
      </div>
    );
  }
}

class PenLink extends React.Component {
  render () {
    return (
      <a
        href={this.props.href}
        target='_blank'
        className='pen-link'
        >
        {this.props.children}
      </a>
    );
  }
}

class Pen extends React.Component {
  constructor () {
    super();
    this.state = {
      expanded: false,
      ready: false
    };
  }

  componentDidMount () {
    const { slug } = this.props;
    safetyChecker.checkPen(slug).then(details => {
      let status = 'safe';
      for (const detail in details) {
        if (details[detail].status !== 'safe') {
          status = details[detail].status;
          break;
        }
      }
      this.setState({
        ready: true,
        details,
        status
      });
    });
  }

  handleClick () {
    const { expanded } = this.state;
    this.setState({
      expanded: !expanded
    });
  }

  stopProp (e) {
    e.stopPropagation();
  }

  render () {
    const { details, ready } = this.state;
    const { title, link } = this.props;
    if (!ready) return null;
    const secureLink = link.replace('http://', 'https://');
    let penDetails = [];
    let expandedInfo = [
      <div>
        <PenLink href={link}>🔗 <code>http</code> link</PenLink>
        <br />
        <PenLink href={secureLink}>🔒 <code>https</code> link</PenLink>
      </div>
    ];

    for (const title in details) {
      const detail = details[title];
      penDetails.push(
        <PenDetail
          title={title}
          details={detail}
          />
      );
      const { status, message, data } = detail;
      expandedInfo.push(
        <PenMessage
          title={title}
          status={status}
          message={message}
          data={data}
          />
      );
    }

    const expanded = (
      <div className='pen-details'>
        {expandedInfo}
      </div>
    );

    const classes = classNames(
      'pen',
      {'pen--error': (this.state.status === 'error')},
      {'pen--warn': (this.state.status === 'warn')},
      {'pen--safe': (this.state.status === 'safe')},
      {'pen--unsafe': (this.state.status === 'unsafe')}
    );
    return (
      <li className={classes}>
        <div className='pen-info-breakdown' onClick={() => this.handleClick()}>
          <h2 className='pen-title' dangerouslySetInnerHTML={{__html: title}} />
          <div className='pen-status-chunk'>
            {penDetails}
          </div>
        </div>
        {this.state.expanded ? expanded : null}
      </li>
    );
  }
}

class PenList extends React.Component {
  render () {
    const { pens } = this.props;
    const list = pens.map(pen => {
      return <Pen key={pen.slug} {...pen} />;
    });
    return (
      <div>
        <ul className='pens-list'>{list}</ul>
      </div>
    );
  }
}

class App extends React.Component {
  constructor () {
    super();
    this.state = {
      pens: [],
      invalid: false
    };
  }

  updateSlugs (pen) {
    const { pens } = this.state;
    pens.push(pen);
    this.setState({
      pens
    });
  }

  fetchPens (username) {
    cpv2.getPensForUser(username, slug => {
      this.updateSlugs(slug);
    }).catch(() => {
      this.setState({
        invalid: true
      });
    });
  }

  render () {
    const { pens } = this.state;
    const userForm = (
      <UserForm
        onChange={() => { this.setState({invalid: false}); }}
        invalid={this.state.invalid}
        onSubmit={username => this.fetchPens(username)}
        />
    );
    const penList = (
      <PenList pens={pens} />
    );
    return (
      <div>
        {pens.length > 0 ? penList : userForm}
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));

            
          
!
999px
🕑 One or more of the npm packages you are using needs to be built. You're the first person to ever need it! We're building it right now and your preview will start updating again when it's ready.

Console