<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(request) {
              logger.debug(`Requesting ${request.method} ${request.url}`);
              return request; // you can return a modified Request, or you can short-circuit the request by returning a
              // Response
            },
            response(response) {
              logger.debug(`Received ${response.status} ${response.url}`);
              return response; // 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({
          headers: {
            'Accept': 'application/json',
            'X-Requested-With': 'Fetch'
          }
        })
        .rejectErrorResponses()
        .withInterceptor({
          request(request) {
              logger.debug(`Requesting ${request.method} ${request.url}`);
              return request; // you can return a modified Request, or you can short-circuit the request by returning a
              // Response
            },
            response(response) {
              logger.debug(`Received ${response.status} ${response.url}`);
              return response; // 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(request) {
              logger.debug(`Requesting ${request.method} ${request.url}`);
              return request; // you can return a modified Request, or you can short-circuit the request by returning a
              // Response
            },
            response(response) {
              logger.debug(`Received ${response.status} ${response.url}`);
              return response; // 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)
      .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('New Data: ', 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) {
    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}`)
      });
  }

  /**
   * Get BEL Evidence from API Service
   *
   * @param id
   * @returns evidence object
   */
  // getBelEvidence(id) {
  //   return this.apiClient.fetch(`/evidence/${id}`)
  //     .then(response => response.json())
  //     .then(data => {
  //       return data.evidence;
  //     })
  //     .then(evidence => {
  //       logger.debug("Ev1: ", evidence);
  //       this.getBelComponents(evidence.bel_statement)
  //       .then(comp => {
  //         evidence.bel_subject = comp.subject;
  //         evidence.bel_object = comp.object;
  //         evidence.bel_relationship = comp.relationship;
  //         logger.debug('Evidence: ', evidence);
  //         logger.debug('Comp: ', comp);
  //         return evidence;
  //       })
  //       .catch(function(reason) {
  //         logger.error(`GET Statement components error: ${reason}`)
  //       });
  //       return evidence;
  //     })
  //     .catch(function(reason) {
  //       logger.error(`GET BEL Evidence Error: ${reason}`)
  //     });
  // }

  getBelEvidence(id) {
    return this.apiClient.fetch(`/evidence/${id}`)
      .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) {
    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'
          },
          body: JSON.stringify(evidence)
        })
        .then(response => response.json())
        .catch(function(reason) {
          logger.error(`PUT BEL Evidence Error: ${reason}`)
        });
    } else {
      return this.apiClient.fetch(`/evidence`, {
          method: 'post',
          headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json; profile=http://next.belframework.org/schema/evidence.schema.json'
          },
          body: JSON.stringify(evidence)
        })
        .then(response => response.headers.get('Location'))
        .catch(function(reason) {
          logger.error(`POST BEL Evidence Error: ${reason}`)
        });
    }
  }

  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'
      }
    })
    .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 => {
        // logger.debug('Pubmed: ', data);
        return data.resultList.result[0];
      })
      .catch(function(reason) {
        logger.error(`GET Pubmed Error: ${reason}`)
      });
  }

  // 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) {

  }

  // 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/

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

    return this.apiClient.fetch('/datasets', {
      method: 'post',
      body: data
    });
  }

  // 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) {
    let matches = url.match(/\/(\w+?)$/);
    // logger.debug('Matches: ', matches[1]);
    return matches[1];
  }

  getDatasets(){
    return this.apiClient.fetch('/datasets')
      .then(response => response.json())
      .then(data => {return data.dataset_collection;})
      .catch(function(reason) {
        logger.error(`GET Datasets Error: ${reason}`)
      });
  }

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

}
<commit_msg>port completion mechanics from OpenBEL/belhop<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(request) {
              logger.debug(`Requesting ${request.method} ${request.url}`);
              return request; // you can return a modified Request, or you can short-circuit the request by returning a
              // Response
            },
            response(response) {
              logger.debug(`Received ${response.status} ${response.url}`);
              return response; // 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({
          headers: {
            'Accept': 'application/json',
            'X-Requested-With': 'Fetch'
          }
        })
        .rejectErrorResponses()
        .withInterceptor({
          request(request) {
              logger.debug(`Requesting ${request.method} ${request.url}`);
              return request; // you can return a modified Request, or you can short-circuit the request by returning a
              // Response
            },
            response(response) {
              logger.debug(`Received ${response.status} ${response.url}`);
              return response; // 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(request) {
              logger.debug(`Requesting ${request.method} ${request.url}`);
              return request; // you can return a modified Request, or you can short-circuit the request by returning a
              // Response
            },
            response(response) {
              logger.debug(`Received ${response.status} ${response.url}`);
              return response; // 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)
      .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('New Data: ', 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) {
    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}`)
      });
  }

  /**
   * Get BEL Evidence from API Service
   *
   * @param id
   * @returns evidence object
   */
  // getBelEvidence(id) {
  //   return this.apiClient.fetch(`/evidence/${id}`)
  //     .then(response => response.json())
  //     .then(data => {
  //       return data.evidence;
  //     })
  //     .then(evidence => {
  //       logger.debug("Ev1: ", evidence);
  //       this.getBelComponents(evidence.bel_statement)
  //       .then(comp => {
  //         evidence.bel_subject = comp.subject;
  //         evidence.bel_object = comp.object;
  //         evidence.bel_relationship = comp.relationship;
  //         logger.debug('Evidence: ', evidence);
  //         logger.debug('Comp: ', comp);
  //         return evidence;
  //       })
  //       .catch(function(reason) {
  //         logger.error(`GET Statement components error: ${reason}`)
  //       });
  //       return evidence;
  //     })
  //     .catch(function(reason) {
  //       logger.error(`GET BEL Evidence Error: ${reason}`)
  //     });
  // }

  getBelEvidence(id) {
    return this.apiClient.fetch(`/evidence/${id}`)
      .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) {
    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'
          },
          body: JSON.stringify(evidence)
        })
        .then(response => response.json())
        .catch(function(reason) {
          logger.error(`PUT BEL Evidence Error: ${reason}`)
        });
    } else {
      return this.apiClient.fetch(`/evidence`, {
          method: 'post',
          headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json; profile=http://next.belframework.org/schema/evidence.schema.json'
          },
          body: JSON.stringify(evidence)
        })
        .then(response => response.headers.get('Location'))
        .catch(function(reason) {
          logger.error(`POST BEL Evidence Error: ${reason}`)
        });
    }
  }

  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'
      }
    })
    .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 => {
        // logger.debug('Pubmed: ', data);
        return data.resultList.result[0];
      })
      .catch(function(reason) {
        logger.error(`GET Pubmed Error: ${reason}`)
      });
  }

  // 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) {

  }

  // 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/

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

    return this.apiClient.fetch('/datasets', {
      method: 'post',
      body: data
    });
  }

  // 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) {
    let matches = url.match(/\/(\w+?)$/);
    // logger.debug('Matches: ', matches[1]);
    return matches[1];
  }

  getDatasets(){
    return this.apiClient.fetch('/datasets')
      .then(response => response.json())
      .then(data => {return data.dataset_collection;})
      .catch(function(reason) {
        logger.error(`GET Datasets Error: ${reason}`)
      });
  }

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

  /*
   * The options hash can handle queryParams and accept keys.
   * (call with url OR path, not both)
   */
  apiGET(url, path, onSuccess, onErr, options) {
    if (url === null) {
      url = baseUrl;
      // append the path
      path = encodeURI(path);
      url += path;
    }

    // setup the options to our AJAX get
    const defaultOptions = {
      queryParams: null,
      accept: null
    };
    const argOptions = $.extend(defaultOptions, options || {});

    if (argOptions.queryParams !== null) {
      // append query parameters
      url += `?${argOptions.queryParams}`;
    }

    const ajaxOptions = {
      url: url,
      success: onSuccess,
      error: onErr
    };

    if (argOptions.accept !== null) {
      ajaxOptions.headers = { Accept: argOptions.accept };
    }
    $.ajax(ajaxOptions);
  }

  /**
   * Applies a completion to the input and returns the result.
   *
   * @memberOf belhop.complete
   *
   * @param {belhop.Completion} completion BEL API completion object
   * @param {string} input BEL expression to autocomplete.
   *
   * @return {string} Completed input string.
   */
  completeApply = function(completion, input) {
    let cDelete = this.completeActionDelete;
    let cInsert = this.completeActionInsert;
    /* applies a single action */
    function actOn(action) {
      if (action.delete) {
        const startPos = action.delete.start_position;
        const endPos = action.delete.end_position;
        input = cDelete(input, startPos, endPos);
      } else if (action.insert) {
        const value = action.insert.value;
        const position = action.insert.position;
        input = cInsert(input, value, position);
      }
    }
    /* apply each action, mutating input */
    let actions = completion.actions;
    actions.forEach(actOn);
    return input;
  };

  /**
   * example:
   * function onSuccess(response, status, request) {
   *   let completionCollection = response.completion_collection;
   *   // pick a completion from the array, first element selected here
   *   let chosenCompletion = completionCollection[0];
   *   // apply it
   *   api.completeApply(chosenCompletion, input);
   * }
   */
  completeExpression = function(input, caretPosition, onSuccess, onErr) {
    const path = `/expressions/${input}/completions`;
    const getOpts = {};
    getOpts.queryParams = `caret_position=${caretPosition}`;
    this.apiGET(null, path, onSuccess, onErr, getOpts);
  };

  /**
   * Delete the characters from startPos to endPos inclusively and return the
   * result.
   *
   * @protected
   * @memberOf belhop.complete.actions
   *
   * @param {string} str Input string to operate on.
   * @param {number} startPos Starting position of the deletion range.
   * @param {number} endPos Ending position of the deletion range.
   *
   * @example
   * > // delete "JUNK" from input
   * > belhop.complete.actions.delete('fooJUNKbar', 3, 6);
   * 'foobar'
   *
   * @return {string} Input string after deletion operation.
   */
  completeActionDelete = function(str, startPos, endPos) {
    const str1 = str.substr(0, startPos);
    const str2 = str.substr(endPos + 1);
    const ret = str1 + str2;
    return ret;
  };

  /**
   * Insert the string value at position and return the result.
   *
   * @protected
   * @memberOf belhop.complete.actions.insert
   *
   * @param {string} str Input string to operate on.
   * @param {string} value String to insert.
   * @param {number} position Insertion position.
   *
   * @example
   * > // insert "bar" into input
   * > belhop.complete.actions.insert('foo', 'bar', 3);
   * 'foobar'
   *
   * @return {string} Input string after insertion operation.
   */
  completeActionInsert = function(str, value, position) {
    const str1 = str.substr(0, position);
    const str2 = value;
    const str3 = str.substr(position);
    const rslt = str1 + str2 + str3;
    return rslt;
  };

}
<|endoftext|>


<commit_before>define([
	"Ember",
	"nwjs/menu",
	"models/localstorage/Settings",
	"hbs!templates/components/StreamPreviewImageComponent"
], function(
	Ember,
	Menu,
	Settings,
	layout
) {

	var get = Ember.get;

	var actions = Settings.gui_streamclick.reduce(function( obj, item ) {
		obj[ item.key ] = item.id;
		return obj;
	}, {} );

	return Ember.Component.extend({
		chat    : Ember.inject.service(),
		settings: Ember.inject.service(),

		layout: layout,
		tagName: "div",
		classNameBindings: [ ":preview", "class" ],
		attributeBindings: [ "title", "noMiddleclickScroll:data-no-middleclick-scroll" ],
		"class": "",
		noMiddleclickScroll: function() {
			// true or null
			return get( this, "settings.gui_streamclick_mid" ) !== actions.disabled || null;
		}.property( "settings.gui_streamclick_mid" ),

		init: function() {
			this._super.apply( this, arguments );
			// FIXME: refactor global openLivestreamer and goto actions
			this.applicationRoute = this.container.lookup( "route:application" );
		},

		clickable: true,

		mouseUp: function( event ) {
			if ( event.button === 1 ) {
				this.click( event );
			}
		},

		click: function( event ) {
			if ( get( this, "clickable" ) ) {
				var action = event.button === 0
					// left mouse button
					? ( event.ctrlKey || event.metaKey
						// with modifier key
						? get( this, "settings.gui_streamclick_mod" )
						// without modifier keys (default action)
						: actions.launch
					)
					: event.button === 1
					// middle mouse button
					? get( this, "settings.gui_streamclick_mid" )
					// everything else (no action)
					: -1;

				switch ( action ) {
					case actions.launch:
						return this.launchStream();
					case actions.chat:
						return this.openChat();
					case actions.channel:
						return this.gotoChannelPage();
					case actions.settings:
						return this.gotoChannelSettings();
				}
			}

			if ( this.attrs.action instanceof Function ) {
				this.attrs.action();
			}
		},

		contextMenu: function( event ) {
			if ( this.attrs.noContextmenu ) { return; }

			var menu = Menu.create();

			menu.items.pushObjects([
				{
					label  : "Launch stream",
					submenu: Settings.qualities.map(function( quality ) {
						return {
							label: quality.label,
							click: this.launchStream.bind( this, quality.id )
						};
					}, this )
				},
				{
					label: "Open chat",
					click: this.openChat.bind( this )
				}
			]);

			if ( !this.attrs.contextmenuNoGotos ) {
				menu.items.pushObjects([
					{
						label: "Channel page",
						click: this.gotoChannelPage.bind( this )
					},
					{
						label: "Channel settings",
						click: this.gotoChannelSettings.bind( this )
					}
				]);
			}

			menu.popup( event.originalEvent.x, event.originalEvent.y );
		},

		launchStream: function( quality ) {
			var stream = get( this, "stream" );
			this.applicationRoute.send( "openLivestreamer", stream, quality );
		},

		openChat: function() {
			var channel = get( this, "stream.channel" );
			get( this, "chat" ).open( channel );
		},

		gotoChannelPage: function() {
			var name = get( this, "stream.channel.id" );
			this.applicationRoute.send( "goto", "channel", name );
		},

		gotoChannelSettings: function() {
			var name = get( this, "stream.channel.id" );
			this.applicationRoute.send( "goto", "channel.settings", name );
		}

	});

});
<commit_msg>Add copy channel url to stream contextmenus<commit_after>define([
	"Ember",
	"nwjs/menu",
	"nwjs/clipboard",
	"models/localstorage/Settings",
	"hbs!templates/components/StreamPreviewImageComponent"
], function(
	Ember,
	Menu,
	clipboard,
	Settings,
	layout
) {

	var get = Ember.get;

	var actions = Settings.gui_streamclick.reduce(function( obj, item ) {
		obj[ item.key ] = item.id;
		return obj;
	}, {} );

	return Ember.Component.extend({
		chat    : Ember.inject.service(),
		settings: Ember.inject.service(),

		layout: layout,
		tagName: "div",
		classNameBindings: [ ":preview", "class" ],
		attributeBindings: [ "title", "noMiddleclickScroll:data-no-middleclick-scroll" ],
		"class": "",
		noMiddleclickScroll: function() {
			// true or null
			return get( this, "settings.gui_streamclick_mid" ) !== actions.disabled || null;
		}.property( "settings.gui_streamclick_mid" ),

		init: function() {
			this._super.apply( this, arguments );
			// FIXME: refactor global openLivestreamer and goto actions
			this.applicationRoute = this.container.lookup( "route:application" );
		},

		clickable: true,

		mouseUp: function( event ) {
			if ( event.button === 1 ) {
				this.click( event );
			}
		},

		click: function( event ) {
			if ( get( this, "clickable" ) ) {
				var action = event.button === 0
					// left mouse button
					? ( event.ctrlKey || event.metaKey
						// with modifier key
						? get( this, "settings.gui_streamclick_mod" )
						// without modifier keys (default action)
						: actions.launch
					)
					: event.button === 1
					// middle mouse button
					? get( this, "settings.gui_streamclick_mid" )
					// everything else (no action)
					: -1;

				switch ( action ) {
					case actions.launch:
						return this.launchStream();
					case actions.chat:
						return this.openChat();
					case actions.channel:
						return this.gotoChannelPage();
					case actions.settings:
						return this.gotoChannelSettings();
				}
			}

			if ( this.attrs.action instanceof Function ) {
				this.attrs.action();
			}
		},

		contextMenu: function( event ) {
			if ( this.attrs.noContextmenu ) { return; }

			var menu = Menu.create();

			menu.items.pushObjects([
				{
					label  : "Launch stream",
					submenu: Settings.qualities.map(function( quality ) {
						return {
							label: quality.label,
							click: this.launchStream.bind( this, quality.id )
						};
					}, this )
				},
				{
					label: "Open chat",
					click: this.openChat.bind( this )
				},
				{
					label: "Copy channel URL",
					click: this.copyChannelURL.bind( this )
				}
			]);

			if ( !this.attrs.contextmenuNoGotos ) {
				menu.items.pushObjects([
					{
						label: "Channel page",
						click: this.gotoChannelPage.bind( this )
					},
					{
						label: "Channel settings",
						click: this.gotoChannelSettings.bind( this )
					}
				]);
			}

			menu.popup( event.originalEvent.x, event.originalEvent.y );
		},

		launchStream: function( quality ) {
			var stream = get( this, "stream" );
			this.applicationRoute.send( "openLivestreamer", stream, quality );
		},

		openChat: function() {
			var channel = get( this, "stream.channel" );
			get( this, "chat" ).open( channel );
		},

		copyChannelURL: function() {
			var url = get( this, "stream.channel.url" );
			clipboard.set( url );
		},

		gotoChannelPage: function() {
			var name = get( this, "stream.channel.id" );
			this.applicationRoute.send( "goto", "channel", name );
		},

		gotoChannelSettings: function() {
			var name = get( this, "stream.channel.id" );
			this.applicationRoute.send( "goto", "channel.settings", name );
		}

	});

});
<|endoftext|>


<commit_before>'use strict';

const Challenge = require('../../models/challenge');

const put = (req, res, next) => {

    Challenge.update(
        { _id: req.params.id },
        req.body,
        (err, challenge)=> {
            if (err) {
                next(err);
            } else {
                res.json(challenge);
            }
        }
    )
};

module.exports = put;
<commit_msg>fix ESLint errors<commit_after>'use strict';

const Challenge = require('../../models/challenge');

const put = (req, res, next) => {

    Challenge.update(
        { _id: req.params.id },
        req.body,
        (err, challenge) => {
            if (err) {
                next(err);
            } else {
                res.json(challenge);
            }
        }
    );
};

module.exports = put;
<|endoftext|>


<commit_before>var InfoboxView = require('../../../../../javascripts/cartodb3/components/infobox/infobox-item-view');

describe('components/infobox/infobox-item-view', function () {
  var view;

  beforeEach(function () {
    view = new InfoboxView({
      type: 'error',
      title: 'Info',
      body: 'Lorem ipsum dolor sit amet.',
      mainAction: {
        label: 'Proceed',
        position: 'right'
      },
      secondAction: {
        label: 'Cancel',
        position: 'left'
      }
    });

    view.render();
  });

  it('should render properly', function () {
    expect(view.$('h2').text()).toBe('Info');
    expect(view.$('div').text()).toContain('Lorem ipsum');
    expect(view.$('.Infobox-buttons button').length).toBe(2);
    expect(view.$('.Infobox-buttons button').eq(0).text()).toContain('Proceed');
    expect(view.$('.Infobox-buttons button').eq(1).text()).toContain('Cancel');
  });

  it('should render loading when it is defined', function () {
    var v = new InfoboxView({
      type: 'alert',
      title: 'Info',
      body: 'Lorem ipsum dolor sit amet.',
      loading: true
    });

    v.render();
    expect(v.$('.CDB-LoaderIcon').length).toBe(1);
  });

  it('should render quota when needed', function () {
    var v = new InfoboxView({
      type: 'alert',
      title: 'Info',
      body: 'Lorem ipsum dolor sit amet.',
      quota: {
        usedQuota: 10,
        totalQuota: 100
      },
      secondAction: {
        label: 'Proceed'
      }
    });

    v.render();
    expect(v.$('.js-quota').length).toBe(1);
    expect(v.$('.Infobox-quotaBar').length).toBe(1);
  });

  it('should trigger events properly', function () {
    var callback = {
      main: function () {},
      second: function () {}
    };

    spyOn(callback, 'main');
    spyOn(callback, 'second');

    view.on('action:main', callback.main);
    view.on('action:second', callback.second);

    view.$('button').eq(0).trigger('click');
    view.$('button').eq(1).trigger('click');

    expect(callback.main.calls.count()).toEqual(1);
    expect(callback.second.calls.count()).toEqual(1);
  });

  it('should not have any leaks', function () {
    expect(view).toHaveNoLeaks();
  });
});
<commit_msg>infobox class tests<commit_after>var InfoboxView = require('../../../../../javascripts/cartodb3/components/infobox/infobox-item-view');

describe('components/infobox/infobox-item-view', function () {
  var view;

  beforeEach(function () {
    view = new InfoboxView({
      type: 'error',
      title: 'Info',
      body: 'Lorem ipsum dolor sit amet.',
      mainAction: {
        label: 'Proceed',
        position: 'right'
      },
      secondAction: {
        label: 'Cancel',
        position: 'left'
      }
    });

    view.render();
  });

  it('should render properly', function () {
    expect(view.$('h2').text()).toBe('Info');
    expect(view.$('div').text()).toContain('Lorem ipsum');
    expect(view.$('.Infobox-buttons button').length).toBe(2);
    expect(view.$('.Infobox-buttons button').eq(0).text()).toContain('Proceed');
    expect(view.$('.Infobox-buttons button').eq(1).text()).toContain('Cancel');
  });

  it('should have className when it is defined', function () {
    var className = 'fs-class-name';

    var v = new InfoboxView({
      type: 'alert',
      title: 'Info',
      body: 'Lorem ipsum dolor sit amet.',
      className: className
    });

    v.render();

    expect(v.$el.hasClass(className)).toBe(true);
  });

  it('should render loading when it is defined', function () {
    var v = new InfoboxView({
      type: 'alert',
      title: 'Info',
      body: 'Lorem ipsum dolor sit amet.',
      loading: true
    });

    v.render();
    expect(v.$('.CDB-LoaderIcon').length).toBe(1);
  });

  it('should render quota when needed', function () {
    var v = new InfoboxView({
      type: 'alert',
      title: 'Info',
      body: 'Lorem ipsum dolor sit amet.',
      quota: {
        usedQuota: 10,
        totalQuota: 100
      },
      secondAction: {
        label: 'Proceed'
      }
    });

    v.render();
    expect(v.$('.js-quota').length).toBe(1);
    expect(v.$('.Infobox-quotaBar').length).toBe(1);
  });

  it('should trigger events properly', function () {
    var callback = {
      main: function () {},
      second: function () {}
    };

    spyOn(callback, 'main');
    spyOn(callback, 'second');

    view.on('action:main', callback.main);
    view.on('action:second', callback.second);

    view.$('button').eq(0).trigger('click');
    view.$('button').eq(1).trigger('click');

    expect(callback.main.calls.count()).toEqual(1);
    expect(callback.second.calls.count()).toEqual(1);
  });

  it('should not have any leaks', function () {
    expect(view).toHaveNoLeaks();
  });
});
<|endoftext|>


<commit_before>var cdb = require('cartodb.js-v3');
var _ = require('underscore-cdb-v3');
var Utils = require('cdb.Utils');
var pluralizeString = require('../../../view_helpers/pluralize_string');

/**
 *  Listing datasets navigation.
 *
 *  - 'Filter by' datasets.
 *  - 'Search' any pattern within dataset collection.
 *
 */
module.exports = cdb.core.View.extend({

  events: {
    'submit .js-search-form':   '_submitSearch',
    'keydown .js-search-form':  '_onSearchKeyDown',
    'click .js-search-form':    'killEvent',
    'click .js-search-link':    '_onSearchClick',
    'click .js-clean-search':   '_onCleanSearchClick',
    'click .js-shared':         '_onSharedClick',
    'click .js-library':        '_onLibraryClick',
    'click .js-connect':        '_onConnectClick',
    'click .js-datasets':       '_onDatasetsClick',
    'click .js-create_empty':   '_onCreateEmptyClick'
  },

  _TEXTS: {
    createFromScratchLabel: {
      map: 'Create empty map',
      dataset: 'Create empty dataset',
      addLayer: 'Add an empty layer'
    }
  },

  initialize: function() {
    this.routerModel = this.options.routerModel;
    this.createModel = this.options.createModel;
    this.user = this.options.user;
    this.template = cdb.templates.getTemplate('common/views/create/listing/navigation');
    this.model = new cdb.core.Model();

    this._preRender();
    this._initBinds();
  },

  // It is necessary to add two static elements because
  // they can't be removed/replaced using render method
  // each time a change (in a model or a collection) happens.
  // This is due to the behaviour of the CSS animations.
  _preRender: function() {
    var $uInner = $('<div>').addClass('u-inner');
    var $filtersInner = $('<div>').addClass('Filters-inner');
    this.$el.append($uInner.append($filtersInner));
  },

  render: function(m, c) {
    this.clearSubViews();

    var selectedItemsCount = this._selectedItems().length;
    // If a change is made from content type we have to know
    // preventing show wrong data about total items
    var changedContentType = c && c.changes && c.changes.content_type;
    var createModelType = this.createModel.get('type');

    this.$('.Filters-inner').html(
      this.template(
        _.extend({
            createModel:           this.createModel,
            canCreateDataset:      this.user.canCreateDatasets(),
            listing:               this.createModel.get('listing'),
            isInsideOrg:           this.user.isInsideOrg(),
            selectedItemsCount:    selectedItemsCount,
            maxLayersByMap:        this.user.getMaxLayers(),
            totalShared:           changedContentType ? 0 : this.collection.total_shared,
            totalItems:            changedContentType ? 0 : this.collection.total_user_entries,
            pageItems:             this.collection.size(),
            routerModel:           this.routerModel,
            pluralizedContentType: pluralizeString('dataset', changedContentType ? 0 : this.collection.total_user_entries),
            pluralizedContentTypeSelected: pluralizeString('dataset', selectedItemsCount),
            createFromScratchLabel: this._TEXTS.createFromScratchLabel[createModelType],
            datasetsTabDisabled: this._datasetsTabDisabled()
          },
          this.routerModel.attributes
        )
      )
    );

    if(this._datasetsTabDisabled()) {
      var tooltipView = new cdb.common.TipsyTooltip({
        el: this.$('.js-datasets'),
        title: function () {
          return _t('There are no datasets connected to your account');
        }
      });
      this.addView(tooltipView);
    }

    this._animate();
    if (this.routerModel.isSearching()) {
      this._focusSearchInput();
    }

    return this;
  },

  _initBinds: function() {
    this.model.on('change:isSearchEnabled', this._onChangeIsSearchEnabled, this);
    this.createModel.bind('change:listing', this.render, this);
    this.routerModel.bind('change', this.render, this);
    this.collection.bind('reset', this.render, this);
    cdb.god.bind('closeDialogs', this._animate, this);
    this.add_related_model(cdb.god);
    this.add_related_model(this.createModel);
    this.add_related_model(this.collection);
    this.add_related_model(this.routerModel);
  },

  _onChangeIsSearchEnabled: function(model, isSearchEnabled) {
    this._enableSearchUI(isSearchEnabled);

    if (this.routerModel.isSearching()) {
      this._cleanSearch();
    } else if (isSearchEnabled) {
      this._$searchInput().val('');
      this._focusSearchInput();
    }
  },

  _$searchInput: function() {
    return this.$('.js-search-input')
  },

  _focusSearchInput: function() {
    this._$searchInput()
      .select()
      .focus();
  },

  _onSearchKeyDown: function(e) {
    // ESC
    if (e.keyCode === 27) {
      this.killEvent(e);
      this._cleanSearch();
    }
  },

  _selectedItems: function() {
    return this.collection.where({ selected: true });
  },

  _animate: function() {
    this._enableSearchUI(!!this.routerModel.isSearching());

    // Check if user doesn't have any table and it is in library section
    // to remove useless shadow
    var inLibrarySection = this.routerModel.get('library');
    var inDatasetsSection = this.createModel.get('listing') === "datasets";
    var hasDatasets = this.collection.total_user_entries > 0;
    this.$el.toggleClass('no-shadow', inLibrarySection && !hasDatasets && inDatasetsSection);
  },

  _enableSearchUI: function(enable) {
    this.$('.js-search-field').toggle(enable);
    this.$('.js-links-list').toggleClass('is-hidden', enable);
    this.$('.js-order-list').toggleClass('is-hidden', enable);
  },

  _onDatasetsClick: function() {
    if (this._datasetsTabDisabled()) return;

    this.routerModel.set({
      shared: 'no',
      library: false,
      page: 1
    });
    this.createModel.set('listing', 'datasets');
  },

  _onSharedClick: function() {
    this.routerModel.set({
      shared: 'only',
      library: false,
      page: 1
    });
    this.createModel.set('listing', 'datasets');
  },

  _onLibraryClick: function() {
    this.routerModel.set({
      shared: 'no',
      library: true,
      page: 1
    });
    this.createModel.set('listing', 'datasets');

    // Event tracking "Clicked Common data"
    cdb.god.trigger('metrics', 'common_data', {
      email: window.user_data.email
    });
  },

  _onConnectClick: function() {
    if (this.user.canCreateDatasets()) {
      this.createModel.set('listing', 'import');
    }
  },

  _onCreateEmptyClick: function() {
    if (this.user.canCreateDatasets()) {
      this.createModel.createFromScratch();
    }
  },

  _datasetsTabDisabled: function () {
    return this.routerModel.get('datasetsTabDisabled')
  },

  // Selection actions

  _onSearchClick: function(e) {
    if (e) this.killEvent(e);
    this.model.set('isSearchEnabled', !this.model.get('isSearchEnabled'));
  },

  // Filter actions

  _onCleanSearchClick: function(e) {
    if (e) e.preventDefault();
    this._cleanSearch();
  },

  _cleanSearch: function() {
    this.routerModel.set({
      q: '',
      tag: '',
      shared: 'no',
      library: this.createModel.showLibrary()
    });
    this.model.set('isSearchEnabled', false);
  },

  _submitSearch: function(e) {
    if (e) this.killEvent(e);
    var val = Utils.stripHTML(this.$('.js-search-input').val().trim(),'');
    var tag = val.search(':') === 0 ? val.replace(':', '') : '';
    var q = val.search(':') !== 0 ? val : '';

    this.routerModel.set({
      page: 1,
      tag: tag,
      q: q,
      shared: 'yes'
    });

    this.createModel.set('listing', 'datasets');
  }

});
<commit_msg>Missing semicolon<commit_after>var cdb = require('cartodb.js-v3');
var _ = require('underscore-cdb-v3');
var Utils = require('cdb.Utils');
var pluralizeString = require('../../../view_helpers/pluralize_string');

/**
 *  Listing datasets navigation.
 *
 *  - 'Filter by' datasets.
 *  - 'Search' any pattern within dataset collection.
 *
 */
module.exports = cdb.core.View.extend({

  events: {
    'submit .js-search-form':   '_submitSearch',
    'keydown .js-search-form':  '_onSearchKeyDown',
    'click .js-search-form':    'killEvent',
    'click .js-search-link':    '_onSearchClick',
    'click .js-clean-search':   '_onCleanSearchClick',
    'click .js-shared':         '_onSharedClick',
    'click .js-library':        '_onLibraryClick',
    'click .js-connect':        '_onConnectClick',
    'click .js-datasets':       '_onDatasetsClick',
    'click .js-create_empty':   '_onCreateEmptyClick'
  },

  _TEXTS: {
    createFromScratchLabel: {
      map: 'Create empty map',
      dataset: 'Create empty dataset',
      addLayer: 'Add an empty layer'
    }
  },

  initialize: function() {
    this.routerModel = this.options.routerModel;
    this.createModel = this.options.createModel;
    this.user = this.options.user;
    this.template = cdb.templates.getTemplate('common/views/create/listing/navigation');
    this.model = new cdb.core.Model();

    this._preRender();
    this._initBinds();
  },

  // It is necessary to add two static elements because
  // they can't be removed/replaced using render method
  // each time a change (in a model or a collection) happens.
  // This is due to the behaviour of the CSS animations.
  _preRender: function() {
    var $uInner = $('<div>').addClass('u-inner');
    var $filtersInner = $('<div>').addClass('Filters-inner');
    this.$el.append($uInner.append($filtersInner));
  },

  render: function(m, c) {
    this.clearSubViews();

    var selectedItemsCount = this._selectedItems().length;
    // If a change is made from content type we have to know
    // preventing show wrong data about total items
    var changedContentType = c && c.changes && c.changes.content_type;
    var createModelType = this.createModel.get('type');

    this.$('.Filters-inner').html(
      this.template(
        _.extend({
            createModel:           this.createModel,
            canCreateDataset:      this.user.canCreateDatasets(),
            listing:               this.createModel.get('listing'),
            isInsideOrg:           this.user.isInsideOrg(),
            selectedItemsCount:    selectedItemsCount,
            maxLayersByMap:        this.user.getMaxLayers(),
            totalShared:           changedContentType ? 0 : this.collection.total_shared,
            totalItems:            changedContentType ? 0 : this.collection.total_user_entries,
            pageItems:             this.collection.size(),
            routerModel:           this.routerModel,
            pluralizedContentType: pluralizeString('dataset', changedContentType ? 0 : this.collection.total_user_entries),
            pluralizedContentTypeSelected: pluralizeString('dataset', selectedItemsCount),
            createFromScratchLabel: this._TEXTS.createFromScratchLabel[createModelType],
            datasetsTabDisabled: this._datasetsTabDisabled()
          },
          this.routerModel.attributes
        )
      )
    );

    if(this._datasetsTabDisabled()) {
      var tooltipView = new cdb.common.TipsyTooltip({
        el: this.$('.js-datasets'),
        title: function () {
          return _t('There are no datasets connected to your account');
        }
      });
      this.addView(tooltipView);
    }

    this._animate();
    if (this.routerModel.isSearching()) {
      this._focusSearchInput();
    }

    return this;
  },

  _initBinds: function() {
    this.model.on('change:isSearchEnabled', this._onChangeIsSearchEnabled, this);
    this.createModel.bind('change:listing', this.render, this);
    this.routerModel.bind('change', this.render, this);
    this.collection.bind('reset', this.render, this);
    cdb.god.bind('closeDialogs', this._animate, this);
    this.add_related_model(cdb.god);
    this.add_related_model(this.createModel);
    this.add_related_model(this.collection);
    this.add_related_model(this.routerModel);
  },

  _onChangeIsSearchEnabled: function(model, isSearchEnabled) {
    this._enableSearchUI(isSearchEnabled);

    if (this.routerModel.isSearching()) {
      this._cleanSearch();
    } else if (isSearchEnabled) {
      this._$searchInput().val('');
      this._focusSearchInput();
    }
  },

  _$searchInput: function() {
    return this.$('.js-search-input')
  },

  _focusSearchInput: function() {
    this._$searchInput()
      .select()
      .focus();
  },

  _onSearchKeyDown: function(e) {
    // ESC
    if (e.keyCode === 27) {
      this.killEvent(e);
      this._cleanSearch();
    }
  },

  _selectedItems: function() {
    return this.collection.where({ selected: true });
  },

  _animate: function() {
    this._enableSearchUI(!!this.routerModel.isSearching());

    // Check if user doesn't have any table and it is in library section
    // to remove useless shadow
    var inLibrarySection = this.routerModel.get('library');
    var inDatasetsSection = this.createModel.get('listing') === "datasets";
    var hasDatasets = this.collection.total_user_entries > 0;
    this.$el.toggleClass('no-shadow', inLibrarySection && !hasDatasets && inDatasetsSection);
  },

  _enableSearchUI: function(enable) {
    this.$('.js-search-field').toggle(enable);
    this.$('.js-links-list').toggleClass('is-hidden', enable);
    this.$('.js-order-list').toggleClass('is-hidden', enable);
  },

  _onDatasetsClick: function() {
    if (this._datasetsTabDisabled()) return;

    this.routerModel.set({
      shared: 'no',
      library: false,
      page: 1
    });
    this.createModel.set('listing', 'datasets');
  },

  _onSharedClick: function() {
    this.routerModel.set({
      shared: 'only',
      library: false,
      page: 1
    });
    this.createModel.set('listing', 'datasets');
  },

  _onLibraryClick: function() {
    this.routerModel.set({
      shared: 'no',
      library: true,
      page: 1
    });
    this.createModel.set('listing', 'datasets');

    // Event tracking "Clicked Common data"
    cdb.god.trigger('metrics', 'common_data', {
      email: window.user_data.email
    });
  },

  _onConnectClick: function() {
    if (this.user.canCreateDatasets()) {
      this.createModel.set('listing', 'import');
    }
  },

  _onCreateEmptyClick: function() {
    if (this.user.canCreateDatasets()) {
      this.createModel.createFromScratch();
    }
  },

  _datasetsTabDisabled: function () {
    return this.routerModel.get('datasetsTabDisabled');
  },

  // Selection actions

  _onSearchClick: function(e) {
    if (e) this.killEvent(e);
    this.model.set('isSearchEnabled', !this.model.get('isSearchEnabled'));
  },

  // Filter actions

  _onCleanSearchClick: function(e) {
    if (e) e.preventDefault();
    this._cleanSearch();
  },

  _cleanSearch: function() {
    this.routerModel.set({
      q: '',
      tag: '',
      shared: 'no',
      library: this.createModel.showLibrary()
    });
    this.model.set('isSearchEnabled', false);
  },

  _submitSearch: function(e) {
    if (e) this.killEvent(e);
    var val = Utils.stripHTML(this.$('.js-search-input').val().trim(),'');
    var tag = val.search(':') === 0 ? val.replace(':', '') : '';
    var q = val.search(':') !== 0 ? val : '';

    this.routerModel.set({
      page: 1,
      tag: tag,
      q: q,
      shared: 'yes'
    });

    this.createModel.set('listing', 'datasets');
  }

});
<|endoftext|>


<commit_before>import PaletteGenerator from '@/components/PaletteGenerator';

const routes = [
  {
    path: '/',
    name: 'PaletteGenerator',
    component: PaletteGenerator,
  },
  {
    path: '/:hex',
    name: 'PaletteGenerator',
    component: PaletteGenerator,
    props: true,
  },
];

export default routes;
<commit_msg>[Fix] Remove clashing names props in routes array<commit_after>import PaletteGenerator from '@/components/PaletteGenerator';

const routes = [
  {
    path: '/',
    component: PaletteGenerator,
  },
  {
    path: '/:hex',
    component: PaletteGenerator,
    props: true,
  },
];

export default routes;
<|endoftext|>


<commit_before>export TrackRouter from './track-router.js';
export FetchRouter from './fetch-router.js';
<commit_msg>fixed syntax error for exporting routers<commit_after>import TrackRouter from './track-router';
import FetchRouter from './fetch-router';

export default {
  TrackRouter,
  FetchRouter
}
<|endoftext|>


<commit_before>describeMixin("painters/withBrushPainter", function(){

  beforeEach(function(){
    setupComponent();
  });

  describe("Brush Property Events", function(){

    beforeEach(function(){
      $('.component-root').trigger("brushPropertyUpdated", {
        key: "fillColor",
        oldValue: "green",
        newValue: "red"
      });
    });

    it("Should have changed brush property", function(){
      expect(this.component.attr.prop.fillColor).toEqual("red");
    });

  });

  describe("Brush Painting Flow", function(){

  });

});<commit_msg>property not changed tested<commit_after>describeMixin("painters/withBrushPainter", function(){

  beforeEach(function(){
    setupComponent();
  });

  describe("Brush Property Events", function(){

    beforeEach(function(){
      $('.component-root').trigger("brushPropertyUpdated", {
        key: "fillColor",
        oldValue: "green",
        newValue: "red"
      });
    });

    it("Should have changed brush property", function(){
      expect(this.component.attr.prop.fillColor).toEqual("red");
    });

    it("Should not have changed the brush property if the event data aren't complete", function(){
      $('.component-root').trigger("brushPropertyUpdated", {
        key: "strokeColor"
      });

      expect(this.component.attr.prop.strokeColor).toEqual("#000000");
    });

  });

  describe("Brush Painting Flow", function(){

  });

});<|endoftext|>


<commit_before>/*
 * mobile checkboxradio unit tests
 */
(function($){
	module( 'vertical controlgroup, no refresh' , {
		setup: function() {
			this.vcontrolgroup = $( "#vertical-controlgroup" );
		}
	});

	test( "vertical controlgroup classes", function() {
		var buttons = this.vcontrolgroup.find( ".ui-btn" ),
			middlebuttons = buttons.filter(function(index) { return index > 0 && index < (length-1)}),
			length = buttons.length;

		ok( !buttons.hasClass( "ui-btn-corner-all" ), "no button should have class 'ui-btn-corner-all'");
		ok( buttons.first().hasClass( "ui-corner-top" ), "first button should have class 'ui-corner-top'" );
		ok( !middlebuttons.hasClass( "ui-corner-top" ), "middle buttons should not have class 'ui-corner-top'" );
		ok( !middlebuttons.hasClass( "ui-corner-bottom" ), "middle buttons should not have class 'ui-corner-bottom'" );
		ok( buttons.last().hasClass( "ui-corner-bottom"), "last button should have class 'ui-corner-bottom'" );
	});

	module( 'vertical controlgroup, refresh', {
		setup: function() {
			this.vcontrolgroup = $( "#vertical-controlgroup" );
			this.vcontrolgroup.find( ".ui-btn" ).show();
			this.vcontrolgroup.controlgroup();
		}
	});

	test( "vertical controlgroup after first button was hidden", function() {
		//https://github.com/jquery/jquery-mobile/issues/1929

		//We hide the first button and refresh
		this.vcontrolgroup.find( ".ui-btn" ).first().hide();
		this.vcontrolgroup.controlgroup();

		var buttons = this.vcontrolgroup.find( ".ui-btn" ).filter( ":visible" ),
			middlebuttons = buttons.filter(function(index) { return index > 0 && index < (length-1)}),
			length = buttons.length;

		ok( buttons.first().hasClass( "ui-corner-top" ), "first visible button should have class 'ui-corner-top'" );
		ok( !middlebuttons.hasClass( "ui-corner-top" ), "middle buttons should not have class 'ui-corner-top'" );
		ok( !middlebuttons.hasClass( "ui-corner-bottom" ), "middle buttons should not have class 'ui-corner-bottom'" );
		ok( buttons.last().hasClass( "ui-corner-bottom"), "last visible button should have class 'ui-corner-bottom'" );
	});

	test( "vertical controlgroup after last button was hidden", function() {
		//https://github.com/jquery/jquery-mobile/issues/1929

		//We hide the last button and refresh
		this.vcontrolgroup.find( ".ui-btn" ).last().hide();
		this.vcontrolgroup.controlgroup();

		var buttons = this.vcontrolgroup.find( ".ui-btn" ).filter( ":visible" ),
			middlebuttons = buttons.filter(function(index) { return index > 0 && index < (length-1)}),
			length = buttons.length;

		ok( buttons.first().hasClass( "ui-corner-top" ), "first visible button should have class 'ui-corner-top'" );
		ok( !middlebuttons.hasClass( "ui-corner-top" ), "middle buttons should not have class 'ui-corner-top'" );
		ok( !middlebuttons.hasClass( "ui-corner-bottom" ), "middle buttons should not have class 'ui-corner-bottom'" );
		ok( buttons.last().hasClass( "ui-corner-bottom"), "last visible button should have class 'ui-corner-bottom'" );
	});

	module( 'horizontal controlgroup, no refresh', {
		setup: function() {
			this.hcontrolgroup = $( "#horizontal-controlgroup" );
		}
	});

	test( "horizontal controlgroup classes", function() {
		var buttons = this.hcontrolgroup.find( ".ui-btn" ),
			middlebuttons = buttons.filter(function(index) { return index > 0 && index < (length-1)}),
			length = buttons.length;

		ok( !buttons.hasClass( "ui-btn-corner-all" ), "no button should have class 'ui-btn-corner-all'");
		ok( buttons.first().hasClass( "ui-corner-left" ), "first button should have class 'ui-corner-left'" );
		ok( !middlebuttons.hasClass( "ui-corner-left" ), "middle buttons should not have class 'ui-corner-left'" );
		ok( !middlebuttons.hasClass( "ui-corner-right" ), "middle buttons should not have class 'ui-corner-right'" );
		ok( buttons.last().hasClass( "ui-corner-right"), "last button should have class 'ui-corner-right'" );
	});

	module( 'horizontal controlgroup, refresh', {
		setup: function() {
			this.hcontrolgroup = $( "#horizontal-controlgroup" );
			this.hcontrolgroup.find( ".ui-btn" ).show();
			this.hcontrolgroup.controlgroup();
		}
	});

	test( "horizontal controlgroup after first button was hidden", function() {
		//We hide the first button and refresh
		this.hcontrolgroup.find( ".ui-btn" ).first().hide();
		this.hcontrolgroup.controlgroup();

		var buttons = this.hcontrolgroup.find( ".ui-btn" ).filter( ":visible" ),
			middlebuttons = buttons.filter(function(index) { return index > 0 && index < (length-1)}),
			length = buttons.length;

		ok( buttons.first().hasClass( "ui-corner-left" ), "first visible button should have class 'ui-corner-left'" );
		ok( !middlebuttons.hasClass( "ui-corner-left" ), "middle buttons should not have class 'ui-corner-left'" );
		ok( !middlebuttons.hasClass( "ui-corner-right" ), "middle buttons should not have class 'ui-corner-right'" );
		ok( buttons.last().hasClass( "ui-corner-right"), "last visible button should have class 'ui-corner-right'" );
	});

	test( "horizontal controlgroup after last button was hidden", function() {
		//We hide the last button and refresh
		this.hcontrolgroup.find( ".ui-btn" ).last().hide();
		this.hcontrolgroup.controlgroup();

		var buttons = this.hcontrolgroup.find( ".ui-btn" ).filter( ":visible" ),
			middlebuttons = buttons.filter(function(index) { return index > 0 && index < (length-1)}),
			length = buttons.length;

		ok( buttons.first().hasClass( "ui-corner-left" ), "first visible button should have class 'ui-corner-left'" );
		ok( !middlebuttons.hasClass( "ui-corner-left" ), "middle buttons should not have class 'ui-corner-left'" );
		ok( !middlebuttons.hasClass( "ui-corner-right" ), "middle buttons should not have class 'ui-corner-right'" );
		ok( buttons.last().hasClass( "ui-corner-right"), "last visible button should have class 'ui-corner-right'" );
	});

	test( "controlgroups will create when inside a container that receives a 'create' event", function(){
		ok( !$("#enhancetest").appendTo(".ui-page-active").find(".ui-controlgroup").length, "did not have enhancements applied" );
		ok( $("#enhancetest").trigger("create").find(".ui-controlgroup").length, "enhancements applied" );
	});

	test( "controlgroups in ignored containers aren't enhanced", function() {
		var $unenhancedFieldSet = $( "#unenhanced-fieldset" ),
			$enhancedFieldSet = $( "#enhanced-fieldset" );

		$.mobile.ignoreContentEnabled = true;

		// attempt to enhance the controlgroup
		$unenhancedFieldSet.parent().trigger("create");

		deepEqual( $unenhancedFieldSet.length, 1, "the fieldset test fixtures exist" );
		ok( !$unenhancedFieldSet.is(".ui-controlgroup"), "there is no control group" );

		// attempt to enhance the controlgroup
		$enhancedFieldSet.parent().trigger("create");

		deepEqual( $enhancedFieldSet.length, 1, "the fieldset test fixtures exist" );
		ok( $enhancedFieldSet.is(".ui-controlgroup"), "there is a control group" );

		$.mobile.ignoreContentEnabled = false;
	});
})(jQuery);
<commit_msg>[controlgroup tests] The new way to refresh a controlgroup is not to recreate it, but to call its refresh() method - let the unit tests reflect that<commit_after>/*
 * mobile checkboxradio unit tests
 */
(function($){
	module( 'vertical controlgroup, no refresh' , {
		setup: function() {
			this.vcontrolgroup = $( "#vertical-controlgroup" );
		}
	});

	test( "vertical controlgroup classes", function() {
		var buttons = this.vcontrolgroup.find( ".ui-btn" ),
			middlebuttons = buttons.filter(function(index) { return index > 0 && index < (length-1)}),
			length = buttons.length;

		ok( !buttons.hasClass( "ui-btn-corner-all" ), "no button should have class 'ui-btn-corner-all'");
		ok( buttons.first().hasClass( "ui-corner-top" ), "first button should have class 'ui-corner-top'" );
		ok( !middlebuttons.hasClass( "ui-corner-top" ), "middle buttons should not have class 'ui-corner-top'" );
		ok( !middlebuttons.hasClass( "ui-corner-bottom" ), "middle buttons should not have class 'ui-corner-bottom'" );
		ok( buttons.last().hasClass( "ui-corner-bottom"), "last button should have class 'ui-corner-bottom'" );
	});

	module( 'vertical controlgroup, refresh', {
		setup: function() {
			this.vcontrolgroup = $( "#vertical-controlgroup" );
			this.vcontrolgroup.find( ".ui-btn" ).show();
			this.vcontrolgroup.controlgroup( "refresh" );
		}
	});

	test( "vertical controlgroup after first button was hidden", function() {
		//https://github.com/jquery/jquery-mobile/issues/1929

		//We hide the first button and refresh
		this.vcontrolgroup.find( ".ui-btn" ).first().hide();
		this.vcontrolgroup.controlgroup( "refresh" );

		var buttons = this.vcontrolgroup.find( ".ui-btn" ).filter( ":visible" ),
			middlebuttons = buttons.filter(function(index) { return index > 0 && index < (length-1)}),
			length = buttons.length;

		ok( buttons.first().hasClass( "ui-corner-top" ), "first visible button should have class 'ui-corner-top'" );
		ok( !middlebuttons.hasClass( "ui-corner-top" ), "middle buttons should not have class 'ui-corner-top'" );
		ok( !middlebuttons.hasClass( "ui-corner-bottom" ), "middle buttons should not have class 'ui-corner-bottom'" );
		ok( buttons.last().hasClass( "ui-corner-bottom"), "last visible button should have class 'ui-corner-bottom'" );
	});

	test( "vertical controlgroup after last button was hidden", function() {
		//https://github.com/jquery/jquery-mobile/issues/1929

		//We hide the last button and refresh
		this.vcontrolgroup.find( ".ui-btn" ).last().hide();
		this.vcontrolgroup.controlgroup( "refresh" );

		var buttons = this.vcontrolgroup.find( ".ui-btn" ).filter( ":visible" ),
			middlebuttons = buttons.filter(function(index) { return index > 0 && index < (length-1)}),
			length = buttons.length;

		ok( buttons.first().hasClass( "ui-corner-top" ), "first visible button should have class 'ui-corner-top'" );
		ok( !middlebuttons.hasClass( "ui-corner-top" ), "middle buttons should not have class 'ui-corner-top'" );
		ok( !middlebuttons.hasClass( "ui-corner-bottom" ), "middle buttons should not have class 'ui-corner-bottom'" );
		ok( buttons.last().hasClass( "ui-corner-bottom"), "last visible button should have class 'ui-corner-bottom'" );
	});

	module( 'horizontal controlgroup, no refresh', {
		setup: function() {
			this.hcontrolgroup = $( "#horizontal-controlgroup" );
		}
	});

	test( "horizontal controlgroup classes", function() {
		var buttons = this.hcontrolgroup.find( ".ui-btn" ),
			middlebuttons = buttons.filter(function(index) { return index > 0 && index < (length-1)}),
			length = buttons.length;

		ok( !buttons.hasClass( "ui-btn-corner-all" ), "no button should have class 'ui-btn-corner-all'");
		ok( buttons.first().hasClass( "ui-corner-left" ), "first button should have class 'ui-corner-left'" );
		ok( !middlebuttons.hasClass( "ui-corner-left" ), "middle buttons should not have class 'ui-corner-left'" );
		ok( !middlebuttons.hasClass( "ui-corner-right" ), "middle buttons should not have class 'ui-corner-right'" );
		ok( buttons.last().hasClass( "ui-corner-right"), "last button should have class 'ui-corner-right'" );
	});

	module( 'horizontal controlgroup, refresh', {
		setup: function() {
			this.hcontrolgroup = $( "#horizontal-controlgroup" );
			this.hcontrolgroup.find( ".ui-btn" ).show();
			this.hcontrolgroup.controlgroup( "refresh" );
		}
	});

	test( "horizontal controlgroup after first button was hidden", function() {
		//We hide the first button and refresh
		this.hcontrolgroup.find( ".ui-btn" ).first().hide();
		this.hcontrolgroup.controlgroup( "refresh" );

		var buttons = this.hcontrolgroup.find( ".ui-btn" ).filter( ":visible" ),
			middlebuttons = buttons.filter(function(index) { return index > 0 && index < (length-1)}),
			length = buttons.length;

		ok( buttons.first().hasClass( "ui-corner-left" ), "first visible button should have class 'ui-corner-left'" );
		ok( !middlebuttons.hasClass( "ui-corner-left" ), "middle buttons should not have class 'ui-corner-left'" );
		ok( !middlebuttons.hasClass( "ui-corner-right" ), "middle buttons should not have class 'ui-corner-right'" );
		ok( buttons.last().hasClass( "ui-corner-right"), "last visible button should have class 'ui-corner-right'" );
	});

	test( "horizontal controlgroup after last button was hidden", function() {
		//We hide the last button and refresh
		this.hcontrolgroup.find( ".ui-btn" ).last().hide();
		this.hcontrolgroup.controlgroup( "refresh" );

		var buttons = this.hcontrolgroup.find( ".ui-btn" ).filter( ":visible" ),
			middlebuttons = buttons.filter(function(index) { return index > 0 && index < (length-1)}),
			length = buttons.length;

		ok( buttons.first().hasClass( "ui-corner-left" ), "first visible button should have class 'ui-corner-left'" );
		ok( !middlebuttons.hasClass( "ui-corner-left" ), "middle buttons should not have class 'ui-corner-left'" );
		ok( !middlebuttons.hasClass( "ui-corner-right" ), "middle buttons should not have class 'ui-corner-right'" );
		ok( buttons.last().hasClass( "ui-corner-right"), "last visible button should have class 'ui-corner-right'" );
	});

	test( "controlgroups will create when inside a container that receives a 'create' event", function(){
		ok( !$("#enhancetest").appendTo(".ui-page-active").find(".ui-controlgroup").length, "did not have enhancements applied" );
		ok( $("#enhancetest").trigger("create").find(".ui-controlgroup").length, "enhancements applied" );
	});

	test( "controlgroups in ignored containers aren't enhanced", function() {
		var $unenhancedFieldSet = $( "#unenhanced-fieldset" ),
			$enhancedFieldSet = $( "#enhanced-fieldset" );

		$.mobile.ignoreContentEnabled = true;

		// attempt to enhance the controlgroup
		$unenhancedFieldSet.parent().trigger("create");

		deepEqual( $unenhancedFieldSet.length, 1, "the fieldset test fixtures exist" );
		ok( !$unenhancedFieldSet.is(".ui-controlgroup"), "there is no control group" );

		// attempt to enhance the controlgroup
		$enhancedFieldSet.parent().trigger("create");

		deepEqual( $enhancedFieldSet.length, 1, "the fieldset test fixtures exist" );
		ok( $enhancedFieldSet.is(".ui-controlgroup"), "there is a control group" );

		$.mobile.ignoreContentEnabled = false;
	});
})(jQuery);
<|endoftext|>


<commit_before>
(function (document) {

  var NO_ENDING_TAG = ['br', 'col', 'link', 'hr', 'command',
        'embed', 'img', 'input', 'meta', 'param', 'source'];

  /** Main document constructor
   */
  function Document(document) {
    this.document = document;

    this.elemCache = [];
    this.nodeCache = [];
  }

  // export constructor function
  var enterpoint = function (doc) { return new Document(doc); };
  enterpoint.Document = Document;
  enterpoint.Search = Search;
  enterpoint.Node = Node,
  enterpoint.NO_ENDING_TAG;

  if (typeof module === 'object' && module !== null && module.exports === exports) {
    module.exports = enterpoint;
  } else {
    this.domstream = enterpoint;
  }

  // bind with search constructor
  Document.prototype.find = function () {
    return new Search(this, this.document);
  };

  /** Search constructor
   */
  function Search(document, tree) {
    this.document = document;
    this.tree = tree;

    this.onlyFlag = false;
    this.onlySearch = false;

    this.searchFilled = false;
    this.searchList = {
      'id': null,
      'tag': null,
      'attr': []
    };
    this.nodeList = [];
  }

  Search.prototype._prepearMatch = function () {
    // throw if more search requests are made
    if (this.onlySearch) {
      throw new Error('can not add search criterias after .toArray or .toValue was called with .only');
    }
  };

  Search.prototype.elem = function (tagname) {
    this._prepearMatch();
    this.searchFilled = true;

    var searchList = this.searchList;

    // set tagname
    if (searchList.tag === null) {
      searchList.tag = tagname;
    }

    // An element can only have one tagname
    // using diffrent tagnames should return nothing
    else if (searchList.tag !== tagname) {
      searchList.tag = false;
    }

    return this;
  };

  Search.prototype.attr = function (name, match) {
    this._prepearMatch();
    this.searchFilled = true;

    var searchList = this.searchList;
    var query = searchList.attr;

    if (typeof name !== 'string') {
      throw new Error('Could not understand arguments');
    } else if (match === undefined) {
      query.push(function (elem) {
        return elem.hasAttribute(name);
      });
    } else if (typeof match === 'string') {

      if (name !== 'id') {
        query.push(function (elem) {
          return elem.getAttribute(name) === match;
        });
      }

      // set id
      else if (searchList.id === null) {
        searchList.id = match;
      }

      // An element can only have one id
      // using diffrent ids should return nothing
      else if (searchList.id !== match) {
        searchList.id = false;
      }

    } else if (match instanceof RegExp) {
      query.push(function (elem) {
        return elem.hasAttribute(name) && match.test( elem.getAttribute(name) );
      });
    } else {
      throw new Error('Could not understand arguments');
    }

    return this;
  };

  Search.prototype.only = function () {
    this.onlyFlag = true;
    return this;
  };

  Search.prototype.toArray = function () {
    if (this.searchFilled) {
      this.nodeList = performSearch(this);
      this.filled = true;
      this.searchFilled = false;
      this.searchList = {
        'id': null,
        'tag': null,
        'attr': []
      };

      // throw if more search requests are made
      if (this.onlyFlag) {
        this.onlySearch = true;
      }
    }

    // convert result to real nodes
    var realNodes = this.onlyFlag ? this.nodeList.slice(0, 1) : this.nodeList.slice(0);

    var map = [];
    for (var i = 0, l = realNodes.length; i < l; i++) {
      map.push(Node.create(this.document, realNodes[i]));
    }

    return map;
  };

  Search.prototype.toValue = function () {
    var result = this.toArray();

    if (result.length === 0) {
      return false;
    } else if (this.onlyFlag) {
      return result.shift();
    } else {
      return result;
    }
  };

  function performSearch(search) {
    var searchList = search.searchList;
    var only = search.onlyFlag;
    var temp = null;

    if (searchList.id === false || searchList.tag === false) return [];

    if (search.filled) {
      return listSearch(search.nodeList, only, search.searchList);
    }

    // search by id
    if (searchList.id) {
      temp = search.tree.getElementById(searchList.id);
      if (temp === null) return [];

      return listSearch([temp], only, {
        id: null,
        tag: searchList.tag,
        attr: searchList.attr
      });
    }

    // search by tagname
    if (searchList.tag) {
      temp = search.tree.getElementsByTagName(searchList.tag);

      return listSearch(temp, only, {
        id: null,
        tag: null,
        attr: searchList.attr
      });
    }

    // search tree
    temp = [];
    searchChildrens(searchList.attr, search.tree, temp, search.onlyFlag);
    return temp;
  }

  function listSearch(list, only, search) {
    var query = search.attr.slice(0);

    // Add id search query
    if (search.id) {
      query.unshift(function (elem) {
        return elem.getAttribute('id', search.id);
      });
    }

    // Add tagname search query
    if (search.tag) {
      query.unshift(function (elem) {
        return elem.tagName.toLowerCase() === search.tag;
      });
    }

    // perform search
    var result = [];
    for (var i = 0, l = list.length; i < l; i++) {
      if (doPass(query, list[i])) {
        result.push(list[i]);
        if (only) return result;
      }
    }

    return result;
  }

  function searchChildrens(query, elem, result, only) {
    var next = elem.firstChild;
    while (next !== null) {
      // we only want elements
      if (next.nodeType === 1) {

        // add elements there match to result list
        if (doPass(query, next)) {
          result.push(next);
          if (only) return true;
        }

        // deep resolve children
        var stop = searchChildrens(query, next, result, only);
        if (stop) return true;

      }

      next = next.nextSibling;
    }

    return false;
  }

  function doPass(query, elem) {
    var i = 0, l = query.length;

    // search in normal order, since its likly that will be the optimised order
    // example: .elem('tag').attr('value', /hallo/) would be a shame to do backwards
    for (; i < l; i++) {
      if (query[i](elem) === false) {
        return false;
      }
    }

    return true;
  }

  /** Node constructor
   */

  function Node(document, elem) {
    this.document = document;
    this.elem = elem;

    // precalculate these, since they in any case will be used
    this._isRoot = elem === document;

    if (this._isRoot === false) {
      this._tagName = elem.tagName.toLowerCase();
      this._isSingleton = NO_ENDING_TAG.indexOf(this._tagName) !== -1;
    } else {
      this._tagName = '';
      this._isSingleton = false;
    }

    this.isChunked = false;
    this.isContainer = false;

    // allow node object to be reused
    document.elemCache.push(elem);
    document.nodeCache.push(this);
  }

  // this function will check the cache and
  // return a new Node object if it don't exist
  // in cache, or return the cached Node object.
  Node.create = function (document, elem) {
    // check cache
    var index = document.elemCache.indexOf(elem);

    // not cached, create new
    if (index === -1) {
      return new Node(document, elem);
    }

    // return cache
    return document.nodeCache[index];
  };

  Node.prototype.tagName = function () {
    if (this._isRoot) throw new Error('root has no tagname');

    return this._tagName;
  };

  Node.prototype.isSingleton = function () {
    return this._isSingleton;
  };

  Node.prototype.isRoot = function () {
    return this._isRoot;
  };

  Node.prototype.getParent = function () {
    if (this.isRoot) throw new Error('root has no parent');

    return Node.create(this.document, this.elem.parentNode);
  };

  Node.prototype.getChildren = function () {
    if (this._isSingleton) throw new Error('singleton element has no children');

    var next = this.elem.firstChild;
    var nodes = [];
    while (next !== null) {
      if (next.nodeType === 1) {
        nodes.push(Node.create(this.document, next));
      }

      next = next.nextSibling;
    }

    return nodes;
  };

  Node.prototype.isParentTo = function (node) {
    var parent = node.elem.parentNode;

    return !!parent && this.elem === parent;
  };

  Node.prototype.done = function () {
    return this;
  };

  Node.prototype.insert = function (where, content) {
    var elem = this.elem;

    if (elem.insertAdjacentHTML) {
      elem.insertAdjacentHTML(where, content);
      return this;
    }

    // use createContextualFragment as fallback
    /*
    var fragment = document.createRange().createContextualFragment(content);

    switch (where) {
      case "beforebegin":
        elem.parentNode.insertBefore(fragment, elem);
        break;
      case "afterbegin":
        elem.insertBefore(fragment, elem.firstChild);
        break;
      case "beforeend":
        elem.appendChild(fragment);
        break;
      case "afterend":
        elem.parentNode.insertBefore(fragment, elem.nextSibling);
        break;
      default:
        throw new Error('did not understand first argument');
    }
    */

    return this;
  };

  Node.prototype.append = function (content) {
    this.insert('beforeend', content);

    return this;
  };

  Node.prototype.remove = function () {
    if (this._isRoot) throw new Error('can not remove root element element');

    this.elem.parentNode.removeElement(this.elem);

    return this;
  };

  Node.prototype.trim = function () {
    if (this._isSingleton) throw new Error('can not remove content from singleton element');

    var elem = this.elem;
    while(elem.firstChild) {
      elem.removeChild(elem.firstChild);
    }

    return this;
  };

  Node.prototype.getContent = function () {
    return this.elem.innerHTML;
  };

  Node.prototype.setContent = function (content) {
    this.trim();
    this.insert('afterbegin', content);
    return this;
  };

  Node.prototype.getAttr = function (name) {
    return this.elem.getAttribute(name);
  };

  Node.prototype.hasAttr = function (name) {
    return this.elem.hasAttribute(name);
  };

  Node.prototype.setAttr = function (name, value) {
    if (this._isRoot) throw new Error('can not set attribute on root element');

    this.elem.setAttribute(name, value);
    return this;
  };

  Node.prototype.removeAttr = function (name) {
    if (this._isRoot) throw new Error('can not remove attribute from root element');

    this.elem.removeAttribute(name);
    return this;
  };

  Node.prototype.find = function () {
    return new Search(this.document, this.elem);
  };

})(this.document);
<commit_msg>[fix] bug detected by node-simple<commit_after>
(function (document) {

  var NO_ENDING_TAG = ['br', 'col', 'link', 'hr', 'command',
        'embed', 'img', 'input', 'meta', 'param', 'source'];

  /** Main document constructor
   */
  function Document(document) {
    this.document = document;

    this.elemCache = [];
    this.nodeCache = [];
  }

  // export constructor function
  var enterpoint = function (doc) { return new Document(doc); };
  enterpoint.Document = Document;
  enterpoint.Search = Search;
  enterpoint.Node = Node,
  enterpoint.NO_ENDING_TAG;

  if (typeof module === 'object' && module !== null && module.exports === exports) {
    module.exports = enterpoint;
  } else {
    this.domstream = enterpoint;
  }

  // bind with search constructor
  Document.prototype.find = function () {
    return new Search(this, this.document);
  };

  /** Search constructor
   */
  function Search(document, tree) {
    this.document = document;
    this.tree = tree;

    this.onlyFlag = false;
    this.onlySearch = false;

    this.searchFilled = false;
    this.searchList = {
      'id': null,
      'tag': null,
      'attr': []
    };
    this.nodeList = [];
  }

  Search.prototype._prepearMatch = function () {
    // throw if more search requests are made
    if (this.onlySearch) {
      throw new Error('can not add search criterias after .toArray or .toValue was called with .only');
    }
  };

  Search.prototype.elem = function (tagname) {
    this._prepearMatch();
    this.searchFilled = true;

    var searchList = this.searchList;

    // set tagname
    if (searchList.tag === null) {
      searchList.tag = tagname;
    }

    // An element can only have one tagname
    // using diffrent tagnames should return nothing
    else if (searchList.tag !== tagname) {
      searchList.tag = false;
    }

    return this;
  };

  Search.prototype.attr = function (name, match) {
    this._prepearMatch();
    this.searchFilled = true;

    var searchList = this.searchList;
    var query = searchList.attr;

    if (typeof name !== 'string') {
      throw new Error('Could not understand arguments');
    } else if (match === undefined) {
      query.push(function (elem) {
        return elem.hasAttribute(name);
      });
    } else if (typeof match === 'string') {

      if (name !== 'id') {
        query.push(function (elem) {
          return elem.getAttribute(name) === match;
        });
      }

      // set id
      else if (searchList.id === null) {
        searchList.id = match;
      }

      // An element can only have one id
      // using diffrent ids should return nothing
      else if (searchList.id !== match) {
        searchList.id = false;
      }

    } else if (match instanceof RegExp) {
      query.push(function (elem) {
        return elem.hasAttribute(name) && match.test( elem.getAttribute(name) );
      });
    } else {
      throw new Error('Could not understand arguments');
    }

    return this;
  };

  Search.prototype.only = function () {
    this.onlyFlag = true;
    return this;
  };

  Search.prototype.toArray = function () {
    if (this.searchFilled) {
      this.nodeList = performSearch(this);
      this.filled = true;
      this.searchFilled = false;
      this.searchList = {
        'id': null,
        'tag': null,
        'attr': []
      };

      // throw if more search requests are made
      if (this.onlyFlag) {
        this.onlySearch = true;
      }
    }

    // convert result to real nodes
    var realNodes = this.onlyFlag ? this.nodeList.slice(0, 1) : this.nodeList.slice(0);

    var map = [];
    for (var i = 0, l = realNodes.length; i < l; i++) {
      map.push(Node.create(this.document, realNodes[i]));
    }

    return map;
  };

  Search.prototype.toValue = function () {
    var result = this.toArray();

    if (result.length === 0) {
      return false;
    } else if (this.onlyFlag) {
      return result.shift();
    } else {
      return result;
    }
  };

  function performSearch(search) {
    var searchList = search.searchList;
    var only = search.onlyFlag;
    var temp = null;

    if (searchList.id === false || searchList.tag === false) return [];

    if (search.filled) {
      return listSearch(search.nodeList, only, search.searchList);
    }

    // search by id
    if (searchList.id) {
      temp = search.tree.getElementById(searchList.id);
      if (temp === null) return [];

      return listSearch([temp], only, {
        id: null,
        tag: searchList.tag,
        attr: searchList.attr
      });
    }

    // search by tagname
    if (searchList.tag) {
      temp = search.tree.getElementsByTagName(searchList.tag);

      return listSearch(temp, only, {
        id: null,
        tag: null,
        attr: searchList.attr
      });
    }

    // search tree
    temp = [];
    searchChildrens(searchList.attr, search.tree, temp, search.onlyFlag);
    return temp;
  }

  function listSearch(list, only, search) {
    var query = search.attr.slice(0);

    // Add id search query
    if (search.id) {
      query.unshift(function (elem) {
        return elem.getAttribute('id', search.id);
      });
    }

    // Add tagname search query
    if (search.tag) {
      query.unshift(function (elem) {
        return elem.tagName.toLowerCase() === search.tag;
      });
    }

    // perform search
    var result = [];
    for (var i = 0, l = list.length; i < l; i++) {
      if (doPass(query, list[i])) {
        result.push(list[i]);
        if (only) return result;
      }
    }

    return result;
  }

  function searchChildrens(query, elem, result, only) {
    var next = elem.firstChild;
    while (next !== null) {
      // we only want elements
      if (next.nodeType === 1) {

        // add elements there match to result list
        if (doPass(query, next)) {
          result.push(next);
          if (only) return true;
        }

        // deep resolve children
        var stop = searchChildrens(query, next, result, only);
        if (stop) return true;

      }

      next = next.nextSibling;
    }

    return false;
  }

  function doPass(query, elem) {
    var i = 0, l = query.length;

    // search in normal order, since its likly that will be the optimised order
    // example: .elem('tag').attr('value', /hallo/) would be a shame to do backwards
    for (; i < l; i++) {
      if (query[i](elem) === false) {
        return false;
      }
    }

    return true;
  }

  /** Node constructor
   */

  function Node(document, elem) {
    this.document = document;
    this.elem = elem;

    // precalculate these, since they in any case will be used
    this._isRoot = elem.nodeType === 9;

    if (this._isRoot === false) {
      this._tagName = elem.tagName.toLowerCase();
      this._isSingleton = NO_ENDING_TAG.indexOf(this._tagName) !== -1;
    } else {
      this._tagName = '';
      this._isSingleton = false;
    }

    this.isChunked = false;
    this.isContainer = false;

    // allow node object to be reused
    document.elemCache.push(elem);
    document.nodeCache.push(this);
  }

  // this function will check the cache and
  // return a new Node object if it don't exist
  // in cache, or return the cached Node object.
  Node.create = function (document, elem) {
    // check cache
    var index = document.elemCache.indexOf(elem);

    // not cached, create new
    if (index === -1) {
      return new Node(document, elem);
    }

    // return cache
    return document.nodeCache[index];
  };

  Node.prototype.tagName = function () {
    if (this._isRoot) throw new Error('root has no tagname');

    return this._tagName;
  };

  Node.prototype.isSingleton = function () {
    return this._isSingleton;
  };

  Node.prototype.isRoot = function () {
    return this._isRoot;
  };

  Node.prototype.getParent = function () {
    if (this._isRoot) throw new Error('root has no parent');

    return Node.create(this.document, this.elem.parentNode);
  };

  Node.prototype.getChildren = function () {
    if (this._isSingleton) throw new Error('singleton element has no children');

    var next = this.elem.firstChild;
    var nodes = [];
    while (next !== null) {
      if (next.nodeType === 1) {
        nodes.push(Node.create(this.document, next));
      }

      next = next.nextSibling;
    }

    return nodes;
  };

  Node.prototype.isParentTo = function (node) {
    var parent = node.elem.parentNode;

    return !!parent && this.elem === parent;
  };

  Node.prototype.done = function () {
    return this;
  };

  Node.prototype.insert = function (where, content) {
    var elem = this.elem;

    if (elem.insertAdjacentHTML) {
      elem.insertAdjacentHTML(where, content);
      return this;
    }

    // use createContextualFragment as fallback
    /*
    var fragment = document.createRange().createContextualFragment(content);

    switch (where) {
      case "beforebegin":
        elem.parentNode.insertBefore(fragment, elem);
        break;
      case "afterbegin":
        elem.insertBefore(fragment, elem.firstChild);
        break;
      case "beforeend":
        elem.appendChild(fragment);
        break;
      case "afterend":
        elem.parentNode.insertBefore(fragment, elem.nextSibling);
        break;
      default:
        throw new Error('did not understand first argument');
    }
    */

    return this;
  };

  Node.prototype.append = function (content) {
    this.insert('beforeend', content);

    return this;
  };

  Node.prototype.remove = function () {
    if (this._isRoot) throw new Error('can not remove root element element');

    this.elem.parentNode.removeElement(this.elem);

    return this;
  };

  Node.prototype.trim = function () {
    if (this._isSingleton) throw new Error('can not remove content from singleton element');

    var elem = this.elem;
    while(elem.firstChild) {
      elem.removeChild(elem.firstChild);
    }

    return this;
  };

  Node.prototype.getContent = function () {
    if (this._isSingleton) throw new Error('can not get content from singleton element');

    return this.elem.innerHTML;
  };

  Node.prototype.setContent = function (content) {
    this.trim();
    this.insert('afterbegin', content);
    return this;
  };

  Node.prototype.getAttr = function (name) {
    return this.elem.getAttribute(name);
  };

  Node.prototype.hasAttr = function (name) {
    return this.elem.hasAttribute(name);
  };

  Node.prototype.setAttr = function (name, value) {
    if (this._isRoot) throw new Error('can not set attribute on root element');

    this.elem.setAttribute(name, value);
    return this;
  };

  Node.prototype.removeAttr = function (name) {
    if (this._isRoot) throw new Error('can not remove attribute from root element');

    this.elem.removeAttribute(name);
    return this;
  };

  Node.prototype.find = function () {
    return new Search(this.document, this.elem);
  };

})(this.document);
<|endoftext|>