├── .editorconfig ├── .gitignore ├── .jshintrc ├── .travis.yml ├── CNAME ├── civic.json ├── config.json ├── data.geojson ├── get-help-lex-geocode.gif ├── google-apps-feedback-script.js ├── img ├── GetHelpLextype.png ├── bullhorn-solid.svg ├── comment-alt-solid.svg ├── home-solid.svg ├── info-circle-solid.svg ├── logo.png ├── map-marker-alt-solid.svg ├── print-solid.svg ├── search-solid.svg └── spinner.gif ├── index-app.html ├── index.html ├── karma.conf.js ├── kentucky substance abuse referral list.csv ├── lib ├── bootstrap.min.css ├── bootstrap.min.js ├── d3.min.js ├── es5-sham.min.js ├── es5-shim.min.js ├── flight.min.js ├── fuse.min.js ├── handlebars.js ├── jquery-1.11.1.min.js ├── leaflet.markercluster │ ├── MarkerCluster.Default.css │ ├── MarkerCluster.css │ ├── leaflet.markercluster-src.js │ └── leaflet.markercluster.js ├── leaflet │ ├── L.Control.Locate.css │ ├── L.Control.Locate.js │ ├── images │ │ ├── layers-2x.png │ │ ├── layers.png │ │ ├── locate.png │ │ ├── locate@2x.png │ │ ├── locate_touch.png │ │ ├── marker-icon-2x.png │ │ ├── marker-icon-gray.png │ │ ├── marker-icon.png │ │ ├── marker-shadow.png │ │ ├── spinner.gif │ │ └── spinner@2x.gif │ ├── leaflet-src.js │ ├── leaflet.css │ └── leaflet.js ├── lodash.min.js ├── require.js ├── tabletop.js └── text.js ├── license.md ├── package.json ├── readme.md ├── rules-engine-questions.docx ├── script ├── build.js └── copyfiles.js ├── src ├── data │ ├── analytics.js │ ├── config.js │ ├── facet.js │ ├── geojson.js │ ├── search.js │ └── typeahead.js ├── infotemplates.js ├── script.js ├── templates │ ├── assessment.html │ ├── extraResources.html │ ├── facet.html │ ├── facetControls.html │ ├── form.html │ ├── input.html │ ├── medicalDetoxPrograms.html │ ├── medicationAssistedTreatment.html │ ├── twelveStepPrograms.html │ └── welcome.html ├── timed_with_object.js └── ui │ ├── back-to-top.js │ ├── facet.js │ ├── feedback_widget.js │ ├── filtering.js │ ├── info.js │ ├── list.js │ ├── loading.js │ ├── map.js │ ├── project.js │ ├── scroll.js │ ├── search.js │ ├── search_results.js │ ├── select_county.js │ └── tabs.js ├── styles ├── images │ ├── loader.gif │ ├── search-icon-mobile.png │ └── search-icon.png ├── navbar_header.css ├── properties.css └── style.css ├── test ├── lib │ ├── jasmine-flight.js │ └── jasmine-jquery.js ├── mock.js ├── runner.js └── spec │ ├── data │ ├── analytics_spec.js │ ├── facet_spec.js │ ├── geojson_spec.js │ └── search_spec.js │ ├── infotemplates_spec.js │ └── ui │ ├── facet_spec.js │ ├── info_spec.js │ ├── list_spec.js │ ├── loading_spec.js │ ├── map_spec.js │ ├── project_spec.js │ ├── search_spec.js │ └── tab_spec.js └── treatment-centers.csv /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.js] 2 | indent_style = space 3 | indent_size = 2 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | node_modules 3 | dist/ 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": true, 3 | "browser": true, 4 | "curly": true, 5 | "debug": false, 6 | "eqeqeq": true, 7 | "es3": true, 8 | "forin": true, 9 | "immed": true, 10 | "iterator": false, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "noempty": true, 15 | "nonew": true, 16 | "smarttabs": false, 17 | "strict": true, 18 | "trailing": true, 19 | "undef": true, 20 | "unused": true, 21 | "indent": 2, 22 | "globals": { 23 | "define": false, 24 | "require": false, 25 | // jasmine 26 | "jasmine": false, 27 | "describeComponent": false, 28 | "setupComponent": false, 29 | "spyOn": false, 30 | "spyOnEvent": false, 31 | "setFixtures": false, 32 | "describe": false, 33 | "beforeEach": false, 34 | "afterEach": false, 35 | "it": false, 36 | "expect": false, 37 | "waits": false, 38 | "runs": false 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.10 4 | # explicitly enable building gh-pages branch 5 | branches: 6 | only: 7 | - gh-pages 8 | - /^.*$/ 9 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | gethelplex.org 2 | -------------------------------------------------------------------------------- /civic.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "Alpha", 3 | "tags": [ 4 | "lexington", 5 | "kentucky", 6 | "ky", 7 | "finda", 8 | "health", 9 | "human services", 10 | "treatment", 11 | "substance abuse", 12 | "gis" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "project": { 3 | "name": "GetHelpLex", 4 | "description": "

Finda is a generic \"find-a\" app for geographic datasets.

", 5 | "contact": "Please send feedback, ideas, and bug reports to our Github page." 6 | }, 7 | "map": { 8 | "preview_attribute": "organization_name", 9 | "center":[37.6864, -85.2219], 10 | "zoom":7, 11 | "maxZoom":16, 12 | "maxBounds": [ 13 | [ 14 | 36.4964, 15 | -89.5715 16 | ], 17 | [ 18 | 38.6855, 19 | -81.8509 20 | ] 21 | ] 22 | }, 23 | "properties": [ 24 | "organization_name", 25 | "address", 26 | {"name": "address", "title": "directions", "directions": true }, 27 | {"name": "web_url", "title": "website", "url": true }, 28 | {"name": "contact_names", "title": "Contact Information" }, 29 | "contact_emails", 30 | "phone_numbers", 31 | 32 | {"name": "facility_type", "title": "Facility Type" }, 33 | {"name": "out_patient", "title": "Outpatient Services" }, 34 | {"name": "residential_offered", "title": "Residential Services" }, 35 | {"name": "medical_detox_offered", "title": "Medical Detox Services" }, 36 | {"name": "assessment_offered", "title": "Assessment Services" }, 37 | {"name": "gender", "title": "Genders Served" }, 38 | {"name": "pregnancy_services", "title": "Services for Pregnant Women" }, 39 | {"name": "age", "title": "Ages Served" }, 40 | {"name": "insurance", "title": "Payment Accepted"}, 41 | {"name": "additional_notes", "title": "Additional Notes"} 42 | ], 43 | "list": [ 44 | "organization_name", 45 | "city", 46 | "phone_numbers" 47 | ], 48 | "search": { 49 | "geosearch": true, 50 | "full_text": { 51 | "keys": [ 52 | "address", 53 | "organization_name", 54 | "community", 55 | "youth_category", 56 | "service_class_level_2", 57 | "additional_notes", 58 | "county"] 59 | } 60 | }, 61 | "facets": { 62 | "facility_type": { 63 | "title": "Type of Treatment", 64 | "survey_title": "Treatment can be delivered on an outpatient or residential basis.

I am interested in learning about substance use treatment resources that are:", 65 | "type": "single" 66 | }, 67 | "out_patient": { 68 | "title": "Outpatient", 69 | "type": "single", 70 | "dependency": "outpatient_offered", 71 | "survey_title": "Outpatient treatments I am interested in:" 72 | }, 73 | "gender": { 74 | "title": "Gender", 75 | "survey_title": "I am interested in services for a:", 76 | "type": "single" 77 | }, 78 | "pregnancy": { 79 | "title": "Services for a Pregnant Woman?", 80 | "survey_title": "I am interested in services for someone who is pregnant:", 81 | "dependency": "gender_female", 82 | "type": "single" 83 | }, 84 | "age": { 85 | "title": "Age", 86 | "survey_title": "I am interested in services for a:", 87 | "type": "single" 88 | }, 89 | "insurance": { 90 | "title": "Payment Accepted", 91 | "survey_title": "I am interested in services that accept these payments:", 92 | "type": "single" 93 | }, 94 | "county": { 95 | "title": "County", 96 | "type": "single" 97 | } 98 | }, 99 | "analytics": { 100 | "enabled": true, 101 | "private": false, 102 | "google_tracker": null, 103 | "hostname": "auto", 104 | "detail_enabled": true 105 | }, 106 | "geojson_source": "data.geojson" 107 | } 108 | -------------------------------------------------------------------------------- /data.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "geometry": { 7 | "type": "Point", 8 | "coordinates": [ 9 | -84.4934789, 10 | 38.082537 11 | ] 12 | }, 13 | "properties": { 14 | "address": "1351 Newtown Pike Building 5", 15 | "organization_name": "Bluegrass.org Pride Program", 16 | "city": "Lexington", 17 | "web_url": "http://www.firstchurchuu.org/outreach.html#glbt", 18 | "phone_numbers": [ 19 | "859-425-1210" 20 | ], 21 | "contact_names": [], 22 | "contact_emails": [ 23 | "test@gmail.com" 24 | ], 25 | "facility_type": [ 26 | "out_patient" 27 | ], 28 | "service_class_level_1": [ 29 | "Para-professional Support Services" 30 | ], 31 | "service_class_level_2": [ 32 | "Para-professional Counseling, Therapy and Support" 33 | ], 34 | "target_populations": [ 35 | "LGBTQ" 36 | ], 37 | "age_range": "", 38 | "additional_notes": "" 39 | } 40 | } 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /get-help-lex-geocode.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openlexington/gethelplex/8a6d101d3ed36fd481b4bb7fbd276a9518439bbe/get-help-lex-geocode.gif -------------------------------------------------------------------------------- /google-apps-feedback-script.js: -------------------------------------------------------------------------------- 1 | // Copy of the Google Apps Script that you install in Google sheet to make it a feedback endpoint 2 | // based on: 3 | // https://mashe.hawksey.info/2014/07/google-sheets-as-a-database-insert-with-apps-script-using-postget-methods-with-ajax-example/ 4 | 5 | // 1. Enter sheet name where data is to be written below 6 | var SHEET_NAME = "Sheet1"; 7 | 8 | // 2. Run > setup 9 | // 10 | // 3. Publish > Deploy as web app 11 | // - enter Project Version name and click 'Save New Version' 12 | // - set security level and enable service (most likely execute as 'me' and access 'anyone, even anonymously) 13 | // 14 | // 4. Copy the 'Current web app URL' and post this in your form/script action 15 | // 16 | // 5. Insert column names on your destination sheet matching the parameter names of the data you are passing in (exactly matching case) 17 | 18 | var SCRIPT_PROP = PropertiesService.getScriptProperties(); // new property service 19 | 20 | // If you don't want to expose either GET or POST methods you can comment out the appropriate function 21 | function doGet(e){ 22 | return handleResponse(e); 23 | } 24 | 25 | function doPost(e){ 26 | return handleResponse(e); 27 | } 28 | 29 | function handleResponse(e) { 30 | // shortly after my original solution Google announced the LockService[1] 31 | // this prevents concurrent access overwritting data 32 | // [1] http://googleappsdeveloper.blogspot.co.uk/2011/10/concurrency-and-google-apps-script.html 33 | // we want a public lock, one that locks for all invocations 34 | var lock = LockService.getPublicLock(); 35 | lock.waitLock(30000); // wait 30 seconds before conceding defeat. 36 | 37 | try { 38 | var msg = e.parameters.feedback; 39 | if (e.parameters.email) { msg += ', email: ' + e.parameters.email; } 40 | 41 | MailApp.sendEmail("foo@bar.com", "[GetHelpLex feedback]", msg); 42 | 43 | // next set where we write the data - you could write to multiple/alternate destinations 44 | var doc = SpreadsheetApp.openById(SCRIPT_PROP.getProperty("key")); 45 | var sheet = doc.getSheetByName(SHEET_NAME); 46 | 47 | // we'll assume header is in row 1 but you can override with header_row in GET/POST data 48 | var headRow = e.parameter.header_row || 1; 49 | var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0]; 50 | var nextRow = sheet.getLastRow()+1; // get next row 51 | var row = []; 52 | // loop through the header columns 53 | for (i in headers){ 54 | if (headers[i] == "Timestamp"){ // special case if you include a 'Timestamp' column 55 | row.push(new Date()); 56 | } else { // else use header name to get data 57 | row.push(e.parameter[headers[i]]); 58 | } 59 | } 60 | // more efficient to set values as [][] array than individually 61 | sheet.getRange(nextRow, 1, 1, row.length).setValues([row]); 62 | // return json success results 63 | return ContentService 64 | .createTextOutput(JSON.stringify({"result":"success", "row": nextRow})) 65 | .setMimeType(ContentService.MimeType.JSON); 66 | } catch(e){ 67 | // if error return this 68 | return ContentService 69 | .createTextOutput(JSON.stringify({"result":"error", "error": e})) 70 | .setMimeType(ContentService.MimeType.JSON); 71 | } finally { //release lock 72 | lock.releaseLock(); 73 | } 74 | } 75 | 76 | function setup() { 77 | var doc = SpreadsheetApp.getActiveSpreadsheet(); 78 | SCRIPT_PROP.setProperty("key", doc.getId()); 79 | } 80 | -------------------------------------------------------------------------------- /img/GetHelpLextype.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openlexington/gethelplex/8a6d101d3ed36fd481b4bb7fbd276a9518439bbe/img/GetHelpLextype.png -------------------------------------------------------------------------------- /img/bullhorn-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/comment-alt-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/home-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/info-circle-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openlexington/gethelplex/8a6d101d3ed36fd481b4bb7fbd276a9518439bbe/img/logo.png -------------------------------------------------------------------------------- /img/map-marker-alt-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/print-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/search-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openlexington/gethelplex/8a6d101d3ed36fd481b4bb7fbd276a9518439bbe/img/spinner.gif -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Sat Jan 25 2014 18:43:51 GMT-0500 (EST) 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | 7 | // base path, that will be used to resolve files and exclude 8 | basePath: '', 9 | 10 | 11 | // frameworks to use 12 | frameworks: ['jasmine'], 13 | 14 | 15 | // list of files / patterns to load in the browser 16 | files: [ 17 | 'lib/es5-shim.min.js', 18 | 'lib/es5-sham.min.js', 19 | 'lib/jquery-1.11.1.min.js', 20 | 21 | 'test/lib/jasmine-jquery.js', 22 | 'test/lib/jasmine-flight.js', 23 | 24 | // hack to load RequireJS after the shim libs 25 | 'lib/require.js', 26 | 'node_modules/karma-requirejs/lib/adapter.js', 27 | 28 | {pattern: 'src/**/*.js', included: false}, 29 | {pattern: 'src/templates/*.html', included: false}, 30 | {pattern: 'lib/**/*.js', included: false}, 31 | {pattern: 'test/spec/**/*_spec.js', included: false}, 32 | {pattern: 'test/mock.js', included: false}, 33 | {pattern: 'lib/leaflet/images/*', included: false}, 34 | 'test/runner.js' 35 | ], 36 | 37 | 38 | // list of files to exclude 39 | exclude: [ 40 | 'src/script.js' 41 | ], 42 | 43 | 44 | // test results reporter to use 45 | // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage' 46 | reporters: ['progress'], 47 | 48 | 49 | // web server port 50 | port: 9876, 51 | 52 | 53 | // enable / disable colors in the output (reporters and logs) 54 | colors: true, 55 | 56 | 57 | // level of logging 58 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 59 | logLevel: config.LOG_INFO, 60 | 61 | 62 | // enable / disable watching file and executing tests whenever any file changes 63 | autoWatch: true, 64 | 65 | 66 | // Start these browsers, currently available: 67 | // - Chrome 68 | // - ChromeCanary 69 | // - Firefox 70 | // - Opera (has to be installed with `npm install karma-opera-launcher`) 71 | // - Safari (only Mac; has to be installed with `npm install karma-safari-launcher`) 72 | // - PhantomJS 73 | // - IE (only Windows; has to be installed with `npm install karma-ie-launcher`) 74 | browsers: ['PhantomJS'], 75 | 76 | 77 | // If browser does not capture in given timeout [ms], kill it 78 | captureTimeout: 60000, 79 | 80 | 81 | // Continuous Integration mode 82 | // if true, it capture browsers, run tests and exit 83 | singleRun: false 84 | }); 85 | }; 86 | -------------------------------------------------------------------------------- /kentucky substance abuse referral list.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openlexington/gethelplex/8a6d101d3ed36fd481b4bb7fbd276a9518439bbe/kentucky substance abuse referral list.csv -------------------------------------------------------------------------------- /lib/es5-sham.min.js: -------------------------------------------------------------------------------- 1 | (function(d){"function"==typeof define?define(d):"function"==typeof YUI?YUI.add("es5-sham",d):d()})(function(){function d(a){try{return Object.defineProperty(a,"sentinel",{}),"sentinel"in a}catch(c){}}Object.getPrototypeOf||(Object.getPrototypeOf=function(a){return a.__proto__||(a.constructor?a.constructor.prototype:prototypeOfObject)});Object.getOwnPropertyDescriptor||(Object.getOwnPropertyDescriptor=function(a,c){if(typeof a!="object"&&typeof a!="function"||a===null)throw new TypeError("Object.getOwnPropertyDescriptor called on a non-object: "+ 2 | a);if(owns(a,c)){var b={enumerable:true,configurable:true};if(supportsAccessors){var d=a.__proto__;a.__proto__=prototypeOfObject;var f=lookupGetter(a,c),e=lookupSetter(a,c);a.__proto__=d;if(f||e){if(f)b.get=f;if(e)b.set=e;return b}}b.value=a[c];return b}});Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(a){return Object.keys(a)});Object.create||(Object.create=function(a,c){var b;if(a===null)b={__proto__:null};else{if(typeof a!="object")throw new TypeError("typeof prototype["+typeof a+ 3 | "] != 'object'");b=function(){};b.prototype=a;b=new b;b.__proto__=a}c!==void 0&&Object.defineProperties(b,c);return b});if(Object.defineProperty){var g=d({}),h="undefined"==typeof document||d(document.createElement("div"));if(!g||!h)var e=Object.defineProperty}if(!Object.defineProperty||e)Object.defineProperty=function(a,c,b){if(typeof a!="object"&&typeof a!="function"||a===null)throw new TypeError("Object.defineProperty called on non-object: "+a);if(typeof b!="object"&&typeof b!="function"||b=== 4 | null)throw new TypeError("Property description must be an object: "+b);if(e)try{return e.call(Object,a,c,b)}catch(d){}if(owns(b,"value"))if(supportsAccessors&&(lookupGetter(a,c)||lookupSetter(a,c))){var f=a.__proto__;a.__proto__=prototypeOfObject;delete a[c];a[c]=b.value;a.__proto__=f}else a[c]=b.value;else{if(!supportsAccessors)throw new TypeError("getters & setters can not be defined on this javascript engine");owns(b,"get")&&defineGetter(a,c,b.get);owns(b,"set")&&defineSetter(a,c,b.set)}return a}; 5 | Object.defineProperties||(Object.defineProperties=function(a,c){for(var b in c)owns(c,b)&&b!="__proto__"&&Object.defineProperty(a,b,c[b]);return a});Object.seal||(Object.seal=function(a){return a});Object.freeze||(Object.freeze=function(a){return a});try{Object.freeze(function(){})}catch(j){var i=Object.freeze;Object.freeze=function(a){return typeof a=="function"?a:i(a)}}Object.preventExtensions||(Object.preventExtensions=function(a){return a});Object.isSealed||(Object.isSealed=function(){return false}); 6 | Object.isFrozen||(Object.isFrozen=function(){return false});Object.isExtensible||(Object.isExtensible=function(a){if(Object(a)!==a)throw new TypeError;for(var c="";owns(a,c);)c=c+"?";a[c]=true;var b=owns(a,c);delete a[c];return b})}); 7 | -------------------------------------------------------------------------------- /lib/es5-shim.min.js: -------------------------------------------------------------------------------- 1 | (function(f){"function"==typeof define?define(f):"function"==typeof YUI?YUI.add("es5",f):f()})(function(){Function.prototype.bind||(Function.prototype.bind=function(d){var c=this;if("function"!=typeof c)throw new TypeError("Function.prototype.bind called on incompatible "+c);var a=n.call(arguments,1),b=function(){if(this instanceof b){var e=function(){};e.prototype=c.prototype;var e=new e,i=c.apply(e,a.concat(n.call(arguments)));return Object(i)===i?i:e}return c.apply(d,a.concat(n.call(arguments)))}; 2 | return b});var f=Function.prototype.call,m=Object.prototype,n=Array.prototype.slice,l=f.bind(m.toString),o=f.bind(m.hasOwnProperty);o(m,"__defineGetter__")&&(f.bind(m.__defineGetter__),f.bind(m.__defineSetter__),f.bind(m.__lookupGetter__),f.bind(m.__lookupSetter__));Array.isArray||(Array.isArray=function(d){return l(d)=="[object Array]"});Array.prototype.forEach||(Array.prototype.forEach=function(d,c){var a=j(this),b=-1,e=a.length>>>0;if(l(d)!="[object Function]")throw new TypeError;for(;++b>>0,e=Array(b);if(l(d)!="[object Function]")throw new TypeError(d+" is not a function");for(var i=0;i>>0,e=[],i;if(l(d)!="[object Function]")throw new TypeError(d+" is not a function");for(var f=0;f>>0;if(l(d)!="[object Function]")throw new TypeError(d+" is not a function");for(var e=0;e>>0;if(l(d)!="[object Function]")throw new TypeError(d+" is not a function");for(var e=0;e>>0;if(l(d)!="[object Function]")throw new TypeError(d+" is not a function");if(!a&&arguments.length==1)throw new TypeError("reduce of empty array with no initial value");var b=0,e;if(arguments.length>=2)e=arguments[1];else{do{if(b in c){e=c[b++];break}if(++b>=a)throw new TypeError("reduce of empty array with no initial value");}while(1)}for(;b>>0;if(l(d)!="[object Function]")throw new TypeError(d+" is not a function");if(!a&&arguments.length==1)throw new TypeError("reduceRight of empty array with no initial value");var b,a=a-1;if(arguments.length>=2)b=arguments[1];else{do{if(a in c){b=c[a--];break}if(--a<0)throw new TypeError("reduceRight of empty array with no initial value");}while(1)}do a in this&&(b=d.call(void 0,b,c[a],a,c));while(a--);return b});Array.prototype.indexOf||(Array.prototype.indexOf= 7 | function(d){var c=j(this),a=c.length>>>0;if(!a)return-1;var b=0;arguments.length>1&&(b=p(arguments[1]));for(b=b>=0?b:Math.max(0,a+b);b>>0;if(!a)return-1;var b=a-1;arguments.length>1&&(b=Math.min(b,p(arguments[1])));for(b=b>=0?b:a-Math.abs(b);b>=0;b--)if(b in c&&d===c[b])return b;return-1});if(!Object.keys){var q=!0,r="toString toLocaleString valueOf hasOwnProperty isPrototypeOf propertyIsEnumerable constructor".split(" "), 8 | s=r.length,t;for(t in{toString:null})q=!1;Object.keys=function(d){if(typeof d!="object"&&typeof d!="function"||d===null)throw new TypeError("Object.keys called on a non-object");var c=[],a;for(a in d)o(d,a)&&c.push(a);if(q)for(a=0;a9999?"+":"")+("00000"+Math.abs(b)).slice(0<=b&&b<=9999?-4:-6);for(c=d.length;c--;){a=d[c];a<10&&(d[c]="0"+a)}return b+"-"+d.slice(0,2).join("-")+"T"+d.slice(2).join(":")+"."+("000"+this.getUTCMilliseconds()).slice(-3)+"Z"};Date.now||(Date.now=function(){return(new Date).getTime()});Date.prototype.toJSON||(Date.prototype.toJSON=function(){if(typeof this.toISOString!= 10 | "function")throw new TypeError("toISOString property is not callable");return this.toISOString()});if(!Date.parse||864E13!==Date.parse("+275760-09-13T00:00:00.000Z")){var g=Date,f=function c(a,b,e,f,h,j,l){var k=arguments.length;if(this instanceof g){k=k==1&&String(a)===a?new g(c.parse(a)):k>=7?new g(a,b,e,f,h,j,l):k>=6?new g(a,b,e,f,h,j):k>=5?new g(a,b,e,f,h):k>=4?new g(a,b,e,f):k>=3?new g(a,b,e):k>=2?new g(a,b):k>=1?new g(a):new g;k.constructor=c;return k}return g.apply(this,arguments)},u=RegExp("^(\\d{4}|[+-]\\d{6})(?:-(\\d{2})(?:-(\\d{2})(?:T(\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.(\\d{3}))?)?(?:Z|(?:([-+])(\\d{2}):(\\d{2})))?)?)?)?$"), 11 | h;for(h in g)f[h]=g[h];f.now=g.now;f.UTC=g.UTC;f.prototype=g.prototype;f.prototype.constructor=f;f.parse=function(c){var a=u.exec(c);if(a){a.shift();for(var b=1;b<7;b++){a[b]=+(a[b]||(b<3?1:0));b==1&&a[b]--}var e=+a.pop(),f=+a.pop(),h=a.pop(),b=0;if(h){if(f>23||e>59)return NaN;b=(f*60+e)*6E4*(h=="+"?-1:1)}e=+a[0];if(0<=e&&e<=99){a[0]=e+400;return g.UTC.apply(this,a)+b-126227808E5}return g.UTC.apply(this,a)+b}return g.parse.apply(this,arguments)};Date=f}h="\t\n\x0B\f\r \u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\ufeff"; 12 | if(!String.prototype.trim||h.trim()){h="["+h+"]";var v=RegExp("^"+h+h+"*"),w=RegExp(h+h+"*$");String.prototype.trim=function(){if(this===void 0||this===null)throw new TypeError("can't convert "+this+" to object");return String(this).replace(v,"").replace(w,"")}}var p=function(c){c=+c;c!==c?c=0:c!==0&&(c!==1/0&&c!==-(1/0))&&(c=(c>0||-1)*Math.floor(Math.abs(c)));return c},x="a"!="a"[0],j=function(c){if(c==null)throw new TypeError("can't convert "+c+" to object");return x&&typeof c=="string"&&c?c.split(""): 13 | Object(c)}}); 14 | -------------------------------------------------------------------------------- /lib/fuse.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Fuse - Lightweight fuzzy-search 4 | * 5 | * Copyright (c) 2012 Kirollos Risk . 6 | * All Rights Reserved. Apache Software License 2.0 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | !function(t){function e(t,n){this.list=t,this.options=n=n||{};var i,o,s;for(i=0,keys=["sort","includeScore","shouldSort"],o=keys.length;o>i;i++)s=keys[i],this.options[s]=s in n?n[s]:e.defaultOptions[s];for(i=0,keys=["searchFn","sortFn","keys","getFn"],o=keys.length;o>i;i++)s=keys[i],this.options[s]=n[s]||e.defaultOptions[s]}var n=function(t,e){if(e=e||{},this.options=e,this.options.location=e.location||n.defaultOptions.location,this.options.distance="distance"in e?e.distance:n.defaultOptions.distance,this.options.threshold="threshold"in e?e.threshold:n.defaultOptions.threshold,this.options.maxPatternLength=e.maxPatternLength||n.defaultOptions.maxPatternLength,this.pattern=e.caseSensitive?t:t.toLowerCase(),this.patternLen=t.length,this.patternLen>this.options.maxPatternLength)throw new Error("Pattern length is too long");this.matchmask=1<i;)this._bitapScore(e,l+o)<=u?i=o:d=o,o=Math.floor((d-i)/2+i);for(d=o,s=Math.max(1,l-o+1),r=Math.min(l+o,c)+this.patternLen,a=Array(r+2),a[r+1]=(1<=s;n--)if(p=this.patternAlphabet[t.charAt(n-1)],a[n]=0===e?(a[n+1]<<1|1)&p:(a[n+1]<<1|1)&p|((h[n+1]|h[n])<<1|1)|h[n+1],a[n]&this.matchmask&&(g=this._bitapScore(e,n-1),u>=g)){if(u=g,f=n-1,m.push(f),!(f>l))break;s=Math.max(1,2*l-f)}if(this._bitapScore(e+1,l)>u)break;h=a}return{isMatch:f>=0,score:g}};var i={deepValue:function(t,e){for(var n=0,e=e.split("."),i=e.length;i>n;n++){if(!t)return null;t=t[e[n]]}return t}};e.defaultOptions={id:null,caseSensitive:!1,includeScore:!1,shouldSort:!0,searchFn:n,sortFn:function(t,e){return t.score-e.score},getFn:i.deepValue,keys:[]},e.prototype.search=function(t){var e,n,o,s,r,a=new this.options.searchFn(t,this.options),h=this.list,p=h.length,c=this.options,l=this.options.keys,u=l.length,f=[],d={},g=[],m=function(t,e,n){void 0!==t&&null!==t&&"string"==typeof t&&(s=a.search(t),s.isMatch&&(r=d[n],r?r.score=Math.min(r.score,s.score):(d[n]={item:e,score:s.score},f.push(d[n]))))};if("string"==typeof h[0])for(var e=0;p>e;e++)m(h[e],e,e);else for(var e=0;p>e;e++)for(o=h[e],n=0;u>n;n++)m(this.options.getFn(o,l[n]),o,e);c.shouldSort&&f.sort(c.sortFn);for(var y=c.includeScore?function(t){return f[t]}:function(t){return f[t].item},L=c.id?function(t){return i.deepValue(y(t),c.id)}:function(t){return y(t)},e=0,v=f.length;v>e;e++)g.push(L(e));return g},"object"==typeof exports?module.exports=e:"function"==typeof define&&define.amd?define(function(){return e}):t.Fuse=e}(this); -------------------------------------------------------------------------------- /lib/leaflet.markercluster/MarkerCluster.Default.css: -------------------------------------------------------------------------------- 1 | .marker-cluster-small { 2 | background-color: rgba(181, 226, 140, 0.6); 3 | } 4 | .marker-cluster-small div { 5 | background-color: rgba(110, 204, 57, 0.6); 6 | } 7 | 8 | .marker-cluster-medium { 9 | background-color: rgba(241, 211, 87, 0.6); 10 | } 11 | .marker-cluster-medium div { 12 | background-color: rgba(240, 194, 12, 0.6); 13 | } 14 | 15 | .marker-cluster-large { 16 | background-color: rgba(253, 156, 115, 0.6); 17 | } 18 | .marker-cluster-large div { 19 | background-color: rgba(241, 128, 23, 0.6); 20 | } 21 | 22 | /* IE 6-8 fallback colors */ 23 | .leaflet-oldie .marker-cluster-small { 24 | background-color: rgb(181, 226, 140); 25 | } 26 | .leaflet-oldie .marker-cluster-small div { 27 | background-color: rgb(110, 204, 57); 28 | } 29 | 30 | .leaflet-oldie .marker-cluster-medium { 31 | background-color: rgb(241, 211, 87); 32 | } 33 | .leaflet-oldie .marker-cluster-medium div { 34 | background-color: rgb(240, 194, 12); 35 | } 36 | 37 | .leaflet-oldie .marker-cluster-large { 38 | background-color: rgb(253, 156, 115); 39 | } 40 | .leaflet-oldie .marker-cluster-large div { 41 | background-color: rgb(241, 128, 23); 42 | } 43 | 44 | .marker-cluster { 45 | background-clip: padding-box; 46 | border-radius: 20px; 47 | } 48 | .marker-cluster div { 49 | width: 30px; 50 | height: 30px; 51 | margin-left: 5px; 52 | margin-top: 5px; 53 | 54 | text-align: center; 55 | border-radius: 15px; 56 | font: 12px "Helvetica Neue", Arial, Helvetica, sans-serif; 57 | } 58 | .marker-cluster span { 59 | line-height: 30px; 60 | } -------------------------------------------------------------------------------- /lib/leaflet.markercluster/MarkerCluster.css: -------------------------------------------------------------------------------- 1 | .leaflet-cluster-anim .leaflet-marker-icon, .leaflet-cluster-anim .leaflet-marker-shadow { 2 | -webkit-transition: -webkit-transform 0.3s ease-out, opacity 0.3s ease-in; 3 | -moz-transition: -moz-transform 0.3s ease-out, opacity 0.3s ease-in; 4 | -o-transition: -o-transform 0.3s ease-out, opacity 0.3s ease-in; 5 | transition: transform 0.3s ease-out, opacity 0.3s ease-in; 6 | } 7 | -------------------------------------------------------------------------------- /lib/leaflet/L.Control.Locate.css: -------------------------------------------------------------------------------- 1 | /* Compatible with Leaflet 0.7 */ 2 | 3 | .leaflet-touch .leaflet-bar-part-single { 4 | -webkit-border-radius: 7px 7px 7px 7px; 5 | border-radius: 7px 7px 7px 7px; 6 | border-bottom: none; 7 | } 8 | 9 | .leaflet-control-locate a { 10 | background-image: url(images/locate.png); 11 | background-size:90px 30px; 12 | background-position: -2px -2px; 13 | } 14 | 15 | .leaflet-retina .leaflet-control-locate a { 16 | background-image: url(images/locate@2x.png); 17 | } 18 | 19 | .leaflet-touch .leaflet-control-locate a { 20 | background-image: url(images/locate_touch.png); 21 | } 22 | 23 | .leaflet-control-locate.requesting a { 24 | background-size:12px 12px; 25 | background-image: url(images/spinner.gif); 26 | background-position: 50% 50%; 27 | } 28 | 29 | .leaflet-retina .leaflet-control-locate.requesting a { 30 | background-image: url(images/spinner@2x.gif); 31 | } 32 | 33 | .leaflet-control-locate.active a { 34 | background-position: -32px -2px; 35 | } 36 | 37 | .leaflet-control-locate.active.following a { 38 | background-position: -62px -2px; 39 | } 40 | 41 | .leaflet-touch .leaflet-control-locate { 42 | box-shadow: none; 43 | border: 2px solid rgba(0,0,0,0.2); 44 | background-clip: padding-box; 45 | } 46 | -------------------------------------------------------------------------------- /lib/leaflet/L.Control.Locate.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014 Dominik Moritz 3 | 4 | This file is part of the leaflet locate control. It is licensed under the MIT license. 5 | You can find the project at: https://github.com/domoritz/leaflet-locatecontrol 6 | */ 7 | L.Control.Locate = L.Control.extend({ 8 | options: { 9 | position: 'topleft', 10 | drawCircle: true, 11 | follow: false, // follow with zoom and pan the user's location 12 | stopFollowingOnDrag: false, // if follow is true, stop following when map is dragged (deprecated) 13 | // range circle 14 | circleStyle: { 15 | color: '#136AEC', 16 | fillColor: '#136AEC', 17 | fillOpacity: 0.15, 18 | weight: 2, 19 | opacity: 0.5 20 | }, 21 | // inner marker 22 | markerStyle: { 23 | color: '#136AEC', 24 | fillColor: '#2A93EE', 25 | fillOpacity: 0.7, 26 | weight: 2, 27 | opacity: 0.9, 28 | radius: 5 29 | }, 30 | // changes to range circle and inner marker while following 31 | // it is only necessary to provide the things that should change 32 | followCircleStyle: {}, 33 | followMarkerStyle: { 34 | //color: '#FFA500', 35 | //fillColor: '#FFB000' 36 | }, 37 | circlePadding: [0, 0], 38 | metric: true, 39 | onLocationError: function(err) { 40 | // this event is called in case of any location error 41 | // that is not a time out error. 42 | alert(err.message); 43 | }, 44 | onLocationOutsideMapBounds: function(control) { 45 | // this event is repeatedly called when the location changes 46 | control.stopLocate(); 47 | alert(context.options.strings.outsideMapBoundsMsg); 48 | }, 49 | setView: true, // automatically sets the map view to the user's location 50 | strings: { 51 | title: "Show me where I am", 52 | popup: "You are within {distance} {unit} from this point", 53 | outsideMapBoundsMsg: "You seem located outside the boundaries of the map" 54 | }, 55 | locateOptions: { 56 | maxZoom: Infinity, 57 | watch: true // if you overwrite this, visualization cannot be updated 58 | } 59 | }, 60 | 61 | onAdd: function (map) { 62 | var container = L.DomUtil.create('div', 63 | 'leaflet-control-locate leaflet-bar leaflet-control'); 64 | 65 | var self = this; 66 | this._layer = new L.LayerGroup(); 67 | this._layer.addTo(map); 68 | this._event = undefined; 69 | 70 | this._locateOptions = this.options.locateOptions; 71 | L.extend(this._locateOptions, this.options.locateOptions); 72 | L.extend(this._locateOptions, { 73 | setView: false // have to set this to false because we have to 74 | // do setView manually 75 | }); 76 | 77 | // extend the follow marker style and circle from the normal style 78 | var tmp = {}; 79 | L.extend(tmp, this.options.markerStyle, this.options.followMarkerStyle); 80 | this.options.followMarkerStyle = tmp; 81 | tmp = {}; 82 | L.extend(tmp, this.options.circleStyle, this.options.followCircleStyle); 83 | this.options.followCircleStyle = tmp; 84 | 85 | var link = L.DomUtil.create('a', 'leaflet-bar-part leaflet-bar-part-single', container); 86 | link.href = '#'; 87 | link.title = this.options.strings.title; 88 | 89 | L.DomEvent 90 | .on(link, 'click', L.DomEvent.stopPropagation) 91 | .on(link, 'click', L.DomEvent.preventDefault) 92 | .on(link, 'click', function() { 93 | if (self._active && (self._event === undefined || map.getBounds().contains(self._event.latlng) || !self.options.setView || 94 | isOutsideMapBounds())) { 95 | stopLocate(); 96 | } else { 97 | locate(); 98 | } 99 | }) 100 | .on(link, 'dblclick', L.DomEvent.stopPropagation); 101 | 102 | var locate = function () { 103 | if (self.options.setView) { 104 | self._locateOnNextLocationFound = true; 105 | } 106 | if(!self._active) { 107 | map.locate(self._locateOptions); 108 | } 109 | self._active = true; 110 | if (self.options.follow) { 111 | startFollowing(); 112 | } 113 | if (!self._event) { 114 | L.DomUtil.addClass(self._container, "requesting"); 115 | L.DomUtil.removeClass(self._container, "active"); 116 | L.DomUtil.removeClass(self._container, "following"); 117 | } else { 118 | visualizeLocation(); 119 | } 120 | }; 121 | 122 | var onLocationFound = function (e) { 123 | // no need to do anything if the location has not changed 124 | if (self._event && 125 | (self._event.latlng.lat === e.latlng.lat && 126 | self._event.latlng.lng === e.latlng.lng && 127 | self._event.accuracy === e.accuracy)) { 128 | return; 129 | } 130 | 131 | if (!self._active) { 132 | return; 133 | } 134 | 135 | self._event = e; 136 | 137 | if (self.options.follow && self._following) { 138 | self._locateOnNextLocationFound = true; 139 | } 140 | 141 | visualizeLocation(); 142 | }; 143 | 144 | var startFollowing = function() { 145 | map.fire('startfollowing', self); 146 | self._following = true; 147 | if (self.options.stopFollowingOnDrag) { 148 | map.on('dragstart', stopFollowing); 149 | } 150 | }; 151 | 152 | var stopFollowing = function() { 153 | map.fire('stopfollowing', self); 154 | self._following = false; 155 | if (self.options.stopFollowingOnDrag) { 156 | map.off('dragstart', stopFollowing); 157 | } 158 | visualizeLocation(); 159 | }; 160 | 161 | var isOutsideMapBounds = function () { 162 | if (self._event === undefined) 163 | return false; 164 | return map.options.maxBounds && 165 | !map.options.maxBounds.contains(self._event.latlng); 166 | }; 167 | 168 | var visualizeLocation = function() { 169 | if (self._event.accuracy === undefined) 170 | self._event.accuracy = 0; 171 | 172 | var radius = self._event.accuracy; 173 | if (self._locateOnNextLocationFound) { 174 | if (isOutsideMapBounds()) { 175 | self.options.onLocationOutsideMapBounds(self); 176 | } else { 177 | map.fitBounds(self._event.bounds, { 178 | padding: self.options.circlePadding, 179 | maxZoom: self._locateOptions.maxZoom 180 | }); 181 | } 182 | self._locateOnNextLocationFound = false; 183 | } 184 | 185 | // circle with the radius of the location's accuracy 186 | var style, o; 187 | if (self.options.drawCircle) { 188 | if (self._following) { 189 | style = self.options.followCircleStyle; 190 | } else { 191 | style = self.options.circleStyle; 192 | } 193 | 194 | if (!self._circle) { 195 | self._circle = L.circle(self._event.latlng, radius, style) 196 | .addTo(self._layer); 197 | } else { 198 | self._circle.setLatLng(self._event.latlng).setRadius(radius); 199 | for (o in style) { 200 | self._circle.options[o] = style[o]; 201 | } 202 | } 203 | } 204 | 205 | var distance, unit; 206 | if (self.options.metric) { 207 | distance = radius.toFixed(0); 208 | unit = "meters"; 209 | } else { 210 | distance = (radius * 3.2808399).toFixed(0); 211 | unit = "feet"; 212 | } 213 | 214 | // small inner marker 215 | var mStyle; 216 | if (self._following) { 217 | mStyle = self.options.followMarkerStyle; 218 | } else { 219 | mStyle = self.options.markerStyle; 220 | } 221 | 222 | var t = self.options.strings.popup; 223 | if (!self._circleMarker) { 224 | self._circleMarker = L.circleMarker(self._event.latlng, mStyle) 225 | .bindPopup(L.Util.template(t, {distance: distance, unit: unit})) 226 | .addTo(self._layer); 227 | } else { 228 | self._circleMarker.setLatLng(self._event.latlng) 229 | .bindPopup(L.Util.template(t, {distance: distance, unit: unit})) 230 | ._popup.setLatLng(self._event.latlng); 231 | for (o in mStyle) { 232 | self._circleMarker.options[o] = mStyle[o]; 233 | } 234 | } 235 | 236 | if (!self._container) 237 | return; 238 | if (self._following) { 239 | L.DomUtil.removeClass(self._container, "requesting"); 240 | L.DomUtil.addClass(self._container, "active"); 241 | L.DomUtil.addClass(self._container, "following"); 242 | } else { 243 | L.DomUtil.removeClass(self._container, "requesting"); 244 | L.DomUtil.addClass(self._container, "active"); 245 | L.DomUtil.removeClass(self._container, "following"); 246 | } 247 | }; 248 | 249 | var resetVariables = function() { 250 | self._active = false; 251 | self._locateOnNextLocationFound = self.options.setView; 252 | self._following = false; 253 | }; 254 | 255 | resetVariables(); 256 | 257 | var stopLocate = function() { 258 | map.stopLocate(); 259 | map.off('dragstart', stopFollowing); 260 | 261 | L.DomUtil.removeClass(self._container, "requesting"); 262 | L.DomUtil.removeClass(self._container, "active"); 263 | L.DomUtil.removeClass(self._container, "following"); 264 | resetVariables(); 265 | 266 | self._layer.clearLayers(); 267 | self._circleMarker = undefined; 268 | self._circle = undefined; 269 | }; 270 | 271 | var onLocationError = function (err) { 272 | // ignore time out error if the location is watched 273 | if (err.code == 3 && this._locateOptions.watch) { 274 | return; 275 | } 276 | 277 | stopLocate(); 278 | self.options.onLocationError(err); 279 | }; 280 | 281 | // event hooks 282 | map.on('locationfound', onLocationFound, self); 283 | map.on('locationerror', onLocationError, self); 284 | 285 | // make locate functions available to outside world 286 | this.locate = locate; 287 | this.stopLocate = stopLocate; 288 | this.stopFollowing = stopFollowing; 289 | 290 | return container; 291 | } 292 | }); 293 | 294 | L.Map.addInitHook(function () { 295 | if (this.options.locateControl) { 296 | this.locateControl = L.control.locate(); 297 | this.addControl(this.locateControl); 298 | } 299 | }); 300 | 301 | L.control.locate = function (options) { 302 | return new L.Control.Locate(options); 303 | }; 304 | -------------------------------------------------------------------------------- /lib/leaflet/images/layers-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openlexington/gethelplex/8a6d101d3ed36fd481b4bb7fbd276a9518439bbe/lib/leaflet/images/layers-2x.png -------------------------------------------------------------------------------- /lib/leaflet/images/layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openlexington/gethelplex/8a6d101d3ed36fd481b4bb7fbd276a9518439bbe/lib/leaflet/images/layers.png -------------------------------------------------------------------------------- /lib/leaflet/images/locate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openlexington/gethelplex/8a6d101d3ed36fd481b4bb7fbd276a9518439bbe/lib/leaflet/images/locate.png -------------------------------------------------------------------------------- /lib/leaflet/images/locate@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openlexington/gethelplex/8a6d101d3ed36fd481b4bb7fbd276a9518439bbe/lib/leaflet/images/locate@2x.png -------------------------------------------------------------------------------- /lib/leaflet/images/locate_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openlexington/gethelplex/8a6d101d3ed36fd481b4bb7fbd276a9518439bbe/lib/leaflet/images/locate_touch.png -------------------------------------------------------------------------------- /lib/leaflet/images/marker-icon-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openlexington/gethelplex/8a6d101d3ed36fd481b4bb7fbd276a9518439bbe/lib/leaflet/images/marker-icon-2x.png -------------------------------------------------------------------------------- /lib/leaflet/images/marker-icon-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openlexington/gethelplex/8a6d101d3ed36fd481b4bb7fbd276a9518439bbe/lib/leaflet/images/marker-icon-gray.png -------------------------------------------------------------------------------- /lib/leaflet/images/marker-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openlexington/gethelplex/8a6d101d3ed36fd481b4bb7fbd276a9518439bbe/lib/leaflet/images/marker-icon.png -------------------------------------------------------------------------------- /lib/leaflet/images/marker-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openlexington/gethelplex/8a6d101d3ed36fd481b4bb7fbd276a9518439bbe/lib/leaflet/images/marker-shadow.png -------------------------------------------------------------------------------- /lib/leaflet/images/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openlexington/gethelplex/8a6d101d3ed36fd481b4bb7fbd276a9518439bbe/lib/leaflet/images/spinner.gif -------------------------------------------------------------------------------- /lib/leaflet/images/spinner@2x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openlexington/gethelplex/8a6d101d3ed36fd481b4bb7fbd276a9518439bbe/lib/leaflet/images/spinner@2x.gif -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Code For Boston 2 | ===== 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | **THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE.** 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "finda", 3 | "version": "0.0.0", 4 | "description": "Generic 'find-a' app for geographic datasets", 5 | "directories": { 6 | "test": "test" 7 | }, 8 | "devDependencies": { 9 | "karma": "~0.13.22", 10 | "jshint": "~2.4.4", 11 | "karma-requirejs": "~0.2.6", 12 | "karma-jasmine": "~0.1.5", 13 | "karma-cli": "0.0.3", 14 | "karma-phantomjs-launcher": "~0.1.2", 15 | "almond": "^0.2.9", 16 | "clean-css": "^2.1.6", 17 | "requirejs": "^2.1.11", 18 | "rimraf": "^2.2.6", 19 | "replace": "^0.2.9", 20 | "glob": "^3.2.9", 21 | "mkdirp": "^0.3.5", 22 | "copyfiles": "0.0.1" 23 | }, 24 | "scripts": { 25 | "start": "http-server", 26 | "test": "jshint src && karma start --single-run --browsers PhantomJS", 27 | "test-server": "jshint src && karma start --browsers PhantomJS", 28 | "test-client": "jshint src && karma run", 29 | "build": "npm run clean && npm run copy && npm run cssmin && npm run requirejs && npm run processhtml && npm run index", 30 | "clean": "rimraf dist", 31 | "copy": "copyfiles index.html data.geojson config.json *.md styles/properties.css img/* lib/leaflet/images/marker-* dist", 32 | "cssmin": "cleancss --s1 -o dist/styles/style.css styles/style.css", 33 | "requirejs": "r.js -o script/build.js", 34 | "index": "replace -s 'data-main=\"(src/script.js)\" src=\"lib/require.js\"' 'src=\"$1\"' dist/index.html" 35 | }, 36 | "repository": { 37 | "type": "git", 38 | "url": "https://github.com/codeforboston/finda.git" 39 | }, 40 | "license": "MIT", 41 | "private": true 42 | } 43 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # GetHelpLex [![Build Status](https://travis-ci.org/openlexington/gethelplex.svg)](https://travis-ci.org/openlexington/gethelplex) [![Stories in Ready](https://badge.waffle.io/openlexington/gethelplex.svg?label=ready&title=Ready)](http://waffle.io/openlexington/gethelplex) 2 | 3 | ## I want to help develop Finda 4 | 5 | Great! Quick setup: 6 | 7 | npm install 8 | npm install -g http-server 9 | npm start 10 | 11 | Visit [localhost:8080](http://localhost:8080/) to see the app. 12 | 13 | A bit more background: 14 | 15 | The project is based on Code for Boston's [finda](https://github.com/codeforboston/finda) project. In Lexington we've customized it for our needs: 16 | 17 | * The facilities [are read](https://github.com/openlexington/gethelplex/blob/gh-pages/src/data/geojson.js#L10) from a Google spreadsheet using the [Tabletop.js](https://github.com/jsoma/tabletop) library. This way our stakeholders have realtime ability to update facilities. 18 | * We hacked the [facet handling](https://github.com/openlexington/gethelplex/blob/gh-pages/src/ui/facet.js) to guide the user through a 'survey'. It narrows down the facilities based on type of treatment, the insurance they accept, etc. 19 | 20 | Let's say you want to new information about facilities like "do they let you smoke." You would want to add a column to the [facilities spreadsheet](https://docs.google.com/spreadsheets/d/1LZRal5xPL6fe3BOlBBHc8RdsOPCXQEc5vers2dsg1M8/edit#gid=145432932) called smoking_permited (or similar): 21 | 22 | * make a copy of the existing spreadsheet 23 | * copy paste the new spreadsheet's key to the [Tabletop.js init](https://github.com/openlexington/gethelplex/blob/gh-pages/src/data/geojson.js#L12) 24 | * then you'll update [config.json](https://github.com/openlexington/gethelplex/blob/gh-pages/config.json) [todo, flesh this step out more :)] 25 | 26 | A lot of Code for Boston's [development documentation](https://github.com/codeforboston/finda/wiki/Developing-Finda) is still relevant. Let us know if you hit any key differences for Lexington and we'll update this readme! 27 | 28 | Look in the [waffle board](https://waffle.io/openlexington/finda) for priority issues. 29 | 30 | ## How to Test 31 | 32 | You can run tests once by running: `npm test` 33 | 34 | Keep test server running to speed up tests. Start test server: 35 | 36 | npm run test-server 37 | 38 | Kick off a test run when the test server is running: 39 | 40 | npm run test-client 41 | 42 | ## Analytics and feedback 43 | 44 | GetHelpLex uses Google Tag Manager to manage Google Analytics as described in the [Unified Analytics repository](https://github.com/laurenancona/unified-analytics). 45 | 46 | GetHelpLex posts feedback to a Google Spreadsheet [as described here](https://mashe.hawksey.info/2014/07/google-sheets-as-a-database-insert-with-apps-script-using-postget-methods-with-ajax-example/). 47 | As a backup, feedback is tracked using the [ga-feedback approach](https://github.com/luckyshot/ga-feedback) managed by Google Tag Manager [as described here](http://erikschwartz.net/2016-01-23-google-analytics-events-in-google-tag-manager/). 48 | 49 | Feedback is emailed to addresses defined in the script attached to the [feedback spreadsheet](https://docs.google.com/spreadsheets/d/1lP-OsypwXFkH-S3F3Re34fBPSYgpr1ZXY6bRD85w3V8/edit). 50 | 51 | To make changes to the script that handles feedback requests: 52 | 53 | * edit in the Appscript editor 54 | * `Publish` > `Deploy as webapp` > `Version: new` 55 | 56 | ## Add map coordinates for new facilities 57 | 58 | ![Geocode facilites](./get-help-lex-geocode.gif) 59 | -------------------------------------------------------------------------------- /rules-engine-questions.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openlexington/gethelplex/8a6d101d3ed36fd481b4bb7fbd276a9518439bbe/rules-engine-questions.docx -------------------------------------------------------------------------------- /script/build.js: -------------------------------------------------------------------------------- 1 | ({ 2 | baseUrl: '../src', 3 | mainConfigFile: '../src/script.js', 4 | preserveLicenseComments: true, 5 | wrap: false, 6 | name: '../node_modules/almond/almond', 7 | include:'script', 8 | insertRequire:['script'], 9 | out: '../dist/src/script.js', 10 | optimize: 'uglify2' 11 | }) 12 | -------------------------------------------------------------------------------- /script/copyfiles.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | var path = require('path'); 4 | var fs = require('fs'); 5 | var glob = require('glob'); 6 | var mkdirp = require('mkdirp'); 7 | var input = [ 8 | 'index.html', 9 | 'data.geojson', 10 | 'config.json', 11 | 'styles/properties.css', 12 | 'img/logo.png' 13 | ]; 14 | var inGlobs = [ 15 | '*.md', 16 | 'lib/leaflet/images/marker-*' 17 | ]; 18 | var outDir = 'dist'; 19 | 20 | function move(infile, outpath) { 21 | fs.createReadStream(infile).pipe(fs.createWriteStream(path.join(outpath, infile))); 22 | } 23 | 24 | function moveGlob (inGlob, outpath) { 25 | glob(inGlob, function (err, files) { 26 | if (err) { 27 | console.log(err); 28 | } 29 | files.forEach(function (file) { 30 | move(file, outpath); 31 | }); 32 | }); 33 | } 34 | 35 | mkdirp.sync(path.join(outDir,'lib/leaflet/images')); 36 | mkdirp.sync(path.join(outDir,'styles')); 37 | mkdirp.sync(path.join(outDir,'img')); 38 | input.forEach(function (file) { 39 | move(file, outDir); 40 | }); 41 | inGlobs.forEach(function (file) { 42 | moveGlob(file, outDir); 43 | }); 44 | -------------------------------------------------------------------------------- /src/data/analytics.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports, module) { 2 | 'use strict'; 3 | var flight = require('flight'); 4 | var $ = require('jquery'); 5 | var _ = require('lodash'); 6 | $("