We'll keep this simple: We take your privacy seriously. When you sign in to RegExr, we capture only the following information:
28 |
Your username. To use as your default Author Name for patterns.
29 |
Your email. To identify you and secure your account.
30 |
31 |
Your information is not shared with third parties, or used for any other purposes.
32 |
33 |
--------------------------------------------------------------------------------
/assets/workers/RegExWorker.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is now added to the index.html file
3 | * in .regexWorker
4 | *
5 | * Its currently manually minified. But that should move to the build process.
6 | */
7 |
8 | // in plain JS for now:
9 | onmessage = function (evt) {
10 | postMessage("onload");
11 | var data = evt.data, text = data.text, tests = data.tests, mode = data.mode;
12 | var regex = new RegExp(data.pattern, data.flags);
13 |
14 | // shared between BrowserSolver & RegExWorker
15 | var matches = [], match, index, error;
16 | if (mode === "tests") {
17 | for (var i=0, l=tests.length; i
2 |
3 |
4 |
10 |
--------------------------------------------------------------------------------
/dev/icons/add.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dev/icons/alert.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/dev/icons/arrowleft.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
--------------------------------------------------------------------------------
/dev/icons/arrowright.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
--------------------------------------------------------------------------------
/dev/icons/cheatsheet.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/dev/icons/check.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
--------------------------------------------------------------------------------
/dev/icons/close.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/dev/icons/code.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/dev/icons/copy.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
--------------------------------------------------------------------------------
/dev/icons/delete.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/dev/icons/distractor.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
15 |
--------------------------------------------------------------------------------
/dev/icons/dropdown.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/dev/icons/favorites.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/dev/icons/flags.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/dev/icons/github.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/dev/icons/google.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/dev/icons/help.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
--------------------------------------------------------------------------------
/dev/icons/info.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/dev/icons/link.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/dev/icons/load.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/dev/icons/menu.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/dev/icons/moon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/dev/icons/private.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/dev/icons/reference.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/dev/icons/search.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/dev/icons/share.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/dev/icons/thumb.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
20 |
--------------------------------------------------------------------------------
/dev/icons/user.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/dev/lib/native.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | window._native = function () {
4 | var _options = {};
5 | var _construct = function _construct(e) {
6 | var defaultOptions = {
7 | carbonZoneKey: '',
8 | fallback: '',
9 | ignore: 'false',
10 | placement: '',
11 | prefix: 'native',
12 | targetClass: 'native-ad'
13 | };
14 |
15 | if (typeof e === 'undefined') return defaultOptions;
16 | Object.keys(defaultOptions).forEach(function (key, index) {
17 | if (typeof e[key] === 'undefined') {
18 | e[key] = defaultOptions[key];
19 | }
20 | });
21 | return e;
22 | };
23 |
24 | var init = function init(zone, options) {
25 | _options = _construct(options);
26 |
27 | var jsonUrl = 'https://srv.buysellads.com/ads/' + zone + '.json?callback=_native_go';
28 | if (_options['placement'] !== '') {
29 | jsonUrl += '&segment=placement:' + _options['placement'];
30 | }
31 | if (_options['ignore'] === 'true') {
32 | jsonUrl += '&ignore=yes';
33 | }
34 |
35 | var srv = document.createElement('script');
36 | srv.src = jsonUrl;
37 | document.getElementsByTagName('head')[0].appendChild(srv);
38 | };
39 |
40 | var carbon = function carbon(e) {
41 | var srv = document.createElement('script');
42 | srv.src = '//cdn.carbonads.com/carbon.js?serve=' + e['carbonZoneKey'] + '&placement=' + e['placement'];
43 | srv.id = '_carbonads_js';
44 |
45 | return srv;
46 | };
47 |
48 | var sanitize = function sanitize(ads) {
49 | return ads.filter(function (ad) {
50 | return Object.keys(ad).length > 0;
51 | }).filter(function (ad) {
52 | return ad.hasOwnProperty('statlink');
53 | });
54 | };
55 |
56 | var pixel = function pixel(p, timestamp) {
57 | var c = '';
58 | if (p) {
59 | p.split('||').forEach(function (pixel, index) {
60 | c += '';
61 | });
62 | }
63 | return c;
64 | };
65 |
66 | var options = function options() {
67 | return _options;
68 | };
69 |
70 | return {
71 | carbon: carbon,
72 | init: init,
73 | options: options,
74 | pixel: pixel,
75 | sanitize: sanitize
76 | };
77 | }({});
78 |
79 | window._native_go = function (json) {
80 | var options = _native.options();
81 | var ads = _native.sanitize(json['ads']);
82 | var selectedClass = document.querySelectorAll('.' + options['targetClass']);
83 |
84 | if (ads.length < 1) {
85 | selectedClass.forEach(function (className, index) {
86 | var selectedTarget = document.getElementsByClassName(options['targetClass'])[index];
87 |
88 | if (options['fallback'] !== '' || options['carbonZoneKey'] !== '') selectedTarget.setAttribute('data-state', 'visible');
89 | selectedTarget.innerHTML = options['fallback'];
90 | if (options['carbonZoneKey'] !== '') selectedTarget.appendChild(_native.carbon(options));
91 | });
92 |
93 | // End at this line if no ads are found, avoiding unnecessary steps
94 | return;
95 | }
96 |
97 | selectedClass.forEach(function (className, index) {
98 | var selectedTarget = document.getElementsByClassName(options['targetClass'])[index];
99 | var adElement = selectedTarget.innerHTML||"";
100 | var prefix = options['prefix'];
101 | var ad = ads[index];
102 |
103 | if (ad && className) {
104 | var adInnerHtml = adElement.replace(new RegExp('#' + prefix + '_bg_color#', 'g'), ad['backgroundColor']).replace(new RegExp('#' + prefix + '_bg_color_hover#', 'g'), ad['backgroundHoverColor']).replace(new RegExp('#' + prefix + '_company#', 'g'), ad['company']).replace(new RegExp('#' + prefix + '_cta#', 'g'), ad['callToAction']).replace(new RegExp('#' + prefix + '_cta_bg_color#', 'g'), ad['ctaBackgroundColor']).replace(new RegExp('#' + prefix + '_cta_bg_color_hover#', 'g'), ad['ctaBackgroundHoverColor']).replace(new RegExp('#' + prefix + '_cta_color#', 'g'), ad['ctaTextColor']).replace(new RegExp('#' + prefix + '_cta_color_hover#', 'g'), ad['ctaTextColorHover']).replace(new RegExp('#' + prefix + '_desc#', 'g'), ad['description']).replace(new RegExp('#' + prefix + '_index#', 'g'), prefix + '-' + ad['i']).replace(new RegExp('#' + prefix + '_img#', 'g'), ad['image']).replace(new RegExp('#' + prefix + '_small_img#', 'g'), ad['smallImage']).replace(new RegExp('#' + prefix + '_link#', 'g'), ad['statlink']).replace(new RegExp('#' + prefix + '_logo#', 'g'), ad['logo']).replace(new RegExp('#' + prefix + '_color#', 'g'), ad['textColor']).replace(new RegExp('#' + prefix + '_color_hover#', 'g'), ad['textColorHover']).replace(new RegExp('#' + prefix + '_title#', 'g'), ad['title']);
105 | selectedTarget.innerHTML = adInnerHtml + _native.pixel(ad['pixel'], ad['timestamp']);
106 | selectedTarget.setAttribute('data-state', 'visible');
107 | } else {
108 | selectedTarget.innerHTML = "";
109 | }
110 | });
111 | };
--------------------------------------------------------------------------------
/dev/sass/codemirror.scss:
--------------------------------------------------------------------------------
1 | /* CodeMirror */
2 |
3 |
4 | .CodeMirror {
5 | font-family: $monospace;
6 | background: none;
7 | position: absolute; // need this, and a wrapper with `position:relative` to work in a flexbox
8 | box-sizing: border-box;
9 |
10 | div.CodeMirror-cursor {
11 | pointer-events: none; /* this doesn't work in IE<11 */
12 | border-left: 1px solid $doc-black;
13 | }
14 | }
15 |
16 | .CodeMirror-selected {
17 | background: rgba(170, 170, 170, 0.55);
18 | }
19 |
20 | .CodeMirror-focused .CodeMirror-selected {
21 | background: rgba(140, 150, 255, 0.8);
22 | }
23 |
24 | // this works, but of course the character isn't selectable, or highlightable:
25 | .editor.multiline {
26 | .CodeMirror-line:not(:last-child)>span:after {
27 | pointer-events: none;
28 | color: $invischar-color;
29 | content: "\AC"; // ¬ alternately: \B6 ¶
30 | }
31 | }
32 |
33 | .CodeMirror-line {
34 | .cm-space::before, .cm-special::before {
35 | color: $invischar-color;
36 | content: "•"; // alternately: \B7 ·
37 | position: absolute;
38 | }
39 |
40 | .cm-special::before {
41 | color: $error-color;
42 | }
43 |
44 | .cm-tab {
45 | background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAMCAYAAAAkuj5RAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTM4IDc5LjE1OTgyNCwgMjAxNi8wOS8xNC0wMTowOTowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTcgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6REZENEEyN0Q3NTc0MTFFNzlFMTZGQ0Q1MEREODEyREEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6REZENEEyN0U3NTc0MTFFNzlFMTZGQ0Q1MEREODEyREEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpERkQ0QTI3Qjc1NzQxMUU3OUUxNkZDRDUwREQ4MTJEQSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpERkQ0QTI3Qzc1NzQxMUU3OUUxNkZDRDUwREQ4MTJEQSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgWEz28AAABVSURBVHja7JVBCgAgCAQt+rf6cstjYN4iFxoQxJMjos3MCJlO4HyB26iqZLXyAswskQTUCmUSI7OruE4usxX9jCKELKK8w04e6Qqdmnfa/8SPmQIMANcZrZCVJGBIAAAAAElFTkSuQmCC);
46 | background-position: 100%;
47 | background-repeat: no-repeat;
48 | /*
49 | // ::before implementation. Doesn't stretch.
50 | pointer-events: none;
51 | content: "⟶";
52 | position: absolute;
53 | color: $invischar-color;
54 | */
55 | }
56 | }
--------------------------------------------------------------------------------
/dev/sass/colors.scss:
--------------------------------------------------------------------------------
1 | // base colors:
2 | $theme-color: #70B0E0; // #66CCFF
3 | $base-color: mix(#808080, $theme-color, 85);
4 |
5 | // core colors:
6 | $black: darken($base-color, 46%);
7 | $darkest: mix($black, $base-color, 78%);
8 | $darker: mix($black, $base-color, 62%);
9 | $dark: mix($black, $base-color, 34%);
10 |
11 | $mid: $base-color;
12 |
13 | $white: lighten($base-color, 42%);
14 | $lightest: mix($white, $base-color, 92%);
15 | $lighter: mix($white, $base-color, 78%);
16 | $light: mix($white, $base-color, 50%);
17 |
18 | // doc colors:
19 | $doc-black: $black;
20 | $doc-darkest: mix($doc-black, $base-color, 80%);
21 | $doc-darker: mix($doc-black, $base-color, 64%);
22 | $doc-dark: mix($doc-black, $base-color, 35%);
23 |
24 | $doc-mid: $base-color;
25 |
26 | $doc-white: $white;
27 | $doc-lightest: mix($doc-white, $base-color, 92%);
28 | $doc-lighter: mix($doc-white, $base-color, 78%);
29 | $doc-light: mix($doc-white, $base-color, 50%);
30 |
31 | // control colors:
32 | $title-bg: $doc-light;
33 | $tooltip-bg: rgba($doc-black,0.85);
34 | $selected-stroke-color: rgba($doc-black, 0.3);
35 | $match-color: rgba($theme-color, 0.5);
36 |
37 | $dark-shadow: rgba(0,0,0, 0.25);
38 | $light-shadow: rgba(0,0,0, 0.1);
39 | $strong-shadow: rgba(0,0,0, 0.45);
40 |
41 | $details-group-alpha: 0.5;
42 |
43 | // notice colors:
44 | $error-color: #D22;
45 | $warning-color: $error-color;
46 | $fail-color: $error-color;
47 | $pass-color: #0A0;
48 |
49 | // syntax colors:
50 | $group-color: #0A0;
51 | $groupbg-color: #0E0;
52 | $alt-color: $group-color;
53 |
54 | $set-color: #D70;
55 | $setbg-color: #FE0;
56 |
57 | $anchor-color: #840;
58 | $quant-color: #58F;
59 | $esc-color: #C0C;
60 | $special-color: $esc-color;
61 |
62 | // invisible char color:
63 | // this isn't bound to the theme color, because it's used in the tab bitmap:
64 | $invischar-color: rgba(127,127,127,0.33);
65 |
--------------------------------------------------------------------------------
/dev/sass/colors_dark.scss:
--------------------------------------------------------------------------------
1 | @import "colors";
2 |
3 | // base colors:
4 | $theme-color: desaturate(darken($theme-color, 18), 20);
5 | $base-color: mix(#808080, darken($theme-color, 20), 90);
6 |
7 | // core colors:
8 | // inherited
9 |
10 | // doc colors:
11 | $doc-black: $lighter;
12 | $doc-darkest: mix($doc-black, $base-color, 90%);
13 | $doc-darker: mix($doc-black, $base-color, 72%);
14 | $doc-dark: mix($doc-black, $base-color, 50%);
15 |
16 | $doc-mid: $mid;
17 |
18 | $doc-white: mix($black, $darkest, 30%);
19 | $doc-lightest: mix($doc-white, $base-color, 92%);
20 | $doc-lighter: mix($doc-white, $base-color, 80%);
21 | $doc-light: mix($doc-white, $base-color, 60%);
22 |
23 | // control colors:
24 | $title-bg: $doc-light;
25 | $tooltip-bg: rgba($doc-black, 0.85);
26 | $selected-stroke-color: rgba($doc-black, 0.4);
27 | $match-color: rgba($theme-color, 0.6);
28 |
29 | $details-group-alpha: 0.33;
30 |
31 | // shadows inherited
32 |
33 | // notice colors:
34 |
35 | // syntax coloring:
36 | // inherited
37 |
38 | // invisible char color:
39 | // inherited
40 |
--------------------------------------------------------------------------------
/dev/sass/colors_light.scss:
--------------------------------------------------------------------------------
1 | @import "colors";
2 |
3 | // base colors:
4 | // inherit theme-color
5 | $theme-color: darken($theme-color, 6);
6 | $base-color: mix(#808080, invert($theme-color), 95);
7 |
8 | // core colors:
9 | $black: lighten($base-color, 44%);
10 | $darkest: mix($black, $base-color, 80%);
11 | $darker: mix($black, $base-color, 64%);
12 | $dark: mix($black, $base-color, 35%);
13 |
14 | $mid: $base-color;
15 |
16 | $white: darken($base-color, 45%);
17 | $lightest: mix($white, $base-color, 92%);
18 | $lighter: mix($white, $base-color, 78%);
19 | $light: mix($white, $base-color, 50%);
20 |
21 | // doc colors:
22 | $doc-black: $white;
23 | $doc-darkest: mix($doc-black, $base-color, 80%);
24 | $doc-darker: mix($doc-black, $base-color, 64%);
25 | $doc-dark: mix($doc-black, $base-color, 35%);
26 |
27 | $doc-mid: $base-color;
28 |
29 | $doc-white: mix($black, $base-color, 90%);
30 | $doc-lightest: mix($doc-white, $base-color, 92%);
31 | $doc-lighter: mix($doc-white, $base-color, 78%);
32 | $doc-light: mix($doc-white, $base-color, 50%);
33 |
34 | // control colors:
35 | $title-bg: $doc-light;
36 | $tooltip-bg: rgba($doc-black,0.9);
37 | $selected-stroke-color: rgba($doc-black, 0.4);
38 | $match-color: rgba($theme-color, 0.45);
39 | $dark-shadow: rgba(0,0,0, 0.25);
40 | $light-shadow: rgba(0,0,0, 0.1);
41 | $strong-shadow: rgba(0,0,0, 0.35);
42 |
43 | // syntax coloring:
44 | // inherited
45 |
46 | // invisible char color:
47 | // inherited
48 |
--------------------------------------------------------------------------------
/dev/sass/export.scss:
--------------------------------------------------------------------------------
1 | // this shares values with js:
2 | #export {
3 | &.theme { color: $theme-color; }
4 | &.match { color: $match-color; }
5 | &.selected-stroke { color: $selected-stroke-color; }
6 | }
--------------------------------------------------------------------------------
/dev/sass/fonts.scss:
--------------------------------------------------------------------------------
1 | $font-size: 16px;
2 | $font-family: 'Roboto Condensed', sans-serif;
3 | $monospace: 'Source Code Pro', monospace;
4 |
--------------------------------------------------------------------------------
/dev/sass/mobile.scss:
--------------------------------------------------------------------------------
1 | // keep synced with RegExr._initUI:
2 | @media (max-width: 900px) {
3 | body {
4 | //font-size: 14px;
5 |
6 | >.container > .header {
7 | > .etc > .github {
8 | display: none;
9 | }
10 |
11 | > .file .savekey {
12 | display: none;
13 | }
14 | }
15 |
16 | > .container > .app {
17 | display: block;
18 | position:relative;
19 | background: $doc-lightest;
20 |
21 |
22 | > .doc {
23 | position: absolute;
24 | top: 0;
25 | bottom: 0;
26 | left: $closed-sidebar-width;
27 | right: 0;
28 |
29 | > .blocker {
30 | display: none;
31 | width: 100%;
32 | height: 100%;
33 | position: fixed;
34 | background: rgba($doc-lighter, 0.5);
35 | z-index: 10;
36 | }
37 |
38 | &.fadeback {
39 | filter: blur(2px);
40 |
41 | > .blocker {
42 | display: block;
43 | }
44 | }
45 | }
46 |
47 | > .sidebar {
48 | position:absolute;
49 | top: 0;
50 | bottom: 0;
51 | left: 0;
52 | z-index: 10000;
53 | transition: width $transition-t ease-out;
54 | width: calc(10% + 350px);
55 | box-shadow: 10px 0px 12px $strong-shadow;
56 | background: $darkest;
57 | opacity: 0.94;
58 |
59 | /*
60 | &:before {
61 | content: "";
62 | position: fixed;
63 | width: 100%;
64 | height: 100%;
65 | background: rgba($black, 0.7);
66 | z-index: -1;
67 | }
68 | */
69 | &.closed {
70 | width: $closed-sidebar-width;
71 | min-width: $closed-sidebar-width;
72 | box-shadow: 1px 2px 8px $strong-shadow;
73 | opacity: 1;
74 | &:before { content: none; }
75 | }
76 | }
77 | }
78 | }
79 | }
--------------------------------------------------------------------------------
/dev/sass/regexr.scss:
--------------------------------------------------------------------------------
1 | // default and theme colors imported via build process
2 | @import "variables";
3 | @import "fonts";
4 |
5 | @import "styles";
6 | @import "controls";
7 | @import "views/header";
8 | @import "views/doc";
9 | @import "views/expression";
10 | @import "views/text";
11 | @import "views/tools";
12 | @import "views/tools/explain";
13 | @import "views/tools/details";
14 | @import "views/sidebar";
15 | @import "views/community";
16 | @import "views/share";
17 |
18 | @import "views/ad";
19 |
20 | @import "mobile";
21 | @import "export";
22 |
23 | @import "codemirror";
24 |
--------------------------------------------------------------------------------
/dev/sass/styles.scss:
--------------------------------------------------------------------------------
1 | %title {
2 | display: flex;
3 | align-items: center;
4 | padding: 0 $pad 0 $pad;
5 | flex: 0 $title-height;
6 |
7 | h1 {
8 | flex: 1;
9 | }
10 | }
11 |
12 | %link {
13 | transition: color $transition-t;
14 | color: $lighter;
15 |
16 | &:not(.inactive) {
17 | cursor: pointer;
18 |
19 | &:hover {
20 | color: $lightest;
21 |
22 | svg.icon {
23 | color: currentColor;
24 | }
25 | }
26 | }
27 |
28 | svg.icon {
29 | transition: fill $transition-t;
30 | }
31 | }
32 |
33 | %ellipsis {
34 | background: $dark;
35 | color: $white;
36 | font-weight: normal;
37 | padding: 0 0.25em;
38 | margin-left: 0.25em;
39 | }
40 |
41 | %selected-token {
42 | outline: $selected-stroke;
43 | z-index: 10;
44 | }
45 |
46 | %related-token {
47 | outline: $related-stroke;
48 | z-index: 9;
49 | }
50 |
51 | html, body {
52 | margin: 0;
53 | width: 100%;
54 | height: 100%;
55 | min-height: 540px;
56 | min-width: 500px;
57 | font-family: $font-family;
58 | font-size: $font-size;
59 | color: $white;
60 | user-select: none;
61 | cursor: default;
62 |
63 | // This shouldn't be necessary if everything is working correctly
64 | // overflow: hidden;
65 |
66 | > .container {
67 | /* wraps all content, since flex layouts don't work reliably on body */
68 | height: 100%;
69 | display: flex;
70 | flex-flow: column;
71 |
72 | > .app {
73 | /* wraps the sidebar and doc */
74 | flex: 1;
75 | min-height: 0; // fix for Chrome 72+
76 | display: flex;
77 | align-items: stretch;
78 | }
79 | }
80 | }
81 |
82 | // TODO: revisit for support in browsers other than Chrome & Safari:
83 | *::-webkit-scrollbar {
84 | width: 0.5em;
85 | height: 0.5em;
86 | }
87 | *::-webkit-scrollbar-track {
88 | background: rgba($mid, 0.25);
89 | }
90 |
91 | *::-webkit-scrollbar-thumb {
92 | background-color: $mid;
93 | border-radius: 0.5em;
94 | }
95 |
96 | .app .sidebar, .container .header, .tooltip {
97 | -moz-osx-font-smoothing: grayscale;
98 | -webkit-font-smoothing: antialiased;
99 | }
100 |
101 | code, pre {
102 | font-family: $monospace;
103 | }
104 |
105 | pre {
106 | margin: 0;
107 | }
108 |
109 | a {
110 | @extend %link;
111 | color: $theme-color;
112 | text-decoration: none;
113 | }
114 |
115 | h1, h2 {
116 | font-size: 1rem;
117 | font-weight: bold;
118 | margin: 0;
119 | }
120 |
121 | .list {
122 | display: flex;
123 | flex-direction: column;
124 | margin: 0;
125 | padding: 0;
126 |
127 | li {
128 | display: block;
129 | }
130 | }
131 |
132 | hr {
133 | border: 0;
134 | border-top: 1px solid rgba($mid, 0.25);
135 | margin: 0.75em 0 1em 0;
136 | }
137 |
138 |
139 | span.match {
140 | background: $match-color;
141 | color: $doc-black;
142 | }
143 |
144 | span.error {
145 | color: $error-color;
146 | font-weight: bold;
147 |
148 | &.warning {
149 | color: $warning-color;
150 | }
151 | }
152 |
153 | .anim-spin {
154 | animation: 1s linear infinite spin;
155 | }
156 |
157 | @keyframes spin {
158 | from {
159 | transform: rotate(0deg);
160 | }
161 |
162 | to {
163 | transform: rotate(360deg);
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/dev/sass/variables.scss:
--------------------------------------------------------------------------------
1 | $pad: 1rem;
2 | $control-radius: 0.125rem;
3 | $input-height: 3.75rem;
4 | $title-height: 2.5rem;
5 | $transition-t: 0.2s;
6 |
7 | $control-opacity: 0.2;
8 | $button-opacity: 0.35;
9 | $input-opacity: 0.05;
10 | $button-hover-opacity: 0.75;
11 | $disabled-opacity: 0.35;
12 |
13 | $related-stroke-width: 1px;
14 | $related-stroke: solid $related-stroke-width $selected-stroke-color;
15 | $selected-stroke-width: 2px;
16 | $selected-stroke: solid $selected-stroke-width $selected-stroke-color;
17 |
18 | $closed-sidebar-width: 50px;
--------------------------------------------------------------------------------
/dev/sass/views/ad.scss:
--------------------------------------------------------------------------------
1 | .native-js {
2 | transition: all 0.25s ease-in-out;
3 | opacity: 0;
4 | position: absolute;
5 | top: 0;
6 | }
7 |
8 | .native-js[data-state=visible] {
9 | position: static;
10 | opacity: 1;
11 | + .noad {
12 | display: none;
13 | }
14 | }
15 |
16 | .native-img {
17 | margin-right: $pad*0.75;
18 | max-height: 40px;
19 | max-width: 27%;
20 | border-radius: 3px;
21 | }
22 |
23 | .native-flex {
24 | display: flex;
25 | padding: $pad*0.75;
26 | text-decoration: none;
27 |
28 | flex-flow: row nowrap;
29 | justify-content: space-between;
30 | align-items: center;
31 | }
32 |
33 | .native-main {
34 | display: flex;
35 |
36 | flex-flow: row nowrap;
37 | align-items: center;
38 | }
39 |
40 | .native-details {
41 | display: flex;
42 | font-size: 12px;
43 | position: relative;
44 |
45 | max-height: 5em;
46 |
47 | flex-flow: column;
48 | }
49 |
50 | .native-company {
51 | margin-bottom: 4px;
52 | text-transform: uppercase;
53 | letter-spacing: 1px;
54 | font-weight: bold;
55 | font-size: 11px;
56 | }
57 |
58 | .native-desc {
59 | letter-spacing: 1px;
60 | font-weight: 300;
61 | line-height: 1.3em;
62 | text-overflow: ellipsis;
63 | max-height: 5em;
64 | overflow: hidden;
65 | position: relative;
66 | }
67 |
68 |
69 |
70 | /* Carbon Ads */
71 |
72 | #carbonads {
73 | display: block;
74 | overflow: hidden;
75 | background-color: $black;
76 | font-size: 13px;
77 | line-height: 1.5;
78 | }
79 |
80 | #carbonads a {
81 | color: inherit;
82 | text-decoration: none;
83 | }
84 |
85 | #carbonads a:hover {
86 | color: inherit;
87 | }
88 |
89 | #carbonads span {
90 | position: relative;
91 | display: block;
92 | overflow: hidden;
93 | }
94 |
95 | .carbon-img {
96 | display: block;
97 | float: left;
98 | margin: 0;
99 | line-height: 1;
100 | }
101 |
102 | .carbon-img img {
103 | display: block;
104 | }
105 |
106 | .carbon-text {
107 | display: block;
108 | float: left;
109 | padding: 8px 1em;
110 | max-width: calc(100% - 130px - 2em);
111 | text-align: left;
112 | letter-spacing: .5px;
113 | }
114 |
115 | .carbon-poweredby {
116 | position: absolute;
117 | right: 0;
118 | bottom: 0;
119 | left: 130px;
120 | display: block;
121 | padding: 8px 13px;
122 | border-top: solid 1px $darkest;
123 | text-transform: uppercase;
124 | letter-spacing: 1px;
125 | font-weight: 400;
126 | font-size: 9px;
127 | line-height: 1;
128 | }
129 |
130 |
--------------------------------------------------------------------------------
/dev/sass/views/community.scss:
--------------------------------------------------------------------------------
1 | .community {
2 |
3 | > header {
4 | .icon {
5 | @extend %link;
6 | color: $dark;
7 |
8 | &.selected:not(:hover) {
9 | color: $theme-color;
10 | }
11 | }
12 |
13 | background: $black;
14 | padding: $pad;
15 | margin: -$pad;
16 | margin-bottom: $pad*0.5;
17 | display: flex;
18 | justify-content: space-between;
19 |
20 | > .name {
21 | color: $white;
22 | font-weight: bold;
23 | overflow: hidden;
24 | display: block;
25 | white-space: nowrap;
26 | text-overflow: ellipsis;
27 | }
28 |
29 | > div {
30 | display: flex;
31 |
32 | .favorites {
33 | margin: 0 $pad;
34 | }
35 | }
36 | }
37 |
38 | .row.rate {
39 | .icon.thumbdown, .icon.thumbup {
40 | cursor: pointer;
41 |
42 | &:hover, &.selected:hover {
43 | color: $white;
44 | }
45 |
46 | &.selected {
47 | color: $theme-color;
48 | }
49 | }
50 |
51 | .icon.thumbdown {
52 | transform: translate(0,0.125em) scale(1,-1);
53 | }
54 | }
55 |
56 | >.author {
57 | margin-top: 0.125em;
58 | color: $mid;
59 | }
60 |
61 | .actions.list {
62 | background: 0;
63 |
64 | margin: -0.5*$pad -1*$pad 0 -1*$pad;
65 |
66 | }
67 |
68 | >.desc {
69 | margin-top: $pad;
70 | }
71 | }
--------------------------------------------------------------------------------
/dev/sass/views/doc.scss:
--------------------------------------------------------------------------------
1 | .doc {
2 | flex: 8 800px;
3 |
4 | display: flex;
5 | flex-flow: column;
6 |
7 | > section {
8 | background: $doc-white;
9 | color: $doc-dark;
10 | flex: 1 1 0%; // fix for FireFox (vs just `1`)
11 | min-height: 0; // fix for Chrome 72+
12 | transition: flex $transition-t ease-out;
13 |
14 | display: flex;
15 | flex-direction: column;
16 |
17 | > header {
18 | @extend %title;
19 | color: $doc-darkest;
20 | background: $title-bg;
21 |
22 | > .max {
23 | transition: transform $transition-t;
24 | color: $doc-mid;
25 | margin-left: 1rem;
26 | cursor: pointer;
27 |
28 | &:hover {
29 | color: $doc-white;
30 | }
31 | }
32 | }
33 |
34 | > article {
35 | flex: 1;
36 | min-height: 0; // fix for Chrome 72+
37 | }
38 |
39 | &.closed {
40 | flex: 0 $title-height;
41 |
42 | > header {
43 | cursor: pointer;
44 |
45 | > *:not(h1) {
46 | opacity: 0.4;
47 | }
48 |
49 | > .max {
50 | transform: rotate(45deg);
51 | }
52 | }
53 | }
54 | }
55 |
56 | &.tests-mode {
57 | section.tools {
58 | display: none;
59 | }
60 | }
61 |
62 | // overwrite colors:
63 | input, textarea {
64 | background: rgba($doc-white, $input-opacity);
65 | border-color: rgba($doc-white, $input-opacity);
66 | }
67 |
68 | .control {
69 | background: rgba($doc-white, $control-opacity);
70 | }
71 |
72 | .button, .buttonbar > *, .segcontrol > * {
73 | background: rgba($doc-white, $button-opacity);
74 |
75 | &:hover, &.selected {
76 | background: rgba($doc-white, $button-hover-opacity);
77 | }
78 |
79 | &.default:hover {
80 | background: $doc-white;
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/dev/sass/views/expression.scss:
--------------------------------------------------------------------------------
1 | .doc > section.expression {
2 | flex: 0 $title-height+$input-height;
3 |
4 | > header {
5 | background: $theme-color;
6 | }
7 |
8 | > .editor {
9 | position: relative;
10 |
11 | .icon.alert {
12 | display: none;
13 | position: relative;
14 | float: right;
15 | margin: $pad * 1.25;
16 | color: $error-color;
17 | z-index: 10;
18 | user-select: none;
19 | }
20 |
21 | > .CodeMirror {
22 | padding: $pad 0 0 $pad;
23 | font-weight: bold;
24 | }
25 | }
26 |
27 | > .editor.error > .icon.alert {
28 | display: block;
29 | }
30 | }
31 |
32 | // tooltips:
33 | #tooltip-flavor, #tooltip-flags {
34 | > .list {
35 | li {
36 | cursor: pointer;
37 | color: $doc-light;
38 |
39 | &:hover {
40 | color: $doc-white;
41 | }
42 |
43 | + li {
44 | margin-top: 0.5em;
45 | }
46 |
47 | .check.icon {
48 | color: $doc-dark;
49 | margin-right: 0.25em;
50 | }
51 |
52 | &.selected .check.icon {
53 | color: $theme-color;
54 | }
55 |
56 | em {
57 | font-style: normal;
58 | font-weight: bold;
59 | color: $doc-white;
60 | padding: 0 0.15em;
61 | background: rgba($doc-mid, 0.3);
62 | border-radius: $control-radius;
63 | }
64 | }
65 | }
66 | }
67 |
68 |
69 |
70 | /* Syntax highlighting */
71 |
72 | .exp-related {
73 | border-bottom: $related-stroke;
74 | border-top: $related-stroke;
75 | margin-bottom: -$related-stroke-width;
76 | margin-top: -$related-stroke-width;
77 | }
78 |
79 | .exp-related-left {
80 | border-left: $related-stroke;
81 | margin-left: -$related-stroke-width;
82 | }
83 |
84 | .exp-related-right {
85 | border-right: $related-stroke;
86 | margin-right: -$related-stroke-width;
87 | }
88 |
89 | .exp-selected {
90 | border-top: $selected-stroke;
91 | border-bottom: $selected-stroke;
92 | }
93 |
94 | .exp-selected-left {
95 | border-left: $selected-stroke;
96 | margin-left: -$selected-stroke-width;
97 | }
98 |
99 | .exp-selected-right {
100 | border-right: $selected-stroke;
101 | margin-right: -$selected-stroke-width;
102 | }
103 |
104 | .exp-error {
105 | border-bottom: solid 2px $error-color;
106 | }
107 |
108 | .exp-warning {
109 | border-bottom: dotted 2px $warning-color;
110 | }
111 |
112 | .exp-char {
113 | color: $doc-black;
114 | }
115 |
116 | .exp-decorator {
117 | color: $doc-light;
118 | }
119 |
120 | .exp-esc {
121 | color: $esc-color;
122 | }
123 |
124 | .exp-quant, .exp-lazy, .exp-possessive {
125 | color: $quant-color;
126 | }
127 |
128 | .exp-alt {
129 | color: $alt-color;
130 | }
131 |
132 | .exp-anchor {
133 | color: $anchor-color;
134 | }
135 |
136 | .exp-group, .exp-ref, .exp-lookaround {
137 | color: $group-color;
138 | }
139 |
140 | .exp-charclass, .exp-set, .exp-subst {
141 | color: $set-color;
142 | }
143 |
144 | .exp-group-0 { background: rgba($groupbg-color, 0.11); }
145 |
146 | .exp-group-1 { background: rgba($groupbg-color, 0.22); }
147 |
148 | .exp-group-2 { background: rgba($groupbg-color, 0.33); }
149 |
150 | .exp-group-3 { background: rgba($groupbg-color, 0.44); }
151 |
152 | .exp-group-set { background: rgba($setbg-color, 0.3); }
153 |
154 | .exp-comment {
155 | color: $doc-light;
156 | background: rgba($doc-black, 0.05);
157 | font-style: italic;
158 | border-bottom: solid 3px $doc-lighter;
159 | }
160 |
161 | .exp-special { color: $special-color; }
162 |
--------------------------------------------------------------------------------
/dev/sass/views/header.scss:
--------------------------------------------------------------------------------
1 | .container .header {
2 | display: flex;
3 | align-items: center;
4 | justify-content: space-between;
5 |
6 | background: $black;
7 | padding: 0 $pad;
8 | height: 54px;
9 | z-index: 1000;
10 |
11 | .logo {
12 | flex: none;
13 | font-size: 1.125rem;
14 | margin-top: $pad/16;
15 | color: $theme-color;
16 | width: 1.5em;
17 | height: 1.5em;
18 | }
19 |
20 | .settings {
21 | @extend %link;
22 | color: $theme-color;
23 | display: flex;
24 | min-width: 0;
25 | max-width: 20em;
26 |
27 | .name {
28 | margin: 0 $pad*0.25 0 $pad*0.5;
29 | text-overflow: ellipsis;
30 | white-space: nowrap;
31 | overflow: hidden;
32 | }
33 |
34 | .icon.share {
35 | flex: none;
36 | color: $dark;
37 | width: 1em;
38 | margin-right: $pad*0.5;
39 | }
40 | }
41 |
42 | .signin.selected a {
43 | color: $white !important;
44 | }
45 |
46 | ul {
47 | padding: 0;
48 | white-space: nowrap;
49 |
50 | li {
51 | display: inline-block;
52 |
53 | &:first-child {
54 | margin-left: 0;
55 | }
56 | }
57 | }
58 |
59 | ul.file {
60 | text-align: right;
61 |
62 | li {
63 | @extend .button;
64 |
65 | color: $light;
66 | background: $darkest;
67 |
68 | &:hover {
69 | background: $darker;
70 | }
71 |
72 | &.save {
73 | color: $theme-color;
74 | font-weight: bold;
75 |
76 | .savekey {
77 | font-size: 0.875em;
78 | font-weight: normal;
79 | color: $mid;
80 | }
81 | }
82 | }
83 | }
84 |
85 | .button.theme {
86 | padding: $pad * 0.3755;;
87 | background: $darkest;
88 |
89 | &.selected .icon {
90 | color: $theme-color;
91 | }
92 |
93 | &:hover {
94 | background: $darker;
95 | }
96 | }
97 |
98 | ul.etc {
99 | flex: 1;
100 | text-align: right;
101 | font-size: 0.875rem;
102 | color: $dark;
103 |
104 | li {
105 | margin-left: $pad;
106 | }
107 | }
108 | }
109 |
110 | #tooltip-signin {
111 | ul.list li {
112 | @extend .button;
113 | margin: $pad*0.25 0;
114 | padding: $pad*0.625;
115 | color: $lightest;
116 |
117 | opacity: 0.85;
118 | &:hover {
119 | opacity: 1;
120 | }
121 |
122 | &[data-id='GitHub'] {
123 | background: #777777 !important;
124 | }
125 |
126 | &[data-id='Google'] {
127 | background: #C94130 !important;
128 | }
129 |
130 | >svg.icon {
131 | margin-right: $pad*0.625;
132 | }
133 | }
134 |
135 | .signout {
136 | display: none;
137 |
138 | .signoutbtn {
139 | cursor: pointer;
140 | color: $theme-color;
141 | font-weight: bold;
142 |
143 | &:hover {
144 | color: $doc-white;
145 | }
146 | }
147 | }
148 |
149 | &.authenticated {
150 | .signin {
151 | display: none;
152 | }
153 | .signout {
154 | display: block;
155 | }
156 | }
157 |
158 | .distract {
159 | display: none;
160 | color: $mid;
161 |
162 | .distractor {
163 | margin-right: $pad/2;
164 | }
165 | }
166 |
167 | &.wait {
168 | .distract {
169 | display: block;
170 | }
171 |
172 | .signout .signoutbtn, .signin .list {
173 | display: none;
174 | }
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/dev/sass/views/share.scss:
--------------------------------------------------------------------------------
1 | #share_main, #share_community {
2 | margin: -$pad;
3 |
4 | .list {
5 | background: transparent;
6 | border: none;
7 | }
8 |
9 | .inputs {
10 | input, textarea {
11 | color: $white;
12 | margin: 2px 0;
13 | }
14 | }
15 |
16 | .button {
17 | padding-left: $pad*2;
18 | padding-right: $pad*2;
19 | }
20 |
21 | .buttons.wait {
22 | .button {
23 | opacity: $disabled-opacity;
24 | pointer-events: none;
25 | }
26 | }
27 |
28 | .status {
29 | margin-left: $pad/2;
30 | }
31 | }
32 |
33 | #share_main {
34 | .signin.row {
35 | .signout {
36 | display: none;
37 | }
38 |
39 | &.authenticated {
40 | display: none;
41 | }
42 | }
43 |
44 | .row:not(.active) .icon.check {
45 | display: none;
46 | }
47 |
48 | .delete.row:hover {
49 | background: rgba($error-color, 0.6);
50 | }
51 |
52 | > .info {
53 | padding: $pad;
54 | margin-bottom: $pad * 0.5;
55 | border-bottom: solid 1px $darker;
56 |
57 | > .row {
58 | padding: 0 0 $pad/2 0;
59 | border: 0;
60 | color: $light;
61 | align-items: flex-start;
62 |
63 | svg.icon {
64 | color: currentColor;
65 | margin: 0;
66 | }
67 | }
68 | }
69 |
70 | > .save {
71 | flex-direction: column;
72 | align-items: flex-start;
73 | padding: $pad*1.25 $pad*0.5;
74 | margin-bottom: $pad*0.5;
75 | background: $darker;
76 | border: 0;
77 | box-shadow: -3px 3px 6px $light-shadow;
78 |
79 | .message {
80 | color: $white;
81 | }
82 |
83 | .buttons {
84 | padding: $pad*0.5 0 0 0;
85 | border: 0;
86 | color: $white;
87 |
88 | .button {
89 | margin: 0 $pad*0.5 0 0;
90 | }
91 | }
92 | }
93 | }
94 |
95 |
96 |
97 | #share_community {
98 | margin: 0 $pad/2;
99 |
100 | > .buttons {
101 | justify-content: flex-end;
102 | color: $white;
103 | border: none;
104 |
105 | }
106 |
107 | .inputs, .buttons {
108 | margin: $pad 0;
109 | }
110 | }
--------------------------------------------------------------------------------
/dev/sass/views/tools.scss:
--------------------------------------------------------------------------------
1 | .doc > section.tools > article {
2 | display: flex;
3 | flex-direction: column;
4 | background: $doc-lightest;
5 |
6 | .icon.help {
7 | @extend %link;
8 | // this is a bit hacky, but lets us reuse the help icon everywhere.
9 | position: absolute;
10 | right: $pad*0.25;
11 | transform: translate(0, -50%) translate(0, $input-height/2);
12 | padding: $pad;
13 | color: rgba($doc-black, 0.25);
14 | z-index: 10;
15 | &:hover {
16 | color: $doc-darkest !important;
17 | }
18 | }
19 |
20 | .inputtool {
21 | display: none;
22 | flex-direction: column;
23 | flex: 1;
24 |
25 | .editor {
26 | position: relative;
27 | flex: 0 $input-height;
28 | background: $doc-white;
29 | border-bottom: solid 1px $doc-lighter;
30 |
31 | > .CodeMirror {
32 | padding: $pad 0 0 $pad;
33 | height: 100%;
34 | font-weight: bold;
35 | }
36 | }
37 |
38 |
39 | .result {
40 | flex: 1;
41 |
42 | textarea {
43 | box-sizing: border-box;
44 | padding: $pad 0 $pad $pad;
45 | resize: none;
46 | display: block;
47 | border: 0;
48 | background: none;
49 | width: 100%;
50 | height: 100%;
51 | margin: 0;
52 | font-family: $monospace;
53 | font-size: $font-size;
54 | color: $doc-black;
55 | &:focus {
56 | outline: none;
57 | }
58 | }
59 | }
60 | }
61 |
62 | .content {
63 | flex: 1 1 0%; // fix for FireFox (vs just `1`)
64 | min-height: 0; // fix for Chrome 72+
65 | overflow-y: auto;
66 | display: block;
67 | padding: $pad;
68 | }
69 |
70 | &.showinput {
71 | .inputtool {
72 | display: flex;
73 | }
74 | .content {
75 | display: none;
76 | }
77 | }
78 | }
--------------------------------------------------------------------------------
/dev/sass/views/tools/details.scss:
--------------------------------------------------------------------------------
1 | .doc > section.tools > article .details {
2 |
3 | > div.desc {
4 | padding: $pad * 0.5;
5 | color: $doc-mid;
6 | margin-bottom: 0.5em;
7 |
8 | code {
9 | color: $doc-dark;
10 | font-weight: bold;
11 | }
12 | }
13 |
14 | table {
15 | font-family: $monospace;
16 | width: 100%;
17 | border-spacing: 0;
18 | border: 0;
19 |
20 | tr:nth-child(odd) td {
21 | background: mix($doc-lighter, $doc-lightest, 40);
22 | }
23 |
24 | tr.match td {
25 | background: $doc-lighter;
26 | border-bottom: 1px dotted $doc-light;
27 | }
28 |
29 | tr.group:hover {
30 | @extend %selected-token;
31 | }
32 |
33 | td {
34 | padding: $pad * 0.5 $pad;
35 | vertical-align: top;
36 |
37 | &:first-child {
38 | font-weight: bold;
39 | white-space: nowrap;
40 | padding-right: 0;
41 | }
42 |
43 | &:nth-child(2) {
44 | color: $doc-mid;
45 | white-space: nowrap;
46 | border-right: 1px dotted $doc-light;
47 | }
48 |
49 | &:nth-child(3) {
50 | white-space: pre-wrap;
51 | width: 100%;
52 | color: $doc-black;
53 | user-select: text;
54 | user-select: contain;
55 |
56 | em {
57 | color: $doc-mid;
58 | }
59 | }
60 | }
61 | }
62 |
63 | span.hover {
64 | @extend %selected-token;
65 | }
66 |
67 | .group-1 { background: hsla(60, 100, 50, $details-group-alpha); }
68 | .group-2 { background: hsla(120, 100, 50, $details-group-alpha); }
69 | .group-3 { background: hsla(230, 100, 70, $details-group-alpha); }
70 | .group-4 { background: hsla(280, 100, 60, $details-group-alpha); }
71 | .group-5 { background: hsla(-10, 100, 60, $details-group-alpha); }
72 | .group-0 { background: hsla(30, 100, 50, $details-group-alpha); }
73 | .group-0, .group-1, .group-2, .group-3, .group-4, .group-5 { outline: 0.5px solid rgba($doc-black, 0.25); }
74 |
75 | }
--------------------------------------------------------------------------------
/dev/sass/views/tools/explain.scss:
--------------------------------------------------------------------------------
1 | .doc > section.tools > article .explain {
2 |
3 | > .desc {
4 | color: $doc-mid;
5 | display: block;
6 | padding: $pad*0.5;
7 | }
8 |
9 | div {
10 | padding: 0.5em 1em 0.5em 2.5em;
11 | margin-top: 0.75em;
12 | border: solid 1px rgba($doc-light, 0.5);
13 | border-left: solid 0.25em rgba($doc-light, 0.5);
14 | background: $doc-white;
15 | cursor: pointer;
16 | }
17 |
18 | div.selected {
19 | @extend %selected-token;
20 | }
21 |
22 | div.related {
23 | @extend %related-token;
24 | }
25 |
26 | div.applied {
27 | margin-left: 1em;
28 | margin-top: 0;
29 | border: none;
30 | background: rgba($doc-light, 0.3);
31 |
32 | + div.applied {
33 | margin-top: 1px;
34 | margin-left: 2em;
35 | }
36 | }
37 |
38 | div.error {
39 | background: mix($doc-white, $error-color, 85);
40 | border-color: $error-color;
41 |
42 | &.warning {
43 | /* nothing yet */
44 | }
45 |
46 | .error-title {
47 | font-weight: bold;
48 | }
49 |
50 | .warningtext {
51 | color: $doc-mid;
52 | margin-left: 0.5rem;
53 | }
54 | }
55 |
56 | code {
57 | &.token {
58 | display: inline-block;
59 | min-width: 1.25em;
60 | margin: -1px 0.5em 0 -1.75em;
61 | float: left;
62 | }
63 | font-weight: bold;
64 | }
65 |
66 | div.close {
67 | margin: 0;
68 | padding: 0;
69 | border: none;
70 | background: none;
71 | }
72 |
73 | /*
74 | Group backgrounds need to be transparent so the selection shows through, but it looks wrong for explain.
75 | */
76 | .exp-group-0, .exp-group-1, .exp-group-2, .exp-group-3 {
77 | border-color: rgba($group-color, 0.25);
78 | }
79 |
80 | .exp-group-0 { background: mix($doc-white, $groupbg-color, 92); }
81 |
82 | .exp-group-1 { background: mix($doc-white, $groupbg-color, 87); }
83 |
84 | .exp-group-2 { background: mix($doc-white, $groupbg-color, 82); }
85 |
86 | .exp-group-3 { background: mix($doc-white, $groupbg-color, 77); }
87 |
88 | // silly:
89 | .exp-group-4 { background: $doc-darker; color: $doc-white; padding: $pad; }
90 |
91 | .exp-group-set {
92 | background: mix($doc-white, $setbg-color, 75);
93 | border-color: rgba($set-color, 0.25);
94 | }
95 | }
--------------------------------------------------------------------------------
/dev/src/Flavor.js:
--------------------------------------------------------------------------------
1 | /*
2 | RegExr: Learn, Build, & Test RegEx
3 | Copyright (C) 2017 gskinner.com, inc.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | import Track from "./utils/Track";
20 |
21 | import EventDispatcher from "./events/EventDispatcher.js";
22 | import BrowserSolver from "./helpers/BrowserSolver.js";
23 | import ServerSolver from "./helpers/ServerSolver.js";
24 | import profiles from "./profiles/profiles.js";
25 |
26 | import app from "./app";
27 |
28 | export default class Flavor extends EventDispatcher {
29 |
30 | constructor(flavor) {
31 | super();
32 | this.value = app.prefs.read("flavor");
33 | this._browserSolver = new BrowserSolver();
34 | this._serverSolver = new ServerSolver();
35 | }
36 |
37 | set value(id) {
38 | let profile = profiles[(id && id.toLowerCase()) || "js"];
39 | if (!profile || profile === this._profile) { return; }
40 |
41 | this._profile = profile;
42 | this._buildSupportMap(profile);
43 | app.prefs.write("flavor", id);
44 | this.dispatchEvent("change");
45 | }
46 |
47 | get value() {
48 | return this._profile.id;
49 | }
50 |
51 | get profile() {
52 | return this._profile;
53 | }
54 |
55 | get profiles() {
56 | return [profiles.js, profiles.pcre];
57 | }
58 |
59 | get solver() {
60 | return this._profile.browser ? this._browserSolver : this._serverSolver;
61 | }
62 |
63 | isTokenSupported(id) {
64 | return !!this._profile._supportMap[id];
65 | }
66 |
67 | getDocs(id) {
68 | return this._profile.docs[id];
69 | }
70 |
71 | validateFlags(list) {
72 | let flags = this._profile.flags, dupes = {};
73 | return list.filter((id)=>(!!flags[id] && !dupes[id] && (dupes[id] = true)));
74 | }
75 |
76 | validateFlagsStr(str) {
77 | return this.validateFlags(str.split("")).join("");
78 | }
79 |
80 | isFlagSupported(id) {
81 | return !!this._profile.flags[id];
82 | }
83 |
84 | _buildSupportMap(profile) {
85 | if (profile._supportMap) { return; }
86 | let map = profile._supportMap = {}, props = Flavor.SUPPORT_MAP_PROPS, n;
87 | for (n in props) { this._addToSupportMap(map, profile[n], !!props[n]); }
88 | let o = profile.escCharCodes, esc = profile.escChars;
89 | for (n in o) { map["esc_"+o[n]] = true; }
90 | for (n in esc) { map["esc_"+esc[n]] = true; }
91 | }
92 |
93 | _addToSupportMap(map, o, rev) {
94 | if (rev) { for (let n in o) { map[o[n]] = true; } }
95 | else { for (let n in o) { map[n] = o[n]; } }
96 | }
97 | }
98 |
99 | Flavor.SUPPORT_MAP_PROPS = {
100 | // 1 = reverse, 0 - normal
101 | flags: 1,
102 | // escape is handled separately
103 | // escCharCodes is handled separately
104 | escCharTypes: 1,
105 | charTypes: 1,
106 | // unquantifiables not included
107 | // unicodeScripts not included
108 | // unicodeCategories not included
109 | // posixCharClasses not included
110 | // modes not included
111 | tokens: 0,
112 | substTokens: 0
113 | // config not included
114 | // docs not included
115 | };
--------------------------------------------------------------------------------
/dev/src/RefCoverage.js:
--------------------------------------------------------------------------------
1 | /*
2 | RegExr: Learn, Build, & Test RegEx
3 | Copyright (C) 2017 gskinner.com, inc.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | import core from "./profiles/core.js";
20 | import app from "./app";
21 |
22 | export default class RefCoverage {
23 | constructor() {
24 | app.flavor._buildSupportMap(core);
25 | let ref = app.reference._idMap, undoc=[], unused=[], all=core._supportMap;
26 | let ignore = {
27 | "escchar": true, // literal char
28 | "groupclose": true,
29 | "setclose": true,
30 | "condition": true, // proxies to conditional
31 | "conditionalelse": true, // proxies to conditional
32 | subst_$group: true, // resolved to subst_group
33 | subst_$bgroup: true, // resolved to subst_group
34 | subst_bsgroup: true, // resolved to subst_group
35 | escoctalo: true // resolved to escoctal
36 | }
37 |
38 | for (let n in all) { if (!ref[n] && !ignore[n]) { undoc.push(n); } }
39 | for (let n in ref) { if (!all[n] && !ref[n].kids) { unused.push(n); } }
40 |
41 | console.log("--- UNDOCUMENTED IDS ---\n"+undoc.join("\n")+"\n\n--- UNUSED DOCS? ---\n"+unused.join("\n"));
42 | }
43 | }
--------------------------------------------------------------------------------
/dev/src/SubstLexer.js:
--------------------------------------------------------------------------------
1 | /*
2 | RegExr: Learn, Build, & Test RegEx
3 | Copyright (C) 2017 gskinner.com, inc.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | import Utils from "./utils/Utils";
20 |
21 | import app from "./app";
22 |
23 | export default class SubstLexer {
24 | constructor() {
25 | this.profile = null;
26 | }
27 |
28 | set profile(profile) {
29 | this._profile = profile;
30 | this.string = this.token = this.errors = null;
31 | }
32 |
33 | parse(str) {
34 | if (!this._profile) { return null; }
35 |
36 | this.token = null;
37 | this.string = str;
38 | this.errors = [];
39 |
40 | // TODO: should this be passed in from Tools?
41 | let capGroups = app.expression.lexer.captureGroups;
42 |
43 | let prev=null, token, c;
44 | for (let i=0, l=str.length; i= 10 && capGroups[(num = (num/10|0))-1]) { l = numStr.length-1; }
116 | if (l) {
117 | token.l += l;
118 | // we don't assign the original type, because the docs combine them all into one id:
119 | token.type = num > 0 ? "subst_group" : "subst_0match";
120 | token.clss = "subst";
121 | if (num > 0) { token.group = capGroups[num-1]; }
122 | }
123 | }
124 | }
125 |
126 | SubstLexer.$_TYPES = {
127 | "$": "subst_$esc",
128 | "&": "subst_$&match",
129 | "`": "subst_$before",
130 | "'": "subst_$after",
131 | "0": "subst_0match"
132 | };
133 |
134 | SubstLexer.SUBST_ESC_RE = new RegExp("^"+Utils.SUBST_ESC_RE.source,"i");
135 |
--------------------------------------------------------------------------------
/dev/src/app.js:
--------------------------------------------------------------------------------
1 | /*
2 | RegExr: Learn, Build, & Test RegEx
3 | Copyright (C) 2017 gskinner.com, inc.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | import "../lib/codemirror.js";
20 | import "../lib/clipboard.js";
21 | import "../lib/native.js";
22 | import RegExr from "./RegExr";
23 |
24 | let app = new RegExr();
25 | export default app;
--------------------------------------------------------------------------------
/dev/src/controls/LinkRow.js:
--------------------------------------------------------------------------------
1 | /*
2 | RegExr: Learn, Build, & Test RegEx
3 | Copyright (C) 2017 gskinner.com, inc.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | import $ from "../utils/DOMUtils";
20 | import Utils from "../utils/Utils";
21 | import app from "../app";
22 |
23 | export default class LinkRow {
24 | constructor(el) {
25 | this.el = el;
26 | this._initUI();
27 | this.url = null;
28 | }
29 |
30 | set pattern(val) {
31 | let url = Utils.getPatternURLStr(val)
32 | this._pattern = val;
33 | $.query(".url", this.el).innerText = url || "";
34 | $.toggleClass(this.el, "disabled", !url);
35 | $.toggleClass(this.el, "active", !!url);
36 | }
37 |
38 | showMessage(message) {
39 | // for some reason this displays one line too low if it's synchronous:
40 | setTimeout(()=>app.tooltip.toggle.showOn("linkrow", message, $.query(".copy.icon", this.el), true, 0), 1);
41 | }
42 |
43 | _initUI() {
44 | this.el.onclick = (evt) => this._onClick(evt);
45 |
46 | let fld=$.query(".url", this.el), copyBtn = $.query(".copy", this.el);
47 | let clipboard = new Clipboard(copyBtn, { target: () => fld });
48 | clipboard.on("success", () => app.tooltip.toggle.toggleOn("copy", "Copied to clipboard.", copyBtn, true, 3));
49 | clipboard.on("error", (e) => app.tooltip.toggle.toggleOn("copy", Utils.getCtrlKey()+"-C to copy.", copyBtn, true, 3)); // TODO: cmd/ctrl
50 | }
51 |
52 | _onClick(evt) {
53 | if ($.query(".copy", this.el).contains(evt.target)) { return; }
54 | if (evt.which === 2 || evt.metaKey || evt.ctrlKey) {
55 | window.open(Utils.getPatternURL(this._pattern));
56 | } else {
57 | app.load(this._pattern);
58 | }
59 | }
60 | };
--------------------------------------------------------------------------------
/dev/src/controls/List.js:
--------------------------------------------------------------------------------
1 | /*
2 | RegExr: Learn, Build, & Test RegEx
3 | Copyright (C) 2017 gskinner.com, inc.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | import $ from "../utils/DOMUtils";
20 | import EventDispatcher from "../events/EventDispatcher";
21 |
22 | export default class List extends EventDispatcher {
23 | constructor(el, opts) {
24 | super();
25 | this.el = el;
26 | this.multi = opts.multi;
27 | this.template = opts.template;
28 | this.data = opts.data;
29 | if (opts.selected) { this.selected = opts.selected; }
30 | }
31 |
32 | set data(data) {
33 | $.empty(this.el);
34 | this._data = data;
35 | if (!data || !data.length) { return; }
36 | for (let i=0, l=data.length; i$.addClass($.query("[data-id='"+id+"']",this.el), "selected"));
49 |
50 | if (!this.multi) { this.scrollTo(ids[0]); }
51 | }
52 |
53 | get selected() {
54 | let els = $.queryAll("li.selected", this.el);
55 | if (!els[0]) { return null; }
56 | if (!this.multi) { return els[0].dataset.id; }
57 | let ids = [];
58 | for (let i=0, l=els.length; i o.id === id);
70 | }
71 |
72 | get selectedItem() {
73 | let el = this.selectedEl;
74 | return el && el.item;
75 | }
76 |
77 | get selectedEl() {
78 | return $.query("li.selected", this.el);
79 | }
80 |
81 | refresh() {
82 | let sel = this.selected;
83 | this.data = this._data;
84 | this.selected = sel;
85 | }
86 |
87 | addItem(o, selected=null) {
88 | let label, id, sel;
89 | let f=(evt) => this.handleClick(evt), template=this.template;
90 | if (typeof o === "string") {
91 | id = o;
92 | label = template ? template(o) : o;
93 | } else {
94 | if (o.hide) { return; }
95 | id = o.id || o.label;
96 | label = template ? template(o) : o.label;
97 | if (selected === null) { sel = o.selected; }
98 | }
99 | let item = $.create("li", sel ? "selected" : null, label, this.el);
100 | item.dataset.id = id;
101 | item.item = o;
102 | item.addEventListener("click", f);
103 | item.addEventListener("dblclick", f);
104 |
105 | if (selected) {
106 | this.selected = o.id;
107 | }
108 | }
109 |
110 | removeItem(id) {
111 | let el = $.query("[data-id='"+id+"']",this.el);
112 | el && el.remove();
113 | }
114 |
115 | handleClick(evt) {
116 | let id = evt.currentTarget.dataset.id, old = this.selected;
117 | if (!this.getEl(id)) { return; }
118 | if (evt.type === "dblclick") {
119 | if (id != null) { this.dispatchEvent("dblclick"); }
120 | return;
121 | } else if (this.multi) {
122 | $.toggleClass(evt.currentTarget, "selected");
123 | } else if (old === id) {
124 | if (id != null) { this.dispatchEvent("selclick"); }
125 | return;
126 | } else {
127 | this.selected = id;
128 | }
129 | if (!this.dispatchEvent("change", false, true)) { this.selected = old; }
130 | }
131 |
132 | scrollTo(id=this.selected) {
133 | let el = this.getEl(id);
134 | if (!el) { return; }
135 | //el.scrollIntoView(); // this is too jumpy, but would handle horizontal.
136 |
137 | let scrollEl = this.scrollEl || this.el;
138 | let top = el.offsetTop - scrollEl.offsetTop;
139 | if (top + el.offsetHeight > scrollEl.scrollTop+scrollEl.offsetHeight) {
140 | scrollEl.scrollTop = top+el.offsetHeight-scrollEl.offsetHeight+10;
141 | } else if (top < scrollEl.scrollTop) {
142 | scrollEl.scrollTop = top-10;
143 | }
144 | }
145 |
146 | getEl(id) {
147 | return $.query("[data-id='"+id+"']", this.el);
148 | }
149 | };
--------------------------------------------------------------------------------
/dev/src/controls/Status.js:
--------------------------------------------------------------------------------
1 | /*
2 | RegExr: Learn, Build, & Test RegEx
3 | Copyright (C) 2017 gskinner.com, inc.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | import $ from "../utils/DOMUtils";
20 | import app from "../app";
21 |
22 | export default class Status {
23 | constructor(el) {
24 | if (!el) { el = document.createElement("div"); }
25 | this.el = el;
26 | $.addClass(el, "status");
27 |
28 | el.addEventListener("mouseover", () => this._showTooltip());
29 | el.addEventListener("mouseout", () => this._hideTooltip());
30 | }
31 |
32 | distract() {
33 | this.el.innerHTML = '';
34 | this._show();
35 | return this;
36 | }
37 |
38 | hide(t=0) {
39 | this._clearTimeout();
40 | if (t) {
41 | this._timeoutId = setTimeout(()=>this._hide(), t*1000);
42 | } else { this._hide(); }
43 | return this;
44 | }
45 |
46 | success() {
47 | this.el.innerHTML = '';
48 | this._show();
49 | return this;
50 | }
51 |
52 | error(msg) {
53 | let el = this.el;
54 | el.innerHTML = '';
55 | this._show();
56 | this._ttMsg = msg;
57 | return this;
58 | }
59 |
60 | _showTooltip() {
61 | if (!this._ttMsg) { return; }
62 | app.tooltip.hover.showOn("status", this._ttMsg, this.el, true, 0);
63 | }
64 |
65 | _hideTooltip() {
66 | app.tooltip.hover.hide("status");
67 | }
68 |
69 | _show() {
70 | this.el.style.display = null;
71 | this._ttMsg = null;
72 | this._hideTooltip();
73 | this._clearTimeout();
74 | }
75 |
76 | _hide() {
77 | this.el.style.display = "none";
78 | this._hideTooltip();
79 | this._clearTimeout();
80 | }
81 |
82 | _clearTimeout() {
83 | if (this._timeoutId == null) { return; }
84 | clearTimeout(this._timeoutId);
85 | this._timeoutId = null;
86 | }
87 | }
--------------------------------------------------------------------------------
/dev/src/controls/Tooltip.js:
--------------------------------------------------------------------------------
1 | /*
2 | RegExr: Learn, Build, & Test RegEx
3 | Copyright (C) 2017 gskinner.com, inc.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | import $ from "../utils/DOMUtils";
20 |
21 | export default class Tooltip {
22 |
23 | constructor(el, transition=false) {
24 | this.el = $.remove(el);
25 | this.transition = transition;
26 | this.contentEl = $.query(".content", el);
27 | this.tipEl = $.query(".tip", el);
28 | this.hideF = (evt)=> Date.now()>this._showT && this.handleBodyClick(evt);
29 | this.curId = null;
30 | }
31 |
32 | toggle(id, content, x, y, autohide, th) {
33 | if (id === this.curId) { return this.hide(id); }
34 | this.show(id, content, x, y, autohide, th);
35 | }
36 |
37 | toggleOn(id, content, el, autohide, th) {
38 | if (id === this.curId) { return this.hide(id); }
39 | this.showOn(id, content, el, autohide, th);
40 | this.toggleEl = el;
41 | $.addClass(el, "selected");
42 | }
43 |
44 | hide(id) {
45 | if (id && this.curId !== id) { return; }
46 | let el = this.el, elStyle = el.style;
47 | $.empty($.query(".content", $.remove(el)));
48 | $.removeClass(el, "flipped");
49 | document.body.removeEventListener("mousedown", this.hideF);
50 |
51 | if (this.toggleEl) {
52 | $.removeClass(this.toggleEl, "selected");
53 | this.toggleEl = null;
54 | }
55 |
56 | // reset position and width so that content wrapping resolves properly:
57 | elStyle.left = elStyle.top = "0";
58 | elStyle.width = "";
59 | if (this.transition) {
60 | elStyle.opacity = 0;
61 | elStyle.marginTop = "-0.25em";
62 | }
63 | this.curId = null;
64 | }
65 |
66 | show(id, content, x, y, autohide = false, th = 0) {
67 | this.hide();
68 | if (!content) { return; }
69 |
70 | let el = this.el, elStyle = el.style, contentEl = this.contentEl, body = document.body, pad = 8;
71 | if (content instanceof HTMLElement) { contentEl.appendChild(content); }
72 | else { contentEl.innerHTML = content; }
73 |
74 | if (autohide) {
75 | this._showT = Date.now()+30; // ignore double clicks and events in the current stack.
76 | body.addEventListener("mousedown", this.hideF);
77 | }
78 |
79 | body.appendChild(el);
80 |
81 | let wh = window.innerHeight, ww = window.innerWidth;
82 | let rect = el.getBoundingClientRect(), w = rect.right - rect.left, h = rect.bottom - rect.top, off = 0;
83 | if (y + h > wh - pad) {
84 | $.addClass(el, "flipped");
85 | y -= th;
86 | }
87 | if (x - w / 2 < pad) { off = pad - x + w / 2; }
88 | else if (x + w / 2 > ww - pad) { off = ww - pad - x - w / 2; }
89 | this.tipEl.style.marginRight = Math.max(-w / 2 + 10, Math.min(w / 2 - 10, off)) * 2 + "px";
90 | elStyle.width = Math.ceil(w/2)*2 + "px";
91 | elStyle.top = Math.round(y) + "px";
92 | elStyle.left = Math.round(x + off) + "px";
93 | if (this.transition) {
94 | elStyle.opacity = 1;
95 | elStyle.marginTop = 0;
96 | }
97 |
98 | this.curId = id;
99 | }
100 |
101 | showOn(id, content, el, autohide, th=0) {
102 | let rect = el.getBoundingClientRect();
103 | let x = Math.round((rect.left+rect.right)/2);
104 | let y = rect.bottom+th;
105 | let h = rect.bottom-rect.top;
106 | this.show(id, content, x, y, autohide, h);
107 | }
108 |
109 | handleBodyClick(evt) {
110 | let id = this.curId;
111 | if (this.el.contains(evt.target) || (this.toggleEl && this.toggleEl.contains(evt.target))) { return; }
112 | this.hide(id);
113 | }
114 | }
--------------------------------------------------------------------------------
/dev/src/helpers/Prefs.js:
--------------------------------------------------------------------------------
1 | /*
2 | RegExr: Learn, Build, & Test RegEx
3 | Copyright (C) 2017 gskinner.com, inc.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 |
20 | export default class Prefs {
21 | constructor (el) {
22 | this._load();
23 | }
24 |
25 | read(key) {
26 | return this._data[key];
27 | }
28 |
29 | write(key, value) {
30 | if (this._data[key] === value) { return; }
31 | this._data[key] = value;
32 | this._save();
33 | }
34 |
35 | clear(key) {
36 | delete(this._data[key]);
37 | this._save();
38 | }
39 |
40 | _load() {
41 | let match = /(?:^|;\s*)prefs=\s*([^;]*)/.exec(document.cookie);
42 | if (match && match[1]) {
43 | try {
44 | this._data = JSON.parse(unescape(match[1]));
45 | return;
46 | } catch (e) {}
47 | }
48 | this._data = {};
49 | }
50 |
51 | _save() {
52 | let str = escape(JSON.stringify(this._data));
53 | document.cookie = "prefs="+str+"; expires=Fri, 31 Dec 9999 23:59:59 GMT;";
54 | }
55 |
56 | }
--------------------------------------------------------------------------------
/dev/src/helpers/ServerSolver.js:
--------------------------------------------------------------------------------
1 | /*
2 | RegExr: Learn, Build, & Test RegEx
3 | Copyright (C) 2017 gskinner.com, inc.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | import Utils from "../utils/Utils";
20 | import Server from "../net/Server";
21 |
22 | export default class ServerSolver {
23 |
24 | solve(o, callback) {
25 | // unescape tool input:
26 | if (o.tool && o.tool.input != null) { o.tool.input = Utils.unescSubstStr(o.tool.input); }
27 | if (this._serverPromise) { this._serverPromise.abort(); }
28 | Utils.defer(()=>this._solve(o, callback), "ServerSolver._solve", 250);
29 | }
30 |
31 | _solve(o, callback) {
32 | this._callback = callback;
33 | this._serverPromise = Server.solve(o).then((o) => this._onLoad(o)).catch((o) => this._onError(o));
34 | }
35 |
36 | _onLoad(data) {
37 | this._callback(data);
38 | }
39 |
40 | _onError(msg) {
41 | this._callback({error:{id:msg}});
42 | }
43 | }
--------------------------------------------------------------------------------
/dev/src/profiles/pcre.js:
--------------------------------------------------------------------------------
1 | /*
2 | RegExr: Learn, Build, & Test RegEx
3 | Copyright (C) 2017 gskinner.com, inc.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | /*
20 | The PCRE profile is almost a straight copy of the core profile.
21 | */
22 | let y=true, n=false;
23 |
24 | let pcre = {
25 | id: "pcre",
26 | label: "PCRE",
27 | browser: false,
28 |
29 | flags: {
30 | "u": n,
31 | "y": n
32 | },
33 |
34 | badEscChars: "uUlLN".split("").reduce((o, c) => { o[c] = y; return o}, {}),
35 |
36 | escCharCodes: {
37 | "v": n // vertical tab // PCRE support \v as vertical whitespace
38 | },
39 |
40 | tokens: {
41 | "escunicodeu": n, // \uFFFF
42 | "escunicodeub": n, // \u{00A9}
43 | // octalo PCRE 8.34+
44 | },
45 |
46 | substTokens: {
47 | "subst_$esc": n, // $$
48 | "subst_$&match": n, // $&
49 | "subst_$before": n, // $`
50 | "subst_$after": n // $'
51 | },
52 |
53 | config: {
54 | "reftooctalalways": n, // does a single digit reference \1 become an octal? (vs remain an unmatched ref)
55 | "substdecomposeref": n, // will a subst reference decompose? (ex. \3 becomes "\" & "3" if < 3 groups)
56 | "looseesc": n // should unrecognized escape sequences match the character (ex. \u could match "u") // disabled when `u` flag is set
57 | },
58 |
59 | docs: {
60 | "escoctal":{ext:"+
The syntax \\o{FFF} is also supported.
"},
61 | "numref":{
62 | ext:"
There are multiple syntaxes for this feature: \\1\\g1\\g{1}.
"+
63 | "
The latter syntaxes support relative values preceded by + or -. For example \\g-1 would match the group preceding the reference.
"
64 | },
65 | "lazy": { ext:"+
This behaviour is reversed by the ungreedy (U) flag/modifier.
" }
66 | }
67 | };
68 |
69 | export default pcre;
--------------------------------------------------------------------------------
/dev/src/profiles/profiles.js:
--------------------------------------------------------------------------------
1 | /*
2 | RegExr: Learn, Build, & Test RegEx
3 | Copyright (C) 2017 gskinner.com, inc.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | import core from "./core";
20 | import pcre from "./pcre";
21 | import js from "./javascript";
22 |
23 | let profiles = {core};
24 | export default profiles;
25 |
26 | profiles.pcre = merge(core, pcre);
27 | profiles.js = merge(core, js);
28 |
29 | function merge(p1, p2) {
30 | // merges p1 into p2, essentially just a simple deep copy without array support.
31 | for (let n in p1) {
32 | if (p2[n] === false) { continue; }
33 | else if (typeof p1[n] === "object") { p2[n] = merge(p1[n], p2[n] || {}); }
34 | else if (p2[n] === undefined) { p2[n] = p1[n]; }
35 | }
36 | return p2;
37 | };
--------------------------------------------------------------------------------
/dev/src/utils/CMUtils.js:
--------------------------------------------------------------------------------
1 | /*
2 | RegExr: Learn, Build, & Test RegEx
3 | Copyright (C) 2017 gskinner.com, inc.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | /*
20 | Utilities for working with CodeMirror.
21 | */
22 |
23 | import Utils from "../utils/Utils";
24 | import $ from "../utils/DOMUtils";
25 |
26 | let CMUtils = {};
27 | export default CMUtils;
28 |
29 | CMUtils.create = function (target, opts={}, width="100%", height="100%") {
30 | let keys = {}, ctrlKey = Utils.getCtrlKey();
31 | //keys[ctrlKey + "-Z"] = keys[ctrlKey + "-Y"] = keys["Shift-" + ctrlKey + "-Z"] = () => false; // block CM handling
32 |
33 | let o = Utils.copy({
34 | lineNumbers: false,
35 | tabSize: 3,
36 | indentWithTabs: true,
37 | extraKeys: keys,
38 | specialChars: /[ \u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/,
39 | specialCharPlaceholder: (ch) => $.create("span", ch === " " ? "cm-space" : "cm-special", " ") // needs to be a space so wrapping works
40 | }, opts);
41 |
42 | let cm = CodeMirror(target, o);
43 | cm.setSize(width, height);
44 |
45 |
46 | if (cm.getOption("maxLength")) {
47 | cm.on("beforeChange", CMUtils.enforceMaxLength);
48 | }
49 | if (cm.getOption("singleLine")) {
50 | cm.on("beforeChange", CMUtils.enforceSingleLine);
51 | }
52 |
53 | return cm;
54 | };
55 |
56 | CMUtils.getCharIndexAt = function (cm, winX, winY) {
57 | let pos = cm.coordsChar({left: winX, top: winY}, "page");
58 | // test current and prev character, since CM seems to use the center of each character for coordsChar:
59 | for (let i = 0; i <= 1; i++) {
60 | let rect = cm.charCoords(pos, "page");
61 | if (winX >= rect.left && winX <= rect.right && winY >= rect.top && winY <= rect.bottom) {
62 | return cm.indexFromPos(pos);
63 | }
64 | if (pos.ch-- <= 0) {
65 | break;
66 | }
67 | }
68 | return null;
69 | };
70 | /*
71 | // unused?
72 | CMUtils.getEOLPos = function (cm, pos) {
73 | if (!isNaN(pos)) {
74 | pos = cm.posFromIndex(pos);
75 | }
76 | let rect = cm.charCoords(pos, "local"), w = cm.getScrollInfo().width;
77 | return cm.coordsChar({left: w - 1, top: rect.top}, "local");
78 | };
79 | */
80 | CMUtils.getCharRect = function (cm, index) {
81 | if (index == null) { return null; }
82 | let pos = cm.posFromIndex(index), rect = cm.charCoords(pos);
83 | rect.x = rect.left;
84 | rect.y = rect.top;
85 | rect.width = rect.right - rect.left;
86 | rect.height = rect.bottom - rect.top;
87 | return rect;
88 | };
89 |
90 |
91 | CMUtils.enforceMaxLength = function (cm, change) {
92 | let maxLength = cm.getOption("maxLength");
93 | if (maxLength && change.update) {
94 | let str = change.text.join("\n");
95 | let delta = str.length - (cm.indexFromPos(change.to) - cm.indexFromPos(change.from));
96 | if (delta <= 0) { return true;
97 | }
98 | delta = cm.getValue().length + delta - maxLength;
99 | if (delta > 0) {
100 | str = str.substr(0, str.length - delta);
101 | change.update(change.from, change.to, str.split("\n"));
102 | }
103 | }
104 | return true;
105 | };
106 |
107 | CMUtils.enforceSingleLine = function (cm, change) {
108 | if (change.update) {
109 | let str = change.text.join("").replace(/(\n|\r)/g, "");
110 | change.update(change.from, change.to, [str]);
111 | }
112 | return true;
113 | };
114 |
115 | CMUtils.selectAll = function(cm) {
116 | cm.focus();
117 | cm.setSelection({ch:0,line:0},{ch:0, line:cm.lineCount()});
118 | }
119 |
120 | CMUtils.calcRangePos = function(cm, i, l=0, o={}) {
121 | let doc = cm.getDoc();
122 | o.startPos = doc.posFromIndex(i);
123 | o.endPos = doc.posFromIndex(i+l);
124 | return o;
125 | }
--------------------------------------------------------------------------------
/dev/src/utils/Track.js:
--------------------------------------------------------------------------------
1 | /*
2 | RegExr: Learn, Build, & Test RegEx
3 | Copyright (C) 2017 gskinner.com, inc.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | let Track = {};
20 | export default Track;
21 |
22 | Track.GA_ID = "UA-3579542-6";
23 |
24 | Track.page = function(path) {
25 | gtag("config", Track.GA_ID, {"page_path": "/"+path});
26 | };
27 |
28 | // https://developers.google.com/analytics/devguides/collection/gtagjs/events
29 | Track.event = function(name, category, label) {
30 | let o = {};
31 | if (category) { o.event_category = category; }
32 | if (label) { o.event_label = label; }
33 | gtag("event", name, o);
34 | }
35 |
36 |
37 |
--------------------------------------------------------------------------------
/dev/src/utils/UID.js:
--------------------------------------------------------------------------------
1 | /*
2 | RegExr: Learn, Build, & Test RegEx
3 | Copyright (C) 2017 gskinner.com, inc.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | let UID = {
20 | _next: 0,
21 | get id() { return Date.now() + "_" + this._next++; },
22 | assign(list, force=false) {
23 | list.forEach((o) => o.id = o.id == null || force ? this.id : o.id );
24 | }
25 | };
26 | export default UID;
27 |
28 |
--------------------------------------------------------------------------------
/dev/src/views/Account.js:
--------------------------------------------------------------------------------
1 | /*
2 | RegExr: Learn, Build, & Test RegEx
3 | Copyright (C) 2017 gskinner.com, inc.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | import EventDispatcher from "../events/EventDispatcher";
20 |
21 | import $ from "../utils/DOMUtils"
22 | import Track from "../utils/Track"
23 |
24 | import List from "../controls/List";
25 | import Server from "../net/Server";
26 |
27 | import app from "../app";
28 |
29 | export default class Account extends EventDispatcher {
30 | constructor () {
31 | super();
32 | this._value = {};
33 | this._initUI();
34 | }
35 |
36 | get value() {
37 | return this._value;
38 | }
39 |
40 | set value(val={}) {
41 | this._value = val;
42 | this._updateUI();
43 | this.dispatchEvent("change");
44 | }
45 |
46 | get userId() { return this._value.userId; }
47 | get author() { return this._value.author || this._value.username || ""; }
48 | get username() { return this._value.username || ""; }
49 | get authenticated() { return !!this._value.username; } // this._value.authenticated;
50 | get type() { return this._value.type; }
51 |
52 | showTooltip() {
53 | app.tooltip.toggle.toggleOn("signin", this.tooltipEl, this.signinBtn, true, 20);
54 | }
55 |
56 | // private methods:
57 | _initUI() {
58 | let template = (o) => ''+o;
59 | this.signinBtn = $.query(".header .signin");
60 | this.tooltipEl = $.query("#library > #tooltip-signin");
61 | this.signinEl = $.query(".signin", this.tooltipEl);
62 | this.signoutEl = $.query(".signout", this.tooltipEl);
63 | $.query(".signoutbtn", this.signoutEl).addEventListener("click", (evt) => this._doSignout());
64 | this.signinBtn.addEventListener("click", (evt) => this.showTooltip());
65 | $.query(".icon.help", this.signinEl).addEventListener("click", ()=> app.sidebar.goto("signin"));
66 | this.signinList = new List($.query("ul.list", this.signinEl), {data:["GitHub", "Google"], template});
67 | this.signinList.on("change", ()=>this._signinListChange());
68 | }
69 |
70 | _updateUI() {
71 | let auth = this.authenticated;
72 | $.toggleClass(this.tooltipEl, "authenticated", auth);
73 | $.query(".label", this.signinBtn).innerText = auth ? "Sign Out" : "Sign In";
74 | if (auth) {
75 | $.query(".username", this.signoutEl).innerText = this.username;
76 | $.query(".type", this.signoutEl).innerText = this.type;
77 | }
78 | }
79 |
80 | _doSignout() {
81 | $.addClass(this.tooltipEl, "wait");
82 | Server.logout().then((data) => { this._handleSignout(data); }).finally(()=>this._cleanSignout());
83 | }
84 |
85 | _handleSignout(data) {
86 | this.value = data;
87 | }
88 |
89 | _cleanSignout(err) {
90 | $.removeClass(this.tooltipEl, "wait");
91 | }
92 |
93 | _signinListChange() {
94 | let service = this.signinList.selected.toLowerCase();
95 | $.addClass(this.tooltipEl, "wait");
96 | Track.event("login", "access", service);
97 | setTimeout(() => Server.login(service), 100);
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/dev/src/views/Community.js:
--------------------------------------------------------------------------------
1 | /*
2 | RegExr: Learn, Build, & Test RegEx
3 | Copyright (C) 2017 gskinner.com, inc.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | import LinkRow from "../controls/LinkRow";
20 | import $ from "../utils/DOMUtils";
21 | import Utils from "../utils/Utils";
22 | import Example from "./Example";
23 | import Server from "../net/Server";
24 |
25 | import app from "../app";
26 |
27 | // also used for My Patterns.
28 | export default class CommunityContent {
29 | constructor (el) {
30 | this.el = el;
31 | this.example = new Example();
32 | el.appendChild(this.example.el);
33 | $.query(".icon.thumbup", el).addEventListener("click", ()=>this._rate(1));
34 | $.query(".icon.thumbdown", el).addEventListener("click", ()=>this._rate(-1));
35 | $.query(".icon.favorites", el).addEventListener("click", ()=>this._favorite());
36 | this.linkRow = new LinkRow($.query(".row.link", el))
37 | $.query(".icon.share", el).addEventListener("click", ()=>this._share());
38 | }
39 |
40 | set item(o) {
41 | let el = this.el;
42 | this._pattern = o;
43 | $.query(".author", el).innerText = o.author ? "by "+o.author : "";
44 | $.query(".name.label", el).innerText = o.name;
45 | $.query(".desc", el).innerText = o.description || "No description available.";
46 | this._updateRating();
47 | this._updateFavorite();
48 | this.example.example = [o.expression, o.text];
49 |
50 | this.linkRow.pattern = o;
51 | }
52 |
53 | // private methods:
54 | _updateRating() {
55 | let o = this._pattern, el = this.el;
56 | $.query(".rating", el).innerText = o.rating.toFixed(1);
57 | $.removeClass($.query(".icon.rate.selected", el), "selected");
58 | if (o.userRating === 1) { $.addClass($.query(".icon.thumbup", el), "selected"); }
59 | else if (o.userRating === -1) { $.addClass($.query(".icon.thumbdown", el), "selected"); }
60 | }
61 |
62 | _updateFavorite() {
63 | let o = this._pattern, el = this.el;
64 | $.toggleClass($.query(".icon.favorites", el), "selected", !!o.favorite);
65 | }
66 |
67 | _rate(val) {
68 | let o = this._pattern;
69 | o.userRating = (val === o.userRating) ? 0 : val;
70 | this._updateRating();
71 |
72 | Server.rate(o.id, o.userRating).then((data) => this._handleRate(data));
73 | }
74 |
75 | _share() {
76 | app.load(this._pattern);
77 | app.share.show();
78 | }
79 |
80 | _handleRate(data) {
81 | if (data.id === this._pattern.id) {
82 | this._pattern.rating = data.rating;
83 | this._updateRating();
84 | }
85 | }
86 |
87 | _favorite() {
88 | let o = this._pattern;
89 | Server.favorite(o.id, !o.favorite).then((data) => this._handleFavorite(data));
90 | }
91 |
92 | _handleFavorite(data) {
93 | if (data.id === this._pattern.id) {
94 | this._pattern.favorite = data.favorite;
95 | this._updateFavorite();
96 | }
97 | }
98 |
99 | }
--------------------------------------------------------------------------------
/dev/src/views/Example.js:
--------------------------------------------------------------------------------
1 | /*
2 | RegExr: Learn, Build, & Test RegEx
3 | Copyright (C) 2017 gskinner.com, inc.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | import $ from "../utils/DOMUtils";
20 | import Utils from "../utils/Utils.js";
21 |
22 | import app from "../app";
23 |
24 | export default class Example {
25 | constructor (title, ex) {
26 | this.el = $.create("div", "example");
27 | this.title = title;
28 | this.example = ex;
29 | }
30 |
31 | set example(ex) {
32 | if (ex === this._example) { return; }
33 | this._example = ex;
34 |
35 | let str = "", txt, exp, regex;
36 | if (ex) {
37 | exp = ex[0];
38 | txt = ex[1];
39 | regex = Utils.getRegExp(exp, "g");
40 | if (this.title) { str += "
" + this.title + "
"; }
41 | str += "" + Utils.htmlSafe(exp) + "";
42 | if (txt && regex) {
43 | let over=Math.max(0, txt.length-160), s=txt;
44 | if (over) { s = Utils.htmlSafe(s.substr(0,159)); }
45 | if (regex) { s = s.replace(regex, "$&"); }
46 | // TODO: this won't match on html elements:
47 | str += "" + s + (over?"\u2026" : "") + "";
48 | }
49 | }
50 | this.el.innerHTML = str;
51 | if (exp) {
52 | $.query("code.expression > .load", this.el).addEventListener("click", ()=> {
53 | // TODO: this will need to be updated when we support other delimiters:
54 | app.expression.value = exp[0] === "/" ? exp : "/"+exp+"/g";
55 | });
56 |
57 | }
58 | if (txt) { $.query("code.text > .load", this.el).addEventListener("click", ()=> app.text.value = txt); }
59 | }
60 |
61 |
62 | }
--------------------------------------------------------------------------------
/dev/src/views/ExpressionHighlighter.js:
--------------------------------------------------------------------------------
1 | /*
2 | RegExr: Learn, Build, & Test RegEx
3 | Copyright (C) 2017 gskinner.com, inc.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 |
20 | import EventDispatcher from "../events/EventDispatcher";
21 | import CMUtils from "../utils/CMUtils";
22 |
23 | export default class ExpressionHighlighter extends EventDispatcher {
24 | constructor(cm) {
25 | super();
26 | this.cm = cm;
27 | this._activeMarks = [];
28 | this._hoverMarks = [];
29 | this._hoverToken = null;
30 | }
31 |
32 | clear() {
33 | this.cm.operation(() => {
34 | let marks = this._activeMarks;
35 | for (var i = 0, l = marks.length; i < l; i++) {
36 | marks[i].clear();
37 | }
38 | marks.length = 0;
39 | });
40 | }
41 |
42 | draw(token) {
43 | let cm = this.cm, pre = ExpressionHighlighter.CSS_PREFIX;
44 |
45 | this.clear();
46 | cm.operation(() => {
47 |
48 | let groupClasses = ExpressionHighlighter.GROUP_CLASS_BY_TYPE;
49 | let doc = cm.getDoc(), endToken, marks = this._activeMarks;
50 |
51 | while (token) {
52 | if (token.clear) {
53 | token = token.next;
54 | continue;
55 | }
56 | token = this._calcTokenPos(token);
57 |
58 | var className = pre + (token.clss || token.type);
59 | if (token.error) {
60 | className += " " + pre + (token.error.warning ? "warning" : "error");
61 | }
62 |
63 | if (className) {
64 | marks.push(doc.markText(token.startPos, token.endPos, {className: className}));
65 | }
66 |
67 | if (token.close) {
68 | endToken = this._calcTokenPos(token.close);
69 | className = groupClasses[token.clss || token.type];
70 | if (className) {
71 | className = className.replace("%depth%", token.depth);
72 | marks.push(doc.markText(token.startPos, endToken.endPos, {className: className}));
73 | }
74 | }
75 | token = token.next;
76 | }
77 | });
78 | }
79 |
80 | set hoverToken(token) {
81 | if (token === this._hoverToken) { return; }
82 | if (token && token.set && token.set.indexOf(this._hoverToken) !== -1) { return; }
83 | while (this._hoverMarks.length) { this._hoverMarks.pop().clear(); }
84 |
85 | this._hoverToken = token;
86 | if (token) {
87 | if (token.open) {
88 | this._drawSelect(token.open);
89 | } else {
90 | this._drawSelect(token);
91 | }
92 | if (token.related) {
93 | for (let i = 0, l=token.related.length; i < l; i++) {
94 | this._drawSelect(token.related[i], ExpressionHighlighter.CSS_PREFIX + "related");
95 | }
96 | }
97 | }
98 |
99 | this.dispatchEvent("hover");
100 | };
101 |
102 | get hoverToken() {
103 | return this._hoverToken;
104 | }
105 |
106 |
107 | // private methods:
108 | _drawSelect(token, style = ExpressionHighlighter.CSS_PREFIX+"selected") {
109 | let doc = this.cm.getDoc(), endToken = token.close || token;
110 | if (token.set) {
111 | endToken = token.set[token.set.length - 1];
112 | token = token.set[0];
113 | }
114 |
115 | this._calcTokenPos(endToken);
116 | this._calcTokenPos(token);
117 | this._hoverMarks.push(doc.markText(token.startPos, endToken.endPos, {
118 | className: style,
119 | startStyle: style + "-left",
120 | endStyle: style + "-right"
121 | }));
122 | };
123 |
124 | _calcTokenPos(token) {
125 | if (token.startPos || token == null) {
126 | return token;
127 | }
128 | CMUtils.calcRangePos(this.cm, token.i, token.l, token);
129 | return token;
130 | };
131 |
132 | };
133 |
134 | ExpressionHighlighter.CSS_PREFIX = "exp-";
135 |
136 | ExpressionHighlighter.GROUP_CLASS_BY_TYPE = {
137 | set: ExpressionHighlighter.CSS_PREFIX+"group-set",
138 | setnot: ExpressionHighlighter.CSS_PREFIX+"group-set",
139 | group: ExpressionHighlighter.CSS_PREFIX+"group-%depth%",
140 | lookaround: ExpressionHighlighter.CSS_PREFIX+"group-%depth%"
141 | };
--------------------------------------------------------------------------------
/dev/src/views/ExpressionHover.js:
--------------------------------------------------------------------------------
1 | /*
2 | RegExr: Learn, Build, & Test RegEx
3 | Copyright (C) 2017 gskinner.com, inc.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | import CMUtils from "../utils/CMUtils";
20 |
21 | import app from "../app";
22 |
23 | export default class ExpressionHover {
24 | constructor (editor, highlighter) {
25 | this.editor = editor;
26 | this.highlighter = highlighter;
27 | this.isMouseDown = false;
28 | this.token = null;
29 |
30 | let o = editor.display.lineDiv;
31 | o.addEventListener("mousemove", (evt)=> this._handleMouseMove(evt));
32 | o.addEventListener("mouseout", (evt)=> this._handleMouseOut(evt));
33 | o.addEventListener("mousedown", (evt)=> this._handleMouseDown(evt));
34 |
35 | }
36 |
37 |
38 | // private methods:
39 | _handleMouseMove(evt) {
40 | if (this.isMouseDown) { return; }
41 |
42 | let index, editor = this.editor, token = this.token, target = null;
43 |
44 | if (evt && token && (index = CMUtils.getCharIndexAt(editor, evt.clientX, evt.clientY + window.pageYOffset)) != null) {
45 | while (token) {
46 | if (index >= token.i && index < token.i+token.l) {
47 | target = token;
48 | break;
49 | }
50 | token = token.next;
51 | }
52 | }
53 |
54 | while (target) {
55 | if (target.open) { target = target.open; }
56 | else if (target.proxy) { target = target.proxy; }
57 | else { break; }
58 | }
59 |
60 | this.highlighter.hoverToken = target;
61 | let rect = (index != null) && CMUtils.getCharRect(editor, index);
62 | if (rect) { rect.right = rect.left = evt.clientX; }
63 | app.tooltip.hover.show("ExpressionHover", app.reference.tipForToken(target), evt.clientX, rect.bottom, true, 0);
64 | }
65 |
66 | _handleMouseOut(evt) {
67 | this.highlighter.hoverToken = null;
68 | app.tooltip.hover.hide("ExpressionHover");
69 | }
70 |
71 | _handleMouseDown(evt) {
72 | // TODO: Should this also be in TextHover?
73 | if (evt.which !== 1 && evt.button !== 1) { return; }
74 |
75 | this.isMouseDown = true;
76 | let f, t = window.addEventListener ? window : document;
77 | t.addEventListener("mouseup", f = () => {
78 | t.removeEventListener("mouseup", f);
79 | this.isMouseDown = false;
80 | });
81 | }
82 | }
--------------------------------------------------------------------------------
/dev/src/views/TextHover.js:
--------------------------------------------------------------------------------
1 | /*
2 | RegExr: Learn, Build, & Test RegEx
3 | Copyright (C) 2017 gskinner.com, inc.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | import CMUtils from "../utils/CMUtils";
20 | import $ from "../utils/DOMUtils";
21 |
22 | import app from "../app";
23 |
24 | export default class TextHover {
25 | constructor (editor, highlighter) {
26 | this.editor = editor;
27 | this.highlighter = highlighter;
28 | this._matches = this._x = null;
29 |
30 | let o = editor.display.lineDiv;
31 | o.addEventListener("mousemove", (evt)=> this._handleMouseMove(evt));
32 | o.addEventListener("mouseout", (evt)=> this._handleMouseOut(evt));
33 | }
34 |
35 | set matches(val) {
36 | this._matches = val;
37 | this._update();
38 | }
39 |
40 | // private methods:
41 | _handleMouseMove(evt) {
42 | this._x = evt.clientX;
43 | this._y = evt.clientY + window.pageYOffset;
44 | this._update();
45 | }
46 |
47 | _handleMouseOut(evt) {
48 | this._x = null;
49 | this._update();
50 | }
51 |
52 | _update() {
53 | if (this._x === null) {
54 | this.highlighter.hoverMatch = null;
55 | app.tooltip.hover.hide("TextHover");
56 | return;
57 | }
58 | let index, cm = this.editor, match, matches = this._matches, x = this._x, y = this._y;
59 |
60 | if (matches && matches.length && (index = CMUtils.getCharIndexAt(cm, x, y)) != null) {
61 | match = this.highlighter.hoverMatch = app.text.getMatchAt(index);
62 | }
63 | let rect = (index != null) && CMUtils.getCharRect(cm, index);
64 | if (rect) { rect.right = rect.left = x; }
65 | let tip = app.reference.tipForMatch(match, cm.getValue());
66 | if (tip) {
67 | let div = $.create("div", "texthover", tip);
68 | app.tooltip.hover.show("TextHover", div, x, rect.bottom, true, 0);
69 | }
70 |
71 | }
72 | }
--------------------------------------------------------------------------------
/dev/src/views/Theme.js:
--------------------------------------------------------------------------------
1 | /*
2 | RegExr: Learn, Build, & Test RegEx
3 | Copyright (C) 2017 gskinner.com, inc.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | import EventDispatcher from "../events/EventDispatcher";
20 |
21 | import $ from "../utils/DOMUtils"
22 | import app from "../app";
23 |
24 | export default class Theme extends EventDispatcher {
25 | constructor (el) {
26 | super();
27 | this.el = el;
28 | this.urlTemplate = "./assets/themes/%name%.css";
29 | this.targetNode = this._node = null;
30 | this._dark = false;
31 | this._initUI();
32 | this.dark = !!app.prefs.read("dark");
33 | }
34 |
35 | set dark(val) {
36 | val = !!val;
37 | if (this._dark === val) { return; }
38 | this._dark = val;
39 | this._load(val ? "dark" : null);
40 | $.toggleClass(this.themeBtn, "selected", val);
41 | app.prefs.write("dark", val);
42 | }
43 |
44 | get dark() {
45 | return this._dark;
46 | }
47 |
48 | _initUI() {
49 | this.themeBtn = $.query(".header .button.theme", this.el);
50 | this.themeBtn.addEventListener("click", (evt) => this._toggleTheme());
51 | }
52 |
53 | _load(id) {
54 | if (id === this._id) { return; }
55 | this._id = id;
56 | if (this._node) { this._node.remove(); }
57 | if (!id) { this._change(); return; }
58 | let tmpl = this.urlTemplate, n = $.create("link");
59 | n.addEventListener("load", () => this._change());
60 | n.rel = "stylesheet";
61 | n.type = "text/css";
62 | n.href = tmpl ? tmpl.replace(/%name%/g, id) : id;
63 | this._node = (this.targetNode || document.head).appendChild(n);
64 | }
65 |
66 | _change() {
67 | this.dispatchEvent("change");
68 | }
69 |
70 | _toggleTheme() {
71 | this.dark = !this.dark;
72 | }
73 |
74 | }
--------------------------------------------------------------------------------
/dev/src/views/tools/Details.js:
--------------------------------------------------------------------------------
1 | /*
2 | RegExr: Learn, Build, & Test RegEx
3 | Copyright (C) 2017 gskinner.com, inc.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | import $ from "../../utils/DOMUtils";
20 | import Utils from "../../utils/Utils";
21 |
22 | import app from "../../app";
23 |
24 | export default class Details {
25 |
26 | constructor(el) {
27 | this.el = el;
28 | $.addClass(el, "details");
29 | this._update();
30 |
31 | this._bound_handleEvent = (evt) => this._handleEvent(evt);
32 | app.addEventListener("result", this._bound_handleEvent);
33 | app.text.addEventListener("select", this._bound_handleEvent);
34 | }
35 |
36 |
37 | cleanup() {
38 | $.empty(this.el);
39 | $.removeClass(this.el, "details");
40 | app.removeEventListener("result", this._bound_handleEvent);
41 | app.text.removeEventListener("select", this._bound_handleEvent);
42 | Utils.defer(null, "Details._update");
43 | }
44 |
45 | // private methods:
46 | _update() {
47 | $.empty(this.el);
48 | $.create("div", "desc", "Click a match above to display match & group details. Mouse over a Group row to highlight it in the Expression.", this.el);
49 | this._addMatch(app.text.selectedMatch, app.text.value);
50 | }
51 |
52 | _addMatch(match, textVal) {
53 | if (!match) { return; }
54 | let groups = match.groups, l=groups&&groups.length, ext=l&&(groups[0].i != null), matchVal=this._getMatchVal(match, textVal), extStr="", me = match.i+match.l;
55 | let groupTokens = app.expression.lexer.captureGroups;
56 |
57 | let tableEl = $.create("table", null, null, this.el);
58 | let matchEl = $.create("tr", "match", "
Match "+match.num+"
"+this._getRangeStr(match)+"
", tableEl);
59 |
60 | if (l) {
61 | let inGroups = [], lastIndex = match.i;
62 | for (let i = 0; i <= l; i++) {
63 | let group = groups[i], index = group ? group.i : me, num = i + 1, token = groupTokens[i];
64 | if (ext) {
65 | for (let j = inGroups.length - 1; j >= 0; j--) {
66 | let inGroup = inGroups[j], ge = inGroup.i + inGroup.l;
67 | if (ge > index) { break; }
68 | inGroups.pop();
69 | extStr += Utils.htmlSafe(textVal.substring(lastIndex, ge)) + "";
70 | lastIndex = ge;
71 | }
72 | }
73 | if (!group) { break; }
74 | if (group.l) {
75 | extStr += Utils.htmlSafe(textVal.substring(lastIndex, index)) + "";
76 | inGroups.push(group);
77 | lastIndex = index;
78 | }
79 | let val = "" + this._getMatchVal(group, textVal) + "";
80 | let label = token.name ? "'"+token.name+"'" : ("Group " + num);
81 | let tr = $.create("tr", "group", "