cssAudio - Activefile-genericCSS - ActiveGeneric - ActiveHTML - ActiveImage - ActiveJS - ActiveSVG - ActiveText - Activefile-genericVideo - ActiveLovehtmlicon-new-collectionicon-personicon-teamlog-outoctocatpop-outspinnerstartv

Pen Settings

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

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

+ add another resource

Looking for quick-add? Try the external resource search, it's quicker and gives you access to the most recent version of thousands of libraries. ☝️

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

Add External Scripts/Pens

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

+ add another resource

Looking for quick-add? Try the external resource search, it's quicker and gives you access to the most recent version of thousands of libraries. ☝️

Use npm Packages

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

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

Code Indentation

     

Save Automatically?

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

Auto-Updating Preview

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

            
               <div id="css-table">
  <div id="col-1">
  <ul>
    <li><a href="#col-1-tab-1">Example 7</a></li>
  </ul>
  <div id="col-1-tab-1">
  <article>
   <h1>select matching brackets</h1>
   <p>
    <b>text to search:</b><br>
    <textarea class="input" id="cursor-area">
var brackets = function() {
  for(var i = 0; i < 10; i += 1){
    if(i === 1){
      console.log("two opening brackets, {, here");
    }else if(i = 2){
      console.log("only one closing bracket, }, so far");
      if(true){
        console.log("now we are three deep in opening brackets, {, so far");
        if(true){
          console.log("lots of { and } to deal with here");
        }
      }
    }
  }
}</textarea>
   </p>
   <p>
   <b>Problem:</b> When the mouse clicks on a bracket, <code>{</code> or <code>}</code>,
   highlight its matching bracket by selecting all text in the phrase
   including the opening and closing brackets. Brackets in quoted strings are to be ignored.
   </p>
   <p>
   <b>Solution:</b> The working functions are <code>findOpening()</code> and <code>findClosing()</code>
   which respectively find the opening or closing member of a matched pair of brackets.
   </p>
   <b>Pattern Syntax:</b> The ABNF pattern syntax for matching nested pairs of brackets is:
   <pre id="pattern"></pre> 
   </p>
   <p>
   <b>Notes: </b> Working with textareas is notoriously browser-dependent.
   Here I'm do a minimum of cursor management in 
   the functions <code>getCursorPos()</code> and <code>setSelection()</code>.
   They have been tested on Chrome 49, Firefox 44 and IE 11.
   My apologies if they don't work in your browser.
   (On the other hand, you are more than welcome to jump over to the JS panel and fix them. :) )
   </p>
   <p>
   </p>
   <div class="discussion">
   </div>
  </article>
  </div>
  </div>
  <div id="col-2">
  <ul>
    <li><a href="#col-2-tab-1">message</a></li>
  </ul>
  <div id="col-2-tab-1">
  <article>
   <div id="apgexp-result"></div>
  </article>
  </div>
  </div>
 </div>

            
          
!
            
              /* Pen-specific styles */
* {
  box-sizing: border-box;
}

body {
  color: #000;
  font-size: .8rem;
  line-height: 150%;
}

h1 {
  font-size: 1.75rem;
  margin: 0 0 0.75rem 0;
  font-family: 'Open Sans', sans-serif;
  font-weight: 700;
}

h2 {
  font-family: 'Open Sans', sans-serif;
  font-weight: 700;
}

article {
  font-family: 'Open Sans', sans-serif;
  font-weight: 400;
}

b {
  font-family: 'Open Sans', sans-serif;
  font-weight: 700;
}

i {
  font-family: 'Open Sans', sans-serif;
  font-style: italic;
}

textarea {
  width: 100%;
}

button {
  cursor: pointer;
  cursor: hand;
}

p.title {
  margin: 0px 0px 3px 0px;
}

a.link {
  color: #4284B0;
}

a.link:visited {
  color: #9c4be7;
}

textarea.input {
  font-size: .9rem;
  font-family: monospace;
}

/* Pattern styles */
#css-table {
  display: table;
  width: 100%
}

#col-1 {
  display: table-cell;
  width: 40%;
  border: 1px solid #4284B0;
}

#col-2 {
  display: table-cell;
  width: 60%;
  border-width: 1px 0px;
  border-style: solid;
  border-color: #4284B0;
}

button {
  font-size: 1em;
  font-weight: bold;
  cursor: pointer;
  cursor: hand;
  margin: 0px 0px 2px 0px;
}

button.tryit-button {
  width: 10rem;
}


#apgexp-def, #input-data {
  width: 95%;
  font-size: .9rem;
  font-family: monospace;
}

#cursor-area {
  height: 20em;
}

span.error{
	color: red;
}

            
          
!
            
              var exp, expSQuote, expDQuote, result, msg;
var $e, $area, $msg;

// given the position of the opening bracket
// find the position of the closing bracket
var findClosing = function(pos) {
  msg = "";
  msg += "<h2>Match From Opening Bracket Forward To Closing Pair</h2>"

  // anchor the search at the position of the opening bracket
  exp.lastIndex = pos;
  result = exp.exec($e.value);
  if(result){
    
    // find the index of the closing bracket (end of phrase) 
    var end = pos + result.rules.P[0].phrase.length;
    setSelection(pos, end);
    
    // display a success message
    msg += "opening bracket at index: " + pos;
    msg += "<br>closing bracket at index: "+ (end-1);
  }else{
    
    // display an error message
    msg += '<span class="error">no matching closing bracket found</span>';
  }
  $msg.html(msg);
}

//given the position of the closing bracket
//find the position of the opening bracket
var findOpening = function(pos) {
  msg = "";
  msg += "<h2>Match From Closing Bracket Back To Opening Pair</h2>"
  var found = false;
  
  // search from beginning of string for **correct** opening bracket
  for(var i = 0; i < pos; i += 1){
    var char = $e.value.charCodeAt(i);
    if(char === 34){
      // skip over double-quoted strings
      expDQuote.lastIndex = i;
      result = expDQuote.exec($e.value);
      if(result){
        i += result.length -1;
      }
    }else if(char === 39){
      // skip over single-quoted strings
      expSQuote.lastIndex = i;
      result = expSQuote.exec($e.value);
      if(result){
        i += result.length -1;
      }
    }else if(char === 123){
      
      // found opening bracket, anchor the search to its index
      exp.lastIndex = i;
      result = exp.exec($e.value);
      if(result){
        
        // found a bracket pair
        // test closing bracket of this pair against the closing bracket we started with
        var beg = result.rules.P[0].index;
        var end = result.rules.P[0].index + result.rules.P[0].phrase.length;
        if(end === (pos + 1)){
          
          // found it
          setSelection(beg, end);
          found = true;
          
          // display a success message
          msg += "opening bracket at index: " + beg;
          msg += "<br>closing bracket at index: "+ pos;
          break;
        }
      }
    }
  }
  if(!found){
    
    // display an error message
    msg += '<span class="error">no matching opening bracket found</span>';
  }
  $msg.html(msg);
}

// return the cursor position after a mouse click
var getCursorPos = function(){
  var pos = -1;
  if ($e.selectionStart !== undefined) {
    pos = $e.selectionStart;
  }else{
    msg = "<h2>No Browser Support For Cursor Management</h2>";
    msg += "<b>browser:</b> ";
    if(navigator && navigator.appCodeName){
      msg += navigator.appCodeName;
      if(navigator.appName){
        msg += ": " + navigator.appName;
      }
      if(navigator.appVersion){
        msg += ": " + navigator.appVersion;
      }
    }else{
      msg += "unknown";
    }
    $msg.html(msg);
  }
  return pos;
}

// set the selection
var setSelection = function(beg, end){
  if ($e.selectionStart !== undefined) {
    $e.selectionStart = beg;
    $e.selectionEnd = end;
  }
}

// mouse up event
// the cursor has now been set to the mouse click position
var mouseUp = function(event) {
  var pos = getCursorPos();
  if(pos >= 0){
    var char = $e.value.charCodeAt(pos);
    if(char === 123){
      
      // cursor is on an opening bracket, {: find the matching }
      findClosing(pos);
    }else if(char === 125){
      
      // cursor is on an closing bracket, }: find the matching {
      findOpening(pos);
    }else{
      
      // cursor not on a bracket: just display the cursor location
      msg = "";
      msg += "<h2>Cursor Not On Bracket</h2>";
      msg += "cursor at index: " + pos;
      $msg.html(msg);
    }
  }
}

// mouse down event
// clear any selection before the cursor position is set
var mouseDown = function(event) {
  setSelection($e, 0, 0);
}
$(document).ready(function() {
  
  // the ABNF pattern syntax for matching nested, pairs of brackets with text between
  var pairs = "";
  pairs += "P     = L text 1*(P text) R              ; the nested pairs with text between\n";
  pairs += "      / L text R                         ; the middle pair\n";
  pairs += 'L     = "{"                              ; the left, opening bracket\n';
  pairs += 'R     = "}"                              ; the right, closing bracket\n';
  pairs += 'text  = *(squote                         ; single-quoted text\n';
  pairs += '       / dquote                          ; double-quoted text\n';
  pairs += '       / %d32-122 / %d124 / %d126        ; all ASCII characters except { and }\n';
  pairs += '       / %d9-10 / %d13)                  ; including tabs and line breaks\n';
  pairs += 'squote = %d39 *(%d32-38 / %d40-126) %d39 ; single-quoted text\n';
  pairs += 'dquote = %d34 *(%d32-33 / %d35-126) %d34 ; double-quoted text\n';
  
  // set the "sticky" flag to anchor the search at exp.lastIndex
  exp = new ApgExp(pairs, "y");
  
  // quoted string objects
  expSQuote = new ApgExp("squote = %d39 *(%d32-38 / %d40-126) %d39\n", "y");
  expDQuote = new ApgExp('dquote = %d34 *(%d32-33 / %d35-126) %d34\n', "y");
  
  // jQuery values
  $msg = $("#apgexp-result");
  $area = $("#cursor-area");
  $e = $area[0]

  // page set up
  $area.mouseup(mouseUp);
  $area.mousedown(mouseDown);
  $("#col-1").tabs();
  $("#col-2").tabs();
  $("#pattern").html(pairs);
  $msg.html('<i>click the cursor in the text area</i>');
});

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

Console