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