<commit_before>"use strict";

const O            = require ('es7-object-polyfill'),
      bullet       = require ('string.bullet'),
      isBrowser    = (typeof window !== 'undefined') && (window.window === window) && window.navigator,
      maxOf        = (arr, pick) => arr.reduce ((max, s) => Math.max (max, pick ? pick (s) : s), 0),
      isInteger    = Number.isInteger || (value => (typeof value === 'number') && isFinite (value) && (Math.floor (value) === value)),
      isTypedArray = x => (x instanceof Float32Array) ||
                          (x instanceof Float64Array) ||
                          (x instanceof Int8Array) ||
                          (x instanceof Uint8Array) ||
                          (x instanceof Uint8ClampedArray) ||
                          (x instanceof Int16Array) ||
                          (x instanceof Int32Array) ||
                          (x instanceof Uint32Array)

const assignProps = (to, from) => { for (const prop in from) { O.defineProperty (to, prop, O.getOwnPropertyDescriptor (from, prop)) }; return to }

const configure = cfg => {

    const stringify = x => {

            const state = O.assign ({ parents: new Set (), siblings: new Map () }, cfg)

            if (cfg.pretty === 'auto') {
                const   oneLine =                         stringify.configure ({ pretty: false, siblings: new Map () }) (x)
                return (oneLine.length <= 80) ? oneLine : stringify.configure ({ pretty: true,  siblings: new Map () }) (x) }

            var customFormat = cfg.formatter && cfg.formatter (x, stringify)

            if (typeof customFormat === 'string') {
                return customFormat }

            if ((typeof jQuery !== 'undefined') && (x instanceof jQuery)) {
                x = x.toArray () }

            else if (isTypedArray (x)) {
                x = Array.from (x) }

            if (isBrowser && (x === window)) {
                return 'window' }

            else if (!isBrowser && (typeof global !== 'undefined') && (x === global)) {
                return 'global' }

            else if (x === null) {
                return 'null' }

            else if (x instanceof Date) {
                return state.pure ? x.getTime () : "📅  " + x.toString () }

            else if (state.parents.has (x)) {
                return state.pure ? undefined : '<cyclic>' }

            else if (!state.pure && state.siblings.has (x)) {
                return '<ref:' + state.siblings.get (x) + '>' }

            else if (x && (typeof Symbol !== 'undefined')
                       && (customFormat = x[Symbol.for ('String.ify')])
                       && (typeof (customFormat = customFormat.call (x, stringify.configure (state))) === 'string')) {

                return customFormat }

            else if (x instanceof Function) {
                return (cfg.pure ? x.toString () : (x.name ? ('<function:' + x.name + '>') : '<function>')) }

            else if (typeof x === 'string') {
                return '"' + stringify.limit (x, cfg.pure ? Number.MAX_SAFE_INTEGER : cfg.maxStringLength) + '"' }

            else if (typeof x === 'object') {

                state.parents.add (x)
                state.siblings.set (x, state.siblings.size)

                const result = stringify.configure (O.assign ({}, state, { depth: state.depth + 1 })).object (x)

                state.parents.delete (x)

                return result }

            else if (!isInteger (x) && (cfg.precision > 0)) {
                return x.toFixed (cfg.precision) }

            else {
                return String (x) }
        }

    /*  API  */

        assignProps (stringify, {

            state: cfg,

            configure: newConfig => configure (O.assign ({}, cfg, newConfig)),

        /*  TODO: generalize generation of these chain-style .configure helpers (maybe in a separate library, as it looks like a common pattern)    */

            get pretty   () { return stringify.configure ({ pretty: true }) },
            get noPretty () { return stringify.configure ({ pretty: false }) },

            get json () { return stringify.configure ({ json: true, pure: true }) },
            get pure () { return stringify.configure ({ pure: true }) },

            maxStringLength (n = Number.MAX_SAFE_INTEGER) { return stringify.configure ({ maxStringLength: n }) },
            maxArrayLength  (n = Number.MAX_SAFE_INTEGER) { return stringify.configure ({ maxArrayLength: n }) },
            maxDepth        (n = Number.MAX_SAFE_INTEGER) { return stringify.configure ({ maxDepth: n }) },

            precision (p) { return stringify.configure ({ precision: p }) },
            formatter (f) { return stringify.configure ({ formatter: f }) },

        /*  Some undocumented internals    */

            limit: (s, n) => s && ((s.length <= n) ? s : (s.substr (0, n - 1) + '…')),

            rightAlign: strings => {
                            var max = maxOf (strings, s => s.length)
                            return strings.map (s => ' '.repeat (max - s.length) + s) },

            object: x => {

                if (x instanceof Set) {
                    x = Array.from (x.values ()) }

                else if (x instanceof Map) {
                    x = Array.from (x.entries ()) }

                const isArray = Array.isArray (x)

                if (isBrowser) {

                    if (x instanceof Element) {
                        return '<' + (x.tagName.toLowerCase () +
                                    ((x.id && ('#' + x.id)) || '') +
                                    ((x.className && ('.' + x.className)) || '')) + '>' }

                    else if (x instanceof Text) {
                        return '@' + stringify.limit (x.wholeText, 20) } }

                if (!cfg.pure && ((cfg.depth > cfg.maxDepth) || (isArray && (x.length > cfg.maxArrayLength)))) {
                    return isArray ? '<array[' + x.length + ']>' : '<object>' }

                const pretty   = cfg.pretty ? true : false,
                      entries  = O.entries (x),
                      oneLine  = !pretty || (entries.length < 2),
                      quoteKey = cfg.json ? (k => '"' + k + '"') : (k => k)

                if (pretty) {

                    const values        = O.values (x),
                          printedKeys   = stringify.rightAlign (O.keys (x).map (k => quoteKey (k) + ': ')),
                          printedValues = values.map (stringify),
                          leftPaddings  = printedValues.map ((x, i) => (((x[0] === '[') ||
                                                                         (x[0] === '{'))
                                                                            ? 3
                                                                            : ((typeof values[i] === 'string') ? 1 : 0))),
                          maxLeftPadding = maxOf (leftPaddings),

                          items = leftPaddings.map ((padding, i) => {
                                            const value = ' '.repeat (maxLeftPadding - padding) + printedValues[i]
                                            return isArray ? value : bullet (printedKeys[i], value) }),

                          printed = bullet (isArray ? '[ ' :
                                                      '{ ', items.join (',\n')),

                          lines    = printed.split ('\n'),
                          lastLine = lines[lines.length - 1]

                    return printed +  (' '.repeat (maxOf (lines, l => l.length) - lastLine.length) + (isArray ? ' ]' : ' }')) }

                else {

                    const items   = entries.map (kv => (isArray ? '' : (quoteKey (kv[0]) + ': ')) + stringify (kv[1])),
                          content = items.join (', ')

                    return isArray
                            ? ('['  + content +  ']')
                            : ('{ ' + content + ' }')
                }
            }
        })

        return stringify
    }

module.exports = configure ({

                    depth:           0,
                    pure:            false,
                    json:            false,
                //  color:           false, // not supported yet
                    maxDepth:        5,
                    maxArrayLength:  60,
                    maxStringLength: 60,
                    precision:       undefined,
                    formatter:       undefined,
                    pretty:         'auto'

                })

<commit_msg>a bugfix<commit_after>"use strict";

const O            = require ('es7-object-polyfill'),
      bullet       = require ('string.bullet'),
      isBrowser    = (typeof window !== 'undefined') && (window.window === window) && window.navigator,
      maxOf        = (arr, pick) => arr.reduce ((max, s) => Math.max (max, pick ? pick (s) : s), 0),
      isInteger    = Number.isInteger || (value => (typeof value === 'number') && isFinite (value) && (Math.floor (value) === value)),
      isTypedArray = x => (x instanceof Float32Array) ||
                          (x instanceof Float64Array) ||
                          (x instanceof Int8Array) ||
                          (x instanceof Uint8Array) ||
                          (x instanceof Uint8ClampedArray) ||
                          (x instanceof Int16Array) ||
                          (x instanceof Int32Array) ||
                          (x instanceof Uint32Array)

const assignProps = (to, from) => { for (const prop in from) { O.defineProperty (to, prop, O.getOwnPropertyDescriptor (from, prop)) }; return to }

const configure = cfg => {

    const stringify = x => {

            const state = O.assign ({ parents: new Set (), siblings: new Map () }, cfg)

            if (cfg.pretty === 'auto') {
                const   oneLine =                         stringify.configure ({ pretty: false, siblings: new Map () }) (x)
                return (oneLine.length <= 80) ? oneLine : stringify.configure ({ pretty: true,  siblings: new Map () }) (x) }

            var customFormat = cfg.formatter && cfg.formatter (x, stringify)

            if (typeof customFormat === 'string') {
                return customFormat }

            if ((typeof jQuery !== 'undefined') && (x instanceof jQuery)) {
                x = x.toArray () }

            else if (isTypedArray (x)) {
                x = Array.from (x) }

            if (isBrowser && (x === window)) {
                return 'window' }

            else if (!isBrowser && (typeof global !== 'undefined') && (x === global)) {
                return 'global' }

            else if (x === null) {
                return 'null' }

            else if (x instanceof Date) {
                return state.pure ? x.getTime () : "📅  " + x.toString () }

            else if (state.parents.has (x)) {
                return state.pure ? undefined : '<cyclic>' }

            else if (!state.pure && state.siblings.has (x)) {
                return '<ref:' + state.siblings.get (x) + '>' }

            else if (x && (typeof Symbol !== 'undefined')
                       && (customFormat = x[Symbol.for ('String.ify')])
                       && (typeof customFormat === 'function')
                       && (typeof (customFormat = customFormat.call (x, stringify.configure (state))) === 'string')) {

                return customFormat }

            else if (x instanceof Function) {
                return (cfg.pure ? x.toString () : (x.name ? ('<function:' + x.name + '>') : '<function>')) }

            else if (typeof x === 'string') {
                return '"' + stringify.limit (x, cfg.pure ? Number.MAX_SAFE_INTEGER : cfg.maxStringLength) + '"' }

            else if (typeof x === 'object') {

                state.parents.add (x)
                state.siblings.set (x, state.siblings.size)

                const result = stringify.configure (O.assign ({}, state, { depth: state.depth + 1 })).object (x)

                state.parents.delete (x)

                return result }

            else if (!isInteger (x) && (cfg.precision > 0)) {
                return x.toFixed (cfg.precision) }

            else {
                return String (x) }
        }

    /*  API  */

        assignProps (stringify, {

            state: cfg,

            configure: newConfig => configure (O.assign ({}, cfg, newConfig)),

        /*  TODO: generalize generation of these chain-style .configure helpers (maybe in a separate library, as it looks like a common pattern)    */

            get pretty   () { return stringify.configure ({ pretty: true }) },
            get noPretty () { return stringify.configure ({ pretty: false }) },

            get json () { return stringify.configure ({ json: true, pure: true }) },
            get pure () { return stringify.configure ({ pure: true }) },

            maxStringLength (n = Number.MAX_SAFE_INTEGER) { return stringify.configure ({ maxStringLength: n }) },
            maxArrayLength  (n = Number.MAX_SAFE_INTEGER) { return stringify.configure ({ maxArrayLength: n }) },
            maxDepth        (n = Number.MAX_SAFE_INTEGER) { return stringify.configure ({ maxDepth: n }) },

            precision (p) { return stringify.configure ({ precision: p }) },
            formatter (f) { return stringify.configure ({ formatter: f }) },

        /*  Some undocumented internals    */

            limit: (s, n) => s && ((s.length <= n) ? s : (s.substr (0, n - 1) + '…')),

            rightAlign: strings => {
                            var max = maxOf (strings, s => s.length)
                            return strings.map (s => ' '.repeat (max - s.length) + s) },

            object: x => {

                if (x instanceof Set) {
                    x = Array.from (x.values ()) }

                else if (x instanceof Map) {
                    x = Array.from (x.entries ()) }

                const isArray = Array.isArray (x)

                if (isBrowser) {

                    if (x instanceof Element) {
                        return '<' + (x.tagName.toLowerCase () +
                                    ((x.id && ('#' + x.id)) || '') +
                                    ((x.className && ('.' + x.className)) || '')) + '>' }

                    else if (x instanceof Text) {
                        return '@' + stringify.limit (x.wholeText, 20) } }

                if (!cfg.pure && ((cfg.depth > cfg.maxDepth) || (isArray && (x.length > cfg.maxArrayLength)))) {
                    return isArray ? '<array[' + x.length + ']>' : '<object>' }

                const pretty   = cfg.pretty ? true : false,
                      entries  = O.entries (x),
                      oneLine  = !pretty || (entries.length < 2),
                      quoteKey = cfg.json ? (k => '"' + k + '"') : (k => k)

                if (pretty) {

                    const values        = O.values (x),
                          printedKeys   = stringify.rightAlign (O.keys (x).map (k => quoteKey (k) + ': ')),
                          printedValues = values.map (stringify),
                          leftPaddings  = printedValues.map ((x, i) => (((x[0] === '[') ||
                                                                         (x[0] === '{'))
                                                                            ? 3
                                                                            : ((typeof values[i] === 'string') ? 1 : 0))),
                          maxLeftPadding = maxOf (leftPaddings),

                          items = leftPaddings.map ((padding, i) => {
                                            const value = ' '.repeat (maxLeftPadding - padding) + printedValues[i]
                                            return isArray ? value : bullet (printedKeys[i], value) }),

                          printed = bullet (isArray ? '[ ' :
                                                      '{ ', items.join (',\n')),

                          lines    = printed.split ('\n'),
                          lastLine = lines[lines.length - 1]

                    return printed +  (' '.repeat (maxOf (lines, l => l.length) - lastLine.length) + (isArray ? ' ]' : ' }')) }

                else {

                    const items   = entries.map (kv => (isArray ? '' : (quoteKey (kv[0]) + ': ')) + stringify (kv[1])),
                          content = items.join (', ')

                    return isArray
                            ? ('['  + content +  ']')
                            : ('{ ' + content + ' }')
                }
            }
        })

        return stringify
    }

module.exports = configure ({

                    depth:           0,
                    pure:            false,
                    json:            false,
                //  color:           false, // not supported yet
                    maxDepth:        5,
                    maxArrayLength:  60,
                    maxStringLength: 60,
                    precision:       undefined,
                    formatter:       undefined,
                    pretty:         'auto'

                })

<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

function openPage(pageName, elmnt, color) {
  // Hide all elements with class="tabcontent" by default */
  var i, tabcontent, tablinks;
  tabcontent = document.getElementsByClassName("tabcontent");
  for (i = 0; i < tabcontent.length; i++) {
    tabcontent[i].style.display = "none";
  }

  // Remove the background color of all tablinks/buttons
  tablinks = document.getElementsByClassName("tablink");
  for (i = 0; i < tablinks.length; i++) {
    tablinks[i].style.backgroundColor = "";
  }

  // Show the specific tab content
  document.getElementById(pageName).style.display = "block";

  // Add the specific color to the button used to open the tab content
  elmnt.style.backgroundColor = color;

}

function defaultPage() {
  document.getElementById("defaultOpen").click();
}

// Posts and fetches messages from the server and adds them to the DOM.
async function sendAndGet() {
  // Get post parameters.
  const textEle = document.getElementById('text-input');
  const nameEle = document.getElementById('sender');
  const tagEle = document.getElementById('tags');

  // To load the recent post BlogHashMap needs a loadFactor(limit) of 1.
  const loadFactor = 1;

  //TODO: use FormData function to post image file.
  // const imgEle...

  // Create a |URLSearchParams()| and append parameters to it.
  const params = new URLSearchParams();
  params.append('text-input', textEle.value);
  params.append('sender', nameEle.value);
  params.append('tags', tagEle.value);
  params.append('load_factor', loadFactor);

  // Post parameters to the server and fetch instantly to build the page.
  fetch('/data', {method: 'POST', body: params}).then(response => response.json()).then((msgs) => {
    const statsListElement = document.getElementById('posts-list');
    msgs.forEach((msg) => {
      statsListElement.appendChild(createListElement(msg));
      //statsListElement.appendChild(createImgElement(msg.imgUrl));
    })
  });
}

async function loadPosts(){
  fetch('/data').then(response => response.json()).then((msgs) => {
    const statsListElement = document.getElementById('posts-list');
    msgs.forEach((msg) => {
      statsListElement.appendChild(createListElement(msg));
      //statsListElement.appendChild(createImgElement(msg.imgUrl));
    })
  });
}

// Creates an
 element containing message details.
function createListElement(msg) {
  const postElement = document.createElement('li');
  postElement.style.margin = "10px";

  const messageElement = document.createElement('span');
  messageElement.innerText = msg.message;

  const userElement = document.createElement('span');
  if (msg.nickname === undefined || msg.nickname === null) {
    userElement.innerHTML = "_Anonymous";
  } else {
    userElement.innerHTML = "_" + msg.nickname + "";
  }
  userElement.style.marginLeft = "15px";

  const timeElement = document.createElement('span');
  var date = new Date(msg.timestamp);
  timeElement.innerText = date.toString().slice(0, 24);
  timeElement.style.marginTop = "5px";
  timeElement.style.float = "right";
  timeElement.style.clear ="left";

  const deleteMsgElement = document.createElement('button');
  deleteMsgElement.innerText = 'Delete';
  deleteMsgElement.style.marginTop = "5px";
  deleteMsgElement.style.float = "right";
  deleteMsgElement.addEventListener('click', () => {
    deleteMessage(msg);
    postElement.remove();
  });

  postElement.appendChild(messageElement);
  postElement.appendChild(userElement);
  postElement.appendChild(deleteMsgElement);
  postElement.appendChild(timeElement);

  return postElement;
}

/** Creates an  element containing text. */
function createImgElement(text) {
  const imgElement = document.createElement('img');
  imgElement.src = text;
  return imgElement;
}

async function deleteMessage(msg) {
  const params = new URLSearchParams();
  params.append('messageId', msg.id);
  fetch('/delete-data', {method: 'POST', body: params}).then(response => response.text()).then((text) => {
    const confirmationElement = document.getElementById('confirm');
    confirmationElement.innerHTML = text;
  });
}

function fetchBlobstoreUrlAndShowForm() {
  fetch('/blobstore-upload-url')
      .then((response) => {
        return response.text();
      })
      .then((imageUploadUrl) => {
        const messageForm = document.getElementById('my-form');
        messageForm.action = imageUploadUrl;
        messageForm.classList.remove('hidden');
      });
}

function showPostForm() {
  fetch('/login_status').then(response => response.json()).then((isLoggedIn) => {
    if (isLoggedIn) {
      document.getElementById("blogcontent").style.display = "block";
    } else {
    window.open("/login")}
  });
}

function setNicknameForm() {
    window.open("/login");
}

<commit_msg>take out |loadFactor| and remove commented out code<commit_after>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

function openPage(pageName, elmnt, color) {
  // Hide all elements with class="tabcontent" by default */
  var i, tabcontent, tablinks;
  tabcontent = document.getElementsByClassName("tabcontent");
  for (i = 0; i < tabcontent.length; i++) {
    tabcontent[i].style.display = "none";
  }

  // Remove the background color of all tablinks/buttons
  tablinks = document.getElementsByClassName("tablink");
  for (i = 0; i < tablinks.length; i++) {
    tablinks[i].style.backgroundColor = "";
  }

  // Show the specific tab content
  document.getElementById(pageName).style.display = "block";

  // Add the specific color to the button used to open the tab content
  elmnt.style.backgroundColor = color;

}

function defaultPage() {
  document.getElementById("defaultOpen").click();
}

// Posts and fetches messages from the server and adds them to the DOM.
async function sendAndGet() {
  // Get post parameters.
  const textEle = document.getElementById('text-input');
  const nameEle = document.getElementById('sender');
  const tagEle = document.getElementById('tags');

  // TODO: use FormData function to post image file.

  // Create a |URLSearchParams()| and append parameters to it.
  const params = new URLSearchParams();
  params.append('text-input', textEle.value);
  params.append('sender', nameEle.value);
  params.append('tags', tagEle.value);

  // Post parameters to the server and fetch instantly to build the page.
  fetch('/data', {method: 'POST', body: params})
        .then(response => response.json()).then((msgs) => {
    const statsListElement = document.getElementById('posts-list');
    msgs.forEach((msg) => {
      statsListElement.appendChild(createListElement(msg));
    })
  });
}

async function loadPosts(){
  fetch('/data').then(response => response.json()).then((msgs) => {
    const statsListElement = document.getElementById('posts-list');
    msgs.forEach((msg) => {
      statsListElement.appendChild(createListElement(msg));
    })
  });
}

// Creates an
 element containing message details.
function createListElement(msg) {
  const postElement = document.createElement('li');
  postElement.style.margin = "10px";

  const messageElement = document.createElement('span');
  messageElement.innerText = msg.message;

  const userElement = document.createElement('span');
  if (msg.nickname === undefined || msg.nickname === null) {
    userElement.innerHTML = "_Anonymous";
  } else {
    userElement.innerHTML = "_" + msg.nickname + "";
  }
  userElement.style.marginLeft = "15px";

  const timeElement = document.createElement('span');
  var date = new Date(msg.timestamp);
  timeElement.innerText = date.toString().slice(0, 24);
  timeElement.style.marginTop = "5px";
  timeElement.style.float = "right";
  timeElement.style.clear ="left";

  const deleteMsgElement = document.createElement('button');
  deleteMsgElement.innerText = 'Delete';
  deleteMsgElement.style.marginTop = "5px";
  deleteMsgElement.style.float = "right";
  deleteMsgElement.addEventListener('click', () => {
    deleteMessage(msg);
    postElement.remove();
  });

  postElement.appendChild(messageElement);
  postElement.appendChild(userElement);
  postElement.appendChild(deleteMsgElement);
  postElement.appendChild(timeElement);

  return postElement;
}

/** Creates an  element containing text. */
function createImgElement(text) {
  const imgElement = document.createElement('img');
  imgElement.src = text;
  return imgElement;
}

async function deleteMessage(msg) {
  const params = new URLSearchParams();
  params.append('messageId', msg.id);
  fetch('/delete-data', {method: 'POST', body: params})
        .then(response => response.text()).then((text) => {
    const confirmationElement = document.getElementById('confirm');
    confirmationElement.innerHTML = text;
  });
}

function fetchBlobstoreUrlAndShowForm() {
  fetch('/blobstore-upload-url')
      .then((response) => {
        return response.text();
      })
      .then((imageUploadUrl) => {
        const messageForm = document.getElementById('my-form');
        messageForm.action = imageUploadUrl;
        messageForm.classList.remove('hidden');
      });
}

function showPostForm() {
  fetch('/login_status').then(response => response.json()).then((isLoggedIn) => {
    if (isLoggedIn) {
      document.getElementById("blogcontent").style.display = "block";
    } else {
    window.open("/login")}
  });
}

function setNicknameForm() {
    window.open("/login");
}

<|endoftext|>


<commit_before>define(["./textTagsView", "../model/textTags",
 "backbone","jquery","handlebar"], function(TextTagsView, TextTags){

  Handlebars.registerHelper('toUpper', function(options) {
    return options.toUpperCase();
});

  var Controller = Backbone.View.extend({

    template: Handlebars.compile("<button class='{{feature}}'>{{toUpper feature}}</button>"),

    initialize: function() {
      this.model.on('end', function(){
        this.$('button').prop('disabled',false);
      }, this);
    },

    events: {
      'click button': "renderTextTag"
    },

    renderTextTag: function(e) {
      this.model.collection.trigger('showTextTags', this.model);
      this.$('button').prop('disabled',true);
    },

    render: function () {
      this.$el.html(this.template(this.model.attributes));
      //to fix. position
      return this;
    }

  });

  return Controller;
});<commit_msg>add button inactive detect<commit_after>define(["backbone","jquery","handlebar"], function(){

  Handlebars.registerHelper('toUpper', function(options) {
    return options.toUpperCase();
});

  var Controller = Backbone.View.extend({

    template: Handlebars.compile("<button class='{{feature}}'>{{toUpper feature}}</button>"),

    initialize: function() {
      this.model.on('inactivate', this.inactivate, this);
    },

    events: {
      'click button': "renderTextTag"
    },

    inactivate: function(eventName) {
      if (!this.$("button").hasClass('inactive')) {
        this.$("button").addClass('inactive');
      }
    },

    renderTextTag: function(e) {
      this.model.collection.trigger('showTextTags', this.model);
      if (this.$("button").hasClass('inactive')) {
        this.$("button").removeClass('inactive');
      }
    },

    render: function () {
      this.$el.html(this.template(this.model.attributes));
      //to fix. position
      return this;
    }

  });

  return Controller;
});<|endoftext|>


<commit_before>(function (root, factory) {
  if(typeof define === "function" && define.amd) {
    define(["openlayers"], factory);
  } else if(typeof module === "object" && module.exports) {
    module.exports = factory(require("openlayers"));
  } else {
    root.LayerSwitcher = factory(root.ol);
  }
}(this, function(ol) {
  // This section addded by Jessica to save the expand/collapse state
  var savedPanel = undefined;
  var savedClass = undefined;
  // End this section added by Jessica

  /**
   * OpenLayers v3/v4 Layer Switcher Control.
   * See [the examples](./examples) for usage.
   * @constructor
   * @extends {ol.control.Control}
   * @param {Object} opt_options Control options, extends olx.control.ControlOptions adding:
   *                              **`tipLabel`** `String` - the button tooltip.
   */
  ol.control.LayerSwitcher = function(opt_options) {

    var options = opt_options || {};

    var tipLabel = options.tipLabel ?
      options.tipLabel : 'Legend';

    this.mapListeners = [];

    this.hiddenClassName = 'ol-unselectable ol-control layer-switcher';
    if (ol.control.LayerSwitcher.isTouchDevice_()) {
      this.hiddenClassName += ' touch';
    }
    this.shownClassName = 'shown';

    var element = document.createElement('div');
    element.className = this.hiddenClassName;

    var button = document.createElement('button');
    button.setAttribute('title', tipLabel);
    element.appendChild(button);

    this.panel = document.createElement('div');
    this.panel.className = 'panel';
    element.appendChild(this.panel);
    ol.control.LayerSwitcher.enableTouchScroll_(this.panel);

    var this_ = this;

    button.onmouseover = function(e) {
      this_.showPanel();
    };

    button.onclick = function(e) {
      e = e || window.event;
      this_.showPanel();
      e.preventDefault();
    };

    this_.panel.onmouseout = function(e) {
      e = e || window.event;
      if (!this_.panel.contains(e.toElement || e.relatedTarget)) {
        this_.hidePanel();
      }
    };

    ol.control.Control.call(this, {
      element: element,
      target: options.target
    });

  };

  ol.inherits(ol.control.LayerSwitcher, ol.control.Control);

  /**
   * Show the layer panel.
   */
  ol.control.LayerSwitcher.prototype.showPanel = function() {
    if (!this.element.classList.contains(this.shownClassName)) {
      this.element.classList.add(this.shownClassName);
      this.renderPanel();
    }
  };

  /**
   * Hide the layer panel.
   */
  ol.control.LayerSwitcher.prototype.hidePanel = function() {
    if (this.element.classList.contains(this.shownClassName)) {
      this.element.classList.remove(this.shownClassName);
      // This section addded by Jessica to save the expand/collapse state
      savedPanel = angular.copy(this.panel);
      // End this part added by Jessica
    }
  };

  /**
   * Re-draw the layer panel to represent the current state of the layers.
   */
  ol.control.LayerSwitcher.prototype.renderPanel = function() {

    this.ensureTopVisibleBaseLayerShown_();

    while(this.panel.firstChild) {
      this.panel.removeChild(this.panel.firstChild);
    }

    var ul = document.createElement('ul');
    this.panel.appendChild(ul);
    this.renderLayers_(this.getMap(), ul);

  };

  /**
   * Set the map instance the control is associated with.
   * @param {ol.Map} map The map instance.
   */
  ol.control.LayerSwitcher.prototype.setMap = function(map) {
    // Clean up listeners associated with the previous map
    for (var i = 0, key; i < this.mapListeners.length; i++) {
      ol.Observable.unByKey(this.mapListeners[i]);
    }
    this.mapListeners.length = 0;
    // Wire up listeners etc. and store reference to new map
    ol.control.Control.prototype.setMap.call(this, map);
    if (map) {
      var this_ = this;
      this.mapListeners.push(map.on('pointerdown', function() {
        this_.hidePanel();
      }));
      this.renderPanel();
    }
  };

  /**
   * Ensure only the top-most base layer is visible if more than one is visible.
   * @private
   */
  ol.control.LayerSwitcher.prototype.ensureTopVisibleBaseLayerShown_ = function() {
    var lastVisibleBaseLyr;
    ol.control.LayerSwitcher.forEachRecursive(this.getMap(), function(l, idx, a) {
      if (l.get('type') === 'base' && l.getVisible()) {
        lastVisibleBaseLyr = l;
      }
    });
    if (lastVisibleBaseLyr) this.setVisible_(lastVisibleBaseLyr, true);
  };

  /**
   * Toggle the visible state of a layer.
   * Takes care of hiding other layers in the same exclusive group if the layer
   * is toggle to visible.
   * @private
   * @param {ol.layer.Base} The layer whos visibility will be toggled.
   */
  ol.control.LayerSwitcher.prototype.setVisible_ = function(lyr, visible) {
    var map = this.getMap();
    lyr.setVisible(visible);
    if (visible && lyr.get('type') === 'base') {
      // Hide all other base layers regardless of grouping
      ol.control.LayerSwitcher.forEachRecursive(map, function(l, idx, a) {
        if (l != lyr && l.get('type') === 'base') {
          l.setVisible(false);
        }
      });
    }
    if (lyr.getLayers && !lyr.get('combine')){
      var lyrs = lyr.getLayers().getArray().slice().reverse();
      for (var i = 0; i < lyrs.length; i++) {
        var lyr = lyrs[i];
        var lyrId = lyr.get('id');
        var subLyr = document.getElementById(lyrId);
        subLyr.disabled = !visible;
      }
    }
  };

  /**
   * Render all layers that are children of a group.
   * @private
   * @param {ol.layer.Base} lyr Layer to be rendered (should have a title property).
   * @param {Number} idx Position in parent group list.
   */
  ol.control.LayerSwitcher.prototype.renderLayer_ = function(lyr, idx) {

    var this_ = this;

    var li = document.createElement('li');

    var lyrTitle = lyr.get('title');
    var lyrId = ol.control.LayerSwitcher.uuid();
    lyr.set('id', lyrId);

    var label = document.createElement('label');

    if (lyr.getLayers && !lyr.get('combine')) {

      // This section addded by Jessica to expand and collapse groups
      var toggleBtn = document.createElement('i');

      // Load the expand/collapse state
      if (savedPanel !== undefined) {
       var matchingEle = _.find(savedPanel.getElementsByClassName('group'), function (groupEle) {
          return groupEle.querySelector('label').innerText === lyrTitle;
        });
        if (matchingEle !== undefined) savedClass = matchingEle.querySelector('i').className;
        else savedClass = undefined;
      }
      else savedClass = undefined;

      toggleBtn.className = savedClass || 'icon ion-minus-round';
      toggleBtn.id = 'toggleButton';
      li.appendChild(toggleBtn);
      toggleBtn.addEventListener ('click', function() {
        toggleGroup(lyr);
        if (toggleBtn.className === 'icon ion-minus-round') toggleBtn.className = 'icon ion-plus-round';
        else toggleBtn.className = 'icon ion-minus-round';
      });
      // End this section added by Jessica

      var input = document.createElement('input');
      input.type = 'checkbox';
      input.id = lyrId;
      input.checked = lyr.get('visible');
      input.onchange = function(e) {
        this_.setVisible_(lyr, e.target.checked);
      };
      li.appendChild(input);

      li.className = 'group';
      label.innerHTML = lyrTitle;
      label.htmlFor = lyrId;
      li.appendChild(label);
      var ul = document.createElement('ul');
      li.appendChild(ul);

      this.renderLayers_(lyr, ul);

    } else {

      li.className = 'layer';
      li.id = 'layer' + lyrId;    // This addded by Jessica to expand and collapse groups
      var input = document.createElement('input');
      if (lyr.get('type') === 'base') {
        input.type = 'radio';
        input.name = 'base';
      } else {
        input.type = 'checkbox';
      }
      input.id = lyrId;
      input.checked = lyr.get('visible');
      input.onchange = function(e) {
        this_.setVisible_(lyr, e.target.checked);
      };
      li.appendChild(input);

      label.htmlFor = lyrId;
      label.innerHTML = lyrTitle;

      var rsl = this.getMap().getView().getResolution();
      if (rsl > lyr.getMaxResolution() || rsl < lyr.getMinResolution()){
        label.className += ' disabled';
      }

      // This section addded by Jessica to apply the expand/collapse state
      if (savedClass === 'icon ion-plus-round') li.style.display = 'none';
      // End this section added by Jessica
      li.appendChild(label);

    }

    return li;

  };

  /**
   * Render all layers that are children of a group.
   * @private
   * @param {ol.layer.Group} lyr Group layer whos children will be rendered.
   * @param {Element} elm DOM element that children will be appended to.
   */
  ol.control.LayerSwitcher.prototype.renderLayers_ = function(lyr, elm) {
    var lyrs = lyr.getLayers().getArray().slice().reverse();
    for (var i = 0, l; i < lyrs.length; i++) {
      l = lyrs[i];
      if (l.get('title')) {
        elm.appendChild(this.renderLayer_(l, i));
      }
    }
  };

  /**
   * **Static** Call the supplied function for each layer in the passed layer group
   * recursing nested groups.
   * @param {ol.layer.Group} lyr The layer group to start iterating from.
   * @param {Function} fn Callback which will be called for each `ol.layer.Base`
   * found under `lyr`. The signature for `fn` is the same as `ol.Collection#forEach`
   */
  ol.control.LayerSwitcher.forEachRecursive = function(lyr, fn) {
    lyr.getLayers().forEach(function(lyr, idx, a) {
      fn(lyr, idx, a);
      if (lyr.getLayers) {
        ol.control.LayerSwitcher.forEachRecursive(lyr, fn);
      }
    });
  };

  /**
   * Generate a UUID
   * @returns {String} UUID
   *
   * Adapted from http://stackoverflow.com/a/2117523/526860
   */
  ol.control.LayerSwitcher.uuid = function() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
      var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
      return v.toString(16);
    });
  }

  /**
   * @private
   * @desc Apply workaround to enable scrolling of overflowing content within an
   * element. Adapted from https://gist.github.com/chrismbarr/4107472
   */
  ol.control.LayerSwitcher.enableTouchScroll_ = function(elm) {
    if(ol.control.LayerSwitcher.isTouchDevice_()){
      var scrollStartPos = 0;
      elm.addEventListener("touchstart", function(event) {
        scrollStartPos = this.scrollTop + event.touches[0].pageY;
      }, false);
      elm.addEventListener("touchmove", function(event) {
        this.scrollTop = scrollStartPos - event.touches[0].pageY;
      }, false);
    }
  };

  /**
   * @private
   * @desc Determine if the current browser supports touch events. Adapted from
   * https://gist.github.com/chrismbarr/4107472
   */
  ol.control.LayerSwitcher.isTouchDevice_ = function() {
    try {
      document.createEvent("TouchEvent");
      return true;
    } catch(e) {
      return false;
    }
  };

  // This section addded by Jessica to expand and collapse groups
  function toggleGroup(lyr, toggleBtn) {
    lyr.getLayersArray().forEach(function (layer) {
      var layerEle = document.getElementById('layer' + layer.get('id'));
      if (layerEle.style.display === '' || layerEle.style.display === 'table') layerEle.style.display = 'none';
      else layerEle.style.display = 'table';
    });
  }
  // End this section addded by Jessica

  var LayerSwitcher = ol.control.LayerSwitcher;
  return LayerSwitcher;
}));<commit_msg>Added commenting for sections added in ol3-layerswitcher library to add checkmarks to parent grouped layers<commit_after>(function (root, factory) {
  if(typeof define === "function" && define.amd) {
    define(["openlayers"], factory);
  } else if(typeof module === "object" && module.exports) {
    module.exports = factory(require("openlayers"));
  } else {
    root.LayerSwitcher = factory(root.ol);
  }
}(this, function(ol) {
  // This section addded by Jessica to save the expand/collapse state
  var savedPanel = undefined;
  var savedClass = undefined;
  // End this section added by Jessica

  /**
   * OpenLayers v3/v4 Layer Switcher Control.
   * See [the examples](./examples) for usage.
   * @constructor
   * @extends {ol.control.Control}
   * @param {Object} opt_options Control options, extends olx.control.ControlOptions adding:
   *                              **`tipLabel`** `String` - the button tooltip.
   */
  ol.control.LayerSwitcher = function(opt_options) {

    var options = opt_options || {};

    var tipLabel = options.tipLabel ?
      options.tipLabel : 'Legend';

    this.mapListeners = [];

    this.hiddenClassName = 'ol-unselectable ol-control layer-switcher';
    if (ol.control.LayerSwitcher.isTouchDevice_()) {
      this.hiddenClassName += ' touch';
    }
    this.shownClassName = 'shown';

    var element = document.createElement('div');
    element.className = this.hiddenClassName;

    var button = document.createElement('button');
    button.setAttribute('title', tipLabel);
    element.appendChild(button);

    this.panel = document.createElement('div');
    this.panel.className = 'panel';
    element.appendChild(this.panel);
    ol.control.LayerSwitcher.enableTouchScroll_(this.panel);

    var this_ = this;

    button.onmouseover = function(e) {
      this_.showPanel();
    };

    button.onclick = function(e) {
      e = e || window.event;
      this_.showPanel();
      e.preventDefault();
    };

    this_.panel.onmouseout = function(e) {
      e = e || window.event;
      if (!this_.panel.contains(e.toElement || e.relatedTarget)) {
        this_.hidePanel();
      }
    };

    ol.control.Control.call(this, {
      element: element,
      target: options.target
    });

  };

  ol.inherits(ol.control.LayerSwitcher, ol.control.Control);

  /**
   * Show the layer panel.
   */
  ol.control.LayerSwitcher.prototype.showPanel = function() {
    if (!this.element.classList.contains(this.shownClassName)) {
      this.element.classList.add(this.shownClassName);
      this.renderPanel();
    }
  };

  /**
   * Hide the layer panel.
   */
  ol.control.LayerSwitcher.prototype.hidePanel = function() {
    if (this.element.classList.contains(this.shownClassName)) {
      this.element.classList.remove(this.shownClassName);
      // This section addded by Jessica to save the expand/collapse state
      savedPanel = angular.copy(this.panel);
      // End this part added by Jessica
    }
  };

  /**
   * Re-draw the layer panel to represent the current state of the layers.
   */
  ol.control.LayerSwitcher.prototype.renderPanel = function() {

    this.ensureTopVisibleBaseLayerShown_();

    while(this.panel.firstChild) {
      this.panel.removeChild(this.panel.firstChild);
    }

    var ul = document.createElement('ul');
    this.panel.appendChild(ul);
    this.renderLayers_(this.getMap(), ul);

  };

  /**
   * Set the map instance the control is associated with.
   * @param {ol.Map} map The map instance.
   */
  ol.control.LayerSwitcher.prototype.setMap = function(map) {
    // Clean up listeners associated with the previous map
    for (var i = 0, key; i < this.mapListeners.length; i++) {
      ol.Observable.unByKey(this.mapListeners[i]);
    }
    this.mapListeners.length = 0;
    // Wire up listeners etc. and store reference to new map
    ol.control.Control.prototype.setMap.call(this, map);
    if (map) {
      var this_ = this;
      this.mapListeners.push(map.on('pointerdown', function() {
        this_.hidePanel();
      }));
      this.renderPanel();
    }
  };

  /**
   * Ensure only the top-most base layer is visible if more than one is visible.
   * @private
   */
  ol.control.LayerSwitcher.prototype.ensureTopVisibleBaseLayerShown_ = function() {
    var lastVisibleBaseLyr;
    ol.control.LayerSwitcher.forEachRecursive(this.getMap(), function(l, idx, a) {
      if (l.get('type') === 'base' && l.getVisible()) {
        lastVisibleBaseLyr = l;
      }
    });
    if (lastVisibleBaseLyr) this.setVisible_(lastVisibleBaseLyr, true);
  };

  /**
   * Toggle the visible state of a layer.
   * Takes care of hiding other layers in the same exclusive group if the layer
   * is toggle to visible.
   * @private
   * @param {ol.layer.Base} The layer whos visibility will be toggled.
   */
  ol.control.LayerSwitcher.prototype.setVisible_ = function(lyr, visible) {
    var map = this.getMap();
    lyr.setVisible(visible);
    if (visible && lyr.get('type') === 'base') {
      // Hide all other base layers regardless of grouping
      ol.control.LayerSwitcher.forEachRecursive(map, function(l, idx, a) {
        if (l != lyr && l.get('type') === 'base') {
          l.setVisible(false);
        }
      });
    }
    // Added by Jessica to add checkmarks to parent grouped layers
    // From pull request by OlaKov in ol3-layerswitcher github
    if (lyr.getLayers && !lyr.get('combine')){
      var lyrs = lyr.getLayers().getArray().slice().reverse();
      for (var i = 0; i < lyrs.length; i++) {
        var lyr = lyrs[i];
        var lyrId = lyr.get('id');
        var subLyr = document.getElementById(lyrId);
        subLyr.disabled = !visible;
      }
    }
    // End added by Jessica
  };

  /**
   * Render all layers that are children of a group.
   * @private
   * @param {ol.layer.Base} lyr Layer to be rendered (should have a title property).
   * @param {Number} idx Position in parent group list.
   */
  ol.control.LayerSwitcher.prototype.renderLayer_ = function(lyr, idx) {

    var this_ = this;

    var li = document.createElement('li');

    var lyrTitle = lyr.get('title');
    var lyrId = ol.control.LayerSwitcher.uuid();
    lyr.set('id', lyrId);    // Added by Jessica to add checkmarks to parent grouped layers

    var label = document.createElement('label');

    if (lyr.getLayers && !lyr.get('combine')) {

      // This section addded by Jessica to expand and collapse groups
      var toggleBtn = document.createElement('i');

      // Load the expand/collapse state
      if (savedPanel !== undefined) {
       var matchingEle = _.find(savedPanel.getElementsByClassName('group'), function (groupEle) {
          return groupEle.querySelector('label').innerText === lyrTitle;
        });
        if (matchingEle !== undefined) savedClass = matchingEle.querySelector('i').className;
        else savedClass = undefined;
      }
      else savedClass = undefined;

      toggleBtn.className = savedClass || 'icon ion-minus-round';
      toggleBtn.id = 'toggleButton';
      li.appendChild(toggleBtn);
      toggleBtn.addEventListener ('click', function() {
        toggleGroup(lyr);
        if (toggleBtn.className === 'icon ion-minus-round') toggleBtn.className = 'icon ion-plus-round';
        else toggleBtn.className = 'icon ion-minus-round';
      });
      // End this section added by Jessica

      var input = document.createElement('input');
      input.type = 'checkbox';
      input.id = lyrId;
      input.checked = lyr.get('visible');
      input.onchange = function(e) {
        this_.setVisible_(lyr, e.target.checked);
      };
      li.appendChild(input);

      li.className = 'group';
      label.innerHTML = lyrTitle;
      label.htmlFor = lyrId;
      li.appendChild(label);
      var ul = document.createElement('ul');
      li.appendChild(ul);

      this.renderLayers_(lyr, ul);

    } else {

      li.className = 'layer';
      li.id = 'layer' + lyrId;    // This addded by Jessica to expand and collapse groups
      var input = document.createElement('input');
      if (lyr.get('type') === 'base') {
        input.type = 'radio';
        input.name = 'base';
      } else {
        input.type = 'checkbox';
      }
      input.id = lyrId;
      input.checked = lyr.get('visible');
      input.onchange = function(e) {
        this_.setVisible_(lyr, e.target.checked);
      };
      li.appendChild(input);

      label.htmlFor = lyrId;
      label.innerHTML = lyrTitle;

      var rsl = this.getMap().getView().getResolution();
      if (rsl > lyr.getMaxResolution() || rsl < lyr.getMinResolution()){
        label.className += ' disabled';
      }

      // This section addded by Jessica to apply the expand/collapse state
      if (savedClass === 'icon ion-plus-round') li.style.display = 'none';
      // End this section added by Jessica
      li.appendChild(label);

    }

    return li;

  };

  /**
   * Render all layers that are children of a group.
   * @private
   * @param {ol.layer.Group} lyr Group layer whos children will be rendered.
   * @param {Element} elm DOM element that children will be appended to.
   */
  ol.control.LayerSwitcher.prototype.renderLayers_ = function(lyr, elm) {
    var lyrs = lyr.getLayers().getArray().slice().reverse();
    for (var i = 0, l; i < lyrs.length; i++) {
      l = lyrs[i];
      if (l.get('title')) {
        elm.appendChild(this.renderLayer_(l, i));
      }
    }
  };

  /**
   * **Static** Call the supplied function for each layer in the passed layer group
   * recursing nested groups.
   * @param {ol.layer.Group} lyr The layer group to start iterating from.
   * @param {Function} fn Callback which will be called for each `ol.layer.Base`
   * found under `lyr`. The signature for `fn` is the same as `ol.Collection#forEach`
   */
  ol.control.LayerSwitcher.forEachRecursive = function(lyr, fn) {
    lyr.getLayers().forEach(function(lyr, idx, a) {
      fn(lyr, idx, a);
      if (lyr.getLayers) {
        ol.control.LayerSwitcher.forEachRecursive(lyr, fn);
      }
    });
  };

  /**
   * Generate a UUID
   * @returns {String} UUID
   *
   * Adapted from http://stackoverflow.com/a/2117523/526860
   */
  ol.control.LayerSwitcher.uuid = function() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
      var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
      return v.toString(16);
    });
  }

  /**
   * @private
   * @desc Apply workaround to enable scrolling of overflowing content within an
   * element. Adapted from https://gist.github.com/chrismbarr/4107472
   */
  ol.control.LayerSwitcher.enableTouchScroll_ = function(elm) {
    if(ol.control.LayerSwitcher.isTouchDevice_()){
      var scrollStartPos = 0;
      elm.addEventListener("touchstart", function(event) {
        scrollStartPos = this.scrollTop + event.touches[0].pageY;
      }, false);
      elm.addEventListener("touchmove", function(event) {
        this.scrollTop = scrollStartPos - event.touches[0].pageY;
      }, false);
    }
  };

  /**
   * @private
   * @desc Determine if the current browser supports touch events. Adapted from
   * https://gist.github.com/chrismbarr/4107472
   */
  ol.control.LayerSwitcher.isTouchDevice_ = function() {
    try {
      document.createEvent("TouchEvent");
      return true;
    } catch(e) {
      return false;
    }
  };

  // This section addded by Jessica to expand and collapse groups
  function toggleGroup(lyr, toggleBtn) {
    lyr.getLayersArray().forEach(function (layer) {
      var layerEle = document.getElementById('layer' + layer.get('id'));
      if (layerEle.style.display === '' || layerEle.style.display === 'table') layerEle.style.display = 'none';
      else layerEle.style.display = 'table';
    });
  }
  // End this section addded by Jessica

  var LayerSwitcher = ol.control.LayerSwitcher;
  return LayerSwitcher;
}));<|endoftext|>


<commit_before>/* @flow */
'use strict';

var expect = require('chai').expect;

var Q = require('q');

var React = require('react/addons'),
    TwoBit = require('../src/TwoBit'),
    Bam = require('../src/bam'),
    BigBed = require('../src/BigBed'),
    VcfFile = require('../src/vcf'),
    Root = require('../src/Root'),
    RemoteFile = require('../src/RemoteFile'),
    TwoBitDataSource = require('../src/TwoBitDataSource'),
    BigBedDataSource = require('../src/BigBedDataSource'),
    BamDataSource = require('../src/BamDataSource'),
    VcfDataSource = require('../src/VcfDataSource'),
    GenomeTrack = require('../src/GenomeTrack'),
    GeneTrack = require('../src/GeneTrack'),
    PileupTrack = require('../src/PileupTrack'),
    VariantTrack = require('../src/VariantTrack');

var WAIT_FOR_POLL_INTERVAL_MS = 100;
function waitFor(predFn, timeoutMs) {
  var def = Q.defer();

  var checkTimeoutId = null;

  var timeoutId = window.setTimeout(() => {
    if (checkTimeoutId) window.clearTimeout(checkTimeoutId);
    def.reject('Timed out');
  }, timeoutMs);

  var check = function() {
    if (def.promise.isRejected()) return;
    if (predFn()) {
      def.resolve(null);  // no arguments needed
      window.clearTimeout(timeoutId);
    } else {
      checkTimeoutId = window.setTimeout(check, WAIT_FOR_POLL_INTERVAL_MS);
    }
  };
  checkTimeoutId = window.setTimeout(check, 0);

  return def.promise;
}

describe('Root component', function() {
  var genome = new TwoBit('/test/data/test.2bit');
  var dataSource = TwoBitDataSource.create(genome);

  // This file contains just the TP53 gene, shifted so that it starts at the
  // beginning of chr17 (to match test.2bit). See test/data/README.md.
  var ensembl = new BigBed('/test/data/tp53.shifted.bb');
  var ensemblDataSource = BigBedDataSource.create(ensembl);

  var bam = new Bam(new RemoteFile('/test/data/index_test.bam'),
                    new RemoteFile('/test/data/index_test.bam.bai'));
  var bamSource = BamDataSource.create(bam);

  var vcf = new VcfFile(new RemoteFile('/test/data/snv.chr17.vcf'));
  var vcfSource = VcfDataSource.create(vcf);

  var tracks = [
    {
      visualization: GenomeTrack,
      source: dataSource,
      track: {
        type: 'reference',
        data: {}
      }
    },
    {
      visualization: VariantTrack,
      source: vcfSource,
      track: {
        type: 'variants',
        data: {}
      }
    },
    {
      visualization: GeneTrack,
      source: ensemblDataSource,
      track: {
        type: 'genes',
        data: {}
      }
    },
    {
      visualization: PileupTrack,
      source: bamSource,
      track: {
        type: 'pileup',
        data: {}
      }
    }
  ];

  var testDiv = document.getElementById('testdiv');

  afterEach(function() {
    testDiv.innerHTML = '';  // avoid pollution between tests.
  });

  it('should render reference genome and genes', function() {
    this.timeout(5000);

    var div = document.createElement('div');
    div.setAttribute('style', 'width: 800px; height: 200px;');
    testDiv.appendChild(div);

    // TODO: changing to {start:1, stop:50} makes the test fail.
    React.render(<Root referenceSource={dataSource}
                       tracks={tracks}
                       initialRange={{contig:"chr17", start: 100, stop: 150}} />, div);

    var ready = (() =>
      div.querySelectorAll('.basepair').length > 0 &&
      div.querySelectorAll('.gene').length > 0
    );

    return waitFor(ready, 5000)
      .then(() => {
        var basepairs = div.querySelectorAll('.basepair');
        expect(basepairs).to.have.length.at.least(10);
        var geneNames = div.querySelectorAll('.gene text');
        expect(geneNames.length).to.equal(1);
        expect(geneNames[0].textContent).to.equal('TP53');

        // Note: there are 11 exons, but two are split into coding/non-coding
        expect(div.querySelectorAll('.gene .exon').length).to.equal(13);
      });
  });
});
<commit_msg>Update components-test to use the public API<commit_after>/* @flow */
'use strict';

var expect = require('chai').expect;

var Q = require('q');
var pileup = require('../src/pileup');

var WAIT_FOR_POLL_INTERVAL_MS = 100;
function waitFor(predFn, timeoutMs) {
  var def = Q.defer();

  var checkTimeoutId = null;

  var timeoutId = window.setTimeout(() => {
    if (checkTimeoutId) window.clearTimeout(checkTimeoutId);
    def.reject('Timed out');
  }, timeoutMs);

  var check = function() {
    if (def.promise.isRejected()) return;
    if (predFn()) {
      def.resolve(null);  // no arguments needed
      window.clearTimeout(timeoutId);
    } else {
      checkTimeoutId = window.setTimeout(check, WAIT_FOR_POLL_INTERVAL_MS);
    }
  };
  checkTimeoutId = window.setTimeout(check, 0);

  return def.promise;
}

describe('Root component', function() {
  var tracks = [
    {
      viz: 'genome',
      isReference: true,
      data: {
        url: '/test/data/test.2bit'
      }
    },
    {
      viz: 'variants',
      data: {
        url: '/test/data/snv.chr17.vcf'
      }
    },
    {
      viz: 'genes',
      data: {
        // This file contains just TP53, shifted so that it starts at the
        // beginning of chr17 (to match test.2bit). See test/data/README.md.
        url: '/test/data/tp53.shifted.bb'
      }
    },
    {
      viz: 'pileup',
      data: {
        url: '/test/data/index_test.bam',
        indexUrl: '/test/data/index_test.bam.bai'
      }
    }
  ];

  var testDiv = document.getElementById('testdiv');

  afterEach(function() {
    testDiv.innerHTML = '';  // avoid pollution between tests.
  });

  it('should render reference genome and genes', function() {
    this.timeout(5000);

    var div = document.createElement('div');
    div.setAttribute('style', 'width: 800px; height: 200px;');
    testDiv.appendChild(div);

    pileup.create(div, {
      range: {contig:"chr17", start: 100, stop: 150},
      tracks: tracks
    });

    var ready = (() =>
      div.querySelectorAll('.basepair').length > 0 &&
      div.querySelectorAll('.gene').length > 0
    );

    return waitFor(ready, 5000)
      .then(() => {
        var basepairs = div.querySelectorAll('.basepair');
        expect(basepairs).to.have.length.at.least(10);
        var geneNames = div.querySelectorAll('.gene text');
        expect(geneNames.length).to.equal(1);
        expect(geneNames[0].textContent).to.equal('TP53');

        // Note: there are 11 exons, but two are split into coding/non-coding
        expect(div.querySelectorAll('.gene .exon').length).to.equal(13);
      });
  });
});
<|endoftext|>


<commit_before>import api, { getLinks } from '../api';
import { fetchRelationships } from './accounts';
import { openModal } from '../../mastodon/actions/modal';

export const MUTES_FETCH_REQUEST = 'MUTES_FETCH_REQUEST';
export const MUTES_FETCH_SUCCESS = 'MUTES_FETCH_SUCCESS';
export const MUTES_FETCH_FAIL    = 'MUTES_FETCH_FAIL';

export const MUTES_EXPAND_REQUEST = 'MUTES_EXPAND_REQUEST';
export const MUTES_EXPAND_SUCCESS = 'MUTES_EXPAND_SUCCESS';
export const MUTES_EXPAND_FAIL    = 'MUTES_EXPAND_FAIL';

export const MUTES_INIT_MODAL = 'MUTES_INIT_MODAL';
export const MUTES_TOGGLE_HIDE_NOTIFICATIONS = 'MUTES_TOGGLE_HIDE_NOTIFICATIONS';

export function fetchMutes() {
  return (dispatch, getState) => {
    dispatch(fetchMutesRequest());

    api(getState).get('/api/v1/mutes').then(response => {
      const next = getLinks(response).refs.find(link => link.rel === 'next');
      dispatch(fetchMutesSuccess(response.data, next ? next.uri : null));
      dispatch(fetchRelationships(response.data.map(item => item.id)));
    }).catch(error => dispatch(fetchMutesFail(error)));
  };
};

export function fetchMutesRequest() {
  return {
    type: MUTES_FETCH_REQUEST,
  };
};

export function fetchMutesSuccess(accounts, next) {
  return {
    type: MUTES_FETCH_SUCCESS,
    accounts,
    next,
  };
};

export function fetchMutesFail(error) {
  return {
    type: MUTES_FETCH_FAIL,
    error,
  };
};

export function expandMutes() {
  return (dispatch, getState) => {
    const url = getState().getIn(['user_lists', 'mutes', 'next']);

    if (url === null) {
      return;
    }

    dispatch(expandMutesRequest());

    api(getState).get(url).then(response => {
      const next = getLinks(response).refs.find(link => link.rel === 'next');
      dispatch(expandMutesSuccess(response.data, next ? next.uri : null));
      dispatch(fetchRelationships(response.data.map(item => item.id)));
    }).catch(error => dispatch(expandMutesFail(error)));
  };
};

export function expandMutesRequest() {
  return {
    type: MUTES_EXPAND_REQUEST,
  };
};

export function expandMutesSuccess(accounts, next) {
  return {
    type: MUTES_EXPAND_SUCCESS,
    accounts,
    next,
  };
};

export function expandMutesFail(error) {
  return {
    type: MUTES_EXPAND_FAIL,
    error,
  };
};

export function initMuteModal(account) {
  return dispatch => {
    dispatch({
      type: MUTES_INIT_MODAL,
      account,
    });

    dispatch(openModal('MUTE'));
  };
}

export function toggleHideNotifications() {
  return dispatch => {
    dispatch({ type: MUTES_TOGGLE_HIDE_NOTIFICATIONS });
  };
}<commit_msg>Update mutes.js (#5882)<commit_after>import api, { getLinks } from '../api';
import { fetchRelationships } from './accounts';
import { openModal } from './modal';

export const MUTES_FETCH_REQUEST = 'MUTES_FETCH_REQUEST';
export const MUTES_FETCH_SUCCESS = 'MUTES_FETCH_SUCCESS';
export const MUTES_FETCH_FAIL    = 'MUTES_FETCH_FAIL';

export const MUTES_EXPAND_REQUEST = 'MUTES_EXPAND_REQUEST';
export const MUTES_EXPAND_SUCCESS = 'MUTES_EXPAND_SUCCESS';
export const MUTES_EXPAND_FAIL    = 'MUTES_EXPAND_FAIL';

export const MUTES_INIT_MODAL = 'MUTES_INIT_MODAL';
export const MUTES_TOGGLE_HIDE_NOTIFICATIONS = 'MUTES_TOGGLE_HIDE_NOTIFICATIONS';

export function fetchMutes() {
  return (dispatch, getState) => {
    dispatch(fetchMutesRequest());

    api(getState).get('/api/v1/mutes').then(response => {
      const next = getLinks(response).refs.find(link => link.rel === 'next');
      dispatch(fetchMutesSuccess(response.data, next ? next.uri : null));
      dispatch(fetchRelationships(response.data.map(item => item.id)));
    }).catch(error => dispatch(fetchMutesFail(error)));
  };
};

export function fetchMutesRequest() {
  return {
    type: MUTES_FETCH_REQUEST,
  };
};

export function fetchMutesSuccess(accounts, next) {
  return {
    type: MUTES_FETCH_SUCCESS,
    accounts,
    next,
  };
};

export function fetchMutesFail(error) {
  return {
    type: MUTES_FETCH_FAIL,
    error,
  };
};

export function expandMutes() {
  return (dispatch, getState) => {
    const url = getState().getIn(['user_lists', 'mutes', 'next']);

    if (url === null) {
      return;
    }

    dispatch(expandMutesRequest());

    api(getState).get(url).then(response => {
      const next = getLinks(response).refs.find(link => link.rel === 'next');
      dispatch(expandMutesSuccess(response.data, next ? next.uri : null));
      dispatch(fetchRelationships(response.data.map(item => item.id)));
    }).catch(error => dispatch(expandMutesFail(error)));
  };
};

export function expandMutesRequest() {
  return {
    type: MUTES_EXPAND_REQUEST,
  };
};

export function expandMutesSuccess(accounts, next) {
  return {
    type: MUTES_EXPAND_SUCCESS,
    accounts,
    next,
  };
};

export function expandMutesFail(error) {
  return {
    type: MUTES_EXPAND_FAIL,
    error,
  };
};

export function initMuteModal(account) {
  return dispatch => {
    dispatch({
      type: MUTES_INIT_MODAL,
      account,
    });

    dispatch(openModal('MUTE'));
  };
}

export function toggleHideNotifications() {
  return dispatch => {
    dispatch({ type: MUTES_TOGGLE_HIDE_NOTIFICATIONS });
  };
}
<|endoftext|>


<commit_before>import React from "react";

import {formatNumberWithCommas, encodeFileAsBase64, truncate} from "../utils/generalUtils";
import {ProjectContext, perPlotLimit, sampleLimit} from "./constants";

export class SampleDesign extends React.Component {
    /// Render Functions

    renderLabeledInput = (label, property) => (

            {label}
            <input
                className="form-control form-control-sm"
                id={property}
                min="0"
                onChange={e => this.context.setProjectDetails({[property]: Number(e.target.value)})}
                step="1"
                type="number"
                value={this.context[property] || ""}
            />

    );

    renderFileInput = fileType => (

                    Upload sample file
                    <input
                        accept={fileType === "csv" ? "text/csv" : "application/zip"}
                        defaultValue=""
                        id="sample-distribution-file"
                        onChange={e => {
                            const file = e.target.files[0];
                            encodeFileAsBase64(file, base64 => this.context.setProjectDetails({
                                sampleFileName: file.name,
                                sampleFileBase64: base64
                            }));
                        }}
                        style={{display: "none"}}
                        type="file"
                    />

                    File: {this.context.sampleFileName
                            || ((this.context.projectId === -1 || this.context.plotFileName) ? " None" : " Use existing data")}

                Download example sample {fileType === "csv" ? "csv" : "shape"} file

    );

    toggleSampleGeometry = geometry => {
        const {sampleGeometries} = this.context.designSettings;
        this.context.setProjectDetails({
            designSettings: {
                ...this.context.designSettings,
                sampleGeometries: {...sampleGeometries, [geometry]: !sampleGeometries[geometry]}
            }
        });
    };

    render() {
        const {
            allowDrawnSamples,
            designSettings: {sampleGeometries, qaqcAssignment: {qaqcMethod}},
            plotDistribution,
            sampleDistribution,
            setProjectDetails
        } = this.context;
        const totalPlots = this.props.getTotalPlots();
        const samplesPerPlot = this.props.getSamplesPerPlot();
        const qaqcEnabled = ["overlap", "sme"].includes(qaqcMethod);

        const sampleOptions = {
            random: {
                display: "Random",
                description: "Sample points will be randomly distributed within the plot boundary.",
                inputs: [() => this.renderLabeledInput("Number of samples", "samplesPerPlot")],
                disabled: plotDistribution === "shp"
            },
            gridded: {
                display: "Gridded",
                description: "Sample points will be arranged on a grid within the plot boundary using the sample spacing selected below.",
                inputs: [() => this.renderLabeledInput("Sample spacing (m)", "sampleResolution")],
                disabled: plotDistribution === "shp"
            },
            center: {
                display: "Center",
                description: "A single sample point will be placed in the center of the plot.",
                inputs: []
            },
            csv: {
                display: "CSV File",
                description: "Specify your own sample points by uploading a CSV with these fields: LON,LAT,PLOTID,SAMPLEID.",
                inputs: [() => this.renderFileInput("csv")],
                disabled: !["csv", "shp"].includes(plotDistribution)
            },
            shp: {
                display: "SHP File",
                description: "Specify your own sample shapes by uploading a zipped Shapefile (containing SHP, SHX, DBF, and PRJ files) of polygon features. Each feature must have PLOTID and SAMPLEID fields.",
                inputs: [() => this.renderFileInput("shp")],
                disabled: !["csv", "shp"].includes(plotDistribution)
            },
            none: {
                display: "None",
                description: "Do not predefine any samples. Requires users to draw their own samples during collection.",
                inputs: [() =>
Users will draw samples at collection time.
],
                disabled: qaqcEnabled
            }
        };

        const geometries = {
            points: {
                display: "Points",
                alwaysEnabled: ["random", "gridded", "center"]
            },
            lines: {
                display: "Lines"
            },
            polygons: {
                display: "Polygons"
            }
        };

        return (

Sample Generation

                    Spatial Distribution
                    <select
                        className="form-control form-control-sm ml-3"
                        onChange={e => {
                            const newDistributionType = e.target.value;
                            const pointRequired = ["random", "gridded", "center"].includes(newDistributionType);
                            const {points} = sampleGeometries;
                            setProjectDetails({
                                allowDrawnSamples: (newDistributionType === "none") || allowDrawnSamples,
                                sampleDistribution: newDistributionType,
                                designSettings: {
                                    ...this.context.designSettings,
                                    sampleGeometries: {
                                        ...sampleGeometries,
                                        points: points || pointRequired
                                    }
                                }
                            });
                        }}
                        style={{width: "initial"}}
                        value={sampleDistribution}
                    >
                        {Object.entries(sampleOptions).map(([key, options]) => (
                            <option key={key} disabled={options.disabled} value={key}>
                                {options.display}
                            </option>
                        ))}
                    </select>

                    - {sampleOptions[sampleDistribution].description}

                    {sampleOptions[sampleDistribution].inputs.map((i, idx) => (

                            {i()}

                    ))}

 perPlotLimit ? "#8B0000" : "#006400",
                        fontSize: "1rem",
                        whiteSpace: "pre-line"
                    }}
                >
                    {samplesPerPlot > 0 && `Each plot will contain around ${formatNumberWithCommas(samplesPerPlot)} samples.`}
                    {totalPlots > 0 && samplesPerPlot > 0
                        ? `  There will be around ${formatNumberWithCommas(totalPlots * samplesPerPlot)} total samples in the project.`
                        : sampleDistribution === "none"
                            ? "  No samples will be added to the plot."
                            : ""}
                    {totalPlots > 0 && samplesPerPlot > 0 && samplesPerPlot > perPlotLimit
                        && `* The maximum allowed for the selected sample distribution is ${formatNumberWithCommas(perPlotLimit)}`
                            + ` samples per plot. * The maximum allowed samples per project is ${formatNumberWithCommas(sampleLimit)}.`}

                        <input
                            checked={allowDrawnSamples || sampleDistribution === "none"}
                            className="form-check-input"
                            disabled={sampleDistribution === "none" || qaqcEnabled}
                            id="allow-drawn-samples"
                            onChange={() => setProjectDetails({allowDrawnSamples: !allowDrawnSamples})}
                            type="checkbox"
                        />

                            Allow users to draw their own samples

                        - Enable this to allow users to draw and label points, lines,
                        and polygons during data collection.

                    {qaqcEnabled && (

                            - When Quality Control is enabled, the project can no longer support User Drawn samples.
                            Set Quality Control to "None" to re-enable User-Drawn Samples.

                    )}
                    {allowDrawnSamples && (

                                Allowed sample geometries:

                                - Allow sample geometries to points, lines, and/or polygons.

                            {Object.entries(geometries).map(([geometry, options]) => (

                                    <input
                                        checked={sampleGeometries[geometry]}
                                        className="form-check-input"
                                        disabled={options.alwaysEnabled?.includes(sampleDistribution)}
                                        id={geometry}
                                        onChange={() => this.toggleSampleGeometry(geometry)}
                                        type="checkbox"
                                        value={geometry}
                                    />

                                        {options.display}

                            ))}

                    )}

        );
    }
}
SampleDesign.contextType = ProjectContext;

export function SampleReview() {
    return (
        <ProjectContext.Consumer>
            {({
                allowDrawnSamples,
                sampleDistribution,
                sampleFileName,
                sampleResolution,
                samplesPerPlot,
                useTemplatePlots
            }) => (

                    {useTemplatePlots &&
Samples will be copied from template project
}
                    {sampleDistribution === "none"
                        ?
No samples are predefined.

                        : (

                                                Spatial Distribution

                                                    {sampleDistribution}

                                                Samples Per Plot

                                                    {samplesPerPlot} / plot

                                            {sampleDistribution === "gridded" && (

                                                    Sample Spacing

                                                        {sampleResolution} m

                                            )}
                                            {sampleFileName && (

                                                    Sample File

                                                            {sampleFileName.split(".").map(s => truncate(s, 13)).join("")}

{sampleFileName}

                                            )}

                        )}
                    {allowDrawnSamples &&
Users can draw additional samples at collection time.
}

            )}
        </ProjectContext.Consumer>
    );
}

export function SamplePreview() {
    // TODO: Update messaging so it's more clear when shp is defined at the plot level, especially with CSV samples
    return (
        <ProjectContext.Consumer>
            {({plotDistribution, sampleDistribution, plotShape}) => (

                    {(plotDistribution === "shp")
                        ?
The system cannot currently generate a preview of plot shp files.

                        : (sampleDistribution === "csv")
                            ?
The system cannot currently generate a preview of sample csv files.

                            : (sampleDistribution === "shp")
                                ?
The system cannot currently generate a preview of sample shp files.

                                : (

                                            The following is a mock up of a {plotShape}
                                            plot with {sampleDistribution} samples.

                                )}

            )}
        </ProjectContext.Consumer>
    );
}
<commit_msg>CEO-210 Simplify qaqcMethod check<commit_after>import React from "react";

import {formatNumberWithCommas, encodeFileAsBase64, truncate} from "../utils/generalUtils";
import {ProjectContext, perPlotLimit, sampleLimit} from "./constants";

export class SampleDesign extends React.Component {
    /// Render Functions

    renderLabeledInput = (label, property) => (

            {label}
            <input
                className="form-control form-control-sm"
                id={property}
                min="0"
                onChange={e => this.context.setProjectDetails({[property]: Number(e.target.value)})}
                step="1"
                type="number"
                value={this.context[property] || ""}
            />

    );

    renderFileInput = fileType => (

                    Upload sample file
                    <input
                        accept={fileType === "csv" ? "text/csv" : "application/zip"}
                        defaultValue=""
                        id="sample-distribution-file"
                        onChange={e => {
                            const file = e.target.files[0];
                            encodeFileAsBase64(file, base64 => this.context.setProjectDetails({
                                sampleFileName: file.name,
                                sampleFileBase64: base64
                            }));
                        }}
                        style={{display: "none"}}
                        type="file"
                    />

                    File: {this.context.sampleFileName
                            || ((this.context.projectId === -1 || this.context.plotFileName) ? " None" : " Use existing data")}

                Download example sample {fileType === "csv" ? "csv" : "shape"} file

    );

    toggleSampleGeometry = geometry => {
        const {sampleGeometries} = this.context.designSettings;
        this.context.setProjectDetails({
            designSettings: {
                ...this.context.designSettings,
                sampleGeometries: {...sampleGeometries, [geometry]: !sampleGeometries[geometry]}
            }
        });
    };

    render() {
        const {
            allowDrawnSamples,
            designSettings: {sampleGeometries, qaqcAssignment: {qaqcMethod}},
            plotDistribution,
            sampleDistribution,
            setProjectDetails
        } = this.context;
        const totalPlots = this.props.getTotalPlots();
        const samplesPerPlot = this.props.getSamplesPerPlot();
        const qaqcEnabled = qaqcMethod !== "none";

        const sampleOptions = {
            random: {
                display: "Random",
                description: "Sample points will be randomly distributed within the plot boundary.",
                inputs: [() => this.renderLabeledInput("Number of samples", "samplesPerPlot")],
                disabled: plotDistribution === "shp"
            },
            gridded: {
                display: "Gridded",
                description: "Sample points will be arranged on a grid within the plot boundary using the sample spacing selected below.",
                inputs: [() => this.renderLabeledInput("Sample spacing (m)", "sampleResolution")],
                disabled: plotDistribution === "shp"
            },
            center: {
                display: "Center",
                description: "A single sample point will be placed in the center of the plot.",
                inputs: []
            },
            csv: {
                display: "CSV File",
                description: "Specify your own sample points by uploading a CSV with these fields: LON,LAT,PLOTID,SAMPLEID.",
                inputs: [() => this.renderFileInput("csv")],
                disabled: !["csv", "shp"].includes(plotDistribution)
            },
            shp: {
                display: "SHP File",
                description: "Specify your own sample shapes by uploading a zipped Shapefile (containing SHP, SHX, DBF, and PRJ files) of polygon features. Each feature must have PLOTID and SAMPLEID fields.",
                inputs: [() => this.renderFileInput("shp")],
                disabled: !["csv", "shp"].includes(plotDistribution)
            },
            none: {
                display: "None",
                description: "Do not predefine any samples. Requires users to draw their own samples during collection.",
                inputs: [() =>
Users will draw samples at collection time.
],
                disabled: qaqcEnabled
            }
        };

        const geometries = {
            points: {
                display: "Points",
                alwaysEnabled: ["random", "gridded", "center"]
            },
            lines: {
                display: "Lines"
            },
            polygons: {
                display: "Polygons"
            }
        };

        return (

Sample Generation

                    Spatial Distribution
                    <select
                        className="form-control form-control-sm ml-3"
                        onChange={e => {
                            const newDistributionType = e.target.value;
                            const pointRequired = ["random", "gridded", "center"].includes(newDistributionType);
                            const {points} = sampleGeometries;
                            setProjectDetails({
                                allowDrawnSamples: (newDistributionType === "none") || allowDrawnSamples,
                                sampleDistribution: newDistributionType,
                                designSettings: {
                                    ...this.context.designSettings,
                                    sampleGeometries: {
                                        ...sampleGeometries,
                                        points: points || pointRequired
                                    }
                                }
                            });
                        }}
                        style={{width: "initial"}}
                        value={sampleDistribution}
                    >
                        {Object.entries(sampleOptions).map(([key, options]) => (
                            <option key={key} disabled={options.disabled} value={key}>
                                {options.display}
                            </option>
                        ))}
                    </select>

                    - {sampleOptions[sampleDistribution].description}

                    {sampleOptions[sampleDistribution].inputs.map((i, idx) => (

                            {i()}

                    ))}

 perPlotLimit ? "#8B0000" : "#006400",
                        fontSize: "1rem",
                        whiteSpace: "pre-line"
                    }}
                >
                    {samplesPerPlot > 0 && `Each plot will contain around ${formatNumberWithCommas(samplesPerPlot)} samples.`}
                    {totalPlots > 0 && samplesPerPlot > 0
                        ? `  There will be around ${formatNumberWithCommas(totalPlots * samplesPerPlot)} total samples in the project.`
                        : sampleDistribution === "none"
                            ? "  No samples will be added to the plot."
                            : ""}
                    {totalPlots > 0 && samplesPerPlot > 0 && samplesPerPlot > perPlotLimit
                        && `* The maximum allowed for the selected sample distribution is ${formatNumberWithCommas(perPlotLimit)}`
                            + ` samples per plot. * The maximum allowed samples per project is ${formatNumberWithCommas(sampleLimit)}.`}

                        <input
                            checked={allowDrawnSamples || sampleDistribution === "none"}
                            className="form-check-input"
                            disabled={sampleDistribution === "none" || qaqcEnabled}
                            id="allow-drawn-samples"
                            onChange={() => setProjectDetails({allowDrawnSamples: !allowDrawnSamples})}
                            type="checkbox"
                        />

                            Allow users to draw their own samples

                        - Enable this to allow users to draw and label points, lines,
                        and polygons during data collection.

                    {qaqcEnabled && (

                            - When Quality Control is enabled, the project can no longer support User Drawn samples.
                            Set Quality Control to "None" to re-enable User-Drawn Samples.

                    )}
                    {allowDrawnSamples && (

                                Allowed sample geometries:

                                - Allow sample geometries to points, lines, and/or polygons.

                            {Object.entries(geometries).map(([geometry, options]) => (

                                    <input
                                        checked={sampleGeometries[geometry]}
                                        className="form-check-input"
                                        disabled={options.alwaysEnabled?.includes(sampleDistribution)}
                                        id={geometry}
                                        onChange={() => this.toggleSampleGeometry(geometry)}
                                        type="checkbox"
                                        value={geometry}
                                    />

                                        {options.display}

                            ))}

                    )}

        );
    }
}
SampleDesign.contextType = ProjectContext;

export function SampleReview() {
    return (
        <ProjectContext.Consumer>
            {({
                allowDrawnSamples,
                sampleDistribution,
                sampleFileName,
                sampleResolution,
                samplesPerPlot,
                useTemplatePlots
            }) => (

                    {useTemplatePlots &&
Samples will be copied from template project
}
                    {sampleDistribution === "none"
                        ?
No samples are predefined.

                        : (

                                                Spatial Distribution

                                                    {sampleDistribution}

                                                Samples Per Plot

                                                    {samplesPerPlot} / plot

                                            {sampleDistribution === "gridded" && (

                                                    Sample Spacing

                                                        {sampleResolution} m

                                            )}
                                            {sampleFileName && (

                                                    Sample File

                                                            {sampleFileName.split(".").map(s => truncate(s, 13)).join("")}

{sampleFileName}

                                            )}

                        )}
                    {allowDrawnSamples &&
Users can draw additional samples at collection time.
}

            )}
        </ProjectContext.Consumer>
    );
}

export function SamplePreview() {
    // TODO: Update messaging so it's more clear when shp is defined at the plot level, especially with CSV samples
    return (
        <ProjectContext.Consumer>
            {({plotDistribution, sampleDistribution, plotShape}) => (

                    {(plotDistribution === "shp")
                        ?
The system cannot currently generate a preview of plot shp files.

                        : (sampleDistribution === "csv")
                            ?
The system cannot currently generate a preview of sample csv files.

                            : (sampleDistribution === "shp")
                                ?
The system cannot currently generate a preview of sample shp files.

                                : (

                                            The following is a mock up of a {plotShape}
                                            plot with {sampleDistribution} samples.

                                )}

            )}
        </ProjectContext.Consumer>
    );
}
<|endoftext|>


<commit_before>// In React, an owner is the component that sets the props of other components, if desired.
// (see https://facebook.github.io/react/docs/multiple-components.html)
//
// NOTE: If you want to reference another Origami component in this file's JSX below, import
// it's src/js/component-owner.js directly from this project's /node_modules.

import '../scss/component-specific.scss';

import React, {PropTypes} from 'react';
import {intlShape, injectIntl} from 'react-intl';
import {messages} from './defaultMessages';

class ComponentOwner extends React.Component {

  //
  // Modify or add prop types to validate the properties passed to this component!
  // This is defined using an ES7 class property (transpiled by Babel Stage 0)
  //
  static propTypes = {
    intl: intlShape.isRequired,
    data: PropTypes.shape({
      elementId: PropTypes.string.isRequired,
      locale: PropTypes.string
    })
  };

  constructor(props) {

    super(props);

    //
    // FOR DEMO - use state when you need to respond to user input, a server request or the passage of time
    //
    this.state = {
      text: ''
    };
  }

  //
  // Note that combining the fat arrow syntax with ES7 class properties (transpiled by Babel Stage 0), we eliminate the
  // need to do manual binding of the 'this' context in event handlers or callbacks. React binds all other contexts
  // as expected.
  //
  // FOR DEMO and should be removed:
  _change = () => {
    this.setState({text: this.props.data.greeting});
  };

  render() {

    const {formatMessage} = this.props.intl;
    //
    // FOR DEMO and should be refactored for your purposes:
    //
    return (

        <button className="pe-btn pe-btn--primary" onClick={this._change}>{formatMessage(messages.buttonText)}</button>

          <input type="text" placeholder={formatMessage(messages.placeholder)} value={this.state.text} />

    )
  }

}

export default injectIntl(ComponentOwner); // Inject this.props.intl into the component context
<commit_msg>docs: typo<commit_after>// In React, an owner is the component that sets the props of other components, if desired.
// (see https://facebook.github.io/react/docs/multiple-components.html)
//
// NOTE: If you want to reference another Origami component in this file's JSX below, import
// its src/js/component-owner.js directly from this project's /node_modules.

import '../scss/component-specific.scss';

import React, {PropTypes} from 'react';
import {intlShape, injectIntl} from 'react-intl';
import {messages} from './defaultMessages';

class ComponentOwner extends React.Component {

  //
  // Modify or add prop types to validate the properties passed to this component!
  // This is defined using an ES7 class property (transpiled by Babel Stage 0)
  //
  static propTypes = {
    intl: intlShape.isRequired,
    data: PropTypes.shape({
      elementId: PropTypes.string.isRequired,
      locale: PropTypes.string
    })
  };

  constructor(props) {

    super(props);

    //
    // FOR DEMO - use state when you need to respond to user input, a server request or the passage of time
    //
    this.state = {
      text: ''
    };
  }

  //
  // Note that combining the fat arrow syntax with ES7 class properties (transpiled by Babel Stage 0), we eliminate the
  // need to do manual binding of the 'this' context in event handlers or callbacks. React binds all other contexts
  // as expected.
  //
  // FOR DEMO and should be removed:
  _change = () => {
    this.setState({text: this.props.data.greeting});
  };

  render() {

    const {formatMessage} = this.props.intl;
    //
    // FOR DEMO and should be refactored for your purposes:
    //
    return (

        <button className="pe-btn pe-btn--primary" onClick={this._change}>{formatMessage(messages.buttonText)}</button>

          <input type="text" placeholder={formatMessage(messages.placeholder)} value={this.state.text} />

    )
  }

}

export default injectIntl(ComponentOwner); // Inject this.props.intl into the component context
<|endoftext|>