source view: references.php


<?php






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



  $_DB_USE_MYSQLI = true;
  require './classes/Admin.php';
  require './classes/DB.php';
  if ( isset( $_GET['bibtex'] ) && intval( $_GET['bibtex'] ) == $_GET['bibtex'] )
  {
    $reference_id = intval( $_GET['bibtex'] );
    $bibtex_query = DB::getDB()->prepare( 'SELECT r.bibTeX FROM tblReferences r WHERE r.referenceID = ?' );
    $bibtex_query->bind_param( 'i', $reference_id );
    $bibtex_query->execute();
    $bibtex_query->bind_result( $bibtex );
    $bibtex_query->fetch();
    $bibtex_query->close();

    header( 'Content-Type: application/json' );
    print "['" . JSSafe( str_replace( array( "\n", "\t" ), array( "\\n", "\\t" ), $bibtex ) ) . "']";
    exit();
  }

  if ( isset( $_GET['remove'] ) && intval( $_GET['remove'] ) == intval( $_GET['remove'] ) && Admin::isLoggedIn() )
  {
    $reference_id = intval( $_GET['remove'] );
    $file_query   = DB::getDB()->prepare( 'SELECT r.filename FROM tblReferences r WHERE r.referenceID = ?' );
    $file_query->bind_param( 'i', $reference_id );
    $file_query->execute();
    $file_query->bind_result( $filename );
    if ( $file_query->fetch() )
    {
      $file_query->close();
      $basedir = ( preg_match( '/^dev\./', $_SERVER['SERVER_NAME'] ) ? '.' : '' ) . './blog-data/pdfs/references/';
      unlink( $basedir . $filename );

      $delete_query = DB::getDB()->prepare( 'DELETE FROM tblReferences WHERE referenceID = ?' );
      $delete_query->bind_param( 'i', $reference_id );
      $delete_query->execute();
      $delete_query->close();
    }
    else
    {
      $file_query->close();
    }

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



  // parse query, return json
  if ( isset( $_GET['q'] ) )
  {
  }






  require './classes/Page.php';
  $page = new Page();
  $page->setTitle( 'references' );
  $page->setParentURL( 'research.php' );



  $page->addHeader( <<<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%;
  }

  ul
  {
    list-style-type: square;
    margin-left:     0ex;
    padding-left:    4ex;
  }
</style>

EOS
);



  $page->printPageHeader();



?>
<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;

    this.install();
  }

  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 ( Admin::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|v|u|H)(?:\{(\w)\}|(\w))/g, /[\{\}]/g, /[^a-zA-Z0-9\-\+]+/g ], [ '$1$2', '', ' ' ], this.authors ).toLowerCase();
    this.search_title   = Elt.serialReplace( [ /\\(?:[c'"`^~]|check|v|u|H)(?:\{(\w)\}|(\w))/g, /[\{\}]/g, /[^a-zA-Z0-9\-]+/g, /(?:``|'')/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|v)\{(\w)\}/g, /\\c\{(\w)\}/g, /\\H\{(\w)\}/g, /\\u\{(\w)\}/g, /[\{\}]/g, /\,.*?(?:\+|$)/g ], [ '$1$2\u0300', '$1$2\u0301', '$1$2\u0302', '$1$2\u0304', '$1$2\u0308', '$1\u030C', '\xE7', '$1\u030B', '$1\u0306', '', ',' ], this.authors );
    this.journal        = Elt.serialReplace( [ /\\`(?:\{(\w)\}|(\w))/g, /\\'(?:\{(\w)\}|(\w))/g, /\\\^(?:\{(\w)\}|(\w))/g, /\\~(?:\{(\w)\}|(\w))/g, /\\"(?:\{(\w)\}|(\w))/g, /\\(?:check|v)\{(\w)\}/g, /\\c\{(\w)\}/g, /\\H\{(\w)\}/g, /\\u\{(\w)\}/g, /[\{\}]/g, /\,.*?(?:\+|$)/g, /\\&/g ], [ '$1$2\u0300', '$1$2\u0301', '$1$2\u0302', '$1$2\u0304', '$1$2\u0308', '$1\u030C', '\xE7', '$1\u030B', '$1\u0306', '', ',', '&' ], this.journal );
    this.title          = Elt.serialReplace( [ /\\`(?:\{(\w)\}|(\w))/g, /\\'(?:\{(\w)\}|(\w))/g, /\\\^(?:\{(\w)\}|(\w))/g, /\\~(?:\{(\w)\}|(\w))/g, /\\"(?:\{(\w)\}|(\w))/g, /\\(?:check|v)\{(\w)\}/g, /\\c\{(\w)\}/g, /\\H\{(\w)\}/g, /\\u\{(\w)\}/g, /[\{\}]/g, /(?:``|'')/g, /\\&/g ], [ '$1$2\u0300', '$1$2\u0301', '$1$2\u0302', '$1$2\u0304', '$1$2\u0308', '$1\u030C', '\xE7', '$1\u030B', '$1\u0306', '', '"', '&' ], 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();
  };

    new App()<?php
  $reference_query_string = <<<EOQ
SELECT
  r.referenceID,
  r.authorList,
  r.title,
  r.year,
  r.journal,
  r.filename
FROM tblReferences r
ORDER BY
  r.authorList,
  r.year,
  r.title
EOQ;
  $reference_query = DB::getDB()->prepare( $reference_query_string );
  $reference_query->execute();
  $reference_query->bind_result( $reference_id, $author_list, $title, $year, $journal, $filename );
  while ( $reference_query->fetch() )
  {
    printf( ".addElt( new Elt( %d, '%s', '%s', %d, '%s', '%s' ) )",
      $reference_id,
      JSSafe( $author_list ),
      JSSafe( $title ),
      $year,
      JSSafe( $journal ),
      JSSafe( $filename )
    );
  }

  $reference_query->close();
?>.update();

  let focus = function () { document.getElementById( 'search' ).focus(); };
  window.addEventListener( 'keypress', function ( e ) {
    let k = e.which || e.keyCode;
    if ( ( k == 17 || k == 113 || k == 81 ) && e.ctrlKey ) { focus(); }
  } );
  focus();
</script>
<?php



  $page->printPageFooter();



?>