Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ add another resource

JavaScript

Babel includes JSX processing.

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

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Auto Save

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.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <h1>A really basic implementation of <a href="https://nytimes.github.io/pourover/">PourOver</a></h1>

<p>Use the filters below to learn about monsters!</p>

<div id="buttons">
  <div id="mythology" class="group">
    <strong>Mythology:</strong>
    <a id="greek" href="#" class="mythology_filter">Greek</a>
    <a id="norse" href="#" class="mythology_filter">Norse</a>
  </div>
  <div id="gender" class="group">
    <strong>Gender:</strong>
    <a id="male" href="#" class="gender_filter">Male</a>
    <a id="female" href="#" class="gender_filter">Female</a>
  </div>
  <div id="hobbies" class="group">
    <strong>Hobbies:</strong>
    <a id="riddles" class="hobby_filter" href="#">Riddles</a>
    <a id="sitting" class="hobby_filter" href="#">Sitting</a>
    <a id="terrorizing" class="hobby_filter" href="#">Terrorizing</a>
    <a id="god-killing" class="hobby_filter" href="#">God Killing</a>
    <a id="coiling" class="hobby_filter" href="#">Coiling</a>
  </div>
  <div id="clear-all" class="group">
    <a id="clear" href="#" class="mythology_filter">Reset</a>
  </div>

</div>

<div id="container"></div>

<script type="text/template" id="feature-grid-template">
  <% if (items.length === 0) { %>
    <p><strong>No monsters match your search, try again</strong></p>
  <% } %>
  <ul>
    <% for (var i = 0; i < items.length; i++ ) { %>
      <% var item = items[i]; %>
        <li id="monster-<%= i %>" class="<%= item.mythology %>">
          <strong><%= item.name %></strong>
          <span>
            <%= item.hobbies %>
          </span>
        </li>
      <% } %>
  </ul>
        </script>
              
            
!

CSS

              
                @import url(https://fonts.googleapis.com/css?family=Roboto:400,700);

body {
  font-family: 'Roboto', sans-serif;
}

#buttons{
  padding: 25px 0;
  display:inline-block;
}

#buttons a {
  background: #ccc;
  text-decoration:none;
  color: #333;
  padding: 15px;
  border-radius: 12px;
  display:inline-block;
  margin-bottom:5px;
}

#buttons a:hover{
  background: #888;
  color: #f6f6f6;
}

#buttons a.selected{
  background: #333;
  color: #f6f6f6;
}

#buttons a#clear{
  background: #cff1fa;
}

#buttons .group{
  margin:10px 30px 0 0;
  float: left;
  display:inline-block;
}

#container{
  width:100%;
  display: inline-block;
  clear:both;
}

#footer{
  margin: 15px 0;
  padding: 0;
  border-top: 1px solid #ccc;
  clear:both;
  font-size:14px;
}

ul {
  padding:0;
}

li {
  float:left;
  display:block;
  height:60px;
  width: 150px;
  text-align:center;
  padding-top:25px;  
}

li.norse {
  background: #0e708c;
  color: #fff;
  
}
li.greek {
  background: #47c7ec;
}

li strong{
  font-size:16px;
}

li span{
  font-size:12px;
  display:block;
}
              
            
!

JS

              
                // #Basic PourOver example
// This will cover the creation of a collection and the basic use of filters, views, and sorts

// ###Data

// Let's start with a nice array of data. All PourOver collections *must* be instantiated on
// arrays of hashes like the following, each item a hash of attributes.
var monsters = [{name: "sphinx", mythology: "greek", eyes: 2, sex: "f", hobbies: ["riddles","sitting","being a wonder"]},
                {name: "hydra", mythology: "greek", eyes: 18, sex: "m", hobbies: ["coiling","terrorizing","growing"]},
                {name: "huldra", mythology: "norse", eyes: 2, sex: "f", hobbies: ["luring","terrorizing"]},
                {name: "cyclops", mythology: "greek", eyes: 1, sex: "m", hobbies: ["staring","terrorizing"]},
                {name: "fenrir", mythology: "norse", eyes: 2, sex: "m", hobbies: ["growing","god-killing"]},
                {name: "medusa",  mythology: "greek", eyes: 2, sex: "f", hobbies: ["coiling","staring"]},
                {name: "asbolus",  mythology: "greek", eyes: 2, sex: "m", hobbies: ["running","divining"]},
                {name: "gorgon",  mythology: "greek", eyes: 2, sex: "f", hobbies: ["coiling","staring"]},
                {name: "mare",  mythology: "norse", eyes: 2, sex: "f", hobbies: ["terrorizing","sitting"]},
               ];

// ###Collection creation

// We create a new PourOver collection by passing the data array into the `PourOver.Collection` constructor.
var collection = new PourOver.Collection(monsters);

// ###Filter creation

// To do anything interesting with collections, we -- almost always -- need to add some filters. 
// The most common filter is the exactFilter, a filter that describes an attribute that is satisfied by exactly
// one choice of several possibilities. For example, in the above example, the "mythology" attribute has two possibilities:
// "greek" or "norse". Every item has the value "greek" or "norse" for its mythology.
// *NOTE: `exactFilter`s' names must be identical to the item attribute that they index*
//
// For the most common filter types, such as "exactFilter", PourOver ships with convenience constructors.
// *NOTE: Constructors for preset filters and sorts -- like the ones below -- are not initialized with "new".
// They are simply passed a name and the set of possibilities.*
var mythology_filter = PourOver.makeExactFilter("mythology", ["greek","norse"]);
var gender_filter = PourOver.makeExactFilter("sex", ["m","f"]);

// Now, we will construct the other most-common filter, the `inclusionFilter`. The `inclusionFilter` is similar to the `exactFilter`.
// However, rather than items have a single choice, `inclusionFilter`s describe attributes that can have multiple choices per item.
// *NOTE: `inclusionFilter`s' names must be identical to the item attribute that they index*
var hobbies_filter = PourOver.makeInclusionFilter("hobbies",["riddles","sitting","being a wonder","coiling","terrorizing",
                                                             "growing","luring","staring","god-killing"]);

// ###Adding filters

// After constructing our filters, we have to add them to the collection. This will causes the filters to index
// the collection, pre-computing which collection elements satisfy each possibility's predicate. For exact filters, the
// predicate is equality; a collection item satisfies a possibility if it's value is equal to the value of the possibility.
//
// Adding a filter to a collection will also tell the filter to smartly reconstruct itself when items are added to, removed from, or
// updated in the collection.
collection.addFilters([mythology_filter, gender_filter, hobbies_filter]);

// ###Non-stateful (pure) querying of filters

// Now that we have a nice set of filters, we would like to do something interesting: query them and combine the queries into more
// complex queries. PourOver supports both pure and stateful queries. We will cover the former first.

// Here we see that we query `exactFilter`s and `inclusionFilter`s the same way, by passing in the value for which we would like to search.
// *NOTE: Always access the filter through the `collection.filters` object. Do not use the "foo_filter" that you initialized earlier. When 
// you add filters to a collection, the filter gets cloned first. Querying the original filters, the pre-added filters, will not work.* 
var greek_monsters = collection.filters.mythology.getFn("greek");
var terror_monsters = collection.filters.hobbies.getFn("terrorizing");

// Querying filters returns a `MatchSet`, an objects that wraps a result and can be combined -- AND, OR, NOT -- with other `MatchSets`.
// The boolean combinations will also return `MatchSet`s and can, in turn, be further combined.
var greek_terrors = greek_monsters.and(terror_monsters);

// To get the value of a MatchSet, simply access its `cids` -- read "collection ids" -- property. These cids can be passed to a collection's `get`
// function to transform the cids into the actual objects they represent.
// The value of my_monsters will be:
//
//     [{"name":"hydra","mythology":"greek","eyes":18,"sex":"m","hobbies":["coiling","terrorizing","growing"],"cid":1},
//      {"name":"cyclops","mythology":"greek","eyes":1,"sex":"m","hobbies":["staring","terrorizing"],"cid":3}]
var my_monsters = collection.get(greek_terrors.cids);

// ###Stateful querying

// Pure queries are always nice but they don't map nicely to the core use case for PourOver: 
// modelling UI's in which users query a collection by clicking, sliding, scrubbing and editing
// controls. You will want to remember what their current query is. Otherwise, you'd lose their work!
// 
// Stateful queries are very similar to pure queries. You just use the `query` method of a filter instead of the `getFn` method.
// This will store the result of the query  on the `current_query` attribute of the filter.
//collection.filters.mythology.query("greek");
//collection.filters.hobbies.query("terrorizing");
var getCurrentMonsters = function(){
  var myth_set = collection.filters.mythology.current_query,
      hobby_set = collection.filters.hobbies.current_query,
      output_set = myth_set.and(hobby_set);

  return collection.get(output_set.cids);
}

// Now, whenever a user queries a filter (through some currently undefined UI action), we can just call `getCurrentMonsters()`
// and get the current set of monsters, filtered by mythology and hobbies. By default, an unqueried filter or empty query 
// returns a match set representing the entire collection. This means that intersections and unions will work regardless of 
// whether or not a query has been made.
// 
// But something is missing. We don't want to have to write `getCurrentMonsters` everytime we make an app. 
// Also, it would be nice if we could cache the *result* of getCurrentMonsters to speed up subsequent renders.
// Moreover, we don't have any way (yet) to page through results or sort them.
//
// Enter views ... 

// ### Views

// In PourOver, a View is used to cache the combination of many queries, paging through and sorting the results.
//
// To construct a View, we pass a name and a collection into the constructor.
//var view = new PourOver.View("default_view", collection);

var MyView = PourOver.View.extend({
  template: _.template($("#feature-grid-template").html()),
  render: function(){
    var items = this.getCurrentItems();
    console.log(items);
    var html = this.template({items:items});
    $('#container').html(html);
  }
});
var newsday_view = new MyView( "newsday_view", collection );

newsday_view.on("update",function(){
  console.log("updated!");
  newsday_view.render(); 
})

collection.on("change",function(){
  console.log("changed!");
  newsday_view.render(); 
})

newsday_view.on("all", function(eventName){
  console.log(eventName + ' was triggered!');
});

// Whenever a stateful query occurs, the View will update its cached `MatchSet` with the result of its `selectionFn`. This 
// describes how a View should combine the filters on a collection. The default View selectionFn simply intersects all the
// collection's filters together. This should cover the standard use case of a UI in which each control maps to a filter.
// If a user selects "greek" for "mythology" and "terrorizing" for "hobbies", she expects the result to be all monsters which are
// Greek AND terrorize, the intersection of all the filters. (Sex will also be intersected but, since it hasn't been queried in 
// our example, it will not affect the result of the intersection)
//
// To get this cached result of a `View`, call the `getCurrentItems` method;
var current_monsters = newsday_view.getCurrentItems();

// ### Conclusion
// This example should cover 90% of use cases for PourOver. For more complicated behavior.
// - the "advanced_views" example will demonstrate sorting, the creation of custom selectionFns, and other arcana
// - the "advanced_filters" example will explain the default filters and demonstrate how to create a new filter type
// - the "buffering" example will demonstate how to use BufferedCollection and BufferedViews to lazily load non-categorical data
// - the "events" example will demonstrate how to plug into the overly complex event system and uses silent updating to optimize queries
// - the "ui" example will demonstrate how to use the PourOver.UI module to simplify the making of UIs

$(function() {
  newsday_view.render();
  var hobbyArray = []

  $("#buttons a").click(function(e) {
    e.preventDefault();

    var type = $(this).attr("id");
    var parent = $(this).parent().attr("id");

    if ( parent === "mythology" ) {
      if ( $(this).hasClass("selected") ){
        $(this).removeClass("selected");
        newsday_view.collection.filters.mythology.clearQuery()
      } else{
        $("a.mythology_filter").removeClass("selected");
        if ( type === "greek" ) {
          $(this).toggleClass("selected");
          newsday_view.collection.filters.mythology.query("greek");
        } else if ( type === "norse" ) {
          $(this).toggleClass("selected");
          newsday_view.collection.filters.mythology.query("norse");
        }
      }
    } else if ( parent === "gender" ) {
      if ( $(this).hasClass("selected") ){
        $(this).removeClass("selected");
        newsday_view.collection.filters.sex.clearQuery()
      } else{
        $("a.gender_filter").removeClass("selected");
        if ( type === "male" ) {
          $(this).toggleClass("selected");
          newsday_view.collection.filters.sex.query("m");
        } else if ( type === "female" ) {
          $(this).toggleClass("selected");
          newsday_view.collection.filters.sex.query("f");
        }
      }
    } else if ( parent === "hobbies" ) {
      if ( $(this).hasClass("selected") ){
        $(this).removeClass("selected");
        hobbyArray = jQuery.grep(hobbyArray, function(value) {
          return value != type;
        });
      } else{
        hobbyArray.push( type );
        $(this).toggleClass("selected");
      }
      newsday_view.collection.filters.hobbies.query( hobbyArray );
    }

    if ( type === "clear" ){
      newsday_view.collection.filters.mythology.clearQuery()
      newsday_view.collection.filters.hobbies.clearQuery()
      newsday_view.collection.filters.sex.clearQuery()
      hobbyArray = []
      $("#buttons a").removeClass("selected");
    }


  });
});
              
            
!
999px

Console