2016-08-24 09:43:05 -04:00
|
|
|
/**
|
2016-08-24 10:19:11 -04:00
|
|
|
* Summary page program
|
|
|
|
*
|
2017-12-11 12:10:04 -05:00
|
|
|
* Copyright (C) 2016, 2017 LoVullo Associates, Inc.
|
2016-08-24 10:19:11 -04:00
|
|
|
*
|
|
|
|
* This file is part of the Liza Data Collection Framework
|
|
|
|
*
|
|
|
|
* liza is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
2016-08-24 09:43:05 -04:00
|
|
|
* This file is used for direct interaction with the rater for testing purposes.
|
|
|
|
* As such, much of it is a rushed implementation; it's a bit of a kluge and
|
|
|
|
* could use some refactoring.
|
|
|
|
*
|
|
|
|
* Also, it is terriby stateful and difficult to work with. I have the utmost
|
|
|
|
* confidence in your ability to look away and pretend you never saw this
|
|
|
|
* script.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// intentionally global; developers can override
|
|
|
|
var program = document.location.pathname.match( '/raters/(.*?)/' )[1],
|
|
|
|
submit_url = '/raters/submit-test.php?program=' + program,
|
|
|
|
supplier = rater.supplier,
|
|
|
|
prior_url = '/raters/submit-test.php?retrieve=' + supplier +
|
|
|
|
'&program=' + program,
|
|
|
|
qdata_host = 'dev';
|
|
|
|
|
|
|
|
var client = ( function()
|
|
|
|
{
|
|
|
|
// URL to which quote/result submissions should be POSTed
|
|
|
|
var form = document.querySelector( 'form.entry-form' ),
|
|
|
|
final_prem = form.querySelector( '.final-premium' ),
|
|
|
|
final_accept = form.querySelector( '.final-accept' ),
|
|
|
|
final_comments = form.querySelector( '.final-comments' ),
|
|
|
|
voi = document.getElementById( 'voi-list' ),
|
|
|
|
coview = document.getElementById( 'class-overview-list' ),
|
|
|
|
|
|
|
|
final_good = document.getElementById( 'final-accept-good' ),
|
|
|
|
final_bad = document.getElementById( 'final-accept-bad' ),
|
|
|
|
|
|
|
|
load_prior = document.getElementById( 'load-prior' ),
|
|
|
|
|
|
|
|
workstatus = null,
|
|
|
|
|
|
|
|
valspan = {},
|
|
|
|
bucket = {},
|
|
|
|
rate_result = {},
|
|
|
|
|
|
|
|
// used to overwrite existing test cases rather than create a new
|
|
|
|
save_id = '',
|
|
|
|
prior_result,
|
|
|
|
|
|
|
|
rate_callback = function() {},
|
|
|
|
|
|
|
|
// whether to ignore user input (do not put in bucket)
|
|
|
|
ignore_input = false;
|
|
|
|
|
|
|
|
|
|
|
|
populateBucket();
|
|
|
|
|
|
|
|
|
|
|
|
function setWorkStatus( message )
|
|
|
|
{
|
|
|
|
if ( workstatus === null )
|
|
|
|
{
|
|
|
|
workstatus = document.createElement( 'div' );
|
|
|
|
workstatus.id = 'workstatus';
|
|
|
|
|
|
|
|
document.body.appendChild( workstatus );
|
|
|
|
}
|
|
|
|
|
|
|
|
workstatus.innerHTML = message;
|
|
|
|
workstatus.className = ( message ) ? 'show' : '';
|
|
|
|
}
|
|
|
|
|
|
|
|
function populateBucket()
|
|
|
|
{
|
|
|
|
Array.prototype.slice.call( form.querySelectorAll( '[name]' ) ).forEach(
|
|
|
|
function( field )
|
|
|
|
{
|
|
|
|
var name = field.name.replace( /\[\]$/, '' );
|
|
|
|
|
|
|
|
if ( !name )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if the name does not match, then we removed the square
|
|
|
|
// brackets, meaning that this is a set
|
|
|
|
bucket[ name ] = ( name === field.name )
|
|
|
|
? field.value
|
|
|
|
: [ field.value ];
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function overrideBucket( boverride )
|
|
|
|
{
|
|
|
|
for ( var name in boverride )
|
|
|
|
{
|
|
|
|
bucket[ name ] = boverride[ name ];
|
|
|
|
}
|
|
|
|
|
|
|
|
emptyBucket();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function removeEntryFocus()
|
|
|
|
{
|
|
|
|
form.className = form.className.replace( /\bfocus\b/, '' );
|
|
|
|
}
|
|
|
|
|
|
|
|
document.body.addEventListener( 'mouseup', function( e )
|
|
|
|
{
|
|
|
|
var overform = hasParent( form, e.target );
|
|
|
|
|
|
|
|
if ( overform === false )
|
|
|
|
{
|
|
|
|
removeEntryFocus();
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
|
|
|
|
form.addEventListener( 'reset', function()
|
|
|
|
{
|
|
|
|
if ( ignore_input )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// wait until *after* reset
|
|
|
|
setTimeout( function()
|
|
|
|
{
|
|
|
|
clearTestCase();
|
|
|
|
|
|
|
|
bucket = {};
|
|
|
|
populateBucket();
|
|
|
|
clearSummaryPremium();
|
|
|
|
|
|
|
|
setWorkStatus();
|
|
|
|
}, 0 );
|
|
|
|
} );
|
|
|
|
|
|
|
|
form.addEventListener( 'mouseover', function()
|
|
|
|
{
|
|
|
|
showEntryForm();
|
|
|
|
} );
|
|
|
|
|
|
|
|
|
|
|
|
function clearTestCase()
|
|
|
|
{
|
|
|
|
save_id = '';
|
|
|
|
prior_result = undefined;
|
|
|
|
|
|
|
|
// clear prior message
|
|
|
|
Prior.setPriorMessage( null );
|
|
|
|
|
|
|
|
// clear prior class from body
|
|
|
|
document.body.className = document.body.className.replace(
|
|
|
|
/\bprior\b/, ''
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function setTestCase( id, result )
|
|
|
|
{
|
|
|
|
save_id = ''+( id );
|
|
|
|
prior_result = result;
|
|
|
|
|
|
|
|
// this really should be set...
|
|
|
|
if ( !( prior_result.vars ) )
|
|
|
|
{
|
|
|
|
prior_result.vars = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
// add prior class name to body
|
|
|
|
document.body.className += ' prior';
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function showEntryForm()
|
|
|
|
{
|
|
|
|
if ( form.className.match( /\bfocus\b/ ) )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
form.className += ' focus';
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// on field change, update bucket
|
|
|
|
form.addEventListener( 'change', function( e )
|
|
|
|
{
|
|
|
|
if ( ignore_input )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we changed something, then the displayed premium (if any) must be
|
|
|
|
// invalidated
|
|
|
|
clearSummaryPremium();
|
|
|
|
|
|
|
|
var target = e.target,
|
|
|
|
name = target.name.replace( /\[\]$/, '' ),
|
|
|
|
value = target.value.trim();
|
|
|
|
|
|
|
|
if ( !name )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if this is a set, we want to store every value
|
|
|
|
if ( name !== target.name )
|
|
|
|
{
|
|
|
|
var toarr = Array.prototype.slice;
|
|
|
|
|
|
|
|
// retrieve all the rows
|
|
|
|
var rows = toarr.call(
|
|
|
|
target.parentElement.parentElement.parentElement
|
|
|
|
.querySelectorAll( '.entry-row' )
|
|
|
|
);
|
|
|
|
|
|
|
|
// determine if we're working with a matrix
|
|
|
|
var matrix = /\bmatrix\b/.test( rows[ 0 ].className );
|
|
|
|
|
|
|
|
value = [];
|
|
|
|
rows.forEach( function( row, i )
|
|
|
|
{
|
|
|
|
var ref = value;
|
|
|
|
|
|
|
|
// for matricies, add value to a sub-array; vectors, just keep
|
|
|
|
// appending to the original array
|
|
|
|
if ( matrix )
|
|
|
|
{
|
|
|
|
ref = value[ i ] = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
// add each value
|
|
|
|
toarr.call( row.querySelectorAll( '[name]' ) ).forEach(
|
|
|
|
function( node )
|
|
|
|
{
|
|
|
|
ref.push( node.value.trim() );
|
|
|
|
}
|
|
|
|
);
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
|
|
|
bucket[ name ] = value;
|
|
|
|
} );
|
|
|
|
|
|
|
|
// update screen on submit
|
|
|
|
form.addEventListener( 'submit', function( e )
|
|
|
|
{
|
|
|
|
// do not submit the form
|
|
|
|
e.preventDefault();
|
|
|
|
rate( bucket );
|
|
|
|
} );
|
|
|
|
|
|
|
|
form.addEventListener( 'click', function( e )
|
|
|
|
{
|
|
|
|
if ( e.target.className === 'entry-add' )
|
|
|
|
{
|
|
|
|
addRow( e.target.parentElement, e.target );
|
|
|
|
e.preventDefault();
|
|
|
|
}
|
|
|
|
else if ( e.target.className === 'entry-rm' )
|
|
|
|
{
|
|
|
|
removeColumn( e.target );
|
|
|
|
e.preventDefault();
|
|
|
|
}
|
|
|
|
else if ( e.target.className === 'entry-add-matrix' )
|
|
|
|
{
|
|
|
|
addColumn( e.target );
|
|
|
|
e.preventDefault();
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final_good.addEventListener( 'click', function( e )
|
|
|
|
{
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
showFinalComments( true, function( comment, _, waiting )
|
|
|
|
{
|
|
|
|
var prem = rate_result.premium;
|
|
|
|
|
|
|
|
hideFinalAccept();
|
|
|
|
submitQuote( bucket, rate_result, comment, true, waiting, prem, save_id );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
final_bad.addEventListener( 'click', function( e )
|
|
|
|
{
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
showFinalComments( false, function( comment, expect, waiting )
|
|
|
|
{
|
|
|
|
hideFinalAccept();
|
|
|
|
submitQuote( bucket, rate_result, comment, false, waiting, expect, save_id );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
|
|
|
|
function showFinalComments( looksgood, callback )
|
|
|
|
{
|
|
|
|
final_comments.className += ' show';
|
|
|
|
|
|
|
|
var submit = document.getElementById( 'final-submit' ),
|
|
|
|
cancel = document.getElementById( 'final-cancel' ),
|
|
|
|
|
|
|
|
expect_container = document.getElementById(
|
|
|
|
'final-expect-container'
|
|
|
|
),
|
|
|
|
|
|
|
|
submit_new = document.getElementById( 'final-submit-new' ),
|
|
|
|
|
|
|
|
listener;
|
|
|
|
|
|
|
|
// we do not care about the expected value if the premium looks good
|
|
|
|
expect_container.style.display = ( looksgood )
|
|
|
|
? 'none'
|
|
|
|
: 'inline';
|
|
|
|
|
|
|
|
// if a test case is set, give them the option to clear it and submit it
|
|
|
|
// as a new test case
|
|
|
|
submit_new.style.display = ( save_id )
|
|
|
|
? 'inline'
|
|
|
|
: 'none';
|
|
|
|
|
|
|
|
// make it very clear what the user is about to do
|
|
|
|
submit.innerHTML = ( save_id )
|
|
|
|
? 'Update Existing Test Case'
|
|
|
|
: 'Submit';
|
|
|
|
|
|
|
|
// we won't use addEventListener becuase we only want one event to be
|
|
|
|
// attached
|
|
|
|
submit.onclick = function( e )
|
|
|
|
{
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
var comments = document.getElementById( 'final-comments' ),
|
|
|
|
expected = document.getElementById( 'final-expected' ),
|
|
|
|
waiting = document.getElementById( 'final-waiting' );
|
|
|
|
|
|
|
|
callback(
|
|
|
|
comments.value,
|
|
|
|
+( expected.value.replace( /^\$/, '' ) ),
|
|
|
|
!!waiting.checked
|
|
|
|
);
|
|
|
|
|
|
|
|
rmclass( final_comments, 'show' );
|
|
|
|
};
|
|
|
|
|
|
|
|
submit_new.onclick = function( e )
|
|
|
|
{
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
// clear save id and trigger normal submit
|
|
|
|
save_id = '';
|
|
|
|
submit.onclick( e );
|
|
|
|
};
|
|
|
|
|
|
|
|
cancel.onclick = function( e )
|
|
|
|
{
|
|
|
|
e.preventDefault();
|
|
|
|
rmclass( final_comments, 'show' );
|
|
|
|
};
|
|
|
|
|
|
|
|
// give focus to final comments
|
|
|
|
document.getElementById( 'final-comments' ).focus();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function hideFinalAccept()
|
|
|
|
{
|
|
|
|
// replace all shows since there may be multiple
|
|
|
|
final_accept.className = final_accept.className.replace(
|
|
|
|
/\bshow\b/,
|
|
|
|
''
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getXhrJsonSync( method, url, data )
|
|
|
|
{
|
|
|
|
var xhttp = new XMLHttpRequest();
|
|
|
|
|
|
|
|
xhttp.open( method, url, false );
|
|
|
|
|
|
|
|
if ( method.toLowerCase() === 'post' )
|
|
|
|
{
|
|
|
|
xhttp.setRequestHeader( 'Content-type',
|
|
|
|
'application/x-www-form-urlencoded'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
xhttp.send( ( data ) ? 'data=' + JSON.stringify( data ) : null );
|
|
|
|
|
|
|
|
if ( xhttp.status !== 200 )
|
|
|
|
{
|
|
|
|
throw Error( 'Submit failed; status: ' + xhttp.status );
|
|
|
|
}
|
|
|
|
|
|
|
|
// this will fail if the response is crap, but will be caught by the
|
|
|
|
// exception
|
|
|
|
return JSON.parse( xhttp.responseText );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getXhrJson( method, url, data, callback )
|
|
|
|
{
|
|
|
|
var xhttp = new XMLHttpRequest();
|
|
|
|
xhttp.open( method, url, true );
|
|
|
|
|
|
|
|
if ( method.toLowerCase() === 'post' )
|
|
|
|
{
|
|
|
|
xhttp.setRequestHeader( 'Content-type',
|
|
|
|
'application/x-www-form-urlencoded'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
xhttp.onload = function()
|
|
|
|
{
|
|
|
|
if ( xhttp.status !== 200 )
|
|
|
|
{
|
|
|
|
callback( null,
|
|
|
|
Error( 'Submit failed; status: ' + xhttp.status )
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
callback( JSON.parse( xhttp.responseText ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
xhttp.send( ( data ) ? 'data=' + JSON.stringify( data ) : null );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function submitQuote(
|
|
|
|
bucket, result, comment, looksgood, waiting, expected, caseid, success_callback
|
|
|
|
)
|
|
|
|
{
|
|
|
|
// we don't want to modify the original result (could use
|
|
|
|
// Object.create() here, but they may be using IE)
|
|
|
|
var tmpresult = function() {};
|
|
|
|
tmpresult.prototype = result;
|
|
|
|
|
|
|
|
// it is absolutely pointless to store debug information since the ids
|
|
|
|
// change at any time and are dependent on the XSL processor
|
|
|
|
var submit_result = new tmpresult();
|
|
|
|
|
|
|
|
// so that it's property serialized
|
|
|
|
for ( var name in result )
|
|
|
|
{
|
|
|
|
submit_result[ name ] = result[ name ];
|
|
|
|
}
|
|
|
|
|
|
|
|
// we do not need the debug information (there's a lot of it and it
|
|
|
|
// changes frequently)
|
|
|
|
submit_result.debug = undefined;
|
|
|
|
|
|
|
|
// nor do we need constants (especially large ones)!
|
|
|
|
submit_result.consts = undefined;
|
|
|
|
|
|
|
|
var data = {
|
|
|
|
bucket: bucket,
|
|
|
|
result: submit_result,
|
|
|
|
looksgood: !!looksgood,
|
|
|
|
waiting: !!waiting,
|
|
|
|
comment: encodeURIComponent( comment ),
|
|
|
|
expected: expected,
|
|
|
|
supplier: supplier,
|
|
|
|
|
|
|
|
// will cause an existing test case to be overwritten, if set
|
|
|
|
caseid: caseid,
|
|
|
|
};
|
|
|
|
|
|
|
|
getXhrJson( 'POST', submit_url, data, function( response, err )
|
|
|
|
{
|
|
|
|
// check the response of the actual save (just because we
|
|
|
|
// got a HTTP 200 doesn't mean we successfully saved to the
|
|
|
|
// server; we could have also hit the wrong page
|
|
|
|
// (misconfigured)!)
|
|
|
|
if ( err )
|
|
|
|
{
|
|
|
|
alert(
|
|
|
|
'Ah, crap! Quote submission failed! Contact IT before ' +
|
|
|
|
'you do anything else.\n\n' +
|
|
|
|
'Here are the boring details:\n' +
|
|
|
|
' ' + err.message
|
|
|
|
);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
success_callback && success_callback();
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function addRow( parent, before )
|
|
|
|
{
|
|
|
|
before = before || parent.querySelector( '.entry-add' );
|
|
|
|
|
|
|
|
// get the row to duplicate
|
|
|
|
var dup = parent.querySelector( '.entry-row' )
|
|
|
|
.cloneNode( true );
|
|
|
|
|
|
|
|
parent.insertBefore( dup, before );
|
|
|
|
|
|
|
|
// trigger change so that its value can be recorded
|
|
|
|
triggerChange( dup.querySelector( '[name]' ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function addColumn( event_target )
|
|
|
|
{
|
|
|
|
// get the field to duplicate
|
|
|
|
var dup = event_target.parentElement.querySelector( '.entry-field' )
|
|
|
|
.cloneNode( true );
|
|
|
|
|
|
|
|
event_target.parentElement.insertBefore( dup, event_target );
|
|
|
|
|
|
|
|
// trigger change so that its value can be recorded
|
|
|
|
triggerChange( dup.querySelector( '[name]' ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function removeColumn( event_target )
|
|
|
|
{
|
|
|
|
var rm = event_target.parentElement,
|
|
|
|
parent = rm.parentElement,
|
|
|
|
rowParent = parent.parentElement,
|
|
|
|
|
|
|
|
rows = rowParent.querySelectorAll( '.entry-row' ).length,
|
|
|
|
cols = parent.querySelectorAll( '.entry-field' ).length;
|
|
|
|
|
|
|
|
// do not remove last column of last row
|
|
|
|
if ( ( rows + cols ) === 2 )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// remove the element
|
|
|
|
parent.removeChild( rm );
|
|
|
|
|
|
|
|
// if there are no more columns, remove the row
|
|
|
|
if ( cols === 1 )
|
|
|
|
{
|
|
|
|
rowParent.removeChild( parent );
|
|
|
|
}
|
|
|
|
|
|
|
|
// re-gather values in bucket to accomodate missing value (we can do so
|
|
|
|
// simply by triggering a change on one of the elements of the same
|
|
|
|
// name)
|
|
|
|
triggerChange( rowParent.querySelector( '[name]' ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function triggerChange( element )
|
|
|
|
{
|
|
|
|
if ( !element )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// create change event
|
|
|
|
var event = document.createEvent( 'Event' );
|
|
|
|
event.initEvent( 'change', true, true );
|
|
|
|
|
|
|
|
// trigger event
|
|
|
|
element.dispatchEvent( event );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function rate( args, showresults, exception )
|
|
|
|
{
|
|
|
|
showresults = ( showresults === undefined ) ? true : !!showresults;
|
|
|
|
exception = !!exception;
|
|
|
|
|
|
|
|
var rater = window.rater;
|
|
|
|
|
|
|
|
if ( !( window.rater ) )
|
|
|
|
{
|
|
|
|
alert( 'fatal: rater unavailable.' );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
setWorkStatus( 'Performing rating...' );
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
var result = rater( args );
|
|
|
|
|
|
|
|
// XXX: ewwww
|
|
|
|
rate_result = result;
|
|
|
|
|
|
|
|
if ( !( showresults ) )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// log result to the console in case we want to peeky peeky
|
|
|
|
console.log( result );
|
|
|
|
|
|
|
|
rate_callback( result );
|
|
|
|
|
|
|
|
setWorkStatus( 'Updating premium...' );
|
|
|
|
updateSummaryPremium( result.premium );
|
|
|
|
|
|
|
|
// VOIs are referenced immediately, so render them first
|
|
|
|
updateVois( result.vars, function()
|
|
|
|
{
|
|
|
|
// classes are faster to process than the other summary values
|
|
|
|
updateSummaryClasses(
|
|
|
|
result.classes,
|
|
|
|
result.vars,
|
|
|
|
undefined,
|
|
|
|
|
|
|
|
function()
|
|
|
|
{
|
|
|
|
updateSummaryValues( result.vars );
|
|
|
|
}
|
|
|
|
);
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
catch ( e )
|
|
|
|
{
|
|
|
|
setWorkStatus( 'Rating error occurred.' );
|
|
|
|
|
|
|
|
console && console.log( e );
|
|
|
|
|
|
|
|
if ( exception )
|
|
|
|
{
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
alert( 'fatal: ' + e.message );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function updateSummaryPremium( premium )
|
|
|
|
{
|
|
|
|
final_prem.innerHTML = premium;
|
|
|
|
final_prem.className += ' show';
|
|
|
|
|
|
|
|
final_accept.className += ' show';
|
|
|
|
|
|
|
|
setPlaceholderValue( 'yields_premium', '', premium );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function clearSummaryPremium()
|
|
|
|
{
|
|
|
|
final_prem.innerHTML = '';
|
|
|
|
|
|
|
|
rmclass( final_prem, 'show' );
|
|
|
|
rmclass( final_accept, 'show' );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getValueDisplay( value )
|
|
|
|
{
|
|
|
|
if ( Array.isArray( value ) )
|
|
|
|
{
|
|
|
|
return joinValues( value );
|
|
|
|
}
|
|
|
|
|
|
|
|
return ( value === undefined )
|
|
|
|
? ''
|
|
|
|
: ''+( value );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function updateVois( vars, callback )
|
|
|
|
{
|
|
|
|
setWorkStatus( 'Processing VOIs...' );
|
|
|
|
voi.innerHTML = '';
|
|
|
|
|
|
|
|
var queue = [];
|
|
|
|
for ( var name in vars )
|
|
|
|
{
|
|
|
|
queue.push( name );
|
|
|
|
}
|
|
|
|
|
|
|
|
var vois = {},
|
|
|
|
qlen = queue.length,
|
|
|
|
i = qlen;
|
|
|
|
|
|
|
|
dequeueSetsOf( 10, function( c )
|
|
|
|
{
|
|
|
|
if ( i-- === 0 )
|
|
|
|
{
|
|
|
|
// display the VOIs
|
|
|
|
processVois( vois );
|
|
|
|
document.getElementById( 'voi-container' ).className += ' show';
|
|
|
|
|
|
|
|
setWorkStatus();
|
|
|
|
callback && callback();
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
setWorkStatus(
|
|
|
|
'Processing VOIs (' +
|
|
|
|
Math.floor( ( ( qlen - i ) / qlen ) * 100 ) +
|
|
|
|
'%)...'
|
|
|
|
);
|
|
|
|
|
|
|
|
var name = queue[ i ],
|
|
|
|
value = vars[ name ];
|
|
|
|
|
|
|
|
if ( value
|
|
|
|
&& /^prem|^min|^surcharge|^cov(erage)?|^credit|^factor|^rate|Prem|[tT]otal/
|
|
|
|
.test( name )
|
|
|
|
&& !( /^_/.test( name ) )
|
|
|
|
)
|
|
|
|
{
|
|
|
|
var display = getValueDisplay( value ),
|
|
|
|
prior = ( prior_result && prior_result.vars[ name ] )
|
|
|
|
? getValueDisplay( prior_result.vars[ name ] )
|
|
|
|
: '';
|
|
|
|
|
|
|
|
// update values of interest (voi)
|
|
|
|
if ( display !== '[]' )
|
|
|
|
{
|
|
|
|
vois[ name ] = [ display, prior ];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// continue
|
|
|
|
c();
|
|
|
|
} )();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function updateSummaryValues( vars, placeid, callback )
|
|
|
|
{
|
|
|
|
var queue = [];
|
|
|
|
|
|
|
|
for ( var name in vars )
|
|
|
|
{
|
|
|
|
queue.push( name );
|
|
|
|
}
|
|
|
|
|
|
|
|
var qlen = queue.length;
|
|
|
|
|
|
|
|
// repaint frequently; this is intensive
|
|
|
|
dequeueSetsOf( 10, function( c )
|
|
|
|
{
|
|
|
|
if ( queue.length === 0 )
|
|
|
|
{
|
|
|
|
setWorkStatus();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
name = queue.pop();
|
|
|
|
|
|
|
|
setWorkStatus(
|
|
|
|
'Formatting summary values (' +
|
|
|
|
Math.floor( ( ( qlen - queue.length ) / qlen ) * 100 ) +
|
|
|
|
'%)...'
|
|
|
|
);
|
|
|
|
|
|
|
|
var value = vars[ name ],
|
|
|
|
display = getValueDisplay( value ),
|
|
|
|
prior = ( prior_result && prior_result.vars[ name ] )
|
|
|
|
? getValueDisplay( prior_result.vars[ name ] )
|
|
|
|
: '';
|
|
|
|
|
|
|
|
|
|
|
|
setPlaceholderValue( name, '', display );
|
|
|
|
|
|
|
|
if ( prior )
|
|
|
|
{
|
|
|
|
setPlaceholderValue( name, '-prior', prior );
|
|
|
|
}
|
|
|
|
|
|
|
|
setLetListPlaceholders( name, display, prior );
|
|
|
|
|
|
|
|
// continue
|
|
|
|
c();
|
|
|
|
} )();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function dequeueSetsOf( n, c )
|
|
|
|
{
|
|
|
|
return function dq( i )
|
|
|
|
{
|
|
|
|
i = i || 0;
|
|
|
|
|
|
|
|
c( function()
|
|
|
|
{
|
|
|
|
if ( i === 0 )
|
|
|
|
{
|
|
|
|
setTimeout( function()
|
|
|
|
{
|
|
|
|
dq( n )
|
|
|
|
}, 0 );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dq( i - 1 );
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function processVois( vois )
|
|
|
|
{
|
|
|
|
// add the vois to the screen in the proper order (reversed)
|
|
|
|
var i = window.voi_order.length;
|
|
|
|
while ( i-- )
|
|
|
|
{
|
|
|
|
var data = window.voi_order[ i ],
|
|
|
|
name = data[ 0 ],
|
|
|
|
depth = data[ 1 ],
|
|
|
|
href = data[ 2 ];
|
|
|
|
|
|
|
|
if ( vois[ name ] )
|
|
|
|
{
|
|
|
|
var voi = vois[ name ];
|
|
|
|
addVoi( name, voi[ 0 ], voi[ 1 ], href, depth );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function addVoi( name, value, prior, href, depth )
|
|
|
|
{
|
|
|
|
depth = depth || 0;
|
|
|
|
|
|
|
|
// if the VOI has a value other than 0 (our poor-man check is using a
|
|
|
|
// regex to remove anything and see if we have a non-empty string left)
|
|
|
|
if ( ( value.replace( /[\[\]0,]/g, '' ) === '' )
|
|
|
|
&& ( prior.replace( /[\[\]0,]/g, '' ) === '' )
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var depthstr = '',
|
|
|
|
i = depth;
|
|
|
|
while ( i-- )
|
|
|
|
{
|
|
|
|
if ( i === 0 )
|
|
|
|
{
|
|
|
|
depthstr += '|-';
|
|
|
|
}
|
|
|
|
|
|
|
|
depthstr += ' ';
|
|
|
|
}
|
|
|
|
|
|
|
|
// if href is not given, then use name
|
|
|
|
href = href || name;
|
|
|
|
|
|
|
|
// got lazy.
|
|
|
|
var tr = document.createElement( 'tr' );
|
|
|
|
tr.className = 'depth' + depth;
|
|
|
|
tr.innerHTML = (
|
|
|
|
'<td>' +
|
|
|
|
'<a href="#' + href + '">' +
|
|
|
|
depthstr + name +
|
|
|
|
'</a>' +
|
|
|
|
'</td>' +
|
|
|
|
'<td>' + depthstr.replace( /-/, '' ) + value + '</td>' +
|
|
|
|
( ( !prior ) ? '' :
|
|
|
|
'<td class="prior">' + prior + '</td>'
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
tr.addEventListener( 'click', function( e )
|
|
|
|
{
|
|
|
|
// ignore link clicks
|
|
|
|
if ( e.target.nodeName === 'A' )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var val = JSON.parse( value );
|
|
|
|
if ( Array.isArray( val ) )
|
|
|
|
{
|
|
|
|
var t = 0;
|
|
|
|
for ( var i in val )
|
|
|
|
{
|
|
|
|
t += val[ i ];
|
|
|
|
}
|
|
|
|
|
|
|
|
val = t;
|
|
|
|
}
|
|
|
|
|
|
|
|
voiPainterAdd( tr, val );
|
|
|
|
} );
|
|
|
|
|
|
|
|
voi.appendChild( tr );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function addClassOverview( name, value )
|
|
|
|
{
|
|
|
|
var prior = ( prior_result && prior_result.vars[ name ] )
|
|
|
|
? getValueDisplay( prior_result.vars[ name ] )
|
|
|
|
: '';
|
|
|
|
|
|
|
|
// got lazy.
|
|
|
|
var tr = document.createElement( 'tr' );
|
|
|
|
tr.innerHTML = (
|
|
|
|
'<td>' +
|
|
|
|
'<a href="#:class:' + name + '">' +
|
|
|
|
name +
|
|
|
|
'</a>' +
|
|
|
|
'</td>'
|
|
|
|
);
|
|
|
|
|
|
|
|
coview.appendChild( tr );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function joinValues( values )
|
|
|
|
{
|
|
|
|
var ret = '[';
|
|
|
|
|
|
|
|
if ( Array.isArray( values[ 0 ] ) )
|
|
|
|
{
|
|
|
|
var subvals = [];
|
|
|
|
|
|
|
|
for ( var i in values )
|
|
|
|
{
|
|
|
|
subvals.push( joinValues( values[ i ] ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
ret += subvals.join( ', ' );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ret += ( Array.isArray( values ) )
|
|
|
|
? values.join( ', ' )
|
|
|
|
: values;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret + ']';
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function updateSummaryClasses( classes, vars, placeid, callback )
|
|
|
|
{
|
|
|
|
coview.innerHTML = '';
|
|
|
|
|
|
|
|
var queue = [];
|
|
|
|
|
|
|
|
for ( var name in classes )
|
|
|
|
{
|
|
|
|
queue.push( name );
|
|
|
|
}
|
|
|
|
|
|
|
|
var qlen = queue.length;
|
|
|
|
|
|
|
|
dequeueSetsOf( 10, function( c )
|
|
|
|
{
|
|
|
|
if ( queue.length === 0 )
|
|
|
|
{
|
|
|
|
setWorkStatus();
|
|
|
|
callback && callback();
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var name = queue.pop();
|
|
|
|
|
2017-12-11 12:10:04 -05:00
|
|
|
// hide internal classes ("-" prefix)
|
|
|
|
if ( /^-/.test( name ) )
|
|
|
|
{
|
|
|
|
return c();
|
|
|
|
}
|
|
|
|
|
2016-08-24 09:43:05 -04:00
|
|
|
setWorkStatus(
|
|
|
|
'Formatting class summary values (' +
|
|
|
|
Math.floor( ( ( qlen - queue.length ) / qlen ) * 100 ) +
|
|
|
|
'%)...'
|
|
|
|
);
|
|
|
|
|
|
|
|
// output the classification and the total premium for the class
|
|
|
|
setPlaceholderValue( 'class-' + name, placeid,
|
|
|
|
(
|
|
|
|
''+( classes[ name ] ) +
|
|
|
|
' -> ' +
|
|
|
|
vars[ name ]
|
|
|
|
),
|
|
|
|
classes[ name ]
|
|
|
|
);
|
|
|
|
|
|
|
|
if ( prior_result
|
|
|
|
&& prior_result.classes
|
|
|
|
&& prior_result.classes[ name ]
|
|
|
|
)
|
|
|
|
{
|
|
|
|
// XXX: duplicate
|
|
|
|
setPlaceholderValue( 'class-' + name, '-prior',
|
|
|
|
(
|
|
|
|
''+( prior_result.classes[ name ] ) +
|
|
|
|
' -> ' +
|
|
|
|
prior_result.vars[ name ]
|
|
|
|
),
|
|
|
|
classes[ name ]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// if this class was a match, add it to the overview with its
|
|
|
|
// accumulator value
|
|
|
|
if ( classes[ name ] )
|
|
|
|
{
|
|
|
|
addClassOverview( name, vars[ name ] );
|
|
|
|
}
|
|
|
|
|
|
|
|
c();
|
|
|
|
} )();
|
|
|
|
|
|
|
|
document.getElementById( 'class-overview' ).className += ' show';
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function updateSummaryDebug( debug, parent, callback )
|
|
|
|
{
|
|
|
|
var queue = [];
|
|
|
|
|
|
|
|
// do nothing if debug data is not yet available
|
|
|
|
if ( !debug )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// loop through each element on the DOM, *not* each debug id returned to
|
|
|
|
// us, since we want to clear any that may be missing
|
|
|
|
Array.prototype.slice.call( parent.querySelectorAll( '.debugid' ) )
|
|
|
|
.forEach( function( element )
|
|
|
|
{
|
|
|
|
queue.push( element.id );
|
|
|
|
} );
|
|
|
|
|
|
|
|
var qlen = queue.length;
|
|
|
|
|
|
|
|
dequeueSetsOf( 10, function( c )
|
|
|
|
{
|
|
|
|
if ( queue.length === 0 )
|
|
|
|
{
|
|
|
|
setWorkStatus();
|
|
|
|
callback && callback();
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
setWorkStatus(
|
|
|
|
'Processing breakdown values (' +
|
|
|
|
Math.floor( ( ( qlen - queue.length ) / qlen ) * 100 ) +
|
|
|
|
'%)...'
|
|
|
|
);
|
|
|
|
|
|
|
|
var id = queue.pop(),
|
|
|
|
did = id.replace( /^ubd-/, '' );
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
setPlaceholderValue( id, '', ( debug[ did ] )
|
|
|
|
? JSON.stringify( debug[ did ] )
|
|
|
|
: ''
|
|
|
|
);
|
|
|
|
}
|
|
|
|
catch ( e )
|
|
|
|
{
|
|
|
|
console.error(
|
|
|
|
'Debug (stringify debug ' + did + ' ): ' +
|
|
|
|
e.message
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
c();
|
|
|
|
} )();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var getPlaceholder = ( function()
|
|
|
|
{
|
|
|
|
var domcache = {};
|
|
|
|
|
|
|
|
function getPlaceholder( name, placeid )
|
|
|
|
{
|
|
|
|
var classname = ( 'entry-value' + ( placeid || '' ) );
|
|
|
|
|
|
|
|
var current = domcache[ name + placeid ];
|
|
|
|
if ( current )
|
|
|
|
{
|
|
|
|
return current;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ignore system vars
|
|
|
|
if ( name.match( /^___/ ) )
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
var parent = document.getElementById( name );
|
|
|
|
|
|
|
|
if ( !parent )
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
var legend = parent.getElementsByTagName( 'legend' ),
|
|
|
|
dest = ( legend.length ) ? legend[ 0 ] : parent;
|
|
|
|
|
|
|
|
var element = document.createElement( 'span' );
|
|
|
|
element.className = classname;
|
|
|
|
dest.appendChild( element );
|
|
|
|
|
|
|
|
// rather than re-scanning the DOM each time
|
|
|
|
domcache[ name + placeid ] = element;
|
|
|
|
|
|
|
|
return element;
|
|
|
|
}
|
|
|
|
|
|
|
|
return getPlaceholder;
|
|
|
|
} )();
|
|
|
|
|
|
|
|
|
|
|
|
function setPlaceholderValue( name, placeid, value, hasval )
|
|
|
|
{
|
|
|
|
var p = getPlaceholder( name, placeid );
|
|
|
|
if ( p === null )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
p.innerHTML = value;
|
|
|
|
|
|
|
|
// do not handle prior flagging
|
|
|
|
if ( placeid === '-prior' )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get fieldset
|
|
|
|
var fs = p.parentNode.parentNode;
|
|
|
|
if ( fs.nodeName === 'FIELDSET' )
|
|
|
|
{
|
|
|
|
fs.className = fs.className.replace( /\Bhasval\B/, '' );
|
|
|
|
if ( ( hasval !== undefined && hasval )
|
|
|
|
// progressively more time-consuming checks
|
|
|
|
|| ( ( hasval === undefined )
|
|
|
|
&& value
|
|
|
|
&& +value !== 0
|
|
|
|
&& value.replace( /[\[\],0]/g, '' )
|
|
|
|
)
|
|
|
|
)
|
|
|
|
{
|
|
|
|
fs.className += ' hasval';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function setLetListPlaceholders( name, value, prior )
|
|
|
|
{
|
|
|
|
if ( name.match( /^___/ ) )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// certainly room for improvement here (especially performance-wise),
|
|
|
|
// but this is a quick implementation
|
|
|
|
var elements = document.querySelectorAll( '.letlist-' + name );
|
|
|
|
Array.prototype.slice.call( elements ).forEach( function( element )
|
|
|
|
{
|
|
|
|
if ( !( element.id ) )
|
|
|
|
{
|
|
|
|
// prefix with alpha so as not to cause a syntax error on query
|
|
|
|
element.id = 'll' + Math.floor(
|
|
|
|
( new Date() ).getTime() * Math.random()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
setPlaceholderValue( element.id, '', value );
|
|
|
|
|
|
|
|
// include prior values, if available
|
|
|
|
if ( prior )
|
|
|
|
{
|
|
|
|
setPlaceholderValue( element.id, '-prior', prior );
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function rmclass( element, name )
|
|
|
|
{
|
|
|
|
element.className = element.className.replace(
|
|
|
|
new RegExp( '\\b' + name + '\\b', 'g' ),
|
|
|
|
''
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function hasParent( parent, element )
|
|
|
|
{
|
|
|
|
var parentElement = element.parentElement;
|
|
|
|
|
|
|
|
if ( parentElement === parent )
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ( parentElement )
|
|
|
|
? hasParent( parent, parentElement )
|
|
|
|
: false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// XXX: This is a mess. THIS IS WHAT TIME CONSTRAINTS DO TO CODE QUALITY!
|
|
|
|
// LET ME HACK IN PEACE! >:@ (What? Unconstrained development is a fantasy?
|
|
|
|
// Is unlimited time unreasonable? Phf. Maybe that Time Weaver frog person
|
|
|
|
// knows how to help with that. If you don't know that reference and you're
|
|
|
|
// in here hacking this code, then that implies that you are new; it then
|
|
|
|
// begs the question: why has it persisted for so long!!! Of course it has,
|
|
|
|
// though. That's how TODOs/XXXs work: they don't get fixed; they just turn
|
|
|
|
// text in your editor pretty [obnoxious] colors.)
|
|
|
|
function resetFields()
|
|
|
|
{
|
|
|
|
// XXX: gahhhhhh!!!!!!!
|
|
|
|
ignore_input = true;
|
|
|
|
|
|
|
|
function rowquery( name )
|
|
|
|
{
|
|
|
|
return document.querySelectorAll(
|
|
|
|
'#param-input-' + name + ' > .entry-row'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
for ( var field in bucket )
|
|
|
|
{
|
|
|
|
// may happen if we're loading data from another source
|
|
|
|
if ( bucket[ field ] === undefined )
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
var fdata = bucket[ field ],
|
|
|
|
elements = rowquery( field ),
|
|
|
|
|
|
|
|
length = ( fdata.length > elements.length )
|
|
|
|
? fdata.length
|
|
|
|
: elements.length;
|
|
|
|
|
|
|
|
if ( elements.length === 0 )
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// not everything is an array of values
|
|
|
|
if ( Array.isArray( fdata ) )
|
|
|
|
{
|
|
|
|
// add/clear fields on the form as necessary to accomdate bucket
|
|
|
|
// data
|
|
|
|
for ( var i = 0; i < length; i++ )
|
|
|
|
{
|
|
|
|
// field exists in bucket but not on the form
|
|
|
|
if ( ( fdata[ i ] !== undefined ) && !( elements[ i ] ) )
|
|
|
|
{
|
|
|
|
addRow( elements[ 0 ].parentNode );
|
|
|
|
}
|
|
|
|
// field exists on form but not in the bucket
|
|
|
|
else if ( elements[ i ] && ( fdata[ i ] === undefined ) )
|
|
|
|
{
|
|
|
|
// TODO: remove field instead
|
|
|
|
elements[ i ].querySelector( '[name]' ).value = '';
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we have a matrix of values, we must also add columns
|
|
|
|
// for each
|
|
|
|
if ( Array.isArray( fdata[ i ] ) )
|
|
|
|
{
|
|
|
|
var element = rowquery( field )[ i ],
|
|
|
|
cols = element.querySelectorAll(
|
|
|
|
'.entry-field'
|
|
|
|
),
|
|
|
|
|
|
|
|
len = ( fdata[ i ].length > cols.length )
|
|
|
|
? fdata[ i ].length
|
|
|
|
: cols.length;
|
|
|
|
|
|
|
|
// check each column
|
|
|
|
for ( var j = 0; j < len; j++ )
|
|
|
|
{
|
|
|
|
if ( ( fdata[ i ][ j ] !== undefined )
|
|
|
|
&& !( cols[ j ] )
|
|
|
|
)
|
|
|
|
{
|
|
|
|
// re-query in case we just added a row
|
|
|
|
addColumn( element.querySelector(
|
|
|
|
'.entry-add-matrix'
|
|
|
|
) );
|
|
|
|
}
|
|
|
|
else if ( cols[ j ]
|
|
|
|
&& ( fdata[ i ][ j ] === undefined )
|
|
|
|
)
|
|
|
|
{
|
|
|
|
// TODO: remove field instead
|
|
|
|
cols[ i ].querySelector( '[name]' ).value = '';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
form.reset();
|
|
|
|
ignore_input = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function emptyBucket()
|
|
|
|
{
|
|
|
|
resetFields();
|
|
|
|
|
|
|
|
// prevent form updates from propagating to the bucket
|
|
|
|
ignore_input = true;
|
|
|
|
|
|
|
|
for ( var field in bucket )
|
|
|
|
{
|
|
|
|
var fdata = bucket[ field ];
|
|
|
|
|
|
|
|
// not everything is an array; if not, simply set the value and move
|
|
|
|
// on
|
|
|
|
if ( !( Array.isArray( fdata ) ) )
|
|
|
|
{
|
|
|
|
var element = document.querySelector(
|
|
|
|
'[name="' + field + '"]'
|
|
|
|
);
|
|
|
|
|
|
|
|
if ( element )
|
|
|
|
{
|
|
|
|
element.value = fdata;
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
var elements = document.querySelectorAll(
|
|
|
|
'[name="' + field + '[]"]'
|
|
|
|
);
|
|
|
|
|
|
|
|
var total = 0;
|
|
|
|
for ( var i = 0, l = fdata.length; i < l; i++ )
|
|
|
|
{
|
|
|
|
if ( !( elements[ i ] ) )
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if a matrix, update each value
|
|
|
|
if ( Array.isArray( fdata[ i ] ) )
|
|
|
|
{
|
|
|
|
for ( var j = 0, jl = fdata[ i ].length; j < jl; j++ )
|
|
|
|
{
|
|
|
|
elements[ total++ ].value = fdata[ i ][ j ];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// not a matrix
|
|
|
|
elements[ total++ ].value = fdata[ i ];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// re-allow input
|
|
|
|
ignore_input = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getUserFromHostname( hostname )
|
|
|
|
{
|
|
|
|
// strip off any domain, remove number from username and strip anything
|
|
|
|
// after a dash (e.g. gerwitm-ubuntu2.lovullo.local => gerwitm)
|
|
|
|
return hostname.split( '.' )[ 0 ].replace( /[0-9]+$/, '' )
|
|
|
|
.split( '-' )[ 0 ];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prior module: load prior quotes (test cases)
|
|
|
|
*
|
|
|
|
* Not to be confused in speech with the Friar module, which would have your
|
|
|
|
* premiums divinely calculated and communicated through a deep meditation.
|
|
|
|
*/
|
|
|
|
var Prior = ( function ___loadprior( dom )
|
|
|
|
{
|
|
|
|
var exports = {},
|
|
|
|
|
|
|
|
// current set of loaded test cases
|
|
|
|
curset = {};
|
|
|
|
|
|
|
|
var getLoadDialog = function()
|
|
|
|
{
|
|
|
|
// URL with fragment to automatically display this dialog
|
|
|
|
var url = document.location.href.replace( /#.*$/, '' ) + '#prior';
|
|
|
|
|
|
|
|
var dialog = dom.createElement( 'div' );
|
|
|
|
dialog.id = 'prior';
|
|
|
|
dialog.className = 'load-dialog';
|
|
|
|
dialog.innerHTML =
|
|
|
|
"<h1>Load Prior Quotes</h1>" +
|
|
|
|
"<p>" +
|
|
|
|
"Below is a list of all prior saved quotes; choose one " +
|
|
|
|
"to load it into the test area." +
|
|
|
|
"</p>" +
|
|
|
|
"<p>" +
|
|
|
|
"To load this dialog automatically on page load, you " +
|
|
|
|
"may use the following link: <a href=\"" + url + "\">" +
|
|
|
|
url + "</a>"
|
|
|
|
"</p>";
|
|
|
|
|
|
|
|
// re-test button
|
|
|
|
var retest = dom.createElement( 'button' );
|
|
|
|
retest.innerHTML = 'Regression Test';
|
|
|
|
retest.addEventListener( 'click', function( e )
|
|
|
|
{
|
|
|
|
e.preventDefault();
|
|
|
|
e.target.disabled = 'disabled';
|
|
|
|
|
|
|
|
retestAll( function()
|
|
|
|
{
|
|
|
|
e.target.disabled = '';
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
// load quote number
|
|
|
|
var loadquote = dom.createElement( 'button' );
|
|
|
|
loadquote.innerHTML = 'Load Quote #';
|
|
|
|
loadquote.addEventListener( 'click', function( e )
|
|
|
|
{
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
var qid = prompt( 'Enter quote #:' );
|
|
|
|
|
|
|
|
if ( !qid )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
exports.hideLoad();
|
|
|
|
|
|
|
|
loadQuote( qid, qdata_host );
|
|
|
|
} );
|
|
|
|
|
|
|
|
dialog.appendChild( retest );
|
|
|
|
dialog.appendChild( loadquote );
|
|
|
|
dialog.appendChild( getPriorTable() );
|
|
|
|
|
|
|
|
dom.body.appendChild( dialog );
|
|
|
|
|
|
|
|
// reassign the function to always return the instance
|
|
|
|
getLoadDialog = function()
|
|
|
|
{
|
|
|
|
return dialog;
|
|
|
|
};
|
|
|
|
|
|
|
|
return getLoadDialog();
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
var getPriorTable = function()
|
|
|
|
{
|
|
|
|
var table = dom.createElement( 'table' ),
|
|
|
|
headings = [
|
|
|
|
"Date", "Description", "User", "Premium", "Expected"
|
|
|
|
];
|
|
|
|
|
|
|
|
// add headings
|
|
|
|
for ( var head in headings )
|
|
|
|
{
|
|
|
|
var th = dom.createElement( 'th' );
|
|
|
|
th.innerHTML = headings[ head ];
|
|
|
|
th.className = headings[ head ].toLowerCase();
|
|
|
|
|
|
|
|
table.appendChild( th );
|
|
|
|
}
|
|
|
|
|
|
|
|
// add count
|
|
|
|
var count = dom.createElement( 'caption' );
|
|
|
|
count.innerHTML =
|
|
|
|
'<b>Total Count:</b> <span class="count">0</span>';
|
|
|
|
table.appendChild( count );
|
|
|
|
|
|
|
|
table.clear = function()
|
|
|
|
{
|
|
|
|
var rows = table.querySelectorAll( 'tr' );
|
|
|
|
for ( var i = 0; i < rows.length; i++ )
|
|
|
|
{
|
|
|
|
table.removeChild( rows[ i ] );
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
table.addRow = function( looksgood, waiting /*, ... */ )
|
|
|
|
{
|
|
|
|
var tr = dom.createElement( 'tr' );
|
|
|
|
|
|
|
|
// the first argument is the id
|
|
|
|
var id = arguments[ 0 ];
|
|
|
|
tr.id = '_testcase_' + id;
|
|
|
|
|
|
|
|
// the second argument will determine the row color (looksgood)
|
|
|
|
tr.className =
|
|
|
|
( ( arguments[ 1 ] )
|
|
|
|
? 'good'
|
|
|
|
: 'bad'
|
|
|
|
) +
|
|
|
|
( ( arguments[ 2 ] )
|
|
|
|
? ' waiting'
|
|
|
|
: ''
|
|
|
|
);
|
|
|
|
|
|
|
|
// all other arguments will be cells
|
|
|
|
for ( var i = 3; i < arguments.length; i++ )
|
|
|
|
{
|
|
|
|
var td = dom.createElement( 'td' );
|
|
|
|
td.innerHTML = arguments[ i ];
|
|
|
|
td.className = headings[ i - 3 ].toLowerCase();
|
|
|
|
|
|
|
|
// first cell will contain a hyperlink for auto-loading on
|
|
|
|
// visit
|
|
|
|
if ( i === 3 )
|
|
|
|
{
|
|
|
|
var a = dom.createElement( 'a' );
|
|
|
|
a.href = ( '#prior/' + id );
|
|
|
|
a.innerHTML = td.innerHTML;
|
|
|
|
|
|
|
|
td.innerHTML = '';
|
|
|
|
td.appendChild( a );
|
|
|
|
|
|
|
|
a.addEventListener( 'click', function( e )
|
|
|
|
{
|
|
|
|
doLoad( id );
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
|
|
|
tr.appendChild( td );
|
|
|
|
}
|
|
|
|
|
|
|
|
table.appendChild( tr );
|
|
|
|
};
|
|
|
|
|
|
|
|
table.setCount = function( count )
|
|
|
|
{
|
|
|
|
table.querySelector( '.count' ).innerHTML = +count;
|
|
|
|
};
|
|
|
|
|
|
|
|
table.mark = function( id, type )
|
|
|
|
{
|
|
|
|
table.querySelector( '#_testcase_' + id ).className = type;
|
|
|
|
};
|
|
|
|
|
|
|
|
table.changePremium = function( id, premium )
|
|
|
|
{
|
|
|
|
var element = table.querySelector(
|
|
|
|
'#_testcase_' + id + ' .premium'
|
|
|
|
);
|
|
|
|
|
|
|
|
// add the value and retain the previous value
|
|
|
|
element.innerHTML = '$' + premium +
|
|
|
|
'<div class="prev-val">(was ' + element.innerHTML +
|
|
|
|
')</div>';
|
|
|
|
};
|
|
|
|
|
|
|
|
table.changeComment = function( id, comment )
|
|
|
|
{
|
|
|
|
var element = table.querySelector(
|
|
|
|
'#_testcase_' + id + ' .description'
|
|
|
|
);
|
|
|
|
|
|
|
|
element.innerHTML = comment.replace( /\n/g, '<br />' );
|
|
|
|
};
|
|
|
|
|
|
|
|
function doLoad( id )
|
|
|
|
{
|
|
|
|
exports.hideLoad();
|
|
|
|
|
|
|
|
// give them an indication that something is happening
|
|
|
|
setTimeout( function()
|
|
|
|
{
|
|
|
|
loadPriorTestCase( id );
|
|
|
|
}, 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
// when a row is clicked, trigger the load
|
|
|
|
table.addEventListener( 'click', function( e )
|
|
|
|
{
|
|
|
|
// we care only of row clicks
|
|
|
|
if ( e.target.nodeName.toLowerCase() !== 'td' )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get the unique id for this test case
|
|
|
|
var id = e.target.parentNode.id.replace( /^_testcase_/, '' );
|
|
|
|
doLoad( id );
|
|
|
|
} );
|
|
|
|
|
|
|
|
getPriorTable = function()
|
|
|
|
{
|
|
|
|
return table;
|
|
|
|
};
|
|
|
|
|
|
|
|
return getPriorTable();
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
function loadPrior()
|
|
|
|
{
|
|
|
|
// first, clear out any existing results
|
|
|
|
getPriorTable().clear();
|
|
|
|
|
|
|
|
// load prior data from server
|
|
|
|
var response = getXhrJsonSync( 'GET', prior_url ),
|
|
|
|
table = getPriorTable(),
|
|
|
|
results = response.results;
|
|
|
|
|
|
|
|
// store the current set of test cases
|
|
|
|
curset = results;
|
|
|
|
|
|
|
|
// add test test case to the table
|
|
|
|
for ( testcase in results )
|
|
|
|
{
|
|
|
|
var result = results[ testcase ];
|
|
|
|
|
|
|
|
table.addRow(
|
|
|
|
result.id,
|
|
|
|
result.looksgood,
|
|
|
|
result.waiting,
|
|
|
|
result.date,
|
|
|
|
|
|
|
|
// comment
|
|
|
|
( result.comment.replace( /\n/g, '<br />' )
|
|
|
|
|| '<em>(no comment)</em>'
|
|
|
|
),
|
|
|
|
|
|
|
|
// username (from hostname)
|
|
|
|
getUserFromHostname( result.hostname ),
|
|
|
|
|
|
|
|
// premium
|
|
|
|
( '$' + ( result.premium || 0.00 ) ),
|
|
|
|
|
|
|
|
// expected premium
|
|
|
|
( ( result.expected )
|
|
|
|
? '$' + result.expected
|
|
|
|
: ( result.looksgood )
|
|
|
|
? '$' + result.premium
|
|
|
|
: '-'
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
table.setCount( results.length );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getTestCaseData( id )
|
|
|
|
{
|
|
|
|
return getXhrJsonSync( 'GET', prior_url + '&id=' + id );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getQuoteData( id, qdata_host )
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
return getXhrJsonSync(
|
|
|
|
'GET', prior_url + '&host=' + qdata_host + '&qid=' + id
|
|
|
|
);
|
|
|
|
}
|
|
|
|
catch ( e )
|
|
|
|
{
|
|
|
|
return { error: 'Invalid response from server.' };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function showRatingResultPage()
|
|
|
|
{
|
|
|
|
dom.location.hash = 'test-data';
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function loadQuote( qid, host, bucket_override )
|
|
|
|
{
|
|
|
|
var data = getQuoteData( qid, host );
|
|
|
|
if ( data.error !== 'OK' )
|
|
|
|
{
|
|
|
|
alert( data.error );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
rater.fromMap( data.results.bucket, function( data )
|
|
|
|
{
|
|
|
|
bucket = data;
|
|
|
|
emptyBucket();
|
|
|
|
|
|
|
|
if ( bucket_override )
|
|
|
|
{
|
|
|
|
overrideBucket( bucket_override );
|
|
|
|
}
|
|
|
|
|
|
|
|
showRatingResultPage();
|
|
|
|
rate( bucket );
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function loadPriorTestCase( id )
|
|
|
|
{
|
|
|
|
var casedata = getTestCaseData( id );
|
|
|
|
|
|
|
|
if ( casedata.status !== 200 )
|
|
|
|
{
|
|
|
|
alert( 'Could not load test case.\n\n' + casedata.error );
|
|
|
|
}
|
|
|
|
|
|
|
|
var data = casedata.results;
|
|
|
|
|
|
|
|
// display the message so that they know what they're looking at
|
|
|
|
exports.setPriorMessage(
|
|
|
|
data.hostname, data.comment, data.looksgood, id
|
|
|
|
);
|
|
|
|
|
|
|
|
// overwrite the bucket
|
|
|
|
bucket = data.bucket;
|
|
|
|
emptyBucket();
|
|
|
|
|
|
|
|
// set this test case so that our next save overwrites it
|
|
|
|
setTestCase( id, data.result );
|
|
|
|
|
|
|
|
// make it obvious to the user that the data has been loaded
|
|
|
|
clearSummaryPremium();
|
|
|
|
showEntryForm();
|
|
|
|
|
|
|
|
// prefill the comment and expected data on the submission form,
|
|
|
|
// leaving room at the top for additional comments
|
|
|
|
document.getElementById( 'final-comments' )
|
|
|
|
.innerHTML = (
|
|
|
|
"\n\n\n" +
|
|
|
|
getPrevSubmitCommentText(
|
|
|
|
data.hostname,
|
|
|
|
data.comment
|
|
|
|
)
|
|
|
|
);
|
|
|
|
document.getElementById( 'final-expected' ).value = data.expected;
|
|
|
|
|
|
|
|
// let the browser catch up and then perform rating
|
|
|
|
setTimeout( function()
|
|
|
|
{
|
|
|
|
// switch to test data and rate
|
|
|
|
document.location.hash = '#test-data';
|
|
|
|
rate( bucket );
|
|
|
|
}, 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getPrevSubmitCommentText( hostname, comment )
|
|
|
|
{
|
|
|
|
return "Previously submitted by " +
|
|
|
|
getUserFromHostname( hostname ) + ": " + comment;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function retestAll( callback )
|
|
|
|
{
|
|
|
|
var queue = [],
|
|
|
|
skipped = 0,
|
|
|
|
|
|
|
|
// regression test results, which may or may not be submitted to
|
|
|
|
// the server
|
|
|
|
history = {};
|
|
|
|
|
|
|
|
// queue each of the test cases
|
|
|
|
for ( var testcase in curset )
|
|
|
|
{
|
|
|
|
queue.push( curset[ testcase ] );
|
|
|
|
}
|
|
|
|
|
|
|
|
var count = failures = changed = 0,
|
|
|
|
start = ( new Date() ).getTime();
|
|
|
|
|
|
|
|
var run = function()
|
|
|
|
{
|
|
|
|
// do not pop(); we want to do them in order so it doesn't look
|
|
|
|
// too odd to the user
|
|
|
|
var test = queue.shift();
|
|
|
|
|
|
|
|
// all done
|
|
|
|
if ( !( test ) )
|
|
|
|
{
|
|
|
|
var time = ( new Date() ).getTime() - start;
|
|
|
|
|
|
|
|
var msg = (
|
|
|
|
'Test complete. Re-ran ' + count + ' test(s) with ' +
|
|
|
|
failures + ' failure(s) in ' + ( time / 1000 ) + 's.' +
|
|
|
|
"\n\n" +
|
|
|
|
|
|
|
|
( ( skipped )
|
|
|
|
? skipped + " test(s) premium checks " +
|
|
|
|
"were skipped because they " +
|
|
|
|
"have no expected premium; please aid in the " +
|
|
|
|
"automated testing of these by selecting " +
|
|
|
|
"them and entering an expected premium when " +
|
|
|
|
"re-submitting it (by clicking Incorrect). " +
|
|
|
|
"These skipped tests are still noted if " +
|
|
|
|
"their premiums changed (in italics), but " +
|
|
|
|
"their success statuses are left untouched."
|
|
|
|
: ''
|
|
|
|
) +
|
|
|
|
|
|
|
|
( ( failures === 0 )
|
|
|
|
? ( !skipped )
|
|
|
|
? "\n\nYou should feel pretty sweet right now."
|
|
|
|
: ''
|
|
|
|
: "\n\nSomeone's got some splainin' to do."
|
|
|
|
) +
|
|
|
|
|
|
|
|
( ( !changed ) ? "\n\nNo test cases have changed." :
|
|
|
|
"\n\n" + changed + " case(s) changed." +
|
|
|
|
"\n\nWould you like the results of this regression " +
|
|
|
|
"to be recorded? This will cause the status of each " +
|
|
|
|
"test case to be updated as shown. If unsure, click " +
|
|
|
|
"'Cancel'."
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// if we have changes, show a box asking if the changes
|
|
|
|
// should be uploaded to the server; otheriwse, just alert
|
|
|
|
// (which will return undefined and cast to false)
|
|
|
|
var submit = !!( changed && confirm || alert )
|
|
|
|
.call( window, msg );
|
|
|
|
|
|
|
|
if ( submit )
|
|
|
|
{
|
|
|
|
saveRegression( history );
|
|
|
|
}
|
|
|
|
|
|
|
|
callback && callback( count, failures, time );
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var table = getPriorTable();
|
|
|
|
table.mark( test.id, 'testing' );
|
|
|
|
|
|
|
|
setTimeout( function()
|
|
|
|
{
|
|
|
|
var testdata = getTestCaseData( test.id ).results;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// rate, but do not update the screen
|
|
|
|
rate( testdata.bucket, false, true );
|
|
|
|
}
|
|
|
|
catch ( e )
|
|
|
|
{
|
|
|
|
console.log( e );
|
|
|
|
table.mark( test.id, 'skip' );
|
|
|
|
|
|
|
|
// abort! abort!
|
|
|
|
//alert( 'An error occurred. Aborting.' );
|
|
|
|
run();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// determine what premium we're expecting (default to
|
|
|
|
// existing premium)
|
|
|
|
var expected = testdata.expected || testdata.result.premium,
|
|
|
|
skipme = !( testdata.looksgood || testdata.expected );
|
|
|
|
|
|
|
|
var correct = (
|
|
|
|
rate_result.premium
|
|
|
|
&& ( rate_result.premium == expected )
|
|
|
|
);
|
|
|
|
|
|
|
|
// add to changed count if the status changed
|
|
|
|
var has_changed = ( testdata.looksgood !== correct );
|
|
|
|
changed += ( +has_changed && !skipme );
|
|
|
|
|
|
|
|
skipped += +skipme;
|
|
|
|
|
|
|
|
// only add to the total count if the premium was actually
|
|
|
|
// compared
|
|
|
|
if ( !skipme )
|
|
|
|
{
|
|
|
|
count++;
|
|
|
|
|
|
|
|
if ( !( correct ) )
|
|
|
|
{
|
|
|
|
failures++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// store in case the user decides to save to the server
|
|
|
|
if ( has_changed )
|
|
|
|
{
|
|
|
|
history[ test.id ] = {
|
|
|
|
looksgood: correct,
|
|
|
|
bucket: testdata.bucket,
|
|
|
|
result: rate_result,
|
|
|
|
expected: expected,
|
|
|
|
comment: testdata.comment,
|
|
|
|
hostname: testdata.hostname,
|
|
|
|
previous: testdata.result,
|
|
|
|
};
|
|
|
|
|
|
|
|
// show the comment that would be saved to the
|
|
|
|
// server, should they choose to do so
|
|
|
|
table.changeComment(
|
|
|
|
test.id,
|
|
|
|
genRegressionComment( history[ test.id ] )
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// update table
|
|
|
|
table.changePremium( test.id, rate_result.premium );
|
|
|
|
table.mark(
|
|
|
|
test.id,
|
|
|
|
(
|
|
|
|
( ( correct )
|
|
|
|
? 'good'
|
|
|
|
: 'bad'
|
|
|
|
) +
|
|
|
|
( ( has_changed )
|
|
|
|
? ' changed'
|
|
|
|
: ''
|
|
|
|
) +
|
|
|
|
( ( rate_result.premium !== testdata.result.premium )
|
|
|
|
? ' premchanged'
|
|
|
|
: ''
|
|
|
|
) +
|
|
|
|
( ( skipme )
|
|
|
|
? ' skipped'
|
|
|
|
: ''
|
|
|
|
)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// continue
|
|
|
|
run();
|
|
|
|
}, 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
// run 'em one by one
|
|
|
|
setTimeout( run, 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function genRegressionComment( item )
|
|
|
|
{
|
|
|
|
return "[Regression Test: " +
|
|
|
|
( ( item.looksgood ) ? "Pass" : "Fail" ) +
|
|
|
|
"] Expected $" + item.expected + "; calculated $" +
|
|
|
|
item.result.premium + "; previously $" +
|
|
|
|
item.previous.premium + "\n\n" +
|
|
|
|
getPrevSubmitCommentText( item.hostname, item.comment );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function saveRegression( history )
|
|
|
|
{
|
|
|
|
for ( var id in history )
|
|
|
|
{
|
|
|
|
var item = history[ id ];
|
|
|
|
|
|
|
|
// generate comment
|
|
|
|
var comment = genRegressionComment( item );
|
|
|
|
|
|
|
|
submitQuote(
|
|
|
|
item.bucket,
|
|
|
|
item.result,
|
|
|
|
comment,
|
|
|
|
item.looksgood,
|
|
|
|
( item.waiting || false ),
|
|
|
|
item.expected,
|
|
|
|
id,
|
|
|
|
function() {}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
exports.initHtml = function()
|
|
|
|
{
|
|
|
|
getLoadDialog();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
exports.showLoad = function()
|
|
|
|
{
|
|
|
|
removeEntryFocus();
|
|
|
|
|
|
|
|
getLoadDialog().className += ' show';
|
|
|
|
loadPrior();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
exports.hideLoad = function()
|
|
|
|
{
|
|
|
|
var dialog = getLoadDialog();
|
|
|
|
dialog.className = dialog.className.replace( /\bshow\b/g, '' );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
exports.setPriorMessage = function( host, message, good, id )
|
|
|
|
{
|
|
|
|
var container = document.getElementById( 'prior-message' );
|
|
|
|
|
|
|
|
if ( !container )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
container.style.display = ( message ) ? 'inline-block' : 'none';
|
|
|
|
container.className = ( good ) ? 'good' : 'bad';
|
|
|
|
container.innerHTML = (
|
|
|
|
'<b>' + getUserFromHostname( host ) + ':</b> ' +
|
|
|
|
message
|
|
|
|
.replace( /^\n+|\n+$/g, '' )
|
|
|
|
.replace( / /g, ' ' )
|
|
|
|
.replace( /\t/g, ' ' )
|
|
|
|
.replace( /\n/g, '<br />' )
|
|
|
|
.replace(
|
|
|
|
/(Previously submitted by [^:]+:)/g,
|
|
|
|
'<b>$1</b>'
|
|
|
|
)
|
|
|
|
+ '<br /><br /><a href="#prior/' + id + '">[Direct Link]</a>'
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.loadQuote = loadQuote;
|
|
|
|
exports.loadPriorTestCase = loadPriorTestCase;
|
|
|
|
|
|
|
|
return exports;
|
|
|
|
} )( document );
|
|
|
|
|
|
|
|
|
|
|
|
function begin()
|
|
|
|
{
|
|
|
|
// initialize prior div
|
|
|
|
Prior.initHtml();
|
|
|
|
|
|
|
|
// allow linking to test cases
|
|
|
|
var pmatch;
|
|
|
|
if ( pmatch = document.location.href.match( /#prior(?:\/(.*))?$/ ) )
|
|
|
|
{
|
|
|
|
var id = pmatch[ 1 ];
|
|
|
|
|
|
|
|
if ( !( id ) )
|
|
|
|
{
|
|
|
|
// no id given; let them choose
|
|
|
|
Prior.showLoad();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// we were given an id; load it!
|
|
|
|
console.log( 'Loading ' + id + '...' );
|
|
|
|
Prior.loadPriorTestCase( id );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// allow settings params from the URL (very basic parsing; barely used); we
|
|
|
|
// use a colon rather than ? because ? is not included in the location
|
|
|
|
// object
|
|
|
|
var pdata;
|
|
|
|
var bucket_override = [];
|
|
|
|
if ( pdata = document.location.hash.match( /:(.*)$/ ) )
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// params delimited by &
|
|
|
|
var params = pdata[1].split( '&' );
|
|
|
|
for ( var param in params )
|
|
|
|
{
|
|
|
|
// values delimited from the name by =
|
|
|
|
var valdata = params[ param ].split( '=' ),
|
|
|
|
val = JSON.parse( valdata[ 1 ] );
|
|
|
|
|
|
|
|
bucket_override[ valdata[ 0 ] ] = val;
|
|
|
|
bucket[ valdata[ 0 ] ] = val;
|
|
|
|
|
|
|
|
console.log( 'Bucket override: ' + valdata[ 0 ] + '=' + val );
|
|
|
|
}
|
|
|
|
|
|
|
|
overrideBucket( bucket_override );
|
|
|
|
}
|
|
|
|
catch ( e )
|
|
|
|
{
|
|
|
|
alert( 'Failed setting param values from URL.\n\n' + e.message );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// allow loading of quote ids
|
|
|
|
var mdata;
|
|
|
|
if ( mdata = document.location.hash.match( /#load\/([a-z]+)\/([0-9]+)/ ) )
|
|
|
|
{
|
|
|
|
var host = mdata[1],
|
|
|
|
id = mdata[2];
|
|
|
|
|
|
|
|
// load the quote
|
|
|
|
Prior.loadQuote( id, host, bucket_override );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var vpt = [ 0, 0, 0 ],
|
|
|
|
vpt_cur = 0;
|
|
|
|
function voiPainterAdd( tr, value )
|
|
|
|
{
|
|
|
|
var c = 'sel' + vpt_cur;
|
|
|
|
tr.classList.toggle( c );
|
|
|
|
|
|
|
|
vpt[ vpt_cur ] += ( value * ( tr.classList.contains( c ) ? 1 : -1 ) );
|
|
|
|
vpt[ vpt_cur ] = +( vpt[ vpt_cur ].toFixed( 6 ) );
|
|
|
|
|
|
|
|
showVoiPainter( vpt[ vpt_cur ] );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var vp_element = null,
|
|
|
|
vpt_dest = [];
|
|
|
|
function showVoiPainter( val )
|
|
|
|
{
|
|
|
|
if ( !vp_element )
|
|
|
|
{
|
|
|
|
vp_element = document.createElement( 'div' );
|
|
|
|
vp_element.id = 'voi-painter';
|
|
|
|
|
|
|
|
for ( var i in vpt )
|
|
|
|
{
|
|
|
|
vpt_dest[ i ] = document.createElement( 'div' );
|
|
|
|
vpt_dest[ i ].classList.add( 'sel' + i );
|
|
|
|
vpt_dest[ i ].innerHTML = '0';
|
|
|
|
vp_element.appendChild( vpt_dest[ i ] );
|
|
|
|
|
|
|
|
( function( i )
|
|
|
|
{
|
|
|
|
vpt_dest[ i ].addEventListener( 'click', function()
|
|
|
|
{
|
|
|
|
vpt_cur = i;
|
|
|
|
} );
|
|
|
|
} )( i );
|
|
|
|
}
|
|
|
|
|
|
|
|
document.getElementById( 'test-data' ).appendChild( vp_element );
|
|
|
|
}
|
|
|
|
|
|
|
|
vpt_dest[ vpt_cur ] .innerHTML = val;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
updateSummaryDebug: updateSummaryDebug,
|
|
|
|
|
|
|
|
onRate: function( callback )
|
|
|
|
{
|
|
|
|
rate_callback = callback;
|
|
|
|
},
|
|
|
|
|
|
|
|
begin: begin,
|
|
|
|
Prior: Prior,
|
|
|
|
};
|
|
|
|
} )();
|