"use strict"; (function($){ var DevMode = true; var State = new Object; State.devMode = DevMode; State.timers = {}; if(DevMode){ window.AppState = State; } (function(){ $(document).ready(function(){ State.developer = new Object; $('#developer-content').on('click','button[data-dev-fn]',function(e){ var method = $(e.target).closest('button').attr('data-dev-fn'); return $.type(State.developer[method])==='function' ? State.developer[method](e) : _no_function(e,method); }); // Developer Function Definitions BEGIN State.developer['Loading State ON'] = function(e){ e.preventDefault(); State.UI.loadingState = true; } State.developer['Loading State ON'] = function(e){ e.preventDefault(); State.UI.loadingState = true; } State.developer['Loading State OFF'] = function(e){ e.preventDefault(); State.UI.loadingState = false; } State.developer['Initial State ON'] = function(e){ e.preventDefault(); State.UI.initState = true; } State.developer['Initial State OFF'] = function(e){ e.preventDefault(); State.UI.initState = false; } /* State.developer['Modal Mask ON'] = function(e){ e.preventDefault(); State.Mask.show('searching_message'); } State.developer['Modal Mask OFF'] = function(e){ e.preventDefault(); State.Mask.hide(); } State.developer['Results Hider ON'] = function(e){ e.preventDefault(); State.Results.hide_results(); } State.developer['Results Hider OFF'] = function(e){ e.preventDefault(); State.Results.show_results(); } State.developer['Searching Message ON'] = function(e){ e.preventDefault(); State.UI.loadingState = false; State.UI.initState = false; State.Mask.show_modal('searching_message'); } State.developer['Request Form ON'] = function(e){ e.preventDefault(); State.UI.loadingState = false; State.UI.initState = false; State.Request.request_type = 'church-request'; State.Mask.show_modal('church_request'); } State.developer['Request Form OFF'] = function(e){ e.preventDefault(); State.UI.enable_global_ui(); State.Mask.hide_modal(); } State.developer['FMAC Form ON'] = function(e){ e.preventDefault(); State.UI.loadingState = false; State.UI.initState = false; State.Request.request_type = 'fmac'; State.Mask.show_modal('church_request'); } State.developer['FMAC Form OFF'] = function(e){ e.preventDefault(); State.UI.enable_global_ui(); State.Mask.hide_modal(); } State.developer['Test Dev UI'] = function(e){ e.preventDefault(); alert('Developer UI Test Successful'); return true; }; */ State.developer['Searching Message ON'] = function(e){ e.preventDefault(); State.UI.loadingState = false; State.UI.initState = false; State.Mask.show_modal('searching_message'); } State.developer['Searching Message OFF'] = function(e){ e.preventDefault(); State.UI.initState = true; State.Mask.hide_modal(); } State.developer['FMAC Form ON'] = function(e){ e.preventDefault(); State.UI.loadingState = false; State.UI.initState = false; State.Request.request_type = 'fmac'; State.Mask.show_modal('church_request'); } State.developer['FMAC Form OFF'] = function(e){ e.preventDefault(); State.UI.enable_global_ui(); State.Mask.hide_modal(); } State.developer['Request Form OFF'] = function(e){ e.preventDefault(); State.UI.enable_global_ui(); State.Mask.hide_modal(); } State.developer['Request Form ON'] = function(e){ e.preventDefault(); State.UI.loadingState = false; State.UI.initState = false; State.Request.request_type = 'church-request'; State.Mask.show_modal('church_request'); } State.developer['Request Form OFF'] = function(e){ e.preventDefault(); State.UI.enable_global_ui(); State.Mask.hide_modal(); } State.developer['Lock Global UI'] = function(e){ e.preventDefault(); State.UI.disable_global_ui(); } State.developer['Unlock Global UI'] = function(e){ e.preventDefault(); State.UI.enable_global_ui(); } State.developer['Dump State'] = function(e){ e.preventDefault(); console.log({State: State}); } State.developer['Dump Session'] = function(e){ e.preventDefault(); $.ajax({ type: 'POST', dataType: 'json', data: { action: 'church_search', _: { method: 'DumpSession', params: {} } }, //url: State.initOptions.ajax_url, url: State.Options.ajax_url, success: function(){ console.log({session: Array.prototype.slice.call(arguments)}); //console.log({success: Array.prototype.slice.call(arguments)}); }, error: function(){ //console.log({error: Array.prototype.slice.call(arguments)}); } }); } /* State.developer['Test Search'] = function(e){ e.preventDefault(); State.IO.test_search(); } State.developer['Test Query'] = function(e){ e.preventDefault(); State.IO.test_query(); } State.developer['Test Ajax'] = function(e){ e.preventDefault(); State.IO.test_ajax(); } State.developer['Test Google Geolocation'] = function(e){ e.preventDefault(); State.detectedLocation = $.type(google.loader.ClientLocation)==='object' ? new google.maps.LatLng( google.loader.ClientLocation.latitude, google.loader.ClientLocation.longitude ) : null; } State.developer['Test Maxmind Geolocation'] = function(e){ e.preventDefault(); State.IO.test_geoip(); } */ State.developer['Clear Data'] = function(e){ e.preventDefault(); State.IO.destroy_proxy(); } State.developer['Test Proxy'] = function(e){ e.preventDefault(); State.IO.test_proxy(); } /* State.developer['Test Dev Console'] = function(e){ e.preventDefault(); _write_to_console('Test Message'); } */ State.developer['Trigger Map Resize'] = function(e){ e.preventDefault(); google.maps.event.trigger( State.Map.map,'resize'); } State.developer['Debug Result Bounds'] = function(e){ var output = {}; e.preventDefault(); $.each(State.Results.ranges,function(radius,collection){ var ne = collection.bounds.getNorthEast(), sw = collection.bounds.getSouthWest(); output[radius] = { data: collection, empty: collection.bounds.isEmpty(), northEast: { latitude: ne.lat(), longitude: ne.lng() }, southWest: { latitude: sw.lat(), longitude: sw.lng() } } }); console.log( output ); } State.developer['Dump Map Data'] = function(e){ e.preventDefault(); console.log({mapData: State.Map.status}); } State.developer['Toggle Front WMC'] = function(e){ e.preventDefault(); State.UI.toggle_front_wmc(); } // Developer Function Definitions END function _no_function(e,fn){ e.preventDefault(); alert('Developer fn "'+fn+'" is undefined.'); return false; } function _write_to_console( msg ){ //console.log( msg ); $('#developer-content .developer-console ul').append( XTag('li',msg) ); } $.each(State.developer,function(fnName,fnBody){ $('#developer-content .developer-ui ul').append( XTag('li', XTag('button[data-dev-fn="'+fnName+'"]',fnName) ) ); }); }); })(); (function(){ var options = { ajax_url: null, base_url: null, allowFMAC: true, allowRequests: true, autoExpand: true, autoExpandNotification: false, defaultZoomLevel: 4, minZoomLevel: 4, maxZoomLevel: 15, defaultLatitude: 37.6, defaultLongitude: -95.665, //showFMACFormOnNoResults: true, //confirmExternalLink: true, graphics: { seeker_pin: 'img/user-pin.png', cooperating_pin: 'img/cooperating-church-pin.png', participating_pin: 'img/participating-church-pin.png' }, language: null, localizedStrings: {}, max_factor: 4, max_results: 50, countries: [], restrictions: 'all', units: 'miles', unit_size: 5 }, instance = new _factory; State.Options = instance; function _initialize( initOptions ){ var defs = {}; $.extend( true, options, initOptions ); $.each(options, function(key,value){ var getter = function(){ return options[key]; } defs[key] = { enumerable: true, get: getter, set: noop }; }); Object.defineProperties( instance, defs ); } function _factory(){ if(instance instanceof _factory) return instance; instance = this; this.initialize = _initialize; } })(); (function(){ var factory = _factory, instance = new _factory, localizedStrings = { pin_label: 'My Location' }; State.Localizer = instance; Object.defineProperties( instance, { localizedStrings: { enumerable: true, get: _get_localized_strings, set: _set_localized_strings } }); function _get_string( name ){ return $.type(localizedStrings[name])==='string' ? localizedStrings[name] : ''; } function _set_localized_strings( strings ){ if($.type(strings)==='object'){ $.each(strings,function(name,localizedString){ localizedStrings[name] = localizedString; }); } State.Localizer = _localize; State.Strings = localizedStrings; } function _localize( stringName, defaultString ){ return $.type(localizedStrings[stringName])==='string' ? localizedStrings[stringName] : ($.type(defaultString)==='undefined' ? '' : defaultString ); } function _get_localized_strings(){ return localizedStrings; } function _factory(){ if(instance instanceof _factory) return instance; this.get_string = _get_string; instance = this; } })(); (function(){ var _searches = new Array, //_previousState; _previousState = null; Object.defineProperties( _searches, { create: { enumerable: true, get: function(){ return _create; }, set: noop }, find: { enumerable: true, get: function(){ return _find; }, set: noop } }); State.Searches = _searches; function _create_bounds_object(){ var points = Array.prototype.slice.call(arguments), bounds = new google.maps.LatLngBounds(); $.each(points,function( idx, latlng ){ bounds.extend( latlng ); }); return bounds; } function _normalize_geometry( result ){ var type = result.types, bounds = $.type(result.geometry.bounds)!=='object' ? _create_bounds_object( result.geometry.location ) : result.geometry.bounds, center = bounds.getCenter(), centerLat = center.lat(), centerLng = center.lng(), NE = bounds.getNorthEast(), SW = bounds.getSouthWest(), NELat = NE.lat(), NELng = NE.lng(), SWLat = SW.lat(), SWLng = SW.lng(), NELatRad = rad(NELat), NELngRad = rad(NELng), SWLatRad = rad(SWLat), SWLngRad = rad(SWLng), miles = Math.acos( Math.cos( NELatRad ) * Math.cos( SWLatRad ) * Math.cos( SWLngRad - NELngRad ) + Math.sin( NELatRad ) * Math.sin( SWLatRad ) ) * 3959, kilom = miles * 1.60934; return { type: result.geometry.location_type, bounds: bounds, center: { obj: center, str: String(centerLat)+','+String(centerLng), deg: {lat: centerLat, lng: centerLng}, rad: {lat: rad(centerLat), lng: rad(centerLng)} }, northEast: { obj: NE, str: String(NELat)+','+String(NELng), deg: {lat: NELat, lng: NELng}, rad: {lat: NELatRad, lng: NELngRad} }, southWest: { obj: SW, str: String(SWLat)+','+String(SWLng), deg: {lat: SWLat, lng: SWLng}, rad: {lat: SWLatRad, lng: SWLngRad} }, mi: miles, km: kilom } } function _find( input_string ){ $.each(_searches,function(idx,obj){ if( obj.input_string == input_string ){ return obj; } }); return null; } function _create( input_string ){ var obj; if( State.Origin && !State.Origin.deadzone ){ _previousState = State.Origin; //? State.Origin //: null; } State.Results.deactivate(); State.UI.disable_global_ui(); State.UI.initState = false; //console.log('initState=false'); //return false; State.Mask.show_modal('geolocating_message'); //State.Mask.show_modal('searching_message'); obj = _find( input_string ); if( $.type(obj)==='object' ){ obj.revisit(); }else{ obj = new Search( input_string, _searches.length ); State.Searches.push( obj ); obj.execute(); } } function get_explicit_bias( opts ){ var ne, sw, canDo=true, chex = 'north_lat|east_lng|south_lat|west_lng'.split('|'); $.each(chex,function(i,k){ var p = 'bias_'+k; if( State.Options[p]=='' || State.Options[p]==null ){ canDo = false; return false; } }); if( canDo ){ ne = new google.maps.LatLng( State.Options.bias_north_lat * 1 , State.Options.bias_east_lng * 1 ), sw = new google.maps.LatLng( State.Options.bias_south_lat * 1 , State.Options.bias_west_lng * 1 ); opts.bounds = new google.maps.LatLngBounds( sw, ne ); return true; } return false; } function Search( input_string, ind ){ var thisSearch = this, searchIndex = ind, results = [], valid_results = [], invalid_results = [], ambiguity = {}, error = null, error_message = null, success = false, retries = 3, resolved; /* public properties */ this.input_string = input_string; this.results = results; this.ambiguity = ambiguity; this.valid_results = valid_results; this.invalid_results = invalid_results; Object.defineProperties(thisSearch,{ error: {enumerable:true,get:function(){return error;},set:noop}, error_message: {enumerable:true,get:function(){return error_message;},set:noop}, index: {enumerable:true,get:function(){return searchIndex;},set:noop}, success: {enumerable:true,get:function(){return success;},set:noop}, resolved: {enumerable:true,get:function(){return resolved;},set:noop}, original_data: {enumerable:true,get:_get_origin_data.bind(this,'original_data'),set:noop}, formatted: {enumerable:true,get:_get_origin_data.bind(this,'formatted'),set:noop}, country: {enumerable:true,get:_get_origin_data.bind(this,'country'),set:noop}, state: {enumerable:true,get:_get_origin_data.bind(this,'state'),set:noop}, city: {enumerable:true,get:_get_origin_data.bind(this,'city'),set:noop}, postal_code: {enumerable:true,get:_get_origin_data.bind(this,'postal_code'),set:noop}, postal_code_suffix: {enumerable:true,get:_get_origin_data.bind(this,'postal_code_suffix'),set:noop}, route: {enumerable:true,get:_get_origin_data.bind(this,'route'),set:noop}, street_number: {enumerable:true,get:_get_origin_data.bind(this,'street_number'),set:noop} }) /* public methods */ this.execute = _execute; this.revisit = _revisit; /* private methods */ function _validate_precision( precision ){ $.each( this.valid_results, function( resIdx, resObj ){ if( $.type( countries[resObj.country] )==='undefined' ){ countries[resObj.country] = {}; } }); } function _execute(){ var requestOptions = { address: this.input_string }; State.Map.apply_bias( requestOptions ); return State.Geocoder.geocode( requestOptions, _receive.bind( this ) ); } function _get_origin_data( key ){ return $.type(resolved)==='object' ? resolved[key] : null; } function _receive( geo_results, geo_status ){ return geo_status!==window.google.maps.GeocoderStatus.OK ? _receive_error.call( this, geo_status ) : _receive_results.call( this, geo_results ); } function _receive_results( geo_results ){ //console.log('RECEIVING'); _normalize_results.call( this, geo_results ); _validate_results.call( this ); return this.valid_results.length==0 ? _no_valid_results.call( this ) : (this.valid_results.length==1 ? _resolve_location.call( this, this.valid_results[0] ) : _resolve_ambiguity.call( this ) ); } function _normalize_results( geo_results ){ var thisSearch = this; $.each( geo_results, function( idx, obj ){ var location = new State.Result( idx, obj, thisSearch ); location.initialize(); results.push( location ); }); } function _resolve_location( location ){ //console.log({resolve_location: location}); resolved = _create_origin.call( this, location ); State.Mask.show_modal('searching_message'); resolved.locate_churches(); } function _receive_error( geo_status ){ switch( geo_status ){ case 'UNKNOWN_ERROR': if( retries > 0 ){ retries--; //console.log('UNKNOWN_ERROR - trying again (retries: '+String(retries)+')' ); State.Geocoder.geocode( {address: this.input_string}, _receive.bind( this ) ); } break; case 'ZERO_RESULTS': //console.log('ZERO_RESULTS'); return _no_valid_results.call( this ); break; default: // general error handler //console.log({geocoder_error: geo_status}); break; } } function _rewind_state(){ return _previousState ? _rewind() : _reset(); } function _validate_results(){ var $this = this; $.each( this.results, function( idx, result ){ if( result.valid_type && result.valid_country ){ $this.valid_results.push( result ); }else{ $this.invalid_results.push( result ); } }); } function _no_valid_results(){ return _fail_and_rewind( this.invalid_results ); } function _fail_and_rewind( invalids ){ return invalids.length && !invalids[0].valid_country ? State.Mask.show_modal( 'restricted_location', null, _rewind_state ) : State.Mask.show_modal( 'invalid_location', null, _rewind_state ); } function _create_origin( locationObj ){ var geometryObj = _normalize_geometry( locationObj.originalObj ); return State.Origins.create( locationObj, geometryObj, this ); } function _resolve_ambiguity(){ var $this = this, shown = [], defacto; _calculate_ambiguity( ambiguity, valid_results ); $('#ambiguous_location_list_wrapper ul.buttons-list').empty(); $.each(valid_results,function( idx, loc ){ var selection_string = loc.formatted+', '+loc.country_full; if( shown.indexOf( selection_string )===-1 ){ shown.push( selection_string ); defacto = idx; $('#ambiguous_location_list_wrapper ul.buttons-list').append( XTag('li',XTag('button[type="button"]',selection_string)) ); } }); if( shown.length==1 ){ _select_location.call( $this, defacto ); }else{ State.Mask.show_modal( 'ambiguous_location' ); $('#ambiguous_location_list_wrapper ul.buttons-list').one( 'click', 'button', function(e){ var btn = $(e.target), li = btn.closest('li'), index = $('#ambiguous_location_list_wrapper ul.buttons-list').children().index( li ); State.Mask.hide_modal( _select_location.bind( $this, index ) ); } ); } } function _select_location( index ){ resolved = valid_results[index]; _resolve_location.call( this, valid_results[index] ); } function _revisit(){} function _rewind(){ _previousState.activate(); $('#search-field').focus(); } function _reset(){ State.UI.initState = true; $('#search-field').val('').focus(); } } function _calculate_ambiguity( ambiguity, results ){ $.each( results, function( resIdx, resObj ){ resObj.apply_disambiguation( ambiguity ); }); } function _validate_country( country ){ return State.Options.restrictions!='all' ? (State.Options.restrictions=='inc' ? State.Options.countries.indexOf( country )!==-1 : State.Options.countries.indexOf( country )===-1) : true; } function _validate_type( result ){ return ['ROOFTOP','RANGE_INTERPOLATED'].indexOf( result.type ) > -1 ? /street_(address|number)|(sub)?premise|floor|room/.test( result.originalObj.types.join(' ') ) : false; } })(); (function(){ var instance = new _factory, geocoderOptions = {}, current_origin = null, factory = _factory, locationString = '', initSearch = true, geoipLatLng, geoipLocation = {latitude:null,longitude:null}, detectedLocation, patterns = { city: '([a-z\-\.]+\s?)+', route: '([A-Z|a-z|0-9|\-|\.|\s]+)+', postal_code: '[a-z](?:(?:\d[a-z]\s\d[a-z]\d|[a-z](?:\d{4}|[a-z]\s\d{4}))|(?:\d(?:\d|[a-z])?|[a-z](?:\d(?:\d|[a-z])?|[a-z]))\s\d[ABDEFGHJLNPQRSTUWXYZ]{2})|AD\d{3}|(?:AZ|MD|V(?:C|G)|WS)\d{4}|(?:BB|HN)\d{5}|(?:LV|MD|SL)\-\d{4}|(?:AX|LT)\-\d{5}|(?:JE|IM)\d\d?\s\d[a-z]{2}|\d{3,5}(?:\d{2}|\-\d{4})|(?:TKCA|PCRN|BBND|(?:S|F|B)IQQ)\s1ZZ', street_number: '([0-9]+(\-[a-z])?)' } // Getters and Setters Object.defineProperties( instance, { current_latlng: { enumerable: true, get: _get_current_latlng, set: noop }, geoip_latitude: { enumerable: true, get: function(){ return geoipLocation.latitude; }, set: function(v){ geoipLocation.latitude = v==null ? null : v*1; } }, geoip_location: { enumerable: true, get: _get_geoip_location, set: noop }, geoip_longitude: { enumerable: true, get: function(){ return geoipLocation.longitude; }, set: function(v){ geoipLocation.longitude = v==null ? null : v*1;} }, location_string: { enumerable: true, get: _get_location_string, set: _set_location_string }, detected_location: { enumerable: true, get: _get_detected_location, set: noop }, detected_latitude: { enumerable: true, get: function(){ if($.type(detectedLocation)==='undefined'){ detectedLocation = $.type(google.loader.ClientLocation)==='object' ? new google.maps.LatLng( google.loader.ClientLocation.latitude, google.loader.ClientLocation.longitude ) : null; } return detectedLocation==null ? null : detectedLocation.lat(); }, set: noop }, detected_longitude: { enumerable: true, get: function(){ if($.type(detectedLocation)==='undefined'){ detectedLocation = $.type(google.loader.ClientLocation)==='object' ? new google.maps.LatLng( google.loader.ClientLocation.latitude, google.loader.ClientLocation.longitude ) : null; } return detectedLocation==null ? null : detectedLocation.lng(); }, set: noop } }); State.Location = instance; function _change_search_string( txt ){ State.UI.locationListener = false; $('#search-field').val( txt ); State.UI.locationListener = true; } function _no_origin(){ State.UI.locationListener = false; $('#search-field').val(''); State.Map.map.setCenter( {lat: State.Options.defaultLatitude * 1, lng: State.Options.defaultLongitude * 1} ); State.Map.map.setZoom( State.Options.defaultZoomLevel * 1 ); State.UI.locationListener = true; State.UI.enable_global_ui(); if( initSearch ){ State.UI.initState = true; } } function _get_current_latlng(){ return $.type(State.Origin)==='undefined' ? (State.Location.detected_location==null ? (State.Location.geoip_location==null ? (true) : State.Location.geoip_location) : State.Location.detected_location) : State.Origin.latlng; } function _clone_bounds( bounds ){ var newBounds = new google.maps.LatLngBounds(); newBounds.union( bounds ); return newBounds; } function _get_detected_location(){ if($.type(detectedLocation)==='undefined'){ detectedLocation = $.type(google.loader.ClientLocation)==='object' ? new google.maps.LatLng( google.loader.ClientLocation.latitude, google.loader.ClientLocation.longitude ) : null; } return detectedLocation; } function _get_geoip_location(){ if($.type(geoipLatLng)==='undefined'){ geoipLatLng = (geoipLocation.latitude!=null && geoipLocation.longitude!=null) ? new google.maps.LatLng( geoipLocation.latitude, geoipLocation.longitude ) : null; } return geoipLatLng; } function _change_location( input ){ State.Searches.create( input ); } function _get_location_string(){ return $.type(State.Origin)==='undefined' || State.Origin==null ? locationString : State.Origin.search_string; } function _set_location_string( val ){ return $.trim(val)!='' ? ($.type(State.Origin)==='undefined' || State.Origin==null ? _change_location( val ) : (State.Origin.is_different( val ) ? _change_location( val ) : false ) ) : false; } // function _identify_component( types ){ // var // result = false, // specs = { // 'street_address': 'street_number', // 'street_number': 'street_number', // 'route': 'route', // 'locality': 'city', // 'sublocality_level_1': 'city', // 'administrative_area_level_1': 'state', // 'postal_code': 'postal_code', // 'postal_code_suffix': 'postal_code_suffix', // 'country': 'country' // }; // // if($.type(types)==='string'){ // result = $.type(specs[types])==='string' // ? specs[types] // : false; // }else{ // $.each(types,function( idx, name ){ // if($.type(specs[name])==='string'){ // result = specs[name]; // return false; // } // }); // } // // return result; // } // function _create_location( index, result, search ){ // return new Location( index, result, search ); // } function _factory(){ if(instance instanceof _factory) return instance; instance = this; //this.create = _create_location; //this.identify = _identify_component; this.clone_bounds = _clone_bounds; this.no_origin = _no_origin; } /* function Location( resultIndex, rawResult, searchObj ){ var thisLocation = this; this.index = resultIndex; this.original_data = rawResult; this.formatted = rawResult.formatted_address; this.country = null; this.state = null; this.city = null; this.postal_code = null; this.postal_code_suffix = null; this.route = null; this.street_number = null; this.location_type = _identify_component(rawResult.types); this.location_mask = searchObj.input_string; this.partial_match = !!rawResult.partial_match; this.specificity_valid = true, this.specificity_message = null; this.specificity_error = null; this.search = searchObj; this.specificity = { country: null, city: null, state: null, postal_code: null, region: null, route: null, street_number: null } this.input_data = { street_number: { indicated: null, matched: null, inferred: null }, route: { indicated: null, matched: null, inferred: null }, city: { indicated: null, matched: null, inferred: null }, state: { indicated: null, matched: null, inferred: null }, postal_code: { indicated: null, matched: null, inferred: null }, postal_code_suffix: { indicated: null, matched: null, inferred: null }, country: { indicated: null, matched: null, inferred: null } } this.input_chain = null; this.initialize = _initialize.bind( this ); this.validate_specificity = _validate_specificity.bind( this ); this.validate_exactitude = _validate_exactitude.bind( this ); function _initialize(){ $.each( rawResult.address_components, _init_component ); } function _init_component( componentIndex, componentSpec ){ var val, long_name = componentSpec.long_name, short_name = componentSpec.short_name, types = componentSpec.types, type = false, component, specs = { 'street_address': 'street_number', 'street_number': 'street_number', 'route': 'route', 'locality': 'city', 'sublocality_level_1': 'city', 'administrative_area_level_1': 'state', 'postal_code': 'postal_code', 'postal_code_suffix': 'postal_code_suffix', 'country': 'country' }; if($.type(types)==='string'){ type = $.type(specs[componentSpec])==='string' ? specs[componentSpec] : false; }else{ $.each(types,function( idx, name ){ if($.type(specs[name])==='string'){ type = specs[name]; return false; } }); } val = type==='route' ? long_name : short_name; if( type!==false ){ if(searchObj.components[type]==null){ searchObj.components[type] = new Object; } if( $.type(searchObj.components[type][val])==='undefined' ){ component = new State.Component( type, short_name, long_name, thisLocation ); component.init(); }else{ component = searchObj.components[type][val]; component.add_location( thisLocation ); } } } function _extrapolate( components ){ var location = this, mask_chain = _mask_to_chain( this ); // $.each(components,function(i,c){ // var m = 'extrapolate_'+c; // if($.type(location[m])==='function'){ // location[m](); // } // }); } function _mask_to_chain( location ){ //console.log({maskThis: this, location: location}); var has = [], chain = [], extrapolation, missingComponents = [], unidentifiedValues = [], mask = location.location_mask, tmp = mask.replace(/\{/g,'|{').replace(/\}/g,'}|').replace(/\|\|/g,'|').split('|'); $.each( tmp, function( idx, str ){ var value, component; if( /[a-z|0-9]/i.test(str) ){ if(/^\{(city|postal_code(_suffix)?|route|st(ate|reet_number))\}$/.test(str)){ component = str.substr(1,str.length-2); value = location[component].value; has.push( component ); }else{ component = null, value = $.trim(str); unidentifiedValues.push({idx: idx, value: value}); } chain.push({component: component, value: value}); } }); $.each( location.input_data, function( componentName, componentData ){ if( has.indexOf( componentName )===-1 ){ missingComponents.push( componentName ); } }); extrapolation = missingComponents.length > 0 && unidentifiedValues.length > 0 ? _extrapolate_components( missingComponents, unidentifiedValues, chain ) : []; // console.log({ // tmp: tmp, // mask: mask, // chain: chain, // location: location, // missingComponents: missingComponents, // unidentifiedValues: unidentifiedValues // }); } function _extrapolate_components( components, values, chain ){ while( values.length > 0 ){ _identify_unmatched_component( values.shift(), components, chain ); } } function _identify_unmatched_component( value, possibles, chain ){ var matched = {}; $.each( possibles, function( idx, component ){ var detector = 'extrapolate_'+component, result = $.type( this[detector] )==='function' ? this[detector]( value.idx, value.value, chain ) : null; if( result!=null ){ matched[component] = result; return false; } }); } function _match_component( component ){ var p, go = true, index = 0, results = [], pattern = component.pattern; while( go ){ if( pattern.test(thisLocation.location_mask.substring( index )) ){ p = thisLocation.location_mask.substring( index ).match( pattern ); results.push({ //match: p, //from: index, index: index + p.index, string: p[0], //strlen: p[0].length, //next: index + p.index + p[0].length }); index = index + p.index + p[0].length; }else{ go = false; } } return results; } function _apply_component_mask( component, match ){ var index = match.index, substr = match.string, strlen = substr.length, pre = thisLocation.location_mask.substr( 0, index ), post = thisLocation.location_mask.substr( index + strlen ); thisLocation.location_mask = pre+'{'+component+'}'+post; } function _validate_exactitude( searchObj ){ this.input_chain = new State.Chain( this ); this.input_chain.initialize(); _validate_specificity.call( this, searchObj ); switch( State.Options.min_specificity ){ case 'number': if( this.input_chain.exactitude!='street_number' ){ return false; } break; case 'route': if( ['street_number','route'].indexOf(this.input_chain.exactitude)===-1 ){ return false; } break; case 'region': if( ['street_number','route','region'].indexOf(this.input_chain.exactitude)===-1 ){ return false; } break; }; return true; } function _validate_specificity( searchObj ){ var cmps = searchObj.components, outcome; this.specificity.street_number = cmps.street_number && cmps.street_number[this.street_number.value].matched ? 1 : 0; this.specificity.route = cmps.route && cmps.route[this.route.value].matched ? 1 : 0; this.specificity.city = cmps.city && cmps.city[this.city.value].matched ? 1 : 0; this.specificity.postal_code = cmps.postal_code && cmps.postal_code[this.postal_code.value].matched ? 1 : 0; this.specificity.state = cmps.state && cmps.state[this.state.value].matched ? 1 : 0; //this.specificity.region = (this.specificity.city + this.specificity.postal_code + this.specificity.state) >= 2 ? 1 : 0; this.specificity.region = (this.specificity.postal_code || (this.specificity.city + this.specificity.state) >= 2) ? 1 : 0; switch( State.Options.min_specificity ){ case 'number': outcome = this.specificity.region==1 ? (this.specificity.route==1 ? (this.specificity.street_number==1 ? true : 'number' ) : 'street' ) : 'region'; break; case 'street': outcome = this.specificity.region==1 ? (this.specificity.route==1 ? true : 'street' ) : 'region'; break; case 'region': outcome = this.specificity.region==1 ? true : 'region'; break; case 'city': outcome = this.specificity.city==1 ? true : 'city' break; case 'zipcode': outcome = this.specificity.postal_code==1 ? true : 'postal_code'; break; case 'state': outcome = this.specificity.state==1 ? true : 'state'; break; } this.specificity_valid = outcome===true; this.specificity_error = outcome===true ? null : outcome; return this.specificity_valid ? searchObj.specificity = true : searchObj.specificity_error = outcome; } } */ })(); "use strict"; (function(){ State.Result = Result; State.ComponentPrecedence = [ 'administrative_area_level_1', 'postal_town', 'administrative_area_level_2', 'administrative_area_level_3', 'administrative_area_level_4', 'administrative_area_level_5', 'colloquial_area', 'locality', 'ward', 'sublocality', 'sublocality_level_1', 'sublocality_level_2', 'sublocality_level_3', 'sublocality_level_4', 'sublocality_level_5', 'neighborhood' ]; function Result( resultIdx, resultObj, searchObj ){ var queue = [ 'initialize_components', 'initialize_format', 'map_components', 'map_orphans', 'resolve_orphans', 'resolve_precision', 'validate' ]; this.valid_country = null; this.valid_type = null; this.valid_precision = null; this.validation_issue = null; this.resultIndex = resultIdx; this.originalObj = resultObj; this.searchObj = searchObj; this.components = {}; this.component_map = {}; this.component_sequence = []; this.mask = null; this.formatted = null; this.formatted_sequence = []; this.type = null; this.latitude = null; this.longitude = null; this.precision = null; this.country = null; this.country_full = null; this.initialized = false; this.orphans = {}; this.unlinked = []; this.initialize = function( ){ var tmpA = resultObj.formatted_address.split(','), tmpB = tmpA.pop(); $.each( tmpA, function( i, e ){ tmpA[i] = $.trim( e ); }); this.formatted = $.trim(tmpA.join(', ')); this.mask = this.formatted; this.type = resultObj.geometry.location_type; this.latitude = resultObj.geometry.location.lat; this.longitude = resultObj.geometry.location.lng; this.process_queue(); } this.process_queue = function(){ var next_task = queue.length ? queue.shift() : null; if( next_task!=null ){ this[next_task](); }else{ this.initialized = true; } } this.initialize_components = function(){ var $this = this; $.each( resultObj.address_components, function( idx, cmp ){ var type = get_type( cmp.types, $this.components ), subStr; if( type!=null ){ if( type=='country' ){ $this.country = cmp.short_name; $this.country_full = cmp.long_name=='United Kingdom' ? 'Great Britain' : cmp.long_name; }else{ $this.components[type] = cmp; cmp.mask = mask_component( cmp.long_name, cmp.short_name ); if( cmp.mask.test( $this.mask ) ){ subStr = $this.mask.match( $this.components[type].mask )[0]; $this.components[type].substring = type=='postal_town' ? subStr.toUpperCase() : subStr; $this.components[type].similarity = 1.0; $this.mask = $.trim( $this.mask.replace( cmp.mask, '['+type+']' ) ); $this.component_sequence.push( type ); cmp.formatted = true; }else{ $this.unlinked.push( type ); cmp.formatted = false; } } } }); return this.process_queue(); } this.initialize_format = function(){ var $this = this, tmp = this.mask.match( /\[[a-z0-9_]+\]/gi ); this.formatted_sequence.splice( 0, this.formatted_sequence.length ); $.each( tmp, function( idx, cmp ){ $this.formatted_sequence.push( cmp.substr( 1, cmp.length-2 ) ); }); return this.process_queue(); } this.map_orphans = function(){ var $this = this, prevComp = null, all = '([^\\[]+)', firstComp = this.formatted_sequence[0], lastComp = this.formatted_sequence[this.formatted_sequence.length-1], leading = /^\[/.test( this.mask ) ? null : this.mask.split('[').shift(), trailing = /\]$/.test( this.mask ) ? null : this.mask.split(']').pop(), mids = [], debug = {}; // check leading orphan if( !/^\[/.test( this.mask ) ){ this.register_orphan( firstComp, null, $.trim( this.mask.split('[').shift()) ); } // check trailing orphan if( !/\]$/.test( this.mask ) ){ //this.orphans[lastComp+':'] = $.trim( this.mask.split(']').pop() ) //this.register_orphan( null, lastComp, $.trim( this.mask.split(']').shift()) ); this.register_orphan( null, lastComp, $.trim( this.mask.split( /\](?![^\]]\])$/ ).shift()) ); } // check internal orphans $.each( this.formatted_sequence, function( idx, comp ){ var rgx, tmp, res; if( prevComp!=null ){ rgx = new RegExp( '(?:\\['+prevComp+'\\])([^\\[]+)(?:\\['+comp+'\\])' ); res = $this.mask.match( rgx ); if( res ){ $this.register_orphan( comp, prevComp, $.trim( res.pop() ) ); } } prevComp = comp; }); return this.process_queue(); } this.map_components = function(){ var divisions = get_political_divisions( this.components ); if( divisions.length ){ if( divisions.length == 1 ){ switch( divisions[0] ){ case 'administrative_area_level_1': case 'postal_town': this.component_map.region = divisions.shift(); break; default: this.component_map.city = divisions.shift(); break; } }else{ this.component_map.region = divisions.shift(); this.component_map.city = divisions.shift(); if( divisions.length ){ this.component_map.town = divisions.shift(); } } } return this.process_queue(); } this.register_orphan = function( nextComp, prevComp, content ){ return [',','#','.','-'].indexOf( content )===-1 && $.trim(content)!='' ? (nextComp==null ? this.orphans.trailing = content : (prevComp==null ? this.orphans.leading = content : this.orphans[prevComp+'-'+nextComp] = content)) : false; } this.resolve_orphans = function(){ var $this = this, orphanContents = [], orphanComponents = [], returnValue; $.each( this.orphans, function( orphanKey, orphanContent ){ var idealMatch, potentials = []; if( $this.unlinked.length ){ $.each( $this.unlinked, function( idx, cmpKey ){ var cmp = $this.components[cmpKey], s1 = get_similarity( orphanContent, cmp.long_name ), s2 = get_similarity( orphanContent, cmp.short_name ); potentials.push({ component: cmpKey, value: s1 > s2 ? s1 : s2 }); }); potentials.sort(function( a, b ){ return a.value > b.value ? -1 : (b.value > a.value ? 1 : 0); } ); idealMatch = potentials.shift(); $this.resolve_orphan( idealMatch, orphanKey ); }else{ // formatted_string contains something missing from components /* TODO: Not sure how exactly to handle this situation (if the contents of address_components contradict formatted_string). switch( orphanKey ){ case 'leading': $this.resolve_anomalous_leading_orphan( orphanContent ); break; case 'trailing': $this.resolve_anomalous_trailing_orphan( orphanContent ); break; default: $this.resolve_anomalous_orphan( orphanContent, orphanKey ); break; } */ } }); return this.process_queue(); } this.resolve_anomalous_orphan = function( content, key ){} this.resolve_anomalous_leading_orphan = function( content ){ if( this.has_route() ){ // toss it... }else{ } } this.resolve_anomalous_trailing_orphan = function( content ){} this.resolve_orphan = function( matchedComponent, orphanKey ){ var component = matchedComponent.component, orphanContent = this.orphans[orphanKey], orphanComponent = this.components[component]; orphanComponent.mask = mask_component( orphanContent, orphanComponent.short_name ); orphanComponent.similarity = matchedComponent.value orphanComponent.substring = orphanContent; orphanComponent.formatted = true; this.mask = $.trim( this.mask.replace( orphanContent, '['+component+']' )); if( this.unlinked.indexOf( component ) > -1 ){ this.unlinked.splice( this.unlinked.indexOf( component ), 1 ); } this.initialize_format(); } this.resolve_precision = function(){ this.precision = this.components.street_number || this.components.street_address ? 'number' : (this.components.route ? 'route' : (this.component_map.city ? 'city' : (this.component_map.region ? 'region' : 'country' ) ) ); return this.process_queue(); } this.resolve_anomalies = function(){} this.validate = function(){ this.validate_country(); this.validate_type(); } this.validate_country = function(){ this.valid_country = State.Options.restrictions!='all' ? (State.Options.restrictions=='inc' ? State.Options.countries.indexOf( this.country )!==-1 : State.Options.countries.indexOf( this.country )===-1) : true; } this.validate_type = function(){ // ROOFTOP || RANGE_INTERPOLATED var specRule = State.Options.min_specificity, validType = specRule=='number' ? this.validate_number() : (specRule=='street' ? this.validate_street() : this.validate_region()), precision = this.precision=='city' ? 'region' : this.precision, caseKey = specRule+' '+precision; this.valid_type = true; if( validType ){ //return this.valid_type = true; }else{ //console.log({caseKey: caseKey}); switch( caseKey ){ case 'number route': this.validation_issue = this.orphans.leading ? 'required street_number missing in components, but may be present in formatted string' : 'required street_number missing'; break; case 'number region': this.validation_issue = this.orphans.leading ? 'required number/street missing in components, but may be present in formatted string' : 'required number/street missing'; break; case 'street region': this.validation_issue = this.orphans.leading ? 'required route missing in components, but may be present in formatted string' : 'required route missing'; break; case 'street country': this.validation_issue = this.orphans.leading ? 'required route missing in components, but may be present in formatted string' : 'required route missing'; break; case 'region country': this.validation_issue = this.orphans.leading || this.orphans.trailing ? 'required city/region missing in components, but may be present in formatted string' : 'required city/region missing'; break; } //return this.valid_type = false; } } this.validate_number = function(){ return this.type=='ROOFTOP' || ( this.type=='RANGE_INTERPOLATED' && /street_(address|number)|(sub)?premise|floor|room/.test( this.originalObj.types.join(' ') ) ); } this.validate_street = function(){ return this.type=='ROOFTOP' || this.type=='RANGE_INTERPOLATED' || ( this.type=='GEOMETRIC_CENTER' && /street_(address|number)|route|intersection|(sub)?premise|floor|room/.test( this.originalObj.types.join(' ') ) ); } this.validate_region = function(){ return this.precision != 'country'; } this.apply_disambiguation = function( ambiguity ){ var output = {}, country = this.country, region = this.get_region(), city = this.get_city(), route = this.get_route(), number = this.get_number(); output[country] = {}; if( region!=null ){ output[country][region] = {}; if( city!=null ){ output[country][region][city] = {}; if( route!=null ){ output[country][region][city][route] = {}; if( number!=null ){ output[country][region][city][route][number] = {}; output[country][region][city][route][number][resultIdx] = true; } } } } return output; } this.get_lowest_linked_component = function(){ return this.has_subpremise() ? 'subpremise' : (this.has_street_number() ? 'street_number' : (this.has_route() ? 'route' : (this.has_city() ? 'city' : (this.has_region() ? 'region' : (this.has_postal() ? 'postal' : null))))); } this.get_leading_orphan = function(){ var txt = $.type( this.orphans.leading )!=='undefined' ? $.trim( this.orphans.leading ) : ''; return txt.replace(/\s+/gi,' ').replace(/[,&\-@]*$/,''); } this.get_region = function(){ // var rKey = this.component_map.region; // return rKey // ? this.components[rKey].substring // : null; return this.has_region() ? this.components[this.component_map.region].substring : null; } this.get_city = function(){ // var cKey = this.component_map.city; // return cKey // ? this.components[cKey].substring // : null; return this.has_city() ? (this.components[this.component_map.city].substring ? this.components[this.component_map.city].substring : this.components[this.component_map.city].long_name ) : null; } this.get_city_address = function(){ var city = this.get_city(), town = this.get_town(); return town==null ? city : town+', '+city; } this.get_town = function(){ return this.has_town() ? this.components[this.component_map.town].substring : null; } this.get_postal = function(){ return this.has_postal() ? this.components.postal_code.substring : null; } this.get_route = function(){ return this.has_route() ? this.components.route.substring : null; } this.get_number = function(){ return this.has_street_number() ? this.components.street_number.substring : null; } this.get_sub = function(){ return this.has_subpremise() ? (/^[0-9\-]+$/.test(this.components.subpremise.substring) ? '#'+this.components.subpremise.substring : ($.type( this.components.subpremise.substring.length==1) ? this.components.subpremise.substring.toUpperCase()+'.' : this.components.subpremise.substring ) ) : null; } this.get_street_number = function(){ return this.has_street_number() ? (this.has_subpremise() ? (this.has_number_subpremise() ? this.components.street_number.substring+' '+this.get_sub() : this.components.street_number.substring) : this.components.street_number.substring) : null; } this.get_street_route = function(){ return this.has_route() ? (this.has_subpremise() ? (this.has_route_subpremise() ? this.components.route.long_name+' '+this.get_sub() : this.components.route.long_name) : this.components.route.long_name) : null; } // For filling out address form; number/route/subpremise according to formatted_string this.get_street_address = function(){ var route = this.get_street_route(), number = this.get_street_number(), rIdx = this.formatted_sequence.indexOf('route'), nIdx = this.formatted_sequence.indexOf('street_number'); return route==null ? this.get_leading_orphan() : (number==null ? route : (rIdx > nIdx ? number+' '+route : route+' '+number) ); } this.has_street_address = function(){} this.has_orphans = function(){ var result = false; $.each( $this.orphans, function( idx, ele ){ result = true; return false; }); return result; } this.has_region = function(){ return $.type(this.component_map.region)!=='undefined'; } this.has_postal = function(){ return this.has( 'postal_code' ); } this.has_city = function(){ return $.type(this.component_map.city)!=='undefined'; } this.has_town = function(){ return $.type(this.component_map.town)!=='undefined'; } this.has_street_number = function(){ return this.has( 'street_number' ); } this.has_route = function(){ return this.has( 'route' ); } this.has_subpremise = function(){ return this.has( 'subpremise' ) && this.components.subpremise.formatted; } this.has_route_subpremise = function(){ var spIdx = this.formatted_sequence.indexOf('subpremise'), rIdx = this.formatted_sequence.indexOf('route'); return spIdx == rIdx + 1; } this.has_number_subpremise = function(){ var spIdx = this.formatted_sequence.indexOf('subpremise'), nIdx = this.formatted_sequence.indexOf('street_number'); return spIdx == nIdx + 1; } this.has = function( cmp ){ return $.type( this.components[cmp] )!=='undefined'; } this.reverse = function(){ State.Geocoder.geocode( {location: resultObj.geometry.location}, function( result, status ){ // console.log({ // result: result, // status: status // }); } ); } } function get_political_divisions( components ){ var ret = [], spares = [], plug; $.each( State.ComponentPrecedence, function( idx, key ){ if( $.type( components[key] )!=='undefined' ){ //console.log('considering '+key+'...'); if( components[key].formatted ){ //console.log(key+' is in formatted string, adding to collection...'); ret.push( key ); }else{ if( ret.length > 0 ){ //console.log(key+' is not in formatted string, and ret has length, adding to spares...'); spares.push( key ); }else{ if( component_redundant( key, ret, components) ){ //console.log(key+' is not in formatted string but is redundant, adding to spares...'); spares.push( key ); }else{ //console.log(key+' is not in formatted string, and ret.length===0, so skipping...'); } } // if( ret.length > 0 || component_redundant( key, ret, components)){ // spares.push( key ); // }else{ // console.log(key+' is not in formatted string, and ret.length===0, so skipping...'); // } } } }); while( ret.length < 2 && spares.length ){ plug = spares.pop(); //console.log('return length < 2, considering spare "'+plug+'"...'); //if( !component_redundant( plug, ret, components ) ){ //console.log(plug+' is not redundant, adding to collection...'); ret.push( plug ); //} } return ret; } function component_redundant( potential, queued, components ){ var isRedundant = false, pShort = components[potential].short_name, pLong = components[potential].long_name; $.each( components, function( i, e ){ if( i != potential ){ if( e.long_name == pLong && e.short_name == pShort ){ //console.log(potential+' is redundant (matches '+i+')'); isRedundant = true; return false; } } }); if( !isRedundant ){ $.each( queued, function( i, q ){ if( q != potential ){ if( components[q].long_name == pLong && components[q].short_name == pShort ){ //console.log(potential+' is redundant (matches '+q+')'); isRedundant = true; return false; } } }); } return isRedundant } function get_type( types, components ){ var ret = null; $.each( types, function( i, e ){ if( e!='political' ){ if( $.type( components[e] )==='undefined' ){ ret = e; return false; } } }); return ret; } function mask_component( longName, shortName ){ return new RegExp( '(?:^|\\b|\\s)(?:'+( longName==shortName ? regexify( longName ) : regexify( longName )+'|'+regexify( shortName ) )+')(?:\\b|$)' ); } function regexify( input ){ return input .replace( /\-/g, '[-]' ) .replace( /\./g, '[.]' ) .replace( /\s+/g, '\\s+' ) .replace( /\s&\s/g, '\\s(and|&)\\s' ); } function get_similarity( s1, s2 ){ var s1Longer = s1.length > s2.length, longer = s1Longer ? s1 : s2, shorter = s1Longer ? s2 : s1, longerLength = longer.length; return longerLength == 0 ? 1.0 : ((longerLength - levenshtein(longer, shorter)) / parseFloat(longerLength)); } function levenshtein( s1, s2 ) { s1 = s1.toLowerCase(); s2 = s2.toLowerCase(); var costs = new Array(); for (var i = 0; i <= s1.length; i++) { var lastValue = i; for (var j = 0; j <= s2.length; j++) { if (i == 0){ costs[j] = j; }else{ if (j > 0) { var newValue = costs[j - 1]; if (s1.charAt(i - 1) != s2.charAt(j - 1)){ newValue = Math.min(Math.min(newValue, lastValue), costs[j]) + 1; costs[j - 1] = lastValue; lastValue = newValue; } } } } if (i > 0){ costs[s2.length] = lastValue; } } return costs[s2.length]; } })(); (function(){ State.Component = Component; function Component( type, short_name, long_name, location ){ var $this = this, search = location.search, results = new Array, aliases = {short_name: short_name, long_name: long_name}, value = type==='route' ? long_name : short_name, inputChains = [], masterTokens = [], variantTokens = [], convergence, pattern, gpattern, matched; this.type = type; this.results = results; this.search = search; this.aliases = aliases; this.convergence = convergence; this.short_name = short_name; this.long_name = long_name; this.input_substring = null; this.matched = matched; this.init = _init_location; this.add_location = _add_location; this.test = _test; this.pattern = pattern; this.gpattern = gpattern; Object.defineProperties( this, { pattern: { enumerable: true, get: function(){ return pattern; }, set: function(v){ return pattern = v; } }, matched: { enumerable: true, get: function(){ return matched; }, set: function(v){ return matched=v; } }, value: { enumerable: true, get: function(){ return value; }, set: function(v){ return value=v; } } }); function _test( subject ){ return pattern.test( $.trim( subject.replace(/[\.\-\+,#]/g,' ').replace(' ',' ')) ); } function _init_location(){ if( $.type(search.components[type][value])==='undefined' ){ search.components[type][value] = $this; } initialize(); location[type] = this; results.push( location.index ); return this; } function _add_location( locationObj ){ locationObj[type] = this; results.push( locationObj.index ); return this; } //-------INITIALIZATION--------// function initialize(){ init_meta(); init_grouping(); init_pattern(); if( search.collections[type].indexOf( value )===-1 ){ search.collections[type].push( value ); } return matched = pattern.test( search.input_string ); } function init_meta(){ $.each( aliases, function( i, str ){ if($.type(str)==='string'){ inputChains.push( $.trim(str).split(' ') ); } }); inputChains.sort(function(a,b){ return a.length > b.length ? 1 : (a.length < b.length ? -1 : 0); }); } function init_grouping(){ var temp, groups = []; $.each( inputChains, function( chainIdx, input_chain ){ if( chainIdx===0 ){ $.each( input_chain, function( tokenIdx, token ){ masterTokens.push( token ); }); }else{ $.each( input_chain, function( tokenIdx, token ){ variantTokens.push( token ); }); } }); temp = _find_convergence( masterTokens, variantTokens ); $.each( temp, function( groupIdx, groupArr ){ var segments = []; $.each( groupArr, function( segmentIdx, segment ){ segments.push( regexify_segment( segment ) ); }); groups.push( segments.join('\\s') ); }); convergence = groups.join('\\s'); } function init_pattern(){ switch( type ){ case 'country': pattern = new RegExp( '\\b(?:'+convergence+')$','i' ); gpattern = new RegExp( '\\b(?:'+convergence+')$','ig' ); break; case 'street_number': pattern = new RegExp( '^(?:'+convergence+')\\b','i' ); gpattern = new RegExp( '^(?:'+convergence+')\\b','ig' ); break; default: pattern = new RegExp( '\\b(?:'+convergence+')\\b','i' ); gpattern = new RegExp( '\\b(?:'+convergence+')\\b','ig' ); break; } } function _refactor_segments( masters, variants ){ var next_match, groups = [], current_set = [], master_buffer = [], variant_buffer = []; while( masters.length > 0 && variants.length > 0 ){ if( _submatch( masters[0], variants[0] ) ){ current_set.push([masters.shift(),variants.shift()]); }else{ if( current_set.length > 0 ){ groups.push( current_set.slice(0) ); current_set = new Array; } next_match = _get_next( masters[0], variants, 0, _submatch ); if( next_match==null ){ current_set.push([masters.join(' '),variants.join(' ')]); masters = []; variants = []; }else{ while(next_match > 0){ variant_buffer.push( variants.shift() ); } current_set.push([masters.shift(),variant_buffer.join(' ')]); } master_buffer = []; variant_buffer = []; if( current_set.length > 0 ){ groups.push( current_set.slice(0) ); current_set = new Array; } } } if( current_set.length > 0 ){ groups.push( current_set.slice(0) ); current_set = new Array; } return groups; } function _group_segments( groups ){ var output_buffer = []; $.each( groups, function( groupIdx, groupArr ){ var group = []; $.each( groupArr, function( segmentIdx, segmentArr ){ var segment = []; $.each( segmentArr, function( tokenIdx, token ){ if( segment.indexOf( token )===-1 ){ segment.push( token ); } }); group.push( segment.length > 1 ? segment : segment[0] ); }); output_buffer.push( group ); }); return output_buffer; } function _find_convergence( a, b ){ var masters = a.length > b.length ? a : b, variants = a.length > b.length ? b : a, groups = _refactor_segments( masters, variants ); return _group_segments( groups ); } function _get_next( a, b, bIdx, cFn ){ var b_ptr = bIdx, b_len = b.length, b_last = b_len - 1; for( b_ptr; b_ptr <= b_last; b_ptr++ ){ if( cFn( a, b[b_ptr] )){ return b_ptr; } } return null; } function _submatch( a, b ){ var compare_a = $.trim(a.toLowerCase().replace(/[^a-z|0-9]/gi,'')), compare_b = $.trim(b.toLowerCase().replace(/[^a-z|0-9]/gi,'')); return compare_a==compare_b; } function regexify_segment( input ){ return $.type( input )==='array' ? function( inputs ){ var output = []; $.each( inputs, function( i, str ){ output.push( _regexify( str ) ); }); return '(?:'+output.join('|')+')'; }( input ) : _regexify( input ); } function regexify_group( input ){ var output = []; $.each( input, function( segmentIdx, segment ){ if( $.type( segment )==='array' ){ output.push( segment.join('\\s') ); } }); return output; } function _regexify( input ){ // return input.replace('-' ,'([-]|\\s)') // .replace('.','[.]?') // .replace(/\s+/,'\\s+') // .replace(' & ','\\s(and|&)\\s'); return input.replace('-' ,'([-]|\\s)') .replace('.','[.]?') .replace(/\s+/,'\\s+') .replace(' & ','\\s(and|&)\\s'); } } })(); (function(){ State.Chain = Chain; function Chain( location ){ var chain = new Array, missing = new Array, orphanValues = new Array, mask = location.location_mask; Object.defineProperties( chain, { exactitude: { enumerable: true, get: _get_exactitude.bind( chain ), set: noop }, location: { enumerable: false, get: function(){ return location; }, set: noop }, mask: { enumerable: false, get: function(){ return location.location_mask; }, set: noop }, last: { enumerable: false, get: function(){ return this[this.length - 1]; }, set: noop }, lastIndex: { enumerable: true, get: function(){ return this.length - 1; }, set: noop }, bindings: { enumerable: true, get: _get_bindings.bind( chain ), set: noop }, orphans: { enumerable: true, get: _get_orphans.bind( chain ), set: noop }, missing: { enumerable: true, get: _get_missing_components.bind( chain ), set: noop }, street_number_index: { enumerable: true, get: get_component_index.bind( chain, 'street_number' ), set: set_component_index.bind( chain, 'street_number' ), }, street_number_value: { enumerable: true, get: get_component_value.bind( chain, 'street_number' ), set: set_component_value.bind( chain, 'street_number' ), }, street_number: { enumerable: true, get: get_component.bind( chain, 'street_number' ), set: set_component_index.bind( chain, 'street_number' ) }, route_index: { enumerable: true, get: get_component_index.bind( chain, 'route' ), set: set_component_index.bind( chain, 'route' ), }, route_value: { enumerable: true, get: get_component_value.bind( chain, 'route' ), set: set_component_value.bind( chain, 'route' ), }, route: { enumerable: true, get: get_component.bind( chain, 'route' ), set: set_component_index.bind( chain, 'route' ) }, city_index: { enumerable: true, get: get_component_index.bind( chain, 'city' ), set: set_component_index.bind( chain, 'city' ), }, city_value: { enumerable: true, get: get_component_value.bind( chain, 'city' ), set: set_component_value.bind( chain, 'city' ), }, city: { enumerable: true, get: get_component.bind( chain, 'city' ), set: set_component_index.bind( chain, 'city' ) }, state_index: { enumerable: true, get: get_component_index.bind( chain, 'state' ), set: set_component_index.bind( chain, 'state' ), }, state_value: { enumerable: true, get: get_component_value.bind( chain, 'state' ), set: set_component_value.bind( chain, 'state' ), }, state: { enumerable: true, get: get_component.bind( chain, 'state' ), set: set_component_index.bind( chain, 'state' ) }, postal_code_index: { enumerable: true, get: get_component_index.bind( chain, 'postal_code' ), set: set_component_index.bind( chain, 'postal_code' ), }, postal_code_value: { enumerable: true, get: get_component_value.bind( chain, 'postal_code' ), set: set_component_value.bind( chain, 'postal_code' ), }, postal_code: { enumerable: true, get: get_component.bind( chain, 'postal_code' ), set: set_component_index.bind( chain, 'postal_code' ) }, postal_code_suffix_index: { enumerable: true, get: get_component_index.bind( chain, 'postal_code_suffix' ), set: set_component_index.bind( chain, 'postal_code_suffix' ), }, postal_code_suffix_value: { enumerable: true, get: get_component_value.bind( chain, 'postal_code_suffix' ), set: set_component_value.bind( chain, 'postal_code_suffix' ), }, postal_code_suffix: { enumerable: true, get: get_component.bind( chain, 'postal_code_suffix' ), set: set_component_index.bind( chain, 'postal_code_suffix' ) } }); chain.initialize = _initialize.bind( chain ); chain.resolve = _resolve.bind( chain ); chain.index_of = _resolve_to_index.bind( chain ); chain.matches = _get_matches.bind( chain ); chain.is_bound = is_bound.bind( chain ); chain.is_orphan = is_orphan.bind( chain ); chain.is_first = is_first.bind( chain ); chain.is_first_orphan = is_first_orphan.bind( chain ); chain.is_first_bound = is_first_bound.bind( chain ); chain.is_last = is_last.bind( chain ); chain.is_last_orphan = is_last_orphan.bind( chain ); chain.is_last_bound = is_last_bound.bind( chain ); chain.is_after = is_after.bind( chain ); chain.is_before = is_before.bind( chain ); chain.is_adjacent = is_adjacent.bind( chain ); chain.is_previous = is_previous.bind( chain ); chain.is_next = is_next.bind( chain ); chain.is_specified = is_specified.bind( chain ); chain.is_matched = is_matched.bind( chain ); chain.is_unique = is_unique.bind( chain ); chain.exists = exists.bind( chain ); return chain; function _initialize(){ var these = this; $.each([ 'street_number', 'route', 'city', 'state', 'postal_code', 'postal_code_suffix', 'country' ],function( i, c ){ _init_component.call( these, c ); }); _refresh_chain.call( this ); _resolve.call( this ); } function _init_component( component ){ var matches, these = this, search = location.search, mask_string = '{'+component+'}'; if( search.components[component] ){ if( location[component] ){ if( search.components[component][location[component].value].pattern.test( location.location_mask )){ matches = _match_component.call( this, search.components[component][location[component].value] ); if( matches.length > 1 ){ switch( component ){ case 'street_number': case 'route': case 'city': _apply_component_mask.call( this, component, matches.shift() ); break; default: _apply_component_mask.call( this, component, matches.pop() ); break; } }else{ location.location_mask = location.location_mask.replace( search.components[component][location[component].value].pattern, mask_string ); } location.input_data[component].matched = true; location.input_data[component].inferred = true; location.input_data[component].indicated = true; return true; }else{ missing.push( component ); location.input_data[component].matched = true; location.input_data[component].inferred = false; location.input_data[component].indicated = false; return false; } } } missing.push( component ); location.input_data[component].matched = false; location.input_data[component].inferred = false; location.input_data[component].indicated = false; return false; } function _apply_component_mask( component, match ){ var index = match.index, substr = match.string, strlen = substr.length, pre = location.location_mask.substr( 0, index ), post = location.location_mask.substr( index + strlen ); location.location_mask = pre+'{'+component+'}'+post; } function _match_component( component ){ var p, go = true, index = 0, results = [], pattern = component.pattern; while( go ){ if( pattern.test( location.location_mask.substring( index )) ){ p = location.location_mask.substring( index ).match( pattern ); results.push({ //match: p, //from: index, index: index + p.index, string: p[0], //strlen: p[0].length, //next: index + p.index + p[0].length }); index = index + p.index + p[0].length; }else{ go = false; } } return results; } function _resolve(){ var these = this, start_over = false; if( this.missing.length > 0 ){ if( this.orphans.length > 0 ){ $.each( this.orphans, function( i, link ){ if( _resolve_orphan.call( these, link ) ){ start_over = true; return false; } }); } } return this.missing.length > 0 && start_over ? _resolve.call( this ) : true; } function _resolve_orphan( link ){ var possibles = _find_possible_components.call( this, link ); return possibles.length > 0 ? _resolve_component.call( this, link, possibles ) : false; } function _resolve_component( link, components ){ return _commit_component.call( this, link, components[0] ); } function _commit_component( link, component ){ var componentObj = new State.Component( component, link.clipped, link.clipped, location ); if( location.search.components[component]==null ){ location.search.components[component] = new Object; } location.search.components[component][link.clipped] = componentObj; link.component = component; link.resolved = true; link.input_match = true; link.location_match = false; componentObj.init(); _update_mask.call( this, link ); location.input_data[component] = { indicated: true, matched: false, inferred: false }; return true; } function _find_possible_components( link ){ var these = this, result = []; $.each( this.missing, function( i, c ){ var m = c=='street_number' ? _is_possibly_street_number.bind( these ) : (c=='route' ? _is_possibly_route.bind( these ) : (c=='city' ? _is_possibly_city.bind( these ) : (c=='state' ? _is_possibly_state.bind( these ) : (c=='postal_code' ? _is_possibly_postal_code.bind( these ) : (c=='postal_code_suffix' ? _is_possibly_postal_code_suffix.bind( these ) : _is_possibly_concatenated.bind( these ) ))))); m( link, result ); //if( ){ // result.push( c ); //} }); return result; } function _is_possibly_street_number( link, results ){ return link.idx==0 && /[0-9]/.test( link.clipped ) ? results.unshift( 'street_number' ) : false; } function _is_possibly_route( link, results ){ return /[a-z|0-9]+\s+[a-z|0-9]/.test( link.clipped ) ? (this.street_number!=null ? (this.is_next( link, this.street_number ) ? results.unshift( 'route' ) : results.push( 'route' )) : (link.idx==0 ? results.push( 'route' ) : false)) : false; } function _is_possibly_city( link, results ){ return /[a-z]([a-z][\-\.]\s)*[a-z\.]/.test( link.clipped ) ? (this.route!=null ? (is_next.call( this, link, this.route ) ? (this.state!=null ? (is_before.call( this, link, this.state ) ? results.unshift( 'city' ) : false ) : results.push('city') ) : (this.state!=null ? (is_before.call( this, link, this.state ) ? results.unshift( 'city' ) : false ) : (is_after.call( this, link, this.route ) ? results.unshift( 'city' ) : false ) ) ) : (this.street_number!=null ? (is_after.call( this, link, this.street_number) ? (this.state!=null ? (is_before.call( this, link, this.state ) ? results.unshift( 'city' ) : false ) : results.push('city') ) : results.push('city') ) : results.push('city') ) ) : false } function _is_possibly_state( link, results ){ return /[a-z]([a-z][\-\.]\s)*[a-z\.]/.test( link.clipped ) ? (this.city!=null ? (this.is_after( link, this.city) ? results.unshift('state') : false) : (this.postal_code!=null ? (this.is_before( link, this.postal_code ) ? results.unshift('state') : false ) : results.unshift('state'))) : false; } function _is_possibly_postal_code( link, results ){ return /([a-z](?:(?:\d[a-z]\s\d[a-z]\d|[a-z](?:\d{4}|[a-z]\s\d{4}))|(?:\d(?:\d|[a-z])?|[a-z](?:\d(?:\d|[a-z])?|[a-z]))\s\d[ABDEFGHJLNPQRSTUWXYZ]{2})|AD\d{3}|(?:AZ|MD|V(?:C|G)|WS)\d{4}|(?:BB|HN)\d{5}|(?:LV|MD|SL)\-\d{4}|(?:AX|LT)\-\d{5}|(?:JE|IM)\d\d?\s\d[a-z]{2}|\d{3,5}(?:\d{2}|\-\d{4})|(?:TKCA|PCRN|BBND|(?:S|F|B)IQQ)\s1ZZ)?$/i.test( link.clipped ) ? (this.state!=null ? (is_after.call( this, link, this.state ) ? results.unshift('postal_code') : results.push('postal_code') ) : (this.city!=null ? (is_after.call( this, link, this.city ) ? results.unshift('postal_code') : results.push('postal_code') ) : (this.route!=null ? (is_after.call( this, link, this.route ) ? results.unshift('postal_code') : results.push('postal_code') ) : (this.street_number!=null ? (is_after.call( this, link, this.street_number ) ? results.push('postal_code') : false ) : (is_last.call( this, link ) ? results.unshift('postal_code') : results.push('postal_code')))))) : false } function _is_possibly_postal_code_suffix( link, results ){ var missing = this.missing; return missing.length==1 && missing[0]=='postal_code_suffix' && is_next.call( this, link, this.postal_code ) ? results.push( 'postal_code_suffix' ) : false; } function _is_possibly_concatenated( link ){ var missing = this.missing, result = false; if( missing.length > 1 ){ $.each( missing, function( i, c ){ switch( c ){ case 'street_number': if( missing.indexOf('route')!==-1 ){ result = true; return false; } break; case 'route': if( missing.indexOf('street_number')!==-1 || missing.indexOf('city')!==-1 ){ result = true; return false; } break; case 'city': if( missing.indexOf('route')!==-1 || missing.indexOf('state')!==-1 ){ result = true; return false; } break; case 'state': if( missing.indexOf('city')!==-1 || missing.indexOf('postal_code')!==-1 ){ result = true; return false; } break; case 'postal_code': if( missing.indexOf('state')!==-1 || missing.indexOf('postal_code_suffix')!==-1 ){ result = true; return false; } break; case 'postal_code_suffix': if( missing.indexOf('postal_code')!==-1 ){ result = true; return false; } break; } }); } return result; } function _clip_value( input ){ return /[a-z|0-9]/i.test( input ) ? input.match( /[a-z|0-9](?:.*[a-z|0-9])?/i ).shift() : null; } function _refresh_chain(){ var mask = location.location_mask, tmp = mask.replace(/\{/g,'|{').replace(/\}/g,'}|').replace(/\|\|/g,'|').split('|'); chain.splice(0,chain.length); orphanValues.splice(0,orphanValues.length); $.each( tmp, function( idx, str ){ var value, component, resolved, location_match; if( /[a-z|0-9]/i.test(str) ){ //if(/^\{(city|postal_code(_suffix)?|route|st(ate|reet_number))\}$/.test(str)){ if(/^\{(country|city|postal_code(_suffix)?|route|st(ate|reet_number))\}$/.test(str)){ component = str.substr(1,str.length-2); value = location[component].value; }else{ component = null, value = $.trim(str); orphanValues.push({idx: chain.length, value: value}); } resolved = component!=null && missing.indexOf(component)!==-1; chain.push({ idx: chain.length, component: component, value: value, clipped: _clip_value( value ), resolved: resolved, input_match: true, location_match: !resolved }); } }); } function _update_mask( link ){ var replacement = link.value.replace( link.clipped, '{'+link.component+'}' ); return location.location_mask = location.location_mask.replace( link.value, replacement ); } function _get_orphans(){ var these = this, result = []; $.each( these, function( i, o ){ if( o.component==null ){ result.push( o ); } }); return result; } function _get_bindings(){ var these = this, result = []; $.each( these, function( i, o ){ if( o.component!=null ){ result.push( o ); } }); return result; } function _get_missing_components(){ var these = this, result = []; //console.log({missing: missing}); $.each( missing , function( i, c ){ if(these[c]==null){ result.push( c ); } // if(get_component_index(c)==null){ // result.push( c ); // } }); return result; } function _is_a_value( x ){ return $.type( x )==='string' ? true : ($.type(x)==='object' ? x instanceof RegExp : x instanceof State.Component); } function _matches( subject, value ){ return $.type( value )==='string' ? subject==value : ($.type( value )==='object' ? (value instanceof RegExp ? value.test( subject ) : (value instanceof State.Component ? value.pattern.test( subject ) : false)) : false); } function _get_exactitude(){ var tmp, these = this, result = 'country'; if( _get_region_exactitude.call( this ) ){ result = 'region'; tmp = _get_component_exactitude.call( this, 'route' ); if( tmp!==false ){ result = tmp===true ? 'route' : 'region'; tmp = _get_component_exactitude.call( this, 'street_number' ); if( tmp!==false ){ result = tmp===true ? 'street_number' : 'route'; } } }else{ tmp = _get_component_exactitude.call( this, 'postal_code' ); if( tmp!==false ){ result = tmp===true ? 'postal_code' : result; tmp = _get_component_exactitude.call( this, 'state' ); if( tmp!==false ){ result = tmp===true ? 'state' : result; tmp = _get_component_exactitude.call( this, 'city' ); if( tmp!==false ){ result = tmp===true ? 'city' : result; tmp = _get_component_exactitude.call( this, 'route' ); if( tmp!==false ){ result = tmp===true ? 'route' : result; tmp = _get_component_exactitude.call( this, 'street_number' ); if( tmp!==false ){ result = tmp===true ? 'street_number' : 'route'; } } } }else{ tmp = _get_component_exactitude.call( this, 'postal_code' ); if( tmp!==false ){ result = tmp===true ? 'postal_code' : result; tmp = _get_component_exactitude.call( this, 'city' ); if( tmp!==false ){ result = tmp===true ? 'city' : result; tmp = _get_component_exactitude.call( this, 'route' ); if( tmp!==false ){ result = tmp===true ? 'route' : result; tmp = _get_component_exactitude.call( this, 'street_number' ); if( tmp!==false ){ result = tmp===true ? 'street_number' : result; } } } } } } } return result; } function _get_component_exactitude( component ){ var componentLink = this[component]; return componentLink!=null ? (componentLink.input_match ? (componentLink.location_match ? true : (component=='city' ? (location.partial_match ? false : null ) : false ) ) : null ) : null; return componentLink!=null ? (componentLink.input_match ? componentLink.location_match : null ) : null; } function _get_region_exactitude(){ var a = _get_component_exactitude.call( this, 'postal_code' ) ? 1 : 0, b = _get_component_exactitude.call( this, 'state' ) ? 1 : 0, c = _get_component_exactitude.call( this, 'city' ) ? 1 : 0; return a + b + c >= 2; } function _unbind_component( component ){ var these = this, result = false; $.each(these,function( i, o ){ if( o.component==component ){ o.component = null; result = true; return false; } }); return result; } function _set_component_value( component, value ){ var these = this, result = false; $.each(these,function( i, o ){ if( o.component==component ){ o.value = o.value.replace(/[a-z|0-9](?:.*[a-z|0-9])?/i,value); o.clipped = _clip_value( o.value ); result = true; return false; } }); if(!result){ $.each(these,function( i, o ){ if( _matches(o.clipped,value) ){ o.component = component; result = true; return false; } }); } return result; } function _get_bound_component( x ){ var these = this, result = null; $.each( these, function( i, o ){ if( o.component!=null && _matches(o.clipped,x)){ result = o.component; return false; } }); return result; } function _component_is_matched( x ){ return location.input_data[x].matched; } function _is_component_string( x ){ return $.type( x )==='string' && x.substr(0,1)=='{' && x.substr(-1,1)=='}'; } function _unwrap_component_string( x ){ return x.substr( 1, x.length - 2 ); } function _is_chain_link( x ){ return $.type( x )==='object' && $.type( x.component )!=='undefined'; } function _is_orphan_link( x ){ return _is_chain_link( x ) && x.component==null; } function _is_bound_link( x ){ return _is_chain_link( x ) && x.component!=null; } function _is_bound_value( x ){ var these = this, result = false; $.each( these, function( i, o ){ if( _matches(o.clipped,x) ){ if( o.component!=null ){ result = true; return false; } } }); return result; } function _is_component_first( component ){ return this[0].component==component; } function _is_component_last( component ){ return this.last.component==component; } function _is_first_bound_component( x ){ var these = this, result = false; $.each( these, function( idx, obj ){ if( obj.component!=null ){ result = obj.component==x; return false; } }); return result; } function _is_first_bound_value( x ){ var these = this, result = false; $.each( these, function( idx, obj ){ if( obj.component!=null ){ result = _matches(obj.clipped,x); return false; } }); return result; } function _is_first_value( x ){ return _matches(this[0].clipped,x); } function _is_first_orphan_value( x ){ var these = this, result = false; $.each( these, function( idx, obj ){ if( obj.component==null ){ result = _matches(obj.clipped,x); return false; } }); return result; } function _is_first_orphan_link( x ){ var these = this, result = false; $.each( these, function( idx, obj ){ if( obj.component==null ){ result = obj.idx==x.idx; return false; } }); return result; } function _is_last_bound_component( x ){ var these = this, lastBound = null; $.each( these, function( idx, obj ){ if( obj.component!=null ){ lastBound = obj.component; } }); return lastBound==x; } function _is_last_bound_value( x ){ var these = this, lastBound = false; $.each( these, function( idx, obj ){ if( obj.component!=null ){ lastBound = obj.clipped; } }); return _matches(lastBound,x); } function _is_last_value( x ){ return _matches(this.last.clipped,x) } function _is_last_orphan_value( x ){ var these = this, lastOrphan = null; $.each( these, function( idx, obj ){ if( obj.component==null ){ lastOrphan = obj.clipped; } }); return _matches(lastOrphan,x) } function _is_last_orphan_link( x ){ var these = this, lastOrphan = null; $.each( these, function( idx, obj ){ if( obj.component==null ){ lastOrphan = obj; } }); return lastOrphan.idx==x.idx; } function _resolve_to_index( x ){ return _is_a_value.call( this, x ) ? (_is_component_string.call( this, x ) ? get_component_index.call( this, _unwrap_component_string( x ) ) : _get_value_index.call( this, x )) : (_is_chain_link.call( this, x ) ? x.idx : null ); } function _resolve_to_chain_link( x ){ return _is_a_value.call( this, x ) ? (_is_component_string.call( this, x ) ? _get_component_chain_link.call( this, _unwrap_component_string( x ) ) : _get_value_chain_link.call( this, x )) : (_is_chain_link.call( this, x ) ? x : null ); } function _get_component_chain_link( x ){ var these = this, result = null; $.each( these, function( i, o ){ if( o.component==x ){ result = o; return false; } }); return result; } function _get_value_chain_link( x ){ var these = this, result = null; $.each( these, function( i, o ){ if( _matches(o.clipped,x) ){ result = o; return false; } }); return result; } function _get_value_index( x ){ var these = this, result = null; $.each( these, function( i, o ){ if( _matches(o.clipped,x) ){ result = i; return false; } }); return result; } function _get_value_indexes( x ){ var these = this, result = []; $.each( these, function( i, o ){ if( _matches(o.clipped,x) ){ result.push( i ); } }); return result; } function _is_unique_value( x ){ return _get_value_indexes.call( this, x ).length > 1; } function _get_matches( x ){ return _is_a_value.call( this, x ) ? _get_value_indexes.call( this, x ) : []; } function is_first( input ){ return _is_a_value.call( this, input ) ? ( _is_component_string.call( this, input ) ? _is_component_first.call( this, _unwrap_component_string( input ) ) : _matches(this[0].value,input)) : this.indexOf( input )===0; } function is_first_orphan( input ){ return _is_a_value.call( this, input ) ? _is_first_orphan_value.call( this, input ) : _is_first_orphan_link.call( this, input ); } function is_first_bound( input ){ return _is_a_value.call( this, input ) ? (_is_component_strin.call( this,input) ? _is_first_bound_component.call( this, _unwrap_component_string( input )) : _is_first_bound_value.call( this, input )) : (input.component!=null ? _is_first_bound_component.call( this, input ) : false); } function is_last( input ){ return _is_a_value.call( this, input ) ? ( _is_component_string.call( this, input ) ? _is_component_last.call( this, _unwrap_component_string.call( this, input ) ) : _matches(this.last.clipped,input)) : this.indexOf( input )===this.lastIndex; } function is_last_orphan( input ){ return _is_a_value.call( this, input ) ? _is_last_orphan_value.call( this, input ) : _is_last_orphan_link.call( this, input ); } function is_last_bound( input ){ return _is_a_value.call( this, input ) ? (_is_component_string.call( this,input) ? _is_last_bound_component.call( this, _unwrap_component_string.call( this, input )) : _is_last_bound_value.call( this, input )) : (input.component!=null ? _is_last_bound_component.call( this, input ) : false); } function exists( input ){ return _resolve_to_index.call( this, input )!=null; } function is_after( a, b ){ var these = this, subject = _resolve_to_index.call( this, a ), target = _resolve_to_index.call( this, b ); return subject!=null && target!=null ? subject > target : false } function is_before( a, b ){ var these = this, subject = _resolve_to_index.call( this, a ), target = _resolve_to_index.call( this, b ); return subject!=null && target!=null ? subject < target : false } function is_next( a, b ){ var these = this, subject = _resolve_to_index.call( this, a ), target = _resolve_to_index.call( this, b ); return subject!=null && target!=null ? subject==(target+1) : false } function is_previous( a, b ){ var these = this, subject = _resolve_to_index.call( this, a ), target = _resolve_to_index.call( this, b ); return subject!=null && target!=null ? subject==(target-1) : false } function is_adjacent( a, b ){ var these = this, subject = _resolve_to_index.call( this, a ), target = _resolve_to_index.call( this, b ); return subject!=null && target!=null ? Math.abs(subject-target)==1 : false } function is_bound( input ){ return _is_a_value.call( this, input ) ? (_is_component_string.call( this, input ) ? get_component_index.call( this, input )!=null : _is_bound_value.call( this, input )) : (_is_chain_link.call( this, input ) ? input.component!=null : false); } function is_orphan( input ){ return is_bound.call( this, input ) ? false : true; } function is_specified( input ){ return _is_a_value.call( this, input ) ? (_is_component_string.call( this, input ) ? get_component_index.call( this, _unwrap_component_string.call( this, input ) )!=null : _get_value_index.call( this, input )!=null) : _is_chain_link.call( this, input ); } function is_matched( input ){ return _is_a_value.call( this, input ) ? (_is_component_string.call( this, input ) ? _component_is_matched.call( this, _unwrap_component_string.call( this, input ) ) : (_is_bound_value.call( this, input ) ? _component_is_matched.call( this, _get_bound_component.call( this, input ) ) : false)) : (_is_chain_link.call( this, input ) ? (input.component!=null ? _component_is_matched.call( this, input.component ) : false) : false); } function is_unique( input ){ return _is_a_value.call( this, input ) ? (_is_component_string.call( this, input ) ? _is_unique_value.call( this, get_component_value.call( this, _unwrap_component_string.call( this,input) ) ) : _is_unique_value.call( this, input )) : (_is_chain_link.call( this, input ) ? _is_unique_value.call( this, input.value ) : false ); } function set_component_index( component, newIdx ){ return this[newIdx].component = component; } function get_component_index( component ){ var these = this, result = null; $.each( these, function( i, o ){ if( o.component==component ){ result = o.idx; return false; } }); return result; } function get_component_value( component ){ var these = this, result = null; $.each( these, function( i, o ){ if( o.component==component ){ result = o.clipped; return false; } }); return result; } function set_component_value( component, value ){ return value==null ? _unbind_component.call( this, component ) : _set_component_value.call( this, component, value ); } function get_component( component ){ var these = this, result = null; $.each( these, function( i, o ){ if( o.component==component ){ result = i; return false; } }); return result!=null ? this[result] : null; } } })(); (function(){ var _origins = new Array; Object.defineProperties( _origins, { create: { enumerable: true, get: function(){ return _create; }, set: noop } }); State.Origins = _origins; function _create( location, geometry, search ){ return new Origin( location, geometry, search ); }; function Origin( locationObj, geometryObj, searchObj ){ var _origin = this, _deadzone = true, _aliases = [searchObj.input_string], _index = State.Origins.length, _ranger = State.Ranger.create(), _previous_origin = $.type(State.Origin)==='undefined' ? null : State.Origin; Object.defineProperties(this,{ deadzone: { enumerable: true, get: function(){ return _deadzone; }, set: noop }, aliases: { enumerable: true, get: _get_aliases, set: noop }, bounds: { enumerable: true, get: function(){ return geometryObj.bounds; }, set: noop }, center: { enumerable: true, get: function(){ return geometryObj.center; }, set: noop }, index: { enumerable: true, get: function(){ return _index; }, set: noop }, latlng: { enumerable: true, get: function(){ return geometryObj.center.obj;}, set: noop }, lat: {enumerable: true, get: _lat, set: noop}, lat_rad: {enumerable: true, get: _latrad, set: noop}, latitude: { enumerable: true, get: _lat, set: noop }, lng: {enumerable: true, get: _lng, set: noop}, lng_rad: {enumerable: true, get: _lngrad, set: noop}, location: { enumerable: true, get: function(){ return locationObj; }, set: noop }, longitude: { enumerable: true, get: _lng, set: noop }, address: { enumerable: true, get: function(){ return locationObj.formatted; }, set: noop }, search: { enumerable: true, get: function(){ return searchObj; }, set: noop }, search_string: { enumerable: true, get: function(){ return searchObj.input_string; }, set: noop }, street_number: { enumerable: true, get: function(){ return locationObj.get_street_number(); }, set: noop }, route: { enumerable: true, get: function(){ return locationObj.get_route(); }, set: noop }, street: { enumerable: true, get: function(){ return locationObj.get_route(); }, set: noop }, street_address: { enumerable: true, get: function(){ return locationObj.get_street_address(); }, set: noop }, city: { enumerable: true, get: function(){ return locationObj.get_city_address(); }, set: noop }, state: { enumerable: true, get: function(){ return locationObj.get_region(); }, set: noop }, zipcode: { enumerable: true, get: function(){ return locationObj.get_postal(); }, set: noop }, postal_code: { enumerable: true, get: function(){ return locationObj.get_postal(); }, set: noop }, country: { enumerable: true, get: function(){ return locationObj.country_full; }, set: noop }, iso2: { enumerable: true, get: function(){ return locationObj.country; }, set: noop }, previousOrigin: { enumerable: true, get: function(){ return _previous_origin; }, set: noop }, ranger: { enumerable: true, get: function(){ return _ranger; }, set: noop } }); function _lat(){ return geometryObj.center.deg.lat; } function _latrad(){ return geometryObj.center.rad.lat; } function _lng(){ return geometryObj.center.deg.lng; } function _lngrad(){ return geometryObj.center.rad.lng; } function _get_aliases(){ return _aliases.slice(0,_aliases.length); } this.add_church = _add_church.bind( this ); this.get_churches = _get_churches.bind( this ); this.locate_churches = _locate_churches.bind( this ); this.activate = _activate.bind( this ); this.activate_churches = _activate_churches.bind( this ); this.activate_deadzone = _activate_deadzone.bind( this ); this.reactivate_churches = _reactivate_churches.bind( this ); this.is_different = function( str ){ return _aliases.indexOf( str )===-1; } function _add_church( church_id, distance ){ _deadzone = false; _ranger.add( church_id, distance ); } function _get_churches(){ return _ranger.get_churches; } function _reactivate_churches(){} function _activate(){ State.Origin = this; return _deadzone ? _activate_deadzone.call( this ) : _activate_churches.call( this ); } function _activate_churches(){ //_deadzone = false; // State.Origin = this; // // State.Map.map.fitBounds( this.bounds ); // State.UI.drop_user_pin( this.latlng ); //State.Origin = _origin; State.Map.map.fitBounds( _origin.bounds ); State.UI.drop_user_pin( _origin.latlng ); State.Results.clear(); State.Results.populate(); State.Results.activate(); } function _activate_deadzone(){ //_deadzone = true; //State.Origin = this; if( State.Options.allowFMAC==1 ){ State.Request.fmac( null ); }else{ return State.Mask.show_modal( 'no_results_notification', function(){ //console.log('CALLBACK'); }, function(){ //if( State.Origin.previousOrigin ){ //console.log('...previousOrigin'); // State.Origin.previousOrigin.reactivate(); //}else{ // console.log('...noOrigin'); // State.Location.no_origin(); //} } ); } } function _locate_churches(){ State.Churches.locate( this, this.activate ); //State.Churches.locate( this, this.activate_churches, this.activate_deadzone ); } State.Origins.push( this ); } })(); (function(){ var _churches = {}, factory = _factory, instance = new _factory; State.Churches = instance; Object.defineProperties(instance,{ churches: { enumerable: true, get: function(){ return _churches; }, set: noop } }); function _locate( origin, churchesCallback, deadzoneCallback ){ State.IO.postmethod('FindChurches',{ input: origin.search_string, latitude: origin.lat, longitude: origin.lng, formatted: origin.address, street: origin.street, city: origin.city, state: origin.state, zipcode: origin.zipcode, country: origin.country },function(responseObject){ if( $.type(responseObject)==='object' ){ State.IO.last_search_id = responseObject.search_id; return _register_churches( responseObject.churches, origin, churchesCallback ); //return responseObject.churches.length // ? _register_churches( responseObject, origin, churchesCallback ) // : deadzoneCallback(); // return responseObject.churches.length // ? _register_churches( responseObject, origin, churchesCallback ) // : deadzoneCallback(); }else{ console.error({responseObject: responseObject}); } }); } function _church_latlng( church_id ){ return $.type(_churches[church_id])==='object' ? _churches[church_id].latlng : null; } function _church_info( church_id ){ return $.type(_churches[church_id])==='object' ? _churches[church_id] : null; } function _register_churches( churches, origin, callback ){ $.each( churches, function(i,church){ var lat = church.latitude * 1, lng = church.longitude * 1, latlng = new google.maps.LatLng( lat, lng ), distance = church[State.Options.units] * 1; _churches[church.church_id] = { church_id: church.church_id * 1, church_name: church.church_name, church_status: church.church_status, church_website: church.church_website, church_language_id: church.church_language_id * 1, latlng: latlng, latitude: church.latitude * 1, longitude: church.longitude * 1, address_id: church.address_id * 1, street: church.address_street, address: church.address_street, city: church.address_city, state: church.address_state, postal: church.address_postal, zip: church.address_postal, country: church.address_country, formatted: [ church.address_street+',', church.address_city+',', church.address_state, church.address_postal+',', church.address_country ].join(' ') }; origin.add_church( church.church_id, distance ); }); return $.type(callback)==='function' ? callback() : true; } // function _register_churches( responseObject, churchesOrigin, callback ){ // // $.each( responseObject.churches, function(i,church){ // var // lat = church.latitude * 1, // lng = church.longitude * 1, // latlng = new google.maps.LatLng( lat, lng ), // distance = church[State.Options.units] * 1; // // _churches[church.church_id] = { // church_id: church.church_id * 1, // church_name: church.church_name, // church_status: church.church_status, // church_website: church.church_website, // church_language_id: church.church_language_id * 1, // latlng: latlng, // latitude: church.latitude * 1, // longitude: church.longitude * 1, // address_id: church.address_id * 1, // street: church.address_street, // city: church.address_city, // state: church.address_state, // postal: church.address_postal // }; // // churchesOrigin.add_church( church.church_id, distance ); // }); // // return $.type(callback)==='function' // ? callback() // : true; // } function _factory(){ if(instance instanceof _factory) return instance; this.locate = _locate; this.register_churches = _register_churches; this.info = _church_info; instance = this; } })(); (function(){ var factory = _factory, instance = new _factory; State.Ranger = instance; function _create_ranger(){ var output = new Object, units = State.Options.units, unit_size = State.Options.unit_size, unit_factor = State.Options.max_factor, final_max = unit_size * unit_factor, checks = [], range_idx = 0, previous_max = 0, new_max = 0, sizes = [], ranges = {}, range_totals = [], total = 0, sequence = []; output.total = 0; output.range_totals = {}; do{ new_max = unit_size * (++range_idx); sizes.push(new_max); ranges[new_max] = { idx: range_idx, min: previous_max, max: new_max, }; output[new_max] = {}; output.range_totals[new_max] = 0; previous_max = new_max; }while(new_max < final_max); output.ranges = ranges; output.range_sizes = sizes; output.min_range = unit_size; output.max_range = final_max; output.add = function( church_id, distance ){ var ranger=this, correct_range; $.each(this.ranges,function(i,r){ if( (i < ranger.max_range && distance >= ranger.ranges[i].min && distance < ranger.ranges[i].max) || (i==ranger.max_range && distance <= ranger.ranges[i].max) ){ correct_range = i; return false; } }); if($.type(this[correct_range])==='object'){ this[correct_range][Number(church_id)] = distance; this.range_totals[correct_range]++; this.total++; }else{ // console.log('ranger_error',{ // ranger: ranger, // correct_range: correct_range, // church_id: church_id, // distance: distance // }); } } output.get_range = function( from, to ){ var result = [], ranger = this, first_range = $.type(from)==='undefined' ? this.min_range : ($.type(this[from])==='object' ? from : this.min_range), last_range = $.type(to)==='undefined' ? this.max_range : ($.type(this[to])==='object' ? (to >= first_range ? to : this.max_range) : this.max_range); $.each(this.ranges,function(i,r){ if( i >= first_range && i <= last_range ){ $.each(ranger[i],function(id,d){ result.push({church_id:Number(id),distance:d}); }); } }); return result; } output.get_churches = function(){ return this.get_range( this.min_range, this.max_range ); } output.get_ranges = function(){ var ranger=this, outputObj = {}; $.each(this.ranges,function(i,r){ outputObj[i] = ranger[i]; }); return outputObj; } return output; } function _factory(){ if(instance instanceof _factory) return instance; this.create = _create_ranger; instance = this; } })(); (function(){ var factory = _factory, initComplete = false, instance = new _factory, personal_info = { first_name: '', last_name: '', phone: '', email: '' }, detectedLocation = null; State.Seeker = instance; Object.defineProperties( instance, { initComplete: { enumerable: true, get: function(){ return initComplete; }, set: noop }, first_name: { enumerable: true, get: function(){ return personal_info.first_name; }, set: function(v){ return personal_info.first_name = v; } }, last_name: { enumerable: true, get: function(){ return personal_info.last_name; }, set: function(v){ return personal_info.last_name = v; } }, phone: { enumerable: true, get: function(){ return personal_info.phone; }, set: function(v){ return personal_info.phone = v; } }, email: { enumerable: true, get: function(){ return personal_info.email; }, set: function(v){ return personal_info.email = v; } } }); function _init_error_handler( error ){ State.UI.show_init_error(); } function _init_seeker_response_handler( response ){ if($.type(response)==='object'){ //console.log({init: response}); if($.type(response.error)!=='undefined'){ _init_error_handler( response.error ); }else{ State.Localizer.localizedStrings = response.localization; State.Location.geoip_latitude = response.geoip_latitude; State.Location.geoip_longitude = response.geoip_longitude; State.UI.initialize(); State.Results.initialize(); initComplete = true; } }else _init_error_handler( response ); } function _factory(){ if(instance instanceof _factory) return instance; instance = this; this.initialize = function(){ State.IO.init_seeker( _init_seeker_response_handler ); } this.cache_personal_info = function( request_object ){ var flds = 'first_name,last_name,email,phone'.split(','); $.each(flds,function(i,e){ if($.type(request_object[e])==='string' && request_object[e].length){ personal_info[e] = request_object[e]; } }); } } })(); (function(){ var factory = _factory, search_id = null, instance = new _factory, transports = {}, visits = [], requested = [], clicks = { queue: [], ajax: null, ajaxState: 0, timer: null, timerState: 0, history: [] }; // Getters and Setters Object.defineProperties( instance, { last_search_id: { enumerable: true, get: function(){ return search_id; }, set: function(v){ return search_id = v; } }, requested: { enumerable: true, get: function(){ return requested; }, set: noop } }); State.IO = instance; function _request( params, _callback ){ return $.ajax({ type: 'POST', dataType: 'json', data: { action: 'church_search', _: { method: 'ChurchRequest', params: params } }, //url: State.initOptions.ajax_url, url: State.Options.ajax_url, success: _callback }); } function _clicks_ready(){ clicks.timerState = 0; return clicks.ajaxState===0 ? _send_clicks() : true; } function _send_clicks(){ var church_ids = clicks.queue.splice(0,clicks.queue.length); if( church_ids.length ){ //console.log({sending_clicks: church_ids}); clicks.ajaxState = 1; clicks.ajax = $.ajax({ type: 'POST', dataType: 'json', data: { action: 'church_search', _: { method: 'ChurchClicks', params: { church_ids: church_ids, search_id: search_id } } }, //url: State.initOptions.ajax_url, url: State.Options.ajax_url, success: _clicks_sent }); } return true; } function _clicks_sent(){ clicks.ajaxState = 0; return clicks.queue.length ? (clicks.timerState===0 ? _start_church_click_timer() : true) : true; } function _start_church_click_timer(){ clicks.timerState = 1; clearTimeout( clicks.timer ); clicks.timer = setTimeout( _clicks_ready, 5000 ); } function _church_click( church_id, callback ){ if(clicks.history.indexOf( church_id )===-1){ clicks.history.push( church_id ); clicks.queue.push( church_id ); return clicks.timerState===0 ? _start_church_click_timer( church_id ) : true; } } function _locate_churches(originData,callback){ $.ajax({ type: 'POST', dataType: 'json', data: { action: 'church_search', _: { method: 'FindChurches', params: originData } }, //url: State.initOptions.ajax_url, url: State.Options.ajax_url, success: callback, error: function(){ //console.log({error: Array.prototype.slice.call(arguments)}); } }); } function _visit_website( church_id, callback ){ if( visits.indexOf( church_id )===-1 ){ $.ajax({ type: 'POST', dataType: 'json', data: { action: 'church_search', _: { method: 'VisitWebsite', params: { church_id: church_id, search_id: search_id } } }, //url: State.initOptions.ajax_url, url: State.Options.ajax_url, success: function(){ visits.push( church_id ); } }); } } function _postmethod( method, args, callback ){ $.ajax({ type: 'POST', dataType: 'json', data: { action: 'church_search', _: { method: method, params: args } }, //url: State.initOptions.ajax_url, url: State.Options.ajax_url, success: callback, error: function(){ //console.log({error: Array.prototype.slice.call(arguments)}); } }); } function _init_seeker( responseHandler ){ var ajax = $.ajax({ type: 'POST', dataType: 'json', data: { action: 'church_search', _: { method: 'SeekerInit', params: { dlat: State.Location.detected_latitude, dlng: State.Location.detected_longitude, ua: State.UserAgent, ie: State.UserAgentIsIE } } }, //url: State.initOptions.ajax_url, url: State.Options.ajax_url, success: responseHandler, error: function(){ //console.log({error: Array.prototype.slice.call(arguments)}); } }); } function _test_ajax(){ $.ajax({ type: 'POST', dataType: 'json', data: { action: 'church_search', _: { method: 'TestAjax', params: {a:1,b:2} } }, //url: State.initOptions.ajax_url, url: State.Options.ajax_url, success: function(){ //console.log({success: Array.prototype.slice.call(arguments)}); }, error: function(){ //console.log({error: Array.prototype.slice.call(arguments)}); } }); } function _test_proxy(){ $.ajax({ type: 'POST', dataType: 'json', data: { action: 'church_search', _: { method: 'SeekerInit', params: { dlat: State.Location.detected_latitude, dlng: State.Location.detected_longitude, ua: State.UserAgent, ie: State.UserAgentIsIE } } }, //url: State.initOptions.ajax_url, url: State.Options.ajax_url, success: function(){ //console.log({success: Array.prototype.slice.call(arguments)}); }, error: function(){ //console.log({error: Array.prototype.slice.call(arguments)}); } }); } function _test_geoip(){ $.ajax({ type: 'POST', dataType: 'json', data: { action: 'church_search', _: { method: 'TestGeoip', params: {} } }, //url: State.initOptions.ajax_url, url: State.Options.ajax_url, success: function(){ //console.log({success: Array.prototype.slice.call(arguments)}); }, error: function(){ //console.log({error: Array.prototype.slice.call(arguments)}); } }); } function _test_search(){ $.ajax({ type: 'POST', dataType: 'json', data: { action: 'church_search', _: { method: 'TestSearch', params: {} } }, url: State.initOptions.ajax_url, success: function(){ //console.log({success: Array.prototype.slice.call(arguments)}); }, error: function(){ //console.log({error: Array.prototype.slice.call(arguments)}); } }); } function _test_query(){ $.ajax({ type: 'POST', dataType: 'json', data: { action: 'church_search', _: { method: 'TestQuery', params: {} } }, //url: State.initOptions.ajax_url, url: State.Options.ajax_url, success: function(){ //console.log({success: Array.prototype.slice.call(arguments)}); }, error: function(){ //console.log({error: Array.prototype.slice.call(arguments)}); } }); } function _destroy_proxy(){ $.ajax({ type: 'POST', dataType: 'json', data: { action: 'church_search', _: { method: 'DestroyProxy', params: {} } }, //url: State.initOptions.ajax_url, url: State.Options.ajax_url, success: function(){ //console.log({success: Array.prototype.slice.call(arguments)}); }, error: function(){ //console.log({error: Array.prototype.slice.call(arguments)}); } }); } function _factory(){ if(instance instanceof _factory) return instance; instance = this; this.init_seeker = _init_seeker; this.test_search = _test_search; this.test_query = _test_query; this.test_ajax = _test_ajax; this.test_google_geo = _test_geoip; this.test_proxy = _test_proxy; this.test_geoip = _test_geoip; this.destroy_proxy = _destroy_proxy; this.postmethod = _postmethod; this.locate_churches = _locate_churches; this.visit = _visit_website; this.click = _church_click; this.request = _request; } })(); (function(){ var range = 0, timer = null, active = null, focused = null, factory = _factory, ranges = new Object, bounds = new Object, markers = new Object, sequence = new Array, churches = new Object, instance = new _factory, initializing = false, cooperating_icon, participating_icon; State.Results = instance; Object.defineProperties( instance, { active: { enumerable: true, get: function(){ return active; }, set: function( church_id ){ if( $.type(church_id)==='undefined' || church_id==null ){ if( active ){ churches[ active ].deactivate(); } }else{ if( active!=church_id ){ churches[ active ].deactivate(); } churches[church_id].activate(); } } }, focused: { enumerable: true, get: function(){ return focused; }, set: noop }, churches: { enumerable: true, get: function(){ return churches; }, set: noop }, markers: { enumerable: true, get: function(){ return markers; }, set: noop }, range: { enumerable: true, get: function(){ return range; }, set: _change_range }, church_range: { enumerable: true, get: function(){ return ranges[State.UI.ranges[range]]; }, set: noop }, ranges: { enumerable: true, get: function(){ return ranges; }, set: noop }, lpr: { enumerable: true, get: _get_lowest_populated_range, set: noop } }); function _deactivate_active_result(){ return instance.active = null; } function _get_lowest_populated_range(){ var result = null; $.each(State.UI.ranges,function(rangeIdx,rangeDistance){ if(ranges[rangeDistance].length > 0){ result = rangeDistance; return false; } }); return result; } function _list_click_handler( e ){ var church_id = $(e.target).closest('li.result').attr('data-church-id'); State.IO.click( church_id ); return active!==church_id ? churches[church_id].activate() : stomp_event(e); } function _church_visit_handler(e){ State.IO.visit( $(e.target).closest('[data-church-id]').attr('data-church-id') ); return true; } function _church_request_handler(e){ var church_id = $(e.target).closest('[data-church-id]').attr('data-church-id') * 1; stomp_event(e); if(State.IO.requested.indexOf( church_id )===-1){ State.Request.request_church( church_id ); }else{ State.Mask.show_modal('already_requested'); } } function _marker_click_handler( church_id ){ State.IO.click( church_id ); churches[church_id].activate(); } function _marker_focus_handler( church_id ){ churches[church_id].focus(); } function _marker_blur_handler( church_id ){ churches[church_id].unfocus(); } function _change_range( newRange ){ var newClass='range_'+String(newRange), badRanges=[]; $.each(['range_0','range_1','range_2','range_3','range_4','range_5'],function(i,c){ if(c!=newClass){ badRanges.push(c); } }); if((newRange < range) && active && State.UI.ranges.indexOf(churches[active].range) > newRange ){ churches[active].deactivate(); } if((newRange < range) && focused && State.UI.ranges.indexOf(churches[focused].range) > newRange ){ churches[focused].unfocus(); } range = newRange; $('#results-wrapper').addClass(newClass).removeClass(badRanges.join(' ')); if($.type(State.Origin)!=='undefined' && !initializing){ _refresh_boundaries(); _drop_markers(); } } function _refresh_boundaries( ){ clearTimeout( timer ); timer = setTimeout( _do_refresh_bounds, 500 ); } function _do_refresh_bounds(){ var b, ne, sw; if( State.Results && State.Results.church_range && State.Results.church_range.length ){ ne = State.Results.church_range.bounds.getNorthEast(); sw = State.Results.church_range.bounds.getSouthWest(); b = new google.maps.LatLngBounds( sw, ne ); b.extend( State.Origin.latlng ); State.Map.map.fitBounds( b ); // State.Map.map.fitBounds( b ); //State.Map.map.fitBounds( State.Results.church_range.bounds ); } } function _activate_results(){ initializing = true; _fill_results_list(); _initialize_result_range( ); initializing = false; } function _initialize_results(){ $('#modal-wrapper').off('click','button.close-modal',_initialize_results); State.Mask.hide_modal(function(){ _refresh_boundaries(); _show_results(); _drop_markers(); State.UI.enable_global_ui(); }); } function _initialize_result_range(){ var lpr; if(State.Options.autoExpand==1){ lpr = _get_lowest_populated_range(); State.UI.radius = lpr; if(State.Options.autoExpandNotification==1 && lpr > State.UI.ranges[0]){ $('span.populated-range').text( lpr ); $('#modal-wrapper').one('click','button.close-modal',_initialize_results); return State.Mask.show_modal('autoexpand_notification'); } }else State.UI.radius = State.UI.ranges[0]; _initialize_results(); } function _drop_markers(){ var max_range = State.UI.ranges[range]; $.each(churches,function(church_id,church_result){ markers[church_id].setMap( church_result.range <= max_range ? State.Map.map : null ); }); } function _hide_results(){ $('#results-wrapper').addClass('hide-children'); } function _show_results(){ $('#results-wrapper').removeClass('hide-children'); } function _fill_results_list(){ _hide_results(); $('#results-wrapper .results-list').empty(); $.each(sequence,function(idx,church_id){ $('#results-wrapper .results-list').append( churches[church_id].markup ); }); } function _empty_results_list(){ $('#results-wrapper .results-list').empty(); } function _initialize(){ cooperating_icon = { url: State.Options.graphics.cooperating_pin, origin: new google.maps.Point(0,0), labelOrigin: new google.maps.Point(0,0), scaledSize: new google.maps.Size(30,30,"px","px"), size: new google.maps.Size(30,30,"px","px") }; participating_icon = { url: State.Options.graphics.participating_pin, origin: new google.maps.Point(0,0), labelOrigin: new google.maps.Point(0,0), scaledSize: new google.maps.Size(36,52,"px","px"), size: new google.maps.Size(48,70,"px","px") }; $('#results-wrapper').on('click','li.result:not(.current), li.result:not(.current) *',_list_click_handler); $('main').on('click','a.church-website',_church_visit_handler); $('main').on('click','a.church-contact',_church_request_handler); $.each(State.UI.ranges,function(idx,rad){ ranges[rad] = ChurchRange( idx, rad, 'range_'+String(idx) ); bounds[rad] = null; }); } function _populate_churches(){ sequence.splice(0,sequence.length); $.each(ranges,function(rangeRadius,rangeChurches){ rangeChurches.populate( State.Origin.ranger[rangeRadius] ); }); } function _compose_address( church ){ var outputs = [], inputs = $.trim( State.Options.address_format ).split( '\n' ); $.each( inputs, function( idx, ele ){ outputs.push( _compose_address_partial( church, ele ) ); }); return outputs; } function _compose_address_partial( church, templateStr ){ var klass, ptrn =/(?:\{\{([^\}]+)\}\})/gi, matches = templateStr.match( ptrn ), mapping = matches.map( function( e ){ return e.substr( 2, e.length-4 ); } ), outputStr = templateStr, counter = 0, len = mapping.length; while( mapping.length ){ var component = mapping.shift(), toFind = '{{'+component+'}}', replaceCandidate = church[ component ], replaceWith = replaceCandidate!=null ? replaceCandidate : ''; while( outputStr.indexOf( toFind ) > -1 && counter < 25){ outputStr = outputStr.replace( toFind, replaceWith ); } } klass = mapping.indexOf( 'street' ) || mapping.indexOf( 'address' ) ? 'street' : 'region'; return XTag( 'li.'+klass , $.trim( outputStr ) ); } function _clear_churches(){ $.each(ranges,function(rangeRadius,rangeChurches){ rangeChurches.clear(); }); } function _factory(){ if(instance instanceof _factory) return instance; instance = this; this.deactivate = _deactivate_active_result; this.recenter = _refresh_boundaries; this.initialize = _initialize; this.clear = _clear_churches; this.populate = _populate_churches; this.activate = _activate_results; this.hide_results = _hide_results; this.show_results = _show_results; } function _get_directions( church ){ return encodeURI([ '//maps.google.com/maps/dir', State.Origin.address, church.formatted ].join('/')); } function ResultWrapper( church_id, range, distance, classes ){ var church = $.extend({},State.Churches.churches[church_id]), website = church.church_website, statusClass = church.church_status==2 ? '.cooperating' : '.participating'; church.range = range; church.distance = distance; church.get_address_markup = _compose_address.bind( null, church ); church.directions = _get_directions( church ); if($.type(markers[church_id])==='undefined'){ markers[church_id] = new google.maps.Marker({ animation: 'DROP', position: State.Churches.churches[church_id].latlng, map: null, cursor: 'pointer', title: church.church_name, visible: true, icon: church.church_status==2 ? cooperating_icon : participating_icon, optimized: false, zIndex: 5 }); google.maps.event.addListener( markers[church_id], 'click', _marker_click_handler.bind( markers[church_id], church_id ) ); google.maps.event.addListener( markers[church_id], 'mouseover', _marker_focus_handler.bind( markers[church_id], church_id ) ); google.maps.event.addListener( markers[church_id], 'mouseout', _marker_blur_handler.bind( markers[church_id], church_id ) ); } church.marker = markers[church_id]; church.markup = XTag('li.result.'+classes + statusClass+'[data-church-id="'+String(church_id)+'"]', XTag('div.church-icon-wrapper'), XTag('div.church-info-wrapper', XTag('ul.church-details', XTag('li.title',church.church_name), XTag('li.address.collapsible', XTag('ul.address-items', _compose_address( church ) ) ), XTag('li.website.collapsible', church.church_website!=null && $.trim(church.church_website)!='' ? XTag('a.church-website[href="'+website+'"][target="_blank"][data-church-id="'+String(church_id)+'"]',State.Localizer('website_label','Church Website')) : null), XTag('li.connect', XTag('ul', XTag('li.distance', XTag('strong', State.Localizer('distance','Distance')+': '), XTag('span',State.Localizer('approximately','Approx')+' '), XTag('span.value',String(parseFloat(church.distance).toFixed(2))), XTag('span',State.Options.units=='miles' ? 'mi' : 'km')), XTag('li.contact.collapsible', State.Options.allowRequests==1 ? XTag('a.church-contact[href="javascript:"][target="_blank"][data-church-id="'+String(church_id)+'"]',State.Localizer('church_request_link','Request Contact')) : null ) ) ), XTag( 'li.directions.collapsible', XTag('a.church-directions[href="'+church.directions+'"][target="_blank"][data-church-id="'+String(church_id)+'"]',State.Localizer('directions_label','Get Directions')) ) ) ) ); church.bubble = XTag('div.church-info-wrapper', XTag('ul.church-details', XTag('li.title',church.church_name), XTag('li.address', XTag('ul.address-items', _compose_address( church ) ) ), XTag('li.website', church.church_website!=null && $.trim(church.church_website)!='' ? XTag('a.church-website[href="'+website+'"][target="_blank"][data-church-id="'+String(church_id)+'"]',State.Localizer('website_label','Church Website')) : null), XTag('li.contact', State.Options.allowRequests==1 ? XTag('a.church-contact[href="javascript:"][target="_blank"][data-church-id="'+String(church_id)+'"]',State.Localizer('church_request_link','Request Contact')) : null ) ) ); church.minimap = function(){ var uri, center, origin_lat = parseFloat(State.Origin.latlng.lat()).toFixed(5), origin_lng = parseFloat(State.Origin.latlng.lng()).toFixed(5), church_lat = parseFloat(church.latlng.lat()).toFixed(5), church_lng = parseFloat(church.latlng.lng()).toFixed(5), origin_latlng = String(origin_lat)+','+String(origin_lng), church_latlng = String(church_lat)+','+String(church_lng), bounds = new google.maps.LatLngBounds(), zoom = State.Map.map.getZoom(), key = State.Options.staticmap_key; bounds.extend( State.Origin.latlng ); bounds.extend( church.latlng ); center = bounds.getCenter(); return [ //'http://maps.googleapis.com/maps/api/staticmap?',\ '//maps.googleapis.com/maps/api/staticmap?', 'size=350x350', '&markers=color:red|'+church_latlng, '&markers=color:green|'+origin_latlng, //'&key=AIzaSyAGQYsrkpmfBfPJWz2og1Rprk9uAz5N0FE' '&key='+key ].join(''); } church.activate = function(){ if( active && active!=church_id ){ churches[active].deactivate(); } active = church_id; $('#results-wrapper li.result[data-church-id="'+String(church_id)+'"]').addClass('current'); State.Map.bubble = church_id; State.Map.map.panTo( church.latlng ); } church.deactivate = function(){ if(active==church_id){ active = null; State.Map.bubble = null; } $('#results-wrapper li.result[data-church-id="'+String(church_id)+'"]').removeClass('current'); } church.focus = function(){ focused = church_id; $('#results-wrapper li[data-church-id="'+String(church_id)+'"]').addClass('focused').siblings().removeClass('focused'); } church.unfocus = function(){ focused = focused==church_id ? null : focused; $('#results-wrapper li[data-church-id="'+String(church_id)+'"]').removeClass('focused'); } church.drop_pin = function(){ markers[church_id].setMap( State.Map.map ); } church.clear_pin = function(){ markers[church_id].setMap( null ); } church.clear = function(){ church.clear_pin(); delete markers[church_id]; } return church; } function ChurchRange( rangeIdx, rangeRadius, rangeClass ){ var churchRange = new Array, bounds = new google.maps.LatLngBounds(); Object.defineProperties(churchRange,{ bounds: { enumerable: true, get: function(){ return bounds; }, set: noop }, range: { enumerable: true, get: function(){ return rangeIdx; }, set: noop }, radius: { enumerable: true, get: function(){ return rangeRadius; }, set: noop }, rangeClass: { enumerable: true, get: function(){ return rangeClass; }, set: function(){ return rangeClass = v; } } }); churchRange.populate = function( range_results ){ $.each(range_results,function( church_id, distance ){ churchRange.push( church_id ); bounds.extend( State.Churches.churches[church_id].latlng ); churches[church_id] = new ResultWrapper( church_id, rangeRadius, distance, rangeClass ); }); if( rangeIdx > 0 ){ bounds.union( ranges[State.UI.ranges[rangeIdx-1]].bounds ); } churchRange.sort( distance_sorter ); sequence = sequence.concat( churchRange ); } churchRange.clear = function(){ bounds = new google.maps.LatLngBounds(); bounds.extend( State.Origin.latlng ); $.each(churchRange,function(idx,church_id){ churches[church_id].clear(); delete churches[church_id]; }); churchRange.splice(0,churchRange.length); //latlngs.splice(0,latlngs.length); } churchRange.drop_pins = function(){ $.each(churchRange,function(idx,church_id){ churches[church_id].drop_pin(); //markers[church_id].setMap( State.Map.map ); //churches[church_id].marker.setMap( State.Map.map ); }); } churchRange.clear_pins = function(){ $.each(churchRange,function(idx,church_id){ churches[church_id].clear_pin(); //markers[church_id].setMap( null ); //churches[church_id].marker.setMap( null ); }); } function distance_sorter( a, b ){ return churches[a].distance - churches[b].distance; } return churchRange; } })(); (function(){ var world, mapObj, bubbleObj, //pViewState, defaultBounds, defaultNorthEast, defaultSouthWest, factory = _factory, instance = new _factory, scrollTimer = null; State.Map = instance; Object.defineProperties( instance, { map: { enumerable: true, get: _getMap, set: noop }, status: { enumerable: true, get: _dump_map_data, set: noop }, bubble: { enumerable: true, get: _get_bubble, set: _set_bubble } }); function _get_bubble(){ return bubbleObj; } function _set_bubble( church_id ){ if( church_id==null ){ bubbleObj.close(); }else{ bubbleObj.setContent( State.Results.churches[ church_id ].bubble ); bubbleObj.open( mapObj, State.Results.churches[ church_id ].marker ); } } function _dump_map_data(){ var bounds = mapObj.getBounds(), center = mapObj.getCenter(), northEast = bounds.getNorthEast(), southWest = bounds.getSouthWest(), centerLat = center.lat(), centerLng = center.lng(); return { center: { latitude: center.lat(), longitude: center.lng() }, northEast: { latitude: northEast.lat(), longitude: northEast.lng() }, southWest: { latitude: southWest.lat(), longitude: southWest.lng() }, zoom: mapObj.getZoom() } } function _enable_resize_trigger(){ $( window ).on('resize',State.Results.recenter); } function _disable_resize_trigger(){ $( window ).off('resize',State.Results.recenter); } function _enable_initial_mode(){ $('body').addClass('map-animated'); _disable_resize_trigger(); mapObj.setZoom( 14 ); mapObj.setCenter( State.Location.detected_location ); _start_scroll(); } function _start_scroll(){ clearTimeout( scrollTimer ); scrollTimer = setTimeout( _scroll, State.Options.scroll_speed * 1 ); } function _scroll(){ clearTimeout( scrollTimer ); mapObj.panBy( 0.5, 0 ); _start_scroll(); } function _stop_scroll(){ clearTimeout( scrollTimer ); setTimeout(function(){ clearTimeout( scrollTimer ); }, 50 ); } function _disable_initial_mode(){ $('body').removeClass('map-animated'); _enable_resize_trigger(); _stop_scroll(); State.Results.recenter(); // if( State.Results && State.Results.church_range && State.Results.church_range.length ){ // State.Map.map.fitBounds( State.Results.church_range.bounds ); // } } function _getMap(){ return mapObj; } function _initialize(){ _init_viewport_defaults(); _init_map(); } function _init_viewport_defaults(){ var $O = State.Options, geoBounds = $O.geo_default, worldNE = new google.maps.LatLng({lat: 90, lng: 180}), worldSW = new google.maps.LatLng({lat: -90, lng: -180}); world = new google.maps.LatLngBounds( worldSW, worldNE ); defaultNorthEast = new google.maps.LatLng( geoBounds.n * 1, geoBounds.e * 1 ), defaultSouthWest = new google.maps.LatLng( geoBounds.s * 1, geoBounds.w * 1 ); defaultBounds = new google.maps.LatLngBounds( defaultSouthWest, defaultNorthEast ); switch( $O.location_bias ){ case 'geo_bias': State.Map.apply_bias = _get_geo_bias; break; case 'active_bias': State.Map.apply_bias = _get_active_bias; break; case 'world_bias': State.Map.apply_bias = _get_world_bias; break; default: State.Map.apply_bias = _get_auto_bias; break; } } function _init_map(){ mapObj = new google.maps.Map($('#map-canvas').get(0),{ keyboardShortcuts: false, zoom: parseInt(State.Options.defaultZoomLevel), minZoom: parseInt(State.Options.minZoomLevel), maxZoom: parseInt(State.Options.maxZoomLevel), mapTypeControl: false, streetViewControl: State.Options.enableStreetView==1, disableDefaultUi: true, center: defaultBounds.getCenter(),//new google.maps.LatLng( State.Options.defaultLatitude, State.Options.defaultLongitude ), mapTypeId: google.maps.MapTypeId.ROADMAP, styles: [ { featureType: "landscape", elementType: "geometry", stylers: [ { hue: "#0091ff" }, { lightness: 95 }, { saturation: -65 } ] }, { featureType: "water", elementType: "geometry", stylers: [ { color: "#0065AD" }, { lightness: 55 }, { saturation: -25 }, { visibility: "on" } ] }, { featureType: "water", elementType: "labels", stylers: [ { visibility: "off" } ] }, { featureType: "administrative.locality", elementType: "labels.text.fill", stylers: [ { hue: "#0091ff" }, { lightness: 22 }, { saturation: 100 }, { gamma: 0.25 } ] }, { featureType: "administrative.locality", elementType: "labels.text.stroke", stylers: [ { color: "#FFFFFF" }, { weight: 4 } ] }, { featureType: "poi", elementType: "geometry", stylers: [ { visibility: "off" } ] }, { featureType: "poi.sports_complex", elementType: "geometry", stylers: [ { visibility: "on" }, { color: "#0065AD" }, { saturation: -60 }, { lightness: 72 } ] }, { featureType: "poi.business", stylers: [ { visibility: "off" } ] }, { featureType: "poi.place_of_worship", stylers: [ { visibility: "off" } ] }, { featureType: "poi.park", stylers: [ { visibility: "off" } ] }, { featureType: "poi", elementType: "labels.icon", stylers: [ { hue: "#0091ff" }, { lightness: 22 }, { saturation: 100 }, { gamma: 0.25 } ] }, { featureType: "road", elementType: "geometry.fill", stylers: [ { color: "#dce5ed" } ] }, { featureType: "road", elementType: "labels.icon", stylers: [ { visibility: "off" } ] }, { featureType: "road.highway", elementType: "geometry.fill", stylers: [ { color: "#216AA6" }, { saturation: -45 }, { gamma: 1.25 } ] }, { featureType: "road.highway", elementType: "geometry.stroke", stylers: [ { color: "#dce5ed" } ] }, { featureType: "road.highway", elementType: "labels.icon", stylers: [ { visibility: "on" } ] }, { featureType: "road.highway", elementType: "labels.text", stylers: [ { visibility: "on" }, { lightness: 22 } ] }, { featureType: "road.arterial", elementType: "geometry.stroke", stylers: [ { color: "#216AA6" }, { saturation: -45 }, { gamma: 1.25 } ] }, { featureType: "road", elementType: "labels.text.stroke", stylers: [ { color: "#FFFFFF" }, { weight: 5 } ] }, { featureType: "road.local", elementType: "geometry.stroke", stylers: [ { color: "#216AA6" }, { saturation: -45 }, { gamma: 1.25 } ] }, { featureType: "road.local", elementType: "labels", stylers: [ { visibility: "off" } ] }, { featureType: "transit.station", elementType: "geometry", stylers: [ { color: "#0065ad" }, { lightness: 80 }, { saturation: -65 } ] } ] }); State.Geocoder = new google.maps.Geocoder(); State.Places = new google.maps.places.PlacesService( mapObj ); bubbleObj = new google.maps.InfoWindow(); google.maps.event.addListener( bubbleObj, 'closeclick', State.Results.deactivate ); $( '#map-wrapper' ).on( State.transitionEvent, google.maps.event.trigger.bind( null, State.Map.map, 'resize' ) ); google.maps.event.addListenerOnce( mapObj, 'tilesloaded', function(){ $('body').addClass('map-ready'); _enable_initial_mode(); }); } function _get_auto_bias( obj ){ delete obj.bounds; return true; } function _get_geo_bias( obj ){ return obj.bounds = defaultBounds; } function _get_active_bias(obj ){ return obj.bounds = mapObj.getBounds(); } function _get_world_bias(obj ){ return obj.bounds = world; } function _factory(){ if(instance instanceof _factory) return instance; instance = this; this.initialize = _initialize; this.reinitialize = noop; this.marker_hack = function(){ setTimeout(function(){ var cnt = mapObj.getCenter(); cnt.e+=0.000001; mapObj.panTo(cnt); cnt.e-=0.000001; mapObj.panTo(cnt); //console.log('marker_hack'); },400); } this.enable_initial_mode = _enable_initial_mode; this.disable_initial_mode = _disable_initial_mode; } })(); (function(){ var shown = false, afterClose = null, instance = new _factory, transitionEvent = function (){ var t, el = document.createElement('fakeelement'), transitions = { 'transition':'transitionend', 'OTransition':'oTransitionEnd', 'MozTransition':'transitionend', 'WebkitTransition':'webkitTransitionEnd' }; for(t in transitions){ if( el.style[t] !== undefined ){ return transitions[t]; } } }(); State.transitionEvent = transitionEvent; Object.defineProperties( instance, { shown: { enumerable: true, get: function(){ return shown; }, set: noop } }); State.Mask = instance; function _show_loader(){ $('#modal-wrapper').addClass('ajax-working'); } function _hide_loader(){ $('#modal-wrapper').removeClass('ajax-working'); } function _show_modal( modal, callback, afterCloseCback ){ if( afterCloseCback ){ afterClose = afterCloseCback; } if(!shown){ $('body').addClass('no-layout-transitions'); $('#modal-wrapper [data-modal-id="'+modal+'"]').addClass('active'); _show_mask( callback ); }else{ _transition_to( modal, callback, afterCloseCback ); } } function _transition_to( modal, callback, afterCloseCback ){ var leaving = $('#modal-wrapper [data-modal-id].active'), entering = $('#modal-wrapper [data-modal-id="'+modal+'"]'); setTimeout(function(){ leaving.addClass('faded'); setTimeout(function(){ leaving.removeClass('active faded'); entering.addClass('active faded'); setTimeout(function(){ entering.removeClass('faded'); if($.type(callback)==='function'){ callback(); } },60); },60); },200); } function _hide_modal( callback ){ if( shown ){ if($.type(afterClose)==='string'){ _show_modal( afterClose, function(){ afterClose = null; } ); }else{ _hide_mask(function(){ $('body').removeClass('no-layout-transitions'); $('#modal-wrapper [data-modal-id].active').removeClass('active'); if($.type(callback)==='function'){ callback(); } }); } }else{ if($.type(callback)==='function'){ callback(); } if($.type(afterClose)==='function'){ var cback = afterClose; afterClose = null; cback(); } } } function _show_mask( callBack ){ shown = true; _clear_fade_listener(); if( $('#modal-backdrop').hasClass('fading-in') ){ $('#modal-backdrop').removeClass('fading-in'); } $('#modal-backdrop').one( transitionEvent, function(e){ $(e.target).addClass('in').removeClass('fading-in'); if($.type(callBack)==='function'){ callBack(); } }) setTimeout(function(){ $('#modal-backdrop').addClass('fading-in').removeClass('fading-out'); },100); } function _hide_mask( callBack ){ _clear_fade_listener(); if( $('#modal-backdrop').hasClass('fading-out') ){ $('#modal-backdrop').removeClass('fading-out'); } $('#modal-backdrop').one( transitionEvent, function(e){ $(e.target).removeClass('fading-out in'); shown = false; if($.type(callBack)==='function'){ callBack(); } if($.type(afterClose)==='function'){ var cback = afterClose; afterClose = null; cback(); } }); setTimeout(function(){ $('#modal-backdrop').addClass('fading-out').removeClass('fading-in'); },100); } function _clear_fade_listener(){ $('#modal-backdrop').off( transitionEvent ); } function _factory(){ if(instance instanceof _factory) return instance; instance = this; this.show = _show_mask; this.hide = _hide_mask; this.show_modal = _show_modal; this.hide_modal = _hide_modal; this.show_loader = _show_loader; this.hide_loader = _hide_loader; } })(); (function(){ var factory = _factory, instance = new _factory; Object.defineProperties( instance, { shown: { enumerable: true, get: function(){ }, set: noop } }); State.Modal = instance; function _search_failed( result ){ } function _factory(){ if(instance instanceof _factory) return instance; instance = this; } })(); (function(){ // var Queue = new Array; // // Object.defineProperties( Queue, { // // }); })(); (function(){ var acListener, stepper = {ranges:[5,10,15,20]}, radius = 0, factory = _factory, churchMarkers = [], seekerMarker = null, searchFieldTimer = null, radiusListenerState = false, locationListenerState = false, viewStateListenerState = false, onCloseCallbackFn = null, pViewState = 's', seeker_icon, seeker_pin = null, church_ranger = null, recenterTimer = null, instance = new _factory; // Getters and Setters Object.defineProperties( instance, { ranges: { enumerable: true, get: function(){ return stepper.ranges; }, set: noop }, radius: { enumerable: true, get: _current_radius, set: _current_radius }, seeker_pin: { enumerable: true, get: function(){ return seeker_pin; }, set: function(v){ return seeker_pin=v; } }, initState: { enumerable: true, get: _get_initstate, set: _set_initstate }, loadingState: { enumerable: true, get: _get_loadingstate, set: _set_loadingstate }, viewState: { enumerable: true, get: _get_viewstate, set: _set_viewstate }, viewStateListener: { enumerable: true, get: _viewstate_listener, set: _viewstate_listener }, locationListener: { enumerable: true, get: _location_listener, set: _location_listener } }); State.UI = instance; function _close_message_modal(){ //console.log('close modal'); State.Mask.hide_modal(); State.UI.enable_global_ui(); } function _initialize_ui(){ stepper = new Stepper(); _start_radius_listener(); $('#search-field').on({ focus: function(e){ $('#search-address-wrapper').addClass('focused'); }, blur: function(e){ $('#search-address-wrapper').removeClass('focused'); } }); $('#modal-wrapper').on('click','button.close-modal',_close_message_modal); $('button.collapse-btn').on('click',function(e){ $(e.target).closest('.shown').removeClass('shown'); }); $('body').on('click','.wmc-toggle-wrapper a',function(e){ e.preventDefault(); _toggle_front_wmc(); }); State.UI.loadingState = false; seeker_icon = { url: State.Options.graphics.seeker_pin, origin: new google.maps.Point(0,0), labelOrigin: new google.maps.Point(0,0), scaledSize: new google.maps.Size(40,60,"px","px"), size: new google.maps.Size(40,60,"px","px") }; if( $('#search-field').val() ){ _generate_location_event(); } } function _enable_global_ui(){ $('#search-form').removeClass('disabled').prop('disabled',false); $('#search-field').prop('disabled',false); $('#btn_clear').prop('disabled',false); $('#btn_submit').prop('disabled',false); $('#search-radius-wrapper button').prop('disabled',false); $('footer.page-footer button').css('pointer-events','all'); $('form.view-controller').removeClass('disabled',false).prop('disabled',false); $('form.view-controller input').prop('disabled',false); } function _disable_global_ui(){ $('#search-form').addClass('disabled').prop('disabled',true); $('#search-field').prop('disabled',true); $('#btn_clear').prop('disabled',true); $('#btn_submit').prop('disabled',true); $('#search-radius-wrapper button').prop('disabled',true); $('footer.page-footer button').css('pointer-events','none'); $('form.view-controller').addClass('disabled',true).prop('disabled',true); $('form.view-controller input').prop('disabled',true); } // Radius function _change_radius( new_radius ){ radius = new_radius; State.Results.range = stepper.ranges.indexOf( radius ); $('#search-radius-wrapper .distance-value').text( new_radius ); stepper.refresh(); } function _current_radius(v){ return $.type(v)==='undefined' ? _get_current_radius() : _set_current_radius(v); } function _get_current_radius(){ return radius; } function _set_current_radius(v){ return stepper.set_radius( v ); } function _start_radius_listener(){ if(radiusListenerState===false){ $('#search-radius-wrapper').one('click','button',_radius_event_handler); radiusListenerState = true; //console.log('_start_radius_listener'); } } function _stop_radius_listener(){ radiusListenerState = false; $('#search-radius-wrapper').off('click','button',_radius_event_handler); } function _radius_event_handler(e){ stomp_event(e); radiusListenerState = false; return !!($(e.target).closest('button').attr('id')=='btn_increase_radius') ? stepper.increase_radius() : stepper.decrease_radius(); } // Map function _drop_user_pin( latlng ){ if(seekerMarker==null){ seekerMarker = new google.maps.Marker({ position: latlng, map: State.Map.map, title: State.Localizer('pin_label','My Location'),//State.Localizer.get_string('pin_label'), visible: true, //icon: State.initOptions.graphics.seeker_pin, //icon: State.Options.graphics.seeker_pin, icon: seeker_icon, optimized: false }); }else{ seekerMarker.setPosition( latlng ); seekerMarker.setMap( State.Map.map ); seekerMarker.setVisible( true ); } } function _clear_user_pin(){ if(seekerMarker!=null){ seekerMarker.setMap(null); } } function _populate_origin_churches(){ State.Origin.populate_churches(); // var // radius = State.UI.radius, // ranger = State.Origin.ranger, // churches = []; // // $.each(ranger.ranges,function(maxRadius,rangeDetails){}); // // console.log({ // radius: radius, // ranger: ranger // }); } // loading state function _get_loadingstate(){ return $('#search-form').hasClass('loading'); } function _set_loadingstate(val){ //console.log({set_loadingstate: val}); return $('#search-form')[!!val ? 'addClass' : 'removeClass']('loading'); } // initial state function _get_initstate(){ // return $('header.page-header').hasClass('initial'); return $('body').hasClass('initial'); } function _set_initstate(val){ // return $('header.page-header')[!!val ? 'addClass' : 'removeClass']('initial'); return !!val ? _enable_initstate() : _disable_initstate(); // return $('body')[!!val ? 'addClass' : 'removeClass']('initial'); } function _enable_initstate(){ pViewState = _get_viewstate(); $('body').addClass('initial'); return State.Map.enable_initial_mode(); } function _disable_initstate(){ _set_viewstate( pViewState ); if( $('body').hasClass('initial') ){ $('body').removeClass('initial'); } return State.Map.disable_initial_mode(); } // front side WMC function _toggle_front_wmc(){ $('body').hasClass('wmc') ? $('body').removeClass('wmc') : $('body').addClass('wmc'); } // location function _location_listener(val){ switch(true){ case val===true: _start_location_listener(); break; case val===false: _stop_location_listener(); break; default: return locationListenerState; break; } } function _start_location_listener(){ if(locationListenerState===false){ locationListenerState = true; clearTimeout( searchFieldTimer ); if($.type(acListener)==='undefined'){ acListener = State.autocomplete.addListener('place_changed',_generate_location_event); } $('#header-interior').on('submit','#search-form',_generate_location_event); //$('#search-address-wrapper').on('focus','button',_generate_location_event); $('#search-address-wrapper').on('focus','#btn_submit',_generate_location_event); $('#search-form').one('location_entered','#search-field',_receive_location_event); } } function _stop_location_listener(){ locationListenerState = false; clearTimeout( searchFieldTimer ); //google.maps.event.removeListener(acListener); //State.autocomplete.removeListener('place_changed',_generate_location_event); $('#header-interior').off('submit','#search-form',_generate_location_event); //$('#search-address-wrapper').off('focus','button',_generate_location_event); $('#search-address-wrapper').off('focus','#btn_submit',_generate_location_event); $('#search-form').off('location_entered','#search-field',_receive_location_event); } function _generate_location_event(e){ stomp_event(e); $('#search-field').trigger( $.Event('location_entered') ); } function _receive_location_event(){ var search_string = $.trim( $('#search-field').val() ); clearTimeout( searchFieldTimer ); searchFieldTimer = setTimeout(function(){ $('#search-form').one('location_entered','#search-field',_receive_location_event); },250); State.Location.location_string = search_string; } // viewstate function _get_viewstate(){ return $('form.view-controller input[name="view"]:checked').val(); } function _set_viewstate(v){ $( '#map-wrapper' ).one( State.transitionEvent,function(){ clearTimeout( recenterTimer ); recenterTimer = setTimeout(function(){ if( State.Results && State.Results.church_range && $.type(State.Results.church_range.bounds)!=='undefined' ){ State.Map.map.fitBounds( State.Results.church_range.bounds ); } },500); }); switch(v){ case 'l': $('main').addClass('list-view').removeClass('map-view'); break; case 's': $('main').addClass('list-view map-view'); break; case 'm': $('main').addClass('map-view').removeClass('list-view'); break; } } function _viewstate_listener(val){ switch(true){ case val===true: _start_viewstate_listener(); break; case val===false: _stop_viewstate_listener(); break; default: return viewStateListenerState; break; } } function _update_viewstate(e){ stomp_event(e); instance.viewState = instance.viewState; } function _start_viewstate_listener(){ if(viewStateListenerState===false){ viewStateListenerState = true; $('form.view-controller').on('change',_update_viewstate); } } function _stop_viewstate_listener(){ viewStateListenerState = false; $('form.view-controller').off('change',_update_viewstate); } function _factory(){ if(instance instanceof _factory) return instance; instance = this; this.markers = []; this.populate_origin_churches = _populate_origin_churches; this.drop_user_pin = _drop_user_pin; this.clear_user_pin = _clear_user_pin; this.enable_global_ui = _enable_global_ui; this.disable_global_ui = _disable_global_ui; this.enable_initstate = _enable_initstate; this.disable_initstate = _disable_initstate; this.toggle_front_wmc = _toggle_front_wmc; this.show_init_error = function(){ return $('#search-form').addClass('error').removeClass('loading'); } this.initialize = _initialize_ui; } function Stepper(){ var //unit = State.initOptions.units, unit = State.Options.units, ranger = State.Ranger.create(); //console.log({empty_ranger: ranger}); this.set_radius = function(v){ return v >= ranger.min_range && v <= ranger.max_range ? _change_radius( v ) : false; } this.increase_radius = function(e){ stomp_event(e); //console.log('this.increase_radius'); return radius < ranger.max_range ? _change_radius( radius + ranger.min_range ) : _start_radius_listener(); } this.decrease_radius = function(e){ stomp_event(e); //console.log('this.decrease_radius'); return radius > ranger.min_range ? _change_radius( radius - ranger.min_range ) : _start_radius_listener(); } this.refresh = function(){ var minFn = radius==ranger.min_range ? 'addClass' : 'removeClass', maxFn = radius==ranger.max_range ? 'addClass' : 'removeClass'; $('#search-radius-wrapper')[minFn]('at-min')[maxFn]('at-max'); _start_radius_listener(); } this.ranges = ranger.range_sizes; radius = ranger.min_range; $('#search-radius-wrapper .distance-value').text( radius ); //$('#search-radius-wrapper [data-localized-unit="distance"]').text( State.Options.units ); } })(); (function(){ var form_element, form_values, submit_btn, church_id, troubleTimer = null, requestAjax = null, request_type = 'fmac', factory = _factory, fieldNames = 'first_name last_name email confirm_email phone street city state zipcode country'.split(' '), instance = new _factory; State.Request = instance; Object.defineProperties( instance, { request_type: { enumerable: true, get: function(){ return request_type; }, set: function(t){ request_type = t; return request_type=='fmac' ? $('[data-modal-id="church_request"]').removeClass('church-request').addClass('fmac') : $('[data-modal-id="church_request"]').removeClass('fmac').addClass('church-request'); } } }); function _cancel_request(){ State.Mask.hide_modal(function(e){ State.UI.enable_global_ui(); if( State.Origin.deadzone ){ if( State.Origin.previousOrigin ){ State.Origin.previousOrigin.activate(); }else{ State.Location.no_origin(); } } }); } function _prefill(){ _prefill_church(); _prefill_seeker(); } function _set_church( new_church_id ){ church_id = new_church_id; } function _prefill_church(){ var church = State.Results.churches[church_id], minimap_url = church.minimap(), encodedURI = encodeURI(minimap_url); $('#request_form .church-details').empty() .append( XTag( 'li.title', church.church_name ) ) .append( church.get_address_markup() ); $('#request_form .map-wrapper').css({'background-image': "url('"+encodedURI+"')"}); } function _prefill_seeker(){ State.Request.church_form.set_values({ first_name: State.Seeker.first_name, last_name: State.Seeker.last_name, phone: State.Seeker.phone, email: State.Seeker.email, confirm_email: '', street: State.Origin.street_address, city: State.Origin.city, state: State.Origin.state, zipcode: State.Origin.zipcode, country: State.Origin.country }); $.each(['city','state','zipcode','country'],function(i,field){ $( State.Request.church_form.fields[field].element ).prop( 'disabled', State.Request.church_form.values[field]!='' ); }); } function _request_church( church ){ State.Request.request_type = 'church-request'; State.UI.disable_global_ui(); _set_church( church ); _prefill(); State.Mask.show_modal( 'church_request', function(){ State.Map.bubble = null; $('#modal-wrapper').addClass('form'); //$('body').addClass('map-animated'); State.Map.enable_initial_mode(); google.maps.event.trigger( State.Map.map, 'resize' ); }, function(){ $('#modal-wrapper').removeClass('form'); //$('body').removeClass('map-animated'); State.Map.disable_initial_mode(); google.maps.event.trigger( State.Map.map, 'resize' ); } ); } function _fmac(){ State.Request.request_type = 'fmac'; State.UI.disable_global_ui(); _prefill_seeker(); State.Mask.show_modal( 'church_request', function(){ State.Map.bubble = null; $('#modal-wrapper').addClass('form'); //$('body').addClass('map-animated'); State.Map.enable_initial_mode(); google.maps.event.trigger( State.Map.map, 'resize' ); }, function(){ $('#modal-wrapper').removeClass('form'); //$('body').removeClass('map-animated'); State.Map.disable_initial_mode(); google.maps.event.trigger( State.Map.map, 'resize' ); } ); } function _request_success( church_id, response ){ State.Mask.hide_loader(); $('#modal-wrapper').removeClass('form'); // console.log({ // response: response, // response_type: $.type(response), // request_id: response.request_id, // request_id_type: $.type(response.request_id) // }); //$('body').removeClass('map-animated'); State.Map.disable_initial_mode(); if( $.type(response)==='object' && $.type(response.request_id)!=='undefined' && response.request_id!=null ){ if( $.type(church_id)!=='undefined' && church_id!=null ){ State.IO.requested.push( church_id ); } State.Mask.show_modal( 'request_received', null, function(){ if( State.Origin.deadzone ){ if( State.Origin.previousOrigin ){ //State.Origin.previousOrigin.reactivate(); State.Origin.previousOrigin.activate(); }else{ State.Location.no_origin(); } } } ); }else{ State.Mask.show_modal( 'request_failed', null, function(){ if( State.Origin.deadzone ){ if( State.Origin.previousOrigin ){ //State.Origin.previousOrigin.reactivate(); State.Origin.previousOrigin.activate(); }else{ State.Location.no_origin(); } } } ); } } function _do_send_church_request( formProxy ){ var request_data = $.extend( {}, State.Request.church_form.values, {country: State.Origin.iso2}); State.Seeker.cache_personal_info( request_data ); State.Mask.show_loader(); request_data.church_id = church_id; request_data.search_id = State.IO.last_search_id; State.IO.request( request_data, _request_success.bind( null, church_id ) ); } function _do_send_fmac_request( formProxy ){ var request_data = $.extend( {}, State.Request.church_form.values, {country: State.Origin.iso2}); State.Mask.show_loader(); request_data.church_id = null; request_data.search_id = State.IO.last_search_id; State.IO.request( request_data, _request_success.bind( null, null ) ); } function _do_send_request( formProxy ){ //_set_trouble_timer( 10, _oops_ return requestAjax = request_type=='church-request' ? _do_send_church_request( formProxy ) : _do_send_fmac_request( formProxy ); // return request_type=='church-request' // ? _do_send_church_request( formProxy ) // : _do_send_fmac_request( formProxy ); } function _check_submission(formProxy){ var valid=true, errorList = $('[data-modal-id="form_errors"] li').removeClass('shown'); $.each(formProxy.validation,function(field,result){ if(result!==true){ valid = false; $('[data-modal-id="form_errors"] li[data-error-field="'+field+'"]').addClass('shown'); } }); return valid; } function _initialize(){ State.Request.church_form = $('#request_form').dynaform({ fn: { lock_submit_btn: function(e,formProxy){ $('#request_form .submit-request-button').lock(); }, unlock_submit_btn: function(e,formProxy){ $('#request_form .submit-request-button').unlock(); }, send_request: function(e,formProxy){ stomp_event(e); if( _check_submission(formProxy) ){ _do_send_request( formProxy ); }else{ State.Mask.show_modal('form_errors',null,'church_request'); } }, cancel_request: function(e,formProxy){ stomp_event(e); _cancel_request(); } }, fields: { first_name: { type: 'text', value: null, validate: 'not_empty', parameter: 'first_name', label: 'First Name' }, last_name: { type: 'text', value: null, parameter: 'first_name', label: 'Last Name' }, email: { type: 'text', value: null, validate: 'is_email', parameter: 'email', label: 'Email Address' }, confirm_email: { type: 'text', value: null, validate: function(value,proxy){ return $.trim(value) != $.trim(proxy.formProxy.values.email) ? 'Emails must match' : true; }, parameter: 'confirm_email', label: 'Confirm Email Address' }, phone: { type: 'text', value: null, validate: 'valid_phone', parameter: 'phone', label: 'Phone Number' }, street: { type: 'text', value: null, validate: 'not_empty', // validate: function(value,proxy){ // var // sides = $.trim(value).split(/\s/,2), // numerics = sides.shift(), // alphanumerics = sides.pop(); // return /[0-9]/.test(numerics) && alphanumerics.length >= 2 // ? true // : 'Please enter your full street address'; // }, parameter: 'street', label: 'Street Address' }, city: { type: 'text', value: null, validate: 'not_empty', //required: false, parameter: 'city', label: 'City' }, state: { type: 'text', value: null, validate: true,//'not_empty', //required: false, parameter: 'state', label: 'State/Province' }, zipcode: { type: 'text', value: null, validate: true,//'not_empty', //required: false, parameter: 'zipcode', label: 'Postal Code' }, country: { type: 'text', value: null, validate: 'not_empty', parameter: 'country', label: 'Country' } }, init: noop }).data('dynaform'); } function _factory(){ if(instance instanceof _factory) return instance; this.cancel = _cancel_request; this.initialize = _initialize; this.request_church = _request_church; this.fmac = _fmac; instance = this; } })(); function init(options){ State.UserAgent = navigator.userAgent||navigator.vendor||window.opera; State.UserAgentIsIE = /(MSIE|Trident)(\s|\/)[\d.]+/i.test(State.UserAgent); if( State.UserAgentIsIE ){ $('body').addClass('ie-legacy'); } State.Options.initialize( options ); State.Map.initialize(); State.Seeker.initialize(); State.Request.initialize(); $('#optin-wrapper').on('click','a.heading',function(e){ stomp_event(e); $('#optin-wrapper').toggleClass('shown'); }); $('#toggle-disclaimer').on('click',function(e){ stomp_event(e); $('#disclaimer-wrapper').toggleClass('shown'); }); initializeAutocomplete(); function initializeAutocomplete(){ var autocompleteOptions = {types:['address']}; State.Map.apply_bias( autocompleteOptions ); State.autocomplete = new google.maps.places.Autocomplete( document.getElementById('search-field'), autocompleteOptions ); if( State.Options.geo_bias == 'active_bias' ){ State.autocomplete.bindTo( 'bounds', State.Map.map ); } } State.UI.locationListener = true; State.UI.viewStateListener = true; } function noop(){} function throttle( msec, clip, cback, mode ){ if( typeof clip!=='boolean'){ mode=cback; cback=clip; clip=undefined; } var timeout_id, last_exec=0, wrapper=function(){ var that=this, elapsed=+new Date()-last_exec, args=arguments, exec=function(){ last_exec=+new Date(); cback.apply(that,args); }, clear=function(){ timeout_id=undefined; }; if( mode && !timeout_id){ exec(); } timeout_id && clearTimeout(timeout_id); if( mode===undefined && elapsed > msec){ exec(); }else if(clip!==true){ timeout_id=setTimeout( mode ? clear : exec, mode===undefined ? msec-elapsed : msec); } }; if($.guid){ wrapper.guid=cback.guid=( cback.guid || $.guid++); }; return wrapper; } function debounce( msec, before, cback ){ return cback===undefined ? throttle( msec, before, false) : throttle( msec, cback, before!==false); } function stomp_event(e){ if($.type(e)==='object' && $.type(e.preventDefault)==='function'){ e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation(); } } function rad( deg ){ return deg * Math.PI / 180; } function deg( rad ){ return rad * 180 / Math.PI; } jQuery.fn.church_search=init; // jQuery.fn.church_search=function(options){ // Methods.init.apply(this,arguments); // }; })(jQuery);