jQuery.noConflict(); /** * mutli-tree * @author Josh Ribakoff * @copyright Integrated Net Solutions, LLC */ (function($) { var MultiTree = function( element ) { /** * Encapsulate CSS class literals */ var classes = { multiTree: 'multiTree', ajaxUrl: 'multiTree-ajaxUrl', multiTreeAdd: 'multiTree-Add', selections: 'multiTree-selections', newSelections: 'multiTree-new-selections', deletedSelections: 'multiTree-deleted-selections', selection: 'multiTree-selection', selectContainer: 'multiTree-selectContainer', levelName: 'multiTree-levelName', closeLink: 'multiTree-closeLink', elementName: 'multiTree-elementName', more: 'more', noMore: 'noMore', seperator: 'multiTree-selection-seperator' }; /** * Get the name of the level for a select element * * @param jquery select * @return string */ var getLevelName = function( select ) { return select.nextAll( '.' + classes.levelName + ':first' ).text(); }; /** * Get the child ( dependant ) selects for a select element * * @param jquery select * @return jquery collection of select elements */ var getNextSelects = function( select ) { return select.parent().nextAll( '.' + classes.selectContainer ).find( 'select' ); }; /** * Load into a select a collection of options * * @param jquery select to load into * @param jquery collection of options, to contain: * id - the primary key id of this option within it's level * title - the title of this option * more - boolean, wether or not this option has child options in the descendant level */ var loadInto = function( select, data ) { // add the option, if it has more children then apply the correct class name select.html( data ); // show the select select.parent().css( 'display', '' ); }; /** * Hide a select */ var hideSelect = function( select ) { select.find( 'option:selected' ).attr( 'selected', '' ); // make sure it registers as unselected for the value, clearing the html is not sufficient apparently. select.html('').parent().css( 'display', 'none' ); }; /** * Show a select */ var showSelect = function( select ) { select.parent().css( 'display', 'none' ); }; /** * Callback for when an option is changed, will load in children if appropriate, * if this is the last level in the current tree, will return false * If children may be present, initates an ajax request with * callback to loadInto() to load the child options * * @param jquery select object that has been changed * @return boolean wether or not a request to load children was initiated */ var selectChange = function( select ) { if( select.find( 'option:selected' ).hasClass( '.' + classes.noMore ) ) { return false; } var parentId = select.val(); var selectContainer = select.parent(); // clear out & hide the boxes we might need to load into $.each( getNextSelects( select ), function( i, select ) { hideSelect( $(select) ); }); // get the container & element for the child level var childContainer = selectContainer.next( '.' + classes.selectContainer ); var childSelect = childContainer.find( 'select' ); // if this is the final box, no need to do any further lazy loading if( childSelect.length === 0 ) { return false; } // request the data and load it into the child level's select var levelName = getLevelName( select ); $.get( ajaxUrl, { 'level' : levelName, 'parent_id' : parentId }, function( data ) { loadInto( childSelect, data ); } ); return true; }; var remove = function( option ) { var value = option.find( '.multiTree-value' ).text(); var tree = option.parents( '.multiTree' ); var newDiv = tree.find( '.' + classes.newSelections ); var newElem = newDiv.find( "input[value='" + value + "']" ); newElem.remove(); var deletedDiv = tree.find( '.' + classes.deletedSelections ); deletedDiv.append( '' ); option.remove(); }; /** * Callback for removing selections */ var reBindRemoveEvents = function() { $( '.' + classes.closeLink ).click( function() { remove( $( this ).parent() ); return false; }); }; /** * Get the right most select that has a value * @param jquery collection of selects */ var getCurrentLevelSelect = function( selects ) { var selected; selects = $.makeArray( selects ); $.each( selects.reverse(), function() { if( $(this).val() !== null ) { selected = $(this); return false; } }); return selected; }; /** * iterate in reverse order and find value and * level name of right most level that has a selection. */ var getSelected = function( selects ) { var array = new Array(); var selected = getCurrentLevelSelect( selects ); var selectedOptions = selected.find( 'option:selected'); selectedOptions.each( function() { var selectedOption = $(this); var obj = {}; obj.id = selectedOption.val(); obj.level = getLevelName( selected ); // build up the path string var path = []; $.each( selects, function() { var level = getLevelName( $(this) ); if( level == 'year' ) { var text = selectedOption.text(); } else { var text = $(this).find( 'option:selected').text(); } if( text.length ) { path.push( text ); } if( level == obj.level ){ return false; } }); obj.path = path; array.push( obj ); }); return array; }; var collapseChildrenOf = function( select ) { var selects = $.makeArray( getNextSelects( select ) ); $.each( selects, function( i, select ) { hideSelect( $( select ) ); // null out last value so a subsequent click will load children last = null; }); }; var getElementName = function( tree ) { return treeName; }; /** * Callback for when the add button is pressed * find the id of the selected option and the name of the level * the selected option belongs to * * Will iterate all the selects in the current tree to find the deepest level * that has a selection, aka it finds the selected option from the level closest * to the "leaf" level. aka rightermost select on the screen. * * @param jquery collection - the current tree * @param jquery selected - the node to add */ var add = function( tree, selections ) { $.each( selections, function( i ) { var selected = this; var selects = $.makeArray( tree.find( '.' + classes.selectContainer + ' select' ) ); // all the selects in the current tree var value = ''; var level = ''; var parent = $( selects[0] ).parents( '.' + classes.multiTree ); var selections = parent.find( '.' + classes.selections ); var newSelections = parent.find( '.' + classes.newSelections ); var append = ''; var hiddenVal = selected.level + '-' + selected.id; append += '
'; append += ' '; append += ''; append += '[ x ]'; append += selected.path.join( '»' ); append += '
'; newSelections.append( '' ); selections.prepend( append ); }); reBindRemoveEvents(); }; var ajaxUrl; var data = $( element ).metadata(); ajaxUrl = data.ajaxUrl; var treeName = data.elementName; /** * Select click event */ $(element).find( 'select' ).click( function() { $(this).parents( '.' + classes.multiTree ).find( '.' + classes.multiTreeAdd ).attr( 'disabled', '' ); collapseChildrenOf( $( this ) ); // if clicking on node already expanded if( element.last === $(this).val() ) { return; } element.last = $(this).val(); return selectChange( $(this) ); }); /** * Add button event */ $(element).find( '.' + classes.multiTreeAdd ).click( function() { $(this).attr( 'disabled', 'disabled' ); var selects = $(element).find( 'select' ); add( $(element), getSelected( $(element).find( 'select' ) ) ); }); reBindRemoveEvents(); }; $.fn.multiTree = function() { this.each( function() { var multiTree = new MultiTree( this ); }); }; // apply the plugin to matching DOM elements $( document ).ready( function() { $( '.multiTree' ).multiTree(); }); })(jQuery); jQuery(document).ready( function( $ ) { function hideLevelsWithoutValues() { var select = jQuery('#modelSelect'); if( !selectHasValue( select ) ) { } var select = jQuery('#optionSelect'); if( !selectHasValue( select ) ) { } var select = jQuery('#yearSelect'); if( !selectHasValue( select ) ) { } } function selectHasValue( select ) { var hasValue = false; select.find('option').each( function() { if( '' != jQuery(this).val() && 0 != jQuery(this).val() ) { hasValue = true; } }); return hasValue; } loadModels = function() { hideLevelsWithoutValues(); var make = jQuery('#makeSelect').val(); var url = 'http://www.boyesen.com/vafAjax.php?'; url = url + '&front=1'; url = url + '&make=' + make; var loadingText = ''; jQuery('#modelSelect') .html( loadingText ) .load( url, {}, function(){ var select = jQuery(this); select .show() .attr('disabled',''); var label = select.prev('label'); label.show(); if( jQuery('#modelSelect option' ).length == 1 ) { loadOptions(); } hideLevelsWithoutValues(); jQuery(this).trigger('vafLevelLoaded'); }); jQuery('#optionSelect').html(''); jQuery('#yearSelect').html(''); } loadOptions = function() { hideLevelsWithoutValues(); var model = jQuery('#modelSelect').val(); var url = 'http://www.boyesen.com/vafAjax.php?'; url = url + '&front=1'; url = url + '&model=' + model; var loadingText = ''; jQuery('#optionSelect') .html( loadingText ) .load( url, {}, function(){ var select = jQuery(this); select .show() .attr('disabled',''); var label = select.prev('label'); label.show(); if( jQuery('#optionSelect option' ).length == 1 ) { loadYears(); } hideLevelsWithoutValues(); jQuery(this).trigger('vafLevelLoaded'); }); jQuery('#yearSelect').html(''); } loadYears = function() { hideLevelsWithoutValues(); var option = jQuery('#optionSelect').val(); var url = 'http://www.boyesen.com/vafAjax.php?'; url = url + '&front=1'; url = url + '&option=' + option; var loadingText = ''; jQuery('#yearSelect') .html( loadingText ) .load( url, {}, function(){ var select = jQuery(this); select .show() .attr('disabled',''); var label = select.prev('label'); label.show(); hideLevelsWithoutValues(); jQuery(this).trigger('vafLevelLoaded'); }); } submitVafForm = function() { if( jQuery('#categorySelect').val() != '?' ) { jQuery('#vafForm').attr( 'action', jQuery('#categorySelect').val() ); } var chooser = jQuery('#categorySelect'); if( !chooser.is('input') ) { chooser.html(''); } jQuery().trigger("vafSubmit"); return false; } jQuery().bind("vafSubmit", function() { document.vafForm.submit(); }); clearVafForm = function() { window.location = '?make=0&model=0&option=0&year=0'; } toggleVafFits = function() { var val = jQuery( '#universal' ).attr( 'checked' ); if( val == true ) { jQuery( '#vaf-toggle' ).hide(); } else { jQuery( '#vaf-toggle' ).show(); } } jQuery('#makeSelect').change( loadModels ); jQuery('#modelSelect').change( loadOptions ); jQuery('#optionSelect').change( loadYears ); jQuery('#vafSubmit').click( submitVafForm ); jQuery('#vafClear').click( clearVafForm ); jQuery( '.vafCheckAll' ).click( function() { jQuery( '.vafcheck' ).attr( 'checked', jQuery(this).attr( 'checked' ) ); }) jQuery( '#universal' ).click( toggleVafFits ); toggleVafFits(); jQuery( '.vaf-cat-toggler' ).click( function() { var icon = jQuery(this).children( 'div.vaf-toggle-icon ' ); icon.toggleClass( 'vaf-toggle-icon-minus' ); var toggleDiv = jQuery(this).nextAll( 'div.vaf-toggle' ); toggleDiv.toggle(); }); jQuery( 'div.vaf-toggle' ).hide(); jQuery( '.vafDeleteSelected' ).click( function() { jQuery( '.vafcheck:checked' ).each( function() { jQuery( this ).nextAll( '.multiTree-closeLink' ).click(); }); }); hideLevelsWithoutValues(); } );