Entire website is fully responsive and viewable on any type of device including desktop, tablet, mobile phones, and even TV browsers.
Heavy javascript infrastructure using depency injection with require.js; allows for clean organized javascript, with a "load what you need" type methodology. The below is responsible for setting datatables up, what is unique about it is, the user has the ability to select which columns they would like to display, and the datatables will dynamically gather the data and present it according to how the user wants to view it. Data sent and received through a homegrown API system allowing for robust calls. This is just a fraction of the code necessary to make the website dance, but it is a decent example of how some of the pages are organized.
define(['events', 'datatables', 'ajaxHandlers', 'ajaxObj', 'universalEvents', 'printPreview', 'displayTableExcalibur', 'tableTools', 'tableRowClick', 'adminHelpers', 'blockui', 'turkHelpers', 'FastMarkerOverlay', 'modals', 'helpers', 'searchHelper', 'settings'], function(events, datatables, ajaxHandlers, ajaxObj, universalEvents, printPreview, displayTableExcalibur, tableTools, tableRowClick, adminHelpers, blockui, turkHelpers, FastMarkerOverlay, modals, helpers, searchHelper, settings) { window.searchResults = []; // window.searchResults; // saves Returned data obj. Better to pass this around than keep global in the future. var inputRanges = $('#inputRanges input'); var dataTableMain; var columnDecision = []; var columnDefs = []; var isRegistered = helpers.isUserRegistered(); // we wont need this in the future, because we are doing registered only for this section. var isMember = helpers.isUserRegistered(); var uiUpdate = function() { $('#listingAmount').html(window.searchResults.explanation); //console.log(window.searchResults.query); // Displays the users search criteria in plain english above the table and map. searchHelper.displayRecapResults(window.searchResults); // closed advanced search box so user focuses on the results. $('#js-searchCriteriaContainer').hide(); $('#js-searchColumnsContainer').hide(); } var createColumnArray = function() { columnDecision = []; columnDefs = []; columnCounter = 0; // clear columns, clear tbody. $('#dataTableMain thead tr, #dataTableMain tfoot tr').empty(); if ($('#dataTableMain tbody')) { $('#dataTableMain tbody').remove(); } // the first column will always be the same, and not give the user of choice. // create detail column, not able to get the first column to read by itself. // Maybe we can add the data ID to the entire column Row and than if you click on the column it gives the Detail? /*var col1 = {}; col1["targets"] = 0; // should always be 0 here. col1["data"] = "realForeclosureID"; col1["render"] = function ( data, type, full, meta ) { // for data perhaps I need the auction tumbler id? return ''; }; col1["orderable"] = false; // add the column to your arrays and ui. columnDefs.push(col1); columnDecision.push({data:"realForeclosureID"}); columnCounter++; $('#dataTableMain thead tr, #dataTableMain tfoot tr').append("Save ");*/ var col1 = {}; col1["targets"] = 0; // should always be 0 here. col1["data"] = { "realForeclosureID": "realForeclosureID", "googleLat": "googleLat", "googleLong": "googleLong", }; col1["render"] = function(data, type, full, meta) { // for data perhaps I need the auction tumbler id? // return ''; }; col1["orderable"] = false; // add the column to your arrays and ui. columnDefs.push(col1); columnDecision.push({ data: { "googleLat": "googleLat", "googleLong": "googleLong", "realForeclosureID": "realForeclosureID" } }); columnCounter++; $('#dataTableMain thead tr, #dataTableMain tfoot tr').append("Click for detail "); // add all of the columns that are checked to the columns of datatables. $('#js-columns label').each(function() { var input = $(this).find('input'); // only include checkboxes that are checked and do NOT have class of exclude if (input.is(':checked') && !input.hasClass('exclude')) { // add too array for the columns. var obj = {}; obj["data"] = input.data('id'); columnDecision.push(obj); // create custom columnDefs if URL var columnDefObj = {}; // Paint URL 2 different ways based on checkbox. if (input.data('type') == "url") { columnDefObj["targets"] = columnCounter; columnDefObj["data"] = input.data('id'); // Checks to see if the user wanted to display actual url or just "Link" if($('#urlDisplay').is(':checked')){ columnDefObj["render"] = function(data, type, full, meta) { return '' + data + ''; }; }else{ // This means we want to just display "url" in the column columnDefObj["render"] = function(data, type, full, meta) { return 'Link'; }; } // add to definitions. columnDefs.push(columnDefObj); } // Case in For Notes. Sets a special columnDef for width. if (obj["data"] == "Notes") { columnDefObj["targets"] = columnCounter; columnDefObj["width"] = "500px"; columnDefs.push(columnDefObj); } // add to head and footer $('#dataTableMain thead tr, #dataTableMain tfoot tr').append("" + $(this).text().trim() + " "); columnCounter++; } }); }; // end createColumnArray var createDataTable = function() { // reset table in case it already exists if ($.fn.dataTable.isDataTable('#dataTableMain')) { dataTableMain.destroy(); } // create column Array, allows user to specify which columns to create createColumnArray(); //console.log(columnDecision) //console.log(columnDefs) // initialize table columns dataTableMain = $('#dataTableMain').DataTable({ // apparently used for TableTools initialization, but no idea dom: 'T<"clear">lfrtip', // uses json data data: window.searchResults.listings, // defines my column "columns": columnDecision, // define column Definitions "columnDefs": columnDefs, // allows for default of 50 "aLengthMenu": [ [10, 20, 50, 100], [10, 20, 50, 100] ], "iDisplayLength": 30, // Allows for callback on the row information. "fnRowCallback": function(nRow, aData, iDisplayIndex, iDisplayIndexFull) { if (aData.Watching == "1") { $(nRow).addClass('row-saved'); } if (aData.Exclude == "1") { $(nRow).addClass('row-grey'); $(nRow).removeClass('row-saved'); // Exclude trumps saved } }, }); var tableTools = new $.fn.dataTable.TableTools(dataTableMain, { "buttons": [ "copy", "csv", "xls", "pdf", { "type": "print", "buttonText": "Print me!" } ] }); // Remove sorting on the first Row Save. $('#dataTableMain thead tr th').eq(0).removeClass('sorting_asc'); $('.js-showTable').show(); }; // end data table creation var fetchListingInfo = function() { var obj = getMapInputs(); $.blockUI({ css: { backgroundColor: '#7E7E7E', color: '#fff' }, message: 'Searching
' }); $.post("/ajax.php", obj).done(function(data) { //$('.error').html(data); // Formates the response object and makes the data more UI friendly by turning numbers into dollars etc... window.searchResults = searchHelper.formatSearchData($.parseJSON(data)); console.log(window.searchResults); // creates entire table with the data createDataTable(); // updates listing count, and current query textbox. uiUpdate(); $.unblockUI(); }).always(function(){ $.unblockUI(); }); }; // Gathers all of the inputs and checkboxes for API call. var getMapInputs = function() { var noAddress = 0, watching = 0, dead = 0, condos = 0, canceledListings=0; // Additional options if ($('#plaintifCheck').is(':checked')) { noAddress = 1; } if ($('#canceledListings').is(':checked')) { canceledListings = 1; } if ($('#watching').is(':checked')) { watching = 1; } if ($('#dead').is(':checked')) { dead = 1; } if ($('#Condos').is(':checked')) { condos = 1; } var county = $('#countySelect').val(); var countyName = $("#countySelect option:selected").text(); // I would change this object to be more organized but that would require altering API and I really dont // feel like updating all of that shit. var fieldValuesObj = { noAddress: noAddress, canceledListings:canceledListings, Watching: watching, Dead: dead, Condos: condos, county: county, countyName: countyName, operation: 'searchListings', // specific for ajax call. //savedSearchType: 'table-view', // The saved search needs to know what view it is coming from, searches will only work for table view or map view specific because they are different. }; // Input Ranges, does not include the checkboxes $('.js-searchCriteria').each(function() { // if the input does not have class exclude, and value is not blank than... if (!$(this).hasClass('exclude') && $(this).val() !== "") { fieldValuesObj[$(this).attr('id')] = $(this).val().replace("$", "").replace(",", ""); } else { fieldValuesObj[$(this).attr('id')] = ""; } }); return fieldValuesObj; }; // ************** FETCHING ACCOUNT SETTINGS ************ // Fetches for all counties. // Or should we make a decision which information to display ahead of time? // Seems like a better idea to figure out the selected county before anything else. var getCountyDecisions = function() { return settings.getCountyDecisions().done(function(data) { $('.error').html(data); //console.log(data); }); }; // personal account preferences. gets preferred county. var getAccountPreferences = function() { return settings.getAccountPreferences().done(function(data) { $('.error').html(data); //console.log(data); }); }; // Goes to counties table and sees if this one is online. var getActiveCounties = function() { return ajaxHandlers.getActiveCounties().done(function(data) {}); }; var initiateUI = function() { // block until everything is ready. $.blockUI({ css: { backgroundColor: '#7E7E7E', color: '#fff' }, message: 'Preparing
' }); if (isRegistered) { $.when(getActiveCounties(), getAccountPreferences(), getCountyDecisions()).done(function(activeCounties, preferences, countyDecisions) { searchHelper.createCountyDropDown(activeCounties, preferences); // User preferences, ALSO includes the saved searches. window.preferences = preferences; // so we can use these preferences from anywhere. //console.log(window.preferences.queryResult); //savedSearches.parseSavedSearches(); // display Saved Searches if they exist. All logic will be done inside that function. //savedSearches.loadSavedSearchDropdown(); window.countyDecisions = countyDecisions.countyDecisionsResponse; // save this for later use. // Set these values to a global object so they can be re-used for in case the user changes county drop down at the top. window.selectedCountyDecision = searchHelper.figureOutDefaultCounty(activeCounties.queryResult, preferences.queryResult, window.countyDecisions); // This function should only have to take in countyDecisionsResponse. The logic for what is default should not be in there. searchHelper.displayProperColumns(window.selectedCountyDecision); $.unblockUI(); }); } else { $.when(getActiveCounties(), getCountyDecisions()).done(function(activeCounties, countyDecisions) { searchHelper.createCountyDropDown(activeCounties, null); window.preferences = false; window.countyDecisions = countyDecisions.countyDecisionsResponse; // figures out the default. window.selectedCountyDecision = searchHelper.figureOutDefaultCounty(activeCounties.queryResult, null, window.countyDecisions); searchHelper.displayProperColumns(window.selectedCountyDecision); $.unblockUI(); }); } }; // end InitiateUI // ************** END MAP FUNCTIONS ************** // Once user hits the main search button this is fired. // Also used as a means to hold off the search until SAVED search is taken care of. var continueWithSearch = function() { fetchListingInfo(); // make ajax call }; // add blue event to the inputs $('#inputRanges input').on('focus', function() { if (!$(this).data('placeholder')) { $(this).data('placeholder', $(this).attr('placeholder')); } $(this).attr('placeholder', ""); }).on('focusout', function() { if ($(this).val() == "") { $(this).attr('placeholder', $(this).data('placeholder')); } }); var columnLabels = $('#js-columns label'); var defaultPast = []; var defaultFuture = []; // click event for default past data columns, and future data columns $('#defaultPastColumns').on('click', function() { columnLabels.find('input').removeAttr('checked'); $('#propertyAddressStreetC').prop('checked', true); // NOTE: .attr, did not work but .prop did. WEIRD!! $('#propertyAddressCityC').prop('checked', true); $('#assessedValueC').prop('checked', true); $('#auctionStatusC').prop('checked', true); $('#estimateValue_2C').prop('checked', true); $('#winningAmountC').prop('checked', true); $('#soldDateC').prop('checked', true); }); $('#defaultFutureColumns').on('click', function() { columnLabels.find('input').removeAttr('checked'); $('#propertyAddressStreetC').prop('checked', true); // NOTE: .attr, did not work but .prop did. WEIRD!! $('#propertyAddressCityC').prop('checked', true); $('#assessedValueC').prop('checked', true); $('#finalJudgementAmountC').prop('checked', true); $('#sqftInteriorValueC').prop('checked', true); $('#auctionDateC').prop('checked', true); }); // POP OVER HELPERS $('#inputRangeHelp').popover({ animation: true, content: 'TIP: If a listing has a missing piece of data such as "Bedrooms", it may not display if a Bedroom number is set for a search. Start with broad searches first, and than if there are too many listings continue to add search criteria.', placement: 'left', trigger: 'click focus', viewport: { "selector": "body", "padding": 0 } }); // Adds Datepicker to Find listings date selection inputs $(".datePicker").datepicker(); // When user selects which county they are, will change which columns are allowed. $('#countySelect').on('change', function() { // re-run the column Decisions hide show // get county Divisions for that particular county. var countyId = $('#countySelect').val(); var countyDecisionsObj = searchHelper.returnsingleCountyDecisionInfo(window.countyDecisions, countyId); // returns the decisions for 1 county. searchHelper.displayProperColumns(countyDecisionsObj); }); initiateUI(); // gets the preferred County, later on could get more preferences. // adds click event to the SEARCH button $('#getListingInfo').on('click', function() { // Add in Save search functionality as a predecessor to getListingInfo // If saved search is checked, we need to bring up a modal window with save search functionality. // YOU WERE AGAIN NOT ABLE TO COMPLETE SAVED SEARCHES DUE TO ITS MASS COMPLEXITY. /*if ($('#savedSearchCheckbox').is(':checked')) { savedSearches.openModal(); } else { continueWithSearch(); }*/ if (helpers.isMember()) { continueWithSearch(); } else { // get proper amount from database $.when(getAccountPreferences()).done(function(preferences) { var searchAmount = Number(preferences.queryResult.searchAmountLeft); if(searchAmount>0){ // if they have more than 0 searches left. continueWithSearch(); // initiates the search as normal. // Update the search limit to be less for next time. // reduce the amount of their searches by 1. var newSearchAmount = searchAmount-1; var obj = { operation: 'genericAtRequestRegistered', apiClassName: 'Usersettings', functionName: 'updateAccountPreferences', searchAmountLeft: newSearchAmount, }; // saves account preference such as count, Object {apiClassName: "usersettings", operation: "updateAccountPreferences", preferredCounty: "97"} ajaxHandlers.genericAtRequest(obj).done(function(data) { //$('.error').html(data); //console.log(data); }).always(function() {}); } else{ // they are not a member and do not have any searches left. modals.openSearchLimitModal(); } }); // end $.when }// end else }); // END CLICK EVENT FOR MAIN SEARCH BUTTON }); // END require