diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..5cf4be8
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "submission"]
+ path = submission
+ url = https://github.com/OpenBounds/SubmissionForm.git
+[submodule "scripts"]
+ path = scripts
+ url = https://github.com/OpenBounds/Processing.git
diff --git a/scripts b/scripts
new file mode 160000
index 0000000..8b8f681
--- /dev/null
+++ b/scripts
@@ -0,0 +1 @@
+Subproject commit 8b8f681ad9b3ef9a1147985f124b94c2064d73a1
diff --git a/scripts/README.md b/scripts/README.md
deleted file mode 100644
index 17c5f72..0000000
--- a/scripts/README.md
+++ /dev/null
@@ -1,50 +0,0 @@
-# Scripts
-
-### Python Environment
-
-Create a virtual environment and install the requirements:
-
-```
-$ virtualenv env
-$ source env/bin/activate
-$ pip install -r requirements.txt
-```
-
-### validate.py
-
-Validate a JSON file against a JSON schema:
-
-```
-$ python scripts/validate.py schemas/source.json sources/US/MT/antelope.json
-```
-
-Usage:
-
-```
-validate.py [OPTIONS] SCHEMA JSONFILE
-
- SCHEMA: JSON schema to validate against. Required.
- JSONFILE: JSON file to validate. Required.
-
-Options:
- --help Show this message and exit.
-```
-
-### upload.py
-
-Upload a directory to S3. Requires environment variables `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and `AWS_BUCKET` to be set.
-
-```
-$ python scripts/upload.py generated/
-```
-
-Usage:
-
-```
-Usage: upload.py [OPTIONS] DIRECTORY
-
- DIRECTORY: Directory to upload. Required.
-
-Options:
- --help Show this message and exit.
-```
\ No newline at end of file
diff --git a/scripts/adapters/__init__.py b/scripts/adapters/__init__.py
deleted file mode 100644
index 2941443..0000000
--- a/scripts/adapters/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-
-import shp
-import kml
-import geojson
diff --git a/scripts/adapters/geojson.py b/scripts/adapters/geojson.py
deleted file mode 100644
index c042dd8..0000000
--- a/scripts/adapters/geojson.py
+++ /dev/null
@@ -1,7 +0,0 @@
-
-def read(url):
- return {}
-
-
-def write():
- pass
diff --git a/scripts/adapters/kml.py b/scripts/adapters/kml.py
deleted file mode 100644
index c042dd8..0000000
--- a/scripts/adapters/kml.py
+++ /dev/null
@@ -1,7 +0,0 @@
-
-def read(url):
- return {}
-
-
-def write():
- pass
diff --git a/scripts/adapters/shp.py b/scripts/adapters/shp.py
deleted file mode 100644
index d0d459a..0000000
--- a/scripts/adapters/shp.py
+++ /dev/null
@@ -1,74 +0,0 @@
-
-from functools import partial
-
-import fiona
-from fiona.transform import transform_geom
-
-
-def _explode(coords):
- """Explode a GeoJSON geometry's coordinates object and
- yield coordinate tuples. As long as the input is conforming,
- the type of the geometry doesn't matter.
-
- From @sgillies answer: http://gis.stackexchange.com/a/90554/27367
- """
- for e in coords:
- if isinstance(e, (float, int, long)):
- yield coords
- break
- else:
- for f in _explode(e):
- yield f
-
-
-def _bbox(feat):
- x, y = zip(*list(_explode(feat['geometry']['coordinates'])))
- return min(x), min(y), max(x), max(y)
-
-
-def _transformer(crs, feat):
- tg = partial(transform_geom, crs, 'EPSG:4326',
- antimeridian_cutting=True, precision=6)
- feat['geometry'] = tg(feat['geometry'])
- return feat
-
-
-def read(fp, prop_map):
- """Read shapefile.
-
- :param fp: file-like object
- """
- layers = fiona.listlayers('/', vfs='zip://' + fp.name)
-
- if not layers:
- raise IOError
-
- filename = '/' + layers[0] + '.shp'
-
- with fiona.open(filename, vfs='zip://' + fp.name) as source:
- collection = {
- 'type': 'FeatureCollection',
- 'features': [],
- 'bbox': [float('inf'), float('inf'), float('-inf'), float('-inf')]
- }
-
- for rec in source:
- transformed = _transformer(source.crs, rec)
- transformed['properties'] = {
- key: str(transformed['properties'][value])
- for key, value in prop_map.iteritems()
- }
- collection['bbox'] = [
- comparator(values)
- for comparator, values in zip(
- [min, min, max, max],
- zip(collection['bbox'], _bbox(transformed))
- )
- ]
- collection['features'].append(transformed)
-
- return collection
-
-
-def write():
- pass
diff --git a/scripts/process.py b/scripts/process.py
deleted file mode 100644
index 19bd0da..0000000
--- a/scripts/process.py
+++ /dev/null
@@ -1,67 +0,0 @@
-
-import os
-from urlparse import urlparse
-
-import click
-
-import adapters
-import utils
-
-
-@click.command()
-@click.argument('sources', type=click.Path(exists=True), required=True)
-@click.argument('output', type=click.Path(exists=True), required=True)
-@click.option('--force', is_flag=True)
-def process(sources, output, force):
- """Download sources and process the file to the output directory.
-
- \b
- SOURCES: Source JSON file or directory of files. Required.
- OUTPUT: Destination directory for generated data. Required.
- """
- for path in utils.get_files(sources):
- pathparts = utils.get_path_parts(path)
- pathparts[0] = output.strip(os.sep)
- pathparts[-1] = pathparts[-1].replace('.json', '.geojson')
-
- outdir = os.sep.join(pathparts[:-1])
- outfile = os.sep.join(pathparts)
-
- source = utils.read_json(path)
- urlfile = urlparse(source['url']).path.split('/')[-1]
-
- if not hasattr(adapters, source['filetype']):
- utils.error('Unknown filetype', source['filetype'], '\n')
- continue
-
- if os.path.isfile(outfile) and not force:
- utils.error('Skipping', path, 'since generated file exists.',
- 'Use --force to regenerate.', '\n')
- continue
-
- utils.info('Downloading', source['url'])
-
- try:
- fp = utils.download(source['url'])
- except IOError:
- utils.error('Failed to download', source['url'], '\n')
- continue
-
- utils.info('Reading', urlfile)
-
- try:
- geojson = getattr(adapters, source['filetype']).read(fp, source['properties'])
- except IOError:
- utils.error('Failed to read', urlfile)
- continue
- finally:
- os.remove(fp.name)
-
- utils.make_sure_path_exists(outdir)
- utils.write_json(outfile, geojson)
-
- utils.success('Done. Processed to', outfile, '\n')
-
-
-if __name__ == '__main__':
- process()
diff --git a/scripts/upload.py b/scripts/upload.py
deleted file mode 100644
index 5c39e68..0000000
--- a/scripts/upload.py
+++ /dev/null
@@ -1,78 +0,0 @@
-
-import os
-
-from boto.s3.connection import S3Connection
-from boto.s3.key import Key
-from boto.exception import S3ResponseError
-import click
-
-import utils
-
-AWS_BUCKET = os.environ.get('AWS_BUCKET')
-
-
-def get_or_create_bucket(conn, bucket_name):
- """Get or create an S3 bucket.
-
- :param conn: boto.s3.connection.S3Connection
- :param bucket_name: string
- :returns: boto.s3.bucket.Bucket
- """
- try:
- bucket = conn.get_bucket(bucket_name)
- except S3ResponseError:
- bucket = conn.create_bucket(bucket_name)
-
- return bucket
-
-
-def sizeof_fmt(num):
- """Human readable file size.
-
- Modified from http://stackoverflow.com/a/1094933/1377021
-
- :param num: float
- :returns: string
- """
- for unit in ['', 'k', 'm', 'g', 't', 'p', 'e', 'z']:
- if abs(num) < 1024.0:
- return "%.0f%s%s" % (num, unit, 'b')
- num /= 1024.0
-
- return "%.f%s%s" % (num, 'y', 'b')
-
-
-@click.command()
-@click.argument('directory', type=click.Path(exists=True), required=True)
-def upload(directory):
- """Upload a directory to S3.
-
- DIRECTORY: Directory to upload. Required.
- """
- if not AWS_BUCKET:
- utils.error('AWS_BUCKET environment variable not set. Exiting.')
- return
-
- conn = S3Connection()
- bucket = get_or_create_bucket(conn, AWS_BUCKET)
-
- files = list(utils.get_files(directory))
- total_size = 0
-
- utils.info('Found', len(files), 'files to upload to s3://' + AWS_BUCKET)
-
- for path in files:
- filesize = os.path.getsize(path)
- total_size += filesize
-
- utils.info('Uploading', path, '-', sizeof_fmt(filesize))
-
- k = Key(bucket)
- k.key = path
- k.set_contents_from_filename(path)
-
- utils.success('Done. Uploaded', sizeof_fmt(total_size))
-
-
-if __name__ == '__main__':
- upload()
diff --git a/scripts/utils.py b/scripts/utils.py
deleted file mode 100644
index 8972856..0000000
--- a/scripts/utils.py
+++ /dev/null
@@ -1,109 +0,0 @@
-
-import os
-import ujson
-import logging
-import tempfile
-import sys
-from urlparse import urlparse
-
-import click
-import requests
-
-CHUNK_SIZE = 1024
-
-logging.basicConfig(level=logging.INFO)
-
-
-def get_files(path):
- """Returns an iterable containing the full path of all files in the
- specified path.
-
- :param path: string
- :yields: string
- """
- if os.path.isdir(path):
- for (dirpath, dirnames, filenames) in os.walk(path):
- for filename in filenames:
- if not filename[0] == '.':
- yield os.path.join(dirpath, filename)
- else:
- yield path
-
-
-def read_json(path):
- """Returns JSON dict from file.
-
- :param path: string
- :returns: dict
- """
- with open(path, 'r') as jsonfile:
- return ujson.loads(jsonfile.read())
-
-
-def write_json(path, data):
- with open(path, 'w') as jsonfile:
- jsonfile.write(ujson.dumps(data, double_precision=5))
-
-
-def make_sure_path_exists(path):
- """Make directories in path if they do not exist.
-
- Modified from http://stackoverflow.com/a/5032238/1377021
-
- :param path: string
- """
- try:
- os.makedirs(path)
- except:
- pass
-
-
-def get_path_parts(path):
- """Splits a path into parent directories and file.
-
- :param path: string
- """
- return path.split(os.sep)
-
-
-def download(url):
- """Downloads a file and returns a file pointer to a temporary file.
-
- :param url: string
- """
- res = requests.get(url, stream=True, verify=False)
- urlfile = urlparse(url).path.split('/')[-1]
- _, extension = os.path.split(urlfile)
-
- if not res.ok:
- raise IOError
-
- fp = tempfile.NamedTemporaryFile('wb', suffix=extension, delete=False)
-
- for chunk in res.iter_content(CHUNK_SIZE):
- fp.write(chunk)
-
- fp.close()
-
- return fp
-
-
-def info(*strings):
- if sys.stdout.isatty():
- click.echo(' '.join(strings))
- else:
- logging.info(' '.join(strings))
-
-
-def error(*strings):
- if sys.stdout.isatty():
- click.secho(' '.join(strings), fg='red')
- else:
- logging.error(' '.join(strings))
-
-
-def success(*strings):
- if sys.stdout.isatty():
- click.secho(' '.join(strings), fg='green')
- else:
- logging.info(' '.join(strings))
diff --git a/scripts/validate.py b/scripts/validate.py
deleted file mode 100644
index d0667d0..0000000
--- a/scripts/validate.py
+++ /dev/null
@@ -1,41 +0,0 @@
-
-import json
-import re
-
-import click
-import jsonschema
-
-import utils
-
-
-@click.command()
-@click.argument('schema', type=click.File('r'), required=True)
-@click.argument('jsonfiles', type=click.Path(exists=True), required=True)
-def validate(schema, jsonfiles):
- """Validate a JSON files against a JSON schema.
-
- \b
- SCHEMA: JSON schema to validate against. Required.
- JSONFILE: JSON files to validate. Required.
- """
- schema = json.loads(schema.read())
-
- for path in utils.get_files(jsonfiles):
- if path.startswith('sources'):
- regex = r'sources/[A-Z]{2}/[A-Z]{2}/[a-z-]+.json'
- elif path.startswith('generated'):
- regex = r'generated/[A-Z]{2}/[A-Z]{2}/[a-z-]+.geojson'
- else:
- regex = r''
-
- if not re.compile(regex).match(path):
- raise AssertionError('Path does not match spec for ' + path)
-
- with open(path) as f:
- jsonfile = json.loads(f.read())
-
- jsonschema.validate(jsonfile, schema)
-
-
-if __name__ == '__main__':
- validate()
diff --git a/submission b/submission
new file mode 160000
index 0000000..e30fa51
--- /dev/null
+++ b/submission
@@ -0,0 +1 @@
+Subproject commit e30fa5127b7f34403cc49a0dc13fea602a6527c2
diff --git a/submission/.babelrc b/submission/.babelrc
deleted file mode 100644
index eaf3238..0000000
--- a/submission/.babelrc
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "presets": ["es2015", "stage-0"]
-}
diff --git a/submission/.eslintrc b/submission/.eslintrc
deleted file mode 100644
index b435e4f..0000000
--- a/submission/.eslintrc
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "parser": "babel-eslint",
- "rules": {
- "strict": 0
- },
- "extends": "eslint:recommended",
- "env": {
- "browser": true
- }
-}
diff --git a/submission/README.md b/submission/README.md
deleted file mode 100644
index 0304964..0000000
--- a/submission/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-## OpenBounds Data Submission
-
-A Javascript app running on Github Pages for data submission via pull request.
\ No newline at end of file
diff --git a/submission/dist/submission.min.css b/submission/dist/submission.min.css
deleted file mode 100644
index 8ea1186..0000000
--- a/submission/dist/submission.min.css
+++ /dev/null
@@ -1 +0,0 @@
-.dropdown-menu::after,.dropdown-menu::before{position:absolute;content:"";left:auto;display:inline-block}.dropdown-btn.selected:focus,.dropdown-btn:focus,.dropdown-btn:focus:hover{border-color:#b5b5b5}.dropdown-btn:focus,.dropdown-btn:focus:hover{box-shadow:none}.dropdown-btn.selected,.dropdown-btn.selected:focus{background-color:#dcdcdc;box-shadow:inset 0 2px 4px rgba(0,0,0,.15)}.dropdown-btn::after{display:inline-block;width:0;height:0;content:"";vertical-align:-2px;margin-left:5px;border:4px solid;border-right-color:transparent;border-left-color:transparent;border-bottom-color:transparent}.dropdown-menu{width:180px;position:absolute;right:10px;top:45px;padding-top:5px;padding-bottom:5px;background-clip:padding-box;box-shadow:0 3px 12px rgba(0,0,0,.15)}.dropdown-menu::before{border:8px solid transparent;border-bottom-color:rgba(0,0,0,.15);top:-16px;right:9px}.dropdown-menu::after{border:7px solid transparent;border-bottom-color:#fff;top:-14px;right:10px}.dropdown-item{padding:4px 10px 4px 15px;color:#333}.dropdown-item:hover{color:#fff;text-decoration:none;text-shadow:none;background-color:#4078c0}#main{position:relative;margin-top:50px}#manual textarea{width:100%;font-family:monospace}#doneerror{float:right}.form.form-inline{display:inline-block;margin-right:10px}.form.form-inline:last-child{margin-right:0}dl.form>dd input[type=text]{width:100%}h2{margin-top:5px;margin-bottom:30px}.avatar{width:20px;margin-right:5px}table,td input{width:100%}table{margin:15px 0}td,th{text-align:left;padding:6px 15px;border-bottom:1px solid #E1E1E1}tr:last-child td{border-bottom:none}td:first-child,th:first-child{padding-left:0}td:last-child,th:last-child{padding-right:0}
\ No newline at end of file
diff --git a/submission/dist/submission.min.js b/submission/dist/submission.min.js
deleted file mode 100644
index 1fb5c26..0000000
--- a/submission/dist/submission.min.js
+++ /dev/null
@@ -1 +0,0 @@
-!function(modules){function __webpack_require__(moduleId){if(installedModules[moduleId])return installedModules[moduleId].exports;var module=installedModules[moduleId]={exports:{},id:moduleId,loaded:!1};return modules[moduleId].call(module.exports,module,module.exports,__webpack_require__),module.loaded=!0,module.exports}var installedModules={};return __webpack_require__.m=modules,__webpack_require__.c=installedModules,__webpack_require__.p="",__webpack_require__(0)}([function(module,exports,__webpack_require__){"use strict";function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{"default":obj}}function _interopRequireWildcard(obj){if(obj&&obj.__esModule)return obj;var newObj={};if(null!=obj)for(var key in obj)Object.prototype.hasOwnProperty.call(obj,key)&&(newObj[key]=obj[key]);return newObj["default"]=obj,newObj}var _github=__webpack_require__(1),github=_interopRequireWildcard(_github),_utils=__webpack_require__(3),utils=_interopRequireWildcard(_utils),_dropdown=__webpack_require__(4),_dropdown2=_interopRequireDefault(_dropdown),BASE_REPO="OpenBounds/OpenHuntingData",REPO_NAME="OpenHuntingData",TIMEOUT_SECS=15,params=utils.getParams(),form=window.document.forms.submission,pr=window.document.getElementById("pr"),alert=window.document.getElementById("alert"),manual=window.document.getElementById("manual"),signinUser=function(err,user){var button=window.document.getElementById("signin"),signout=window.document.getElementById("signout"),blank=window.document.getElementById("unauthenticated");button.setAttribute("href","#"),button.innerHTML='
'+user.login,blank.style.display="none",form.style.display="block",signout.addEventListener("click",signoutUser),new _dropdown2["default"](button)},signoutUser=function(){github.clearToken(),window.location.href=window.location.pathname},startSubmitting=function(){pr.setAttribute("disabled","disabled"),pr.textContent="Submitting..."},doneSubmitting=function(){pr.removeAttribute("disabled"),pr.textContent="Submit Pull Request"},errorSubmitting=function(msg,content){alert.innerHTML=msg,manual.getElementsByTagName("textarea")[0].textContent=content,alert.style.display="block",manual.style.display="block"},doneError=function(){alert.innerHTML="",manual.getElementsByTagName("textarea")[0].textContent="",alert.style.display="none",manual.style.display="none",doneSubmitting()},addSource=function(username,repo,source){var filename=source.species.join("-").replace(/[\s]/g,"").toLowerCase(),path="sources/"+source.country+"/"+source.state+"/"+filename+".json",branch="add-"+source.country+"-"+source.state+"-"+filename,msg="add "+source.country+"/"+source.state+"/"+filename+".json",errMsg="Error submitting pull request. Create the file "+path+" with the JSON below.",raw=JSON.stringify(source,null,3),content=window.btoa(raw);github.getHead(repo,function(err,sha){return err?errorSubmitting(errMsg,raw):void github.branchRepo(repo,branch,sha,function(err){return err?errorSubmitting(errMsg,raw):void github.createFile(repo,branch,path,content,msg,function(err){return err?errorSubmitting(errMsg,raw):void github.pullRequest(BASE_REPO,username+":"+branch,msg,function(err){return err?errorSubmitting(errMsg,raw):void doneSubmitting()})})})})},submit=function(e){var source=void 0,filename=void 0,path=void 0,errMsg=void 0,raw=void 0;if(e.preventDefault(),github.getToken()){startSubmitting(),source={url:form.url.value,species:form.species.value.split(", "),attribution:form.attribution.value,properties:{},country:form.country.value,state:form.state.value,filetype:form.filetype.value};for(var _arr=["id","name"],_i=0;_i<_arr.length;_i++){var property=_arr[_i];source.properties[property]=form[property].value}filename=source.species.join("-").replace(/[\s]/g,"").toLowerCase(),path="sources/"+source.country+"/"+source.state+"/"+filename+".json",errMsg="Error submitting pull request. Create the file "+path+" with the JSON below.",raw=JSON.stringify(source,null,3),github.getUser(function(err,user){if(err)return errorSubmitting(errMsg,raw);var username=user.login,repo=username+"/"+REPO_NAME;github.getRepo(repo,function(err,response){return err?errorSubmitting(errMsg,raw):void(response?addSource(username,repo,source):github.forkRepo(BASE_REPO,function(err){return err?errorSubmitting(errMsg,raw):void github.getRepo(repo,function(err){if(err)return errorSubmitting(errMsg,raw);var count=0,ping=window.setInterval(function(){github.getHead(repo,function(err,sha){sha?(window.clearInterval(ping),addSource(username,repo,source)):(count+=1,count>2*TIMEOUT_SECS&&(window.clearInterval(ping),errorSubmitting(errMsg,raw)))})},500)})}))})})}};github.getToken()?github.getUser(signinUser):params.code&&github.accessToken(params.code,function(){window.history.replaceState({},window.document.title,window.location.pathname),github.getUser(signinUser)}),form.addEventListener("submit",submit,!1),window.document.getElementById("doneerror").addEventListener("click",doneError,!1)},function(module,exports,__webpack_require__){"use strict";function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{"default":obj}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.pullRequest=exports.createFile=exports.branchRepo=exports.forkRepo=exports.getHead=exports.getRepo=exports.getUser=exports.ajax=exports.accessToken=exports.clearToken=exports.getToken=void 0;var _nanoajax=__webpack_require__(2),_nanoajax2=_interopRequireDefault(_nanoajax),API_BASE="https://api.github.com",token=window.localStorage.getItem("token"),ajax=(exports.getToken=function(){return token},exports.clearToken=function(){window.localStorage.removeItem("token")},exports.accessToken=function(code,cb){_nanoajax2["default"].ajax({url:"http://github-gatekeeper.aws.gaiagps.com/authenticate/"+code},function(code,response){token=JSON.parse(response).token,window.localStorage.setItem("token",token),cb(token)})},exports.ajax=function(options,cb){options.headers={Authorization:"token "+token},_nanoajax2["default"].ajax(options,function(code,response){var parsed=void 0;try{parsed=JSON.parse(response)}catch(e){return cb(e)}return cb(null,parsed)})});exports.getUser=function(cb){ajax({url:API_BASE+"/user"},cb)},exports.getRepo=function(repo,cb){ajax({url:API_BASE+"/repos/"+repo},function(err,response){return err?cb(err):response.message&&"Not Found"===response.message?cb(null,null):void cb(null,response)})},exports.getHead=function(repo,cb){ajax({url:API_BASE+"/repos/"+repo+"/git/refs/heads/master"},function(err,response){return err?cb(err):response.message&&"Git Repository is empty."===response.message?cb(null,null):void cb(null,response.object.sha)})},exports.forkRepo=function(repo,cb){ajax({url:API_BASE+"/repos/"+repo+"/forks",method:"POST"},cb)},exports.branchRepo=function(repo,branch,sha,cb){ajax({url:API_BASE+"/repos/"+repo+"/git/refs",body:JSON.stringify({ref:"refs/heads/"+branch,sha:sha})},cb)},exports.createFile=function(repo,branch,path,content,message,cb){ajax({url:API_BASE+"/repos/"+repo+"/contents/"+path,method:"PUT",body:JSON.stringify({message:message,content:content,branch:branch})},cb)},exports.pullRequest=function(repo,head,message,cb){ajax({url:API_BASE+"/repos/"+repo+"/pulls",body:JSON.stringify({title:message,head:head,base:"master"})},cb)}},function(module,exports){(function(global){function getRequest(cors){return cors&&global.XDomainRequest&&!/MSIE 1/.test(navigator.userAgent)?new XDomainRequest:global.XMLHttpRequest?new XMLHttpRequest:void 0}function setDefault(obj,key,value){obj[key]=obj[key]||value}var reqfields=["responseType","withCredentials","timeout","onprogress"];exports.ajax=function(params,callback){function cb(statusCode,responseText){return function(){called||callback(req.status||statusCode,req.response||req.responseText||responseText,req),called=!0}}var headers=params.headers||{},body=params.body,method=params.method||(body?"POST":"GET"),called=!1,req=getRequest(params.cors);req.open(method,params.url,!0);var success=req.onload=cb(200);req.onreadystatechange=function(){4===req.readyState&&success()},req.onerror=cb(null,"Error"),req.ontimeout=cb(null,"Timeout"),req.onabort=cb(null,"Abort"),body&&(setDefault(headers,"X-Requested-With","XMLHttpRequest"),setDefault(headers,"Content-Type","application/x-www-form-urlencoded"));for(var field,i=0,len=reqfields.length;len>i;i++)field=reqfields[i],void 0!==params[field]&&(req[field]=params[field]);for(var field in headers)req.setRequestHeader(field,headers[field]);return req.send(body),req}}).call(exports,function(){return this}())},function(module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:!0});exports.getParams=function(){var params={},_iteratorNormalCompletion=!0,_didIteratorError=!1,_iteratorError=void 0;try{for(var _step,_iterator=window.location.search.substring(1).split("&")[Symbol.iterator]();!(_iteratorNormalCompletion=(_step=_iterator.next()).done);_iteratorNormalCompletion=!0){var param=_step.value,nv=param.split("=");nv[0]&&(params[nv[0]]=nv[1]||!0)}}catch(err){_didIteratorError=!0,_iteratorError=err}finally{try{!_iteratorNormalCompletion&&_iterator["return"]&&_iterator["return"]()}finally{if(_didIteratorError)throw _iteratorError}}return params}},function(module,exports){"use strict";function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(exports,"__esModule",{value:!0});var Dropdown=function Dropdown(button){var _this=this;_classCallCheck(this,Dropdown),this.open=function(e){e.preventDefault(),_this.closed?(_this.closed=!1,_this.button.nextElementSibling.style.display="block",_this.button.classList.add("selected"),window.setTimeout(function(){window.document.addEventListener("click",_this.close)},50)):_this.close()},this.close=function(e){e.preventDefault(),window.document.removeEventListener("click",_this.close),_this.button.classList.remove("selected"),_this.button.nextElementSibling.style.display="none",_this.closed=!0},this.closed=!0,this.button=button,this.button.classList.add("dropdown-btn"),this.button.addEventListener("click",this.open,!0)};exports["default"]=Dropdown}]);
\ No newline at end of file
diff --git a/submission/package.json b/submission/package.json
deleted file mode 100644
index ba06e15..0000000
--- a/submission/package.json
+++ /dev/null
@@ -1,36 +0,0 @@
-{
- "name": "openboundsdata",
- "version": "1.0.0",
- "description": "OpenBounds Data Submission",
- "main": "src/index.js",
- "repository": {
- "type": "git",
- "url": "https://github.com/OpenBounds/OpenHuntingData.git"
- },
- "scripts": {
- "lint": "eslint src/*.js",
- "build-css": "cleancss --output dist/submission.min.css src/css/*.css",
- "build-js": "webpack && uglifyjs dist/submission.js -c -o dist/submission.min.js",
- "build": "npm run build-js && npm run build-css"
- },
- "author": "OpenBounds",
- "license": "MIT",
- "bugs": {
- "url": "https://github.com/OpenBounds/OpenHuntingData/issues"
- },
- "homepage": "https://github.com/OpenBounds/OpenHuntingData",
- "dependencies": {
- "nanoajax": "^0.4.0"
- },
- "devDependencies": {
- "babel-core": "^6.1.2",
- "babel-eslint": "^4.1.4",
- "babel-loader": "^6.0.1",
- "babel-preset-es2015": "^6.1.2",
- "babel-preset-stage-0": "^6.1.18",
- "clean-css": "^3.4.7",
- "eslint": "^1.9.0",
- "uglify": "^0.1.5",
- "webpack": "^1.12.3"
- }
-}
diff --git a/submission/src/css/dropdown.css b/submission/src/css/dropdown.css
deleted file mode 100644
index ee5dae7..0000000
--- a/submission/src/css/dropdown.css
+++ /dev/null
@@ -1,74 +0,0 @@
-
-.dropdown-btn:focus,
-.dropdown-btn:focus:hover,
-.dropdown-btn.selected:focus {
- border-color: #b5b5b5;
-}
-
-.dropdown-btn:focus,
-.dropdown-btn:focus:hover {
- box-shadow: none;
-}
-
-.dropdown-btn.selected, .dropdown-btn.selected:focus {
- background-color: #dcdcdc;
- box-shadow: inset 0 2px 4px rgba(0,0,0,0.15);
-}
-
-.dropdown-btn::after {
- display: inline-block;
- width: 0;
- height: 0;
- content: "";
- vertical-align: -2px;
- margin-left: 5px;
- border: 4px solid;
- border-right-color: transparent;
- border-left-color: transparent;
- border-bottom-color: transparent;
-}
-
-.dropdown-menu {
- width: 180px;
- position: absolute;
- right: 10px;
- top: 45px;
- padding-top: 5px;
- padding-bottom: 5px;
- background-clip: padding-box;
- box-shadow: 0 3px 12px rgba(0,0,0,0.15);
-}
-
-.dropdown-menu::before {
- position: absolute;
- display: inline-block;
- content: "";
- border: 8px solid transparent;
- border-bottom-color: rgba(0,0,0,0.15);
- top: -16px;
- left: auto;
- right: 9px;
-}
-
-.dropdown-menu::after {
- position: absolute;
- display: inline-block;
- content: "";
- border: 7px solid transparent;
- border-bottom-color: #fff;
- top: -14px;
- left: auto;
- right: 10px;
-}
-
-.dropdown-item {
- padding: 4px 10px 4px 15px;
- color: #333;
-}
-
-.dropdown-item:hover {
- color: #fff;
- text-decoration: none;
- text-shadow: none;
- background-color: #4078c0;
-}
diff --git a/submission/src/css/submission.css b/submission/src/css/submission.css
deleted file mode 100644
index 2ac10d5..0000000
--- a/submission/src/css/submission.css
+++ /dev/null
@@ -1,67 +0,0 @@
-
-#main {
- position: relative;
- margin-top: 50px;
-}
-
-#manual textarea {
- width: 100%;
- font-family: monospace;
-}
-
-#doneerror {
- float: right;
-}
-
-.form.form-inline {
- display: inline-block;
- margin-right: 10px;
-}
-
-.form.form-inline:last-child {
- margin-right: 0;
-}
-
-dl.form > dd input[type="text"] {
- width: 100%;
-}
-
-h2 {
- margin-top: 5px;
- margin-bottom: 30px;
-}
-
-.avatar {
- width: 20px;
- margin-right: 5px;
-}
-
-table {
- width: 100%;
- margin: 15px 0;
-}
-
-th,
-td {
- text-align: left;
- padding: 6px 15px;
- border-bottom: 1px solid #E1E1E1;
-}
-
-tr:last-child td {
- border-bottom: none;
-}
-
-td input {
- width: 100%;
-}
-
-th:first-child,
-td:first-child {
- padding-left: 0;
-}
-
-th:last-child,
-td:last-child {
- padding-right: 0;
-}
diff --git a/submission/src/dropdown.js b/submission/src/dropdown.js
deleted file mode 100644
index 3d227db..0000000
--- a/submission/src/dropdown.js
+++ /dev/null
@@ -1,39 +0,0 @@
-
-/**
- * Create dropdown with button
- *
- */
-export default class Dropdown {
- constructor (button) {
- this.closed = true
- this.button = button
- this.button.classList.add('dropdown-btn')
- this.button.addEventListener('click', this.open, true)
- }
-
- open = e => {
- e.preventDefault()
-
- if (this.closed) {
- this.closed = false
- this.button.nextElementSibling.style.display = 'block'
- this.button.classList.add('selected')
-
- window.setTimeout(() => {
- window.document.addEventListener('click', this.close)
- }, 50)
- } else {
- this.close()
- }
- }
-
- close = e => {
- e.preventDefault()
-
- window.document.removeEventListener('click', this.close)
-
- this.button.classList.remove('selected')
- this.button.nextElementSibling.style.display = 'none'
- this.closed = true
- }
-}
diff --git a/submission/src/github.js b/submission/src/github.js
deleted file mode 100644
index 0890ff9..0000000
--- a/submission/src/github.js
+++ /dev/null
@@ -1,172 +0,0 @@
-
-import nanoajax from 'nanoajax'
-
-const API_BASE = 'https://api.github.com'
-
-/*
- * Github is restrictive on cookie usage on Github Pages. Use localStorage
- * to store the OAuth token.
- */
-let token = window.localStorage.getItem('token')
-
-
-export const getToken = () => token
-
-export const clearToken = () => {
- window.localStorage.removeItem('token')
-}
-
-/**
- * Get access token from Github OAuth code.
- *
- * @param {string} code OAuth code from Github API
- */
-export const accessToken = (code, cb) => {
- nanoajax.ajax({
- url: 'http://github-gatekeeper.aws.gaiagps.com/authenticate/' + code
- }, (code, response) => {
- token = JSON.parse(response).token
- window.localStorage.setItem('token', token)
-
- cb(token)
- })
-}
-
-/**
- * AJAX call with Github Authorization header.
- *
- * @param {object} options nanoajax options
- * @param {function} cb callback(err, data)
- */
-export const ajax = (options, cb) => {
- options.headers = {'Authorization': 'token ' + token}
-
- nanoajax.ajax(options, (code, response) => {
- let parsed
-
- try {
- parsed = JSON.parse(response)
- } catch (e) {
- return cb(e)
- }
-
- return cb(null, parsed)
- })
-}
-
-/**
- * Get user.
- *
- * @param {function} cb callback(err, data)
- */
-export const getUser = cb => {
- ajax({ url: API_BASE + '/user' }, cb)
-}
-
-/**
- * Get repo if exists.
- *
- * @param {string} repo repo to check.
- * @param {function} cb callback(err, data)
- */
-export const getRepo = (repo, cb) => {
- ajax({ url: API_BASE + '/repos/' + repo }, (err, response) => {
- if (err) return cb(err)
-
- if (response.message && response.message === 'Not Found') {
- return cb(null, null)
- }
-
- cb(null, response)
- })
-}
-
-/**
- * Get latest commit SHA on master branch.
- *
- * @param {string} repo repo to get commit from.
- * @param {function} cb callback(err, data)
- */
-export const getHead = (repo, cb) => {
- ajax({ url: API_BASE + '/repos/' + repo + '/git/refs/heads/master' }, (err, response) => {
- if (err) return cb(err)
-
- if (response.message && response.message === 'Git Repository is empty.') {
- return cb(null, null)
- }
-
- cb(null, response.object.sha)
- })
-}
-
-/**
- * Fork repo.
- *
- * @param {string} repo repo to fork, ie. 'OpenBounds/OpenHuntingData'
- * @param {function} cb callback(err, data)
- */
-export const forkRepo = (repo, cb) => {
- ajax({
- url: API_BASE + '/repos/' + repo + '/forks',
- method: 'POST'
- }, cb)
-}
-
-/**
- * Create branch in repo.
- *
- * @param {string} repo repo to create the branch in.
- * @param {string} branch branch name.
- * @param {string} sha SHA1 to set the branch to.
- * @param {function} cb callback(err, data)
- */
-export const branchRepo = (repo, branch, sha, cb) => {
- ajax({
- url: API_BASE + '/repos/' + repo + '/git/refs',
- body: JSON.stringify({
- ref: 'refs/heads/' + branch,
- sha: sha
- })
- }, cb)
-}
-
-/**
- * Create file in repo.
- *
- * @param {string} repo repo to create the file in.
- * @param {string} branch branch to create the file in.
- * @param {string} path file path.
- * @param {base64} content base64 encoded file content.
- * @param {string} message commit message.
- * @param {function} cb callback(err, data)
- */
-export const createFile = (repo, branch, path, content, message, cb) => {
- ajax({
- url: API_BASE + '/repos/' + repo + '/contents/' + path,
- method: 'PUT',
- body: JSON.stringify({
- message: message,
- content: content,
- branch: branch
- })
- }, cb)
-}
-
-/**
- * Create a pull request
- *
- * @param {string} repo repo to create the pull request in.
- * @param {string} head branch to pull request, ie. user:add-source
- * @param {string} message pull request title.
- * @param {function} cb callback(err, data)
- */
-export const pullRequest = (repo, head, message, cb) => {
- ajax({
- url: API_BASE + '/repos/' + repo + '/pulls',
- body: JSON.stringify({
- title: message,
- head: head,
- base: 'master'
- })
- }, cb)
-}
diff --git a/submission/src/index.js b/submission/src/index.js
deleted file mode 100644
index e9d9f31..0000000
--- a/submission/src/index.js
+++ /dev/null
@@ -1,229 +0,0 @@
-
-import * as github from './github'
-import * as utils from './utils'
-import Dropdown from './dropdown'
-
-const BASE_REPO = 'OpenBounds/OpenHuntingData'
-const REPO_NAME = 'OpenHuntingData'
-const TIMEOUT_SECS = 15
-
-
-let params = utils.getParams()
- , form = window.document.forms['submission']
- , pr = window.document.getElementById('pr')
- , alert = window.document.getElementById('alert')
- , manual = window.document.getElementById('manual')
-
-
-/**
- * Sign in user, when loading the page or after authentication.
- *
- * @param {object} user Github user object.
- */
-const signinUser = (err, user) => {
- let button = window.document.getElementById('signin')
- , signout = window.document.getElementById('signout')
- , blank = window.document.getElementById('unauthenticated')
-
- button.setAttribute('href', '#')
- button.innerHTML = `
${user.login}`
-
- blank.style.display = 'none'
- form.style.display = 'block'
-
- signout.addEventListener('click', signoutUser)
-
- new Dropdown(button)
-}
-
-const signoutUser = () => {
- github.clearToken()
-
- window.location.href = window.location.pathname
-}
-
-/*
- * Handle UI changes for start, done and error submitting events.
- */
-const startSubmitting = () => {
- pr.setAttribute('disabled', 'disabled')
- pr.textContent = 'Submitting...'
-
-}
-
-const doneSubmitting = () => {
- pr.removeAttribute('disabled')
- pr.textContent = 'Submit Pull Request'
-}
-
-const errorSubmitting = (msg, content) => {
- alert.innerHTML = msg
- manual.getElementsByTagName('textarea')[0].textContent = content
-
- alert.style.display = 'block'
- manual.style.display = 'block'
-}
-
-const doneError = () => {
- alert.innerHTML = ''
- manual.getElementsByTagName('textarea')[0].textContent = ''
-
- alert.style.display = 'none'
- manual.style.display = 'none'
-
- doneSubmitting()
-}
-
-/**
- * Create a pull request to add a source file.
- *
- * Get the head sha of the master branch. Create a feature branch at that sha
- * named after the file being submitted. In the branch, create the source file
- * with Base64 encoded JSON pretty-printed content. Then submit a pull request
- * of the feature branch to the base repo.
- *
- * @param {string} username Github user's username.
- * @param {string} repo Repo to create the file in, ie. user/OpenHuntingData
- * @param {object} source Source object.
- */
-const addSource = (username, repo, source) => {
- let filename = source.species.join('-').replace(/[\s]/g, '').toLowerCase()
- , path = `sources/${source.country}/${source.state}/${filename}.json`
- , branch = `add-${source.country}-${source.state}-${filename}`
- , msg = `add ${source.country}/${source.state}/${filename}.json`
- , errMsg = `Error submitting pull request. Create the file ${path} with the JSON below.`
- , raw = JSON.stringify(source, null, 3)
- , content = window.btoa(raw)
-
- github.getHead(repo, (err, sha) => {
- if (err) return errorSubmitting(errMsg, raw)
-
- github.branchRepo(repo, branch, sha, (err) => {
- if (err) return errorSubmitting(errMsg, raw)
-
- github.createFile(repo, branch, path, content, msg, (err) => {
- if (err) return errorSubmitting(errMsg, raw)
-
- github.pullRequest(BASE_REPO, username + ':' + branch, msg, (err) => {
- if (err) return errorSubmitting(errMsg, raw)
-
- doneSubmitting()
- })
- })
- })
- })
-}
-
-/*
- * Submit source form to Github pull request.
- *
- * Create a source object from the source form. Get the authenticated user and
- * username from Github, then check if the user has already forked the repo.
- *
- * If the repo is found, add the source to the repo. Otherwise, create a fork
- * of the repo, and wait until it becomes available (async call). If fork
- * does not become available within TIMEOUT_SEC, fail.
- */
-const submit = e => {
- let source
- , filename
- , path
- , errMsg
- , raw
-
- e.preventDefault()
-
- if (!github.getToken()) return
-
- startSubmitting()
-
- source = {
- url: form.url.value,
- species: form.species.value.split(', '),
- attribution: form.attribution.value,
- properties: {},
- country: form.country.value,
- state: form.state.value,
- filetype: form.filetype.value
- }
-
- for (let property of ['id', 'name']) {
- source.properties[property] = form[property].value
- }
-
- filename = source.species.join('-').replace(/[\s]/g, '').toLowerCase()
- path = `sources/${source.country}/${source.state}/${filename}.json`
- errMsg = `Error submitting pull request. Create the file ${path} with the JSON below.`
- raw = JSON.stringify(source, null, 3)
-
- github.getUser((err, user) => {
- if (err) return errorSubmitting(errMsg, raw)
-
- let username = user.login
- , repo = username + '/' + REPO_NAME
-
- github.getRepo(repo, (err, response) => {
- if (err) return errorSubmitting(errMsg, raw)
-
- if (response) {
- addSource(username, repo, source)
- } else {
- github.forkRepo(BASE_REPO, (err) => {
- if (err) return errorSubmitting(errMsg, raw)
-
- github.getRepo(repo, (err) => {
- if (err) return errorSubmitting(errMsg, raw)
-
- let count = 0
- , ping = window.setInterval(() => {
- github.getHead(repo, (err, sha) => {
- if (sha) {
- window.clearInterval(ping)
- addSource(username, repo, source)
- } else {
- count += 1
-
- if (count > TIMEOUT_SECS * 2) {
- window.clearInterval(ping)
-
- errorSubmitting(errMsg, raw)
- }
- }
- })
- }, 500)
- })
- })
- }
- })
- })
-}
-
-/*
- * Handle user authentication and OAuth response. If the user token is present
- * when the page loads, retrieve the user object from Github and update the
- * UI. Otherwise, if the URL parameter `code` is set (a resposne from Github's
- * OAuth API), exchange it for a token with Gatekeeper.
- *
- * Replace the window history state to prevent multiple tokens being created,
- * then update the UI.
- */
-if (github.getToken()) {
- github.getUser(signinUser)
-} else {
- if (params.code) {
- github.accessToken(params.code, () => {
- window.history.replaceState({}, window.document.title, window.location.pathname)
- github.getUser(signinUser)
- })
- }
-}
-
-/*
- * Listen for the form submit event, and submit a pull request of the new source.
- */
-form.addEventListener('submit', submit, false)
-
-/*
- * Clear error message when done button is clicked.
- */
-window.document.getElementById('doneerror').addEventListener('click', doneError, false)
diff --git a/submission/src/utils.js b/submission/src/utils.js
deleted file mode 100644
index 633aa03..0000000
--- a/submission/src/utils.js
+++ /dev/null
@@ -1,19 +0,0 @@
-
-/**
- * Get params from URL.
- *
- * Modified from http://stackoverflow.com/a/979996/1377021
- */
-export const getParams = () => {
- let params = {}
-
- for (let param of window.location.search.substring(1).split('&')) {
- let nv = param.split('=')
-
- if (!nv[0]) continue;
-
- params[nv[0]] = nv[1] || true
- }
-
- return params
-}
diff --git a/submission/webpack.config.js b/submission/webpack.config.js
deleted file mode 100644
index 8494ecb..0000000
--- a/submission/webpack.config.js
+++ /dev/null
@@ -1,17 +0,0 @@
-
-webpack = require('webpack')
-
-module.exports = {
- entry: "./src/index.js",
- module: {
- loaders: [{
- test: /\.js$/,
- exclude: /node_modules/,
- loader: "babel-loader"
- }]
- },
- output: {
- path: './dist/',
- filename: 'submission.js'
- }
-};