source view: references.php


<?php






  function JSSafe ( $s )
  {
    return str_replace( array( "\\", "'" ), array( "\\\\", "\\'" ), $s );
  }



  require( 'blog.login.php' );
  require( 'db.connect.php' );
  if ( isset( $_GET['bibtex'] ) && intval( $_GET['bibtex'] ) == $_GET['bibtex'] )
  {
    if ( isLoggedIn() && isset( $_GET['action'] ) && $_GET['action'] == 'update' )
    {
    }

    $bibtex_query = mysql_query( 'SELECT r.bibTeX FROM tblReferences r WHERE r.referenceID = ' . intval( $_GET['bibtex'] ) );
    $bibtex_row   = mysql_fetch_row( $bibtex_query );
    header( 'Content-Type: application/json' );
    print "['" . JSSafe( str_replace( array( "\n", "\t" ), array( "\\n", "\\t" ), $bibtex_row[0] ) ) . "']";
    exit();
  }

  if ( isset( $_GET['remove'] ) && intval( $_GET['remove'] ) == intval( $_GET['remove'] ) && isLoggedIn() )
  {
    $file_query = mysql_query( 'SELECT r.filename FROM tblReferences r WHERE r.referenceID = ' . intval( $_GET['remove'] ) );
    if ( $file_row = mysql_fetch_row( $file_query ) )
    {
      $basedir = ( preg_match( '/^dev\./', $_SERVER['SERVER_NAME'] ) ? '.' : '' ) . './blog-data/pdfs/references/';
      unlink( $basedir . $file_row[0] );
      mysql_query( 'DELETE FROM tblReferences WHERE referenceID = ' . intval( $_GET['remove'] ) );
    }

    header( 'Content-Type: application/json' );
    print "['removed']";
    exit();
  }






  require( '_pagewrapper.php' );
  $style = <<<EOS
<style type="text/css">
  div#results {
    padding: 0ex 1ex;
  }

  div#results div
  {
    margin-top: 2ex;
  }

  div#results a
  {
    color: #777;
    text-decoration: none;
  }

  div#results div > span
  {
    display: block;
  }

  div#results textarea
  {
    background-color: #f2f2f2;
    border:           1px solid #999;
    box-shadow:       none;
    color:            #666;
    font-family:      monospace;
    font-size:        8pt;
    margin:           1ex 0ex;
    outline:          none;
    resize:           none;
    width:            100%;
  }
</style>

EOS;
  wrapPageTop( 'research.php', 'references', array( $style ) );






?>
<h3>reference search</h3>
<p>
  This page is in active development, and feedback is welcome. To get in touch, head to the <a href="/about.php">about page</a>.
  <ul>
    <li>Search operates on the initial portion of words, so <span style="font-style:italic;">rati</span> will match <span style="font-style:italic;">ratio</span> and <span style="font-style:italic;">rational</span>, but not <span style="font-style:italic;">irrational</span></li>
    <li>Search is conjunctive and breaks on spaces, so <span style="font-style:italic;">rati age</span> requires that the citation has words beginning with <span style="font-style:italic;">rati</span> and <span style="font-style:italic;">age</span></li>
    <li>Text will match article titles and author surnames</li>
    <li>Use four-digit numbers to search by publication year</li>
    <li>No special characters; all indexed strings are alphanumeric and lack accents, so your searches should be in the same format</li>
  </ul>
</p>
<hr />
<input id="search" style="margin-top:3ex;width:50%;" title="search" />
<div id="results">
</div>
<script type="text/javascript">
  function ajaxConnect ( url, cb, post )
  {
    var a;
    if ( typeof XMLHttpRequest != 'undefined' )
    {
      a = new XMLHttpRequest();
    }
    else
    {
      try
      {
        a = new ActiveXObject( 'MSXML2.XMLHTTP' );
      }
      catch ( e )
      {
        try
        {
          a = new ActiveXObject( 'Microsoft.XMLHTTP' );
        }
        catch ( e )
        {
          return;
        }
      }
    }

    if ( !a ) { return; }
    var is_post = typeof post !== 'undefined';
    a.open( is_post ? 'POST' : 'GET', url, true );
    a.onreadystatechange = function () {
      a.readyState == 4 && a.status == '200' && cb( a.responseText );
    };

    if ( is_post )
    {
      a.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' );
      a.send( post );
    }
    else
    {
      a.send( '' );
    }
  }

  function haltEvent ( e )
  {
    e = e || event || window.event;
    e.stopPropagation();
    e.preventDefault();
  }

  // fix "double toggle" bug
  function toggleBibTeX ( e, edit )
  {
    var div = e.draw();
    if ( !div.parentNode )
    {
      return;
    }

    var textareas = div.getElementsByTagName( 'textarea' );
    if ( textareas.length > 0 )
    {
      for ( var i = 0; i < textareas.length; ++i )
      {
        div.removeChild( textareas[i] );
      }
    }
    else
    {
      var textarea = document.createElement( 'textarea' );
      textarea.appendChild( document.createTextNode( 'loading...' ) );
      if ( !edit )
      {
        textarea.setAttribute( 'readonly', 'true' );
      }
      div.appendChild( textarea );

      ajaxConnect( '/references.php?bibtex=' + e.id, function ( R ) {
        if ( !textarea.parentNode )
        {
          return;
        }

        R = eval( R );
        textarea.removeChild( textarea.firstChild );
        textarea.appendChild( document.createTextNode( R[0].replace( /\\n/g, '\n' ).replace( /\\t/g, '\t' ) ) );
        textarea.focus();
        textarea.select();
        textarea.onclick = function () { this.select(); };
        textarea.addEventListener( 'keyup', function () {
          var ev  = ( event || window.event );
          ( ( ev.which || ev.keyCode ) == 27 ) && this.parentNode && this.parentNode.removeChild( this );
        } );

        var height            = 1;
        textarea.style.height = height + 'ex';
        while ( parseInt( textarea.clientHeight ) < parseInt( textarea.scrollHeight ) ) { textarea.style.height = ++height + 'ex'; }

        if ( edit )
        {
          var button = document.createElement( 'input' );
          button.setAttribute( 'type', 'button' );
          button.setAttribute( 'value', 'Update' );
          button.style.float = 'right';
          button.addEventListener( 'click', function () {
            ajaxConnect( '/references-admin.php?referenceid=' + e.id + '&action=update', function ( R ) {
              if ( !R && !R.length )
              {
                return;
              }

              R = eval( R );
              if ( !R.length )
              {
                return;
              }

              e.authors = R[0].authors;
              e.journal = R[0].journal;
              e.title   = R[0].title;
              e.year    = R[0].year;

              e.install();
              e.redraw();
            }, 'bibtex=' + encodeURIComponent( textarea.value ) );
            haltEvent();
          } );

          var clear = document.createElement( 'div' );
          clear.style.clear = 'both';
          div.appendChild( button );
          div.appendChild( clear );
        }
      } );
    }
  }
  
  function App ()
  {
    this.elts = [];
    this.prev = '';
    var self  = this;
    this.view = [];

    App.prototype.addElt = function ( e )
    {
      e.app = this;
      this.elts.push( e );
      return this;
    };

    App.prototype.draw = function ()
    {
      while ( this.panel.lastChild ) this.panel.removeChild( this.panel.lastChild );

      for ( var i in this.view )
      {
        this.panel.appendChild( this.view[i].draw() );
      }

      if ( this.view.length == 0 )
      {
        var no_results = document.createElement( 'div' );
        no_results.appendChild( document.createTextNode( 'No references match all search terms; try being less specific.' ) );
        no_results.style.color = '#666';
        this.panel.appendChild( no_results );
      }
    };

    App.prototype.removeById = function ( id )
    {
      for ( var i = 0; i < this.elts.length; ++i )
      {
        if ( this.elts[i].id == id )
        {
          this.elts[i].undraw();
          this.elts.splice( i, 1 );
          break;
        }
      }
    };

    App.prototype.searchFrom = function ( a, q )
    {
      var b  = a.slice( 0 );
      var qs = q.split( /\s+/ );
      for ( var j in qs )
      {
        if ( /^\d{4}$/.exec( qs[j] ) )
        {
          for ( var i = b.length - 1; i >= 0; --i )
          {
            if ( !b[i].matchesYear( qs[j] ) )
            {
              b.splice( i, 1 );
            }
          }
        }
        else if ( /^\d{1,3}$/.exec( qs[j] ) )
        {
          continue;
        }
        else
        {
          var re_author = Elt.REAuthor( qs[j] );
          var re_title  = Elt.RETitle( qs[j] );

          for ( var i = b.length - 1; i >= 0; --i )
          {
            if ( !b[i].matchesString( re_author, re_title ) )
            {
              b.splice( i, 1 );
            }
          }
        }
      }

      this.view = b;
      this.prev = q;
      this.draw();
    };

    App.prototype.update = function ()
    {
      this.view = this.elts;
      this.draw();
    };

    document.getElementById( 'search' ).addEventListener( 'keyup', function () {
      self.searchFrom( self.prev == this.value.substr( 0, self.prev.length ) ? self.view : self.elts, this.value );
    } );
    this.panel = document.getElementById( 'results' );
  }

  function Elt ( id, authors, title, year, journal, file )
  {
    this.app     = null;
    this.authors = authors;
    this.file    = file;
    this.id      = id;
    this.journal = journal;
    this.title   = title;
    this.year    = year;

    Elt.prototype.draw = function ()
    {
      if ( this.div )
      {
        return this.div;
      }

      var self = this;

      var d = document.createElement( 'div' );
      var a = document.createElement( 'a' );
      a.setAttribute( 'href', '/blog-data/pdfs/references/' + this.file );
      a.appendChild( document.createTextNode( this.title ) );
      a.setAttribute( 'target', '_new' + this.id );
      a.style.display = 'block';
      d.appendChild( a );

      var s = document.createElement( 'span' );
      s.style.float = 'right';
      a = document.createElement( 'a' );
      a.setAttribute( 'href', '#' );
      a.appendChild( document.createTextNode( 'BibTeX' ) );
      a.addEventListener( 'click', function () {
        toggleBibTeX( self );
        haltEvent();
      } );
      s.appendChild( a );<?php

  if ( isLoggedIn() )
  {
?>
      a_edit = document.createElement( 'a' );
      a_edit.setAttribute( 'href', '#' );
      a_edit.appendChild( document.createTextNode( 'Edit' ) );
      a_edit.addEventListener( 'click', function () {
        toggleBibTeX( self, true );
        haltEvent();
      } );

      a_remove = document.createElement( 'a' );
      a_remove.setAttribute( 'href', '#' );
      a_remove.appendChild( document.createTextNode( 'Remove' ) );
      a_remove.addEventListener( 'click', function () {
        self.remove();
        ajaxConnect( '/references.php?remove=' + self.id, function ( R ) {} );
        haltEvent();
      } );

      s.appendChild( document.createTextNode( ' | ' ) );
      s.appendChild( a_edit );
      s.appendChild( document.createTextNode( ' | ' ) );
      s.appendChild( a_remove );
<?php
  }

?>
      d.appendChild( s );

      s = document.createElement( 'span' );
      s.appendChild( document.createTextNode( this.authors ) );
      d.appendChild( s );
      if ( this.journal )
      {
        var j  = document.createElement( 'span' );
        var jj = document.createElement( 'span' );
        jj.style.fontStyle = 'italic';
        jj.appendChild( document.createTextNode( this.journal ) );
        j.appendChild( jj );
        j.appendChild( document.createTextNode( ', ' + this.year ) );
        d.appendChild( j );
      }
      else
      {
        s.appendChild( document.createTextNode( ', ' + this.year ) );
      }

      this.div = d;
      return d;
    };

    Elt.prototype.matchesString = function ( re_author, re_title )
    {
      return re_author.exec( this.search_authors ) || re_title.exec( this.search_title );
    };

    Elt.prototype.matchesYear = function ( y )
    {
      return this.year == y;
    };

    Elt.prototype.redraw = function ()
    {
      var d_old = this.draw();
      this.div  = null;
      var d_new = this.draw();
      d_old.parentNode.insertBefore( d_new, d_old );
      d_old.parentNode.removeChild( d_old );
    };

    Elt.prototype.remove = function ()
    {
      this.app.removeById( this.id );
    };

    Elt.prototype.undraw = function ()
    {
      var d = this.draw();
      if ( d.parentNode )
      {
        d.parentNode.removeChild( d );
      }

      this.div = null;
    };

    Elt.REAuthor = function ( s )
    {
      return new RegExp( '(?:^|\\+)' + s.toLowerCase() );
    };

    Elt.RETitle = function ( s )
    {
      return new RegExp( '\\b' + s.toLowerCase() );
    };

    Elt.serialReplace = function ( re, to, s )
    {
      for ( var i in re )
      {
        s = s.replace( re[i], to[i] );
      }

      return s;
    };

    // strip Bibtex encoding...
    // http://www.javascripter.net/faq/accentedcharacters.htm
    Elt.prototype.install = function ()
    {
      this.search_authors = Elt.serialReplace( [ /\\(?:[c'"`^~]|check)(?:\{(\w)\}|(\w))/g, /[\{\}]/g, /[^a-zA-Z0-9\-\+]+/g ], [ '$1$2', '', ' ' ], this.authors ).toLowerCase();
      this.search_title   = Elt.serialReplace( [ /\\(?:[c'"`^~]|check)(?:\{(\w)\}|(\w))/g, /[\{\}]/g, /[^a-zA-Z0-9\-]+/g ], [ '$1$2', '', ' ' ], this.title ).toLowerCase();
      this.authors        = Elt.serialReplace( [ /\\`(?:\{(\w)\}|(\w))/g, /\\'(?:\{(\w)\}|(\w))/g, /\\\^(?:\{(\w)\}|(\w))/g, /\\~(?:\{(\w)\}|(\w))/g, /\\"(?:\{(\w)\}|(\w))/g, /\\check\{(\w)\}/g, /\\c\{(\w)\}/g, /[\{\}]/g, /\,.*?(?:\+|$)/g ], [ '$1$2\u0300', '$1$2\u0301', '$1$2\u0302', '$1$2\u0304', '$1$2\u0308', '$1\u030C', '\xE7', '', ',' ], this.authors );
      this.journal        = Elt.serialReplace( [ /\\`(?:\{(\w)\}|(\w))/g, /\\'(?:\{(\w)\}|(\w))/g, /\\\^(?:\{(\w)\}|(\w))/g, /\\~(?:\{(\w)\}|(\w))/g, /\\"(?:\{(\w)\}|(\w))/g, /\\check\{(\w)\}/g, /\\c\{(\w)\}/g, /[\{\}]/g, /\,.*?(?:\+|$)/g ], [ '$1$2\u0300', '$1$2\u0301', '$1$2\u0302', '$1$2\u0304', '$1$2\u0308', '$1\u030C', '\xE7', '', ',' ], this.journal );
      this.title          = Elt.serialReplace( [ /\\`(?:\{(\w)\}|(\w))/g, /\\'(?:\{(\w)\}|(\w))/g, /\\\^(?:\{(\w)\}|(\w))/g, /\\~(?:\{(\w)\}|(\w))/g, /\\"(?:\{(\w)\}|(\w))/g, /\\check\{(\w)\}/g, /\\c\{(\w)\}/g, /[\{\}]/g ], [ '$1$2\u0300', '$1$2\u0301', '$1$2\u0302', '$1$2\u0304', '$1$2\u0308', '$1\u030C', '\xE7', '' ], this.title );

      this.authors = this.authors.split( /,/ );
      this.authors.pop();
      var author   = this.authors.pop();
      this.authors = this.authors.length == 0 ? author : ( this.authors.length == 1 ? ( this.authors[0] + ' and ' + author ) : ( this.authors.join( ', ' ) + ', and ' + author ) );

      this.div = this.draw();
    }



    this.install();
  }

  new App()<?php
  $reference_query = mysql_query( 'SELECT r.referenceID, r.authorList, r.title, r.year, r.journal, r.filename FROM tblReferences r ORDER BY r.authorList, r.year, r.title' );
  while ( $reference_row = mysql_fetch_row( $reference_query ) )
  {
    printf( ".addElt( new Elt( %d, '%s', '%s', %d, '%s', '%s' ) )",
      $reference_row[0],
      JSSafe( $reference_row[1] ),
      JSSafe( $reference_row[2] ),
      $reference_row[3],
      JSSafe( $reference_row[4] ),
      JSSafe( $reference_row[5] )
    );
  }
?>.update();
  document.getElementById( 'search' ).focus();
</script>
<?php



  wrapPageBottom();



?>