PSL Generator Prototype

This tool is designed to compliment the position of the TSS for customers with limited access to on-site TSS support. The PSL Generator's purpose is to create perfect PSL every time. When the user clicks Generate PSL it will display the user's PSL in the bottom text box and a email will/can be sent to the assigned TSS. This will allow the TSS to examine the PSL and provide expert feedback on enhancing the PSL for better search results.

by justinbrown

HTML

<script src="https://github.com/downloads/wycats/handlebars.js/handlebars-1.0.0.beta.6.js"></script>
<script src="https://github.com/downloads/emberjs/ember.js/ember-latest.js"></script>
<script src="https://raw.github.com/jackmoore/autosize/master/jquery.autosize.js"></script>
<script src="https://raw.github.com/wordnik/swagger.js/master/lib/swagger.js"></script>
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/ui-lightness/jquery-ui.css">
<script type="text/x-handlebars" data-template-name="application">
  <div class="header">
    <h1>PSL Builder</h1>
  </div>
  {{view App.WordsView}}
  {{view App.WordEditorView contentBinding="App.wordsController.selectedWord"}}
  {{view App.PslAssemblyView}}
</script>
<script type="text/x-handlebars" data-template-name="blockLayout">
  {{#if view.content.group}}
    <button class="remove-btn" data-tooltip="Click here to remove this." {{action remove target="view.controller"}}>x</button>
  {{/if}}
  {{yield}}
</script>
<script type="text/x-handlebars" data-template-name="suggestList">
  <p>{{view.title}}</p>
  <p class="description">{{view.description}}</p>
  <ul>
  {{#each view.content}}
    <li><a {{action selectRelatedWord target="controller"}}>{{this}}</a></li>
  {{/each}}
  </ul>
</script>
<script type="text/x-handlebars" data-template-name="words">
  <p>Words ({{App.wordsController.length}}) <a {{action createWord target="controller"}}>New</a></p>
  {{view App.WordsList contentBinding="App.wordsController" controllerBinding="App.wordsController"}}
</script>
<script type="text/x-handlebars" data-template-name="psl">
  <div class="main-psl">
    <strong>PSL</strong>
    <pre>{{queryTreePsl}}</pre>
  </div>
</script>
<script type="text/x-handlebars" data-template-name="wordEditor">
  <div class="cf">
    <div class="editor-input">
      <p>Type a word or phrase you want to find...</p>
      {{view App.PslInput contentBinding="view.content"...

SCSS

* { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }

.cf:before,
.cf:after {
    content: " "; /* 1 */
    display: table; /* 2 */
}

.cf:after {
    clear: both;
}

/**
 * For IE 6/7 only
 * Include this rule to trigger hasLayout and contain floats.
 */
.cf {
    *zoom: 1;
}

html, body, .application {
 height: 100%;
 position: relative;
 width: 100%;
}
a { color: blue !important; }

.application {
  > * { overflow: auto; }

  .header {
    background: #3D668F;
    color: white;
    height: 6%;
  }

  .words {
    background-color: #abc;
    height: 20%;
  }

  .word-editor {
    height: 74%;
    
    > * {
      height: 50%;
      overflow: auto;
    }
  }

  .pslAssembly {
    background-color: #efefef;
    height: 74%;
    position: absolute;
    width: 100%;
  }
}

textarea { width: 100%; }


.char {
  background-color: white;
  font-family: monospace;
  font-size: 2em;
  height: 1.3em;
  text-align: center;
  width: 0.9em;
  
  &.is-optional {
    opacity: .5;
  }

  &.is-lowercase-only {
    text-decoration: underline;
  }

  &.is-match-any-char, &.is-match-many-chars {
    color: blue;
  }

  &.is-match-one-char {
    color: blue;
  }
}

.words {
  background-color: green;
  height: 30%;
    
  .words-list {
    border: 1px solid;
    width: 100%;
    
    .word-list-item {
      &.is-selected {
        background-color: lightskyblue;
      }

      .psl { width: 80%; }
      .remove { width: 20%; }
    }
  }
}

.sample-document-view {
  position: absolute;
}

.word-editor {
  background-color: #dedede;
}

.suggestions {
    
  .suggestList {
    float: left;
    margin: 1em;
  }
}

.keyboard-shortcuts {
  border: 1px solid;
  padding: 1em;
    
  strong { font-weight: bold; }
    
  .shortcut {
    margin-bottom: 0.3em;
    
    &:last-of-type { margin-bottom: 0; }

    &.enabled {
      background-color: #CCFFCD;
    
      &:after {
        content: "\2713";
        margin-left: 10px;
      }
    }
  }

  .char {
...

JavaScript

/*
TODO:
- Strip leading/trailing whitespace when copying from sample
- Table of contents comment structure
- Ability to toggle viewing comments in psl
- ^^ '*istan' 'istan*' '*istan*'
- Find commonalities in alternatives
- 'John [|Quincy ]Adams' not 'John [|Quincy] Adams'
- Alternative mode? Suspends regular navigation so you can type multiple in alts
- http://jsfiddle.net/chrisconley/NN35P/

BUGS:
- Can't navigate left from charinput that is a space
- If text is selected, don't append just allow to replace normally

Tell me about this PSL:
- Uppercase chars only match uppercase
- Lowercase chars match upper and lower case
- Words that start with...
- Words that end with...
- ? matches one alpha-numeric character
- . matches any one character

Use case story:
- Highlight a term in displaydoc
- Popup bubble "Build entity using this?"
- Open PSL generator with term populated
- Quicksearch for term runs asynchronously
- Take a sample of returned quicksearch documents and find common terms
- Suggest common terms to make psl more focused
*/
App = Ember.Application.create({
  operators: ['AND', 'OR', 'AND NOT'],
  extents: ['line', 'document', 'word', 'paragraph'],
  ready: function() {      
      $('body').delegate('[data-tooltip]', 'mouseenter', function(evt) {
         var target = $(this),
             tooltip = target.data('tooltip'),
             arrow = $('.arrow_box'),
             targetOffset = target.offset();          
         
         arrow.find('.content').text(tooltip);
         
         arrow.css({
           left: targetOffset.left - arrow.width()/2 + target.width()/2,
           top: targetOffset.top - arrow.height() - 30
         }).fadeIn();
      });
      
      $('body').delegate('[data-tooltip]', 'mouseleave', function(evt) {
         $('.arrow_box').fadeOut();
      });
  }
});

App.Group = Em.Object.extend({
  init: function() {
    this._super();
    if(!this.get('groups')) this.set('groups', Em.A([]));
    if(!this.get('terms'))...