//| library for 'smart' single-box search (at Comic Relief)
//|------------------------------------------------------------------------------
//| Copyright 2007 by Mark Anderson for Shoantel Limited (mark@shoantel.com)
//| Site's catalogue content is copyright site owner, templates and support
//| JS/NP files are copyright Shoantel (except where attributed) and may not be
//| copied or re-used without prior permission of Shoantel
//|------------------------------------------------------------------------------
//| Language               : JavaScript
//| Author                 : Mark Anderson (mark@shoantel.com)
//|------------------------------------------------------------------------------
//| Description:
//|   Defines utility data variables/functions for Portfolio NetPublish site.
//|
//| History:
//|   04 Sep 2007          : 1.0 (Release).
//|------------------------------------------------------------------------------
//|


//-------------------------------------------
// sub-function for checkSearch()
// sets up multi-dimension arrays
//-------------------------------------------
function multiDimensionalArray(iRows,iCols) {
  var i;
  var j;
  var a = [];
  for (i=0; i < iRows; i=i+1)
  {
    a[i] = [];
    for (j=0; j < iCols; j=j+1) {
      a[i][j] = ''; //initialise as empty strings (no 'undefined' values)
    }
  }
  return(a);
}

//-------------------------------------------
// sub-function for checkSearch()
// returns word version of number 1- 10
//-------------------------------------------
function makeText(numParams){
  var theText = '';
  switch(numParams) {
    case 1:
      theText = 'ONE';
      break;
    case 2:
      theText = 'TWO';
      break;
    case 3:
      theText = 'THREE';
      break;
    case 4:
      theText = 'FOUR';
      break;
    case 5:
      theText = 'FIVE';
      break;
    case 6:
      theText = 'SIX';
      break;
    case 7:
      theText = 'SEVEN';
      break;
    case 8:
      theText = 'EIGHT';
      break;
    case 9:
      theText = 'NINE';
      break;
    case 10:
      theText = 'TEN';
  }
  return theText
}

//-------------------------------------------
// data for checkSearch()
// sets up global var to hold a 'screen-readable' 
// version of parsed search string
//-------------------------------------------
var screenStr = '';

//------------------------------------------------------------------
// Purpose: parses use input keyword list to search values
// Used by: search, refine
//------------------------------------------------------------------
// sourceStr - the text typed into the on-screen input box (String)
// screenStr - stub text for pre-pending to screen-readable output of the input string for on-screen use (String)
// genericJoin - use 'AND' or 'OR', use either case as input is tested coverted to uppercase (String)
// opTypeNum - type of search , set up in arrOp, values 0 (contains), 1 (matches) or 2 (starts with) (Number)
// genericField - field against which to search (String - capitalise field name as per client UI)
// numParams - the number of parameters sets the user may allow must be between 1 - 10 (Number)
// noPhrases = allow "-delimited multi-word phrase values (Boolean)
// methodGet = are we supplying GET or POST form data? GET = true, POST = False (Boolean)
//
function checkSearch(sourceStr,screenStr,genericJoin,opTypeNum,genericField,numParams,noPhrases,methodGet){
  /*if (sourceStr === ''){ // if the input is empty, bail out straight away
    var theMsg1 = 'You must provide one or more search values.\n\nYour search has not been submitted - please check your input and try again.';
    alert(theMsg1);
    return false;
  }*/ //We now check this before calling
  //alert('shoantelSearch called with value:\n\n'+sourceStr);
  // set up join and operator values
  genericJoin = genericJoin.toUpperCase();
  var genericJoinAlt ='';
  if (genericJoin === 'AND') {
    genericJoinAlt = 'OR';
  } else {
    genericJoin = 'OR'; // if not set via input default value is 'or'
    genericJoinAlt = 'AND';
  }
  opTypeNum = opTypeNum ? opTypeNum : 0; // use a 'contains' match if no value set
  var arrOp = [['contains','does not contain'],
              ['matches','does not match'],
              ['starts with','does not start with']];
  opTypeNum = opTypeNum > 2 ? 0 : opTypeNum; // use a 'contains' op if value set is > 2
  var genericOp = arrOp[opTypeNum][0];
  var genericOpAlt = arrOp[opTypeNum][1];
  genericField = genericField ? genericField : 'Keywords'; // use 'Keywords' a search field if no value set
  if (numParams > 10) {numParams = 10;} // don't allow >10 parameter sets.
  var theMatches = sourceStr.match(/"/g); // try to match a double quote
  if (theMatches !== null) {
    var theMsg2 = '';
    if (noPhrases === true) { // want and quit
      theMsg2 = 'This search does not support double-quote delimited phrases. Please delete all double-quotes ';
      theMsg2 += 'before trying the search again';
      alert(theMsg2);
      return false;
    } else {
      var numQuotes = theMatches.length;
      if (numQuotes > 0 && (numQuotes % 2) !== 0) { // warn and quit
        theMsg2 = 'Found ' + numQuotes + ' double quotes. This not an even number of double quotes.\n\n';
        theMsg2 += 'Please ensure there are even numbers of double quotes in your search terms. Double quotes are needed only';
        theMsg2 += 'if you wish to imply two or more words are to be treated as a single search term, i.e. use the term ';
        theMsg2 += '"Fred Smith" if you want only matches to \'Fred Smith\' and not \'Fred\' and/or \'Smith\' as separate terms.\n\n';
        theMsg2 += 'Your search has not been submitted - please check your input and try again.';
        alert(theMsg2);
        return false;
      }
    }
  }
  var arr;
  var noQuotes=/("(\\"|[^"])*")|("(\\"|[^"])*$)|[^ \f\n\r\t "]+/g;
  arr=sourceStr.match(noQuotes);
  for(var i=0;i<arr.length;i=i+1){
    arr[i] = arr[i].replace(/[,]+/g, ''); //strip all commas
    if (arr[i].toLowerCase() === 'not') {arr[i] = 'NOT';} // allows for not/Not/NOT etc...
    if (arr[i] === '-') {arr[i] = 'NOT';} // coerce minus on its own to NOT
    if (arr[i].toLowerCase() === 'and') {arr[i] = 'AND';}
    if (arr[i] === '+') {arr[i] = 'AND';} // coerce plus on its own to AND
    if (arr[i].toLowerCase() === 'or') {arr[i] = 'OR';}
    if (arr[i].slice(0,1) !== '"') { // this is a single word not a phrase
      if (arr[i].slice(0,1) === '-'){
        arr[i] = 'NOT ' + arr[i].slice(1);
      }
      if (arr[i].slice(0,1) === '+') {
        arr[i] = 'AND ' + arr[i].slice(1);
      }
    }
  }
  sourceStr = arr.join(' ');
  sourceStr = sourceStr.replace(/[\ ]+/g, ' ');
  var blnChecks;
  do {
    blnChecks = false;
    if (sourceStr.indexOf('NOT NOT') !== -1) {
      sourceStr = sourceStr.replace(/NOT NOT/g, 'NOT');
      blnChecks = true;
    } else if (sourceStr.indexOf('AND AND') !== -1) {
      sourceStr = sourceStr.replace(/(AND AND)/g, 'AND');
      blnChecks = true;
    } else if (sourceStr.indexOf('OR OR') !== -1) {
      sourceStr = sourceStr.replace(/(OR OR)/g, 'OR');
      blnChecks = true;
    } else if (sourceStr.indexOf('AND OR') !== -1) {
      sourceStr = sourceStr.replace(/(AND OR)/g, 'OR');
      blnChecks = true;
    } else if (sourceStr.indexOf('OR AND') !== -1) {
      sourceStr = sourceStr.replace(/(OR AND)/g, 'AND');
      blnChecks = true;
    } else if (sourceStr.indexOf('NOT OR') !== -1) {
      sourceStr = sourceStr.replace(/(NOT OR)/g, 'OR NOT');
      blnChecks = true;
    } else if (sourceStr.indexOf('NOT AND') !== -1) {
      sourceStr = sourceStr.replace(/(NOT AND)/g, 'AND NOT');
      blnChecks = true;
    }
  } while (blnChecks);
  while ((sourceStr.slice(-2)=== 'OR')|| (sourceStr.slice(-3)=== 'NOT')||(sourceStr.slice(-3)=== 'AND')) {
    if (sourceStr.slice(-2)=== 'OR') {sourceStr = sourceStr.slice(0,-2);}
    if (sourceStr.slice(-3)=== 'NOT') {sourceStr = sourceStr.slice(0,-3);}
    if (sourceStr.slice(-3)=== 'AND') {sourceStr = sourceStr.slice(0,-3);}
  }
  while ((sourceStr.slice(0,2)=== 'OR')||(sourceStr.slice(0,3)=== 'AND')) {
    if (sourceStr.slice(0,2)=== 'OR') {sourceStr = sourceStr.slice(2);}
    if (sourceStr.slice(0,3)=== 'AND') {sourceStr = sourceStr.slice(3);}
  }
  arr = sourceStr.split(' ');
  arr=sourceStr.match(noQuotes);
  if (!arr) {
    alert('Only invalid search values detected \(e.g. \'and\',\'or\',\'not\',\'+\',\'-\'\). Please review your input for ' + genericField + ' and try again.');
    return false; //quit this routine
  }
  var valueArr = multiDimensionalArray(arr.length,2);
  var n=0;
  for (var m=0; m < arr.length; m=m+1) {
    var nextJoin = genericJoin;
    if (arr[m] === 'NOT') {valueArr[n][1] = arr[m];}
    valueArr[n][2] = genericJoin;
    if ((arr[m] === genericJoinAlt)&& (n>0)) {
      valueArr[n-1][2] = genericJoinAlt;
    }
    if ((arr[m]!== 'OR') && (arr[m] !== 'AND') && (arr[m] !== 'NOT')) {
      if ((arr[m].substr(0,1) === '"') && (arr[m].substr(-1,1) === '"')) {
        valueArr[n][0] = arr[m].substr(1,arr[m].length-2);
      } else {
        valueArr[n][0] = arr[m];
      }
      n = n + 1;
    }
  }
  for (i=0;i<valueArr.length;i=i+1){
    if (valueArr [i][0] === '') {
      valueArr.length = valueArr.length - 1;
      i = i-1;
    }
  }
// Warn if max allowed values exceeded
  var numParamsStr = makeText(numParams);
  var valueType = noPhrases ? ' keywords ' : ' keywords/phrases ';
  if (valueArr.length > numParams){
    alert('WARNING: only the first ' + numParamsStr + valueType + 'listed will be submitted for search.');
  }
  var counterNum = valueArr.length < numParams ? valueArr.length : numParams ;
  var exportArr = multiDimensionalArray(counterNum,3);
  for (i=0;i<counterNum;i=i+1) {
    if (valueArr[i][0]) {
      if (methodGet) { // if GET method spaces must become pluses, e.g. starts+with
        exportArr[i][0] = genericField.replace(/\ /g,'+');
        exportArr[i][2] = valueArr[i][0].replace(/\ /g,'+');
        exportArr[i][1] = (valueArr[i][1] == 'NOT')? genericOpAlt.replace(/\ /g,'+') : genericOp.replace(/\ /g,'+') ;
      } else {
        exportArr[i][0] = genericField;
        exportArr[i][2] = valueArr[i][0];
        exportArr[i][1] = (valueArr[i][1] == 'NOT')? genericOpAlt : genericOp ;
      }
      if (i<(counterNum-1)) {
        exportArr[i][3] = valueArr[i][2].toLowerCase();
      }
    }
  }
  for (i=0;i<counterNum;i=i+1) {
    if (valueArr[i][0]) {
      screenStr += valueArr[i][1]? ' ' + valueArr[i][1]: '';
      screenStr += ' ' + valueArr[i][0];
      if (i<(counterNum-1)) {
        screenStr += ' ' + valueArr[i][2];
      }
    }
  }
  return exportArr;
}


