fixing librejs on defectivebydesign.org
[weblabels.fsf.org.git] / crm.fsf.org / 20131203 / files / sites / all / modules / civicrm / js / Common.js
1 /*
2 +--------------------------------------------------------------------+
3 | CiviCRM version 4.3 |
4 +--------------------------------------------------------------------+
5 | Copyright CiviCRM LLC (c) 2004-2013 |
6 +--------------------------------------------------------------------+
7 | This file is a part of CiviCRM. |
8 | |
9 | CiviCRM is free software; you can copy, modify, and distribute it |
10 | under the terms of the GNU Affero General Public License |
11 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
12 | |
13 | CiviCRM is distributed in the hope that it will be useful, but |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
16 | See the GNU Affero General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU Affero General Public |
19 | License and the CiviCRM Licensing Exception along |
20 | with this program; if not, contact CiviCRM LLC |
21 | at info[AT]civicrm[DOT]org. If you have questions about the |
22 | GNU Affero General Public License or the licensing of CiviCRM, |
23 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
24 +--------------------------------------------------------------------+
25 */
26
27 /**
28 * @file: global functions for CiviCRM
29 * FIXME: We are moving away from using global functions. DO NOT ADD MORE.
30 * @see CRM object - the better alternative to adding global functions
31 */
32
33 var CRM = CRM || {};
34 var cj = jQuery;
35
36 /**
37 * Short-named function for string translation, defined in global scope so it's available everywhere.
38 *
39 * @param $text string string for translating
40 * @param $params object key:value of additional parameters
41 *
42 * @return string the translated string
43 */
44 function ts(text, params) {
45 "use strict";
46 text = CRM.strings[text] || text;
47 if (params && typeof(params) === 'object') {
48 for (var i in params) {
49 // sprintf emulation: escape % characters in the replacements to avoid conflicts
50 text = text.replace(new RegExp('%'+i, 'g'), params[i].replace(/%/g, '%-crmescaped-'));
51 }
52 return text.replace(/%-crmescaped-/g, '%');
53 }
54 return text;
55 }
56
57 /**
58 * This function is called by default at the bottom of template files which have forms that have
59 * conditionally displayed/hidden sections and elements. The PHP is responsible for generating
60 * a list of 'blocks to show' and 'blocks to hide' and the template passes these parameters to
61 * this function.
62 *
63 * @access public
64 * @param showBlocks Array of element Id's to be displayed
65 * @param hideBlocks Array of element Id's to be hidden
66 * @param elementType Value to set display style to for showBlocks (e.g. 'block' or 'table-row' or ...)
67 * @return none
68 */
69 function on_load_init_blocks(showBlocks, hideBlocks, elementType)
70 {
71 if ( elementType == null ) {
72 var elementType = 'block';
73 }
74
75 /* This loop is used to display the blocks whose IDs are present within the showBlocks array */
76 for ( var i = 0; i < showBlocks.length; i++ ) {
77 var myElement = document.getElementById(showBlocks[i]);
78 /* getElementById returns null if element id doesn't exist in the document */
79 if (myElement != null) {
80 myElement.style.display = elementType;
81 } else {
82 alert('showBlocks array item not in .tpl = ' + showBlocks[i]);
83 }
84 }
85
86 /* This loop is used to hide the blocks whose IDs are present within the hideBlocks array */
87 for ( var i = 0; i < hideBlocks.length; i++ ) {
88 var myElement = document.getElementById(hideBlocks[i]);
89 /* getElementById returns null if element id doesn't exist in the document */
90 if (myElement != null) {
91 myElement.style.display = 'none';
92 } else {
93 alert('showBlocks array item not in .tpl = ' + hideBlocks[i]);
94 }
95 }
96 }
97
98 /**
99 * This function is called when we need to show or hide a related form element (target_element)
100 * based on the value (trigger_value) of another form field (trigger_field).
101 *
102 * @access public
103 * @param trigger_field_id HTML id of field whose onchange is the trigger
104 * @param trigger_value List of integers - option value(s) which trigger show-element action for target_field
105 * @param target_element_id HTML id of element to be shown or hidden
106 * @param target_element_type Type of element to be shown or hidden ('block' or 'table-row')
107 * @param field_type Type of element radio/select
108 * @param invert Boolean - if true, we HIDE target on value match; if false, we SHOW target on value match
109 * @return none
110 */
111 function showHideByValue(trigger_field_id, trigger_value, target_element_id, target_element_type, field_type, invert ) {
112 if ( target_element_type == null ) {
113 var target_element_type = 'block';
114 } else if ( target_element_type == 'table-row' ) {
115 var target_element_type = '';
116 }
117
118 if (field_type == 'select') {
119 var trigger = trigger_value.split("|");
120 var selectedOptionValue = document.getElementById(trigger_field_id).options[document.getElementById(trigger_field_id).selectedIndex].value;
121
122 var target = target_element_id.split("|");
123 for(var j = 0; j < target.length; j++) {
124 if ( invert ) {
125 cj('#' + target[j]).show();
126 } else {
127 cj('#' + target[j]).hide();
128 }
129 for(var i = 0; i < trigger.length; i++) {
130 if (selectedOptionValue == trigger[i]) {
131 if ( invert ) {
132 cj('#' + target[j]).hide();
133 } else {
134 cj('#' + target[j]).show();
135 }
136 }
137 }
138 }
139
140 } else if (field_type == 'radio') {
141 var target = target_element_id.split("|");
142 for(var j = 0; j < target.length; j++) {
143 if (document.getElementsByName(trigger_field_id)[0].checked) {
144 if ( invert ) {
145 cj('#' + target[j]).hide();
146 } else {
147 cj('#' + target[j]).show();
148 }
149 } else {
150 if ( invert ) {
151 cj('#' + target[j]).show();
152 } else {
153 cj('#' + target[j]).hide();
154 }
155 }
156 }
157 }
158 }
159
160 /**
161 *
162 * Function for checking ALL or unchecking ALL check boxes in a resultset page.
163 *
164 * @access public
165 * @param fldPrefix - common string which precedes unique checkbox ID and identifies field as
166 * belonging to the resultset's checkbox collection
167 * @param object - checkbox
168 * Sample usage: onClick="javascript:changeCheckboxValues('chk_', cj(this) );"
169 *
170 * @return
171 */
172 function toggleCheckboxVals(fldPrefix, object) {
173 if ( object.id == 'toggleSelect' && cj(object).is(':checked') ) {
174 cj( 'Input[id*="' + fldPrefix + '"],Input[id*="toggleSelect"]').attr('checked', true);
175 } else {
176 cj( 'Input[id*="' + fldPrefix + '"],Input[id*="toggleSelect"]').attr('checked', false);
177 }
178 // change the class of selected rows
179 on_load_init_checkboxes(object.form.name);
180 }
181
182 function countSelectedCheckboxes(fldPrefix, form) {
183 fieldCount = 0;
184 for( i=0; i < form.elements.length; i++) {
185 fpLen = fldPrefix.length;
186 if (form.elements[i].type == 'checkbox' && form.elements[i].name.slice(0,fpLen) == fldPrefix && form.elements[i].checked == true) {
187 fieldCount++;
188 }
189 }
190 return fieldCount;
191 }
192
193 /**
194 * Function to enable task action select
195 */
196 function toggleTaskAction( status ) {
197 var radio_ts = document.getElementsByName('radio_ts');
198 if (!radio_ts[1]) {
199 radio_ts[0].checked = true;
200 }
201 if ( radio_ts[0].checked || radio_ts[1].checked ) {
202 status = true;
203 }
204
205 var formElements = ['task', 'Go', 'Print'];
206 for(var i=0; i<formElements.length; i++ ) {
207 var element = document.getElementById( formElements[i] );
208 if ( element ) {
209 if ( status ) {
210 element.disabled = false;
211 } else {
212 element.disabled = true;
213 }
214 }
215 }
216 }
217
218 /**
219 * This function is used to check if any actio is selected and also to check if any contacts are checked.
220 *
221 * @access public
222 * @param fldPrefix - common string which precedes unique checkbox ID and identifies field as
223 * belonging to the resultset's checkbox collection
224 * @param form - name of form that checkboxes are part of
225 * Sample usage: onClick="javascript:checkPerformAction('chk_', myForm );"
226 *
227 */
228 function checkPerformAction (fldPrefix, form, taskButton, selection) {
229 var cnt;
230 var gotTask = 0;
231
232 // taskButton TRUE means we don't need to check the 'task' field - it's a button-driven task
233 if (taskButton == 1) {
234 gotTask = 1;
235 } else if (document.forms[form].task.selectedIndex) {
236 //force user to select all search contacts, CRM-3711
237 if ( document.forms[form].task.value == 13 || document.forms[form].task.value == 14 ) {
238 var toggleSelect = document.getElementsByName('toggleSelect');
239 if ( toggleSelect[0].checked || document.forms[form].radio_ts[0].checked ) {
240 return true;
241 } else {
242 alert( "Please select all contacts for this action.\n\nTo use the entire set of search results, click the 'all records' radio button." );
243 return false;
244 }
245 }
246 gotTask = 1;
247 }
248
249 if (gotTask == 1) {
250 // If user wants to perform action on ALL records and we have a task, return (no need to check further)
251 if (document.forms[form].radio_ts[0].checked) {
252 return true;
253 }
254
255 cnt = (selection == 1) ? countSelections() : countSelectedCheckboxes(fldPrefix, document.forms[form]);
256 if (!cnt) {
257 alert ("Please select one or more contacts for this action.\n\nTo use the entire set of search results, click the 'all records' radio button.");
258 return false;
259 }
260 } else {
261 alert ("Please select an action from the drop-down menu.");
262 return false;
263 }
264 }
265
266 /**
267 * This function changes the style for a checkbox block when it is selected.
268 *
269 * @access public
270 * @param chkName - it is name of the checkbox
271 * @return null
272 */
273 function checkSelectedBox( chkName ) {
274 var checkElement = cj('#' + chkName );
275 if ( checkElement.attr('checked') ) {
276 cj('input[value=ts_sel]:radio').attr('checked',true );
277 checkElement.parents('tr').addClass('crm-row-selected');
278 } else {
279 checkElement.parents('tr').removeClass('crm-row-selected');
280 }
281 }
282
283 /**
284 * This function is to show the row with selected checkbox in different color
285 * @param form - name of form that checkboxes are part of
286 *
287 * @access public
288 * @return null
289 */
290 function on_load_init_checkboxes(form)
291 {
292 var formName = form;
293 var fldPrefix = 'mark_x';
294 for( i=0; i < document.forms[formName].elements.length; i++) {
295 fpLen = fldPrefix.length;
296 if (document.forms[formName].elements[i].type == 'checkbox' && document.forms[formName].elements[i].name.slice(0,fpLen) == fldPrefix ) {
297 checkSelectedBox (document.forms[formName].elements[i].name, formName);
298 }
299 }
300 }
301
302 /**
303 * Function to change the color of the class
304 *
305 * @param form - name of the form
306 * @param rowid - id of the <tr>, <div> you want to change
307 *
308 * @access public
309 * @return null
310 */
311 function changeRowColor (rowid, form) {
312 switch (document.getElementById(rowid).className) {
313 case 'even-row' : document.getElementById(rowid).className = 'selected even-row';
314 break;
315 case 'odd-row' : document.getElementById(rowid).className = 'selected odd-row';
316 break;
317 case 'selected even-row' : document.getElementById(rowid).className = 'even-row';
318 break;
319 case 'selected odd-row' : document.getElementById(rowid).className = 'odd-row';
320 break;
321 case 'form-item' : document.getElementById(rowid).className = 'selected';
322 break;
323 case 'selected' : document.getElementById(rowid).className = 'form-item';
324 }
325 }
326
327 /**
328 * This function is to show the row with selected checkbox in different color
329 * @param form - name of form that checkboxes are part of
330 *
331 * @access public
332 * @return null
333 */
334 function on_load_init_check(form)
335 {
336 for( i=0; i < document.forms[form].elements.length; i++) {
337 if ( ( document.forms[form].elements[i].type == 'checkbox'
338 && document.forms[form].elements[i].checked == true )
339 || ( document.forms[form].elements[i].type == 'hidden'
340 && document.forms[form].elements[i].value == 1 ) ) {
341 var ss = document.forms[form].elements[i].id;
342 var row = 'rowid' + ss;
343 changeRowColor(row, form);
344 }
345 }
346 }
347
348 /**
349 * reset all the radio buttons with a given name
350 *
351 * @param string fieldName
352 * @param object form
353 * @return null
354 */
355 function unselectRadio(fieldName, form) {
356 for( i=0; i < document.forms[form].elements.length; i++) {
357 if (document.forms[form].elements[i].name == fieldName) {
358 document.forms[form].elements[i].checked = false;
359 }
360 }
361 return;
362 }
363
364 /**
365 * Function to change button text and disable one it is clicked
366 *
367 * @param obj object - the button clicked
368 * @param formID string - the id of the form being submitted
369 * @param string procText - button text after user clicks it
370 * @return null
371 */
372 var submitcount=0;
373 /* Changes button label on submit, and disables button after submit for newer browsers.
374 Puts up alert for older browsers. */
375 function submitOnce(obj,formId,procText) {
376 // if named button clicked, change text
377 if (obj.value != null) {
378 obj.value = procText + " ...";
379 }
380 if (document.getElementById) { // disable submit button for newer browsers
381 obj.disabled = true;
382 document.getElementById(formId).submit();
383 return true;
384 } else { // for older browsers
385 if (submitcount == 0) {
386 submitcount++;
387 return true;
388 } else {
389 alert("Your request is currently being processed ... Please wait.");
390 return false;
391 }
392 }
393 }
394
395 function popUp(URL) {
396 day = new Date();
397 id = day.getTime();
398 eval("page" + id + " = window.open(URL, '" + id + "', 'toolbar=0,scrollbars=1,location=0,statusbar=0,menubar=0,resizable=0,width=640,height=420,left = 202,top = 184');");
399 }
400
401 function imagePopUp ( path ) {
402 window.open(path,'popupWindow','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,screenX=150,screenY=150,top=150,left=150');
403 }
404
405 /**
406 * Function to show / hide the row in optionFields
407 *
408 * @param element name index, that whose innerHTML is to hide else will show the hidden row.
409 */
410 function showHideRow( index ) {
411 if ( index ) {
412 cj( 'tr#optionField_' + index ).hide( );
413 if( cj( 'table#optionField tr:hidden:first' ).length ) cj( 'div#optionFieldLink' ).show( );
414 } else {
415 cj( 'table#optionField tr:hidden:first' ).show( );
416 if( ! cj( 'table#optionField tr:hidden:last' ).length ) cj( 'div#optionFieldLink' ).hide( );
417 }
418 return false;
419 }
420
421 /**
422 * Function to check activity status in relavent to activity date
423 *
424 * @param element message JSON object.
425 */
426 function activityStatus( message ) {
427 var d = new Date(), time = [], i;
428 var currentDateTime = d.getTime()
429 var activityTime = cj("input#activity_date_time_time").val().replace(":", "");
430
431 //chunk the time in bunch of 2 (hours,minutes,ampm)
432 for(i=0; i<activityTime.length; i+=2 ) {
433 time.push( activityTime.slice( i, i+2 ) );
434 }
435 var activityDate = new Date( cj("input#activity_date_time_hidden").val() );
436
437 d.setFullYear(activityDate.getFullYear());
438 d.setMonth(activityDate.getMonth());
439 d.setDate(activityDate.getDate());
440 var hours = time['0'];
441 var ampm = time['2'];
442
443 if (ampm == "PM" && hours != 0 && hours != 12) {
444 // force arithmetic instead of string concatenation
445 hours = hours*1 + 12;
446 } else if (ampm == "AM" && hours == 12) {
447 hours = 0;
448 }
449 d.setHours(hours);
450 d.setMinutes(time['1']);
451
452 var activity_date_time = d.getTime();
453
454 var activityStatusId = cj('#status_id').val();
455
456 if ( activityStatusId == 2 && currentDateTime < activity_date_time ) {
457 if (! confirm( message.completed )) {
458 return false;
459 }
460 } else if ( activity_date_time && activityStatusId == 1 && currentDateTime >= activity_date_time ) {
461 if (! confirm( message.scheduled )) {
462 return false;
463 }
464 }
465 }
466
467 CRM.strings = CRM.strings || {};
468 CRM.validate = CRM.validate || {
469 params: {},
470 functions: []
471 };
472
473 (function($, undefined) {
474 "use strict";
475 $(document).ready(function() {
476 $().crmtooltip();
477 $('.crm-container table.row-highlight').on('change', 'input.select-row, input.select-rows', function() {
478 var target, table = $(this).closest('table');
479 if ($(this).hasClass('select-rows')) {
480 target = $('tbody tr', table);
481 $('input.select-row', table).prop('checked', $(this).prop('checked'));
482 }
483 else {
484 target = $(this).closest('tr');
485 $('input.select-rows', table).prop('checked', $(".select-row:not(':checked')", table).length < 1);
486 }
487 target.toggleClass('crm-row-selected', $(this).is(':checked'));
488 });
489 $('#crm-container').live('click', function(event) {
490 if ($(event.target).is('.btn-slide')) {
491 var currentActive = $('#crm-container .btn-slide-active');
492 currentActive.children().hide();
493 currentActive.removeClass('btn-slide-active');
494 $(event.target).children().show();
495 $(event.target).addClass('btn-slide-active');
496 } else {
497 $('.btn-slide .panel').hide();
498 $('.btn-slide-active').removeClass('btn-slide-active');
499 }
500 });
501 });
502
503 /**
504 * Function to make multiselect boxes behave as fields in small screens
505 */
506 function advmultiselectResize() {
507 var amswidth = $("#crm-container form:has(table.advmultiselect)").width();
508 if (amswidth < 700) {
509 $("form table.advmultiselect td").css('display', 'block');
510 } else {
511 $("form table.advmultiselect td").css('display', 'table-cell');
512 }
513 var contactwidth = $('#crm-container #mainTabContainer').width();
514 if (contactwidth < 600) {
515 $('#crm-container #mainTabContainer').addClass('narrowpage');
516 $('#crm-container #mainTabContainer.narrowpage #contactTopBar td').each( function(index) {
517 if (index > 1) {
518 if (index%2 == 0) {
519 $(this).parent().after('<tr class="narrowadded"></tr>');
520 }
521 var item = $(this);
522 $(this).parent().next().append(item);
523 }
524 });
525 } else {
526 $('#crm-container #mainTabContainer.narrowpage').removeClass('narrowpage');
527 $('#crm-container #mainTabContainer #contactTopBar tr.narrowadded td').each( function() {
528 var nitem = $(this);
529 var parent = $(this).parent();
530 $(this).parent().prev().append(nitem);
531 if ( parent.children().size() == 0 ) {
532 parent.remove();
533 }
534 });
535 $('#crm-container #mainTabContainer.narrowpage #contactTopBar tr.added').detach();
536 }
537 var cformwidth = $('#crm-container #Contact .contact_basic_information-section').width();
538
539 if (cformwidth < 720) {
540 $('#crm-container .contact_basic_information-section').addClass('narrowform');
541 $('#crm-container .contact_basic_information-section table.form-layout-compressed td .helpicon').parent().addClass('hashelpicon');
542 if (cformwidth < 480) {
543 $('#crm-container .contact_basic_information-section').addClass('xnarrowform');
544 } else {
545 $('#crm-container .contact_basic_information-section.xnarrowform').removeClass('xnarrowform');
546 }
547 } else {
548 $('#crm-container .contact_basic_information-section.narrowform').removeClass('narrowform');
549 $('#crm-container .contact_basic_information-section.xnarrowform').removeClass('xnarrowform');
550 }
551 }
552 advmultiselectResize();
553 $(window).resize(function() {
554 advmultiselectResize();
555 });
556
557 $.fn.crmtooltip = function() {
558 $('a.crm-summary-link:not(.crm-processed)')
559 .addClass('crm-processed')
560 .on('mouseover', function(e) {
561 $(this).addClass('crm-tooltip-active');
562 var topDistance = e.pageY - $(window).scrollTop();
563 if (topDistance < 300 | topDistance < $(this).children('.crm-tooltip-wrapper').height()) {
564 $(this).addClass('crm-tooltip-down');
565 }
566 if (!$(this).children('.crm-tooltip-wrapper').length) {
567 $(this).append('<div class="crm-tooltip-wrapper"><div class="crm-tooltip"></div></div>');
568 $(this).children().children('.crm-tooltip')
569 .html('<div class="crm-loading-element"></div>')
570 .load(this.href);
571 }
572 })
573 .on('mouseout', function() {
574 $(this).removeClass('crm-tooltip-active crm-tooltip-down');
575 })
576 .on('click', false);
577 };
578
579 var h;
580 CRM.help = function(title, params) {
581 h && h.close && h.close();
582 var options = {
583 expires: 0
584 };
585 h = CRM.alert('...', title, 'crm-help crm-msg-loading', options);
586 params.class_name = 'CRM_Core_Page_Inline_Help';
587 params.type = 'page';
588 $.ajax(CRM.url('civicrm/ajax/inline'),
589 {
590 data: params,
591 dataType: 'html',
592 success: function (data) {
593 $('#crm-notification-container .crm-help .notify-content:last').html(data);
594 $('#crm-notification-container .crm-help').removeClass('crm-msg-loading').addClass('info');
595 },
596 error: function () {
597 $('#crm-notification-container .crm-help .notify-content:last').html('Unable to load help file.');
598 $('#crm-notification-container .crm-help').removeClass('crm-msg-loading').addClass('error');
599 }
600 }
601 );
602 };
603
604 /**
605 * @param string text Displayable message
606 * @param string title Displayable title
607 * @param string type 'alert'|'info'|'success'|'error' (default: 'alert')
608 * @param {object} options
609 * @return {*}
610 * @see http://wiki.civicrm.org/confluence/display/CRM/Notifications+in+CiviCRM
611 */
612 CRM.alert = function(text, title, type, options) {
613 type = type || 'alert';
614 title = title || '';
615 options = options || {};
616 if ($('#crm-notification-container').length) {
617 var params = {
618 text: text,
619 title: title,
620 type: type
621 };
622 // By default, don't expire errors and messages containing links
623 var extra = {
624 expires: (type == 'error' || text.indexOf('<a ') > -1) ? 0 : (text ? 10000 : 5000),
625 unique: true
626 };
627 options = $.extend(extra, options);
628 options.expires = options.expires === false ? 0 : parseInt(options.expires, 10);
629 if (options.unique && options.unique !== '0') {
630 $('#crm-notification-container .ui-notify-message').each(function() {
631 if (title === $('h1', this).html() && text === $('.notify-content', this).html()) {
632 $('.icon.ui-notify-close', this).click();
633 }
634 });
635 }
636 return $('#crm-notification-container').notify('create', params, options);
637 }
638 else {
639 if (title.length) {
640 text = title + "\n" + text;
641 }
642 alert(text);
643 return null;
644 }
645 };
646
647 /**
648 * Close whichever alert contains the given node
649 *
650 * @param node
651 */
652 CRM.closeAlertByChild = function(node) {
653 $(node).closest('.ui-notify-message').find('.icon.ui-notify-close').click();
654 };
655
656 /**
657 * Prompt the user for confirmation.
658 *
659 * @param buttons {object|function} key|value pairs where key == button label and value == callback function
660 * passing in a function instead of an object is a shortcut for a sinlgle button labeled "Continue"
661 * @param options {object|void} Override defaults, keys include 'title', 'message',
662 * see jQuery.dialog for full list of available params
663 */
664 CRM.confirm = function(buttons, options) {
665 var dialog, callbacks = {};
666 var settings = {
667 title: ts('Confirm Action'),
668 message: ts('Are you sure you want to continue?'),
669 resizable: false,
670 modal: true,
671 close: function() {$(dialog).remove();},
672 buttons: {}
673 };
674 settings.buttons[ts('Cancel')] = function() {dialog.dialog('close');};
675 options = options || {};
676 $.extend(settings, options);
677 if (typeof(buttons) === 'function') {
678 callbacks[ts('Continue')] = buttons;
679 } else {
680 callbacks = buttons;
681 }
682 $.each(callbacks, function(label, callback) {
683 settings.buttons[label] = function() {
684 callback.call(dialog);
685 dialog.dialog('close');
686 };
687 });
688 dialog = $('<div class="crm-container crm-confirm-dialog"></div>')
689 .html(options.message)
690 .appendTo('body')
691 .dialog(settings);
692 return dialog;
693 };
694
695 /**
696 * Sets an error message
697 * If called for a form item, title and removal condition will be handled automatically
698 */
699 $.fn.crmError = function(text, title, options) {
700 title = title || '';
701 text = text || '';
702 options = options || {};
703
704 var extra = {
705 expires: 0
706 };
707 if ($(this).length) {
708 if (title == '') {
709 var label = $('label[for="' + $(this).attr('name') + '"], label[for="' + $(this).attr('id') + '"]').not('[generated=true]');
710 if (label.length) {
711 label.addClass('crm-error');
712 var $label = label.clone();
713 if (text == '' && $('.crm-marker', $label).length > 0) {
714 text = $('.crm-marker', $label).attr('title');
715 }
716 $('.crm-marker', $label).remove();
717 title = $label.text();
718 }
719 }
720 $(this).addClass('error');
721 }
722 var msg = CRM.alert(text, title, 'error', $.extend(extra, options));
723 if ($(this).length) {
724 var ele = $(this);
725 setTimeout(function() {ele.one('change', function() {
726 msg && msg.close && msg.close();
727 ele.removeClass('error');
728 label.removeClass('crm-error');
729 });}, 1000);
730 }
731 return msg;
732 };
733
734 // Display system alerts through js notifications
735 function messagesFromMarkup() {
736 $('div.messages:visible', this).not('.help').not('.no-popup').each(function() {
737 var text, title = '';
738 $(this).removeClass('status messages');
739 var type = $(this).attr('class').split(' ')[0] || 'alert';
740 type = type.replace('crm-', '');
741 $('.icon', this).remove();
742 if ($('.msg-text', this).length > 0) {
743 text = $('.msg-text', this).html();
744 title = $('.msg-title', this).html();
745 }
746 else {
747 text = $(this).html();
748 }
749 var options = $(this).data('options') || {};
750 $(this).remove();
751 // Duplicates were already removed server-side
752 options.unique = false;
753 CRM.alert(text, title, type, options);
754 });
755 // Handle qf form errors
756 $('form :input.error', this).one('blur', function() {
757 $('.ui-notify-message.error a.ui-notify-close').click();
758 $(this).removeClass('error');
759 $(this).next('span.crm-error').remove();
760 $('label[for="' + $(this).attr('name') + '"], label[for="' + $(this).attr('id') + '"]')
761 .removeClass('crm-error')
762 .find('.crm-error').removeClass('crm-error');
763 });
764 }
765
766 $(function() {
767 if ($('#crm-notification-container').length) {
768 // Initialize notifications
769 $('#crm-notification-container').notify();
770 messagesFromMarkup.call($('#crm-container'));
771 $('#crm-container').on('crmFormLoad', '*', messagesFromMarkup);
772 }
773 });
774
775 $.fn.crmAccordions = function(speed) {
776 var container = $('#crm-container');
777 if (speed === undefined) {
778 speed = 200;
779 }
780 if ($(this).length > 0) {
781 container = $(this);
782 }
783 if (container.length > 0 && !container.hasClass('crm-accordion-processed')) {
784 // Allow normal clicking of links
785 container.on('click', 'div.crm-accordion-header a', function (e) {
786 e.stopPropagation && e.stopPropagation();
787 });
788 container.on('click', '.crm-accordion-header, .crm-collapsible .collapsible-title', function () {
789 if ($(this).parent().hasClass('collapsed')) {
790 $(this).next().css('display', 'none').slideDown(speed);
791 }
792 else {
793 $(this).next().css('display', 'block').slideUp(speed);
794 }
795 $(this).parent().toggleClass('collapsed');
796 return false;
797 });
798 container.addClass('crm-accordion-processed');
799 }
800 };
801 $.fn.crmAccordionToggle = function(speed) {
802 $(this).each(function() {
803 if ($(this).hasClass('collapsed')) {
804 $('.crm-accordion-body', this).first().css('display', 'none').slideDown(speed);
805 }
806 else {
807 $('.crm-accordion-body', this).first().css('display', 'block').slideUp(speed);
808 }
809 $(this).toggleClass('collapsed');
810 });
811 };
812 })(jQuery);