├── .babelrc ├── .gitignore ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── screenshot.png ├── src ├── actions │ └── index.js ├── components │ ├── app.js │ └── regex.js ├── data │ └── index.js ├── index.js ├── reducers │ └── index.js └── sass │ └── style.scss ├── test ├── components │ └── app_test.js └── test_helper.js ├── trending.png └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react", "es2015", "stage-1"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | bundle.js 3 | npm-debug.log 4 | index.html 5 | .htaccess 6 | # IntelliJ 7 | *.iml 8 | /.idea 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Luke Haas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Regex Hub 2 | 3 | The project is hosted here: https://lukehaas.me/projects/regexhub 4 | 5 | ### Adding a new pattern 6 | 7 | The patterns are contained in the file src/data/index.js 8 | 9 | Each pattern requires the following details: 10 | - name 11 | - regex 12 | - description 13 | - tags 14 | 15 | 16 | 17 | 18 | ### License 19 | 20 | [MIT License](https://raw.githubusercontent.com/lukehaas/RegexHub/refs/heads/master/LICENSE) 21 | 22 | Browser testing via [![lambda test](https://www.lambdatest.com/support/img/logo.svg)](https://www.lambdatest.com/) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "redux-simple-starter", 3 | "version": "1.0.0", 4 | "description": "Simple starter package for Redux with React and Babel support", 5 | "main": "index.js", 6 | "repository": "git@github.com:StephenGrider/ReduxSimpleStarter.git", 7 | "scripts": { 8 | "start": "node ./node_modules/webpack-dev-server/bin/webpack-dev-server.js", 9 | "test": "mocha --compilers js:babel-core/register --require ./test/test_helper.js --recursive ./test", 10 | "test:watch": "npm run test -- --watch", 11 | "build": "webpack -p --define process.env.NODE_ENV='\"production\"' --progress --colors" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "devDependencies": { 16 | "babel-core": "^6.2.1", 17 | "babel-loader": "^6.2.0", 18 | "babel-preset-es2015": "^6.1.18", 19 | "babel-preset-react": "^6.1.18", 20 | "chai": "^3.5.0", 21 | "chai-jquery": "^2.0.0", 22 | "css-loader": "^0.26.0", 23 | "jquery": "^3.3.1", 24 | "jsdom": "^8.1.0", 25 | "mocha": "^2.4.5", 26 | "node-sass": "^4.13.1", 27 | "react-addons-test-utils": "^0.14.7", 28 | "sass-loader": "^4.0.2", 29 | "style-loader": "^0.13.1", 30 | "webpack": "^1.12.9", 31 | "webpack-dev-server": "^1.14.0" 32 | }, 33 | "dependencies": { 34 | "babel-preset-stage-1": "^6.1.18", 35 | "lodash": "^4.17.19", 36 | "react": "^0.14.3", 37 | "react-dom": "^0.14.3", 38 | "react-redux": "^4.0.0", 39 | "react-router": "^2.0.1", 40 | "redux": "^3.0.4" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehaas/RegexHub/d1d9f19d259745dfdd21935b9ef3e747b62b9bfb/screenshot.png -------------------------------------------------------------------------------- /src/actions/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehaas/RegexHub/d1d9f19d259745dfdd21935b9ef3e747b62b9bfb/src/actions/index.js -------------------------------------------------------------------------------- /src/components/app.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Regex from './regex'; 3 | 4 | import { patterns } from '../data'; 5 | 6 | export default class App extends Component { 7 | 8 | renderPatterns() { 9 | var i = 0,z = 0, 10 | sortedData = [[],[],[]], 11 | index = 0; 12 | 13 | for(var j = 0;j < patterns.length;j++) { 14 | if(index%3===0) { 15 | index = 0; 16 | } 17 | 18 | sortedData[index].push(patterns[j]); 19 | index++; 20 | } 21 | 22 | return sortedData.map((data) => { 23 | 24 | i++; 25 | var panels = data.map((props) => { 26 | z++; 27 | return 28 | }); 29 | return ( 30 |
31 | {panels} 32 |
33 | ); 34 | }); 35 | } 36 | render() { 37 | return ( 38 |
{this.renderPatterns()}
39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/components/regex.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | //import ReactCSSTransitionGroup from 'react-addons-css-transition-group'; 3 | 4 | class Regex extends Component { 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | editableRegex: props.regex.toString(), 10 | test:"", 11 | output:[], 12 | open:false 13 | }; 14 | } 15 | handleRegexChange(val) { 16 | 17 | 18 | this.setState({ 19 | editableRegex:val 20 | },this.evaluateOutput); 21 | 22 | } 23 | handleTestChange(val) { 24 | 25 | this.setState({ 26 | test:val 27 | },this.evaluateOutput); 28 | 29 | } 30 | evaluateOutput() { 31 | 32 | if(this.state.test.length>0 && this.state.editableRegex.length>0) { 33 | var editableRegex = this.state.editableRegex; 34 | 35 | 36 | var flags = /[^\/]+$/.exec(editableRegex); 37 | if(!flags) { 38 | flags = ""; 39 | } 40 | editableRegex = editableRegex.replace(/[^\/]+$/, ""); 41 | 42 | var reg = new RegExp(eval(editableRegex),flags); 43 | 44 | var out; 45 | try { 46 | out = reg.exec(this.state.test); 47 | } catch(e) { 48 | out = ["Invalid regex"]; 49 | } 50 | 51 | if(!out) { 52 | 53 | out = ["null"]; 54 | } else if(out[0]==="") { 55 | out = ["null"]; 56 | } else { 57 | var o = []; 58 | o[0] = out[0]; 59 | for(var k = 1;k < out.length;k++) { 60 | 61 | if(typeof out[k] === "string") { 62 | o.push(out[k]); 63 | } 64 | } 65 | out = o; 66 | } 67 | 68 | this.setState({ 69 | output:out 70 | }); 71 | } else { 72 | this.setState({ 73 | output:[] 74 | }); 75 | } 76 | 77 | 78 | } 79 | handleOpen(e) { 80 | e.preventDefault(); 81 | 82 | if(this.state.open) { 83 | this.setState({ 84 | open:false 85 | }); 86 | } else { 87 | this.setState({ 88 | open:true 89 | }); 90 | } 91 | } 92 | render() { 93 | var output =
Output:
; 94 | return ( 95 |
96 | 97 |
98 |

this.handleOpen(e)}>{this.props.name}

99 | 100 | 101 |

{this.props.description}

102 | 103 |
104 |
105 | this.handleRegexChange(e.target.value)} /> 106 |
107 |
108 |
109 | this.handleTestChange(e.target.value)} /> 110 |
111 | {this.state.output.length>0 ? output : ""} 112 |
113 |
114 | ); 115 | } 116 | } 117 | 118 | export default Regex; 119 | 120 | /* 121 | 122 | run: webpack -p 123 | to generate bundle.js 124 | 125 | Add reset button - once regex is edited the button will restore it? 126 | 127 | 128 | add search 129 | add details at the bottom for pull requests 130 | 131 | https://projects.lukehaas.me/regexhub//?_escaped_fragment_\= 132 | 133 | */ 134 | -------------------------------------------------------------------------------- /src/data/index.js: -------------------------------------------------------------------------------- 1 | export const patterns = [{ 2 | name:"Date in format dd/mm/yyyy", 3 | regex:/^(0?[1-9]|[12][0-9]|3[01])([ /\-])(0?[1-9]|1[012])\2([0-9][0-9][0-9][0-9])(([ -])([0-1]?[0-9]|2[0-3]):[0-5]?[0-9]:[0-5]?[0-9])?$/, 4 | description:"Will match dates with dashes, slashes or with spaces (e.g. dd-mm-yyyy dd/mm/yyyy dd mm yyyy), and optional time separated by a space or a dash (e.g. dd-mm-yyyy-hh:mm:ss or dd/mm/yyyy hh:mm:ss).", 5 | tags:"date,time" 6 | }, 7 | { 8 | name:"Time in 24-hour format", 9 | regex:/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/, 10 | description: "Match times in 24 hour format", 11 | tags:"date,time" 12 | }, 13 | { 14 | name: "Date and time in ISO-8601 format", 15 | regex: /^(?![+-]?\d{4,5}-?(?:\d{2}|W\d{2})T)(?:|(\d{4}|[+-]\d{5})-?(?:|(0\d|1[0-2])(?:|-?([0-2]\d|3[0-1]))|([0-2]\d{2}|3[0-5]\d|36[0-6])|W([0-4]\d|5[0-3])(?:|-?([1-7])))(?:(?!\d)|T(?=\d)))(?:|([01]\d|2[0-4])(?:|:?([0-5]\d)(?:|:?([0-5]\d)(?:|\.(\d{3})))(?:|[zZ]|([+-](?:[01]\d|2[0-4]))(?:|:?([0-5]\d)))))$/, 16 | description: "Will match a valid date and times in the ISO-8601 format, excludes durations.", 17 | tags: "date,time" 18 | }, 19 | { 20 | name:"HTML tags", 21 | regex:/^<([a-z1-6]+)([^<]+)*(?:>(.*)<\/\1>| *\/>)$/, 22 | description:"Match opening and closing HTML tags with content between", 23 | tags:"markup,xml,html" 24 | }, 25 | { 26 | name:"Username", 27 | regex:/^[a-zA-Z0-9_-]{3,16}$/, 28 | description:"A string between 3 and 16 characters, allowing alphanumeric characters and hyphens and underscores", 29 | tags:"username,validation" 30 | }, 31 | { 32 | name:"Hex Color Value", 33 | regex:/^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/, 34 | description:"RGB hex colors", 35 | tags:"hex,color" 36 | }, 37 | { 38 | name:"URL Slug", 39 | regex:/^[a-z0-9-]+$/, 40 | description:"Match valid URL slugs", 41 | tags:"URL" 42 | }, 43 | { 44 | name:"Email", 45 | regex:/^.+@.+$/, 46 | description:"Verify that there is an @ symbol with something before it", 47 | tags:"email,validation" 48 | }, 49 | { 50 | name:"SRC of image tag", 51 | regex:/^<\s*img[^>]+src\s*=\s*(["'])(.*?)\1[^>]*>$/, 52 | description:"Match the src attribute of an HTML image tag", 53 | tags:"html,tag,image" 54 | }, 55 | { 56 | name:"URL", 57 | regex:/^((https?|ftp|file):\/\/)?([\da-z-]+\.)+([a-z]{2,6})([\/\w \.-]*)*\/?$/ 58 | description:"Match URL with optional protocol", 59 | tags:"url,address,http" 60 | }, 61 | { 62 | name:"IPv4 Address", 63 | regex:/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/, 64 | description:"Match IP v4 addresses", 65 | tags:"tcpip,internet,address" 66 | }, 67 | { 68 | name:"IPv6 Address", 69 | regex:/^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/, 70 | description: "Match IP v6 addresses", 71 | tags:"tcpip,internet,address" 72 | }, 73 | { 74 | name:"JWT", 75 | regex:/^[A-Za-z0-9-_=]+\.[A-Za-z0-9-_=]+\.?[A-Za-z0-9-_.+/=]*$/, 76 | description: "JSON Web Token (encoded)", 77 | tags:"www" 78 | }, 79 | { 80 | name:"Positive Integer", 81 | regex:/^\d+$/, 82 | descriptions:"Match whole numbers above zero", 83 | tags:"number" 84 | }, 85 | { 86 | name:"Negative Integer", 87 | regex:/^-\d+$/, 88 | description:"Match whole numbers below zero", 89 | tags:"number" 90 | }, 91 | { 92 | name:"Integer", 93 | regex:/^-?\d+$/, 94 | description:"Match whole numbers, above or below zero", 95 | tags:"number" 96 | }, 97 | { 98 | name:"Positive number", 99 | regex:/^\d*\.?\d+$/, 100 | description:"Match integers or floats that are positive", 101 | tags:"float" 102 | }, 103 | { 104 | name:"Negative number", 105 | regex:/^-\d*\.?\d+$/, 106 | description:"Match integers or floats that are negative", 107 | tags:"float" 108 | }, 109 | { 110 | name:"Positive or negative number", 111 | regex:/^-?\d*\.?\d+$/, 112 | description:"Match integers or floats that are positive or negative", 113 | tags:"float" 114 | }, 115 | { 116 | name:"Phone number", 117 | regex:/^\+?(\d.*){3,}$/, 118 | description:"Match phone numbers at least 3 digits long", 119 | tags:"validation" 120 | }, 121 | { 122 | name:"New line", 123 | regex:/[\r\n]|$/, 124 | description:"Match new lines within text", 125 | tags:"text" 126 | }, 127 | { 128 | name:"ID of Youtube video", 129 | regex:/https?:\/\/(?:youtu\.be\/|(?:[a-z]{2,3}\.)?youtube\.com\/watch(?:\?|#\!)v=)([\w-]{11}).*/gi, 130 | description:"Match the ID of a youtube video URL", 131 | tags:"video,youtube,url" 132 | }, 133 | { 134 | name:"ID of Youtube Channel", 135 | regex:/https?:\/\/(www\.)?youtube.com\/channel\/UC([-_a-z0-9]{22})/i, 136 | description:"Match the ID of a youtube channel URL", 137 | tags:"channel,youtube,url" 138 | }, 139 | { 140 | name:"CSS comment", 141 | regex:/\/\*[^*]*\*+([^/*][^*]*\*+)*\//, 142 | description:"Match standard CSS comments", 143 | tags:"css,comment,code" 144 | }, 145 | { 146 | name:"Wordpress shortcodes", 147 | regex:/^\[([a-z-_0-9]+)([^\[]+)*(?:\](.*)\[\/\1\]|\s+\/\])$/, 148 | description:"Matches opening and closing shortcode tags with content in-between them.", 149 | tags:"wordpress,shortcodes,markup" 150 | }, 151 | { 152 | name:"U.S./Canadian ZIP/Postal Code", 153 | regex:/(^\d{5}(-\d{4})?$)|(^[ABCEGHJKLMNPRSTVXY]{1}\d{1}[A-Z]{1} *\d{1}[A-Z]{1}\d{1}$)/, 154 | description:"Matches US ZIP, ZIP+4, and Canadian Postal Codes", 155 | tags:"address, postal, zip" 156 | }, 157 | { 158 | name:"UK Postal Code", 159 | regex:/^(([gG][iI][rR] {0,}0[aA]{2})|(([aA][sS][cC][nN]|[sS][tT][hH][lL]|[tT][dD][cC][uU]|[bB][bB][nN][dD]|[bB][iI][qQ][qQ]|[fF][iI][qQ][qQ]|[pP][cC][rR][nN]|[sS][iI][qQ][qQ]|[iT][kK][cC][aA]) {0,}1[zZ]{2})|((([a-pr-uwyzA-PR-UWYZ][a-hk-yxA-HK-XY]?[0-9][0-9]?)|(([a-pr-uwyzA-PR-UWYZ][0-9][a-hjkstuwA-HJKSTUW])|([a-pr-uwyzA-PR-UWYZ][a-hk-yA-HK-Y][0-9][abehmnprv-yABEHMNPRV-Y]))) {0,}[0-9][abd-hjlnp-uw-zABD-HJLNP-UW-Z]{2}))$/, 160 | description:"Matches all UK postcodes", 161 | tags:"address, postal, zip" 162 | }, 163 | { 164 | name:"Brazilian ZIP/Postal Code", 165 | regex:/^[0-9]{5}-[0-9]{3}$/, 166 | description:"Matches BR ZIP/Postal Code", 167 | tags:"address, postal, zip" 168 | }, 169 | { 170 | name:"Morse Code", 171 | regex:/^[.-]{1,5}(?:[ \t]+[.-]{1,5})*(?:[ \t]+[.-]{1,5}(?:[ \t]+[.-]{1,5})*)*$/, 172 | description:"Matches valid Morse Code", 173 | tags:"morse, code" 174 | }, 175 | { 176 | name:"Image shortcode", 177 | regex:/\[img\](.*?)\[\/img\]/, 178 | description:"Matches the content in between [img][/img]. Useful for making dynamic WYSIWYG editors", 179 | tags:"img, shortcode, wysiwyg" 180 | }, 181 | { 182 | name:"Brainfuck Code", 183 | regex:/^[+-<>.,\[\] \t\n\r]+$/, 184 | description:"Matches valid code for a brainfuck program.", 185 | tags:"brainfuck, code" 186 | }, 187 | { 188 | name:"Semver", 189 | regex:/^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-((?:0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/, 190 | description:"Matches valid semantic versioning.", 191 | tags:"semver,semantic,versioning,version" 192 | } 193 | ]; 194 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { Provider } from 'react-redux'; 4 | import { createStore, applyMiddleware } from 'redux'; 5 | 6 | import App from './components/app'; 7 | import reducers from './reducers'; 8 | 9 | import './sass/style.scss'; 10 | 11 | const createStoreWithMiddleware = applyMiddleware()(createStore); 12 | 13 | ReactDOM.render( 14 | 15 | 16 | 17 | , document.querySelector('.app')); 18 | -------------------------------------------------------------------------------- /src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | 3 | const rootReducer = combineReducers({ 4 | state: (state = {}) => state 5 | }); 6 | 7 | export default rootReducer; 8 | -------------------------------------------------------------------------------- /src/sass/style.scss: -------------------------------------------------------------------------------- 1 | $panel-bg:#222; 2 | $panel-border:#444; 3 | $body-bg:#111; 4 | $link-color:#85C1E9; 5 | $text-color:#eee; 6 | $desc-bg: #292929; 7 | 8 | .container-fluid { 9 | max-width:1200px; 10 | min-width:300px; 11 | } 12 | h1 { 13 | margin:40px 0 20px 0; 14 | } 15 | .social { 16 | margin:20px 0; 17 | } 18 | .social iframe { 19 | margin-right:3px; 20 | } 21 | a:link, 22 | a:visited { 23 | color:$link-color; 24 | } 25 | .regex-hub { 26 | 27 | color:$text-color; 28 | background-color: $body-bg; 29 | .desc { 30 | -webkit-transition: 0.5s height ease; 31 | transition: 0.5s height ease; 32 | background:$desc-bg; 33 | padding:8px; 34 | color:$text-color; 35 | p { 36 | margin-bottom:0; 37 | } 38 | 39 | &.closed { 40 | /*height:0%; 41 | padding:0; 42 | overflow: hidden;*/ 43 | 44 | display:none; 45 | } 46 | } 47 | .panel-default { 48 | color:$text-color; 49 | border-color:$panel-border; 50 | background-color: $panel-bg; 51 | 52 | input { 53 | width:100%; 54 | background-color:#444; 55 | border:1px solid #333; 56 | color:$text-color; 57 | padding:5px; 58 | } 59 | } 60 | .panel-default>.panel-heading { 61 | background-color: $panel-bg; 62 | 63 | border-color:$panel-border; 64 | border-bottom:0 none; 65 | h2 { 66 | font-size:1em; 67 | font-weight:normal; 68 | padding:0; 69 | margin:0; 70 | } 71 | a { 72 | font-size:1.2em; 73 | padding:5px 10px 10px 0px; 74 | display: block; 75 | 76 | } 77 | 78 | padding:11px 15px 0px 15px; 79 | } 80 | .panel-body { 81 | padding:12px 15px 15px 15px; 82 | input { 83 | background-color: #111; 84 | padding:10px; 85 | color:#AF7AC5; 86 | } 87 | } 88 | .panel-footer { 89 | background-color:$panel-bg; 90 | border-color:$panel-border; 91 | border-top:0 none; 92 | } 93 | .output { 94 | margin-top:10px; 95 | ul { 96 | margin:0; 97 | padding:0; 98 | list-style:none; 99 | } 100 | } 101 | } 102 | 103 | .panel-heading a { 104 | position: relative; 105 | &:before, 106 | &:after { 107 | content:''; 108 | position: absolute; 109 | background-color: #666; 110 | } 111 | &:before { 112 | width:1px; 113 | height:11px; 114 | right:5px; 115 | top:10px; 116 | } 117 | &:after { 118 | width:11px; 119 | height:1px; 120 | top:15px; 121 | right:0px; 122 | } 123 | &.open { 124 | &:before { 125 | display:none; 126 | } 127 | } 128 | } 129 | 130 | #carbonads { 131 | padding:10px 0; 132 | width:130px; 133 | margin:0 auto; 134 | font-size:10px; 135 | line-height:1.2; 136 | a { 137 | display:block; 138 | } 139 | } 140 | /* Small devices (tablets, 768px and up) */ 141 | @media (min-width: 768px) { 142 | #carbonads { 143 | float:right; 144 | margin:0; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /test/components/app_test.js: -------------------------------------------------------------------------------- 1 | import { renderComponent , expect } from '../test_helper'; 2 | import App from '../../src/components/app'; 3 | 4 | describe('App' , () => { 5 | let component; 6 | 7 | beforeEach(() => { 8 | component = renderComponent(App); 9 | }); 10 | 11 | it('renders something', () => { 12 | expect(component).to.exist; 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/test_helper.js: -------------------------------------------------------------------------------- 1 | import _$ from 'jquery'; 2 | import React from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import TestUtils from 'react-addons-test-utils'; 5 | import jsdom from 'jsdom'; 6 | import chai, { expect } from 'chai'; 7 | import chaiJquery from 'chai-jquery'; 8 | import { Provider } from 'react-redux'; 9 | import { createStore } from 'redux'; 10 | import reducers from '../src/reducers'; 11 | 12 | global.document = jsdom.jsdom(''); 13 | global.window = global.document.defaultView; 14 | global.navigator = global.window.navigator; 15 | const $ = _$(window); 16 | 17 | chaiJquery(chai, chai.util, $); 18 | 19 | function renderComponent(ComponentClass, props = {}, state = {}) { 20 | const componentInstance = TestUtils.renderIntoDocument( 21 | 22 | 23 | 24 | ); 25 | 26 | return $(ReactDOM.findDOMNode(componentInstance)); 27 | } 28 | 29 | $.fn.simulate = function(eventName, value) { 30 | if (value) { 31 | this.val(value); 32 | } 33 | TestUtils.Simulate[eventName](this[0]); 34 | }; 35 | 36 | export {renderComponent, expect}; 37 | -------------------------------------------------------------------------------- /trending.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehaas/RegexHub/d1d9f19d259745dfdd21935b9ef3e747b62b9bfb/trending.png -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: [ 3 | './src/index.js' 4 | ], 5 | output: { 6 | path: __dirname, 7 | publicPath: '/', 8 | filename: 'bundle.js' 9 | }, 10 | module: { 11 | loaders: [{ 12 | exclude: /node_modules/, 13 | loader: 'babel', 14 | query: { 15 | presets: ['react', 'es2015', 'stage-1'] 16 | } 17 | }, 18 | { 19 | test:/\.scss$/, 20 | loaders:["style","css","sass"] 21 | }] 22 | }, 23 | resolve: { 24 | extensions: ['', '.js', '.jsx'] 25 | }, 26 | devServer: { 27 | historyApiFallback: true, 28 | contentBase: './' 29 | } 30 | }; 31 | --------------------------------------------------------------------------------