1 /** 2 * @fileOverview This file contains model for building searchclausesets used in user queries. 3 * Consists of two parts: 4 * - SearchClause model [index,relation, value], with basic functionality - 5 * creation, render, autocomplete. 6 * - Searchclauseset - container of actually used searchclauses in app 7 * with basic functionality for building container - add, remove, clear 8 * and conversion functions - conversions from-to querystring 9 * 10 * @author 11 * @version 12 */ 13 14 // client-side modelling of "business-objects": 15 // SearchClause, SearchClauseset, 16 17 18 //TODO use select-options or autocomplete ??? 19 //var relation_autocomplete = new Array('any','all','=','>','<'); 20 //var value_autocomplete = new Array(); 21 22 /** 23 * SearchClause model [index,relation, value], with basic functionality - 24 * creation, render, autocomplete. 25 * @constructor 26 */ 27 function SearchClause(index, relation, value ) { 28 this.index = index; 29 this.relation = relation; 30 this.value = value; 31 this.negation = false; 32 33 this.is_category = false; 34 this.category = ""; 35 36 this.i = 0; 37 this.j = 0; 38 this.listid = ""; 39 this.container = {}; 40 41 if (this.relation == "") { 42 this.relation = "="; 43 } 44 } 45 46 SearchClause.prototype.GetIndexInput = function(){ 47 return $($($('.sc-i','#searchclauselist')[this.i]).find('.sc-j')[this.j]).find('.index-input'); 48 }; 49 50 SearchClause.prototype.GetRelInput = function(){ 51 return $($($('.sc-i','#searchclauselist')[this.i]).find('.sc-j')[this.j]).find('.rel_input'); 52 }; 53 SearchClause.prototype.GetValueInput = function(){ 54 return $($($('.sc-i','#searchclauselist')[this.i]).find('.sc-j')[this.j]).find('.value-input'); 55 }; 56 57 SearchClause.prototype.GetIndexSelect = function(){ 58 return $($($('.sc-i','#searchclauselist')[this.i]).find('.sc-j')[this.j]).find('.index_select'); 59 }; 60 61 62 SearchClause.prototype.GetValueSelect = function(){ 63 return $($($('.sc-i','#searchclauselist')[this.i]).find('.sc-j')[this.j]).find('.value_select'); 64 }; 65 66 SearchClause.prototype.render = function (rel) { 67 68 //TODO defaults 69 //this.relation = '='; 70 //var index_detail = ""; 71 //if ((this.i == 0) && (this.j == 0)) { 72 // index_detail = "<span name='detail_index' class='cmd cmd_detail'></span>"; 73 //} 74 var x = "<div id='" + this.j + "' class='sc-j or_level' ><div id='" + this.listid + "' class='sc-wrapper focused' > " + 75 "<div class='search'><span class='index-search'><select class='index_select'/><input type='text' class='index-input autocomplete-input' /></span>" + 76 //index_detail + 77 //"<select type='text' class='rel_input' />" + 78 "<select class='rel_input'><option value='='>=</option><option value='>'>></option><option value='<'><</option><option value='any'>any</option><option value='contains'>contains</option><option value='all'>all</option></select>" + 79 "<select class='value_select'/><input type='text' class='value-input' /><span name='detail_value' class='cmd cmd_detail'></span>" + 80 "<span class='sc_cmds'><span class='cmd cmd_sc_delete'> </span><span class='sc_id'>" + this.listid + "</span>" + 81 "<span class='cmd cmd_add_and'> </span><span class='cmd cmd_add_or'></span>"+ 82 " </span></div><div class='index-context'><table></table></div></div></div>"; 83 84 addToSClist(x, this.i, this.j, rel); 85 86 87 var e_index = this.GetIndexInput(); 88 //new $.GrowingInput(e_index, {max: 80}); 89 var e_relation = this.GetRelInput(); 90 var e_value = this.GetValueInput(); 91 var index_select = this.GetIndexSelect(); 92 var value_select = this.GetValueSelect(); 93 94 $(index_select).hide(); 95 value_select.hide(); 96 //$(e_index).closest('.sc-wrapper').find('.index-context').hide(); 97 98 //alert("focused: " + $('.focused','#searchclauselist').size()); 99 $('.sc-wrapper','#searchclauselist').each(function(){ 100 $(this).removeClass("focused"); 101 }); 102 $(e_index).closest('.sc-wrapper').addClass("focused"); 103 //alert("focused: " + $('.focused','#searchclauselist').size()); 104 105 // set focus 106 e_index.focusin(function(){ 107 $('.sc-wrapper','#searchclauselist').each(function(){ 108 $(this).removeClass("focused"); 109 }); 110 $(this).closest('.sc-wrapper').addClass("focused"); 111 }); 112 index_select.focusin(function(){ 113 $('.sc-wrapper','#searchclauselist').each(function(){ 114 $(this).removeClass("focused"); 115 }); 116 $(this).closest('.sc-wrapper').addClass("focused"); 117 }); 118 e_relation.focusin(function(){ 119 $('.sc-wrapper','#searchclauselist').each(function(){ 120 $(this).removeClass("focused"); 121 }); 122 $(this).closest('.sc-wrapper').addClass("focused"); 123 }); 124 e_value.focusin(function(){ 125 $('.sc-wrapper','#searchclauselist').each(function(){ 126 $(this).removeClass("focused"); 127 }); 128 $(this).closest('.sc-wrapper').addClass("focused"); 129 }); 130 $($($('.sc-i','#searchclauselist')[this.i]).find('.sc-j')[this.j]).find('.cmd_detail').click(function(){ 131 $('.sc-wrapper','#searchclauselist').each(function(){ 132 $(this).removeClass("focused"); 133 }); 134 $(this).closest('.sc-wrapper').addClass("focused"); 135 }); 136 137 //TODO handle keydown 138 /* e_index.keydown(function(ev){ 139 var evStop = function(){ ev.stopPropagation(); ev.preventDefault(); }; 140 if (ev.which === 13) evStop(); 141 var evFocusNext = function(){ 142 ev.preventDefault(); 143 e_relation.focus(); 144 }; 145 //notifyUser($(this).attr("value").length); 146 if ($(this).attr("value").length > 0 && ev.which === 32) evFocusNext(); // focus next element 147 148 }); 149 150 e_relation.keydown(function(ev){ 151 var evStop = function(){ ev.stopPropagation(); ev.preventDefault(); }; 152 if (ev.which === 13) evStop(); 153 var evFocusNext = function(){ 154 e_value.focus(); 155 }; 156 if (ev.which === 32) evFocusNext(); // focus next element 157 notifyUser(ev.which); 158 }); 159 */ 160 161 //selects for the first use 162 index_select.change(function(){ 163 $(this).next().val($(this).find('option:selected').text()); 164 $(this).hide(); 165 $(this).next().show(); 166 }); 167 value_select.change(function(){ 168 $(this).next().val($(this).find('option:selected').text()); 169 $(this).hide(); 170 $(this).next().show(); 171 }); 172 173 e_index.val(this.index); 174 e_relation.val(this.relation); 175 e_value.val(this.value); 176 //update after text change 177 e_index.bind('change', function(){ 178 179 var i = $(this).closest('.sc-i').attr("id"); 180 var j = $(this).closest('.sc-j').attr("id"); 181 //alert("e-index-chabge;" + i + j + $(this).val()); 182 searchclauseset.searchclauses[i][j].index = $(this).val(); 183 notifyUser("index_sc" + i + "-" + j + " changed to:" + searchclauseset.searchclauses[i][j].index, "debug"); 184 searchclauseset.searchclauses[i][j].is_category = false; 185 }); 186 e_relation.change(function(){ 187 var i = $(this).closest('.sc-i').attr("id"); 188 var j = $(this).closest('.sc-j').attr("id"); 189 //notifyUser("rel change:" + $(this).val(), 'debug'); 190 searchclauseset.searchclauses[i][j].relation = $(this).val(); 191 192 searchclauseset.searchclauses[i][j].is_category = false; 193 }); 194 e_value.change(function(){ 195 var i = $(this).closest('.sc-i').attr("id"); 196 var j = $(this).closest('.sc-j').attr("id"); 197 searchclauseset.searchclauses[i][j].value = $(this).val(); 198 199 searchclauseset.searchclauses[i][j].is_category = false; 200 }); 201 202 try{ 203 this.initAutocomplete(); 204 } catch (e){ 205 notifyUser("autocomplete failed: " + e.message); 206 } 207 208 209 /* 210 e_value.autocompleteArray(value_autocomplete,{ 211 autoFill:true, 212 width:90 213 }); 214 */ 215 216 217 }; 218 219 SearchClause.prototype.initAutocomplete = function(){ 220 221 if (element_autocomplete.length == 0) return; 222 223 //autocomplete 224 function handleTermsSelection(i,j,elem){ 225 226 var context = $('#' + 'sc' + i + '-' + j).find('.index-context'); 227 // fill context 228 //$(context).remove(); 229 $(context).html(elements_hashtable[elem]); 230 231 /* 232 $(context).children('table').children().remove(); 233 for(var ii=0;ii< elements_hashtable[elem].length;ii++){ 234 var row = '<tr><td>' + elements_hashtable[elem][ii] + '</td></tr>'; 235 $(context).children('table').append(row); 236 } 237 */ 238 239 $(context).show(); 240 //notifyUser($('#' + 'sc' + i + '-' + j).find('.index-input').val(), 'debug'); 241 //$('#' + 'sc' + i + '-' + j).find('.index-input').focus(); 242 243 $('#' + 'sc' + i + '-' + j).find('.index-input').blur(function(){ 244 $(context).hide(); 245 }); 246 $('#' + 'sc' + i + '-' + j).find('.index-input').focusin(function(){ 247 $(context).hide(); 248 }); 249 250 251 /* 252 var t = '<div><table><tr><td><a href="">aaaa<a></td></tr><tr><td><a href="">bbbb<a></td></tr><table></div>'; 253 var x = '<div class="index_context"><table></table></div>'; 254 $('.focused','#searchclauselist').find('.index_search').append(x); 255 256 257 for(var i=0;i< elements_hashtable[elem].length;i++){ 258 var li = '<span><a href="">' + elements_hashtable[elem][i] + '</a></span>'; 259 $('.focused','#searchclauselist').find('.index_context').append(li); 260 }*/ 261 262 //$('.focused','#searchclauselist').find('.index-input').hide(); 263 264 }; 265 266 //autocomplete 267 function findValue(e) { 268 //if( li == null ) return alert("No match!"); 269 270 var sValue = e.selectValue; 271 272 var i = $('.focused','#searchclauselist').closest('.sc-i').attr("id"); 273 var j = $('.focused','#searchclauselist').closest('.sc-j').attr("id"); 274 //alert(i+ ',' + j + sValue); 275 searchclauseset.searchclauses[i][j].index = sValue; 276 277 handleTermsSelection(i,j,sValue); 278 279 //$('.focused','#searchclauselist').find('.value-input').focus(); 280 281 } 282 283 function selectItem(li) { 284 findValue(li); 285 } 286 287 $(this.GetIndexInput()).autocompleteArray(element_autocomplete,{ 288 autoFill:true, 289 //width:150, 290 onFindValue:findValue, 291 onItemSelect:selectItem 292 }); 293 294 $('body').find('.ac_results').css({'z-index' : '1000'}); 295 296 }; 297 298 SearchClause.prototype.CreateSelect = function(str, element){ 299 var index_select = GetIndexSelect(); 300 var index_input = GetIndexInput(); 301 index_select.show(); 302 var x = "<option value='" + str + "'>" + str + "</option>"; 303 $(element).each(function(){ 304 x = x+"<option value='" + $(this).text() + "'>" + $(this).text() + "</option>"; 305 }); 306 index_select.html(x); 307 308 index_input.hide(); 309 310 }; 311 312 SearchClause.prototype.PlainText = function(){ 313 if (this.index.trim().length == 0 || this.value.trim().length == 0){ 314 return ""; 315 } 316 if (this.is_category){ 317 return "ISOCAT( " + this.category + ") " + this.relation + " " + this.value; 318 } 319 return this.index.replace(" ","_") + " " + this.relation + " " + this.value; 320 }; 321 /* 322 Searchclause.prototype.submit = function () { 323 324 var uri = link('recordset','htmltable', this.query_uri()); 325 notifyUser("submitting query:" + uri); 326 327 this.container.find('.result').load( uri, function() { 328 notifyUser("result-loaded"); 329 var get = $(this).parent().find('.cmd_get'); 330 331 get.removeClass('cmd_get'); 332 get.addClass('cmd_up'); 333 // get.show(); 334 }); 335 336 } 337 */ 338 339 $('.index-context td').live('click', function(event) { 340 341 searchclauseset.updatedata(this.textContent, false, ""); 342 //$('.focused','#searchclauselist').find('.index-input').val( this.textContent); 343 $('.focused','#searchclauselist').find('.index-context').hide(); 344 345 $('.focused','#searchclauselist').find('.value-input').focus(); 346 347 }); 348 349 350 var searchclauseset_container = $("#searchclauselist"); 351 352 /** 353 * container of actually used Searchclauses in the query-input 354 * with basic functionality for building container - add, remove, clear 355 * and conversion functions - conversions from-to querystring 356 * @constructor 357 */ 358 359 var searchclauseset = { 360 /** @field */ 361 searchclauses: [], 362 container: '#searchclauselist', 363 sctext: '', 364 365 addsearchclause: function (searchclause, rel, _i, _j){ 366 var i,j; 367 368 if (this.searchclauses.length == 0){ 369 i = 0; 370 this.searchclauses[i] = new Array(); 371 j = 0; 372 } else { 373 if (rel == "or") { 374 i = _i;//this.searchclauses.length - 1; 375 j = this.searchclauses[i].length;//this.searchclauses[this.searchclauses.length - 1].length; 376 } else { 377 i = this.searchclauses.length; 378 this.searchclauses[i] = new Array(); 379 j = 0; 380 } 381 } 382 searchclause.i = i; 383 searchclause.j = j; 384 searchclause.listid = "sc" + i + "-" + j; 385 this.searchclauses[i][j] = searchclause; 386 387 searchclause.render(rel); 388 //$('#querylist').html(this.render()); 389 }, 390 391 fillFromURL: function(url){ 392 }, 393 394 clear: function() { 395 notifyUser("clear query",'debug'); 396 397 for (var i = this.searchclauses.length - 1; i > -1; i--){ 398 for (var j = this.searchclauses[i].length - 1; j > -1; j--){ 399 400 401 this.searchclauses[i].splice(j, 1); 402 if (j == 0){ 403 this.searchclauses.splice(i, 1); 404 $('#' + 'sc' + i + '-' + j).parent().parent().remove(); 405 $('#' + 'sc' + i + '-' + j).parent().remove(); 406 } else { 407 $('#' + 'sc' + i + '-' + j).parent().remove(); 408 } 409 410 411 } 412 } 413 414 notifyUser(this.searchclauses.length,'debug'); 415 416 // reset focused 417 //$('.sc-wrapper','#searchclauselist').each(function(){ 418 // $(this).removeClass("focused"); 419 //}); 420 //$('#sc0-0').addClass("focused"); 421 }, 422 423 //TODO remove only last items 424 removesearchclause: function (i, j) { 425 notifyUser("removing sc:" + i + "," + j,'debug'); 426 427 if ((j == 0) && (i == 0)){ 428 searchclauseset.searchclauses[i][j].index = ""; 429 searchclauseset.searchclauses[i][j].is_category = false; 430 searchclauseset.searchclauses[i][j].relation = "="; 431 searchclauseset.searchclauses[i][j].value = ""; 432 $('#sc0-0').find('.index-input').val(""); 433 $('#sc0-0').find('.rel_input').val("="); 434 $('#sc0-0').find('.value-input').val(""); 435 notifyUser("cannot remove", 'debug'); 436 return; 437 } 438 if ((j == 0) && (i < this.searchclauses.length - 1)){ 439 notifyUser("cannot remove",'debug'); 440 return; 441 } 442 if (j < this.searchclauses[i].length - 1){ 443 notifyUser("cannot remove",'debug'); 444 return; 445 } 446 this.searchclauses[i].splice(j, 1); 447 448 if (j == 0){ 449 this.searchclauses.splice(i, 1); 450 $('#' + 'sc' + i + '-' + j).parent().parent().remove(); 451 } else { 452 $('#' + 'sc' + i + '-' + j).parent().remove(); 453 } 454 455 notifyUser("sc removed, new sc.length:" + this.searchclauses.length,'debug'); 456 for (var j = 0; j < this.searchclauses.length; j++) { 457 notifyUser(" new sc.or_length:" + this.searchclauses[j].length,'debug'); 458 } 459 // reset focused 460 $('.sc-wrapper','#searchclauselist').each(function(){ 461 $(this).removeClass("focused"); 462 }); 463 $('#sc0-0').addClass("focused"); 464 465 }, 466 /* 467 // just test 468 load: function(i,j,rel,data) { 469 if (data["triple"] == null ) { 470 this.searchclauses[i][j].index = data["searchClause"]["index"]; 471 this.searchclauses[i][j].relation = data["searchClause"]["relation"]["value"]; 472 this.searchclauses[i][j].value = data["searchClause"]["term"]; 473 474 $('#sc0-0','#searchclauselist').find('.index-input').val(data["searchClause"]["index"]); 475 $('#sc0-0','#searchclauselist').find('.relation_input').val(data["searchClause"]["relation"]["value"]); 476 $('#sc0-0','#searchclauselist').find('.value-input').val(data["searchClause"]["term"]); 477 } else { 478 if (data["triple"]["boolean"]["value"] == "and") { 479 //var sc = new SearchClause("","",""); 480 //this.addsearchclause(sc, "and", _i, _j) 481 // update(leftO) 482 // update(rightO) 483 load(i,j,"",data["triple"]["leftOperand"]); 484 i = this.searchclauses.length; 485 load(i+1,j,"and",data["triple"]["rightOperand"]); 486 } else { 487 load(i,j,"",data["triple"]["leftOperand"]); 488 j = this.searchclauses[i].length; 489 load(i,j,"or",data["triple"]["rightOperand"]); 490 } 491 492 } 493 494 }, 495 */ 496 // from querystring to searchclauses 497 buildfromquerystring: function(){ 498 var arr = Query.simplequerystring(this.sctext); 499 var sc, screl; 500 501 this.clear(); 502 503 //notifyUser(arr,'debug'); 504 var arr_and = arr.split(" and "); 505 screl = ""; 506 for( var i=0;i<arr_and.length;i++){ 507 var scstring = $.trim(arr_and[i]); 508 509 if (scstring.substring(0,1) == "(" && scstring.substring(scstring.length-1) == ")") { 510 scstring = scstring.substring(1,scstring.length-1); 511 scstring = $.trim(scstring); 512 } 513 514 //notifyUser("i:" + scstring,'debug'); 515 var arr_or = scstring.split(" or "); 516 if (i > 0) {screl = "and";} 517 for( var j=0;j<arr_or.length;j++){ 518 var scstr = $.trim(arr_or[j]); 519 520 // parse rel 521 //notifyUser("j:"+scstr,'debug'); 522 var n; 523 var scarr; 524 var rel; 525 n = scstr.indexOf("="); 526 if (n > 0){ 527 scarr = scstr.split("="); 528 rel = "="; 529 } 530 n = scstr.indexOf(" any "); 531 if (n > 0){ 532 scarr = scstr.split(" any "); 533 rel = "any"; 534 } 535 n = scstr.indexOf(" contains "); 536 if (n > 0){ 537 scarr = scstr.split(" contains "); 538 rel = "contains"; 539 } 540 n = scstr.indexOf("<"); 541 if (n > 0){ 542 scarr = scstr.split("<"); 543 rel = "<"; 544 } 545 n = scstr.indexOf(">"); 546 if (n > 0){ 547 scarr = scstr.split(">"); 548 rel = ">"; 549 } 550 n = scstr.indexOf(" all "); 551 if (n > 0){ 552 scarr = scstr.split(" all "); 553 rel = "all"; 554 } 555 //var simplecalusetext = scstr.split(""); 556 if (j > 0) {screl = "or";} 557 //notifyUser("scarr:" + scarr[0] + scarr[1],'debug'); 558 if (scarr == undefined) { 559 sc = new SearchClause('','',''); 560 } else { 561 sc = new SearchClause(scarr[0],rel,scarr[1]); 562 } 563 564 this.addsearchclause(sc,screl,i,j); 565 //searchclauseset.addsearchclause(sc,screl,i,j); 566 } 567 } 568 }, 569 570 // from searchclauses to querystring 571 buildsctext: function(){ 572 var uncompletequery = false; 573 var ptext = ""; 574 575 if ($('.searchtype_text','#search').size() > 0) { 576 this.sctext = $('#query_area').val(); 577 } else { 578 579 this.sctext = ""; 580 for (var i = 0; i < this.searchclauses.length; i++) { 581 if ( i>0) this.sctext = this.sctext + " and "; 582 this.sctext = this.sctext + " ( "; 583 for (var j = 0; j < this.searchclauses[i].length; j++) { 584 if ( j>0) this.sctext = this.sctext + " or "; 585 this.sctext = this.sctext + " ( "; 586 ptext = this.searchclauses[i][j].PlainText(); 587 if (ptext.length == 0){ 588 uncompletequery = true; 589 } 590 this.sctext = this.sctext + ptext; 591 this.sctext = this.sctext + " ) "; 592 } 593 this.sctext = this.sctext + " ) "; 594 } 595 596 } 597 if (uncompletequery){ 598 this.sctext = ""; 599 } 600 notifyUser(this.sctext); 601 }, 602 603 updatedata: function(term, is_category, category, value) { 604 605 var i = $('.focused','#searchclauselist').closest('.sc-i').attr("id"); 606 var j = $('.focused','#searchclauselist').closest('.sc-j').attr("id"); 607 608 if (term != undefined){ 609 $('.focused','#searchclauselist').find('.index-input').val(term); 610 searchclauseset.searchclauses[i][j].index = term.replace(" ","_"); 611 } 612 613 if (value == undefined){ 614 $('.focused','#searchclauselist').find('.value-input').focus(); 615 } else { 616 $('.focused','#searchclauselist').find('.value-input').val(value); 617 searchclauseset.searchclauses[i][j].value = value; 618 } 619 620 if (is_category) { 621 searchclauseset.searchclauses[i][j].is_category = true; 622 searchclauseset.searchclauses[i][j].category = category; 623 } else { 624 searchclauseset.searchclauses[i][j].is_category = false; 625 } 626 627 }, 628 629 initAutocomplete: function() { 630 notifyUser("init autocomplete",'debug'); 631 632 for (var i = this.searchclauses.length - 1; i > -1; i--){ 633 for (var j = this.searchclauses[i].length - 1; j > -1; j--){ 634 this.searchclauses[i][j].initAutocomplete(); 635 } 636 } 637 } 638 }; 639 640