[ Index ] |
PHP Cross Reference of vtigercrm-6.1.0 |
[Summary view] [Print] [Text view]
1 /*+*********************************************************************************** 2 * The contents of this file are subject to the vtiger CRM Public License Version 1.0 3 * ("License"); You may not use this file except in compliance with the License 4 * The Original Code is: vtiger CRM Open Source 5 * The Initial Developer of the Original Code is vtiger. 6 * Portions created by vtiger are Copyright (C) vtiger. 7 * All Rights Reserved. 8 *************************************************************************************/ 9 10 jQuery.Class("Vtiger_Edit_Js",{ 11 12 //Event that will triggered when reference field is selected 13 referenceSelectionEvent : 'Vtiger.Reference.Selection', 14 15 //Event that will triggered when reference field is selected 16 referenceDeSelectionEvent : 'Vtiger.Reference.DeSelection', 17 18 //Event that will triggered before saving the record 19 recordPreSave : 'Vtiger.Record.PreSave', 20 21 refrenceMultiSelectionEvent : 'Vtiger.MultiReference.Selection', 22 23 preReferencePopUpOpenEvent : 'Vtiger.Referece.Popup.Pre', 24 25 editInstance : false, 26 27 postReferenceSelectionEvent: 'Vtiger.PostReference.Selection', 28 29 /** 30 * Function to get Instance by name 31 * @params moduleName:-- Name of the module to create instance 32 */ 33 getInstanceByModuleName : function(moduleName){ 34 if(typeof moduleName == "undefined"){ 35 moduleName = app.getModuleName(); 36 } 37 var parentModule = app.getParentModuleName(); 38 if(parentModule == 'Settings'){ 39 var moduleClassName = parentModule+"_"+moduleName+"_Edit_Js"; 40 if(typeof window[moduleClassName] == 'undefined'){ 41 moduleClassName = moduleName+"_Edit_Js"; 42 } 43 var fallbackClassName = parentModule+"_Vtiger_Edit_Js"; 44 if(typeof window[fallbackClassName] == 'undefined') { 45 fallbackClassName = "Vtiger_Edit_Js"; 46 } 47 } else { 48 moduleClassName = moduleName+"_Edit_Js"; 49 fallbackClassName = "Vtiger_Edit_Js"; 50 } 51 if(typeof window[moduleClassName] != 'undefined'){ 52 var instance = new window[moduleClassName](); 53 }else{ 54 var instance = new window[fallbackClassName](); 55 } 56 return instance; 57 }, 58 59 60 getInstance: function(){ 61 if(Vtiger_Edit_Js.editInstance == false){ 62 var instance = Vtiger_Edit_Js.getInstanceByModuleName(); 63 Vtiger_Edit_Js.editInstance = instance; 64 return instance; 65 } 66 return Vtiger_Edit_Js.editInstance; 67 } 68 69 },{ 70 71 formElement : false, 72 73 getForm : function() { 74 if(this.formElement == false){ 75 this.setForm(jQuery('#EditView')); 76 } 77 return this.formElement; 78 }, 79 80 setForm : function(element){ 81 this.formElement = element; 82 return this; 83 }, 84 85 getPopUpParams : function(container) { 86 var params = {}; 87 var sourceModule = app.getModuleName(); 88 var popupReferenceModule = jQuery('input[name="popupReferenceModule"]',container).val(); 89 var sourceFieldElement = jQuery('input[class="sourceField"]',container); 90 var sourceField = sourceFieldElement.attr('name'); 91 var sourceRecordElement = jQuery('input[name="record"]'); 92 var sourceRecordId = ''; 93 if(sourceRecordElement.length > 0) { 94 sourceRecordId = sourceRecordElement.val(); 95 } 96 97 var isMultiple = false; 98 if(sourceFieldElement.data('multiple') == true){ 99 isMultiple = true; 100 } 101 102 var params = { 103 'module' : popupReferenceModule, 104 'src_module' : sourceModule, 105 'src_field' : sourceField, 106 'src_record' : sourceRecordId 107 } 108 109 if(isMultiple) { 110 params.multi_select = true ; 111 } 112 return params; 113 }, 114 115 116 openPopUp : function(e){ 117 var thisInstance = this; 118 var parentElem = jQuery(e.target).closest('td'); 119 120 var params = this.getPopUpParams(parentElem); 121 122 var isMultiple = false; 123 if(params.multi_select) { 124 isMultiple = true; 125 } 126 127 var sourceFieldElement = jQuery('input[class="sourceField"]',parentElem); 128 129 var prePopupOpenEvent = jQuery.Event(Vtiger_Edit_Js.preReferencePopUpOpenEvent); 130 sourceFieldElement.trigger(prePopupOpenEvent); 131 132 if(prePopupOpenEvent.isDefaultPrevented()) { 133 return ; 134 } 135 136 var popupInstance =Vtiger_Popup_Js.getInstance(); 137 popupInstance.show(params,function(data){ 138 var responseData = JSON.parse(data); 139 var dataList = new Array(); 140 for(var id in responseData){ 141 var data = { 142 'name' : responseData[id].name, 143 'id' : id 144 } 145 dataList.push(data); 146 if(!isMultiple) { 147 thisInstance.setReferenceFieldValue(parentElem, data); 148 } 149 } 150 151 if(isMultiple) { 152 sourceFieldElement.trigger(Vtiger_Edit_Js.refrenceMultiSelectionEvent,{'data':dataList}); 153 } 154 sourceFieldElement.trigger(Vtiger_Edit_Js.postReferenceSelectionEvent,{'data':responseData}); 155 }); 156 }, 157 158 setReferenceFieldValue : function(container, params) { 159 var sourceField = container.find('input[class="sourceField"]').attr('name'); 160 var fieldElement = container.find('input[name="'+sourceField+'"]'); 161 var sourceFieldDisplay = sourceField+"_display"; 162 var fieldDisplayElement = container.find('input[name="'+sourceFieldDisplay+'"]'); 163 var popupReferenceModule = container.find('input[name="popupReferenceModule"]').val(); 164 165 var selectedName = params.name; 166 var id = params.id; 167 168 fieldElement.val(id) 169 fieldDisplayElement.val(selectedName).attr('readonly',true); 170 fieldElement.trigger(Vtiger_Edit_Js.referenceSelectionEvent, {'source_module' : popupReferenceModule, 'record' : id, 'selectedName' : selectedName}); 171 172 fieldDisplayElement.validationEngine('closePrompt',fieldDisplayElement); 173 }, 174 175 proceedRegisterEvents : function(){ 176 if(jQuery('.recordEditView').length > 0){ 177 return true; 178 }else{ 179 return false; 180 } 181 }, 182 183 referenceModulePopupRegisterEvent : function(container){ 184 var thisInstance = this; 185 container.on("click",'.relatedPopup',function(e){ 186 thisInstance.openPopUp(e); 187 }); 188 container.find('.referenceModulesList').chosen().change(function(e){ 189 var element = jQuery(e.currentTarget); 190 var closestTD = element.closest('td').next(); 191 var popupReferenceModule = element.val(); 192 var referenceModuleElement = jQuery('input[name="popupReferenceModule"]', closestTD); 193 var prevSelectedReferenceModule = referenceModuleElement.val(); 194 referenceModuleElement.val(popupReferenceModule); 195 196 //If Reference module is changed then we should clear the previous value 197 if(prevSelectedReferenceModule != popupReferenceModule) { 198 closestTD.find('.clearReferenceSelection').trigger('click'); 199 } 200 }); 201 }, 202 203 getReferencedModuleName : function(parenElement){ 204 return jQuery('input[name="popupReferenceModule"]',parenElement).val(); 205 }, 206 207 searchModuleNames : function(params) { 208 var aDeferred = jQuery.Deferred(); 209 210 if(typeof params.module == 'undefined') { 211 params.module = app.getModuleName(); 212 } 213 214 if(typeof params.action == 'undefined') { 215 params.action = 'BasicAjax'; 216 } 217 AppConnector.request(params).then( 218 function(data){ 219 aDeferred.resolve(data); 220 }, 221 function(error){ 222 //TODO : Handle error 223 aDeferred.reject(); 224 } 225 ) 226 return aDeferred.promise(); 227 }, 228 229 /** 230 * Function to get reference search params 231 */ 232 getReferenceSearchParams : function(element){ 233 var tdElement = jQuery(element).closest('td'); 234 var params = {}; 235 var searchModule = this.getReferencedModuleName(tdElement); 236 params.search_module = searchModule; 237 return params; 238 }, 239 240 /** 241 * Function which will handle the reference auto complete event registrations 242 * @params - container <jQuery> - element in which auto complete fields needs to be searched 243 */ 244 registerAutoCompleteFields : function(container) { 245 var thisInstance = this; 246 container.find('input.autoComplete').autocomplete({ 247 'minLength' : '3', 248 'source' : function(request, response){ 249 //element will be array of dom elements 250 //here this refers to auto complete instance 251 var inputElement = jQuery(this.element[0]); 252 var searchValue = request.term; 253 var params = thisInstance.getReferenceSearchParams(inputElement); 254 params.search_value = searchValue; 255 thisInstance.searchModuleNames(params).then(function(data){ 256 var reponseDataList = new Array(); 257 var serverDataFormat = data.result 258 if(serverDataFormat.length <= 0) { 259 jQuery(inputElement).val(''); 260 serverDataFormat = new Array({ 261 'label' : app.vtranslate('JS_NO_RESULTS_FOUND'), 262 'type' : 'no results' 263 }); 264 } 265 for(var id in serverDataFormat){ 266 var responseData = serverDataFormat[id]; 267 reponseDataList.push(responseData); 268 } 269 response(reponseDataList); 270 }); 271 }, 272 'select' : function(event, ui ){ 273 var selectedItemData = ui.item; 274 //To stop selection if no results is selected 275 if(typeof selectedItemData.type != 'undefined' && selectedItemData.type=="no results"){ 276 return false; 277 } 278 selectedItemData.name = selectedItemData.value; 279 var element = jQuery(this); 280 var tdElement = element.closest('td'); 281 thisInstance.setReferenceFieldValue(tdElement, selectedItemData); 282 283 var sourceField = tdElement.find('input[class="sourceField"]').attr('name'); 284 var fieldElement = tdElement.find('input[name="'+sourceField+'"]'); 285 286 fieldElement.trigger(Vtiger_Edit_Js.postReferenceSelectionEvent,{'data':selectedItemData}); 287 }, 288 'change' : function(event, ui) { 289 var element = jQuery(this); 290 //if you dont have readonly attribute means the user didnt select the item 291 if(element.attr('readonly')== undefined) { 292 element.closest('td').find('.clearReferenceSelection').trigger('click'); 293 } 294 }, 295 'open' : function(event,ui) { 296 //To Make the menu come up in the case of quick create 297 jQuery(this).data('autocomplete').menu.element.css('z-index','100001'); 298 299 } 300 }); 301 }, 302 303 304 /** 305 * Function which will register reference field clear event 306 * @params - container <jQuery> - element in which auto complete fields needs to be searched 307 */ 308 registerClearReferenceSelectionEvent : function(container) { 309 container.find('.clearReferenceSelection').on('click', function(e){ 310 var element = jQuery(e.currentTarget); 311 var parentTdElement = element.closest('td'); 312 var fieldNameElement = parentTdElement.find('.sourceField'); 313 var fieldName = fieldNameElement.attr('name'); 314 fieldNameElement.val(''); 315 parentTdElement.find('#'+fieldName+'_display').removeAttr('readonly').val(''); 316 element.trigger(Vtiger_Edit_Js.referenceDeSelectionEvent); 317 e.preventDefault(); 318 }) 319 }, 320 321 /** 322 * Function which will register event to prevent form submission on pressing on enter 323 * @params - container <jQuery> - element in which auto complete fields needs to be searched 324 */ 325 registerPreventingEnterSubmitEvent : function(container) { 326 container.on('keypress', function(e){ 327 //Stop the submit when enter is pressed in the form 328 var currentElement = jQuery(e.target); 329 if(e.which == 13 && (!currentElement.is('textarea'))) { 330 e. preventDefault(); 331 } 332 }) 333 }, 334 335 /** 336 * Function which will give you all details of the selected record 337 * @params - an Array of values like {'record' : recordId, 'source_module' : searchModule, 'selectedName' : selectedRecordName} 338 */ 339 getRecordDetails : function(params) { 340 var aDeferred = jQuery.Deferred(); 341 var url = "index.php?module="+app.getModuleName()+"&action=GetData&record="+params['record']+"&source_module="+params['source_module']; 342 AppConnector.request(url).then( 343 function(data){ 344 if(data['success']) { 345 aDeferred.resolve(data); 346 } else { 347 aDeferred.reject(data['message']); 348 } 349 }, 350 function(error){ 351 aDeferred.reject(); 352 } 353 ) 354 return aDeferred.promise(); 355 }, 356 357 358 registerTimeFields : function(container) { 359 app.registerEventForTimeFields(container); 360 }, 361 362 referenceCreateHandler : function(container) { 363 var thisInstance = this; 364 var postQuickCreateSave = function(data) { 365 var params = {}; 366 params.name = data.result._recordLabel; 367 params.id = data.result._recordId; 368 thisInstance.setReferenceFieldValue(container, params); 369 } 370 371 var referenceModuleName = this.getReferencedModuleName(container); 372 var quickCreateNode = jQuery('#quickCreateModules').find('[data-name="'+ referenceModuleName +'"]'); 373 if(quickCreateNode.length <= 0) { 374 Vtiger_Helper_Js.showPnotify(app.vtranslate('JS_NO_CREATE_OR_NOT_QUICK_CREATE_ENABLED')) 375 } 376 quickCreateNode.trigger('click',{'callbackFunction':postQuickCreateSave}); 377 }, 378 379 /** 380 * Function which will register event for create of reference record 381 * This will allow users to create reference record from edit view of other record 382 */ 383 registerReferenceCreate : function(container) { 384 var thisInstance = this; 385 container.on('click','.createReferenceRecord', function(e){ 386 var element = jQuery(e.currentTarget); 387 var controlElementTd = element.closest('td'); 388 389 thisInstance.referenceCreateHandler(controlElementTd); 390 }) 391 }, 392 393 /** 394 * Function to register the event status change event 395 */ 396 registerEventStatusChangeEvent : function(container){ 397 var followupContainer = container.find('.followUpContainer'); 398 //if default value is set to Held then display follow up container 399 var defaultStatus = container.find('select[name="eventstatus"]').val(); 400 if(defaultStatus == 'Held'){ 401 followupContainer.show(); 402 } 403 container.find('select[name="eventstatus"]').on('change',function(e){ 404 var selectedOption = jQuery(e.currentTarget).val(); 405 if(selectedOption == 'Held'){ 406 followupContainer.show(); 407 } else{ 408 followupContainer.hide(); 409 } 410 }); 411 }, 412 413 /** 414 * Function which will register basic events which will be used in quick create as well 415 * 416 */ 417 registerBasicEvents : function(container) { 418 this.referenceModulePopupRegisterEvent(container); 419 this.registerAutoCompleteFields(container); 420 this.registerClearReferenceSelectionEvent(container); 421 this.registerPreventingEnterSubmitEvent(container); 422 this.registerTimeFields(container); 423 //Added here instead of register basic event of calendar. because this should be registered all over the places like quick create, edit, list.. 424 this.registerEventStatusChangeEvent(container); 425 this.registerRecordAccessCheckEvent(container); 426 this.registerEventForPicklistDependencySetup(container); 427 }, 428 429 /** 430 * Function to register event for image delete 431 */ 432 registerEventForImageDelete : function(){ 433 var formElement = this.getForm(); 434 var recordId = formElement.find('input[name="record"]').val(); 435 formElement.find('.imageDelete').on('click',function(e){ 436 var element = jQuery(e.currentTarget); 437 var parentTd = element.closest('td'); 438 var imageUploadElement = parentTd.find('[name="imagename[]"]'); 439 var fieldInfo = imageUploadElement.data('fieldinfo'); 440 var mandatoryStatus = fieldInfo.mandatory; 441 var imageId = element.closest('div').find('img').data().imageId; 442 element.closest('div').remove(); 443 var exisitingImages = parentTd.find('[name="existingImages"]'); 444 if(exisitingImages.length < 1 && mandatoryStatus){ 445 formElement.validationEngine('detach'); 446 imageUploadElement.attr('data-validation-engine','validate[required,funcCall[Vtiger_Base_Validator_Js.invokeValidation]]'); 447 formElement.validationEngine('attach'); 448 } 449 450 if(formElement.find('[name=imageid]').length != 0) { 451 var imageIdValue = JSON.parse(formElement.find('[name=imageid]').val()); 452 imageIdValue.push(imageId); 453 formElement.find('[name=imageid]').val(JSON.stringify(imageIdValue)); 454 } else { 455 var imageIdJson = []; 456 imageIdJson.push(imageId); 457 formElement.append('<input type="hidden" name="imgDeleted" value="true" />'); 458 formElement.append('<input type="hidden" name="imageid" value="'+JSON.stringify(imageIdJson)+'" />'); 459 } 460 }); 461 }, 462 463 triggerDisplayTypeEvent : function() { 464 var widthType = app.cacheGet('widthType', 'narrowWidthType'); 465 if(widthType) { 466 var elements = jQuery('#EditView').find('td'); 467 elements.addClass(widthType); 468 } 469 }, 470 471 registerSubmitEvent: function() { 472 var editViewForm = this.getForm(); 473 474 editViewForm.submit(function(e){ 475 //Form should submit only once for multiple clicks also 476 if(typeof editViewForm.data('submit') != "undefined") { 477 return false; 478 } else { 479 var module = jQuery(e.currentTarget).find('[name="module"]').val(); 480 if(editViewForm.validationEngine('validate')) { 481 //Once the form is submiting add data attribute to that form element 482 editViewForm.data('submit', 'true'); 483 //on submit form trigger the recordPreSave event 484 var recordPreSaveEvent = jQuery.Event(Vtiger_Edit_Js.recordPreSave); 485 editViewForm.trigger(recordPreSaveEvent, {'value' : 'edit'}); 486 if(recordPreSaveEvent.isDefaultPrevented()) { 487 //If duplicate record validation fails, form should submit again 488 editViewForm.removeData('submit'); 489 e.preventDefault(); 490 } 491 } else { 492 //If validation fails, form should submit again 493 editViewForm.removeData('submit'); 494 // to avoid hiding of error message under the fixed nav bar 495 app.formAlignmentAfterValidation(editViewForm); 496 } 497 } 498 }); 499 }, 500 501 /* 502 * Function to check the view permission of a record after save 503 */ 504 505 registerRecordAccessCheckEvent : function(form) { 506 507 form.on(Vtiger_Edit_Js.recordPreSave, function(e, data) { 508 var assignedToSelectElement = jQuery('[name="assigned_user_id"]',form); 509 if(assignedToSelectElement.data('recordaccessconfirmation') == true) { 510 return; 511 }else{ 512 if(assignedToSelectElement.data('recordaccessconfirmationprogress') != true) { 513 var recordAccess = assignedToSelectElement.find('option:selected').data('recordaccess'); 514 if(recordAccess == false) { 515 var message = app.vtranslate('JS_NO_VIEW_PERMISSION_AFTER_SAVE'); 516 Vtiger_Helper_Js.showConfirmationBox({'message' : message}).then( 517 function(e) { 518 assignedToSelectElement.data('recordaccessconfirmation',true); 519 assignedToSelectElement.removeData('recordaccessconfirmationprogress'); 520 form.append('<input type="hidden" name="returnToList" value="true" />'); 521 form.submit(); 522 }, 523 function(error, err){ 524 assignedToSelectElement.removeData('recordaccessconfirmationprogress'); 525 e.preventDefault(); 526 }); 527 assignedToSelectElement.data('recordaccessconfirmationprogress',true); 528 } else { 529 return true; 530 } 531 } 532 } 533 e.preventDefault(); 534 }); 535 }, 536 537 /** 538 * Function to register event for setting up picklistdependency 539 * for a module if exist on change of picklist value 540 */ 541 registerEventForPicklistDependencySetup : function(container){ 542 var picklistDependcyElemnt = jQuery('[name="picklistDependency"]',container); 543 if(picklistDependcyElemnt.length <= 0) { 544 return; 545 } 546 var picklistDependencyMapping = JSON.parse(picklistDependcyElemnt.val()); 547 548 var sourcePicklists = Object.keys(picklistDependencyMapping); 549 if(sourcePicklists.length <= 0){ 550 return; 551 } 552 553 var sourcePickListNames = ""; 554 for(var i=0;i<sourcePicklists.length;i++){ 555 sourcePickListNames += '[name="'+sourcePicklists[i]+'"],'; 556 } 557 var sourcePickListElements = container.find(sourcePickListNames); 558 559 sourcePickListElements.on('change',function(e){ 560 var currentElement = jQuery(e.currentTarget); 561 var sourcePicklistname = currentElement.attr('name'); 562 563 var configuredDependencyObject = picklistDependencyMapping[sourcePicklistname]; 564 var selectedValue = currentElement.val(); 565 var targetObjectForSelectedSourceValue = configuredDependencyObject[selectedValue]; 566 var picklistmap = configuredDependencyObject["__DEFAULT__"]; 567 568 if(typeof targetObjectForSelectedSourceValue == 'undefined'){ 569 targetObjectForSelectedSourceValue = picklistmap; 570 } 571 jQuery.each(picklistmap,function(targetPickListName,targetPickListValues){ 572 var targetPickListMap = targetObjectForSelectedSourceValue[targetPickListName]; 573 if(typeof targetPickListMap == "undefined"){ 574 targetPickListMap = targetPickListValues; 575 } 576 var targetPickList = jQuery('[name="'+targetPickListName+'"]',container); 577 if(targetPickList.length <= 0){ 578 return; 579 } 580 581 var listOfAvailableOptions = targetPickList.data('availableOptions'); 582 if(typeof listOfAvailableOptions == "undefined"){ 583 listOfAvailableOptions = jQuery('option',targetPickList); 584 targetPickList.data('available-options', listOfAvailableOptions); 585 } 586 587 var targetOptions = new jQuery(); 588 var optionSelector = []; 589 optionSelector.push(''); 590 for(var i=0; i<targetPickListMap.length; i++){ 591 optionSelector.push(targetPickListMap[i]); 592 } 593 594 jQuery.each(listOfAvailableOptions, function(i,e) { 595 var picklistValue = jQuery(e).val(); 596 if(jQuery.inArray(picklistValue, optionSelector) != -1) { 597 targetOptions = targetOptions.add(jQuery(e)); 598 } 599 }) 600 var targetPickListSelectedValue = ''; 601 var targetPickListSelectedValue = targetOptions.filter('[selected]').val(); 602 targetPickList.html(targetOptions).val(targetPickListSelectedValue).trigger("liszt:updated"); 603 }) 604 }); 605 606 //To Trigger the change on load 607 sourcePickListElements.trigger('change'); 608 }, 609 610 registerLeavePageWithoutSubmit : function(form){ 611 InitialFormData = form.serialize(); 612 window.onbeforeunload = function(e){ 613 if (InitialFormData != form.serialize() && form.data('submit') != "true") { 614 return app.vtranslate("JS_CHANGES_WILL_BE_LOST"); 615 } 616 }; 617 }, 618 619 registerEvents: function(){ 620 var editViewForm = this.getForm(); 621 var statusToProceed = this.proceedRegisterEvents(); 622 if(!statusToProceed){ 623 return; 624 } 625 626 this.registerBasicEvents(this.getForm()); 627 this.registerEventForImageDelete(); 628 this.registerSubmitEvent(); 629 this.registerLeavePageWithoutSubmit(editViewForm); 630 631 app.registerEventForDatePickerFields('#EditView'); 632 633 var params = app.validationEngineOptions; 634 params.onValidationComplete = function(element,valid){ 635 if(valid){ 636 var ckEditorSource = editViewForm.find('.ckEditorSource'); 637 if(ckEditorSource.length > 0){ 638 var ckEditorSourceId = ckEditorSource.attr('id'); 639 var fieldInfo = ckEditorSource.data('fieldinfo'); 640 var isMandatory = fieldInfo.mandatory; 641 var CKEditorInstance = CKEDITOR.instances; 642 var ckEditorValue = jQuery.trim(CKEditorInstance[ckEditorSourceId].document.getBody().getText()); 643 if(isMandatory && (ckEditorValue.length === 0)){ 644 var ckEditorId = 'cke_'+ckEditorSourceId; 645 var message = app.vtranslate('JS_REQUIRED_FIELD'); 646 jQuery('#'+ckEditorId).validationEngine('showPrompt', message , 'error','topLeft',true); 647 return false; 648 }else{ 649 return valid; 650 } 651 } 652 return valid; 653 } 654 return valid 655 } 656 editViewForm.validationEngine(params); 657 658 this.registerReferenceCreate(editViewForm); 659 //this.triggerDisplayTypeEvent(); 660 } 661 });
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:08:37 2014 | Cross-referenced by PHPXref 0.7.1 |