1 //require <xataface/Permissions.js> 2 3 /*------------------------------------------------------------------------------- 4 * Xataface Web Application Framework 5 * Copyright (C) 2005-2009 Web Lite Solutions Corp (shannah@sfu.ca) 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 2 10 * of the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 *------------------------------------------------------------------------------- 21 */ 22 /** 23 * 24 * This jquery plugin allows you to convert any HTML element or field into 25 * a record browser. Clicking the browser button will open a modal dialog 26 * that allows the user to search and browse through a number of records 27 * in a table. 28 * 29 * Example usage: 30 * 31 * <a href="#" id="selector">Click me to find stuff</a> 32 * ... 33 * 34 * $('#selector').RecordBrowser({ 35 * table: 'people', 36 * filters: { 37 * group_id: 10 38 * }, 39 * callback: function(values){ 40 * // values is an object with the records that the user 41 * // selected 42 * for ( var id in values ){ 43 * // id is the id of the record 44 * // values[id] is the title of the record 45 * } 46 * } 47 * }); 48 * 49 * Alternatively you could use the RecordBrowserWidget function to convert a text field 50 * into a RecordBrowser: 51 * 52 * <input type="text" id="textfield"/> 53 * ... 54 * $('#textfield').RecordBrowserWidget({ 55 * table: 'people', 56 * filters: { 57 * group_id: 10 58 * }, 59 * callback: function(values){ 60 * // values is an object with the records that the user 61 * // selected 62 * for ( var id in values ){ 63 * // id is the id of the record 64 * // values[id] is the title of the record 65 * } 66 * } 67 * }); 68 * 69 */ 70 (function ($){ 71 72 if ( typeof(console) == 'undefined' ) console = {'log': function(){}}; 73 if ( typeof(console.log) == 'undefined' ) console.log = function(){}; 74 75 var xataface = XataJax.load('xataface'); 76 77 78 /** 79 * @name Callback 80 * @memberOf xataface.RecordBrowser 81 * @function 82 * @description A callback function that can registered as the callback for a RecordBrowser 83 * object to handle the case where a user has selected one or more records in a RecordBrowser 84 * dialog. 85 * 86 * @param {Object} o The key-value pairs of selected records. The keys are the "values" of the 87 * select list, and the values are the corresponding "texts" of the select list. 88 * 89 * @returns {void} 90 * 91 * @example 92 * //require <RecordBrowser/RecordBrowser.js> 93 * var RecordBrowser = XataJax.load('xataface.RecordBrowser'); 94 * 95 * // Now let's create a dialog to select records from the people table: 96 * var dlg = new RecordBrowser({ 97 * table: 'people', 98 * 99 * // A callback function to be called when the clicks "Select" 100 * callback: function(selected){ 101 * var selectedString = []; 102 * $(selected).each(function(id,title){ 103 * selectedString.push(id+' = '+title); 104 * }); 105 * selectedString = selectedString.join('\n'); 106 * alert("The following key/value pairs were selected: '+selectedString); 107 * } 108 * }); 109 * dlg.display(); // Display the dialog 110 * 111 */ 112 113 114 /** 115 * @name RecordBrowser 116 * @memberOf xataface 117 * @class 118 * @description A dialog to select a record from a found set in a table. This dialog 119 * is used in may parts of Xataface including the lookup and select widgets. 120 * 121 * <p>Screenshot of the RecordBrowser dialog as used inside the lookup widget:</p> 122 * <p><img src="/assets/images/photos/Picture%2023.png"/></p> 123 * 124 * @param {Object} o Initialization parameters. 125 * @param {String} o.table The name of the table from which to browse records. 126 * @param {String} o.value The name of the column to use as the value in the option list. If this 127 * is left null, then the primary key will be used as the value. 128 * @param {String} o.text The name of the column to use as the title in the option list. If this 129 * is left null, then the record's title (i.e. the result of $record->getTitle()) will be used. 130 * @param {Object} o.filters Optional key-value pairs to be used to filter the results. This is 131 * handy if you only want the dialog to show certain records from the table. 132 * @param {xataface.RecordBrowser.Callback} o.callback A callback function that will be called after 133 * the user clicks "select" to close the dialog. An object with the key/value pairs of selected 134 * records will be passed to this callback function. 135 * 136 * 137 * @example 138 * <h2>Basic Example</h2> 139 * //require <RecordBrowser/RecordBrowser.js> 140 * var RecordBrowser = XataJax.load('xataface.RecordBrowser'); 141 * 142 * // Now let's create a dialog to select records from the people table: 143 * var dlg = new RecordBrowser({ 144 * table: 'people', 145 * 146 * // A callback function to be called when the clicks "Select" 147 * callback: function(selected){ 148 * var selectedString = []; 149 * $(selected).each(function(id,title){ 150 * selectedString.push(id+' = '+title); 151 * }); 152 * selectedString = selectedString.join('\n'); 153 * alert("The following key/value pairs were selected: '+selectedString); 154 * } 155 * }); 156 * dlg.display(); // Display the dialog 157 * 158 * @example 159 * <h2>Example with Filters</h2> 160 * <p>The following example only includes a list of people in Canada over the age of 18</p> 161 * 162 * var dlg = new RecordBrowser({ 163 * table: 'people', 164 * filters: { 165 * country: '=Canada', 166 * age: '>18' 167 * }, 168 * callback: function(selected){ 169 * // ... do stuff with selected values 170 * } 171 * }); 172 * dlg.display(); 173 * 174 */ 175 xataface.RecordBrowser = function(o){ 176 /** 177 * @name title 178 * @memberOf xataface.RecordBrowser# 179 * @description The name of the table to browse for records in. 180 * @var string 181 */ 182 this.table = null; 183 184 /** 185 * @name value 186 * @memberOf xataface.RecordBrowser# 187 * @description 188 * The name of the column to use as the value in the option list. 189 * Set this value to __id__ to use the record id. 190 * If this value is blank, then the primary key is used so long as 191 * the primary key only has a single column. If it is a compound 192 * primary key, then the record id is used by default. 193 * @var string 194 */ 195 this.value = null; 196 197 /** 198 * @name text 199 * @memberOf xataface.RecordBrowser# 200 * @description 201 * The name of the column to use as the title in the option list. 202 * Set this value to __title__ to use the record title ( or leave blank). 203 * 204 * @var string 205 */ 206 this.text = null; 207 208 /** 209 * @name filters 210 * @memberOf xataface.RecordBrowser# 211 * @description 212 * Search filters to add to the query. 213 * @var object 214 */ 215 this.filters = {}; 216 217 /** 218 * @name callback 219 * @memberOf xataface.RecordBrowser# 220 * @description 221 * Callback function to be called with the selected values. 222 * 223 * function(values){} 224 * @var function 225 */ 226 this.callback = null; 227 228 /** 229 * @name el 230 * @memberOf xataface.RecordBrowser# 231 * @description The document element that is used to display the dialog. 232 * @var HTMLDOMElement 233 */ 234 this.el = document.createElement('div'); 235 236 /** 237 * @name baseURL 238 * @memberOf xataface.RecordBrowser# 239 * @description 240 * The base url to the RecordBrowser directory. 241 * @var string 242 */ 243 this.baseURL = DATAFACE_URL+'/js/RecordBrowser'; 244 245 for ( var i in o ){ 246 this[i] = o[i]; 247 } 248 249 /** 250 * A flag to indicate whether the record select list 251 * needs to be updated when updateRecords() is called. 252 * The list would need to be updated if the filter parameters change. 253 * @var boolean 254 */ 255 this.dirty = true; 256 //$('head').append('<link rel="stylesheet" type="text/css" href="'+DATAFACE_URL+'/css/smoothness/jquery-ui-1.7.2.custom.css"/>'); 257 258 259 260 261 } 262 263 xataface.RecordBrowser.prototype = { 264 265 /** 266 * @name display 267 * @memberOf xataface.RecordBrowser# 268 * @function 269 * @description Displays the record browser dialog. 270 * @returns {void} 271 */ 272 display : function(){ 273 var rb = this; 274 $('body').append(this.el); 275 $(this.el).load(this.baseURL+'/templates/RecordBrowser.html', function(){ 276 var dialog = this; 277 var searchChangeHandler = function(){ 278 rb.filterRecords({ 279 '-search' : $(this).val() 280 }); 281 }; 282 $(this).find('.xf-RecordBrowser-search-field') 283 .keyup(searchChangeHandler) 284 .change(searchChangeHandler); 285 //.blur(searchChangeHandler); 286 //$(this).find('.xf-RecordBrowser-select').css('height', '90%'); 287 $(this).find('.xf-RecordBrowser-select-field') 288 .css('width', '100%') 289 .attr('size', 8); 290 291 $(this).find('.xf-RecordBrowser-addnew-button').RecordDialog({ 292 table: rb.table, 293 callback: function(){ 294 rb.dirty=true; 295 rb.updateRecords(); 296 } 297 }); 298 299 $(this).dialog({ 300 'title': 'Select Record', 301 'buttons' : { 302 'Select' : function(){ 303 var out = {}; 304 $(dialog).find('.xf-RecordBrowser-select-field :selected').each(function(i, selected){ 305 out[$(selected).attr('value')] = $(selected).text(); 306 }); 307 308 if ( rb.callback ) rb.callback(out); 309 $(this).dialog("close"); 310 311 }, 312 'Cancel' : function(){ 313 $(this).dialog("close"); 314 315 } 316 317 }, 318 'position': 'center', 319 'modal' : true, 320 'resize': function(event, ui){ 321 $(dialog).find('.xf-RecordBrowser-select-field').css('height', ($(dialog).height()-60)+'px'); 322 323 } 324 }); 325 326 rb.updateRecords(); 327 }); 328 }, 329 330 filterRecords : function(filter){ 331 332 for ( var i in filter ){ 333 if ( this.filters[i] != filter[i] ) this.dirty = true; 334 this.filters[i] = filter[i]; 335 } 336 this.updateRecords(); 337 }, 338 339 updateRecords : function(){ 340 341 if ( this.dirty ){ 342 var sel = $(this.el).find('.xf-RecordBrowser-select-field'); 343 var val = $(sel).val(); 344 //var el = $(this.el); 345 sel.load(this.getDataURL(), function(){ 346 sel.val(val); 347 }); 348 this.dirty = false; 349 } 350 }, 351 352 getDataURL : function(){ 353 var url = DATAFACE_SITE_HREF+'?-action=RecordBrowser_data&-table='+encodeURIComponent(this.table); 354 if ( this.value ) url += '&-value='+encodeURIComponent(this.value); 355 if ( this.text ) url += '&-text='+encodeURIComponent(this.text); 356 for ( var i in this.filters ){ 357 url += '&'+encodeURIComponent(i)+'='+encodeURIComponent(this.filters[i]); 358 } 359 return url; 360 } 361 362 }; 363 364 $.fn.RecordBrowser = function(options){ 365 366 return this.each(function(){ 367 var obj = $(this); 368 obj.click(function(){ 369 if ( typeof(options.click) == 'function' ){ 370 options.click(); 371 } 372 var rb = new xataface.RecordBrowser(options); 373 rb.display(); 374 }); 375 }); 376 }; 377 378 $.fn.RecordBrowserWidget = function(options){ 379 return this.each(function(){ 380 381 var obj = $(this); 382 var editable = options.editable || false; 383 384 if ( obj.hasClass("xf-RecordBrowserWidget") ){ 385 // This field is already a record browser with different 386 // settings. We need to change it. So we remove the old 387 // display field. 388 var oldDisplayField = obj.next(); 389 var oldButton = oldDisplayField.next(); 390 oldDisplayField.remove(); 391 oldButton.remove(); 392 393 obj.removeClass('xf-RecordBrowserWidget'); 394 } 395 396 397 var displayField = document.createElement('input'); 398 $(displayField).attr('type','text') 399 .addClass('xf-RecordBrowserWidget-displayField') 400 //.css('width', obj.width()+'px') 401 //.css('height', obj.height()+'px') 402 .css('cursor', 'pointer') 403 //.css('border', '1px solid black') 404 .attr('readonly', 1); 405 406 407 $(displayField).insertAfter(this); 408 409 obj.css('display','none') 410 .addClass('xf-RecordBrowserWidget'); 411 412 if ( !options.frozen ){ 413 obj.change(function(){ 414 var id; 415 if ( options.value && options.value != '__id__' ){ 416 id = encodeURIComponent(options.value)+'='+encodeURIComponent(obj.val()); 417 } else { 418 id = obj.val(); 419 } 420 var url = DATAFACE_SITE_HREF+'?-action=RecordBrowser_lookup_single&-table='+options.table+'&-id='+encodeURIComponent(id); 421 if ( options.text ) url += '&-text='+encodeURIComponent(options.text); 422 $.get(url, function(text){ 423 $(displayField).val(text); 424 }); 425 426 updatePermissions(); 427 428 429 430 }); 431 432 /** 433 * Internal function to load the permissions for the currently selected record and then update 434 * whether the record can be edited or not. 435 */ 436 function updatePermissions(){ 437 try { 438 // Now we check the edit permission to find out if we need to show or hide the edit link 439 // for the field. 440 var theq = { 441 442 '-table': options.table 443 444 }; 445 446 if ( options.value && options.value != '__id__' ){ 447 theq[options.value] = obj.val(); 448 } else { 449 theq['--id'] = obj.val(); 450 } 451 452 var perms = new xataface.Permissions({ 453 query: theq 454 }); 455 //alert('here'); 456 perms.ready(function(){ 457 //alert('there'); 458 if ( perms.checkPermission('edit') ){ 459 editable = true; 460 } else { 461 editable = false; 462 } 463 updateEditable(); 464 465 }); 466 } catch (e){ 467 console.log('Looks like xataface.Permissions is not loaded while handling RecordBrowser change event.'); 468 console.log(e); 469 } 470 471 } 472 473 474 475 var a = document.createElement('a'); 476 $(a).addClass('xf-RecordBrowser-button') 477 .css('cursor', 'pointer') 478 .html('<img src="'+DATAFACE_URL+'/images/search_icon.gif" border="0" /><span class="xf-RecordBrowser-button-label"> Lookup</span>'); 479 $(a).find('.xf-RecordBrowser-button-label') 480 .css('display', 'none'); 481 482 483 484 485 486 487 488 $(a).insertAfter(displayField); 489 490 491 492 493 // If we want to allow editing, we add an edit button after the field that opens a record dialog 494 // for editing. 495 var editButton = $('<a>') 496 .addClass('xf-RecordBrowser-edit-button') 497 .html('<img src="'+DATAFACE_URL+'/images/edit.gif" border="0" /><span class="xf-RecordBrowser-button-label">Edit</span>') 498 .css({cursor: 'hand'}) 499 ; 500 501 $(editButton).find('.xf-RecordBrowser-button-label') 502 .css('display', 'none'); 503 504 505 $(editButton).click(function(){ 506 507 if ( !editable ){ 508 alert('This record is not currently editable.'); 509 } 510 var id = obj.val(); 511 if ( !id ){ 512 alert('No record is currently selected.'); 513 return; 514 } 515 516 var keyColName = '__id__'; 517 if ( options.value ){ 518 keyColName = options.value; 519 } 520 var recordid = encodeURIComponent(options.table)+'?'+encodeURIComponent(keyColName)+'='+encodeURIComponent(id); 521 var dlg = new xataface.RecordDialog({ 522 recordid: recordid, 523 table: options.table 524 }); 525 dlg.display(); 526 }); 527 528 $(editButton).insertAfter(a); 529 $(editButton).hide(); 530 531 function updateEditable(){ 532 if ( editable ) $(editButton).show(); 533 else $(editButton).hide(); 534 } 535 536 var origCallback = function(){}; 537 538 if ( typeof(options.callback == 'function' ) ){ 539 origCallback = options.callback; 540 } 541 options.callback = function(vals){ 542 for ( var i in vals ){ 543 //$(displayField).val(vals[i]); 544 obj.val(i); 545 obj.trigger('change'); 546 } 547 origCallback(vals, obj); 548 }; 549 550 $(a).RecordBrowser(options); 551 $(displayField).RecordBrowser(options); 552 } else { 553 //alert(obj.val()); 554 } 555 556 if ( obj.val() ){ 557 var id; 558 if ( options.value && options.value != '__id__' ){ 559 id = encodeURIComponent(options.value)+'='+encodeURIComponent(obj.val()); 560 } else { 561 id = obj.val(); 562 } 563 var url = DATAFACE_SITE_HREF+'?-action=RecordBrowser_lookup_single&-table='+options.table+'&-id='+encodeURIComponent(id); 564 if ( options.text ) url += '&-text='+encodeURIComponent(options.text); 565 $.get(url, function(text){ 566 //alert(text); 567 $(displayField).val(text); 568 }); 569 updatePermissions(); 570 } 571 572 573 }); 574 }; 575 })(jQuery);