/**
* 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);