<commit_before>var LiveView = (function($) {

  function each(data, fn, context) {
    var i
    for(i in data) {
      if(data.hasOwnProperty(i)) {
        fn.call(context, i, data[i])
      }
    }
  }

  function merge(obj1, obj2) {
    var i
    for(i in obj2) {
      if(obj2.hasOwnProperty(i)) {
        obj1[i] = obj2[i]
      }
    }
  }

  var isArray = Array.isArray || function (array) {
    return Object.prototype.toString.call(array) === '[object Array]'
  }

  // Contstructs a new live view from a template (css selector, html, or template url)
  // and, optional data.
  LiveView = function(template, data, callback) {
    var that = this
    if(typeof template === "string" && template.match(/.*\.html/)) {
      $.get(template, function(template) {
        that.initialize(template, data, callback)
      })
    } else {
      that.initialize(template, data, callback)
    }
  }

  LiveView.prototype.initialize = function(template, data, callback) {
    callback = callback || function() {}
    var that = this
    this.context = $(template)
    this.collections = {}
    this.data = data = data || {}
    this.id = data.id
    this.context.attr("data-id", this.id)

    if($("form", this.context)) {
      this.form = new LiveViewForm(this)
    }
    //shorthand for an array of strings
    if(typeof data === "string") {
      data = {value: data}
    }

    this.substitutePartials(function() {
      that.getAttributesWithVariables()

      each(data, function(key, value) {
        that.set(key, value)
      })

      callback(that)
    })
  }

  LiveView.prototype.addCollection = function(key, value) {
    this.collections[key] = this[key] = new LiveViewCollection(this.getElementFromName(key, this.context), value, key)
  }

  LiveView.prototype.substitutePartials = function(callback) {
    var partials = $("[data-partial]")
      , length = partials.length
      , completed = 0
    if(length == 0) {
      callback()
    } else {
      partials.each(function(index, element) {
        template = $(element).attr("data-partial")
        $.get(template, function(r) {
          $(element).html(r.responseText)
          if((++completed) == length) {
            callback()
          }
        })
      })
    }
  }

  LiveView.prototype.getAttributesWithVariables = function() {
    var orig = []
    $("[data-var]", this.context).each(function(i, el) {
      var attrs = []
      orig.push({attrs: attrs, element: el})
      for(var i = 0; i < el.attributes.length; i++) {
         var attrib = el.attributes[i]
         if(attrib.specified == true) {
           var memo = { name: attrib.name
                      , value: attrib.value }
         }
         attrs.push(memo)
      }
    })

    this.setAttributeOfElement = function(name, value) {
      _(orig).each(function(obj) {
        var element = $(obj.element)
        _(obj.attrs).each(function(memo) {
          regex = new RegExp("#{" + name + "}","g")
          if(memo.value.match(regex)) {
            element.attr(memo.name, memo.value.replace(regex, value.content))
          }
        })
      })
    }
  }

  // Given the name of the data a user passed in, return an element
  // to populate with that data
  LiveView.prototype.getElementFromName = function(name, context) {
    if($(context).is("." + name)) {
      return context
    }
    var elements = $("." + name, context)
    each(this.hiddenElements, function(index, obj) {
      elements = elements.add($(obj.el).find("." + name))
    }, this)
    return elements
  }

  // Toggles whether a named item is attached on the page
  LiveView.prototype.toggle = function(name) {
    if(this.hiddenElements[name]) {
      this.set(name, true)
    } else {
      this.set(name, false)
    }
  }

  LiveView.prototype.setVisible = function(name, value) {
    var element = this.getElementFromName(name, this.context)
      , value
    //anything but strict false
    if (value === false) {
      $(element).hide()
    } else {
      $(element).show()
    }
  }

  // Sets the values of named element to value, also
  // can take an object of name value pairs to bulk set
  LiveView.prototype.set = function(name, value) {
    var that = this
    if(value == null) {
      value = ""
    }
    if(arguments.length !== 2) {
      each(name, this.set, this)
    } else {
      if(isArray(value)) {
        this.addCollection(name, value)
        return
      }

      var element = this.getElementFromName(name, this.context)

      if(typeof value == "boolean") {
        value = {visible: value}
      } else if(typeof value !== "object") {
        value = {content: value}
      }

      this.setAttributeOfElement(name, value)

      element.each(function(index, element) {
        el = $(element)
        tagName = element.tagName
        each(value, function(key, value) {
          if(key === "content") {
            if(tagName.toLowerCase() == "input") {
              if(el.attr("type").toLowerCase() == "file") {
                el.attr("data-value", value)
              } else {
                el.val(value)
              }
            } else {
              el.html(value)
            }
          } else if(key === "visible") {
            that.setVisible(name, value)
          } else if(key === "class") {
            el.attr("class", name + " " + value)
          } else {
            el.attr(key, value)
          }
        })
      })
    }

  }

  LiveView.prototype.remove = function() {
    this.context.remove()
  }

  LiveView.prototype.detach = function() {
    this.context.detach()
  }

  LiveView.prototype.attach = function(element) {
    this.context.appendTo(element)
  }

  LiveView.prototype.attach = function(container) {
    container.append(this.context)
  }

  var LiveViewCollection = function(container, data, name) {
    this.container = container
    this.collection = []
    this.views = {}
    this.events = {}
    this.name = name
    this.templates = container.children().remove()
    this.container.html("")
    this.append(data)
  }

  LiveViewCollection.prototype.getTemplate = function(type) {
    if(!type) {
      return $(this.templates[0])
    } else {
      return $(this.templates.filter("." + type)[0])
    }
  }

  // If one argument, returns view at that index
  // if two, returns first element with data
  // where key arg1 === arg2
  LiveViewCollection.prototype.get = function(id) {
      return this.views[id]
  }

  // number of live views in this collection
  LiveViewCollection.prototype.length = function() {
    return this.collection.length
  }

  LiveViewCollection.prototype.remove = function(id) {
    this.collection.splice(this.collection.indexOf(id), 1)
    var view = this.views[id]
    delete this.views[id]
    return view.remove()
  }

  // add it at the end
  // return a the new liveView when completed
  LiveViewCollection.prototype.insert = function(data, index) {
    var element
      , view
      , type
      , id

    if(!isArray(data)) {
      type = data.type || "";
      id = data.id
      element = this.getTemplate(type).clone(true)
      view = new LiveView(element, data)

      if(index === undefined) {
        this.appendView(view, id)
      } else {
        this.insertView(view, index, id)
      }
      return view
    } else {
      each(data, function(i, item) { this.append(item) }, this)
    }
  }

  LiveViewCollection.prototype.append = function(data) {
    if(!isArray(data)) {
      return this.insert(data)
    } else {
      each(data, function(i, item) { this.append(item) }, this)
    }
  }

  LiveViewCollection.prototype.appendView = function(view, id) {
    this.container.append(view.context.detach())
    this.collection.push(id)
    this.views[id] = view
  }

  LiveViewCollection.prototype.insertView = function(view, index, id) {
    view.context.detach().insertBefore(this.container.children()[index])
    this.collection.splice(index, 0, index)
    this.views[id] = view
  }

  var LiveViewForm = function(view) {
    this.view = view
    var that = this
    this.bindValues()
  }

  LiveViewForm.prototype.bindValues = function() {
    var that = this
    $("input[data-value-bind]", this.context).live("change", function() {
      var element = $(this)
        , name = element.attr("data-value-bind")

      if(this.tagName.toLowerCase() == "input") {
        if(element.attr("type") == "file") {
          fileElement = $(this)
          file = this.files[0]
          reader = new FileReader()
          reader.readAsDataURL(file)
          reader.onload = function (e) {
            that.view.set(name, e.target.result)
          }
        } else {
          that.view.set(name, element.value)
        }

      }
    })
  }

  return LiveView
}(jQuery))
<commit_msg>whitespace and bugs<commit_after>define(["jquery", "underscore"], function($, _) {

  function each(data, fn, context) {
    var i
    for(i in data) {
      if(data.hasOwnProperty(i)) {
        fn.call(context, i, data[i])
      }
    }
  }

  function merge(obj1, obj2) {
    var i
    for(i in obj2) {
      if(obj2.hasOwnProperty(i)) {
        obj1[i] = obj2[i]
      }
    }
  }

  var isArray = Array.isArray || function (array) {
    return Object.prototype.toString.call(array) === '[object Array]'
  }

  // Contstructs a new live view from a template (css selector, html, or template url)
  // and, optional data.
  LiveView = function(template, data, callback) {
    var that = this
    if(typeof template === "string" && template.match(/.*\.html/)) {
      $.get(template, function(template) {
        that.initialize(template, data, callback)
      })
    } else {
      that.initialize(template, data, callback)
    }
  }

  LiveView.prototype.initialize = function(template, data, callback) {
    callback = callback || function() {}
    var that = this
    this.context = $(template)
    this.collections = {}
    this.data = data = data || {}
    this.id = data.id
    this.context.attr("data-id", this.id)

    if($("form", this.context)) {
      this.form = new LiveViewForm(this)
    }
    //shorthand for an array of strings
    if(typeof data === "string") {
      data = {value: data}
    }

    this.substitutePartials(function() {
      that.getAttributesWithVariables()

      each(data, function(key, value) {
        that.set(key, value)
      })

      callback(that)
    })
  }

  LiveView.prototype.addCollection = function(key, value) {
    this.collections[key] = this[key] = new LiveViewCollection(this.getElementFromName(key, this.context), value, key)
  }

  LiveView.prototype.substitutePartials = function(callback) {
    var partials = $("[data-partial]")
      , length = partials.length
      , completed = 0
    if(length == 0) {
      callback()
    } else {
      partials.each(function(index, element) {
        template = $(element).attr("data-partial")
        $.get(template, function(r) {
          $(element).html(r.responseText)
          if((++completed) == length) {
            callback()
          }
        })
      })
    }
  }

  LiveView.prototype.getAttributesWithVariables = function() {
    var orig = []
    $("[data-var]", this.context).each(function(i, el) {
      var attrs = []
      orig.push({attrs: attrs, element: el})
      for(var i = 0; i < el.attributes.length; i++) {
         var attrib = el.attributes[i]
         if(attrib.specified == true) {
           var memo = { name: attrib.name
                      , value: attrib.value }
         }
         attrs.push(memo)
      }
    })

    this.setAttributeOfElement = function(name, value) {
      _(orig).each(function(obj) {
        var element = $(obj.element)
        _(obj.attrs).each(function(memo) {
          regex = new RegExp("#{" + name + "}","g")
          if(memo.value.match(regex)) {
            element.attr(memo.name, memo.value.replace(regex, value.content))
          }
        })
      })
    }
  }

  // Given the name of the data a user passed in, return an element
  // to populate with that data
  LiveView.prototype.getElementFromName = function(name, context) {
    if($(context).is("." + name)) {
      return context
    }
    var elements = $("." + name, context)
    each(this.hiddenElements, function(index, obj) {
      elements = elements.add($(obj.el).find("." + name))
    }, this)
    return elements
  }

  // Toggles whether a named item is attached on the page
  LiveView.prototype.toggle = function(name) {
    if(this.hiddenElements[name]) {
      this.set(name, true)
    } else {
      this.set(name, false)
    }
  }

  LiveView.prototype.setVisible = function(name, value) {
    var element = this.getElementFromName(name, this.context)
      , value
    //anything but strict false
    if (value === false) {
      $(element).hide()
    } else {
      $(element).show()
    }
  }

  // Sets the values of named element to value, also
  // can take an object of name value pairs to bulk set
  LiveView.prototype.set = function(name, value) {
    var that = this
    if(value == null) {
      value = ""
    }
    if(arguments.length !== 2) {
      each(name, this.set, this)
    } else {
      if(isArray(value)) {
        this.addCollection(name, value)
        return
      }

      var element = this.getElementFromName(name, this.context)

      if(typeof value == "boolean") {
        value = {visible: value}
      } else if(typeof value !== "object") {
        value = {content: value}
      }

      this.setAttributeOfElement(name, value)

      element.each(function(index, element) {
        el = $(element)
        tagName = element.tagName
        each(value, function(key, value) {
          if(key === "content") {
            if(tagName.toLowerCase() == "input") {
              if(el.attr("type").toLowerCase() == "file") {
                el.attr("data-value", value)
              } else {
                el.val(value)
              }
            } else {
              el.html(value)
            }
          } else if(key === "visible") {
            that.setVisible(name, value)
          } else if(key === "class") {
            el.attr("class", name + " " + value)
          } else {
            el.attr(key, value)
          }
        })
      })
    }

  }

  LiveView.prototype.remove = function() {
    this.context.remove()
  }

  LiveView.prototype.detach = function() {
    this.context.detach()
  }

  LiveView.prototype.attach = function(element) {
    this.context.appendTo(element)
  }

  LiveView.prototype.attach = function(container) {
    container.append(this.context)
  }

  var LiveViewCollection = function(container, data, name) {
    this.container = container
    this.collection = []
    this.views = {}
    this.events = {}
    this.name = name
    this.templates = container.children().remove()
    this.container.html("")
    this.append(data)
  }

  LiveViewCollection.prototype.getTemplate = function(type) {
    if(!type) {
      return $(this.templates[0])
    } else {
      return $(this.templates.filter("." + type)[0])
    }
  }

  // If one argument, returns view at that index
  // if two, returns first element with data
  // where key arg1 === arg2
  LiveViewCollection.prototype.get = function(id) {
      return this.views[id]
  }

  // number of live views in this collection
  LiveViewCollection.prototype.length = function() {
    return this.collection.length
  }

  LiveViewCollection.prototype.remove = function(id) {
    this.collection.splice(this.collection.indexOf(id), 1)
    var view = this.views[id]
    delete this.views[id]
    return view.remove()
  }

  // add it at the end
  // return a the new liveView when completed
  LiveViewCollection.prototype.insert = function(data, index) {
    var element
      , view
      , type
      , id

    if(!isArray(data)) {
      type = data.type || "";
      id = data.id
      element = this.getTemplate(type).clone(true)
      view = new LiveView(element, data)

      if(index === undefined) {
        this.appendView(view, id)
      } else {
        this.insertView(view, index, id)
      }
      return view
    } else {
      each(data, function(i, item) { this.append(item) }, this)
    }
  }

  LiveViewCollection.prototype.append = function(data) {
    if(!isArray(data)) {
      return this.insert(data)
    } else {
      each(data, function(i, item) { this.append(item) }, this)
    }
  }

  LiveViewCollection.prototype.appendView = function(view, id) {
    this.container.append(view.context.detach())
    this.collection.push(id)
    this.views[id] = view
  }

  LiveViewCollection.prototype.insertView = function(view, index, id) {
    view.context.detach().insertBefore(this.container.children()[index])
    this.collection.splice(index, 0, index)
    this.views[id] = view
  }

  var LiveViewForm = function(view) {
    this.view = view
    var that = this
    this.bindValues()
  }

  LiveViewForm.prototype.bindValues = function() {
    var that = this
    $("input[data-value-bind]", this.context).live("change", function() {
      var element = $(this)
        , name = element.attr("data-value-bind")

      if(this.tagName.toLowerCase() == "input") {
        if(element.attr("type") == "file") {
          fileElement = $(this)
          file = this.files[0]
          reader = new FileReader()
          reader.readAsDataURL(file)
          reader.onload = function (e) {
            that.view.set(name, e.target.result)
          }
        } else {
          that.view.set(name, element.value)
        }

      }
    })
  }

  return LiveView
})
<|endoftext|>


<commit_before>const { components, requiredCerts, config } = require('./params');

/**
 * Setup configuration variables based on the "app" the data-portal is
 * being deployed into (Brain Health Commons, Blood Pack, ...)
 *
 * @param {app, dev, basename, mockStore, hostname} opts overrides for defaults
 */
function buildConfig(opts) {
  const defaults = {
    dev: !!(process.env.NODE_ENV && process.env.NODE_ENV === 'dev'),
    mockStore: !!(process.env.MOCK_STORE && process.env.MOCK_STORE === 'true'),
    app: process.env.APP || 'generic',
    basename: process.env.BASENAME || '/',
    hostname: typeof window !== 'undefined' ? `${window.location.protocol}//${window.location.hostname}/` : 'http://localhost/',
    gaDebug: !!(process.env.GA_DEBUG && process.env.GA_DEBUG === 'true'),
    tierAccessLevel: process.env.TIER_ACCESS_LEVEL || 'private',
    tierAccessLimit: Number.parseInt(process.env.TIER_ACCESS_LIMIT, 10) || 1000,
  };

  //
  // Override default basename if loading via /dev.html
  // dev.html loads bundle.js via https://localhost...
  //
  if (typeof location !== 'undefined' && location.pathname.indexOf(`${defaults.basename}dev.html`) === 0) {
    defaults.basename += 'dev.html';
  }

  const {
    dev,
    mockStore,
    app,
    basename,
    hostname,
    gaDebug,
    tierAccessLevel,
    tierAccessLimit,
  } = Object.assign({}, defaults, opts);

  const submissionApiPath = `${hostname}api/v0/submission/`;
  const apiPath = `${hostname}api/`;
  const submissionApiOauthPath = `${hostname}api/v0/oauth2/`;
  const graphqlPath = `${hostname}api/v0/submission/graphql/`;
  const dataDictionaryTemplatePath = `${hostname}api/v0/submission/template/`;
  const arrangerGraphqlPath = `${hostname}api/v0/flat-search/search/graphql`;
  let userapiPath = `${hostname}user/`;
  const jobapiPath = `${hostname}job/`;
  const credentialCdisPath = `${userapiPath}credentials/cdis/`;
  const coreMetadataPath = `${hostname}coremetadata/`;
  const indexdPath = `${hostname}index/`;
  const wtsPath = `${hostname}wts/oauth2/`;
  let login = {
    url: `${userapiPath}login/google?redirect=`,
    title: 'Login from Google',
  };
  const loginPath = `${userapiPath}login/`;
  const logoutInactiveUsers = !(process.env.LOGOUT_INACTIVE_USERS === 'false');
  const graphqlSchemaUrl = `${hostname}data/schema.json`;
  const workspaceUrl = '/lw-workspace/';
  const workspaceErrorUrl = '/no-workspace-access/';
  const datasetUrl = `${hostname}api/search/datasets`;
  const guppyUrl = `${hostname}guppy`;
  const guppyGraphQLUrl = `${guppyUrl}graphql/`;
  const manifestServiceApiPath = `${hostname}manifests/`;
  // backward compatible: homepageChartNodes not set means using graphql query,
  // which will return 401 UNAUTHORIZED if not logged in, thus not making public
  let indexPublic = true;
  if (typeof components.index.homepageChartNodes === 'undefined') {
    indexPublic = false;
  }

  let useGuppyForExplorer = false;
  if (config.dataExplorerConfig.guppyConfig) {
    useGuppyForExplorer = true;
  }

  // for "libre" data commons, explorer page is public
  let explorerPublic = false;
  if (tierAccessLevel === 'libre') {
    explorerPublic = true;
  }

  const colorsForCharts = {
    categorical9Colors: components.categorical9Colors ? components.categorical9Colors : [
      '#3283c8',
      '#7ec500',
      '#ad91ff',
      '#f4b940',
      '#e74c3c',
      '#05b8ee',
      '#ff7abc',
      '#ef8523',
      '#26d9b1',
    ],
    categorical2Colors: components.categorical2Colors ? components.categorical2Colors : [
      '#3283c8',
      '#e7e7e7',
    ],
  };

  if (app === 'gdc') {
    userapiPath = dev === true ? `${hostname}user/` : `${hostname}api/`;
    login = {
      url: 'https://itrusteauth.nih.gov/affwebservices/public/saml2sso?SPID=https://bionimbus-pdc.opensciencedatacloud.org/shibboleth&RelayState=',
      title: 'Login from NIH',
    };
  }

  const defaultLineLimit = 30;
  const lineLimit = (config.lineLimit == null) ? defaultLineLimit : config.lineLimit;

  const analysisApps = {
    ndhHIV: {
      title: 'NDH HIV Classifier',
      description: 'Classify stored clinical data based on controller status.',
      image: '/src/img/analysis-icons/hiv-classifier.svg',
    },
    ndhVirus: {
      title: 'NDH Virulence Simulation',
      description: `This simulation runs a docker version of the Hypothesis Testing
          using Phylogenies (HyPhy) tool over data submitted in the NIAID Data Hub. \n
          The simulation is focused on modeling a Bayesian Graph Model (BGM) based on a binary matrix input.
          The implemented example predicts the virulence status of different influenza strains based on their mutations
          (the mutation panel is represented as the input binary matrix).`,
      image: '/src/img/analysis-icons/virulence.png',
    },
    vaGWAS: {
      title: 'eGWAS',
      description: 'Expression-based Genome-Wide Association Study',
      image: '/src/img/analysis-icons/gwas.svg',
      options: [
        {
          label: 'Lung',
          value: 'Lung',
        },
        {
          label: 'Gastrointestina',
          value: 'Gastrointestina',
        },
        {
          label: 'Prostate',
          value: 'Prostate',
        },
        {
          label: 'Head and Neck',
          value: 'Head and Neck',
        },
        {
          label: 'Skin',
          value: 'Skin',
        },
        {
          label: 'NULL',
          value: 'NULL',
        },
        {
          label: 'Lymph Node',
          value: 'Lymph Node',
        },
        {
          label: 'Liver',
          value: 'Liver',
        },
        {
          label: 'Musculoskeleta',
          value: 'Musculoskeleta',
        },
        {
          label: 'Occipital Mass',
          value: 'Occipital Mass',
        },
        {
          label: 'Brain',
          value: 'Brain',
        },
        {
          label: 'BxType',
          value: 'BxType',
        },
      ],
    },
  };

  return {
    app,
    basename,
    buildConfig,
    dev,
    hostname,
    gaDebug,
    userapiPath,
    jobapiPath,
    apiPath,
    submissionApiPath,
    submissionApiOauthPath,
    credentialCdisPath,
    coreMetadataPath,
    indexdPath,
    graphqlPath,
    dataDictionaryTemplatePath,
    arrangerGraphqlPath,
    graphqlSchemaUrl,
    appname: components.appName,
    mockStore,
    colorsForCharts,
    login,
    loginPath,
    logoutInactiveUsers,
    requiredCerts,
    lineLimit,
    certs: components.certs,
    workspaceUrl,
    workspaceErrorUrl,
    homepageChartNodes: components.index.homepageChartNodes,
    datasetUrl,
    indexPublic,
    guppyUrl,
    guppyGraphQLUrl,
    manifestServiceApiPath,
    wtsPath,
    useGuppyForExplorer,
    analysisApps,
    tierAccessLevel,
    tierAccessLimit,
    explorerPublic,
  };
}

const defaultConf = buildConfig();
// Commonjs style export, so can load from nodejs into data/gqlSetup
module.exports = defaultConf;
<commit_msg>fix/guppyGQLurl (#532)<commit_after>const { components, requiredCerts, config } = require('./params');

/**
 * Setup configuration variables based on the "app" the data-portal is
 * being deployed into (Brain Health Commons, Blood Pack, ...)
 *
 * @param {app, dev, basename, mockStore, hostname} opts overrides for defaults
 */
function buildConfig(opts) {
  const defaults = {
    dev: !!(process.env.NODE_ENV && process.env.NODE_ENV === 'dev'),
    mockStore: !!(process.env.MOCK_STORE && process.env.MOCK_STORE === 'true'),
    app: process.env.APP || 'generic',
    basename: process.env.BASENAME || '/',
    hostname: typeof window !== 'undefined' ? `${window.location.protocol}//${window.location.hostname}/` : 'http://localhost/',
    gaDebug: !!(process.env.GA_DEBUG && process.env.GA_DEBUG === 'true'),
    tierAccessLevel: process.env.TIER_ACCESS_LEVEL || 'private',
    tierAccessLimit: Number.parseInt(process.env.TIER_ACCESS_LIMIT, 10) || 1000,
  };

  //
  // Override default basename if loading via /dev.html
  // dev.html loads bundle.js via https://localhost...
  //
  if (typeof location !== 'undefined' && location.pathname.indexOf(`${defaults.basename}dev.html`) === 0) {
    defaults.basename += 'dev.html';
  }

  const {
    dev,
    mockStore,
    app,
    basename,
    hostname,
    gaDebug,
    tierAccessLevel,
    tierAccessLimit,
  } = Object.assign({}, defaults, opts);

  const submissionApiPath = `${hostname}api/v0/submission/`;
  const apiPath = `${hostname}api/`;
  const submissionApiOauthPath = `${hostname}api/v0/oauth2/`;
  const graphqlPath = `${hostname}api/v0/submission/graphql/`;
  const dataDictionaryTemplatePath = `${hostname}api/v0/submission/template/`;
  const arrangerGraphqlPath = `${hostname}api/v0/flat-search/search/graphql`;
  let userapiPath = `${hostname}user/`;
  const jobapiPath = `${hostname}job/`;
  const credentialCdisPath = `${userapiPath}credentials/cdis/`;
  const coreMetadataPath = `${hostname}coremetadata/`;
  const indexdPath = `${hostname}index/`;
  const wtsPath = `${hostname}wts/oauth2/`;
  let login = {
    url: `${userapiPath}login/google?redirect=`,
    title: 'Login from Google',
  };
  const loginPath = `${userapiPath}login/`;
  const logoutInactiveUsers = !(process.env.LOGOUT_INACTIVE_USERS === 'false');
  const graphqlSchemaUrl = `${hostname}data/schema.json`;
  const workspaceUrl = '/lw-workspace/';
  const workspaceErrorUrl = '/no-workspace-access/';
  const datasetUrl = `${hostname}api/search/datasets`;
  const guppyUrl = `${hostname}guppy`;
  const guppyGraphQLUrl = `${guppyUrl}/graphql/`;
  const manifestServiceApiPath = `${hostname}manifests/`;
  // backward compatible: homepageChartNodes not set means using graphql query,
  // which will return 401 UNAUTHORIZED if not logged in, thus not making public
  let indexPublic = true;
  if (typeof components.index.homepageChartNodes === 'undefined') {
    indexPublic = false;
  }

  let useGuppyForExplorer = false;
  if (config.dataExplorerConfig.guppyConfig) {
    useGuppyForExplorer = true;
  }

  // for "libre" data commons, explorer page is public
  let explorerPublic = false;
  if (tierAccessLevel === 'libre') {
    explorerPublic = true;
  }

  const colorsForCharts = {
    categorical9Colors: components.categorical9Colors ? components.categorical9Colors : [
      '#3283c8',
      '#7ec500',
      '#ad91ff',
      '#f4b940',
      '#e74c3c',
      '#05b8ee',
      '#ff7abc',
      '#ef8523',
      '#26d9b1',
    ],
    categorical2Colors: components.categorical2Colors ? components.categorical2Colors : [
      '#3283c8',
      '#e7e7e7',
    ],
  };

  if (app === 'gdc') {
    userapiPath = dev === true ? `${hostname}user/` : `${hostname}api/`;
    login = {
      url: 'https://itrusteauth.nih.gov/affwebservices/public/saml2sso?SPID=https://bionimbus-pdc.opensciencedatacloud.org/shibboleth&RelayState=',
      title: 'Login from NIH',
    };
  }

  const defaultLineLimit = 30;
  const lineLimit = (config.lineLimit == null) ? defaultLineLimit : config.lineLimit;

  const analysisApps = {
    ndhHIV: {
      title: 'NDH HIV Classifier',
      description: 'Classify stored clinical data based on controller status.',
      image: '/src/img/analysis-icons/hiv-classifier.svg',
    },
    ndhVirus: {
      title: 'NDH Virulence Simulation',
      description: `This simulation runs a docker version of the Hypothesis Testing
          using Phylogenies (HyPhy) tool over data submitted in the NIAID Data Hub. \n
          The simulation is focused on modeling a Bayesian Graph Model (BGM) based on a binary matrix input.
          The implemented example predicts the virulence status of different influenza strains based on their mutations
          (the mutation panel is represented as the input binary matrix).`,
      image: '/src/img/analysis-icons/virulence.png',
    },
    vaGWAS: {
      title: 'eGWAS',
      description: 'Expression-based Genome-Wide Association Study',
      image: '/src/img/analysis-icons/gwas.svg',
      options: [
        {
          label: 'Lung',
          value: 'Lung',
        },
        {
          label: 'Gastrointestina',
          value: 'Gastrointestina',
        },
        {
          label: 'Prostate',
          value: 'Prostate',
        },
        {
          label: 'Head and Neck',
          value: 'Head and Neck',
        },
        {
          label: 'Skin',
          value: 'Skin',
        },
        {
          label: 'NULL',
          value: 'NULL',
        },
        {
          label: 'Lymph Node',
          value: 'Lymph Node',
        },
        {
          label: 'Liver',
          value: 'Liver',
        },
        {
          label: 'Musculoskeleta',
          value: 'Musculoskeleta',
        },
        {
          label: 'Occipital Mass',
          value: 'Occipital Mass',
        },
        {
          label: 'Brain',
          value: 'Brain',
        },
        {
          label: 'BxType',
          value: 'BxType',
        },
      ],
    },
  };

  return {
    app,
    basename,
    buildConfig,
    dev,
    hostname,
    gaDebug,
    userapiPath,
    jobapiPath,
    apiPath,
    submissionApiPath,
    submissionApiOauthPath,
    credentialCdisPath,
    coreMetadataPath,
    indexdPath,
    graphqlPath,
    dataDictionaryTemplatePath,
    arrangerGraphqlPath,
    graphqlSchemaUrl,
    appname: components.appName,
    mockStore,
    colorsForCharts,
    login,
    loginPath,
    logoutInactiveUsers,
    requiredCerts,
    lineLimit,
    certs: components.certs,
    workspaceUrl,
    workspaceErrorUrl,
    homepageChartNodes: components.index.homepageChartNodes,
    datasetUrl,
    indexPublic,
    guppyUrl,
    guppyGraphQLUrl,
    manifestServiceApiPath,
    wtsPath,
    useGuppyForExplorer,
    analysisApps,
    tierAccessLevel,
    tierAccessLimit,
    explorerPublic,
  };
}

const defaultConf = buildConfig();
// Commonjs style export, so can load from nodejs into data/gqlSetup
module.exports = defaultConf;
<|endoftext|>


<commit_before>//! moment.js locale configuration
//! locale : norwegian nynorsk (nn)
//! author : https://github.com/mechuwind

import moment from '../moment';

export default moment.defineLocale('nn', {
    months : 'januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember'.split('_'),
    monthsShort : 'jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des'.split('_'),
    weekdays : 'sundag_måndag_tysdag_onsdag_torsdag_fredag_laurdag'.split('_'),
    weekdaysShort : 'sun_mån_tys_ons_tor_fre_lau'.split('_'),
    weekdaysMin : 'su_må_ty_on_to_fr_lø'.split('_'),
    longDateFormat : {
        LT : 'HH:mm',
        LTS : 'HH:mm:ss',
        L : 'DD.MM.YYYY',
        LL : 'D. MMMM YYYY',
        LLL : 'D. MMMM YYYY [kl.] H:mm',
        LLLL : 'dddd D. MMMM YYYY [kl.] HH:mm'
    },
    calendar : {
        sameDay: '[I dag klokka] LT',
        nextDay: '[I morgon klokka] LT',
        nextWeek: 'dddd [klokka] LT',
        lastDay: '[I går klokka] LT',
        lastWeek: '[Føregåande] dddd [klokka] LT',
        sameElse: 'L'
    },
    relativeTime : {
        future : 'om %s',
        past : 'for %s sidan',
        s : 'nokre sekund',
        m : 'eit minutt',
        mm : '%d minutt',
        h : 'ein time',
        hh : '%d timar',
        d : 'ein dag',
        dd : '%d dagar',
        M : 'ein månad',
        MM : '%d månader',
        y : 'eit år',
        yy : '%d år'
    },
    ordinalParse: /\d{1,2}\./,
    ordinal : '%d.',
    week : {
        dow : 1, // Monday is the first day of the week.
        doy : 4  // The week that contains Jan 4th is the first week of the year.
    }
});

<commit_msg>Update nn locale as well<commit_after>//! moment.js locale configuration
//! locale : norwegian nynorsk (nn)
//! author : https://github.com/mechuwind

import moment from '../moment';

export default moment.defineLocale('nn', {
    months : 'januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember'.split('_'),
    monthsShort : 'jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des'.split('_'),
    weekdays : 'sundag_måndag_tysdag_onsdag_torsdag_fredag_laurdag'.split('_'),
    weekdaysShort : 'sun_mån_tys_ons_tor_fre_lau'.split('_'),
    weekdaysMin : 'su_må_ty_on_to_fr_lø'.split('_'),
    longDateFormat : {
        LT : 'HH:mm',
        LTS : 'HH:mm:ss',
        L : 'DD.MM.YYYY',
        LL : 'D. MMMM YYYY',
        LLL : 'D. MMMM YYYY [kl.] H:mm',
        LLLL : 'dddd D. MMMM YYYY [kl.] HH:mm'
    },
    calendar : {
        sameDay: '[I dag klokka] LT',
        nextDay: '[I morgon klokka] LT',
        nextWeek: 'dddd [klokka] LT',
        lastDay: '[I går klokka] LT',
        lastWeek: '[Føregåande] dddd [klokka] LT',
        sameElse: 'L'
    },
    relativeTime : {
        future : 'om %s',
        past : '%s sidan',
        s : 'nokre sekund',
        m : 'eit minutt',
        mm : '%d minutt',
        h : 'ein time',
        hh : '%d timar',
        d : 'ein dag',
        dd : '%d dagar',
        M : 'ein månad',
        MM : '%d månader',
        y : 'eit år',
        yy : '%d år'
    },
    ordinalParse: /\d{1,2}\./,
    ordinal : '%d.',
    week : {
        dow : 1, // Monday is the first day of the week.
        doy : 4  // The week that contains Jan 4th is the first week of the year.
    }
});

<|endoftext|>


<commit_before>import { postgresConnection as cn } from '../../config/helpers.js';
import { getMultipleRecipes } from '../recipes/recipeController.js';
const pgp = require('pg-promise')();
const db = pgp(cn);

module.exports = {
  searchRecipes: (request, response, next) => {
    const queryObj = {
      name: 'search-recipes',
      text: 'SELECT id FROM recipes WHERE title ~* 'vegan';',
      // values: [request.body.title, request.body.prep_steps, request.body.cook_steps],
    };

    console.log('REQUEST.BODY****************',request.body)
    db.one(queryObj)
      .then((data) => {
        response.status(200);
        response.json(data);
      })
      .catch((error) => {
        response.status(500);
        response.json({
          error: `Error code: ${error.code}, Error message: ${error.detail}`,
        });
      });
  },

  // searchRecipes: (request, response, next) => {
  //   // munge space-separated words into a single OR-separated string
  //   // NOTE: this *may* be breaking query sanitization provided by pg-promise
  //   if (!request.body) {
  //     request.body = {};
  //   } else {
  //     request.body._queryResultIds = [];
  //   }
  //
  //   const query = decodeURIComponent(request.params.q)
  //     .split(' ')
  //     .join(' | ');
  //
  //   const queryObj = {
  //     name: 'search-recipes-table',
  //     text: `SELECT
  //              *
  //            FROM (
  //              SELECT
  //                to_tsvector(CAST(recipes.id as VARCHAR)) || ' ' ||
  //                to_tsvector(CAST(recipes.ingredients as VARCHAR)) || ' ' ||
  //                to_tsvector(CAST(recipes.cook_steps as VARCHAR)) || ' ' ||
  //                to_tsvector(CAST(recipes.prep_steps as VARCHAR)) || ' ' ||
  //                to_tsvector(CAST(recipes.finish_steps as VARCHAR)) || ' ' ||
  //                to_tsvector(coalesce((string_agg(recipes.title, ' ')), ''))
  //                  as document
  //              FROM
  //                recipes
  //              GROUP BY
  //                recipes.id
  //            ) p_search
  //            WHERE
  //              p_search.document @@ to_tsquery($1);`,
  //     values: [query],
  //   };
  //
  //   db.query(queryObj)
  //     .then((data) => {
  //       response.status(201);
  //       request.body.recipes = data.map((val) => {
  //         return parseInt(val.document.match(/(?:'1':)(\d+)(?=[,\s])/)[1], 10);
  //       });
  //       // console.log(request.body._queryResultIds);
  //
  //       return getMultipleRecipes(request, response, next);
  //     })
  //     .catch((error) => {
  //       // console.log('query error', error)
  //       response.json(error);
  //       next();
  //     });
  // },
};
<commit_msg>fix syntax error that was breaking npm dev-start<commit_after>import { postgresConnection as cn } from '../../config/helpers.js';
import { getMultipleRecipes } from '../recipes/recipeController.js';
const pgp = require('pg-promise')();
const db = pgp(cn);

module.exports = {
  // const vegan = 'vegan';
  searchRecipes: (request, response, next) => {
    const queryObj = {
      name: 'search-recipe-title',
      text: 'SELECT id FROM recipes WHERE title ~* "vegan"',
      // values: [vegan],
    };

    // console.log('REQUEST.BODY****************',request.body)
    db.one(queryObj)
      .then((data) => {
        response.status(200);
        response.json(data);
      })
      .catch((error) => {
        response.status(500);
        response.json({
          error: `Error code: ${error.code}, Error message: ${error.detail}`,
        });
      });
  },

  // searchRecipes: (request, response, next) => {
  //   // munge space-separated words into a single OR-separated string
  //   // NOTE: this *may* be breaking query sanitization provided by pg-promise
  //   if (!request.body) {
  //     request.body = {};
  //   } else {
  //     request.body._queryResultIds = [];
  //   }
  //
  //   const query = decodeURIComponent(request.params.q)
  //     .split(' ')
  //     .join(' | ');
  //
  //   const queryObj = {
  //     name: 'search-recipes-table',
  //     text: `SELECT
  //              *
  //            FROM (
  //              SELECT
  //                to_tsvector(CAST(recipes.id as VARCHAR)) || ' ' ||
  //                to_tsvector(CAST(recipes.ingredients as VARCHAR)) || ' ' ||
  //                to_tsvector(CAST(recipes.cook_steps as VARCHAR)) || ' ' ||
  //                to_tsvector(CAST(recipes.prep_steps as VARCHAR)) || ' ' ||
  //                to_tsvector(CAST(recipes.finish_steps as VARCHAR)) || ' ' ||
  //                to_tsvector(coalesce((string_agg(recipes.title, ' ')), ''))
  //                  as document
  //              FROM
  //                recipes
  //              GROUP BY
  //                recipes.id
  //            ) p_search
  //            WHERE
  //              p_search.document @@ to_tsquery($1);`,
  //     values: [query],
  //   };
  //
  //   db.query(queryObj)
  //     .then((data) => {
  //       response.status(201);
  //       request.body.recipes = data.map((val) => {
  //         return parseInt(val.document.match(/(?:'1':)(\d+)(?=[,\s])/)[1], 10);
  //       });
  //       // console.log(request.body._queryResultIds);
  //
  //       return getMultipleRecipes(request, response, next);
  //     })
  //     .catch((error) => {
  //       // console.log('query error', error)
  //       response.json(error);
  //       next();
  //     });
  // },
};
<|endoftext|>


<commit_before>import { createPropTransforms, propTypesFromMapping } from './props'
import { createActions, createActionTransforms } from './actions'
import { combineReducerObjects, convertReducerArrays } from './reducer'
import { convertConstants } from './create'
import { pathSelector, createSelectors } from './selectors'
import { firstReducerRoot, addReducer, startSaga, cancelSaga } from '../scene/store'
import { createCombinedSaga } from '../scene/saga'
import shallowEqual from '../utils/shallow-equal'
import { createSaga } from '../saga/create'

import { createSelector } from 'reselect'
import { select, call } from 'redux-saga/effects'
import { connectAdvanced } from 'react-redux'

let inlineCache = {}
let actionCache = {}

export function cachedSelectors (path) {
  return inlineCache[path.join('.')] || {}
}

const DEBUG = false

let nonameCounter = 0

export function kea (_this) {
  const hasConnect = !!(_this.connect)
  const hasLogic = !!(_this.path || _this.actions || _this.reducer || _this.selectors)
  const hasSaga = !!(_this.sagas || _this.start || _this.stop || _this.takeEvery || _this.takeLatest || (_this.connect && _this.connect.sagas))
  const isSingleton = !_this.key

  let object = {}

  if (!_this.path) {
    let inlinePath = [firstReducerRoot(), '_kea', `inline-${nonameCounter++}`]
    _this.path = () => inlinePath
  }

  // pregenerate as many things as we can
  object.constants = _this.constants ? convertConstants(_this.constants(object)) : {}

  if (hasLogic) {
    // we don't know yet if it's going to be a singleton (no key) or inline (key)
    // however the actions and constants are common for all, so get a path without the dynamic
    // component and initialize them
    object.path = _this.path('').filter(p => p)
    object.actions = _this.actions ? createActions(_this.actions(object), object.path) : {}
    object.props = {}
  }

  if (hasLogic && isSingleton) {
    object.selector = (state) => pathSelector(object.path, state)
    object.reducers = _this.reducers ? convertReducerArrays(_this.reducers(object)) : {}
    object.reducer = _this.reducer ? _this.reducer(object) : combineReducerObjects(object.path, object.reducers)
    object.selectors = createSelectors(object.path, Object.keys(object.reducers || {}))

    const selectorResponse = _this.selectors ? _this.selectors(object) : {}
    Object.keys(selectorResponse).forEach(selectorKey => {
      // s == [() => args, selectorFunction, propType]
      const s = selectorResponse[selectorKey]
      const args = s[0]()
      if (s[2]) {
        object.reducers[selectorKey] = { type: s[2] }
      }
      object.selectors[selectorKey] = createSelector(...args, s[1])
    })

    object.get = function * (key) {
      return yield select(key ? object.selectors[key] : object.selector)
    }

    object.fetch = function * () {
      let results = {}

      const keys = Array.isArray(arguments[0]) ? arguments[0] : arguments

      for (let i = 0; i < keys.length; i++) {
        results[keys[i]] = yield object.get(keys[i])
      }

      return results
    }
  }

  let propTypes = {}
  let connect = {}
  let connectedActions = {}
  let connectedSelectors = {}

  if (hasConnect) {
    // the { connect: { props, actions } } part
    connect = _this.connect || {}

    // get default proptypes and add connected ones
    propTypes = Object.assign({}, connect.props ? propTypesFromMapping(connect) : {})

    // connected actions and props/selectors
    connectedActions = createActionTransforms(connect.actions).actions
    connectedSelectors = createPropTransforms(connect.props).selectorFunctions

    if (isSingleton) {
      object.actions = Object.assign({}, connectedActions, object.actions)
      object.selectors = Object.assign({}, connectedSelectors, object.selectors)
    }

    // we have _this: { connect: { sagas: [] } }, add to _this: { sagas: [] }
    if (connect.sagas) {
      _this.sagas = _this.sagas ? _this.sagas.concat(connect.sagas) : connect.sagas
    }
  }

  if (hasSaga && isSingleton) {
    object.saga = function * () {
      let sagas = (_this.sagas || []).map(saga => {
        return saga && saga._hasKeaSaga && saga.saga ? saga.saga : saga
      })

      if (_this.start || _this.stop || _this.takeEvery || _this.takeLatest) {
        if (!object._createdSaga) {
          const hasSelectors = !!(object.selectors && Object.keys(object.selectors).length > 0)
          const _singletonSagaBase = {
            start: _this.start,
            stop: _this.stop,
            takeEvery: _this.takeEvery,
            takeLatest: _this.takeLatest,
            workers: _this.workers ? Object.assign({}, _this.workers) : {},
            key: object.key,
            path: object.path,
            get: hasSelectors ? function * (key) {
              return yield select(key ? object.selectors[key] : object.selector)
            } : undefined,
            fetch: hasSelectors ? function * () {
              let results = {}
              const keys = Array.isArray(arguments[0]) ? arguments[0] : arguments
              for (let i = 0; i < keys.length; i++) {
                results[keys[i]] = yield this.get(keys[i])
              }
              return results
            } : undefined
          }

          let sagaActions = Object.assign({}, connectedActions, object.actions)

          object._createdSaga = createSaga(_singletonSagaBase, { actions: sagaActions })
        }

        sagas.push(object._createdSaga)
      }

      yield call(createCombinedSaga(sagas))
    }
  }

  const response = function (Klass) {
    // initializing as a singleton
    if (Klass === false) {
      if (!isSingleton) {
        console.error(`[KEA-LOGIC] Standalone "export default kea({})" functions must have no "key"!`, object.path, Klass)
      }

      return Object.assign(_this, object)
    }

    if (Klass && Klass.propTypes) {
      propTypes = Object.assign({}, propTypes, Klass.propTypes)
    }

    if (hasLogic) {
      // add proptypes from reducer
      const reducers = _this.reducers ? convertReducerArrays(_this.reducers(object)) : {}
      Object.keys(reducers).forEach(reducerKey => {
        if (reducers[reducerKey].type) {
          propTypes[reducerKey] = reducers[reducerKey].type
        }
      })

      // add proptypes from selectors
      const selectorsThatDontWork = _this.selectors ? _this.selectors({}) : {}
      Object.keys(selectorsThatDontWork).forEach(selectorKey => {
        if (selectorsThatDontWork[selectorKey][2]) {
          propTypes[selectorKey] = selectorsThatDontWork[selectorKey][2]
        }
      })
    }

    if (Klass && hasLogic) {
      // add kea metadata to component
      Klass.kea = {
        path: _this.path,
        constants: object.constants,
        actions: object.actions,
        find: (key) => cachedSelectors(_this.path(key))
      }
    }

    if (Klass && (hasLogic || hasConnect)) {
      // convert this.props.actions to this.actions in the component
      const originalComponentWillMount = Klass.prototype.componentWillMount
      Klass.prototype.componentWillMount = function () {
        this.actions = this.props.actions
        originalComponentWillMount && originalComponentWillMount.bind(this)()
      }
    }

    // check for sagas
    if (Klass && hasSaga) {
      const originalComponentDidMount = Klass.prototype.componentDidMount
      Klass.prototype.componentDidMount = function () {
        if (DEBUG) {
          console.log('component did mount')
        }

        // this === component instance
        this._keaSagaBase = {}
        this._keaRunningSaga = null

        const key = _this.key ? _this.key(this.props) : 'index'
        const path = _this.path(key)

        let sagas = (_this.sagas || []).map(saga => {
          return saga && saga._hasKeaSaga && saga.saga ? saga.saga : saga
        })

        if (_this.start || _this.stop || _this.takeEvery || _this.takeLatest) {
          this._keaSagaBase = {
            start: _this.start,
            stop: _this.stop,
            takeEvery: _this.takeEvery,
            takeLatest: _this.takeLatest,
            workers: _this.workers ? Object.assign({}, _this.workers) : {},
            key: key,
            path: path,
            props: this.props,
            get: function * (key) {
              const { selectors, selector } = cachedSelectors(path)
              return yield select(key ? selectors[key] : selector)
            },
            fetch: function * () {
              let results = {}
              const keys = Array.isArray(arguments[0]) ? arguments[0] : arguments
              for (let i = 0; i < keys.length; i++) {
                results[keys[i]] = yield this._keaSagaBase.get(keys[i])
              }
              return results
            }
          }

          let sagaActions = Object.assign({}, connectedActions)

          // inject key to the payload of inline actions
          Object.keys(object.actions || {}).forEach(actionKey => {
            sagaActions[actionKey] = (...args) => {
              const createdAction = object.actions[actionKey](...args)
              return {
                ...createdAction,
                payload: {
                  key,
                  ...createdAction.payload
                }
              }
            }
            sagaActions[actionKey].toString = object.actions[actionKey].toString
          })

          const saga = createSaga(this._keaSagaBase, { actions: sagaActions })
          sagas.push(saga)
        }

        if (sagas.length > 0) {
          this._keaRunningSaga = startSaga(createCombinedSaga(sagas))
        }

        originalComponentDidMount && originalComponentDidMount.bind(this)()
      }

      const originalComponentWillReceiveProps = Klass.prototype.componentWillReceiveProps
      Klass.prototype.componentWillReceiveProps = function (nextProps) {
        this._keaSagaBase.props = nextProps

        originalComponentWillReceiveProps && originalComponentWillReceiveProps.bind(this)(nextProps)
      }

      const originalComponentWillUnmount = Klass.prototype.componentWillUnmount
      Klass.prototype.componentWillUnmount = function () {
        if (DEBUG) {
          console.log('component will unmount')
        }
        if (this._keaRunningSaga) {
          cancelSaga(this._keaRunningSaga)
        }

        originalComponentWillUnmount && originalComponentWillUnmount.bind(this)()
      }
    }

    const selectorFactory = (dispatch, options) => {
      let lastProps = {}
      let result = null

      return (nextState, nextOwnProps) => {
        let key
        let path
        let joinedPath

        let nextProps = Object.assign({}, nextOwnProps)

        if (hasConnect) {
          // connected props
          Object.keys(connectedSelectors).forEach(propKey => {
            nextProps[propKey] = connectedSelectors[propKey](nextState)
          })
        }

        if (hasLogic) {
          key = _this.key ? _this.key(nextOwnProps) : 'index'

          if (typeof key === 'undefined') {
            console.error(`"key" can't be undefined in path: ${_this.path('undefined').join('.')}`)
          }

          path = _this.path(key)
          joinedPath = path.join('.')

          if (DEBUG) {
            console.log(`Inline selectorFactory for ${joinedPath}`)
            console.log({ nextOwnProps, nextState, key, path })
          }

          let selector
          let selectors

          // now we must check if the reducer is already in redux, or we need to add it
          // if we need to add it, create "dummy" selectors for the default values until then

          // is the reducer created? if we have "true" in the cache, it's definitely created
          let reducerCreated = inlineCache[joinedPath] && inlineCache[joinedPath].reducerCreated

          // if it's not let's double check. maybe it is now?
          if (!reducerCreated) {
            try {
              selector = (state) => pathSelector(path, state)
              reducerCreated = typeof selector(nextState) !== 'undefined'
            } catch (e) {
              reducerCreated = false
            }
          }

          // we have the selectors cached! with the current reducerCreated state!
          if (inlineCache[joinedPath] && inlineCache[joinedPath].reducerCreated === reducerCreated) {
            if (DEBUG) {
              console.log('cache hit!')
            }
            selector = inlineCache[joinedPath].selector
            selectors = inlineCache[joinedPath].selectors

          // either we have nothing cached or the cache is invalid. regenerate the selectors!
          } else {
            if (!selector) {
              selector = (state) => pathSelector(path, state)
            }

            // add { path } and { key } to the reducer creator function
            let localObject = Object.assign({}, object, { path, key, props: nextOwnProps })
            localObject.reducers = _this.reducers ? convertReducerArrays(_this.reducers(localObject)) : {}
            localObject.reducer = _this.reducer ? _this.reducer(localObject) : combineReducerObjects(path, localObject.reducers)

            // if the reducer is in redux, get real reducer selectors. otherwise add dummies that return defaults
            if (reducerCreated) {
              selectors = createSelectors(path, Object.keys(localObject.reducers))
            } else {
              addReducer(path, localObject.reducer, true)
              selectors = {}
              Object.keys(localObject.reducers).forEach(key => {
                selectors[key] = () => localObject.reducers[key].value
              })
            }

            // create
            const selectorResponse = _this.selectors ? _this.selectors(Object.assign({}, localObject, { selectors })) : {}

            Object.keys(selectorResponse).forEach(selectorKey => {
              // s == [() => args, selectorFunction, propType]
              const s = selectorResponse[selectorKey]

              const args = s[0]()
              selectors[selectorKey] = createSelector(...args, s[1])
            })

            // store in the cache
            inlineCache[joinedPath] = {
              reducerCreated,
              selector,
              selectors
            }
          }

          if (DEBUG) {
            console.log({ selector, selectors })
          }

          Object.keys(selectors).forEach(selectorKey => {
            nextProps[selectorKey] = selectors[selectorKey](nextState)
          })
        }

        if (DEBUG) {
          console.log({ nextProps })
        }

        // TODO: cache these even if no path present
        let actions = joinedPath ? actionCache[joinedPath] : null

        if (!actions) {
          actions = {}

          if (hasConnect) {
            // pass conneted actions as they are
            Object.keys(connectedActions).forEach(actionKey => {
              actions[actionKey] = (...args) => dispatch(connectedActions[actionKey](...args))
            })
          }

          if (hasLogic) {
            // inject key to the payload of inline actions
            Object.keys(object.actions).forEach(actionKey => {
              actions[actionKey] = (...args) => {
                const createdAction = object.actions[actionKey](...args)
                return dispatch({
                  ...createdAction,
                  payload: {
                    key,
                    ...createdAction.payload
                  }
                })
              }
            })
          }

          if (DEBUG) {
            console.log({ actions })
          }

          if (joinedPath) {
            actionCache[joinedPath] = actions
          }
        }

        // if the props did not change, return the old cached object
        if (!result || !shallowEqual(lastProps, nextProps)) {
          lastProps = nextProps
          result = Object.assign({}, nextProps, { actions, dispatch })
        }

        return result
      }
    }

    return connectAdvanced(selectorFactory, { methodName: 'kea' })(Klass)
  }

  response.path = object.path
  response.constants = object.constants
  response.actions = object.actions
  response.reducer = object.reducer
  response.reducers = object.reducers
  response.selector = object.selector
  response.selectors = object.selectors
  response.saga = object.saga

  response._isKeaFunction = true
  response._isKeaSingleton = isSingleton
  response._hasKeaConnect = hasConnect
  response._hasKeaLogic = hasLogic
  response._hasKeaSaga = hasSaga

  if (object.path) {
    if (isSingleton) {
      addReducer(object.path, object.reducer, true)
      response._keaReducerConnected = true
    } else {
      response._keaReducerConnected = false
    }
  }

  return response
}
<commit_msg>in non-initailized reducers, fallback to defaults instead of only defaults<commit_after>import { createPropTransforms, propTypesFromMapping } from './props'
import { createActions, createActionTransforms } from './actions'
import { combineReducerObjects, convertReducerArrays } from './reducer'
import { convertConstants } from './create'
import { pathSelector, createSelectors } from './selectors'
import { firstReducerRoot, addReducer, startSaga, cancelSaga } from '../scene/store'
import { createCombinedSaga } from '../scene/saga'
import shallowEqual from '../utils/shallow-equal'
import { createSaga } from '../saga/create'

import { createSelector } from 'reselect'
import { select, call } from 'redux-saga/effects'
import { connectAdvanced } from 'react-redux'

let inlineCache = {}
let actionCache = {}

export function cachedSelectors (path) {
  return inlineCache[path.join('.')] || {}
}

const DEBUG = false

let nonameCounter = 0

export function kea (_this) {
  const hasConnect = !!(_this.connect)
  const hasLogic = !!(_this.path || _this.actions || _this.reducer || _this.selectors)
  const hasSaga = !!(_this.sagas || _this.start || _this.stop || _this.takeEvery || _this.takeLatest || (_this.connect && _this.connect.sagas))
  const isSingleton = !_this.key

  let object = {}

  if (!_this.path) {
    let inlinePath = [firstReducerRoot(), '_kea', `inline-${nonameCounter++}`]
    _this.path = () => inlinePath
  }

  // pregenerate as many things as we can
  object.constants = _this.constants ? convertConstants(_this.constants(object)) : {}

  if (hasLogic) {
    // we don't know yet if it's going to be a singleton (no key) or inline (key)
    // however the actions and constants are common for all, so get a path without the dynamic
    // component and initialize them
    object.path = _this.path('').filter(p => p)
    object.actions = _this.actions ? createActions(_this.actions(object), object.path) : {}
    object.props = {}
  }

  if (hasLogic && isSingleton) {
    object.selector = (state) => pathSelector(object.path, state)
    object.reducers = _this.reducers ? convertReducerArrays(_this.reducers(object)) : {}
    object.reducer = _this.reducer ? _this.reducer(object) : combineReducerObjects(object.path, object.reducers)
    object.selectors = createSelectors(object.path, Object.keys(object.reducers || {}))

    const selectorResponse = _this.selectors ? _this.selectors(object) : {}
    Object.keys(selectorResponse).forEach(selectorKey => {
      // s == [() => args, selectorFunction, propType]
      const s = selectorResponse[selectorKey]
      const args = s[0]()
      if (s[2]) {
        object.reducers[selectorKey] = { type: s[2] }
      }
      object.selectors[selectorKey] = createSelector(...args, s[1])
    })

    object.get = function * (key) {
      return yield select(key ? object.selectors[key] : object.selector)
    }

    object.fetch = function * () {
      let results = {}

      const keys = Array.isArray(arguments[0]) ? arguments[0] : arguments

      for (let i = 0; i < keys.length; i++) {
        results[keys[i]] = yield object.get(keys[i])
      }

      return results
    }
  }

  let propTypes = {}
  let connect = {}
  let connectedActions = {}
  let connectedSelectors = {}

  if (hasConnect) {
    // the { connect: { props, actions } } part
    connect = _this.connect || {}

    // get default proptypes and add connected ones
    propTypes = Object.assign({}, connect.props ? propTypesFromMapping(connect) : {})

    // connected actions and props/selectors
    connectedActions = createActionTransforms(connect.actions).actions
    connectedSelectors = createPropTransforms(connect.props).selectorFunctions

    if (isSingleton) {
      object.actions = Object.assign({}, connectedActions, object.actions)
      object.selectors = Object.assign({}, connectedSelectors, object.selectors)
    }

    // we have _this: { connect: { sagas: [] } }, add to _this: { sagas: [] }
    if (connect.sagas) {
      _this.sagas = _this.sagas ? _this.sagas.concat(connect.sagas) : connect.sagas
    }
  }

  if (hasSaga && isSingleton) {
    object.saga = function * () {
      let sagas = (_this.sagas || []).map(saga => {
        return saga && saga._hasKeaSaga && saga.saga ? saga.saga : saga
      })

      if (_this.start || _this.stop || _this.takeEvery || _this.takeLatest) {
        if (!object._createdSaga) {
          const hasSelectors = !!(object.selectors && Object.keys(object.selectors).length > 0)
          const _singletonSagaBase = {
            start: _this.start,
            stop: _this.stop,
            takeEvery: _this.takeEvery,
            takeLatest: _this.takeLatest,
            workers: _this.workers ? Object.assign({}, _this.workers) : {},
            key: object.key,
            path: object.path,
            get: hasSelectors ? function * (key) {
              return yield select(key ? object.selectors[key] : object.selector)
            } : undefined,
            fetch: hasSelectors ? function * () {
              let results = {}
              const keys = Array.isArray(arguments[0]) ? arguments[0] : arguments
              for (let i = 0; i < keys.length; i++) {
                results[keys[i]] = yield this.get(keys[i])
              }
              return results
            } : undefined
          }

          let sagaActions = Object.assign({}, connectedActions, object.actions)

          object._createdSaga = createSaga(_singletonSagaBase, { actions: sagaActions })
        }

        sagas.push(object._createdSaga)
      }

      yield call(createCombinedSaga(sagas))
    }
  }

  const response = function (Klass) {
    // initializing as a singleton
    if (Klass === false) {
      if (!isSingleton) {
        console.error(`[KEA-LOGIC] Standalone "export default kea({})" functions must have no "key"!`, object.path, Klass)
      }

      return Object.assign(_this, object)
    }

    if (Klass && Klass.propTypes) {
      propTypes = Object.assign({}, propTypes, Klass.propTypes)
    }

    if (hasLogic) {
      // add proptypes from reducer
      const reducers = _this.reducers ? convertReducerArrays(_this.reducers(object)) : {}
      Object.keys(reducers).forEach(reducerKey => {
        if (reducers[reducerKey].type) {
          propTypes[reducerKey] = reducers[reducerKey].type
        }
      })

      // add proptypes from selectors
      const selectorsThatDontWork = _this.selectors ? _this.selectors({}) : {}
      Object.keys(selectorsThatDontWork).forEach(selectorKey => {
        if (selectorsThatDontWork[selectorKey][2]) {
          propTypes[selectorKey] = selectorsThatDontWork[selectorKey][2]
        }
      })
    }

    if (Klass && hasLogic) {
      // add kea metadata to component
      Klass.kea = {
        path: _this.path,
        constants: object.constants,
        actions: object.actions,
        find: (key) => cachedSelectors(_this.path(key))
      }
    }

    if (Klass && (hasLogic || hasConnect)) {
      // convert this.props.actions to this.actions in the component
      const originalComponentWillMount = Klass.prototype.componentWillMount
      Klass.prototype.componentWillMount = function () {
        this.actions = this.props.actions
        originalComponentWillMount && originalComponentWillMount.bind(this)()
      }
    }

    // check for sagas
    if (Klass && hasSaga) {
      const originalComponentDidMount = Klass.prototype.componentDidMount
      Klass.prototype.componentDidMount = function () {
        if (DEBUG) {
          console.log('component did mount')
        }

        // this === component instance
        this._keaSagaBase = {}
        this._keaRunningSaga = null

        const key = _this.key ? _this.key(this.props) : 'index'
        const path = _this.path(key)

        let sagas = (_this.sagas || []).map(saga => {
          return saga && saga._hasKeaSaga && saga.saga ? saga.saga : saga
        })

        if (_this.start || _this.stop || _this.takeEvery || _this.takeLatest) {
          this._keaSagaBase = {
            start: _this.start,
            stop: _this.stop,
            takeEvery: _this.takeEvery,
            takeLatest: _this.takeLatest,
            workers: _this.workers ? Object.assign({}, _this.workers) : {},
            key: key,
            path: path,
            props: this.props,
            get: function * (key) {
              const { selectors, selector } = cachedSelectors(path)
              return yield select(key ? selectors[key] : selector)
            },
            fetch: function * () {
              let results = {}
              const keys = Array.isArray(arguments[0]) ? arguments[0] : arguments
              for (let i = 0; i < keys.length; i++) {
                results[keys[i]] = yield this._keaSagaBase.get(keys[i])
              }
              return results
            }
          }

          let sagaActions = Object.assign({}, connectedActions)

          // inject key to the payload of inline actions
          Object.keys(object.actions || {}).forEach(actionKey => {
            sagaActions[actionKey] = (...args) => {
              const createdAction = object.actions[actionKey](...args)
              return {
                ...createdAction,
                payload: {
                  key,
                  ...createdAction.payload
                }
              }
            }
            sagaActions[actionKey].toString = object.actions[actionKey].toString
          })

          const saga = createSaga(this._keaSagaBase, { actions: sagaActions })
          sagas.push(saga)
        }

        if (sagas.length > 0) {
          this._keaRunningSaga = startSaga(createCombinedSaga(sagas))
        }

        originalComponentDidMount && originalComponentDidMount.bind(this)()
      }

      const originalComponentWillReceiveProps = Klass.prototype.componentWillReceiveProps
      Klass.prototype.componentWillReceiveProps = function (nextProps) {
        this._keaSagaBase.props = nextProps

        originalComponentWillReceiveProps && originalComponentWillReceiveProps.bind(this)(nextProps)
      }

      const originalComponentWillUnmount = Klass.prototype.componentWillUnmount
      Klass.prototype.componentWillUnmount = function () {
        if (DEBUG) {
          console.log('component will unmount')
        }
        if (this._keaRunningSaga) {
          cancelSaga(this._keaRunningSaga)
        }

        originalComponentWillUnmount && originalComponentWillUnmount.bind(this)()
      }
    }

    const selectorFactory = (dispatch, options) => {
      let lastProps = {}
      let result = null

      return (nextState, nextOwnProps) => {
        let key
        let path
        let joinedPath

        let nextProps = Object.assign({}, nextOwnProps)

        if (hasConnect) {
          // connected props
          Object.keys(connectedSelectors).forEach(propKey => {
            nextProps[propKey] = connectedSelectors[propKey](nextState)
          })
        }

        if (hasLogic) {
          key = _this.key ? _this.key(nextOwnProps) : 'index'

          if (typeof key === 'undefined') {
            console.error(`"key" can't be undefined in path: ${_this.path('undefined').join('.')}`)
          }

          path = _this.path(key)
          joinedPath = path.join('.')

          if (DEBUG) {
            console.log(`Inline selectorFactory for ${joinedPath}`)
            console.log({ nextOwnProps, nextState, key, path })
          }

          let selector
          let selectors

          // now we must check if the reducer is already in redux, or we need to add it
          // if we need to add it, create "dummy" selectors for the default values until then

          // is the reducer created? if we have "true" in the cache, it's definitely created
          let reducerCreated = inlineCache[joinedPath] && inlineCache[joinedPath].reducerCreated

          // if it's not let's double check. maybe it is now?
          if (!reducerCreated) {
            try {
              selector = (state) => pathSelector(path, state)
              reducerCreated = typeof selector(nextState) !== 'undefined'
            } catch (e) {
              reducerCreated = false
            }
          }

          // we have the selectors cached! with the current reducerCreated state!
          if (inlineCache[joinedPath] && inlineCache[joinedPath].reducerCreated === reducerCreated) {
            if (DEBUG) {
              console.log('cache hit!')
            }
            selector = inlineCache[joinedPath].selector
            selectors = inlineCache[joinedPath].selectors

          // either we have nothing cached or the cache is invalid. regenerate the selectors!
          } else {
            if (!selector) {
              selector = (state) => pathSelector(path, state)
            }

            // add { path } and { key } to the reducer creator function
            let localObject = Object.assign({}, object, { path, key, props: nextOwnProps })
            localObject.reducers = _this.reducers ? convertReducerArrays(_this.reducers(localObject)) : {}
            localObject.reducer = _this.reducer ? _this.reducer(localObject) : combineReducerObjects(path, localObject.reducers)

            // if the reducer is in redux, get real reducer selectors. otherwise add dummies that return defaults
            if (reducerCreated) {
              selectors = createSelectors(path, Object.keys(localObject.reducers))
            } else {
              addReducer(path, localObject.reducer, true)

              const realSelectors = createSelectors(path, Object.keys(localObject.reducers))

              // if we don't know for sure that the reducer is in the current store object,
              // then fallback to giving the default value
              selectors = {}
              Object.keys(localObject.reducers).forEach(key => {
                selectors[key] = (state) => {
                  try {
                    return realSelectors[key](state)
                  } catch (error) {
                    return localObject.reducers[key].value
                  }
                }
              })
            }

            // create
            const selectorResponse = _this.selectors ? _this.selectors(Object.assign({}, localObject, { selectors })) : {}

            Object.keys(selectorResponse).forEach(selectorKey => {
              // s == [() => args, selectorFunction, propType]
              const s = selectorResponse[selectorKey]

              const args = s[0]()
              selectors[selectorKey] = createSelector(...args, s[1])
            })

            // store in the cache
            inlineCache[joinedPath] = {
              reducerCreated,
              selector,
              selectors
            }
          }

          if (DEBUG) {
            console.log({ selector, selectors })
          }

          Object.keys(selectors).forEach(selectorKey => {
            nextProps[selectorKey] = selectors[selectorKey](nextState)
          })
        }

        if (DEBUG) {
          console.log({ nextProps })
        }

        // TODO: cache these even if no path present
        let actions = joinedPath ? actionCache[joinedPath] : null

        if (!actions) {
          actions = {}

          if (hasConnect) {
            // pass conneted actions as they are
            Object.keys(connectedActions).forEach(actionKey => {
              actions[actionKey] = (...args) => dispatch(connectedActions[actionKey](...args))
            })
          }

          if (hasLogic) {
            // inject key to the payload of inline actions
            Object.keys(object.actions).forEach(actionKey => {
              actions[actionKey] = (...args) => {
                const createdAction = object.actions[actionKey](...args)
                return dispatch({
                  ...createdAction,
                  payload: {
                    key,
                    ...createdAction.payload
                  }
                })
              }
            })
          }

          if (DEBUG) {
            console.log({ actions })
          }

          if (joinedPath) {
            actionCache[joinedPath] = actions
          }
        }

        // if the props did not change, return the old cached object
        if (!result || !shallowEqual(lastProps, nextProps)) {
          lastProps = nextProps
          result = Object.assign({}, nextProps, { actions, dispatch })
        }

        return result
      }
    }

    return connectAdvanced(selectorFactory, { methodName: 'kea' })(Klass)
  }

  response.path = object.path
  response.constants = object.constants
  response.actions = object.actions
  response.reducer = object.reducer
  response.reducers = object.reducers
  response.selector = object.selector
  response.selectors = object.selectors
  response.saga = object.saga

  response._isKeaFunction = true
  response._isKeaSingleton = isSingleton
  response._hasKeaConnect = hasConnect
  response._hasKeaLogic = hasLogic
  response._hasKeaSaga = hasSaga

  if (object.path) {
    if (isSingleton) {
      addReducer(object.path, object.reducer, true)
      response._keaReducerConnected = true
    } else {
      response._keaReducerConnected = false
    }
  }

  return response
}
<|endoftext|>


<commit_before>/**
 * @fileoverview
 * Implementation of line based glyphs defined by a series of nodes and paths.
 * @author Partridge Jiang
 */

/*
 * requires /lan/classes.js
 * requires /core/kekule.common.js
 * requires /core/kekule.structures.js
 * requires /chemdoc/kekule.glyph.base.js
 * requires /chemdoc/kekule.glyph.pathGlyphs.js
 */

(function(){
"use strict";

var NT = Kekule.Glyph.NodeType;
var PT = Kekule.Glyph.PathType;

/**
 * A glyph of straight line.
 * @class
 * @augments Kekule.Glyph.PathGlyph
 */
Kekule.Glyph.StraightLine = Class.create(Kekule.Glyph.PathGlyph,
/** @lends Kekule.Glyph.StraightLine# */
{
	/** @private */
	CLASS_NAME: 'Kekule.Glyph.StraightLine',
	/** @constructs */
	initialize: function($super, id, refLength, initialParams, coord2D, coord3D)
	{
		$super(id, refLength, initialParams, coord2D, coord3D);
	},
	/** @private */
	doCreateDefaultStructure: function(refLength, initialParams)
	{
		// initialParams can include additional field: lineLength
		var C = Kekule.CoordUtils;

		var coord2D = {'x': 0, 'y': 0};
		var coord3D = {'x': 0, 'y': 0, 'z': 0};
		var delta = {'x': refLength * (initialParams.lineLength || 1)};
		var node1 = new Kekule.Glyph.PathGlyphNode(null, null, coord2D, coord3D);
		var node2 = new Kekule.Glyph.PathGlyphNode(null, null, C.add(coord2D, delta), C.add(coord3D, delta));
		var connector = new Kekule.Glyph.PathGlyphConnector(null, PT.LINE, [node1, node2]);
		this._applyParamsToConnector(connector, initialParams);
		this.appendNode(node1);
		this.appendNode(node2);
		this.appendConnector(connector);
	},
	/** @private */
	_applyParamsToConnector: function(connector, initialParams)
	{
		//connector.setPathParams(initialParams);

		var p = Object.create(initialParams);
		if (Kekule.ObjUtils.isUnset(initialParams.autoOffset))
			p.autoOffset = false;
		connector.setPathParams(p);
	}
	/* @ignore */
	/*
	getAllowChildCoordStickTo: function(child, dest)
	{
		return true;
	},
	*/
	/* @ignore */
	/*
	getChildAcceptCoordStickFrom: function(child, fromObj)
	{
		return true;
	}
	*/
});

/**
 * A glyph of polygon formed by straight lines.
 * @class
 * @augments Kekule.Glyph.PathGlyph
 */
Kekule.Glyph.Polygon = Class.create(Kekule.Glyph.PathGlyph,
/** @lends Kekule.Glyph.Polygon# */
{
	/** @private */
	CLASS_NAME: 'Kekule.Glyph.Polygon',
	/** @constructs */
	initialize: function($super, id, refLength, initialParams, coord2D, coord3D)
	{
		$super(id, refLength, initialParams, coord2D, coord3D);
	},
	/** @private */
	getRefLengthRatio: function()
	{
		return 1;
	},
	/** @private */
	doCreateDefaultStructure: function(refLength, initialParams)
	{
		// initialParams can include additional field: lineLength, edgeCount
		//   node props, connector props
		var C = Kekule.CoordUtils;
		var coord2D = {'x': 0, 'y': 0};
		var coord3D = {'x': 0, 'y': 0, 'z': 0};
		var nodeProps = initialParams.nodeProps;
		var connectorProps = initialParams.connectorProps;
		var r = refLength * (initialParams.lineLength || 1) * this.getRefLengthRatio();
		var edgeCount = initialParams.edgeCount || 3;
		var angleDelta = Math.PI * 2 / edgeCount;
		var startingAngle = (edgeCount % 2)?0: -angleDelta / 2;
		var currAngle = startingAngle;
		var firstNode, lastNode;
		for (var i = 0; i < edgeCount; ++i)
		{
			var c = {'x': r * Math.sin(currAngle), 'y': r * Math.cos(currAngle)};
			var node = new Kekule.Glyph.PathGlyphNode(null, null, C.add(coord2D, c), C.add(coord3D, c));
			if (nodeProps)
				node.setPropValues(nodeProps);
			this.appendNode(node);
			if (i === 0)
				firstNode = node;
			if (lastNode)
			{
				var connector = new Kekule.Glyph.PathGlyphConnector(null, PT.LINE, [lastNode, node]);
				if (connectorProps)
					connector.setPropValues(connectorProps);
				this._applyParamsToConnector(connector, initialParams);
				this.appendConnector(connector);
			}
			lastNode = node;
			currAngle += angleDelta;
		}
		var connector = new Kekule.Glyph.PathGlyphConnector(null, PT.LINE, [node, firstNode]);
		if (connectorProps)
			connector.setPropValues(connectorProps);
		this._applyParamsToConnector(connector, initialParams);
		this.appendConnector(connector);
	},
	/** @private */
	_applyParamsToConnector: function(connector, initialParams)
	{
		connector.setPathParams(initialParams);
	}
});

/**
 * A base glyph of arc segment.
 * @class
 * @augments Kekule.Glyph.PathGlyph
 */
Kekule.Glyph.BaseArc = Class.create(Kekule.Glyph.PathGlyph,
/** @lends Kekule.Glyph.BaseArc# */
{
	/** @private */
	CLASS_NAME: 'Kekule.Glyph.BaseArc',
	/** @constructs */
	initialize: function($super, id, refLength, initialParams, coord2D, coord3D)
	{
		$super(id, refLength, initialParams, coord2D, coord3D);
	},
	/** @private */
	doCreateDefaultStructure: function(refLength, initialParams)
	{
		// initialParams can include additional field: lineLength
		var C = Kekule.CoordUtils;

		var coord2D = {'x': 0, 'y': 0};
		var coord3D = {'x': 0, 'y': 0, 'z': 0};
		var delta = {'x': refLength * (initialParams.lineLength || 1)};
		var controllerDelta = {'x': 0, 'y': delta.x / 2};

		var node1 = new Kekule.Glyph.PathGlyphNode(null, null, coord2D, coord3D);  // starting node
		var node2 = new Kekule.Glyph.PathGlyphNode(null, null, C.add(coord2D, delta), C.add(coord3D, delta));  // ending node
		this.appendNode(node1);
		this.appendNode(node2);

		//var node3 = new Kekule.Glyph.PathGlyphNode(null, Kekule.Glyph.NodeType.CONTROLLER, C.add(coord2D, controllerDelta), C.add(coord3D, controllerDelta));  // control node
		var connector = new Kekule.Glyph.PathGlyphArcConnector(null, [node1, node2]);
		this._applyParamsToConnector(connector, initialParams);

		/*
		var controlPoint = connector.getControlPoint();
		//controlPoint.setDistanceToChord(delta.x)
		controlPoint.setCoord2D(controllerDelta)
				.setCoord3D(controllerDelta);
		*/

		this.appendConnector(connector);

		connector.setInteractMode(Kekule.ChemObjInteractMode.HIDDEN);
		//node1.setInteractMode(Kekule.ChemObjInteractMode.HIDDEN);
		//node2.setInteractMode(Kekule.ChemObjInteractMode.HIDDEN);
	},
	/** @private */
	_isValidChemNodeOrConnectorStickTarget: function(targetObj)
	{
		var result = (targetObj instanceof Kekule.ChemStructureNode) || (targetObj instanceof Kekule.ChemStructureConnector);
		if (!result && (targetObj instanceof Kekule.ChemMarker.BaseMarker))
		{
			var parent = targetObj.getParent && targetObj.getParent();
			if (parent)  // the marker of node/connector (e.g., electron pair) can also be a valid target
				result = (parent instanceof Kekule.ChemStructureNode) || (parent instanceof Kekule.ChemStructureConnector);
		}
		return result;
	},
	/** @private */
	_applyParamsToConnector: function(connector, initialParams)
	{
		connector.setPathParams(initialParams);
	},
	/** @ignore */
	getAllowChildCoordStickTo: function(child, dest)
	{
		var result = !dest  // dest not set, a general test, just returns true
			|| (this._isValidChemNodeOrConnectorStickTarget(dest, child) && !this._isChildrenStickingTo(dest, [child]));
			/*
			|| ((dest instanceof Kekule.ChemStructureNode) || (dest instanceof Kekule.ChemStructureConnector)) && (!this._isChildrenStickingTo(dest, [child]))
			|| (dest instanceof Kekule.ChemMarker.BaseMarker);
			*/
		//console.log('allow stick to', dest && dest.getClassName(), result);
		return result;
	},
	/** @ignore */
	getChildUseCoordStickOffset: function($super, child, stickDest)
	{
		if (stickDest instanceof Kekule.ChemStructureNode || stickDest instanceof Kekule.ChemStructureConnector)
		{
			return true;
		}
		else
		{
			return false;
			//return $super(child, stickDest);
		}
	},
	/** @private */
	_isChildrenStickingTo: function(dest, excludeChildren)
	{
		var nodes = this.getNodes();
		nodes = Kekule.ArrayUtils.exclude(nodes, excludeChildren || []);
		for (var i = 0, l = nodes.length; i < l; ++i)
		{
			var n = nodes[i];
			if (n.getCoordStickTarget() === dest)
				return true;
		}
		return false;
	}
});

})();<commit_msg>Initial implementation of BaseTwinArc glyph<commit_after>/**
 * @fileoverview
 * Implementation of line based glyphs defined by a series of nodes and paths.
 * @author Partridge Jiang
 */

/*
 * requires /lan/classes.js
 * requires /core/kekule.common.js
 * requires /core/kekule.structures.js
 * requires /chemdoc/kekule.glyph.base.js
 * requires /chemdoc/kekule.glyph.pathGlyphs.js
 */

(function(){
"use strict";

var NT = Kekule.Glyph.NodeType;
var PT = Kekule.Glyph.PathType;
var CU = Kekule.CoordUtils;
var CM = Kekule.CoordMode;

/**
 * A glyph of straight line.
 * @class
 * @augments Kekule.Glyph.PathGlyph
 */
Kekule.Glyph.StraightLine = Class.create(Kekule.Glyph.PathGlyph,
/** @lends Kekule.Glyph.StraightLine# */
{
	/** @private */
	CLASS_NAME: 'Kekule.Glyph.StraightLine',
	/** @constructs */
	initialize: function($super, id, refLength, initialParams, coord2D, coord3D)
	{
		$super(id, refLength, initialParams, coord2D, coord3D);
	},
	/** @private */
	doCreateDefaultStructure: function(refLength, initialParams)
	{
		// initialParams can include additional field: lineLength
		var C = Kekule.CoordUtils;

		var coord2D = {'x': 0, 'y': 0};
		var coord3D = {'x': 0, 'y': 0, 'z': 0};
		var delta = {'x': refLength * (initialParams.lineLength || 1)};
		var node1 = new Kekule.Glyph.PathGlyphNode(null, null, coord2D, coord3D);
		var node2 = new Kekule.Glyph.PathGlyphNode(null, null, C.add(coord2D, delta), C.add(coord3D, delta));
		var connector = new Kekule.Glyph.PathGlyphConnector(null, PT.LINE, [node1, node2]);
		this._applyParamsToConnector(connector, initialParams);
		this.appendNode(node1);
		this.appendNode(node2);
		this.appendConnector(connector);
	},
	/** @private */
	_applyParamsToConnector: function(connector, initialParams)
	{
		//connector.setPathParams(initialParams);

		var p = Object.create(initialParams);
		if (Kekule.ObjUtils.isUnset(initialParams.autoOffset))
			p.autoOffset = false;
		connector.setPathParams(p);
	}
	/* @ignore */
	/*
	getAllowChildCoordStickTo: function(child, dest)
	{
		return true;
	},
	*/
	/* @ignore */
	/*
	getChildAcceptCoordStickFrom: function(child, fromObj)
	{
		return true;
	}
	*/
});

/**
 * A glyph of polygon formed by straight lines.
 * @class
 * @augments Kekule.Glyph.PathGlyph
 */
Kekule.Glyph.Polygon = Class.create(Kekule.Glyph.PathGlyph,
/** @lends Kekule.Glyph.Polygon# */
{
	/** @private */
	CLASS_NAME: 'Kekule.Glyph.Polygon',
	/** @constructs */
	initialize: function($super, id, refLength, initialParams, coord2D, coord3D)
	{
		$super(id, refLength, initialParams, coord2D, coord3D);
	},
	/** @private */
	getRefLengthRatio: function()
	{
		return 1;
	},
	/** @private */
	doCreateDefaultStructure: function(refLength, initialParams)
	{
		// initialParams can include additional field: lineLength, edgeCount
		//   node props, connector props
		var C = Kekule.CoordUtils;
		var coord2D = {'x': 0, 'y': 0};
		var coord3D = {'x': 0, 'y': 0, 'z': 0};
		var nodeProps = initialParams.nodeProps;
		var connectorProps = initialParams.connectorProps;
		var r = refLength * (initialParams.lineLength || 1) * this.getRefLengthRatio();
		var edgeCount = initialParams.edgeCount || 3;
		var angleDelta = Math.PI * 2 / edgeCount;
		var startingAngle = (edgeCount % 2)?0: -angleDelta / 2;
		var currAngle = startingAngle;
		var firstNode, lastNode;
		for (var i = 0; i < edgeCount; ++i)
		{
			var c = {'x': r * Math.sin(currAngle), 'y': r * Math.cos(currAngle)};
			var node = new Kekule.Glyph.PathGlyphNode(null, null, C.add(coord2D, c), C.add(coord3D, c));
			if (nodeProps)
				node.setPropValues(nodeProps);
			this.appendNode(node);
			if (i === 0)
				firstNode = node;
			if (lastNode)
			{
				var connector = new Kekule.Glyph.PathGlyphConnector(null, PT.LINE, [lastNode, node]);
				if (connectorProps)
					connector.setPropValues(connectorProps);
				this._applyParamsToConnector(connector, initialParams);
				this.appendConnector(connector);
			}
			lastNode = node;
			currAngle += angleDelta;
		}
		var connector = new Kekule.Glyph.PathGlyphConnector(null, PT.LINE, [node, firstNode]);
		if (connectorProps)
			connector.setPropValues(connectorProps);
		this._applyParamsToConnector(connector, initialParams);
		this.appendConnector(connector);
	},
	/** @private */
	_applyParamsToConnector: function(connector, initialParams)
	{
		connector.setPathParams(initialParams);
	}
});

/**
 * A base glyph of arc segment.
 * @class
 * @augments Kekule.Glyph.PathGlyph
 */
Kekule.Glyph.BaseArc = Class.create(Kekule.Glyph.PathGlyph,
/** @lends Kekule.Glyph.BaseArc# */
{
	/** @private */
	CLASS_NAME: 'Kekule.Glyph.BaseArc',
	/** @constructs */
	initialize: function($super, id, refLength, initialParams, coord2D, coord3D)
	{
		$super(id, refLength, initialParams, coord2D, coord3D);
	},
	/** @ignore */
	doCreateDefaultStructure: function(refLength, initialParams)
	{
		// initialParams can include additional field: lineLength
		var C = Kekule.CoordUtils;

		var coord2D = {'x': 0, 'y': 0};
		var coord3D = {'x': 0, 'y': 0, 'z': 0};
		var delta = {'x': refLength * (initialParams.lineLength || 1)};
		//var controllerDelta = {'x': 0, 'y': delta.x / 2};

		var node1 = new Kekule.Glyph.PathGlyphNode(null, null, coord2D, coord3D);  // starting node
		var node2 = new Kekule.Glyph.PathGlyphNode(null, null, C.add(coord2D, delta), C.add(coord3D, delta));  // ending node
		this.appendNode(node1);
		this.appendNode(node2);

		//var node3 = new Kekule.Glyph.PathGlyphNode(null, Kekule.Glyph.NodeType.CONTROLLER, C.add(coord2D, controllerDelta), C.add(coord3D, controllerDelta));  // control node
		var connector = new Kekule.Glyph.PathGlyphArcConnector(null, [node1, node2]);
		this._applyParamsToConnector(connector, initialParams);

		/*
		var controlPoint = connector.getControlPoint();
		//controlPoint.setDistanceToChord(delta.x)
		controlPoint.setCoord2D(controllerDelta)
				.setCoord3D(controllerDelta);
		*/

		this.appendConnector(connector);

		connector.setInteractMode(Kekule.ChemObjInteractMode.HIDDEN);
		//node1.setInteractMode(Kekule.ChemObjInteractMode.HIDDEN);
		//node2.setInteractMode(Kekule.ChemObjInteractMode.HIDDEN);
	},
	/** @private */
	_isValidChemNodeOrConnectorStickTarget: function(targetObj)
	{
		var result = (targetObj instanceof Kekule.ChemStructureNode) || (targetObj instanceof Kekule.ChemStructureConnector);
		if (!result && (targetObj instanceof Kekule.ChemMarker.BaseMarker))
		{
			var parent = targetObj.getParent && targetObj.getParent();
			if (parent)  // the marker of node/connector (e.g., electron pair) can also be a valid target
				result = (parent instanceof Kekule.ChemStructureNode) || (parent instanceof Kekule.ChemStructureConnector);
		}
		return result;
	},
	/** @private */
	_applyParamsToConnector: function(connector, initialParams)
	{
		connector.setPathParams(initialParams);
	},
	/** @ignore */
	getAllowChildCoordStickTo: function(child, dest)
	{
		var result = !dest  // dest not set, a general test, just returns true
			|| (this._isValidChemNodeOrConnectorStickTarget(dest, child) && !this._isChildrenStickingTo(dest, [child]));
			/*
			|| ((dest instanceof Kekule.ChemStructureNode) || (dest instanceof Kekule.ChemStructureConnector)) && (!this._isChildrenStickingTo(dest, [child]))
			|| (dest instanceof Kekule.ChemMarker.BaseMarker);
			*/
		//console.log('allow stick to', dest && dest.getClassName(), result);
		return result;
	},
	/** @ignore */
	getChildUseCoordStickOffset: function($super, child, stickDest)
	{
		if (stickDest instanceof Kekule.ChemStructureNode || stickDest instanceof Kekule.ChemStructureConnector)
		{
			return true;
		}
		else
		{
			return false;
			//return $super(child, stickDest);
		}
	},
	/** @private */
	_isChildrenStickingTo: function(dest, excludeChildren)
	{
		var nodes = this.getNodes();
		nodes = Kekule.ArrayUtils.exclude(nodes, excludeChildren || []);
		for (var i = 0, l = nodes.length; i < l; ++i)
		{
			var n = nodes[i];
			if (n.getCoordStickTarget() === dest)
				return true;
		}
		return false;
	}
});

/**
 * A base glyph of double arc glyph (for bond forming electron pushing arrow).
 * @class
 * @augments Kekule.Glyph.PathGlyph
 */
Kekule.Glyph.BaseTwinArc = Class.create(Kekule.Glyph.PathGlyph,
/** @lends Kekule.Glyph.BaseTwinArc# */
{
	/** @private */
	CLASS_NAME: 'Kekule.Glyph.BaseTwinArc',
	/**
	 * @constructs
	 */
	initialize: function($super, id, refLength, initialParams, coord2D, coord3D)
	{
		/*
		this._insideNodeGetIndirectCoordRefLengthsBind = this._insideNodeGetIndirectCoordRefLengths.bind(this);
		this._insideNodeGetIndirectCoordRefCoordsBind = this._insideNodeGetIndirectCoordRefCoords.bind(this);
		this._insideNodeCalcIndirectCoordStorageBind = this._insideNodeCalcIndirectCoordStorage.bind(this);
		this._insideNodeCalcIndirectCoordValueBind = this._insideNodeCalcIndirectCoordValue.bind(this);
		*/
		$super(id, id, refLength, initialParams, coord2D, coord3D);
	},
	/** @ignore */
	doCreateDefaultStructure: function(refLength, initialParams)
	{
		// initialParams can include additional field: lineLength
		var C = Kekule.CoordUtils;

		var coord2D = {'x': 0, 'y': 0};
		var coord3D = {'x': 0, 'y': 0, 'z': 0};
		var lineLength = initialParams.lineLength || 1;
		var lineGap = (initialParams.lineGap || lineLength / 5);
		var deltaLine = {'x': refLength * ((lineLength - lineGap) / 2)};
		var deltaGap = {'x': refLength * lineGap};
		var deltaTotal = {'x': lineLength};

		var node1 = new Kekule.Glyph.PathGlyphNode(null, null, coord2D, coord3D);  // starting node of first arc
		var node2 = new Kekule.Glyph.PathGlyphNode(null, null);  // ending node of first arc
		node2.setEnableIndirectCoord(true)
			.overwriteMethod('getIndirectCoordRefLengths', this._insideNodeGetIndirectCoordRefLengths)
			.overwriteMethod('getIndirectCoordRefCoords', this._insideNodeGetIndirectCoordRefCoords)
			.overwriteMethod('calcIndirectCoordStorage', this._insideNodeCalcIndirectCoordStorage)
			.overwriteMethod('calcIndirectCoordValue', this._insideNodeCalcIndirectCoordValue);
		var node3 = new Kekule.Glyph.PathGlyphNode(null, null);  // starting node of second arc
		//node3.setEnableIndirectCoord(true);
		var node4 = new Kekule.Glyph.PathGlyphNode(null, null, C.add(coord2D, deltaTotal), C.add(coord3D, deltaTotal));  // ending node of second arc
		this.appendNode(node1);
		this.appendNode(node2);
		this.appendNode(node3);
		this.appendNode(node4);

		node2.setCoord2D(C.add(coord2D, deltaLine)).setCoord3D(C.add(coord3D, deltaLine));
		node3.setCoord2D(C.add(coord2D, CU.add(deltaLine, deltaGap))).setCoord3D(C.add(coord3D, CU.add(deltaLine, deltaGap)));

		var connector1 = new Kekule.Glyph.PathGlyphArcConnector(null, [node1, node2]);
		var connector2 = new Kekule.Glyph.PathGlyphArcConnector(null, [node4, node3]);
		this._applyParamsToConnector(connector1, initialParams);
		this._applyParamsToConnector(connector2, initialParams);

		this.appendConnector(connector1);
		this.appendConnector(connector2);

		connector1.setInteractMode(Kekule.ChemObjInteractMode.HIDDEN);
		connector2.setInteractMode(Kekule.ChemObjInteractMode.HIDDEN);
	},
	/** @private */
	_applyParamsToConnector: function(connector, initialParams)
	{
		connector.setPathParams(initialParams);
	},

	// overwrite methods of two inside nodes of arcs, so this these methods, this var refers to the node but not the glyph
	/** @private */
	_insideNodeGetIndirectCoordRefLengths: function($old, coordMode, allowCoordBorrow)
	{
		var glyph = this.getParent();
		if (glyph && coordMode === Kekule.CoordMode.COORD2D)
		{
			var nodeStart = glyph.getNodeAt(0);
			var nodeEnd = glyph.getNodeAt(glyph.getNodeCount() - 1);
			if (nodeStart && nodeEnd)
			{
				var coord1 = nodeStart.getAbsCoordOfMode(coordMode);
				var coord2 = nodeEnd.getAbsCoordOfMode(coordMode);
				var d = Kekule.CoordUtils.getDistance(coord2, coord1);
				var result = {'x': d, 'y': d, 'length': d};
				//console.log('_insideNodeGetIndirectCoordRefLengths', result);
				return result;
			}
		}
		else
			return $old(coordMode, allowCoordBorrow);
	},
	/** @private */
	_insideNodeGetIndirectCoordRefCoords: function($old, coordMode, allowCoordBorrow)
	{
		var glyph = this.getParent();
		if (glyph && coordMode === CM.COORD2D)
		{
			var nodeStart = glyph.getNodeAt(0);
			var nodeEnd = glyph.getNodeAt(glyph.getNodeCount() - 1);
			if (nodeStart && nodeEnd)
			{
				/*
				var coord1 = nodeStart.getAbsCoordOfMode(coordMode);
				var coord2 = nodeEnd.getAbsCoordOfMode(coordMode);
				*/
				var coord1 = nodeStart.getCoordOfMode(coordMode);
				var coord2 = nodeEnd.getCoordOfMode(coordMode);
				//console.log('_insideNodeGetIndirectCoordRefCoords', coord);
				return [coord1, coord2];
			}
		}
		else
			return $old(coordMode, allowCoordBorrow);
	},
	/** @private */
	_insideNodeCalcIndirectCoordStorage: function($old, coordMode, coordValue, oldCoordValue, allowCoordBorrow)
	{
		var glyph = this.getParent();
		if (glyph && coordMode === CM.COORD2D)
		{
			//var refLength = this._insideNodeGetIndirectCoordRefLengths(null, coordMode, allowCoordBorrow).length;
			var refCoords = this.getIndirectCoordRefCoords(coordMode, allowCoordBorrow);
			//var refLength = CU.getDistance(refCoords[1], refCoords[0]);
			if (refCoords)
			{
				var nodeStart = glyph.getNodeAt(0);
				var baseCoord = nodeStart.getCoordOfMode(coordMode);

				var crossPoint = Kekule.GeometryUtils.getPerpendicularCrossPointFromCoordToLine(coordValue, refCoords[0], refCoords[1], false);
				if (!crossPoint)  // outside ref line
				{
					var d0 =CU.getDistance(coordValue, refCoords[0]);
					var d1 =CU.getDistance(coordValue, refCoords[1]);
					if (d0 < d1)
						crossPoint = refCoords[0];
					else
						crossPoint = refCoords[1];
				}
				var crossPointDistance = CU.getDistance(baseCoord, crossPoint);
				var refDistance = CU.getDistance(refCoords[1], refCoords[0]);

				var ratio = refDistance? crossPointDistance / refDistance: 0;

				if (ratio < 0)
				{
					ratio = 0;
				}
				else if (ratio > 1)
				{
					ratio = 1;
				}

				return {'x': ratio, 'y': ratio, 'ratio': ratio};
			}
		}

		return $old(coordMode, coordValue, oldCoordValue, allowCoordBorrow);
	},
	/** @private */
	_insideNodeCalcIndirectCoordValue: function($old, coordMode, allowCoordBorrow)
	{
		var glyph = this.getParent();
		if (glyph && coordMode === CM.COORD2D)
		{
			var refCoords = this.getIndirectCoordRefCoords(coordMode, allowCoordBorrow);
			if (refCoords)
			{
				var refDelta = CU.substract(refCoords[1], refCoords[0]);
				var nodeStart = glyph.getNodeAt(0);
				var baseCoord = nodeStart.getCoordOfMode(coordMode);
				var ratio = this.getIndirectCoordStorageOfMode(coordMode)['ratio'];
				if (Kekule.ObjUtils.notUnset(ratio))
				{
					var delta = CU.multiply(refDelta, ratio);
					var result = CU.add(baseCoord, delta);
					//console.log('calc', this.getIndirectCoordStorageOfMode(coordMode), ratio, delta, result);
					return result;
				}
			}
		}

		return $old(coordMode, allowCoordBorrow);
	},

	/** @private */
	_isValidChemNodeOrConnectorStickTarget: function(targetObj)
	{
		var result = (targetObj instanceof Kekule.ChemStructureNode) || (targetObj instanceof Kekule.ChemStructureConnector);
		if (!result && (targetObj instanceof Kekule.ChemMarker.BaseMarker))
		{
			var parent = targetObj.getParent && targetObj.getParent();
			if (parent)  // the marker of node/connector (e.g., electron pair) can also be a valid target
				result = (parent instanceof Kekule.ChemStructureNode) || (parent instanceof Kekule.ChemStructureConnector);
		}
		return result;
	},
	/** @ignore */
	getAllowChildCoordStickTo: function(child, dest)
	{
		var index = this.indexOfNode(child);
		if (index === 0 || index === this.getNodeCount() - 1)  // only the leading and tailing node can be sticked
		{
			var result = !dest  // dest not set, a general test, just returns true
				|| (this._isValidChemNodeOrConnectorStickTarget(dest, child) && !this._isChildrenStickingTo(dest, [child]));
			return result;
		}
		else
			return false;
	},
	/** @ignore */
	getChildUseCoordStickOffset: function($super, child, stickDest)
	{
		if (stickDest instanceof Kekule.ChemStructureNode || stickDest instanceof Kekule.ChemStructureConnector)
		{
			return true;
		}
		else
		{
			return false;
		}
	},
	/** @private */
	_isChildrenStickingTo: function(dest, excludeChildren)
	{
		var nodes = this.getNodes();
		nodes = Kekule.ArrayUtils.exclude(nodes, excludeChildren || []);
		for (var i = 0, l = nodes.length; i < l; ++i)
		{
			var n = nodes[i];
			if (n.getCoordStickTarget() === dest)
				return true;
		}
		return false;
	}
});

})();<|endoftext|>