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  * This jquery plugin allows you to convert any HTML element or field into
 24  * a record browser.  Clicking the browser button will open a modal dialog
 25  * that allows the user to search and browse through a number of records
 26  * in a table.
 27  *
 28  * Example usage: 
 29  *
 30  * <a href="#" id="selector">Click me to find stuff</a>
 31  * ...
 32  *
 33  * $('#selector').RecordBrowser({
 34  *		table: 'people',
 35  *		filters: {
 36  *			group_id: 10
 37  *		},
 38  *		callback: function(values){
 39  *			// values is an object with the records that the user 
 40  *			// selected
 41  *			for ( var id in values ){
 42  *				// id is the id of the record
 43  *				// values[id] is the title of the record
 44  *			}
 45  *		}
 46  *	});
 47  *
 48  * Alternatively you could use the RecordBrowserWidget function to convert a text field
 49  * into a RecordBrowser:
 50  *
 51  * <input type="text" id="textfield"/>
 52  * ...
 53  * $('#textfield').RecordBrowserWidget({
 54  *		table: 'people',
 55  *		filters: {
 56  *			group_id: 10
 57  *		},
 58  *		callback: function(values){
 59  *			// values is an object with the records that the user 
 60  *			// selected
 61  *			for ( var id in values ){
 62  *				// id is the id of the record
 63  *				// values[id] is the title of the record
 64  *			}
 65  *		}
 66  *	});
 67  * 
 68  */
 69 (function ($){
 70 	
 71 	if ( typeof(console) == 'undefined' ) console = {'log': function(){}};
 72 	if ( typeof(console.log) == 'undefined' ) console.log = function(){};
 73 	
 74 	var xataface = XataJax.load('xataface');
 75 	xataface.RecordBrowser = function(o){
 76 		/**
 77 		 * The name of the table to browse for records in.
 78 		 * @var string
 79 		 */
 80 		this.table = null;
 81 		
 82 		/**
 83 		 * The name of the column to use as the value in the option list.
 84 		 * Set this value to __id__ to use the record id.
 85 		 * If this value is blank, then the primary key is used so long as
 86 		 * the primary key only has a single column.  If it is a compound
 87 		 * primary key, then the record id is used by default.
 88 		 * @var string
 89 		 */
 90 		this.value = null;
 91 		
 92 		/**
 93 		 * The name of the column to use as the title in the option list.
 94 		 * Set this value to __title__ to use the record title ( or leave blank).
 95 		 *
 96 		 * @var string
 97 		 */
 98 		this.text = null;
 99 		
100 		/**
101 		 * Search filters to add to the query.
102 		 * @var object
103 		 */
104 		this.filters = {};
105 		
106 		/**
107 		 * Callback function to be called with the selected values.
108 		 * function(values){}
109 		 * @var function
110 		 */
111 		this.callback = null;
112 		
113 		/**
114 		 * The document element that is used to display the dialog.
115 		 * @var HTMLDOMElement
116 		 */
117 		this.el = document.createElement('div');
118 		
119 		/**
120 		 * The base url to the RecordBrowser directory.
121 		 * @var string
122 		 */
123 		this.baseURL = DATAFACE_URL+'/js/RecordBrowser';
124 		
125 		for ( var i in o ){
126 			this[i] = o[i];
127 		}
128 		
129 		/**
130 		 * A flag to indicate whether the record select list
131 		 * needs to be updated when updateRecords() is called.
132 		 * The list would need to be updated if the filter parameters change.
133 		 * @var boolean
134 		 */
135 		this.dirty = true;
136 		//$('head').append('<link rel="stylesheet" type="text/css" href="'+DATAFACE_URL+'/css/smoothness/jquery-ui-1.7.2.custom.css"/>');
137 		
138 		
139 		
140 		
141 	}
142 	
143 	xataface.RecordBrowser.prototype = {
144 		display : function(){
145 			var rb = this;
146 			$('body').append(this.el);
147 			$(this.el).load(this.baseURL+'/templates/RecordBrowser.html', function(){
148 				var dialog = this;
149 				var searchChangeHandler = function(){
150 					rb.filterRecords({
151 						'-search' : $(this).val()
152 					});
153 				};
154 				$(this).find('.xf-RecordBrowser-search-field')
155 					.keyup(searchChangeHandler)
156 					.change(searchChangeHandler);
157 					//.blur(searchChangeHandler);
158 				//$(this).find('.xf-RecordBrowser-select').css('height', '90%');
159 				$(this).find('.xf-RecordBrowser-select-field')
160 					.css('width', '100%')
161 					.attr('size', 8);
162 					
163 				$(this).find('.xf-RecordBrowser-addnew-button').RecordDialog({
164 					table: rb.table,
165 					callback: function(){
166 						rb.dirty=true;
167 						rb.updateRecords();
168 					}
169 				});
170 				
171 				$(this).dialog({
172 					'title': 'Select Record',
173 					'buttons' : {
174 						'Select' : function(){
175 							var out = {};
176 							$(dialog).find('.xf-RecordBrowser-select-field :selected').each(function(i, selected){
177 								out[$(selected).attr('value')] = $(selected).text();
178 							});
179 								
180 							if ( rb.callback ) rb.callback(out);
181 							$(this).dialog("close");
182 						
183 						},
184 						'Cancel' : function(){
185 							$(this).dialog("close");
186 						
187 						}
188 						
189 					},
190 					'position': 'center',
191 					'modal' : true,
192 					'resize': function(event, ui){
193 						$(dialog).find('.xf-RecordBrowser-select-field').css('height', ($(dialog).height()-60)+'px');
194 						
195 					}
196 				});
197 				
198 				rb.updateRecords();
199 			});
200 		},
201 		
202 		filterRecords : function(filter){
203 			
204 			for ( var i in filter ){
205 				if ( this.filters[i] != filter[i] ) this.dirty = true;
206 				this.filters[i] = filter[i];
207 			}
208 			this.updateRecords();
209 		},
210 		
211 		updateRecords : function(){
212 			
213 			if ( this.dirty ){
214 				var sel = $(this.el).find('.xf-RecordBrowser-select-field');
215 				var val = $(sel).val();
216 				//var el = $(this.el);
217 				sel.load(this.getDataURL(), function(){
218 					sel.val(val);
219 				});
220 				this.dirty = false;
221 			}
222 		},
223 		
224 		getDataURL : function(){
225 			var url = DATAFACE_SITE_HREF+'?-action=RecordBrowser_data&-table='+encodeURIComponent(this.table);
226 			if ( this.value ) url += '&-value='+encodeURIComponent(this.value);
227 			if ( this.text ) url += '&-text='+encodeURIComponent(this.text);
228 			for ( var i in this.filters ){
229 				url += '&'+encodeURIComponent(i)+'='+encodeURIComponent(this.filters[i]);
230 			}
231 			return url;
232 		}
233 	
234 	};
235 	
236 	$.fn.RecordBrowser = function(options){
237 		
238 		return this.each(function(){
239 			var obj = $(this);
240 			obj.click(function(){
241 				if ( typeof(options.click) == 'function' ){
242 					options.click();
243 				}
244 				var rb = new xataface.RecordBrowser(options);
245 				rb.display();
246 			});
247 		});
248 	};
249 	
250 	$.fn.RecordBrowserWidget = function(options){
251 		return this.each(function(){
252 			
253 			var obj = $(this);
254 			var editable = options.editable || false;
255 			
256 			if ( obj.hasClass("xf-RecordBrowserWidget") ){
257 				// This field is already a record browser with different
258 				// settings.  We need to change it.  So we remove the old
259 				// display field.
260 				var oldDisplayField = obj.next();
261 				var oldButton = oldDisplayField.next();
262 				oldDisplayField.remove();
263 				oldButton.remove();
264 				
265 				obj.removeClass('xf-RecordBrowserWidget');
266 			}
267 			
268 			
269 			var displayField = document.createElement('input');
270 			$(displayField).attr('type','text')
271 				.addClass('xf-RecordBrowserWidget-displayField')
272 				//.css('width', obj.width()+'px')
273 				//.css('height', obj.height()+'px')
274 				.css('cursor', 'pointer')
275 				//.css('border', '1px solid black')
276 				.attr('readonly', 1);
277 			
278 
279 			$(displayField).insertAfter(this);
280 			
281 			obj.css('display','none')
282 				.addClass('xf-RecordBrowserWidget');
283 			
284 			if ( !options.frozen ){
285 				obj.change(function(){
286 					var id;
287 					if ( options.value && options.value != '__id__' ){
288 						id = encodeURIComponent(options.value)+'='+encodeURIComponent(obj.val());
289 					} else {
290 						id = obj.val();
291 					}
292 					var url = DATAFACE_SITE_HREF+'?-action=RecordBrowser_lookup_single&-table='+options.table+'&-id='+encodeURIComponent(id);
293 					if ( options.text ) url += '&-text='+encodeURIComponent(options.text);
294 					$.get(url, function(text){
295 						$(displayField).val(text);
296 					});
297 					
298 					updatePermissions();
299 					
300 					
301 						
302 				});
303 				
304 				/**
305 				 * Internal function to load the permissions for the currently selected record and then update
306 				 * whether the record can be edited or not.
307 				 */
308 				function updatePermissions(){
309 					try {
310 						// Now we check the edit permission to find out if we need to show or hide the edit link
311 						// for the field.
312 						var theq = {
313 						
314 							'-table': options.table
315 							
316 						};
317 						
318 						if ( options.value && options.value != '__id__' ){
319 							theq[options.value] = obj.val();
320 						} else {
321 							theq['--id'] = obj.val();
322 						}
323 						
324 						var perms = new xataface.Permissions({
325 							query: theq
326 						});
327 						//alert('here');
328 						perms.ready(function(){
329 							//alert('there');
330 							if ( perms.checkPermission('edit') ){
331 								editable = true;
332 							} else {
333 								editable = false;
334 							}
335 							updateEditable();
336 						
337 						});
338 					} catch (e){
339 						console.log('Looks like xataface.Permissions is not loaded while handling RecordBrowser change event.');
340 						console.log(e);
341 					}
342 				
343 				}
344 				
345 				
346 				
347 				var a = document.createElement('a');
348 				$(a).addClass('xf-RecordBrowser-button')
349 					.css('cursor', 'pointer')
350 					.html('<img src="'+DATAFACE_URL+'/images/search_icon.gif" border="0" /><span class="xf-RecordBrowser-button-label"> Lookup</span>');
351 				$(a).find('.xf-RecordBrowser-button-label')
352 					.css('display', 'none');
353 					
354 					
355 					
356 				
357 						
358 				
359 				
360 				$(a).insertAfter(displayField);
361 				
362 				
363 				
364 				
365 				// If we want to allow editing, we add an edit button after the field that opens a record dialog
366 				// for editing.
367 				var editButton = $('<a>')
368 					.addClass('xf-RecordBrowser-edit-button')
369 					.html('<img src="'+DATAFACE_URL+'/images/edit.gif" border="0" /><span class="xf-RecordBrowser-button-label">Edit</span>')
370 					.css({cursor: 'hand'})
371 					;
372 					
373 				$(editButton).find('.xf-RecordBrowser-button-label')
374 					.css('display', 'none');
375 					
376 				
377 				$(editButton).click(function(){
378 				
379 					if ( !editable ){
380 						alert('This record is not currently editable.');
381 					}
382 					var id = obj.val();
383 					if ( !id ){
384 						alert('No record is currently selected.');
385 						return;
386 					}
387 					
388 					var keyColName = '__id__';
389 					if ( options.value ){
390 						keyColName = options.value;
391 					}
392 					var recordid = encodeURIComponent(options.table)+'?'+encodeURIComponent(keyColName)+'='+encodeURIComponent(id);
393 					var dlg = new xataface.RecordDialog({
394 						recordid: recordid,
395 						table: options.table
396 					});
397 					dlg.display();
398 				});
399 				
400 				$(editButton).insertAfter(a);
401 				$(editButton).hide();
402 				
403 				function updateEditable(){
404 					if ( editable ) $(editButton).show();
405 					else $(editButton).hide();
406 				}
407 			
408 				var origCallback = function(){};
409 				
410 				if ( typeof(options.callback == 'function' ) ){
411 					origCallback = options.callback;
412 				}
413 				options.callback = function(vals){
414 					for ( var i in vals ){
415 						//$(displayField).val(vals[i]);
416 						obj.val(i);
417 						obj.trigger('change');
418 					}
419 					origCallback(vals, obj);
420 				};
421 				
422 				$(a).RecordBrowser(options);
423 				$(displayField).RecordBrowser(options);
424 			} else {
425 				//alert(obj.val());
426 			}
427 			
428 			if ( obj.val() ){
429 				var id;
430 				if ( options.value && options.value != '__id__' ){
431 					id = encodeURIComponent(options.value)+'='+encodeURIComponent(obj.val());
432 				} else {
433 					id = obj.val();
434 				}
435 				var url = DATAFACE_SITE_HREF+'?-action=RecordBrowser_lookup_single&-table='+options.table+'&-id='+encodeURIComponent(id);
436 				if ( options.text ) url += '&-text='+encodeURIComponent(options.text);
437 				$.get(url, function(text){
438 					//alert(text);
439 					$(displayField).val(text);
440 				});
441 				updatePermissions();
442 			}
443 			
444 			
445 		});
446 	};
447 })(jQuery);