<commit_before>import {inject} from 'aurelia-framework';
import {HttpClient} from 'aurelia-fetch-client';
import 'fetch';
import {LogManager} from 'aurelia-framework';
import Config from '../AppConfig';

let logger = LogManager.getLogger('api');

//let baseUrl = 'http://next.belframework.org/api';
let baseUrl = Config.baseUrl;

let parse = message => JSON.parse(message.response);

// http://europepmc.org/RestfulWebService#search
// http://www.ebi.ac.uk/europepmc/webservices/rest/search/resulttype=core&format=json&query=src:med ext_id:1945500
// http://next.belframework.org/europepmc/webservices/rest/search/resulttype=core&format=json&query=src:med  // proxied
// to remove CORS issue
// http://next.belframework.org/europepmc/webservices/rest/search/resulttype=core&format=json&query=src:med
// ext_id:1945500 Using this technique to proxy http://oskarhane.com/avoid-cors-with-nginx-proxy_pass

// let pubmedBaseUrl = 'http://next.belframework.org/europepmc/webservices/rest/search';
// let pubmedBaseUrl = 'http://www.ebi.ac.uk/europepmc/webservices/rest/search';
let pubmedBaseUrl = Config.pubmedBaseUrl;

export class Api {

  constructor() {
    // Simple API Client - not preconfigured with BaseUrl
    this.client = new HttpClient();
    this.client.configure(config => {
      config
        .withDefaults({
          headers: {
            'Accept': 'application/json',
            'X-Requested-With': 'Fetch'
          }
        })
        .rejectErrorResponses()
        .withInterceptor({
          request(req) {
              logger.debug(`Requesting ${req.method} ${req.url}`);
              return req; // you can return a modified Request, or you can short-circuit the request by returning a
              // Response
            },
            response(resp) {
              logger.debug(`Received ${resp.status} ${resp.url}`);
              return resp; // you can return a modified Response
            }
        });
    });

    // OpenBEL API Client - preconfigured with BaseUrl
    this.apiClient = new HttpClient();
    // this.apiClient.baseUrl = baseUrl;
    this.apiClient.configure(config => {
      config
        .withBaseUrl(baseUrl)
        .withDefaults({
          credentials: 'same-origin',
          headers: {
            'Accept': 'application/json',
            'X-Requested-With': 'Fetch'
          }
        })
        .rejectErrorResponses()
        .withInterceptor({
          request(req) {
              logger.debug(`Requesting ${req.method} ${req.url}`);
              return req; // you can return a modified Request, or you can short-circuit the request by returning a
              // Response
            },
            response(resp) {
              logger.debug(`Received ${resp.status} ${resp.url}`);
              return resp; // you can return a modified Response
            },
            responseError(resp) {
              if (resp.status === 401) {
                logger.info('Backend returned HTTP 401, redirecting to home.');
                window.location.href = window.location.origin;
              }
              logger.debug(`Received ${resp.status} ${resp.url}`);
              return resp; // you can return a modified Response
            }
        });
    });

    // Pubmed API Client - preconfigured with BaseUrl
    this.pubmedClient = new HttpClient();
    this.pubmedClient.configure(config => {
      config
        .withBaseUrl(pubmedBaseUrl)
        .withDefaults({
          headers: {
            'Accept': 'application/json'
          }
        })
        .rejectErrorResponses()
        .withInterceptor({
          request(req) {
              logger.debug(`Requesting ${req.method} ${req.url}`);
              return req; // you can return a modified Request, or you can short-circuit the request by returning a
              // Response
            },
            response(resp) {
              logger.debug(`Received ${resp.status} ${resp.url}`);
              return resp; // you can return a modified Response
            }
        });
    });
  }

  /**
   *
Process facets from search

   *
This removes the facets that won't be used in the search results page
   *    and reorganizes it for presentation in the search results page.
   *

   *
   * @param {Object} facets - facets returned by BEL API from search
   * @returns {Object} facets - facets after processing to be used in web page
   * */
  processFacets(facets) {
    let newFacets = {};

    for (let facet of facets) {
      // logger.debug("Facet: ", facet);
      if (facet.category === 'experiment_context' || facet.name === 'Status' || facet.category === 'citation') {
        // logger.debug("Status Facet: ", facet);
        let facetName = facet.name;

        if (facet.category === 'citation') {
          facetName = 'Reference ID';
        }

        newFacets[facetName] = [];
        for (let value of facet.values) {
          let name = value.value;
          newFacets[facetName].push({
            'name': name,
            'count': value.count,
            'filter': value.filter
          });
        }
      }
    }

    return newFacets;
  }

  /**
   * Search the BELMgr database and return website ready results
   *
   * @param {String} filter - search string
   * @param {Integer} start - result page starting point (default = 0)
   * @param {Integer} size - number of results returned (default = 10)
   * @param {Integer} faceted - facet results if equals 1 (default = 1)
   * @return {Promise} data - processed search results ready to display on the search results web page
   * */
  search(start = 0, size = 10, faceted = 'yes', filters) {
    let max_values_per_facet = 10;
    let getstring = `/evidence?start=${start}&size=${size}&faceted=${faceted}&max_values_per_facet=${max_values_per_facet}`;
    if (filters) {
      for (let filter of filters) {
        getstring += `&filter=${filter}`;
      }
    }
    // logger.debug('Filters2: ', filters);
    logger.debug('Getstring: ', getstring);

    return this.apiClient.fetch(getstring, {
      headers: {
        Authorization: "Bearer " + this.getToken()
      }
    })
      .then(response => response.json())
      .then(data => {
        let new_data = {};
        new_data.evidences = data.evidence_collection;
        new_data.facets = this.processFacets(data.facets);
        new_data.metadata = data.metadata;
        logger.debug('Search results: ', new_data);
        return new_data;
      })
      .catch(function(reason) {
        if (reason.status === 404) {
          return {
            "evidences": null,
            "facets": {}
          };
        } else {
          logger.error("Search API Error: ", reason);
        }
      });
  }

  /**
   * Get BEL components (subject, relation, object) from a BEL statement
   * @param belStatement
   * @returns {"subject": "", "relationship": "", "object": ""}
   */
  getBelComponents(belStatement) {
    if (belStatement) {
      return this.apiClient.fetch(`/expressions/${belStatement}/components?flatten=1`)
        .then(response => response.json())
        .then(data => {
          // logger.debug('BEL Evidence: ', data);
          return data.expression_components;
        })
        .catch(function(reason) {
          logger.error(`GET BEL Components Error: ${reason}`)
        });
    }
    let comp = {};
    return comp;
  }

  getBelEvidence(id) {
    return this.apiClient.fetch(`/evidence/${id}`, {headers:{Authorization: "Bearer " + this.getToken() }})
      .then(response => response.json())
      .then(data => {
        let evidence = data.evidence;
        return evidence;
      })
      .catch(function(reason) {
        logger.error(`GET BEL Evidence Error: ${reason}`)
      });
  }

  /**
   * Load BEL Evidence (POST or PUT)
   *
   * @param id
   * @param evidence
   */
  loadBelEvidence(evidence, id) {
    // Update Evidence given an Id
    if (id) {
      return this.apiClient.fetch(`/evidence/${id}`, {
          method: 'put',
          headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json; profile=http://next.belframework.org/schema/evidence.schema.json',
            'Authorization': "Bearer " + this.getToken()
          },
          body: JSON.stringify(evidence)
        })
        .catch(response => {
          logger.error('PUT Evidence error ', response);
        });
    } else {
      // Create new Evidence
      return this.apiClient.fetch(`/evidence`, {
          method: 'post',
          headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json; profile=http://next.belframework.org/schema/evidence.schema.json',
            'Authorization': "Bearer " + this.getToken()
          },
          body: JSON.stringify(evidence)
        })
        .catch(response => {
          logger.error('POST Evidence error ', response);
        });
    }
  }

  deleteBelEvidence(evidenceId) {
    return this.apiClient.fetch(`/evidence/${evidenceId}`, {
      method: 'delete',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json; profile=http://next.belframework.org/schema/evidence.schema.json',
        'Authorization': "Bearer " + this.getToken()
      }
    })
    .catch(function(reason) {
      logger.error(`DELETE BEL Evidence Error: `, reason);
    });
  }

  /**
   * Get Pubmed document information - e.g. title, abstract, etc.
   *
   * @param id
   * @returns {*}
   */
  getPubmed(id) {

    let getstring = `/resulttype=core&format=json&query=ext_id:${id} src:med`;

    return this.pubmedClient.fetch(getstring)
      .then(response => response.json())
      .then(data => {
        return data.resultList.result[0];
      })
      .catch(response => {
        logger.error(`GET Pubmed Error: `, response);
      });
  }

  // Get list of all BEL Annotation Resources
  getBelAnnotationTypes() {
    return this.apiClient.fetch('/annotations')
      .then(response => response.json())
      .then(data => data.annotation_collection)
      .catch(function(reason) {
        logger.error(`GET BEL Annotation Types Error: ${reason}`)
      });
  }

  // Get BEL Annotation values for the edit form typeahead
  //   the query string should be wildcarded on the OpenBEL API side (e.g. 'ho sap' -> 'ho* sap*')
  getBELAnnotationValues(query, annotation_type) {
    let numResults = 10;
    let query_string = '';

    if (annotation_type) {
      query_string = `/annotations/${annotation_type}/values?filter={"category":"fts","name":"search","value":"${query}"}&start=0&size=${numResults}`;
    } else {
      query_string = `/annotations/values?filter={"category":"fts","name":"search","value":"${query}"}&start=0&size=${numResults}`;
    }

    return this.apiClient.fetch(query_string)
      .then(response => response.json())
      .then(data => data.annotation_value_collection)
      .catch(function(reason) {
        logger.error(`GET BEL Annotation Values Error: ${reason}`)
      });
  }

  // Get BEL term completions for the edit form typeahead
  getBelCompletions(query, cursor) {
    return this.apiClient.fetch(`/expressions/${query}/completions?caret_position=${cursor}`)
      .then(response => {
        return response.json();
      })
      .then(json => {
        let data = json.completion_collection;
        // logger.debug('Data: ', data);
        let completions = this.processBelCompletions(query, data);
        // logger.debug('Completions: ', completions);
        return completions;
      })
      .catch(reason => {
        logger.error('GET Bel Completions error: ', reason);
      });
  }

  processBelCompletions(query, data) {
    let results = [];
    // logger.debug('Data: ', data);
    for (let d of data) {
      // logger.debug('d: ', d);
      let bel = query.slice(0);
      let cursor = 0;
      if (d.completion.actions) {
        for (let action of d.completion.actions) {
          // logger.debug('Action: ', action);
          let len = bel.length;
          let front = '';
          let back = '';
          if (action.delete) {
            if (action.delete.start_position > 0) {
              front = bel.slice(0, action.delete.start_position);
            }
            if (action.delete.end_position < len) {
              back = bel.slice(action.delete.end_position+1);
            }
            bel = front + back;
          }

          if (action.insert) {
            if (action.insert.position > 0) {
              front = bel.slice(0, action.insert.position);
            }
            if (action.insert.position < len) {
              back = bel.slice(action.insert.position);
            }
            bel = front + d.completion.value + back;
          }

          if (action.move_cursor) {
            cursor = action.move_cursor.position;
          }

          // logger.debug('F: ', front, ' B: ', back, ' BEL: ', bel, ' T: ', d.completion.type, ' Label: ', d.completion.label, ' Val: ', d.completion.value);
        }

        let classType = '';
        if (d.completion.type === 'namespace_value') {classType = 'nsval';}
        else if (d.completion.type === 'namespace_identifier') {classType = 'nsid';}
        else if (d.completion.type === 'function') {classType = 'function';}

        if (d.completion.label !== d.completion.value) {
          results.push({term: bel, type: classType, label: d.completion.label, value: d.completion.value, cursor: cursor});
        }
        else {
          results.push({term: bel, type: classType, value: d.completion.value, cursor: cursor});
        }

      }
    }
    return results;
  }

  // Upload BEL Script to OpenBEL Evidence Store
  // Notes:
  //    https://github.com/github/fetch
  //    http://blog.gospodarets.com/fetch_in_action/
  //    http://www.petermorlion.com/file-upload-with-aurelia/

  uploadDataset(file) {
    let data = new FormData();
    data.append('file', file);

    return this.apiClient.fetch('/datasets', {
      method: 'post',
      body: data,
      headers: {
        Authorization: "Bearer " + this.getToken()
      }
    });
  }

  // TODO remove getEvidenceId in favor of getIdFromUrl
  //
  /**
   * Get Evidence ID from self link href in evidence object
   *
   * @param url
   * @returns evidenceID
   */
  getEvidenceId(url) {
    let matches = url.match(/\/(\w+?)$/);
    // logger.debug('Matches: ', matches[1]);
    return matches[1];
  }

    /**
   * Get ID from self link href
   *
   * @param url
   * @returns ID
   */
  getIdFromUrl(url) {
    // logger.debug('U: ', url);
    let matches = url.match(/\/([\-\w]*)$/);
    // logger.debug('Matches: ', matches);
    return matches[1];
  }

  getDatasets(){
    return this.apiClient.fetch('/datasets', {headers:{Authorization: "Bearer " + this.getToken() }})
      .then(response => response.json())
      .then(data => {return data.dataset_collection;})
      .catch(function(reason) {
        logger.error('GET Datasets Error: ', reason);
      });
  }

  deleteDataset(url) {
    let dId = this.getIdFromUrl(url);
    return this.apiClient.fetch(`/datasets/${dId}`, {method: 'DELETE', headers:{Authorization: "Bearer " + this.getToken() }})
      .then(response => {return response})
      .catch(function(reason) {
        logger.error('Delete Datasets Error: ', reason);
      });
  }

  authenticate = function() {
    let current = window.location.href;
    window.location.href = baseUrl + '/authenticate?state=' + current;
  };

  setToken(token) {
    localStorage.setItem('belmgr-token', token);
  };

  removeToken() {
    localStorage.removeItem('belmgr-token');
  };

  getToken() {
    return localStorage.getItem('belmgr-token');
  };

}
<commit_msg>reject promises that errored in response<commit_after>import {inject} from 'aurelia-framework';
import {HttpClient} from 'aurelia-fetch-client';
import 'fetch';
import {LogManager} from 'aurelia-framework';
import Config from '../AppConfig';

let logger = LogManager.getLogger('api');

//let baseUrl = 'http://next.belframework.org/api';
let baseUrl = Config.baseUrl;

let parse = message => JSON.parse(message.response);

// http://europepmc.org/RestfulWebService#search
// http://www.ebi.ac.uk/europepmc/webservices/rest/search/resulttype=core&format=json&query=src:med ext_id:1945500
// http://next.belframework.org/europepmc/webservices/rest/search/resulttype=core&format=json&query=src:med  // proxied
// to remove CORS issue
// http://next.belframework.org/europepmc/webservices/rest/search/resulttype=core&format=json&query=src:med
// ext_id:1945500 Using this technique to proxy http://oskarhane.com/avoid-cors-with-nginx-proxy_pass

// let pubmedBaseUrl = 'http://next.belframework.org/europepmc/webservices/rest/search';
// let pubmedBaseUrl = 'http://www.ebi.ac.uk/europepmc/webservices/rest/search';
let pubmedBaseUrl = Config.pubmedBaseUrl;

export class Api {

  constructor() {
    // Simple API Client - not preconfigured with BaseUrl
    this.client = new HttpClient();
    this.client.configure(config => {
      config
        .withDefaults({
          headers: {
            'Accept': 'application/json',
            'X-Requested-With': 'Fetch'
          }
        })
        .rejectErrorResponses()
        .withInterceptor({
          request(req) {
              logger.debug(`Requesting ${req.method} ${req.url}`);
              return req; // you can return a modified Request, or you can short-circuit the request by returning a
              // Response
            },
            response(resp) {
              logger.debug(`Received ${resp.status} ${resp.url}`);
              return resp; // you can return a modified Response
            }
        });
    });

    // OpenBEL API Client - preconfigured with BaseUrl
    this.apiClient = new HttpClient();
    // this.apiClient.baseUrl = baseUrl;
    this.apiClient.configure(config => {
      config
        .withBaseUrl(baseUrl)
        .withDefaults({
          credentials: 'same-origin',
          headers: {
            'Accept': 'application/json',
            'X-Requested-With': 'Fetch'
          }
        })
        .rejectErrorResponses()
        .withInterceptor({
          request(req) {
              logger.debug(`Requesting ${req.method} ${req.url}`);
              return req; // you can return a modified Request, or you can short-circuit the request by returning a
              // Response
            },
            response(resp) {
              logger.debug(`Received ${resp.status} ${resp.url}`);
              return resp; // you can return a modified Response
            },
            responseError(resp) {
              if (resp.status === 401) {
                logger.info('Backend returned HTTP 401, redirecting to home.');
                window.location.href = window.location.origin;
              }
              logger.debug(`Received ${resp.status} ${resp.url}`);
              let rejection = Promise.reject(resp);
              return rejection;
            }
        });
    });

    // Pubmed API Client - preconfigured with BaseUrl
    this.pubmedClient = new HttpClient();
    this.pubmedClient.configure(config => {
      config
        .withBaseUrl(pubmedBaseUrl)
        .withDefaults({
          headers: {
            'Accept': 'application/json'
          }
        })
        .rejectErrorResponses()
        .withInterceptor({
          request(req) {
              logger.debug(`Requesting ${req.method} ${req.url}`);
              return req; // you can return a modified Request, or you can short-circuit the request by returning a
              // Response
            },
            response(resp) {
              logger.debug(`Received ${resp.status} ${resp.url}`);
              return resp; // you can return a modified Response
            }
        });
    });
  }

  /**
   *
Process facets from search

   *
This removes the facets that won't be used in the search results page
   *    and reorganizes it for presentation in the search results page.
   *

   *
   * @param {Object} facets - facets returned by BEL API from search
   * @returns {Object} facets - facets after processing to be used in web page
   * */
  processFacets(facets) {
    let newFacets = {};

    for (let facet of facets) {
      // logger.debug("Facet: ", facet);
      if (facet.category === 'experiment_context' || facet.name === 'Status' || facet.category === 'citation') {
        // logger.debug("Status Facet: ", facet);
        let facetName = facet.name;

        if (facet.category === 'citation') {
          facetName = 'Reference ID';
        }

        newFacets[facetName] = [];
        for (let value of facet.values) {
          let name = value.value;
          newFacets[facetName].push({
            'name': name,
            'count': value.count,
            'filter': value.filter
          });
        }
      }
    }

    return newFacets;
  }

  /**
   * Search the BELMgr database and return website ready results
   *
   * @param {String} filter - search string
   * @param {Integer} start - result page starting point (default = 0)
   * @param {Integer} size - number of results returned (default = 10)
   * @param {Integer} faceted - facet results if equals 1 (default = 1)
   * @return {Promise} data - processed search results ready to display on the search results web page
   * */
  search(start = 0, size = 10, faceted = 'yes', filters) {
    let max_values_per_facet = 10;
    let getstring = `/evidence?start=${start}&size=${size}&faceted=${faceted}&max_values_per_facet=${max_values_per_facet}`;
    if (filters) {
      for (let filter of filters) {
        getstring += `&filter=${filter}`;
      }
    }
    // logger.debug('Filters2: ', filters);
    logger.debug('Getstring: ', getstring);

    return this.apiClient.fetch(getstring, {
      headers: {
        Authorization: "Bearer " + this.getToken()
      }
    })
      .then(response => response.json())
      .then(data => {
        let new_data = {};
        new_data.evidences = data.evidence_collection;
        new_data.facets = this.processFacets(data.facets);
        new_data.metadata = data.metadata;
        logger.debug('Search results: ', new_data);
        return new_data;
      })
      .catch(function(reason) {
        if (reason.status === 404) {
          return {
            "evidences": null,
            "facets": {}
          };
        } else {
          logger.error("Search API Error: ", reason);
        }
      });
  }

  /**
   * Get BEL components (subject, relation, object) from a BEL statement
   * @param belStatement
   * @returns {"subject": "", "relationship": "", "object": ""}
   */
  getBelComponents(belStatement) {
    if (belStatement) {
      return this.apiClient.fetch(`/expressions/${belStatement}/components?flatten=1`)
        .then(response => response.json())
        .then(data => {
          // logger.debug('BEL Evidence: ', data);
          return data.expression_components;
        })
        .catch(function(reason) {
          logger.error(`GET BEL Components Error: ${reason}`)
        });
    }
    let comp = {};
    return comp;
  }

  getBelEvidence(id) {
    return this.apiClient.fetch(`/evidence/${id}`, {headers:{Authorization: "Bearer " + this.getToken() }})
      .then(response => response.json())
      .then(data => {
        let evidence = data.evidence;
        return evidence;
      })
      .catch(function(reason) {
        logger.error(`GET BEL Evidence Error: ${reason}`)
      });
  }

  /**
   * Load BEL Evidence (POST or PUT)
   *
   * @param id
   * @param evidence
   */
  loadBelEvidence(evidence, id) {
    // Update Evidence given an Id
    if (id) {
      return this.apiClient.fetch(`/evidence/${id}`, {
          method: 'put',
          headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json; profile=http://next.belframework.org/schema/evidence.schema.json',
            'Authorization': "Bearer " + this.getToken()
          },
          body: JSON.stringify(evidence)
        })
        .catch(response => {
          logger.error('PUT Evidence error ', response);
        });
    } else {
      // Create new Evidence
      return this.apiClient.fetch(`/evidence`, {
          method: 'post',
          headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json; profile=http://next.belframework.org/schema/evidence.schema.json',
            'Authorization': "Bearer " + this.getToken()
          },
          body: JSON.stringify(evidence)
        })
        .catch(response => {
          logger.error('POST Evidence error ', response);
        });
    }
  }

  deleteBelEvidence(evidenceId) {
    return this.apiClient.fetch(`/evidence/${evidenceId}`, {
      method: 'delete',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json; profile=http://next.belframework.org/schema/evidence.schema.json',
        'Authorization': "Bearer " + this.getToken()
      }
    })
    .catch(function(reason) {
      logger.error(`DELETE BEL Evidence Error: `, reason);
    });
  }

  /**
   * Get Pubmed document information - e.g. title, abstract, etc.
   *
   * @param id
   * @returns {*}
   */
  getPubmed(id) {

    let getstring = `/resulttype=core&format=json&query=ext_id:${id} src:med`;

    return this.pubmedClient.fetch(getstring)
      .then(response => response.json())
      .then(data => {
        return data.resultList.result[0];
      })
      .catch(response => {
        logger.error(`GET Pubmed Error: `, response);
      });
  }

  // Get list of all BEL Annotation Resources
  getBelAnnotationTypes() {
    return this.apiClient.fetch('/annotations')
      .then(response => response.json())
      .then(data => data.annotation_collection)
      .catch(function(reason) {
        logger.error(`GET BEL Annotation Types Error: ${reason}`)
      });
  }

  // Get BEL Annotation values for the edit form typeahead
  //   the query string should be wildcarded on the OpenBEL API side (e.g. 'ho sap' -> 'ho* sap*')
  getBELAnnotationValues(query, annotation_type) {
    let numResults = 10;
    let query_string = '';

    if (annotation_type) {
      query_string = `/annotations/${annotation_type}/values?filter={"category":"fts","name":"search","value":"${query}"}&start=0&size=${numResults}`;
    } else {
      query_string = `/annotations/values?filter={"category":"fts","name":"search","value":"${query}"}&start=0&size=${numResults}`;
    }

    return this.apiClient.fetch(query_string)
      .then(response => response.json())
      .then(data => data.annotation_value_collection)
      .catch(function(reason) {
        logger.error(`GET BEL Annotation Values Error: ${reason}`)
      });
  }

  // Get BEL term completions for the edit form typeahead
  getBelCompletions(query, cursor) {
    return this.apiClient.fetch(`/expressions/${query}/completions?caret_position=${cursor}`)
      .then(response => {
        return response.json();
      })
      .then(json => {
        let data = json.completion_collection;
        // logger.debug('Data: ', data);
        let completions = this.processBelCompletions(query, data);
        // logger.debug('Completions: ', completions);
        return completions;
      })
      .catch(reason => {
        logger.error('GET Bel Completions error: ', reason);
      });
  }

  processBelCompletions(query, data) {
    let results = [];
    // logger.debug('Data: ', data);
    for (let d of data) {
      // logger.debug('d: ', d);
      let bel = query.slice(0);
      let cursor = 0;
      if (d.completion.actions) {
        for (let action of d.completion.actions) {
          // logger.debug('Action: ', action);
          let len = bel.length;
          let front = '';
          let back = '';
          if (action.delete) {
            if (action.delete.start_position > 0) {
              front = bel.slice(0, action.delete.start_position);
            }
            if (action.delete.end_position < len) {
              back = bel.slice(action.delete.end_position+1);
            }
            bel = front + back;
          }

          if (action.insert) {
            if (action.insert.position > 0) {
              front = bel.slice(0, action.insert.position);
            }
            if (action.insert.position < len) {
              back = bel.slice(action.insert.position);
            }
            bel = front + d.completion.value + back;
          }

          if (action.move_cursor) {
            cursor = action.move_cursor.position;
          }

          // logger.debug('F: ', front, ' B: ', back, ' BEL: ', bel, ' T: ', d.completion.type, ' Label: ', d.completion.label, ' Val: ', d.completion.value);
        }

        let classType = '';
        if (d.completion.type === 'namespace_value') {classType = 'nsval';}
        else if (d.completion.type === 'namespace_identifier') {classType = 'nsid';}
        else if (d.completion.type === 'function') {classType = 'function';}

        if (d.completion.label !== d.completion.value) {
          results.push({term: bel, type: classType, label: d.completion.label, value: d.completion.value, cursor: cursor});
        }
        else {
          results.push({term: bel, type: classType, value: d.completion.value, cursor: cursor});
        }

      }
    }
    return results;
  }

  // Upload BEL Script to OpenBEL Evidence Store
  // Notes:
  //    https://github.com/github/fetch
  //    http://blog.gospodarets.com/fetch_in_action/
  //    http://www.petermorlion.com/file-upload-with-aurelia/

  uploadDataset(file) {
    let data = new FormData();
    data.append('file', file);

    return this.apiClient.fetch('/datasets', {
      method: 'post',
      body: data,
      headers: {
        Authorization: "Bearer " + this.getToken()
      }
    });
  }

  // TODO remove getEvidenceId in favor of getIdFromUrl
  //
  /**
   * Get Evidence ID from self link href in evidence object
   *
   * @param url
   * @returns evidenceID
   */
  getEvidenceId(url) {
    let matches = url.match(/\/(\w+?)$/);
    // logger.debug('Matches: ', matches[1]);
    return matches[1];
  }

    /**
   * Get ID from self link href
   *
   * @param url
   * @returns ID
   */
  getIdFromUrl(url) {
    // logger.debug('U: ', url);
    let matches = url.match(/\/([\-\w]*)$/);
    // logger.debug('Matches: ', matches);
    return matches[1];
  }

  getDatasets(){
    return this.apiClient.fetch('/datasets', {headers:{Authorization: "Bearer " + this.getToken() }})
      .then(response => response.json())
      .then(data => {return data.dataset_collection;})
      .catch(function(reason) {
        logger.error('GET Datasets Error: ', reason);
      });
  }

  deleteDataset(url) {
    let dId = this.getIdFromUrl(url);
    return this.apiClient.fetch(`/datasets/${dId}`, {method: 'DELETE', headers:{Authorization: "Bearer " + this.getToken() }})
      .then(response => {return response})
      .catch(function(reason) {
        logger.error('Delete Datasets Error: ', reason);
      });
  }

  authenticate = function() {
    let current = window.location.href;
    window.location.href = baseUrl + '/authenticate?state=' + current;
  };

  setToken(token) {
    localStorage.setItem('belmgr-token', token);
  };

  removeToken() {
    localStorage.removeItem('belmgr-token');
  };

  getToken() {
    return localStorage.getItem('belmgr-token');
  };

}
<|endoftext|>


<commit_before>/*jslint node: true */
'use strict';
var http = require('http');
var querystring = require('querystring');
var request = require('request');
var JSONStream = require('JSONStream');

var _ = require("lodash");

var ObjectWrapper = require('gremlin-script').ObjectWrapper;
var GremlinScript = require('gremlin-script').GremlinScript;

function buildRequestOptions(gremlin, settings) {
  var requestOptions = {
    json: true,
    uri: 'http://' + settings.host + ':' + settings.port + '/graphs/' + settings.graph + '/tp/gremlin',
    body: {
      script: gremlin.script.replace(/\$/g, "\\$"),
      params: gremlin.params,
      'rexster.showTypes': settings.showTypes,
      load: settings.load
    }
  };

  return requestOptions;
}

var RexsterClient = (function(){
  function RexsterClient(options) {
    var defaultSettings = {
      host: 'localhost',
      port: 8182,
      graph: 'tinkergraph',
      load: [],
      showTypes: false
    };

    this.settings = _.defaults(options || {}, defaultSettings);
    this.fetchHandler = this.settings.fetched || function(response, results) {
      return results;
    };
  }

  /**
   * Send a GremlinScript script to Rexster for execution via HTTP, and
   * retrieve the raw Rexster response.
   *
   * @param {GremlinScript} gremlin A Gremlin-Groovy script to execute
   * @param {Function} callback
   */
  RexsterClient.prototype.execute =
  RexsterClient.prototype.exec = function(gremlin, callback) {
    if (gremlin instanceof ObjectWrapper) {
      var statement = gremlin;
      gremlin = new GremlinScript();
      var appender = gremlin.getAppender();
      appender(statement);
    }

    var options = buildRequestOptions(gremlin, this.settings);

    request.post(options, function(err, response, body) {
      if (err) {
        return callback(new Error(err));
      }

      if (body.error || body.message || body.success === false) {
        return callback(new Error(body.error || body.message || body.results));
      }

      callback(null, body);
    });
  };

  /**
   * Send a Gremlin script to Rexster for execution via HTTP, and
   * retrieve the `results` Array of the Rexster response.
   *
   * @param {GremlinScript} gremlin
   * @param {Function} callback
   */
  RexsterClient.prototype.fetch = function(gremlin, callback) {
    var self = this;

    this.execute(gremlin, function(err, response) {
      if (err) {
        return callback(new Error(err));
      }

      callback(null, self.fetchHandler(response, response.results), response);
    });
  };

  /**
   * Send a Gremlin script to Rexster for execution via HTTP, and
   * retrieve the first element of the `results` Array of the Rexster response.
   *
   * @param {GremlinScript} gremlin
   * @param {Function} callback
   */
  RexsterClient.prototype.fetchOne = function(gremlin, callback) {
    this.fetch(gremlin, function(err, results, response) {
      callback(null, results[0], response);
    });
  };

  RexsterClient.prototype.stream = function(gremlin) {
    var options = buildRequestOptions(gremlin, this.settings);

    return request.post(options).pipe(JSONStream.parse('results.*'));
  };

  return RexsterClient;
}());

module.exports = RexsterClient;<commit_msg>Remove unnused http and querystring require<commit_after>/*jslint node: true */
'use strict';
var request = require('request');
var JSONStream = require('JSONStream');

var _ = require("lodash");

var ObjectWrapper = require('gremlin-script').ObjectWrapper;
var GremlinScript = require('gremlin-script').GremlinScript;

function buildRequestOptions(gremlin, settings) {
  var requestOptions = {
    json: true,
    uri: 'http://' + settings.host + ':' + settings.port + '/graphs/' + settings.graph + '/tp/gremlin',
    body: {
      script: gremlin.script.replace(/\$/g, "\\$"),
      params: gremlin.params,
      'rexster.showTypes': settings.showTypes,
      load: settings.load
    }
  };

  return requestOptions;
}

var RexsterClient = (function(){
  function RexsterClient(options) {
    var defaultSettings = {
      host: 'localhost',
      port: 8182,
      graph: 'tinkergraph',
      load: [],
      showTypes: false
    };

    this.settings = _.defaults(options || {}, defaultSettings);
    this.fetchHandler = this.settings.fetched || function(response, results) {
      return results;
    };
  }

  /**
   * Send a GremlinScript script to Rexster for execution via HTTP, and
   * retrieve the raw Rexster response.
   *
   * @param {GremlinScript} gremlin A Gremlin-Groovy script to execute
   * @param {Function} callback
   */
  RexsterClient.prototype.execute =
  RexsterClient.prototype.exec = function(gremlin, callback) {
    if (gremlin instanceof ObjectWrapper) {
      var statement = gremlin;
      gremlin = new GremlinScript();
      var appender = gremlin.getAppender();
      appender(statement);
    }

    var options = buildRequestOptions(gremlin, this.settings);

    request.post(options, function(err, response, body) {
      if (err) {
        return callback(new Error(err));
      }

      if (body.error || body.message || body.success === false) {
        return callback(new Error(body.error || body.message || body.results));
      }

      callback(null, body);
    });
  };

  /**
   * Send a Gremlin script to Rexster for execution via HTTP, and
   * retrieve the `results` Array of the Rexster response.
   *
   * @param {GremlinScript} gremlin
   * @param {Function} callback
   */
  RexsterClient.prototype.fetch = function(gremlin, callback) {
    var self = this;

    this.execute(gremlin, function(err, response) {
      if (err) {
        return callback(new Error(err));
      }

      callback(null, self.fetchHandler(response, response.results), response);
    });
  };

  /**
   * Send a Gremlin script to Rexster for execution via HTTP, and
   * retrieve the first element of the `results` Array of the Rexster response.
   *
   * @param {GremlinScript} gremlin
   * @param {Function} callback
   */
  RexsterClient.prototype.fetchOne = function(gremlin, callback) {
    this.fetch(gremlin, function(err, results, response) {
      callback(null, results[0], response);
    });
  };

  RexsterClient.prototype.stream = function(gremlin) {
    var options = buildRequestOptions(gremlin, this.settings);

    return request.post(options).pipe(JSONStream.parse('results.*'));
  };

  return RexsterClient;
}());

module.exports = RexsterClient;<|endoftext|>


<commit_before>'use strict';

angular.module('angular-svg-round-progress')
    .directive('roundProgress', ['$window', 'roundProgressService', 'roundProgressConfig', function($window, service, roundProgressConfig){

            var base = {
                restrict: "EA",
                replace: true,
                transclude: true,
                scope:{
                    current:        "=",
                    max:            "=",
                    semi:           "=",
                    rounded:        "=",
                    clockwise:      "=",
                    responsive:     "=",
                    radius:         "@",
                    color:          "@",
                    bgcolor:        "@",
                    stroke:         "@",
                    duration:       "@",
                    animation:      "@"
                }
            };

            if(!service.isSupported){
                return angular.extend(base, {
                    // placeholder element to keep the structure
                    template: '

'
                });
            }

            return angular.extend(base, {
                link: function(scope, element){
                    var svg         = element.find('svg').eq(0);
                    var ring        = svg.find('path').eq(0);
                    var background  = svg.find('circle').eq(0);
                    var options     = angular.copy(roundProgressConfig);
                    var lastAnimationId;

                    var renderCircle = function(){
                        var isSemicircle     = options.semi;
                        var responsive       = options.responsive;
                        var radius           = parseInt(options.radius) || 0;
                        var stroke           = parseInt(options.stroke);
                        var diameter         = radius*2;
                        var backgroundSize   = radius - (stroke/2);

                        svg.css({
                            "top":          0,
                            "left":         0,
                            "position":     responsive ? "absolute" : "static",
                            "width":        responsive ? "100%" : (diameter + "px"),
                            "height":       responsive ? "100%" : (isSemicircle ? radius : diameter) + "px",
                            "overflow":     "hidden" // on some browsers the background overflows, if in semicircle mode
                        });

                        // note that we can't use .attr, because if jQuery is loaded, it lowercases all attributes
                        // and viewBox is case-sensitive
                        svg[0].setAttribute('viewBox', '0 0 ' + diameter + ' ' + (isSemicircle ? radius : diameter));

                        element.css({
                            "width":            responsive ? "100%" : "auto",
                            "position":         "relative",
                            "padding-bottom":   responsive ? (isSemicircle ? "50%" : "100%") : 0
                        });

                        ring.css({
                            "stroke":           options.color,
                            "stroke-width":     stroke,
                            "stroke-linecap":   options.rounded ? "round": "butt"
                        });

                        if(isSemicircle){
                            ring.attr("transform", options.clockwise ? "translate("+ 0 +","+ diameter +") rotate(-90)" : "translate("+ diameter +", "+ diameter +") rotate(90) scale(-1, 1)");
                        }else{
                            ring.attr("transform", options.clockwise ? "" : "scale(-1, 1) translate("+ (-diameter) +" 0)");
                        }

                        background.attr({
                            "cx":           radius,
                            "cy":           radius,
                            "r":            backgroundSize >= 0 ? backgroundSize : 0
                        }).css({
                            "stroke":       options.bgcolor,
                            "stroke-width": stroke
                        });
                    };

                    var renderState = function(newValue, oldValue){
                        var max                 = service.toNumber(options.max || 0);
                        var end                 = newValue > max ? max : (newValue < 0 || !newValue ? 0 : newValue);
                        var start               = (oldValue === end || oldValue < 0) ? 0 : (oldValue || 0); // fixes the initial animation
                        var changeInValue       = end - start;

                        var easingAnimation     = service.animations[options.animation];
                        var startTime           = new Date();
                        var duration            = parseInt(options.duration);
                        var preventAnimation    = (newValue > max && oldValue > max) || (newValue < 0 && oldValue < 0) || duration < 25;

                        var radius              = options.radius;
                        var circleSize          = radius - (options.stroke/2);
                        var elementSize         = radius*2;
                        var isSemicircle        = options.semi;

                        // stops some expensive animating if the value is above the max or under 0
                        if(preventAnimation){
                            service.updateState(end, max, circleSize, ring, elementSize, isSemicircle);
                        }else{
                            $window.cancelAnimationFrame(lastAnimationId);

                            (function animation(){
                                var currentTime = new Date() - startTime;

                                service.updateState(
                                    easingAnimation(currentTime, start, changeInValue, duration),
                                    max,
                                    circleSize,
                                    ring,
                                    elementSize,
                                    isSemicircle);

                                if(currentTime < duration){
                                    lastAnimationId = $window.requestAnimationFrame(animation);
                                }
                            })();
                        }
                    };

                    scope.$watchCollection('[' + Object.keys(base.scope).join(',') + ']', function(newValue, oldValue, scope){
                        // pretty much the same as angular.extend,
                        // but this skips undefined values and internal angular keys
                        angular.forEach(scope, function(value, key){
                            // note the scope !== value is because `this` is part of the scope
                            if(key.indexOf('$') && scope !== value && angular.isDefined(value)){
                                options[key] = value;
                            }
                        });

                        renderCircle();
                        renderState(service.toNumber(newValue[0]), service.toNumber(oldValue[0]));
                    });
                },
                template:[
                    '
',
                        '<svg class="round-progress" xmlns="http://www.w3.org/2000/svg">',
                            '<circle fill="none"/>',
                            '<path fill="none"/>',
                            '<g ng-transclude></g>',
                        '</svg>',
                    '
'
                ].join('\n')
            });

        }]);
<commit_msg>avoid errors if the duration is undefined or null<commit_after>'use strict';

angular.module('angular-svg-round-progress')
    .directive('roundProgress', ['$window', 'roundProgressService', 'roundProgressConfig', function($window, service, roundProgressConfig){

            var base = {
                restrict: "EA",
                replace: true,
                transclude: true,
                scope:{
                    current:        "=",
                    max:            "=",
                    semi:           "=",
                    rounded:        "=",
                    clockwise:      "=",
                    responsive:     "=",
                    radius:         "@",
                    color:          "@",
                    bgcolor:        "@",
                    stroke:         "@",
                    duration:       "@",
                    animation:      "@"
                }
            };

            if(!service.isSupported){
                return angular.extend(base, {
                    // placeholder element to keep the structure
                    template: '

'
                });
            }

            return angular.extend(base, {
                link: function(scope, element){
                    var svg         = element.find('svg').eq(0);
                    var ring        = svg.find('path').eq(0);
                    var background  = svg.find('circle').eq(0);
                    var options     = angular.copy(roundProgressConfig);
                    var lastAnimationId;

                    var renderCircle = function(){
                        var isSemicircle     = options.semi;
                        var responsive       = options.responsive;
                        var radius           = parseInt(options.radius) || 0;
                        var stroke           = parseInt(options.stroke);
                        var diameter         = radius*2;
                        var backgroundSize   = radius - (stroke/2);

                        svg.css({
                            "top":          0,
                            "left":         0,
                            "position":     responsive ? "absolute" : "static",
                            "width":        responsive ? "100%" : (diameter + "px"),
                            "height":       responsive ? "100%" : (isSemicircle ? radius : diameter) + "px",
                            "overflow":     "hidden" // on some browsers the background overflows, if in semicircle mode
                        });

                        // note that we can't use .attr, because if jQuery is loaded, it lowercases all attributes
                        // and viewBox is case-sensitive
                        svg[0].setAttribute('viewBox', '0 0 ' + diameter + ' ' + (isSemicircle ? radius : diameter));

                        element.css({
                            "width":            responsive ? "100%" : "auto",
                            "position":         "relative",
                            "padding-bottom":   responsive ? (isSemicircle ? "50%" : "100%") : 0
                        });

                        ring.css({
                            "stroke":           options.color,
                            "stroke-width":     stroke,
                            "stroke-linecap":   options.rounded ? "round": "butt"
                        });

                        if(isSemicircle){
                            ring.attr("transform", options.clockwise ? "translate("+ 0 +","+ diameter +") rotate(-90)" : "translate("+ diameter +", "+ diameter +") rotate(90) scale(-1, 1)");
                        }else{
                            ring.attr("transform", options.clockwise ? "" : "scale(-1, 1) translate("+ (-diameter) +" 0)");
                        }

                        background.attr({
                            "cx":           radius,
                            "cy":           radius,
                            "r":            backgroundSize >= 0 ? backgroundSize : 0
                        }).css({
                            "stroke":       options.bgcolor,
                            "stroke-width": stroke
                        });
                    };

                    var renderState = function(newValue, oldValue){
                        var max                 = service.toNumber(options.max || 0);
                        var end                 = newValue > max ? max : (newValue < 0 || !newValue ? 0 : newValue);
                        var start               = (oldValue === end || oldValue < 0) ? 0 : (oldValue || 0); // fixes the initial animation
                        var changeInValue       = end - start;

                        var easingAnimation     = service.animations[options.animation];
                        var startTime           = new Date();
                        var duration            = parseInt(options.duration) || 0;
                        var preventAnimation    = (newValue > max && oldValue > max) || (newValue < 0 && oldValue < 0) || duration < 25;

                        var radius              = options.radius;
                        var circleSize          = radius - (options.stroke/2);
                        var elementSize         = radius*2;
                        var isSemicircle        = options.semi;

                        // stops some expensive animating if the value is above the max or under 0
                        if(preventAnimation){
                            service.updateState(end, max, circleSize, ring, elementSize, isSemicircle);
                        }else{
                            $window.cancelAnimationFrame(lastAnimationId);

                            (function animation(){
                                var currentTime = new Date() - startTime;

                                service.updateState(
                                    easingAnimation(currentTime, start, changeInValue, duration),
                                    max,
                                    circleSize,
                                    ring,
                                    elementSize,
                                    isSemicircle);

                                if(currentTime < duration){
                                    lastAnimationId = $window.requestAnimationFrame(animation);
                                }
                            })();
                        }
                    };

                    scope.$watchCollection('[' + Object.keys(base.scope).join(',') + ']', function(newValue, oldValue, scope){
                        // pretty much the same as angular.extend,
                        // but this skips undefined values and internal angular keys
                        angular.forEach(scope, function(value, key){
                            // note the scope !== value is because `this` is part of the scope
                            if(key.indexOf('$') && scope !== value && angular.isDefined(value)){
                                options[key] = value;
                            }
                        });

                        renderCircle();
                        renderState(service.toNumber(newValue[0]), service.toNumber(oldValue[0]));
                    });
                },
                template:[
                    '
',
                        '<svg class="round-progress" xmlns="http://www.w3.org/2000/svg">',
                            '<circle fill="none"/>',
                            '<path fill="none"/>',
                            '<g ng-transclude></g>',
                        '</svg>',
                    '
'
                ].join('\n')
            });

        }]);
<|endoftext|>


<commit_before>'use strict';

const bodyParser = require('body-parser');
const express = require('express');
const humps = require('humps');

const Coffee = require('../models/Coffee.js');

const router = express.Router();

const coffeeList = new Coffee();

/**
 * @api {get} /coffee List all coffee types
 * @apiVersion 1.0.0
 * @apiGroup Coffee
 * @apiSuccess {Object[]} coffee Coffee list
 * @apiSuccess {Number} coffee.id Coffee id
 * @apiSuccess {Number} coffee.producer_id Coffee profucer id
 * @apiSuccess {String} coffee.name Coffee name
 * @apiSuccess {String} coffee.description Coffee description
 * @apiSuccess {String} coffee.flavor_profile Coffee flavor profile
 * @apiSuccess {String} coffee.varieties Coffee varieties
 * @apiSuccess {Date} coffee.created_at Created date
 * @apiSuccess {Date} coffee.updated_at Updated date
 * @apiSuccessExample {json} Success
 *  HTTP/1.1 200 OK
 *    [{
        "createdAt": "2017-06-23T14:56:16.000Z",
        "description": "Lorem ipsum",
        "flavorProfile": "Fruity, radiant, creamy",
        "id": 1,
        "name": "Three Africas",
        "producerId": 1,
        "updatedAt": "2017-06-23T14:56:16.000Z",
        "varieties": "Heirloom"
      }]
 * @apiErrorExample {json} List error
 *    HTTP/1.1 500 Internal Server Error
 */
router.get('/coffee', (req, res) => {
  coffeeList.getCoffee()
    .then(coffeeList => {
      res.send(coffeeList);
    })
    .catch(err => {
      res.status(500).send(err);
    });
});

/**
 * @api {get} /coffee/:id Get coffee by id
 * @apiVersion 1.0.0
 * @apiGroup Coffee
 * @apiSuccess {Object[]} coffee Coffee object
 * @apiSuccess {Number} coffee.id Coffee id
 * @apiSuccess {Number} coffee.producer_id Coffee profucer id
 * @apiSuccess {String} coffee.name Coffee name
 * @apiSuccess {String} coffee.description Coffee description
 * @apiSuccess {String} coffee.flavor_profile Coffee flavor profile
 * @apiSuccess {String} coffee.varieties Coffee varieties
 * @apiSuccess {Date} coffee.created_at Created date
 * @apiSuccess {Date} coffee.updated_at Updated date
 * @apiSuccessExample {json} Success
 *  HTTP/1.1 200 OK
 *    {
        "createdAt": "2017-06-23T14:56:16.000Z",
        "description": "Lorem ipsum",
        "flavorProfile": "Fruity, radiant, creamy",
        "id": 1,
        "name": "Three Africas",
        "producerId": 1,
        "updatedAt": "2017-06-23T14:56:16.000Z",
        "varieties": "Heirloom"
      }
* @apiErrorExample {json} Coffee object not found
*    HTTP/1.1 404 Not Found
* @apiErrorExample {json} Find error
*    HTTP/1.1 500 Internal Server Error
*/
router.get('/coffee/:id', (req, res) => {
  const id = req.params.id;
  coffeeList.getCoffeeById(id)
    .then(coffee => {
      if (coffeeList.length === 0) {
         res.sendStatus(404);
         return;
      }
      res.send(coffee);
    })
    .catch(err => {
      res.status(500).send(err);
    });
});

/**
 * @api {post} /coffee Add a new coffee
 * @apiGroup Coffee
 * @apiParam {String} producer_id Coffee producer_id
 * @apiParam {String} name Coffee name
 * @apiParam {String} flavor_profile Coffee flavor_profile
 * @apiParam {String} varieties Coffee varieties
 * @apiParam {String} description Coffee description
 * @apiParamExample {json} Input
 *    {
 *      "producer_id": "2",
        "name": "Ethiopia Bulga",
        "flavor_profile": "Cotton Candy, Strawberry, Sugar, Tangerine",
        "varieties": "Heirloom",
        "description": "delicious"
 *    }
 * @apiSuccess {Number} id Coffee id
 * @apiParam {String} producer_id Coffee producer_id
 * @apiParam {String} name Coffee name
 * @apiParam {String} flavor_profile Coffee flavor_profile
 * @apiParam {String} varieties Coffee varieties
 * @apiParam {String} description Coffee description
 * @apiSuccessExample {json} Success
 *    HTTP/1.1 200 OK
 *    {
 *      "id": 2,
 *      "producer_id": "2",
        "name": "Ethiopia Bulga",
        "flavor_profile": "Cotton Candy, Strawberry, Sugar, Tangerine",
        "varieties": "Heirloom",
        "description": "delicious"
 *    }
 * @apiErrorExample {json} Add error
 *    HTTP/1.1 500 Internal Server Error
 */
router.post('/coffee', (req, res) => {
  const coffee = req.body;

  if (!coffee.name) {
    return res.status(400)
      .set('Content-Type', 'text/plain')
      .send('Coffee name required');
  }

  coffeeList.addCoffee(coffee)
  .then(coffee => {
    res.setHeader('Content-Type', 'application/json')
    return res.send(coffee[0]);
  })
    .catch(err => {
      res.sendStatus(500);
    });
});

/**
 * @api {post} /coffee/:id Update a coffee
 * @apiGroup Coffee
 * @apiParam {String} producer_id Coffee producer_id
 * @apiParam {String} name Coffee name
 * @apiParam {String} flavor_profile Coffee flavor_profile
 * @apiParam {String} varieties Coffee varieties
 * @apiParam {String} description Coffee description
 * @apiParamExample {json} Input
 *    {
 *      "producer_id": "2",
        "name": "Ethiopia Bulga",
        "flavor_profile": "Cotton Candy, Strawberry, Sugar, Tangerine",
        "varieties": "Heirloom",
        "description": "lorem ipsum"
 *    }
 * @apiSuccess {Number} id Coffee id
 * @apiParam {String} producer_id Coffee producer_id
 * @apiParam {String} name Coffee name
 * @apiParam {String} flavor_profile Coffee flavor_profile
 * @apiParam {String} varieties Coffee varieties
 * @apiParam {String} description Coffee description
 * @apiSuccessExample {json} Success
 *    HTTP/1.1 200 OK
 *    {
 *      "id": 2,
 *      "producer_id": "2",
        "name": "Ethiopia Bulga",
        "flavor_profile": "Cotton Candy, Strawberry, Sugar, Tangerine",
        "varieties": "Heirloom",
        "description": "delicious"
 *    }
 * @apiErrorExample {json} Add error
 *    HTTP/1.1 500 Internal Server Error
 */
router.post('/coffee/:id', (req, res) => {
  const coffee = req.body;
  coffee.id = req.params.id;

  if (!coffee.name) {
    return res.status(400)
      .set('Content-Type', 'text/plain')
      .send('Coffee name required');
  }

  coffeeList.updateCoffee(coffee)
  .then(coffee => {
    res.setHeader('Content-Type', 'application/json')
    return res.send(coffee[0]);
  })
    .catch(err => {
      res.sendStatus(500);
    });
});

router.delete('/coffee/:id', (req, res) => {
  const id = req.params.id;

  if (isNaN(id)) {
    return res.sendStatus(404);
  }

  coffeeList.deleteCoffee(id)
  .then(deletedCoffee => {
    if (!deletedCoffee[0]) {
        res.sendStatus(404);
      }
    res.send(deletedCoffee)
  })
  .catch(err => {
      res.status(500).send(err);
  });
})

module.exports = router;
<commit_msg>Document DELETE coffee route<commit_after>'use strict';

const bodyParser = require('body-parser');
const express = require('express');
const humps = require('humps');

const Coffee = require('../models/Coffee.js');

const router = express.Router();

const coffeeList = new Coffee();

/**
 * @api {get} /coffee List all coffee types
 * @apiVersion 1.0.0
 * @apiGroup Coffee
 * @apiSuccess {Object[]} coffee Coffee list
 * @apiSuccess {Number} coffee.id Coffee id
 * @apiSuccess {Number} coffee.producer_id Coffee profucer id
 * @apiSuccess {String} coffee.name Coffee name
 * @apiSuccess {String} coffee.description Coffee description
 * @apiSuccess {String} coffee.flavor_profile Coffee flavor profile
 * @apiSuccess {String} coffee.varieties Coffee varieties
 * @apiSuccess {Date} coffee.created_at Created date
 * @apiSuccess {Date} coffee.updated_at Updated date
 * @apiSuccessExample {json} Success
 *  HTTP/1.1 200 OK
 *    [{
        "createdAt": "2017-06-23T14:56:16.000Z",
        "description": "Lorem ipsum",
        "flavorProfile": "Fruity, radiant, creamy",
        "id": 1,
        "name": "Three Africas",
        "producerId": 1,
        "updatedAt": "2017-06-23T14:56:16.000Z",
        "varieties": "Heirloom"
      }]
 * @apiErrorExample {json} List error
 *    HTTP/1.1 500 Internal Server Error
 */
router.get('/coffee', (req, res) => {
  coffeeList.getCoffee()
    .then(coffeeList => {
      res.send(coffeeList);
    })
    .catch(err => {
      res.status(500).send(err);
    });
});

/**
 * @api {get} /coffee/:id Get coffee by id
 * @apiVersion 1.0.0
 * @apiGroup Coffee
 * @apiSuccess {Object[]} coffee Coffee object
 * @apiSuccess {Number} coffee.id Coffee id
 * @apiSuccess {Number} coffee.producer_id Coffee profucer id
 * @apiSuccess {String} coffee.name Coffee name
 * @apiSuccess {String} coffee.description Coffee description
 * @apiSuccess {String} coffee.flavor_profile Coffee flavor profile
 * @apiSuccess {String} coffee.varieties Coffee varieties
 * @apiSuccess {Date} coffee.created_at Created date
 * @apiSuccess {Date} coffee.updated_at Updated date
 * @apiSuccessExample {json} Success
 *  HTTP/1.1 200 OK
 *    {
        "createdAt": "2017-06-23T14:56:16.000Z",
        "description": "Lorem ipsum",
        "flavorProfile": "Fruity, radiant, creamy",
        "id": 1,
        "name": "Three Africas",
        "producerId": 1,
        "updatedAt": "2017-06-23T14:56:16.000Z",
        "varieties": "Heirloom"
      }
* @apiErrorExample {json} Coffee object not found
*    HTTP/1.1 404 Not Found
* @apiErrorExample {json} Find error
*    HTTP/1.1 500 Internal Server Error
*/
router.get('/coffee/:id', (req, res) => {
  const id = req.params.id;
  coffeeList.getCoffeeById(id)
    .then(coffee => {
      if (coffeeList.length === 0) {
         res.sendStatus(404);
         return;
      }
      res.send(coffee);
    })
    .catch(err => {
      res.status(500).send(err);
    });
});

/**
 * @api {post} /coffee Add a new coffee
 * @apiGroup Coffee
 * @apiParam {String} producer_id Coffee producer_id
 * @apiParam {String} name Coffee name
 * @apiParam {String} flavor_profile Coffee flavor_profile
 * @apiParam {String} varieties Coffee varieties
 * @apiParam {String} description Coffee description
 * @apiParamExample {json} Input
 *    {
 *      "producer_id": "2",
        "name": "Ethiopia Bulga",
        "flavor_profile": "Cotton Candy, Strawberry, Sugar, Tangerine",
        "varieties": "Heirloom",
        "description": "delicious"
 *    }
 * @apiSuccess {Number} id Coffee id
 * @apiParam {String} producer_id Coffee producer_id
 * @apiParam {String} name Coffee name
 * @apiParam {String} flavor_profile Coffee flavor_profile
 * @apiParam {String} varieties Coffee varieties
 * @apiParam {String} description Coffee description
 * @apiSuccessExample {json} Success
 *    HTTP/1.1 200 OK
 *    {
 *      "id": 2,
 *      "producer_id": "2",
        "name": "Ethiopia Bulga",
        "flavor_profile": "Cotton Candy, Strawberry, Sugar, Tangerine",
        "varieties": "Heirloom",
        "description": "delicious"
 *    }
 * @apiErrorExample {json} Add error
 *    HTTP/1.1 500 Internal Server Error
 */
router.post('/coffee', (req, res) => {
  const coffee = req.body;

  if (!coffee.name) {
    return res.status(400)
      .set('Content-Type', 'text/plain')
      .send('Coffee name required');
  }

  coffeeList.addCoffee(coffee)
  .then(coffee => {
    res.setHeader('Content-Type', 'application/json')
    return res.send(coffee[0]);
  })
    .catch(err => {
      res.sendStatus(500);
    });
});

/**
 * @api {post} /coffee/:id Update a coffee
 * @apiGroup Coffee
 * @apiParam {String} producer_id Coffee producer_id
 * @apiParam {String} name Coffee name
 * @apiParam {String} flavor_profile Coffee flavor_profile
 * @apiParam {String} varieties Coffee varieties
 * @apiParam {String} description Coffee description
 * @apiParamExample {json} Input
 *    {
 *      "producer_id": "2",
        "name": "Ethiopia Bulga",
        "flavor_profile": "Cotton Candy, Strawberry, Sugar, Tangerine",
        "varieties": "Heirloom",
        "description": "lorem ipsum"
 *    }
 * @apiSuccess {Number} id Coffee id
 * @apiParam {String} producer_id Coffee producer_id
 * @apiParam {String} name Coffee name
 * @apiParam {String} flavor_profile Coffee flavor_profile
 * @apiParam {String} varieties Coffee varieties
 * @apiParam {String} description Coffee description
 * @apiSuccessExample {json} Success
 *    HTTP/1.1 200 OK
 *    {
 *      "id": 2,
 *      "producer_id": "2",
        "name": "Ethiopia Bulga",
        "flavor_profile": "Cotton Candy, Strawberry, Sugar, Tangerine",
        "varieties": "Heirloom",
        "description": "delicious"
 *    }
 * @apiErrorExample {json} Add error
 *    HTTP/1.1 500 Internal Server Error
 */
router.post('/coffee/:id', (req, res) => {
  const coffee = req.body;
  coffee.id = req.params.id;

  if (!coffee.name) {
    return res.status(400)
      .set('Content-Type', 'text/plain')
      .send('Coffee name required');
  }

  coffeeList.updateCoffee(coffee)
  .then(coffee => {
    res.setHeader('Content-Type', 'application/json')
    return res.send(coffee[0]);
  })
    .catch(err => {
      res.sendStatus(500);
    });
});

/**
 * @api {delete} /coffee/:id Delete a coffee
 * @apiGroup Coffee
 * @apiParam {id} id Coffee id
 * @apiSuccessExample {json} Success
 *    HTTP/1.1 200 OK
 *    {
 *      "id": 2,
 *      "producer_id": "2",
        "name": "Ethiopia Bulga",
        "flavor_profile": "Cotton Candy, Strawberry, Sugar, Tangerine",
        "varieties": "Heirloom",
        "description": "Lorem ipsum"
 *    }
 * @apiErrorExample {json} Delete error
 *    HTTP/1.1 500 Internal Server Error
 */
router.delete('/coffee/:id', (req, res) => {
  const id = req.params.id;

  if (isNaN(id)) {
    return res.sendStatus(404);
  }

  coffeeList.deleteCoffee(id)
  .then(deletedCoffee => {
    if (!deletedCoffee[0]) {
        res.sendStatus(404);
      }
    res.send(deletedCoffee)
  })
  .catch(err => {
      res.status(500).send(err);
  });
})

module.exports = router;
<|endoftext|>


<commit_before>(function() {
	var socket = initSocket(document.currentScript.getAttribute('host'));
	var context = initCanvas(document.currentScript.getAttribute('canvas'));

	var xPath, yPath = null;
	var scale = 1;

	var drawConfig = {
		pathColour : "#000000",
		robotColour : "#0000FF",
		sensorColour : "#FF0000",
		sensorConeTheta : Math.PI / 32
	}

	function initSocket(host) {
		var socket = new LazyWebSocket('ws://' + host);

		socket.onmessage = function(msg) {
			var currentState = JSON.parse(msg.data);
			updateState(currentState);
			redraw(currentState.x, currentState.y, currentState.dx, currentState.dy, currentState.sensors);
		}

		return socket;
	}

	function initCanvas(canvasId) {
		var canvas = document.getElementById(canvasId);
		var context = canvas.getContext("2d");

		var lenght = 600 // canvas.parentElement.clientWidth;
		canvas.width = lenght;
		canvas.height = lenght;

		return context;
	}

	function updateState(currentState) {
		// Update the path
		xPath.push(currentState.x);
		yPath.push(currentState.y);

		// Update the properties
		currentState.properties.forEach(function(item) {
			var value = currentState[item];
			if (undefined != value) {
				createOrUpdateTableRow('properties', 'state', item, value);
			}
		});

		// Update the sensor information
		currentState.sensors.forEach(function(sensor) {
			createOrUpdateTableRow('sensors', 'sensor', sensor.id, sensor.distance);
		});

		// Update the encoder information
		currentState.encoders.forEach(function(encoder) {
			createOrUpdateTableRow('encoders', 'encoder', encoder.id, encoder.voltage);
		});

		function createOrUpdateTableRow(tableId, prefix, key, value) {
			var id = prefix + key;
			if ($('#' + tableId).find('#' + id).length > 0) {
				$('#' + id).html(value);
			} else {
				$('#' + tableId + ' > tbody:last-child').append('
' + key + '	' + value + '
');
			}
		}
	}

	/**
	 * Scale the value (in metres) to the specific scale which is number of meters
	 * in canvas
	 */
	function scaleValue(value) {
		var s = context.canvas.height / scale;
		return value * s;
	}

	function translateAndScale(x, y) {
		return {
			x : context.canvas.width / 2 + scaleValue(x),
			y : context.canvas.height - (context.canvas.height / 2 + scaleValue(y))
		}
	}

	function redraw(x, y, dx, dy, sensors) {
		context.clearRect(0, 0, context.canvas.width, context.canvas.height);

		var theta = Math.atan2(dy, dx);
		var translated = translateAndScale(x, y);

		drawPath();
		drawSensors(translated.x, translated.y, theta, sensors);
		drawRobot(translated.x, translated.y, theta);
	}

	function drawPath() {
		var canvas = context.canvas;

		context.strokeStyle = drawConfig.pathColour;
		context.lineJoin = "round";
		context.lineWidth = 5 / scale;

		var path = new Path2D();
		path.moveTo(canvas.width / 2, canvas.height / 2);

		for (var i = 0; i < xPath.length; i++) {
			// Calculate x and y coordinates for canvas centred in middle with
			// inverted y-axis
			var translated = translateAndScale(xPath[i], yPath[i]);
			path.lineTo(translated.x, translated.y);
		}

		context.stroke(path);
	}

	function drawRobot(x, y, theta) {
		var width = scaleValue(0.05);
		var length = scaleValue(0.1);

		drawRotated(x, y, theta, function() {
			var pathRobot = new Path2D();
			pathRobot.rect(-width / 2, -length / 2, width, length);
			context.fillStyle = drawConfig.robotColour;
			context.fill(pathRobot);
		});
	}

	function drawSensors(x, y, theta, sensors) {
		drawRotated(x, y, theta, function() {
			sensors.forEach(function(sensor) {
				var sensorX = scaleValue(sensor.x);
				var sensorY = -scaleValue(sensor.y);

				drawRotated(sensorX, sensorY, sensor.theta, function() {
					var pathSensor = new Path2D();
					pathSensor.moveTo(0, 0);

					var h = sensorX + scaleValue(sensor.distance);

					pathSensor.lineTo(Math.cos(drawConfig.sensorConeTheta) * h, Math.sin(drawConfig.sensorConeTheta) * h);
					pathSensor.lineTo(Math.cos(-drawConfig.sensorConeTheta) * h, Math.sin(-drawConfig.sensorConeTheta) * h);

					context.fillStyle = drawConfig.sensorColour;
					context.fill(pathSensor);
				});
			});
		});
	}

	function drawRotated(x, y, theta, drawFunction) {
		context.save();
		context.translate(x, y);
		context.rotate(theta);

		drawFunction();

		context.restore();
	}

	// TODO could use rxJS here....
	// TODO what about sockets and rxJS?
	$('#connect').click(function(event) {
		if (socket.isOpen()) {
			socket.close();
			$('#connect').text("Connect");
		} else {
			socket.open();
			$('#connect').text("Disconnect");
		}
		xPath = new Array();
		yPath = new Array();
	});

	$('#start').click(function(event) {
		socket.send('START');
	});

	$('#stop').click(function(event) {
		socket.send('STOP');
	});

	$('#scale').change(function(event) {
		scale = event.currentTarget.value;
	});

}());<commit_msg>Set path thickness<commit_after>(function() {
	var socket = initSocket(document.currentScript.getAttribute('host'));
	var context = initCanvas(document.currentScript.getAttribute('canvas'));

	var xPath, yPath = null;
	var scale = 1;

	var drawConfig = {
		pathColour : "#000000",
		pathThickness : 3,
		robotColour : "#0000FF",
		sensorColour : "#FF0000",
		sensorConeTheta : Math.PI / 32
	}

	function initSocket(host) {
		var socket = new LazyWebSocket('ws://' + host);

		socket.onmessage = function(msg) {
			var currentState = JSON.parse(msg.data);
			updateState(currentState);
			redraw(currentState.x, currentState.y, currentState.dx, currentState.dy, currentState.sensors);
		}

		return socket;
	}

	function initCanvas(canvasId) {
		var canvas = document.getElementById(canvasId);
		var context = canvas.getContext("2d");

		var lenght = 600 // canvas.parentElement.clientWidth;
		canvas.width = lenght;
		canvas.height = lenght;

		return context;
	}

	function updateState(currentState) {
		// Update the path
		xPath.push(currentState.x);
		yPath.push(currentState.y);

		// Update the properties
		currentState.properties.forEach(function(item) {
			var value = currentState[item];
			if (undefined != value) {
				createOrUpdateTableRow('properties', 'state', item, value);
			}
		});

		// Update the sensor information
		currentState.sensors.forEach(function(sensor) {
			createOrUpdateTableRow('sensors', 'sensor', sensor.id, sensor.distance);
		});

		// Update the encoder information
		currentState.encoders.forEach(function(encoder) {
			createOrUpdateTableRow('encoders', 'encoder', encoder.id, encoder.voltage);
		});

		function createOrUpdateTableRow(tableId, prefix, key, value) {
			var id = prefix + key;
			if ($('#' + tableId).find('#' + id).length > 0) {
				$('#' + id).html(value);
			} else {
				$('#' + tableId + ' > tbody:last-child').append('
' + key + '	' + value + '
');
			}
		}
	}

	/**
	 * Scale the value (in metres) to the specific scale which is number of meters
	 * in canvas
	 */
	function scaleValue(value) {
		var s = context.canvas.height / scale;
		return value * s;
	}

	function translateAndScale(x, y) {
		return {
			x : context.canvas.width / 2 + scaleValue(x),
			y : context.canvas.height - (context.canvas.height / 2 + scaleValue(y))
		}
	}

	function redraw(x, y, dx, dy, sensors) {
		context.clearRect(0, 0, context.canvas.width, context.canvas.height);

		var theta = Math.atan2(dy, dx);
		var translated = translateAndScale(x, y);

		drawPath();
		drawSensors(translated.x, translated.y, theta, sensors);
		drawRobot(translated.x, translated.y, theta);
	}

	function drawPath() {
		var canvas = context.canvas;

		context.strokeStyle = drawConfig.pathColour;
		context.lineJoin = "round";
		context.lineWidth = drawConfig.pathThickness / scale;

		var path = new Path2D();
		path.moveTo(canvas.width / 2, canvas.height / 2);

		for (var i = 0; i < xPath.length; i++) {
			// Calculate x and y coordinates for canvas centred in middle with
			// inverted y-axis
			var translated = translateAndScale(xPath[i], yPath[i]);
			path.lineTo(translated.x, translated.y);
		}

		context.stroke(path);
	}

	function drawRobot(x, y, theta) {
		var width = scaleValue(0.05);
		var length = scaleValue(0.1);

		drawRotated(x, y, theta, function() {
			var pathRobot = new Path2D();
			pathRobot.rect(-width / 2, -length / 2, width, length);
			context.fillStyle = drawConfig.robotColour;
			context.fill(pathRobot);
		});
	}

	function drawSensors(x, y, theta, sensors) {
		drawRotated(x, y, theta, function() {
			sensors.forEach(function(sensor) {
				var sensorX = scaleValue(sensor.x);
				var sensorY = -scaleValue(sensor.y);

				drawRotated(sensorX, sensorY, sensor.theta, function() {
					var pathSensor = new Path2D();
					pathSensor.moveTo(0, 0);

					var h = sensorX + scaleValue(sensor.distance);

					pathSensor.lineTo(Math.cos(drawConfig.sensorConeTheta) * h, Math.sin(drawConfig.sensorConeTheta) * h);
					pathSensor.lineTo(Math.cos(-drawConfig.sensorConeTheta) * h, Math.sin(-drawConfig.sensorConeTheta) * h);

					context.fillStyle = drawConfig.sensorColour;
					context.fill(pathSensor);
				});
			});
		});
	}

	function drawRotated(x, y, theta, drawFunction) {
		context.save();
		context.translate(x, y);
		context.rotate(theta);

		drawFunction();

		context.restore();
	}

	// TODO could use rxJS here....
	// TODO what about sockets and rxJS?
	$('#connect').click(function(event) {
		if (socket.isOpen()) {
			socket.close();
			$('#connect').text("Connect");
		} else {
			socket.open();
			$('#connect').text("Disconnect");
		}
		xPath = new Array();
		yPath = new Array();
	});

	$('#start').click(function(event) {
		socket.send('START');
	});

	$('#stop').click(function(event) {
		socket.send('STOP');
	});

	$('#scale').change(function(event) {
		scale = event.currentTarget.value;
	});

}());<|endoftext|>


<commit_before>var DANBOORU_VERSION = {
  major: 1,
  minor: 13,
  build: 0
}

/* If initial is true, this is a notice set by the notice cookie and not a
 * realtime notice from user interaction. */
function notice(msg, initial) {
  /* If this is an initial notice, and this screen has a dedicated notice
   * container other than the floating notice, use that and don't disappear
   * it. */
  if(initial) {
    var static_notice = $("static_notice");
    if(static_notice) {
      static_notice.update(msg);
      static_notice.show();
      return;
    }
  }

  start_notice_timer();
  $('notice').update(msg);
  $('notice-container').show();
}

function number_to_human_size(size, precision)
{
  if(precision == null)
    precision = 1;

  size = Number(size);
  if(size.toFixed(0) == 1) text = "1 Byte";
  else if(size < 1024)                  text = size.toFixed(0) + " Bytes";
  else if(size < 1024*1024)             text = (size / 1024).toFixed(precision) + " KB";
  else if(size < 1024*1024*1024)        text = (size / (1024*1024)).toFixed(precision) + " MB";
  else if(size < 1024*1024*1024*1024)   text = (size / (1024*1024*1024)).toFixed(precision) + " GB";
  else                                  text = (size / (1024*1024*1024*1024)).toFixed(precision) + " TB";

  text = text.gsub(/([0-9]\.\d*?)0+ /, '#{1} ' ).gsub(/\. /,' ');
  return text;
}

var ClearNoticeTimer;
function start_notice_timer() {
  if(ClearNoticeTimer)
    window.clearTimeout(ClearNoticeTimer);

  ClearNoticeTimer = window.setTimeout(function() {
		  $('notice-container').hide();
  }, 5000);
}

var ClipRange = Class.create({
  initialize: function(min, max) {
    if (min > max)  {
      throw "paramError"
    }

    this.min = min
    this.max = max
  },

  clip: function(x) {
    if (x < this.min) {
      return this.min
    }

    if (x > this.max) {
      return this.max
    }

    return x
  }
})

Object.extend(Element, {
  appendChildBase: Element.appendChild,
  appendChild: function(e) {
    this.appendChildBase(e)
    return e
  }
});

Object.extend(Element.Methods, {
  showBase: Element.show,
  show: function(element, visible) {
    if (visible || visible == null)
      return $(element).showBase();
    else
      return $(element).hide();
  },
  isParentNode: function(element, parentNode) {
    while(element) {
      if(element == parentNode)
        return true;
      element = element.parentNode;
    }
    return false;
  },
  setTextContent: function(element, text)
  {
    if(element.innerText)
      element.innerText = text;
    else
      element.textContent = text;
    return element;
  },

  /* Return the X offset of a node relative to a parent node.  Like cumulativeRange().left,
   * but stops when parent is reached. */
  cumulative_offset_range_x: function(node, parent)
  {
    var offset_x = 0;
    do
    {
      offset_x += node.offsetLeft;
      node = node.parentNode;
    }
    while(node != null && node != parent);
    return offset_x;
  }
});
Element.addMethods()

var KeysDown = new Hash();

/* Many browsers eat keyup events if focus is lost while the button
 * is pressed. */
document.observe("blur", function(e) { KeysDown = new Hash(); })

function OnKeyCharCode(key, f, element)
{
  if(window.opera)
    return;
  if(!element)
    element = document;
  element.observe("keyup", function(e) {
    if (e.keyCode != key)
      return;
    KeysDown.set(KeysDown[e.keyCode], false);
  });
  element.observe("keypress", function(e) {
    if (e.charCode != key)
      return;
    if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey)
      return;
    if(KeysDown.get(KeysDown[e.keyCode]))
      return;
    KeysDown.set(KeysDown[e.keyCode], true);

    var target = e.target;
    if(target.tagName == "INPUT" || target.tagName == "TEXTAREA")
      return;

    f(e);
    e.stop();
    e.preventDefault();
  });
}

function OnKey(key, options, press, release)
{
  if(!options)
    options = {};
  var element = options["Element"]
  if(!element)
    element = document;
  if(element == document && window.opera && !options.AlwaysAllowOpera)
    return;

  element.observe("keyup", function(e) {
    if (e.keyCode != key)
      return;
    KeysDown[e.keyCode] = false
    if(release)
      release(e);
  });

  element.observe("keydown", function(e) {
    if (e.keyCode != key)
      return;
    if (e.metaKey)
      return;
    if (e.shiftKey != !!options.shiftKey)
      return;
    if (e.altKey != !!options.altKey)
      return;
    if (e.ctrlKey != !!options.ctrlKey)
      return;
    if(KeysDown[e.keyCode])
      return;

    KeysDown[e.keyCode] = true
    var target = e.target;
    if(!options.AllowTextAreaFields && target.tagName == "TEXTAREA")
      return;
    if(!options.AllowInputFields && target.tagName == "INPUT")
      return;

    if(press && !press(e))
      return;
    e.stop();
    e.preventDefault();
  });
}

function InitTextAreas()
{
  $$("TEXTAREA").each(function(elem) {
    var form = elem.up("FORM");
    if(!form)
      return;

    if(elem.set_login_handler)
      return;
    elem.set_login_handler = true;

    OnKey(13, { ctrlKey: true, AllowInputFields: true, AllowTextAreaFields: true, Element: elem}, function(f) {
      $(form).submitWithLogin();
    });
  });
}

function InitAdvancedEditing()
{
  if(Cookie.get("show_advanced_editing") != "1")
    return;

  $(document.documentElement).removeClassName("hide-advanced-editing");
}

/* When we resume a user submit after logging in, we want to run submit events, as
 * if the submit had happened normally again, but submit() doesn't do this.  Run
 * a submit event manually. */
Element.addMethods("FORM", {
  simulate_submit: function(form)
  {
    form = $(form);

    if(document.createEvent)
    {
      var e = document.createEvent("HTMLEvents");
      e.initEvent("submit", true, true);
      form.dispatchEvent(e);

      if(!e.stopped)
        form.submit();
    }
    else
    {
      if(form.fireEvent("onsubmit"))
        form.submit();
    }
  }
});

Element.addMethods({
  simulate_anchor_click: function(a, ev)
  {
    a = $(a);

    if(document.dispatchEvent)
    {
      if(a.dispatchEvent(ev) && !ev.stopped)
        window.location.href = a.href;
    }
    else
    {
      if(a.fireEvent("onclick", ev))
        window.location.href = a.href;
    }
  }
});

clone_event = function(orig)
{
  if(document.dispatchEvent)
  {
    var e = document.createEvent("MouseEvent");
    e.initMouseEvent(orig.type, orig.canBubble, orig.cancelable, orig.view,
        orig.detail, orig.screenX, orig.screenY, orig.clientX, orig.clientY,
        orig.ctrlKey, orig.altKey, orig.shiftKey, orig.metaKey,
        orig.button, orig.relatedTarget);
    return Event.extend(e);
  }
  else
  {
    var e = document.createEventObject(orig);
    return Event.extend(e);
  }
}

<commit_msg>add String.subst helper<commit_after>var DANBOORU_VERSION = {
  major: 1,
  minor: 13,
  build: 0
}

/* If initial is true, this is a notice set by the notice cookie and not a
 * realtime notice from user interaction. */
function notice(msg, initial) {
  /* If this is an initial notice, and this screen has a dedicated notice
   * container other than the floating notice, use that and don't disappear
   * it. */
  if(initial) {
    var static_notice = $("static_notice");
    if(static_notice) {
      static_notice.update(msg);
      static_notice.show();
      return;
    }
  }

  start_notice_timer();
  $('notice').update(msg);
  $('notice-container').show();
}

function number_to_human_size(size, precision)
{
  if(precision == null)
    precision = 1;

  size = Number(size);
  if(size.toFixed(0) == 1) text = "1 Byte";
  else if(size < 1024)                  text = size.toFixed(0) + " Bytes";
  else if(size < 1024*1024)             text = (size / 1024).toFixed(precision) + " KB";
  else if(size < 1024*1024*1024)        text = (size / (1024*1024)).toFixed(precision) + " MB";
  else if(size < 1024*1024*1024*1024)   text = (size / (1024*1024*1024)).toFixed(precision) + " GB";
  else                                  text = (size / (1024*1024*1024*1024)).toFixed(precision) + " TB";

  text = text.gsub(/([0-9]\.\d*?)0+ /, '#{1} ' ).gsub(/\. /,' ');
  return text;
}

var ClearNoticeTimer;
function start_notice_timer() {
  if(ClearNoticeTimer)
    window.clearTimeout(ClearNoticeTimer);

  ClearNoticeTimer = window.setTimeout(function() {
		  $('notice-container').hide();
  }, 5000);
}

var ClipRange = Class.create({
  initialize: function(min, max) {
    if (min > max)  {
      throw "paramError"
    }

    this.min = min
    this.max = max
  },

  clip: function(x) {
    if (x < this.min) {
      return this.min
    }

    if (x > this.max) {
      return this.max
    }

    return x
  }
})

Object.extend(Element, {
  appendChildBase: Element.appendChild,
  appendChild: function(e) {
    this.appendChildBase(e)
    return e
  }
});

Object.extend(Element.Methods, {
  showBase: Element.show,
  show: function(element, visible) {
    if (visible || visible == null)
      return $(element).showBase();
    else
      return $(element).hide();
  },
  isParentNode: function(element, parentNode) {
    while(element) {
      if(element == parentNode)
        return true;
      element = element.parentNode;
    }
    return false;
  },
  setTextContent: function(element, text)
  {
    if(element.innerText)
      element.innerText = text;
    else
      element.textContent = text;
    return element;
  },

  /* Return the X offset of a node relative to a parent node.  Like cumulativeRange().left,
   * but stops when parent is reached. */
  cumulative_offset_range_x: function(node, parent)
  {
    var offset_x = 0;
    do
    {
      offset_x += node.offsetLeft;
      node = node.parentNode;
    }
    while(node != null && node != parent);
    return offset_x;
  }
});
Element.addMethods()

var KeysDown = new Hash();

/* Many browsers eat keyup events if focus is lost while the button
 * is pressed. */
document.observe("blur", function(e) { KeysDown = new Hash(); })

function OnKeyCharCode(key, f, element)
{
  if(window.opera)
    return;
  if(!element)
    element = document;
  element.observe("keyup", function(e) {
    if (e.keyCode != key)
      return;
    KeysDown.set(KeysDown[e.keyCode], false);
  });
  element.observe("keypress", function(e) {
    if (e.charCode != key)
      return;
    if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey)
      return;
    if(KeysDown.get(KeysDown[e.keyCode]))
      return;
    KeysDown.set(KeysDown[e.keyCode], true);

    var target = e.target;
    if(target.tagName == "INPUT" || target.tagName == "TEXTAREA")
      return;

    f(e);
    e.stop();
    e.preventDefault();
  });
}

function OnKey(key, options, press, release)
{
  if(!options)
    options = {};
  var element = options["Element"]
  if(!element)
    element = document;
  if(element == document && window.opera && !options.AlwaysAllowOpera)
    return;

  element.observe("keyup", function(e) {
    if (e.keyCode != key)
      return;
    KeysDown[e.keyCode] = false
    if(release)
      release(e);
  });

  element.observe("keydown", function(e) {
    if (e.keyCode != key)
      return;
    if (e.metaKey)
      return;
    if (e.shiftKey != !!options.shiftKey)
      return;
    if (e.altKey != !!options.altKey)
      return;
    if (e.ctrlKey != !!options.ctrlKey)
      return;
    if(KeysDown[e.keyCode])
      return;

    KeysDown[e.keyCode] = true
    var target = e.target;
    if(!options.AllowTextAreaFields && target.tagName == "TEXTAREA")
      return;
    if(!options.AllowInputFields && target.tagName == "INPUT")
      return;

    if(press && !press(e))
      return;
    e.stop();
    e.preventDefault();
  });
}

function InitTextAreas()
{
  $$("TEXTAREA").each(function(elem) {
    var form = elem.up("FORM");
    if(!form)
      return;

    if(elem.set_login_handler)
      return;
    elem.set_login_handler = true;

    OnKey(13, { ctrlKey: true, AllowInputFields: true, AllowTextAreaFields: true, Element: elem}, function(f) {
      $(form).submitWithLogin();
    });
  });
}

function InitAdvancedEditing()
{
  if(Cookie.get("show_advanced_editing") != "1")
    return;

  $(document.documentElement).removeClassName("hide-advanced-editing");
}

/* When we resume a user submit after logging in, we want to run submit events, as
 * if the submit had happened normally again, but submit() doesn't do this.  Run
 * a submit event manually. */
Element.addMethods("FORM", {
  simulate_submit: function(form)
  {
    form = $(form);

    if(document.createEvent)
    {
      var e = document.createEvent("HTMLEvents");
      e.initEvent("submit", true, true);
      form.dispatchEvent(e);

      if(!e.stopped)
        form.submit();
    }
    else
    {
      if(form.fireEvent("onsubmit"))
        form.submit();
    }
  }
});

Element.addMethods({
  simulate_anchor_click: function(a, ev)
  {
    a = $(a);

    if(document.dispatchEvent)
    {
      if(a.dispatchEvent(ev) && !ev.stopped)
        window.location.href = a.href;
    }
    else
    {
      if(a.fireEvent("onclick", ev))
        window.location.href = a.href;
    }
  }
});

clone_event = function(orig)
{
  if(document.dispatchEvent)
  {
    var e = document.createEvent("MouseEvent");
    e.initMouseEvent(orig.type, orig.canBubble, orig.cancelable, orig.view,
        orig.detail, orig.screenX, orig.screenY, orig.clientX, orig.clientY,
        orig.ctrlKey, orig.altKey, orig.shiftKey, orig.metaKey,
        orig.button, orig.relatedTarget);
    return Event.extend(e);
  }
  else
  {
    var e = document.createEventObject(orig);
    return Event.extend(e);
  }
}

Object.extend(String.prototype, {
  subst: function(subs) {
    var text = this;
    for(var s in subs)
    {
      var r = new RegExp("\\${" + s + "}", "g");
      var to = subs[s];
      if(!to) to = "";
      text = text.replace(r, to);
    }

    return text;
  }
});

<|endoftext|>