/** * ContentONE Form Utilities * * Provides common functionality for forms. * * @version 1.0 * @date August 11, 2011 * @copyright (c) World Web Management Services (http://www.worldwebms.com/) */ (function($) { /** * Create C1 jQuery extension. */ if( !$.fn.c1 ) { $.fn.c1 = function( name, options ) { if( this.c1[name] ) return this.c1[name].call( this, options ); return this; } } /** * Create C1 Lightbox jQuery extension. */ $.fn.c1.form = function( options ) { var t = $(this); // Add general options options = $.extend( { 'clean': '', // Selector choosing fields that should have special characters converted 'cleantype': 'default', 'cleanmap': { }, 'placeholder': 'input[title], textarea[title], input[placeholder], textarea[placeholder]' }, options ); var field_filter = ':not(:checkbox, :radio)'; // Add legacy support for default property if( options['default'] ) options.placeholder = options['default']; /*** Removing unwanted characters -------------------------------- ***/ // Replaces common unicode characters if( options.cleantype == 'default' ) { options.cleanmap = { '[\u2018\u2019]': '\'', // curly apostrophes '[\u201C\u201D]': '"', // curly double quotes '[\u2026]': '...' // ellipsis }; // Enforce SMS content } else if( options.cleantype == 'sms' ) { options.cleanmap = { '[\u2018\u2019]': '\'', // curly apostrophes '[\u201C\u201D]': '"', // curly double quotes '[\u2026]': '...', // ellipsis '[^a-zA-Z0-9 !@#$%&*()\\-_+=:;\'",<.>/?]': '?' // any non-sms character }; } // Cleans out any special characters from the field function clean() { var val = $(this).val(); $.each( options.cleanmap, function( regex ) { val = val.replace( new RegExp( regex, 'g' ), this ); } ); $(this).val( val ).change(); } // If fields should be cleaned if( options.clean ) { $(options.clean, this).filter(field_filter).blur( clean ).each( function() { submit = true; return false; } ); } /*** Placeholders ---------------------------------------------- ***/ // If placeholders should be shown var submit = false; if( options.placeholder ) { // Add placeholder attributes on all fields if not provided $(options.placeholder, this).filter(field_filter).each(function() { if ($(this).attr('placeholder') == '') { $(this).attr('placeholder', $(this).attr('title')); if ($(this).val() == $(this).attr('placeholder') || $(this).val() == $(this).attr('title')) $(this).val(''); } }); // If placeholder attribute is not supported then perform fail over if (!('placeholder' in document.createElement('input'))) { options.removePlaceholder = true; $(options.placeholder, this) .filter(field_filter) .focus( function() { if( $(this).is( 'select' ) ) $(this).removeClass( 'empty' ); else if( $(this).val() == $(this).attr( 'placeholder' ) ) $(this).removeClass( 'empty' ).val( $(this).hasClass( 'use-title' ) ? $(this).attr( 'placeholder' ) : '' ); } ) .blur( function() { if( $(this).is( 'select' ) && $(this).val() == '' ) $(this).addClass( 'empty' ); else if( $(this).val() == '' || $(this).val() == $(this).attr( 'placeholder' ) ) $(this).addClass( 'empty' ).val( $(this).attr( 'placeholder' ) ); } ) .change( function() { $(this)[ $(this).val() == $(this).attr( 'placeholder' ) ? 'addClass' : 'removeClass' ]( 'empty' ); } ) .each( function() { submit = true; $(this).blur(); } ); } } /*** Dependencies ------------------------------------------------ ***/ // Attach all dependencies to the original fields if( options.dependencies ) { var t = $(this); // Returns a field by name function getField( name, checked ) { return t.find( 'input[type="text"][name="' + name + '"], input[type="text"][name^="' + name + '["], ' + 'input[type="radio"][name^="' + name + '"]' + ( checked ? ':checked' : '' ) + ', ' + 'input[type="checkbox"][name^="' + name + '"]' + ( checked ? ':checked' : '' ) + ', ' + 'select[name="' + name + '"], select[name^="' + name + '["], ' + 'textarea[name="' + name + '"], textarea[name^="' + name + '["]' ); } // Returns the value for a specific field either from the form or the values array function getFieldValue( name, checked ) { if( options.values && options.values[name] ) return options.values[name]; return getField( name, checked ).val(); } // Returns the field name from a dependency key function getFieldName( name ) { var bits = name.split( ' ' ); return bits[0]; } // Returns the field operator from the dependency key function getFieldOperator( name ) { var bits = name.split( ' ' ); return bits.length == 1 ? '=' : bits[1]; } // Determines if a field or section is visible based on the dependency function isVisible( config ) { // Process multiple conditions if( config[0] ) { var show = false; $.each( config, function( n, values ) { if( isVisible( values ) ) { show = true; return false; } } ); return show; } // Determine dependencies var show = true; $.each( config, function( n, values ) { var value = getFieldValue( getFieldName( n ), true ); var operator = getFieldOperator( n ); if( operator == '=' && $.inArray( value, values ) < 0 || operator == '!=' && ( value == null || $.inArray( value, values ) >= 0 ) ) { show = false; return false; } } ); return show; } // Shows or hides fields according to dependencies function refresh() { // Show or hide fields $.each( options.dependencies, function( name, config ) { var prefix = name.substr( 0, 1 ); var el = ( prefix === '#' || prefix === '.' ) ? t.find( name ) : getField( name ).closest( '.field' ); el[ isVisible( config ) ? 'show' : 'hide' ](); } ); } // Sets up the dependency events var fields = {}; function initDependency( name, config ) { $.each( config, function( field, values ) { var field_name = getFieldName( field ); if( fields[field_name] != true ) { fields[field_name] = true; getField( field_name ).change( function() { refresh(); } ); } } ); } // Set up dependency events $.each( options.dependencies, function( name, config ) { if( config[0] ) { $.each( config, function( n, values ) { initDependency( name, values ); } ); } else { initDependency( name, config ); } } ); // Update dependencies refresh(); } /*** Form Submission --------------------------------------------- ***/ // Add submit function if( submit ) { var form = $(this).is( 'form' ) ? $(this) : $(this).closest( 'form' ); form.submit( function() { // Remove default values if( options.removePlaceholder ) { $(options.placeholder, this) .filter(field_filter) .each( function() { if( $(this).val() == $(this).attr( 'title' ) ) $(this).val( '' ); } ); } // Convert values if( options.convert ) $(options.convert, this).each( convert ); } ); } return this; } /** * Country drop down selection. */ $.fn.c1.country = function( options ) { var t = $(this); // Add default values if( options == null ) options = { }; options = $.extend( { 'country': 'select[name="country"]', 'state': 'input[name="state"], select[name="state"]' }, options ); // Update the states when a country changes var field = t.find( options.country ).change( function() { var code = $(this).val(); $.fn.c1.country.load( code, function( country, details ) { // If the country has changed since the response do nothing if( code != country ) return; // Rebuild as a drop down var existing = t.find( options.state ); var field = $(''); field.attr( 'name', existing.attr( 'name' ) ); $.each( details.states, function( code, name ) { var option = $('').val( code ).text( name ); field.append( option ); } ); // If there are no state options, display an input field instead if( field.find( 'option' ).length <= 1 && field.find( 'option:first' ).val() == '' ) field = $('').attr( 'name', existing.attr( 'name' ) ); // Show the new field field.val( existing.val() ); existing.replaceWith( field ); // Adjust field labels if( options.fields ) { // Show or hide required flags $.each( details.required, function( k, v ) { if( options.fields[k] ) $(options.fields[k]).closest( 'tr, li' ).find( 'label.label em' )[ v ? 'show' : 'hide'](); } ); // Update field labels $.each( details.labels, function( k, v ) { if( options.fields[k] ) { var field = $(options.fields[k]).closest( 'tr, li' ).find( 'label.label' ) var em = field.find( 'em' ); field.text( v + ': ' ); field.append( em ); } } ); } } ); } ).change(); return this; }; /** * Local cache of states. */ $.fn.c1.country.load = function( code, callback ) { if( $.fn.c1.country._countries[code] ) { callback.call( window, code, $.fn.c1.country._countries[code] ); return true; } $.c1.api( { 'module': 'system', 'controller': 'tools', 'method': 'country', 'data': { 'country': code, 'blank': '' }, 'success': function( response ) { $.fn.c1.country._countries[code] = response; callback.call( window, code, $.fn.c1.country._countries[code] ); } }); }; $.fn.c1.country._countries = { }; /** * Grid control. */ $.fn.c1.grid = function( options ) { var t = $(this); // Update the display of the table function update() { t.find('> thead')[ t.find('> tbody > tr').length > 1 ? 'show' : 'hide' ](); } // Add row count if( !options.count ) { options.count = $('tr').length + 1; } // Add add functionality t.find('> tfoot button').click(function() { var template = $(this).closest('table').find('> tbody > tr.grid-template'); var row = template.clone(true).removeClass('grid-template').attr('id', ''); row.find('input, select, textarea').each(function() { $(this).attr('name', $(this).attr('name').replace(options.name + '[-1]', options.name + '[' + options.count + ']')); }); // Re-initialise date pickers var dates = row.find('.ui-date'); if (dates.datepicker) { dates.each(function() { $(this).datepicker('destroy').attr('id', '').datepicker({'dateFormat': 'dd-M-yy', 'changeYear': true}); }); } // Re-initialise browse fields var controls = row.find('.ui-input-object'); if (controls.c1browse) { controls.each(function() { var t = $(this); var options = t.c1browse('cloneOptions'); var field = t.find('input[type="hidden"]').clone(); var parent = t.parent(); var span = $(''); span.append(field); t.replaceWith(span); span.c1browse(options); }); } template.before(row); options.count++; update(); return false; }); // Add remove functionality t.find('> tbody > tr > td > a.grid-remove').click(function() { $(this).closest('tr').remove(); update(); return false; }); // Add sortable functionality t.find('> tbody').sortable( { 'axis': 'y', 'items': '> tr' } ); // Update display update(); return this; }; /** * Ranking tool. */ $.fn.c1.ranking = function( options ) { var t = $(this); options = $.extend( { 'items': '.ranking-option' } ); t.find( options.items ).css( 'cursor', 'pointer' ); t.sortable( { 'items': options.items, 'placeholder': 'ranking-placeholder', 'start': function( event, ui ) { ui.item.css( 'cursor', 'move' ); }, 'stop': function( event, ui ) { ui.item.css( 'cursor', 'pointer' ); }, 'update': function( event, ui ) { var position = 1; t.find(options.items).each(function() { $('select', this).val( position++ ); } ); } } ); return this; }; /** * Suburb autocomplete. */ $.fn.c1.suburb = function( options ) { var t = $(this); // If the options is a command if( typeof options == 'string' ) { // Manually invoke the search if( options == 'search' ) return t.autocomplete('search'); return this; } // Add default options options = $.extend( { 'country': 'select[name="country"]', 'state': 'input[name="state"], select[name="state"]', 'postcode': 'input[name="postcode"]' }, options ); // Determine options var autocomplete = { 'minLength': 2, 'source': function( request, response ) { // Determine search conditions var data = { 'term': request.term }; // Determine what countries and states to limit to var country = $(options.country).val(); var states = []; $(options.state).find('option').each(function() { states.push($(this).val()); }); // Use default if provided if (options.limitTo && !country && states.length == 0) { if (options.limitTo.country) { country = options.limitTo.country; } if (options.limitTo.states) { states = options.limitTo.states; } } // Send countries and states to API if (country) data.country = country; if (states.length > 0) data.states = states; // Send the request $.c1.api( { 'module': 'system', 'controller': 'tools', 'method': 'suburb', 'data': data, 'success': function( data ) { if( data.length == 0 && options.empty ) data = options.empty; response( data ); } } ); }, 'select': function( event, ui ) { if( ui.item ) { // If an item is selected update the post code, country and state var form = t.closest( 'form' ); var postcode = form.find( options.postcode ).val( ui.item.postcode ); var country = form.find( options.country ).val( ui.item.country ); var state = form.find( options.state ).val( ui.item.state ); // If the state is a select and the option does not exist then add the option if( state.val() != ui.item.state ) state.append( $('').text( ui.item.state ).val( ui.item.state ) ).val( ui.item.state ); // Force change events on all fields postcode.change(); country.change(); // Return another value if specified if( options.value ) { ui.item.value = options.value; $.each( ui.item, function( name, value ) { ui.item.value = ui.item.value.replace(name, value); }); } } } }; if( options.change ) autocomplete.change = options.change; // Auto complete the suburb t.autocomplete( autocomplete ); return this; }; })(jQuery);