<commit_before>/**
 * Copyright 2015 IBM Corp. All Rights Reserved.
 *
 * 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
 *
 * http://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.
 */
 /* global normalize, move */
 /* eslint no-unused-vars: "warn" */

/**
 * This is a JS constructor that controls this application's model and state
 * logic.
 * @param {Object} documentTones tone data
 * @param {Array} sentences sentence tone array
 * @param {Object} thresholds threshold data
 * @param {String} selectedSample selected sample id
 * @return {Object} exposed functions that interact with application state logic
 *
 */
function App(documentTones, sentences, thresholds, selectedSample) {
  var _selectedFilter = 'Anger',
    _selectedTone = 'Emotion Tone',
    _selectedSample = selectedSample || 'customer-call',
    _lowToHigh = false,
    _currentHoveredOriginalSentence = document.querySelector('body'),
    _rankedSentences = sentences,
    _originalSentences,
    _documentTones = documentTones,
    _thresholds = thresholds,
    _isHoveringOriginalText = false,
    _socialToneHoverTexts,
    _toneHash,
    TONE_CATEGORIES_RESET = [{
      tones: [{
        score: 0,
        tone_id: 'Anger',
        tone_name: 'Anger',
        tone_category_id: 'emotion_tone',
        tone_category_name: 'Emotion Tone'
      }, {
        score: 0,
        tone_id: 'Disgust',
        tone_name: 'Disgust',
        tone_category_id: 'emotion_tone',
        tone_category_name: 'Emotion Tone'
      }, {
        score: 0,
        tone_id: 'Fear',
        tone_name: 'Fear',
        tone_category_id: 'emotion_tone',
        tone_category_name: 'Emotion Tone'
      }, {
        score: 0,
        tone_id: 'Joy',
        tone_name: 'Joy',
        tone_category_id: 'emotion_tone',
        tone_category_name: 'Emotion Tone'
      }, {
        score: 0,
        tone_id: 'Sadness',
        tone_name: 'Sadness',
        tone_category_id: 'emotion_tone',
        tone_category_name: 'Emotion Tone'
      }],
      category_id: 'emotion_tone',
      category_name: 'Emotion Tone'
    }, {
      tones: [{
        score: 0,
        tone_id: 'Analytical',
        tone_name: 'Analytical',
        tone_category_id: 'language_tone',
        tone_category_name: 'Language Tone'
      }, {
        score: 0,
        tone_id: 'Confident',
        tone_name: 'Confident',
        tone_category_id: 'language_tone',
        tone_category_name: 'Language Tone'
      }, {
        score: 0,
        tone_id: 'Tentative',
        tone_name: 'Tentative',
        tone_category_id: 'language_tone',
        tone_category_name: 'Language Tone'
      }],
      category_id: 'language_tone',
      category_name: 'Language Tone'
    }, {
      tones: [{
        score: 0,
        tone_id: 'Openness_Big5',
        tone_name: 'Openness',
        tone_category_id: 'social_tone',
        tone_category_name: 'Social Tone'
      }, {
        score: 0,
        tone_id: 'Conscientiousness_Big5',
        tone_name: 'Conscientiousness',
        tone_category_id: 'social_tone',
        tone_category_name: 'Social Tone'
      }, {
        score: 0,
        tone_id: 'Extraversion_Big5',
        tone_name: 'Extraversion',
        tone_category_id: 'social_tone',
        tone_category_name: 'Social Tone'
      }, {
        score: 0,
        tone_id: 'Agreeableness_Big5',
        tone_name: 'Agreeableness',
        tone_category_id: 'social_tone',
        tone_category_name: 'Social Tone'
      }, {
        score: 0,
        tone_id: 'Neuroticism_Big5',
        tone_name: 'Emotional Range',
        tone_category_id: 'social_tone',
        tone_category_name: 'Social Tone'
      }],
      category_id: 'social_tone',
      category_name: 'Social Tone'
    }],      SCORE_DECIMAL_PLACE = 2,
    PERCENTAGE_DECIMAL_PLACE = 1,
    SOCIAL_TONE_MIN_RANGE = -1,
    SOCIAL_TONE_MAX_RANGE = 1,
    output = {};

  /**
   * Make sure sentences have proper tone data values.
   * Mutates _rankedSentences
   * @return {undefined}
   */
  function _cleanSentences() {
    // look for empty tone_categories and set tone_categories to 0 values
    _rankedSentences.forEach(function(item) {
      if (item.tone_categories.length === 0) {
        item.tone_categories = TONE_CATEGORIES_RESET.slice(0);
      }
    });
  }

  /**
   * Get index of a tone
   * @param {String} key tone name as key
   * @return {int} index positioning of tone
   */
  function _searchIndex(key) {
    return _toneHash[key].index;
  }

  /**
   * This is a helper function to determine which classname to use by
   * comparing tone score with thresholds.
   * @param {String} toneKey tone name as key
   * @param {int} score score to evaluate
   * @param {String} classNameType class name type
   * @return {String} resulting class name
   */
  function _toneLevel(toneKey, score, classNameType) {
    var outputTone,
      toneValue = _toneHash[toneKey],
      newScore = score,
      baseThreshold = 0;

    if (newScore <= baseThreshold) {
      outputTone = '';
    } else if (newScore < toneValue.low.score) {
      outputTone = toneValue.low[classNameType];
    } else if (newScore > toneValue.high.score) {
      outputTone = toneValue.high[classNameType];
    } else {
      outputTone = toneValue.medium[classNameType];
    }
    return outputTone;
  }

  /**
   * Percentagify
   * @param {float} score decimal score
   * @return {String} percentage score in PERCENTAGE_DECIMAL_PLACE placements
   */
  function _percentagify(score) {
    return parseFloat((score * 100).toFixed(PERCENTAGE_DECIMAL_PLACE));
  }

  /**
   * Getter / Setter for _selectedFilter
   * Also sets _selectedTone appropriately
   * @param {String} str setter tone name
   * @return {String} _selectedFilter
   */
  output.selectedFilter = function(str) {
    if (!arguments.length) return _selectedFilter;
    _selectedFilter = str;
    _selectedTone = _toneHash[_selectedFilter].tone;

    return output;
  };

  /**
   * Getter for _selectedTone
   * @return {String} _selectedTone
   */
  output.selectedTone = function() {
    return _selectedTone;
  };

  /**
   * Getter for _toneHash
   * @return {Object} _toneHash
   */
  output.toneHash = function() {
    return _toneHash;
  };

  /**
   * Getter / Setter for _lowToHigh
   * @param {bool} bool setter boolean
   * @return {bool} (getter) {bool} _lowToHigh
   */
  output.lowToHigh = function(bool) {
    if (!arguments.length) return _lowToHigh;
    _lowToHigh = bool;
    return output;
  };

  /**
   * Switch bool value of _lowToHigh
   * @return {Object} Constructor returned object
   */
  output.toggleLowToHigh = function() {
    _lowToHigh = !_lowToHigh;
    return output;
  };

  /**
   * Getter / Setter for _isHoveringOriginalText
   * @param {bool} bool setter boolean
   * @return {bool} (getter) _isHoveringOriginalText
   */
  output.isHoveringOriginalText = function(bool) {
    if (!arguments.length) return _isHoveringOriginalText;
    _isHoveringOriginalText = bool;
    return output;
  };

  /**
   * Getter for _thresholds
   * @return {Object} _thresholds
   */
  output.thresholds = function() {
    return _thresholds;
  };

  /**
   * Getter / Setter for _currentHoveredOriginalSentence
   * @param {DOMElement} element setter element
   * @return {DOMElement} (getter)  _currentHoveredOriginalSentence
   */
  output.currentHoveredOriginalSentence = function(element) {
    if (!arguments.length) return _currentHoveredOriginalSentence;
    _currentHoveredOriginalSentence = element;
    return output;
  };

  /**
   * Export an adaptation of _rankedSentences model
   * @return {Array} array of {Object} sentence data
   */
  output.updateRankedSentences = function() {
    var sort = _lowToHigh ?
          function(a, b) {
            return a.tone_categories[_searchIndex(_selectedTone)].tones[_searchIndex(_selectedFilter)].score
              - b.tone_categories[_searchIndex(_selectedTone)].tones[_searchIndex(_selectedFilter)].score;
          } :
          function(a, b) {
            return b.tone_categories[_searchIndex(_selectedTone)].tones[_searchIndex(_selectedFilter)].score
              - a.tone_categories[_searchIndex(_selectedTone)].tones[_searchIndex(_selectedFilter)].score;
          },
      map = function(item) {
        var score = item.tone_categories[_searchIndex(_selectedTone)].tones[_searchIndex(_selectedFilter)].score.toFixed(SCORE_DECIMAL_PLACE);
        return {
          text: item.text,
          score: score,
          className: 'sentence-rank--score_' + normalize(_selectedFilter)
        };
      };
    return _rankedSentences.sort(sort).map(map);
  };

  /**
   * Export an adaptation of _originalSentences model
   * @return {Array} array of {Object} sentence data
   */
  output.updateOriginalSentences = function() {
    var map = function(item) {
      var result = item;
      result.className = _toneLevel(_selectedFilter, item.tone_categories[_searchIndex(_selectedTone)].tones[_searchIndex(_selectedFilter)].score, 'className_OT');
      result.text = result.text.replace(/\r?\n/g, '<br />');
      return result;
    };
    return _originalSentences.map(map);
  };

  /**
   * Export an adaptation of original sentences tooltip model
   * @param {int} sentenceIndex sentence index
   * @return {Array} array of {Object} sentence data
   */
  output.updateOriginalSentencesTooltips = function(sentenceIndex) {
    var map = function(item) {
      var result = item;
      result.score_percentage = item.score.toFixed(SCORE_DECIMAL_PLACE);
      result.className = 'original-text--tooltip-li_' + normalize(result.tone_name);
      return result;
    };

    return move(
      _originalSentences[sentenceIndex].tone_categories[_searchIndex(_selectedTone)].tones.slice(0).map(map),
      _searchIndex(_selectedFilter),
      0);
  };

  /**
   * Select initial filter depending on sample text picked
   * @return {undefined}
   */
  output.selectFilterBySample = function() {
    var getHighestTone = function(toneCategory) {
        var highestTone = _documentTones.tone_categories[_searchIndex(toneCategory)].tones[0].tone_name,
          highestScore = 0;
        _documentTones.tone_categories[_searchIndex(toneCategory)].tones.forEach(function(item) {
          if (highestScore < item.score) {
            highestScore = item.score;
            highestTone = item.tone_name;
          }
        });
        return highestTone;
      },
      sample = {
        'customer-call': getHighestTone('Emotion Tone'),
        'email': getHighestTone('Social Tone'),
        'corporate-announcement': getHighestTone('Language Tone'),
        'own-text': getHighestTone('Emotion Tone')
      };

    if (_selectedSample in sample) {
      output.selectedFilter(sample[_selectedSample]);
    }
  };

  /**
   * Expose {Function} _percentagify
   */
  output.percentagify = _percentagify;

  // cleaning and setting up everything
  _cleanSentences(_rankedSentences);
  _originalSentences = _rankedSentences.slice(0);
  _socialToneHoverTexts = {
    'Agreeableness': 'The tendency to be compassionate and cooperative towards others.',
    'Emotional Range': 'The extent a person\'s emotion is sensitive to the environment.',
    'Openness': 'The extent a person is open to experience a variety of activities.',
    'Conscientiousness': 'The tendency to act in an organized or thoughtful way.',
    'Extraversion': 'The tendency to seek stimulation in the company of others.'
  };

  // Constructing the _toneHash hashmap
  _toneHash = sentences[0].tone_categories.reduce(function(prevVal, curVal, curIndex) {
    var reducedPrevVal = curVal.tones.reduce(function(prevVal2, curVal2, curIndex2) {
      prevVal2[curVal2.tone_name] = {
        index: curIndex2,
        tone: curVal.category_name,
        low: {
          score: _thresholds.sentence[curVal2.tone_name][0],
          className_OT: 'original-text--sentence_' + normalize(curVal2.tone_name) + '-low',
          className_SR: 'sentence-rank--score_' + normalize(curVal2.tone_name) + '-low'
        },
        medium: {
          className_OT: 'original-text--sentence_' + normalize(curVal2.tone_name) + '-medium',
          className_SR: 'sentence-rank--score_' + normalize(curVal2.tone_name) + '-medium'
        },
        high: {
          score: _thresholds.sentence[curVal2.tone_name][1],
          className_OT: 'original-text--sentence_' + normalize(curVal2.tone_name) + '-high',
          className_SR: 'sentence-rank--score_' + normalize(curVal2.tone_name) + '-high'
        }
      };

      if (curVal.category_name === 'Social Tone') {
        prevVal2[curVal2.tone_name].tooltip = _socialToneHoverTexts[curVal2.tone_name];
      }
      return prevVal2;
    }, prevVal);
    reducedPrevVal[curVal.category_name] = {
      index: curIndex,
      tone: curVal.category_name
    };
    return reducedPrevVal;
  }, {});

  return output;
}
<commit_msg>Fixed comment<commit_after>/**
 * Copyright 2015 IBM Corp. All Rights Reserved.
 *
 * 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
 *
 * http://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.
 */
 /* global normalize, move */
 /* eslint no-unused-vars: "warn" */

/**
 * This is a JS constructor that controls this application's model and state
 * logic.
 * @param {Object} documentTones tone data
 * @param {Array} sentences sentence tone array
 * @param {Object} thresholds threshold data
 * @param {String} selectedSample selected sample id
 * @return {Object} exposed functions that interact with application state logic
 *
 */
function App(documentTones, sentences, thresholds, selectedSample) {
  var _selectedFilter = 'Anger',
    _selectedTone = 'Emotion Tone',
    _selectedSample = selectedSample || 'customer-call',
    _lowToHigh = false,
    _currentHoveredOriginalSentence = document.querySelector('body'),
    _rankedSentences = sentences,
    _originalSentences,
    _documentTones = documentTones,
    _thresholds = thresholds,
    _isHoveringOriginalText = false,
    _socialToneHoverTexts,
    _toneHash,
    TONE_CATEGORIES_RESET = [{
      tones: [{
        score: 0,
        tone_id: 'Anger',
        tone_name: 'Anger',
        tone_category_id: 'emotion_tone',
        tone_category_name: 'Emotion Tone'
      }, {
        score: 0,
        tone_id: 'Disgust',
        tone_name: 'Disgust',
        tone_category_id: 'emotion_tone',
        tone_category_name: 'Emotion Tone'
      }, {
        score: 0,
        tone_id: 'Fear',
        tone_name: 'Fear',
        tone_category_id: 'emotion_tone',
        tone_category_name: 'Emotion Tone'
      }, {
        score: 0,
        tone_id: 'Joy',
        tone_name: 'Joy',
        tone_category_id: 'emotion_tone',
        tone_category_name: 'Emotion Tone'
      }, {
        score: 0,
        tone_id: 'Sadness',
        tone_name: 'Sadness',
        tone_category_id: 'emotion_tone',
        tone_category_name: 'Emotion Tone'
      }],
      category_id: 'emotion_tone',
      category_name: 'Emotion Tone'
    }, {
      tones: [{
        score: 0,
        tone_id: 'Analytical',
        tone_name: 'Analytical',
        tone_category_id: 'language_tone',
        tone_category_name: 'Language Tone'
      }, {
        score: 0,
        tone_id: 'Confident',
        tone_name: 'Confident',
        tone_category_id: 'language_tone',
        tone_category_name: 'Language Tone'
      }, {
        score: 0,
        tone_id: 'Tentative',
        tone_name: 'Tentative',
        tone_category_id: 'language_tone',
        tone_category_name: 'Language Tone'
      }],
      category_id: 'language_tone',
      category_name: 'Language Tone'
    }, {
      tones: [{
        score: 0,
        tone_id: 'Openness_Big5',
        tone_name: 'Openness',
        tone_category_id: 'social_tone',
        tone_category_name: 'Social Tone'
      }, {
        score: 0,
        tone_id: 'Conscientiousness_Big5',
        tone_name: 'Conscientiousness',
        tone_category_id: 'social_tone',
        tone_category_name: 'Social Tone'
      }, {
        score: 0,
        tone_id: 'Extraversion_Big5',
        tone_name: 'Extraversion',
        tone_category_id: 'social_tone',
        tone_category_name: 'Social Tone'
      }, {
        score: 0,
        tone_id: 'Agreeableness_Big5',
        tone_name: 'Agreeableness',
        tone_category_id: 'social_tone',
        tone_category_name: 'Social Tone'
      }, {
        score: 0,
        tone_id: 'Neuroticism_Big5',
        tone_name: 'Emotional Range',
        tone_category_id: 'social_tone',
        tone_category_name: 'Social Tone'
      }],
      category_id: 'social_tone',
      category_name: 'Social Tone'
    }],      SCORE_DECIMAL_PLACE = 2,
    PERCENTAGE_DECIMAL_PLACE = 1,
    SOCIAL_TONE_MIN_RANGE = -1,
    SOCIAL_TONE_MAX_RANGE = 1,
    output = {};

  /**
   * Make sure sentences have proper tone data values.
   * Mutates _rankedSentences
   * @return {undefined}
   */
  function _cleanSentences() {
    // look for empty tone_categories and set tone_categories to 0 values
    _rankedSentences.forEach(function(item) {
      if (item.tone_categories.length === 0) {
        item.tone_categories = TONE_CATEGORIES_RESET.slice(0);
      }
    });
  }

  /**
   * Get index of a tone
   * @param {String} key tone name as key
   * @return {int} index positioning of tone
   */
  function _searchIndex(key) {
    return _toneHash[key].index;
  }

  /**
   * This is a helper function to determine which classname to use by
   * comparing tone score with thresholds.
   * @param {String} toneKey tone name as key
   * @param {int} score score to evaluate
   * @param {String} classNameType class name type
   * @return {String} resulting class name
   */
  function _toneLevel(toneKey, score, classNameType) {
    var outputTone,
      toneValue = _toneHash[toneKey],
      newScore = score,
      baseThreshold = 0;

    if (newScore <= baseThreshold) {
      outputTone = '';
    } else if (newScore < toneValue.low.score) {
      outputTone = toneValue.low[classNameType];
    } else if (newScore > toneValue.high.score) {
      outputTone = toneValue.high[classNameType];
    } else {
      outputTone = toneValue.medium[classNameType];
    }
    return outputTone;
  }

  /**
   * Percentagify
   * @param {float} score decimal score
   * @return {float} percentage score in PERCENTAGE_DECIMAL_PLACE placements
   */
  function _percentagify(score) {
    return parseFloat((score * 100).toFixed(PERCENTAGE_DECIMAL_PLACE));
  }

  /**
   * Getter / Setter for _selectedFilter
   * Also sets _selectedTone appropriately
   * @param {String} str setter tone name
   * @return {String} _selectedFilter
   */
  output.selectedFilter = function(str) {
    if (!arguments.length) return _selectedFilter;
    _selectedFilter = str;
    _selectedTone = _toneHash[_selectedFilter].tone;

    return output;
  };

  /**
   * Getter for _selectedTone
   * @return {String} _selectedTone
   */
  output.selectedTone = function() {
    return _selectedTone;
  };

  /**
   * Getter for _toneHash
   * @return {Object} _toneHash
   */
  output.toneHash = function() {
    return _toneHash;
  };

  /**
   * Getter / Setter for _lowToHigh
   * @param {bool} bool setter boolean
   * @return {bool} (getter) {bool} _lowToHigh
   */
  output.lowToHigh = function(bool) {
    if (!arguments.length) return _lowToHigh;
    _lowToHigh = bool;
    return output;
  };

  /**
   * Switch bool value of _lowToHigh
   * @return {Object} Constructor returned object
   */
  output.toggleLowToHigh = function() {
    _lowToHigh = !_lowToHigh;
    return output;
  };

  /**
   * Getter / Setter for _isHoveringOriginalText
   * @param {bool} bool setter boolean
   * @return {bool} (getter) _isHoveringOriginalText
   */
  output.isHoveringOriginalText = function(bool) {
    if (!arguments.length) return _isHoveringOriginalText;
    _isHoveringOriginalText = bool;
    return output;
  };

  /**
   * Getter for _thresholds
   * @return {Object} _thresholds
   */
  output.thresholds = function() {
    return _thresholds;
  };

  /**
   * Getter / Setter for _currentHoveredOriginalSentence
   * @param {DOMElement} element setter element
   * @return {DOMElement} (getter)  _currentHoveredOriginalSentence
   */
  output.currentHoveredOriginalSentence = function(element) {
    if (!arguments.length) return _currentHoveredOriginalSentence;
    _currentHoveredOriginalSentence = element;
    return output;
  };

  /**
   * Export an adaptation of _rankedSentences model
   * @return {Array} array of {Object} sentence data
   */
  output.updateRankedSentences = function() {
    var sort = _lowToHigh ?
          function(a, b) {
            return a.tone_categories[_searchIndex(_selectedTone)].tones[_searchIndex(_selectedFilter)].score
              - b.tone_categories[_searchIndex(_selectedTone)].tones[_searchIndex(_selectedFilter)].score;
          } :
          function(a, b) {
            return b.tone_categories[_searchIndex(_selectedTone)].tones[_searchIndex(_selectedFilter)].score
              - a.tone_categories[_searchIndex(_selectedTone)].tones[_searchIndex(_selectedFilter)].score;
          },
      map = function(item) {
        var score = item.tone_categories[_searchIndex(_selectedTone)].tones[_searchIndex(_selectedFilter)].score.toFixed(SCORE_DECIMAL_PLACE);
        return {
          text: item.text,
          score: score,
          className: 'sentence-rank--score_' + normalize(_selectedFilter)
        };
      };
    return _rankedSentences.sort(sort).map(map);
  };

  /**
   * Export an adaptation of _originalSentences model
   * @return {Array} array of {Object} sentence data
   */
  output.updateOriginalSentences = function() {
    var map = function(item) {
      var result = item;
      result.className = _toneLevel(_selectedFilter, item.tone_categories[_searchIndex(_selectedTone)].tones[_searchIndex(_selectedFilter)].score, 'className_OT');
      result.text = result.text.replace(/\r?\n/g, '<br />');
      return result;
    };
    return _originalSentences.map(map);
  };

  /**
   * Export an adaptation of original sentences tooltip model
   * @param {int} sentenceIndex sentence index
   * @return {Array} array of {Object} sentence data
   */
  output.updateOriginalSentencesTooltips = function(sentenceIndex) {
    var map = function(item) {
      var result = item;
      result.score_percentage = item.score.toFixed(SCORE_DECIMAL_PLACE);
      result.className = 'original-text--tooltip-li_' + normalize(result.tone_name);
      return result;
    };

    return move(
      _originalSentences[sentenceIndex].tone_categories[_searchIndex(_selectedTone)].tones.slice(0).map(map),
      _searchIndex(_selectedFilter),
      0);
  };

  /**
   * Select initial filter depending on sample text picked
   * @return {undefined}
   */
  output.selectFilterBySample = function() {
    var getHighestTone = function(toneCategory) {
        var highestTone = _documentTones.tone_categories[_searchIndex(toneCategory)].tones[0].tone_name,
          highestScore = 0;
        _documentTones.tone_categories[_searchIndex(toneCategory)].tones.forEach(function(item) {
          if (highestScore < item.score) {
            highestScore = item.score;
            highestTone = item.tone_name;
          }
        });
        return highestTone;
      },
      sample = {
        'customer-call': getHighestTone('Emotion Tone'),
        'email': getHighestTone('Social Tone'),
        'corporate-announcement': getHighestTone('Language Tone'),
        'own-text': getHighestTone('Emotion Tone')
      };

    if (_selectedSample in sample) {
      output.selectedFilter(sample[_selectedSample]);
    }
  };

  /**
   * Expose {Function} _percentagify
   */
  output.percentagify = _percentagify;

  // cleaning and setting up everything
  _cleanSentences(_rankedSentences);
  _originalSentences = _rankedSentences.slice(0);
  _socialToneHoverTexts = {
    'Agreeableness': 'The tendency to be compassionate and cooperative towards others.',
    'Emotional Range': 'The extent a person\'s emotion is sensitive to the environment.',
    'Openness': 'The extent a person is open to experience a variety of activities.',
    'Conscientiousness': 'The tendency to act in an organized or thoughtful way.',
    'Extraversion': 'The tendency to seek stimulation in the company of others.'
  };

  // Constructing the _toneHash hashmap
  _toneHash = sentences[0].tone_categories.reduce(function(prevVal, curVal, curIndex) {
    var reducedPrevVal = curVal.tones.reduce(function(prevVal2, curVal2, curIndex2) {
      prevVal2[curVal2.tone_name] = {
        index: curIndex2,
        tone: curVal.category_name,
        low: {
          score: _thresholds.sentence[curVal2.tone_name][0],
          className_OT: 'original-text--sentence_' + normalize(curVal2.tone_name) + '-low',
          className_SR: 'sentence-rank--score_' + normalize(curVal2.tone_name) + '-low'
        },
        medium: {
          className_OT: 'original-text--sentence_' + normalize(curVal2.tone_name) + '-medium',
          className_SR: 'sentence-rank--score_' + normalize(curVal2.tone_name) + '-medium'
        },
        high: {
          score: _thresholds.sentence[curVal2.tone_name][1],
          className_OT: 'original-text--sentence_' + normalize(curVal2.tone_name) + '-high',
          className_SR: 'sentence-rank--score_' + normalize(curVal2.tone_name) + '-high'
        }
      };

      if (curVal.category_name === 'Social Tone') {
        prevVal2[curVal2.tone_name].tooltip = _socialToneHoverTexts[curVal2.tone_name];
      }
      return prevVal2;
    }, prevVal);
    reducedPrevVal[curVal.category_name] = {
      index: curIndex,
      tone: curVal.category_name
    };
    return reducedPrevVal;
  }, {});

  return output;
}
<|endoftext|>


<commit_before>var notify = require('notify');
var dhcLib = require('dhc-lib');
var http = require('http');
exports.run = function(opts) {
    var projectName = opts.projectDir;
    var requestList = [];
    var results = {};
    //Get the request list in the DHC project
    requestList = dhcLib.getRequests(projectName);
    //Invoke each request and run the associated tests
    requestList.forEach(function(definition, index) {
        definition = dhcLib.normalize(definition);
        perform(definition, projectName);
    });
};
var perform = function(definition, projectName) {
    invokeDHCRequest(definition, projectName);
    //invokeDHCTests(definition.name,projectName,definition);
};
var decomposeURI = function(uri) {
    var opts = {};
    var comps = uri.split('/');
    var base = comps[0];
    var path = comps.slice(1).join('/');
    var host = base.split(':');
    opts.host = host[0] || 'localhost';
    opts.port = host[1] || '80';
    opts.path = path;
    return opts;
};
var buildRequestOptions = function(definition) {
    var headerDirectives = definition.headers;
    var headers = {};
    var header;
    var opts = decomposeURI(definition.url);
    headerDirectives.forEach(function(headerInfo) {
        if (headerInfo.enabled) {
            headers[headerInfo.name] = headerInfo.value;
        }
    });
    return {
        method: definition.method,
        path: opts.path,
        host: opts.host,
        port: opts.port,
        headers: headers
    };
};
var printRequestDetails = function(options, definition) {
    notify.info(options.method.toUpperCase() + ' ' + options.host + ':' + options.port + '/' + options.path);
    // notify.info('request options: '+JSON.stringify(options));
    // notify.info(JSON.stringify(definition));
    // notify.info('*****');
};
var invokeDHCRequest = function(definition, projectName) {
    var opts = buildRequestOptions(definition);
    var req = http.request(opts, function(response) {
        var data = '';
        printRequestDetails(opts, definition);
        if (opts.method === 'POST') {
            //notify.info('[Sending POST data] for request::' + definition.name);
            //notify.info('\t\t' + definition.body);
        }
        response.on('data', function(str) {
            data += str;
        });
        response.on('end', function() {
            var input = {};
            input.headers = response.headers;
            input.content = data;
            console.log('\tResponse:');
            console.log('\t\t' + data);
            console.log('\tHeaders:');
            console.log('\t\t' + JSON.stringify(response.headers));
            if (response.statusCode) {
                notify.error('Request ' + definition.name + ' failed');
            } else {
                notify.info('Starting testing');
                invokeDHCTests(definition.name, projectName, definition, input);
            }
        });
        response.on('error', function() {
            notify.error('test: ' + definition.name + ' failed.');
        });
    });
    req.on('error', function() {
        printRequestDetails(opts, definition);
        notify.error('Failed to invoke request: ' + definition.name);
        if (opts.method === 'POST') {
            notify.info('[POST data] for request::' + definition.name);
            notify.info('\t\t' + definition.body);
        }
    });
    if (opts.method === 'POST') {
        req.write(definition.body);
    }
    req.end();
};
var invokeDHCTests = function(requestName, projectName, definition, response) {
    var results = {};
    var result;
    var scriptName;
    var tests = dhcLib.getTestsForRequest(requestName, projectName);
    tests.forEach(function(testName) {
        var scriptPath = '../projects/' + projectName + '/' + testName;
        var script = require(scriptPath);
        executeTestScript(script, definition, response);
    });
};
var executeTestScript = function(script, req, res) {
    var result = {};
    for (var testName in script) {
        if (script.hasOwnProperty(testName)) {
            notify.info('Executing test ::  ' + testName);
            try {
                script[testName](req, res);
                result[testName] = {};
                result[testName].passed = true;
                notify.success('test: ' + testName + ' passed');
            } catch (e) {
                result[testName] = {};
                result[testName].passed = false;
                result[testName].result = e;
                notify.error('test: ' + testName + ' failed.');
            }
        }
    }
    return result;
};<commit_msg>Formatted code<commit_after>var notify = require('notify');
var dhcLib = require('dhc-lib');
var http = require('http');
var chalk = require('chalk');
exports.run = function(opts) {
    var projectName = opts.projectDir;
    var requestList = [];
    var results = {};
    //Get the request list in the DHC project
    requestList = dhcLib.getRequests(projectName);
    //Invoke each request and run the associated tests
    requestList.forEach(function(definition, index) {
        definition = dhcLib.normalize(definition);
        perform(definition, projectName);
    });
};
var perform = function(definition, projectName) {
    invokeDHCRequest(definition, projectName);
    //invokeDHCTests(definition.name,projectName,definition);
};
var decomposeURI = function(uri) {
    var opts = {};
    var comps = uri.split('/');
    var base = comps[0];
    var path = comps.slice(1).join('/');
    var host = base.split(':');
    opts.host = host[0] || 'localhost';
    opts.port = host[1] || '80';
    opts.path = path;
    return opts;
};
var buildRequestOptions = function(definition) {
    var headerDirectives = definition.headers;
    var headers = {};
    var header;
    var opts = decomposeURI(definition.url);
    headerDirectives.forEach(function(headerInfo) {
        if (headerInfo.enabled) {
            headers[headerInfo.name] = headerInfo.value;
        }
    });
    return {
        method: definition.method,
        path: opts.path,
        host: opts.host,
        port: opts.port,
        headers: headers
    };
};
var print = function(msgs) {
    var str = msgs.reduce(function(previous, current) {
        return previous + ' ' + current.decorator(current.msg);
    }, '');
    console.log(str);
};
var printRequestDetails = function(options, definition, response) {
    //notify.info(options.method.toUpperCase() + ' ' + options.host + ':' + options.port + '/' + options.path);
    // notify.info('request options: '+JSON.stringify(options));
    // notify.info(JSON.stringify(definition));
    // notify.info('*****');
    var msgs = [];
        msgs.push({
        msg: '[Request:' + definition.name + ']',
        decorator: chalk.yellow
    });
    if (response) {
        msgs.push({
            msg: '[' + (response.statusCode >= 400 ) ? 'FAILED' : 'SUCCESS' + ']',
            decorator: (response.statusCode >= 400 ) ? chalk.bgRed : chalk.bgGreen
        });
    }
    msgs.push({
        msg: '[' + response.statusCode + ']',
        decorator: chalk.inverse
    });
    msgs.push({
        msg: options.method.toUpperCase(),
        decorator: chalk.inverse
    });
    msgs.push({
        msg: definition.schema + '://' + options.host + ':' + options.port + '/' + options.path,
        decorator: chalk.inverse.underline
    });
    print(msgs);
};
var printFailedRequestDetails = function(options,definition){
    var msgs = [];
        msgs.push({
        msg: '[Request:' + definition.name + ']',
        decorator: chalk.yellow
    });
    if (response) {
        msgs.push({
            msg: '[FAILED] Could not reach host! ',
            decorator: (response.statusCode >= 400 ) ? chalk.bgRed : chalk.bgGreen
        });
    }
    msgs.push({
        msg: options.method.toUpperCase(),
        decorator: chalk.inverse
    });
    msgs.push({
        msg: definition.schema + '://' + options.host + ':' + options.port + '/' + options.path,
        decorator: chalk.inverse.underline
    });
    print(msgs);
};
var invokeDHCRequest = function(definition, projectName) {
    var opts = buildRequestOptions(definition);
    var req = http.request(opts, function(response) {
        var data = '';
        printRequestDetails(opts, definition, response);
        if (opts.method === 'POST') {
            //notify.info('[Sending POST data] for request::' + definition.name);
            //notify.info('\t\t' + definition.body);
        }
        response.on('data', function(str) {
            data += str;
        });
        response.on('end', function() {
            var input = {};
            input.headers = response.headers;
            input.content = data;
            console.log(chalk.grey('Response:'));
            console.log('\t' + chalk.grey(data));
            console.log(chalk.grey('Headers:'));
            console.log('\t' + chalk.grey(JSON.stringify(response.headers)));
            if (response.statusCode) {
            } else {
                notify.info('Starting testing');
                invokeDHCTests(definition.name, projectName, definition, input);
            }
        });
        response.on('error', function() {
            notify.error('test: ' + definition.name + ' failed.');
        });
    });
    req.on('error', function() {
        printFailedRequestDetails(opts, definition);
        //notify.error('Failed to invoke request: ' + definition.name);
        if (opts.method === 'POST') {
            console.log(chalk.grey('[POST data] for request::' + definition.name));
            console.log(chalk.grey('\t\t' + definition.body));
        }
    });
    if (opts.method === 'POST') {
        req.write(definition.body);
    }
    req.end();
};
var invokeDHCTests = function(requestName, projectName, definition, response) {
    var results = {};
    var result;
    var scriptName;
    var tests = dhcLib.getTestsForRequest(requestName, projectName);
    tests.forEach(function(testName) {
        var scriptPath = '../projects/' + projectName + '/' + testName;
        var script = require(scriptPath);
        executeTestScript(script, definition, response);
    });
};
var executeTestScript = function(script, req, res) {
    var result = {};
    for (var testName in script) {
        if (script.hasOwnProperty(testName)) {
            notify.info('Executing test ::  ' + testName);
            try {
                script[testName](req, res);
                result[testName] = {};
                result[testName].passed = true;
                notify.success('test: ' + testName + ' passed');
            } catch (e) {
                result[testName] = {};
                result[testName].passed = false;
                result[testName].result = e;
                notify.error('test: ' + testName + ' failed.');
            }
        }
    }
    return result;
};<|endoftext|>


<commit_before>// You need to bring in jQuery first in order for this to work
//
// Call it like this:
// pkTagahead(<?php echo json_encode(url_for("taggableComplete/complete")) ?>);
//
// Or similar. Now all of your input elements with the input-tag class
// automatically gain the typeahead suggestion feature.
//
// If you're not using Symfony and sfDoctrineActAsTaggablePlugin,
// pass your own URL that returns a
 containing
s with the
// FULL TEXT of what the ENTIRE tag string will be if the user takes
// that suggestion, with the new tag you're suggesting an  link
// to #. Then use CSS to hide (visibility: none) the part of the
//
 that is not in the . Don't introduce any extra whitespace.

function pkTagahead(tagaheadUrl)
{
  $(function() {
    function getKey(event)
    {
      // Portable keycodes sigh
      return event.keyCode ? event.keyCode : event.which;
    }
    function setClick(target)
    {
      $(target).find('a').click(function(event)
      {
        // span contains ul contains li contains a
        var span = this.parentNode.parentNode.parentNode;
        var input = $(span).data("tag-peer");
        // Get text from the li
        var parent = this.parentNode;
        $(input).val($(parent).text());
        $(input).focus();
        return false;
      });
    }
    // Add suggestions span (you'll need to style that)
    $('input.tag-input').after("

");
    // Each tag field remembers its suggestions span...
    $('input.tag-input').each(function()
    {
      $(this).data("tag-peer", $(this).next()[0]);
    });
    // And vice versa
    $('div.tag-suggestions').each(function()
    {
      $(this).data("tag-peer", $(this).prev()[0]);
    });
    // Now we can really throw down
    $('input.tag-input').keyup(function(event)
    {
      var key = getKey(event);
      // Tab key
      if (key == 9)
      {
        var peer = $(this).data("tag-peer");
        var suggestions = $(peer).find("li");
        if (suggestions.length)
        {
          $(this).val($(suggestions[0]).text());
          $(this).focus();
        }
        // In any case don't insert the tab
        return false;
      }
      else
      {
        // Trigger ajax update of suggestions
      }
    });
    $('input.tag-input').keypress(function(event)
    {
      // Firefox 2.0 mac is stubborn and only allows cancel here
      // (we will still get the keyup and do the real work there)
      var key = getKey(event);
      if (key == 9)
      {
        // Don't insert tabs, ever
        return false;
      }
    });
    var lastValues = {};
    setInterval(function()
    {
      // AJAX query for suggestions only when changes have taken place
      $('input.tag-input').each(function()
      {
        var last = $(this).data('tag-last');
        var value = $(this).val();
        var peer = $(this).data('tag-peer');
        if (last !== value)
        {
          $(this).data('tag-last', value);
          $.post(
            tagaheadUrl,
            {
              current: $(this).val()
            },
            function(data, textStatus)
            {
              $(peer).html(data);
              setClick(peer);
            }
          );
        }
      });
    }, 200);
  });
}

function aInlineTaggableWidget(selector, options)
{
	var typeaheadUrl = options['typeahead-url'];
	var tagsLabel = options['tagsLabel'];
	var popularTags = options['popular-tags'];
	var existingTags = options['existing-tags'];
	var allTags = options['all-tags'];

	if (typeof(tagsLabel) == 'undefined')
	{
		tagsLabel = "Existing Tags";
	};

	function makePopularLink(attributes, title, text)
	{
		var new_tag = $('');
		var new_link = $('');
		new_tag.html(""+text+"");
		new_tag.attr({ title: title }).addClass('a-tag a-popular');
		new_tag.prepend(new_link);
		new_link.text(title);
		new_link.attr(attributes);
		return new_tag;
	}

	function makeRemoveLink(attributes, title, text)
	{
		title = $.trim(title);

		var new_tag = $('');
		var new_link = $('');
		var tagTitle = title;

		if (typeof(allTags) != 'undefined')
		{
			if (typeof(allTags[title]) != 'undefined')
			{
				title = title + ' - ' + allTags[title];
			}
			else
			{
				title = title + ' - 0';
			}
		}

		new_tag.html(""+title+"");
		new_tag.attr({ title: tagTitle }).addClass('a-tag');
		new_link.text('Remove Tag');
		new_link.attr(attributes);
		new_link.attr({ title: 'Remove' }).addClass('a-btn icon a-close-small no-label no-bg');
		new_link.prepend('');
		new_tag.append(new_link);
		return new_tag;
	}

	function trimExcessCommas(string)
	{
		string = string.replace(/(^,)|(, ?$)/g, '');
		string = string.replace(/(,,)|(, ,)/, ',');
		string = $.trim(string);

		return string;
	}

	$(selector).each(function()
	{
		// We don't want to display popular tags that we're already using
		var unusedPopulars = {};
		for (x in popularTags)
		{
			if (typeof(existingTags[x]) == 'undefined')
			{
				unusedPopulars[x] = popularTags[x];
			}
		}

		var popularsAttributes = {};
		var existingTagsAttributes = {};
		var existingDiv = $('
');
		var popularsDiv = $('
');

		// Establish the quick enhancement
		var tagInput = $(this);
		var typeAheadContainer = $('
');
		var typeAheadBox = $('<input />');
		typeAheadBox.attr('type', 'text');

		var addButton = $('');
		addButton.html('Add');
		addButton.attr({'href' : '#', 'class' : 'a-btn icon a-add add-tags-link', 'title' : 'Add these tags'});

		typeAheadContainer.addClass('a-inline-taggable-widget').append(typeAheadBox).append(addButton);

		tagInput.hide();
		tagInput.parent().append(typeAheadContainer);

		// Add a list of popular tags to be added
		function addTagsToForm(link)
		{
			var tag = link.attr('title');
			var value = tagInput.val() + ', ' + tag;

			value = trimExcessCommas(value);
			tagInput.val(value);

			link.remove();

			var new_link = makeRemoveLink(existingTagsAttributes, tag, tag + ' x');
			new_link.children('a').bind('click', function() { removeTagsFromForm($(this).parent()); return false; });
			existingDiv.append(new_link);
		}

		// Add a list of tags that may be removed
		function removeTagsFromForm(link)
		{
			var tag = link.attr('title');
			var value = tagInput.val();

			value = value.replace(tag, '');
			value = trimExcessCommas(value);
			tagInput.val(value);

			link.remove();

			// As we have just removed it from the list, we want the real deal populars.
			if (typeof(popularTags[tag]) != 'undefined')
			{
				var linkLabel = popularTags[tag];
				var new_link = makePopularLink(existingTagsAttributes, tag, linkLabel);
				new_link.children('a').bind('click', function() { addTagsToForm($(this).parent()); return false; });
				popularsDiv.append(new_link);
			}
		}

		// a maker function for tag containers
		function makeTagContainer(containerLabel, tagArray, linkAttributes, linkLabelType)
		{
			// Add a list of tags that may be removed
			var tagContainer = $('
');
			tagContainer.addClass('a-inline-taggable-widget-tag-container');
			var header = $('
');
			header.text(containerLabel);
			tagContainer.append(header);

			var attributes = {};
			for (x in tagArray)
			{
				var linkLabel = '';
				if (linkLabelType == 'add')
				{
					tagContainer.addClass('a-popular-tags');

					linkLabel = tagArray[x];
					var new_link = makePopularLink(linkAttributes, x, linkLabel);
					new_link.children('a').bind('click', function() { addTagsToForm($(this).parent());  return false; });
				}
				else if (linkLabelType == 'remove')
				{
					tagContainer.addClass('a-existing-tags');

					linkLabel = 'x ' + x;
					var new_link = makeRemoveLink(linkAttributes, x, linkLabel);
					new_link.children('a').bind('click', function() { removeTagsFromForm($(this).parent());  return false; });
				}
				tagContainer.append(new_link);
			}
			return tagContainer;
		}

		// Add the new tags to the existing form input.
		// If the user doesn't click Save changes or add... tough?
		function commitTagsToForm()
		{
			var value = tagInput.val() + ',' + typeAheadBox.val();
			value = trimExcessCommas(value);
			tagInput.val(value);
			typeAheadBox.val('');

			existingTags = {};
			var lp = value.split(',');
			for (x in lp)
			{
				existingTags[lp[x]] = lp[x];
			}

			existingDiv.html(makeTagContainer(tagsLabel, existingTags, existingTagsAttributes, 'remove').html());
			existingDiv.find('a').each(function() {
				$(this).bind('click', function() {
					removeTagsFromForm($(this).parent());
					return false;
				});
			});

			return false;
		}
		addButton.bind('click', function() { commitTagsToForm(); return false; });

		existingDiv = makeTagContainer(tagsLabel, existingTags, existingTagsAttributes, 'remove');
		existingDiv.children('a').bind('click', function() { removeTagsFromForm($(this));  return false; });
		tagInput.parent().prepend(existingDiv);

		popularsDiv = makeTagContainer('Popular Tags', unusedPopulars, popularsAttributes, 'add');
		popularsDiv.children('a').bind('click', function() { addTagsToForm($(this));  return false; });
		tagInput.parent().append(popularsDiv);

		// TypeAhead
		typeAheadBox.autocomplete(typeaheadUrl, {	multiple: true });

		// Catch that enter key, baby!
		$(document).keyup(function(e)
			{
				if (e.keyCode == 13)
				{
					e.preventDefault();
				}
			});

		$(document).keypress(function(e)
			{
				if (e.keyCode == 13)
				{
					e.preventDefault();
				}
			});

		typeAheadBox.keyup(function(e)
			{
		 		if (e.keyCode == 13)
		 		{
		 			commitTagsToForm();
		 		}
		 	});
	});

}
<commit_msg>Apostrophe Ticket #657<commit_after>// You need to bring in jQuery first in order for this to work
//
// Call it like this:
// pkTagahead(<?php echo json_encode(url_for("taggableComplete/complete")) ?>);
//
// Or similar. Now all of your input elements with the input-tag class
// automatically gain the typeahead suggestion feature.
//
// If you're not using Symfony and sfDoctrineActAsTaggablePlugin,
// pass your own URL that returns a
 containing
s with the
// FULL TEXT of what the ENTIRE tag string will be if the user takes
// that suggestion, with the new tag you're suggesting an  link
// to #. Then use CSS to hide (visibility: none) the part of the
//
 that is not in the . Don't introduce any extra whitespace.

function pkTagahead(tagaheadUrl)
{
  $(function() {
    function getKey(event)
    {
      // Portable keycodes sigh
      return event.keyCode ? event.keyCode : event.which;
    }
    function setClick(target)
    {
      $(target).find('a').click(function(event)
      {
        // span contains ul contains li contains a
        var span = this.parentNode.parentNode.parentNode;
        var input = $(span).data("tag-peer");
        // Get text from the li
        var parent = this.parentNode;
        $(input).val($(parent).text());
        $(input).focus();
        return false;
      });
    }
    // Add suggestions span (you'll need to style that)
    $('input.tag-input').after("

");
    // Each tag field remembers its suggestions span...
    $('input.tag-input').each(function()
    {
      $(this).data("tag-peer", $(this).next()[0]);
    });
    // And vice versa
    $('div.tag-suggestions').each(function()
    {
      $(this).data("tag-peer", $(this).prev()[0]);
    });
    // Now we can really throw down
    $('input.tag-input').keyup(function(event)
    {
      var key = getKey(event);
      // Tab key
      if (key == 9)
      {
        var peer = $(this).data("tag-peer");
        var suggestions = $(peer).find("li");
        if (suggestions.length)
        {
          $(this).val($(suggestions[0]).text());
          $(this).focus();
        }
        // In any case don't insert the tab
        return false;
      }
      else
      {
        // Trigger ajax update of suggestions
      }
    });
    $('input.tag-input').keypress(function(event)
    {
      // Firefox 2.0 mac is stubborn and only allows cancel here
      // (we will still get the keyup and do the real work there)
      var key = getKey(event);
      if (key == 9)
      {
        // Don't insert tabs, ever
        return false;
      }
    });
    var lastValues = {};
    setInterval(function()
    {
      // AJAX query for suggestions only when changes have taken place
      $('input.tag-input').each(function()
      {
        var last = $(this).data('tag-last');
        var value = $(this).val();
        var peer = $(this).data('tag-peer');
        if (last !== value)
        {
          $(this).data('tag-last', value);
          $.post(
            tagaheadUrl,
            {
              current: $(this).val()
            },
            function(data, textStatus)
            {
              $(peer).html(data);
              setClick(peer);
            }
          );
        }
      });
    }, 200);
  });
}

function aInlineTaggableWidget(selector, options)
{
	var typeaheadUrl = options['typeahead-url'];
	var tagsLabel = options['tagsLabel'];
	var popularTags = options['popular-tags'];
	var existingTags = options['existing-tags'];
	var allTags = options['all-tags'];
	var commitSelector = options['commit-selector'];

	if (typeof(tagsLabel) == 'undefined')
	{
		tagsLabel = "Existing Tags";
	};

	function makePopularLink(attributes, title, text)
	{
		var new_tag = $('');
		var new_link = $('');
		new_tag.html(""+text+"");
		new_tag.attr({ title: title }).addClass('a-tag a-popular');
		new_tag.prepend(new_link);
		new_link.text(title);
		new_link.attr(attributes);
		return new_tag;
	}

	function makeRemoveLink(attributes, title, text)
	{
		title = $.trim(title);

		var new_tag = $('');
		var new_link = $('');
		var tagTitle = title;

		if (typeof(allTags) != 'undefined')
		{
			if (typeof(allTags[title]) != 'undefined')
			{
				title = title + ' - ' + allTags[title];
			}
			else
			{
				title = title + ' - 0';
			}
		}

		new_tag.html(""+title+"");
		new_tag.attr({ title: tagTitle }).addClass('a-tag');
		new_link.text('Remove Tag');
		new_link.attr(attributes);
		new_link.attr({ title: 'Remove' }).addClass('a-btn icon a-close-small no-label no-bg');
		new_link.prepend('');
		new_tag.append(new_link);
		return new_tag;
	}

	function trimExcessCommas(string)
	{
		string = string.replace(/(^,)|(, ?$)/g, '');
		string = string.replace(/(,,)|(, ,)/, ',');
		string = $.trim(string);

		return string;
	}

	$(selector).each(function()
	{
		// We don't want to display popular tags that we're already using
		var unusedPopulars = {};
		for (x in popularTags)
		{
			if (typeof(existingTags[x]) == 'undefined')
			{
				unusedPopulars[x] = popularTags[x];
			}
		}

		var popularsAttributes = {};
		var existingTagsAttributes = {};
		var existingDiv = $('
');
		var popularsDiv = $('
');

		// Establish the quick enhancement
		var tagInput = $(this);
		var typeAheadContainer = $('
');
		var typeAheadBox = $('<input />');
		var typeAheadBoxId = 'inline-tag-ahead-box-' + Math.floor(Math.random() * 2000);
		typeAheadBox.attr('type', 'text');
		typeAheadBox.attr('id', typeAheadBoxId);
		typeAheadBox.autocomplete(typeaheadUrl, {	multiple: true });

		var addButton = $('');
		addButton.html('Add');
		addButton.attr({'href' : '#', 'class' : 'a-btn icon a-add add-tags-link', 'title' : 'Add these tags'});

		typeAheadContainer.addClass('a-inline-taggable-widget').append(typeAheadBox).append(addButton);

		tagInput.hide();
		tagInput.parent().append(typeAheadContainer);

		// Add a list of popular tags to be added
		function addTagsToForm(link)
		{
			var tag = link.attr('title');
			var value = tagInput.val() + ', ' + tag;

			value = trimExcessCommas(value);
			tagInput.val(value);

			link.remove();

			var new_link = makeRemoveLink(existingTagsAttributes, tag, tag + ' x');
			new_link.children('a').bind('click', function() { removeTagsFromForm($(this).parent()); return false; });
			existingDiv.append(new_link);
		}

		// Add a list of tags that may be removed
		function removeTagsFromForm(link)
		{
			var tag = link.attr('title');
			var value = tagInput.val();

			value = value.replace(tag, '');
			value = trimExcessCommas(value);
			tagInput.val(value);

			link.remove();

			// As we have just removed it from the list, we want the real deal populars.
			if (typeof(popularTags[tag]) != 'undefined')
			{
				var linkLabel = popularTags[tag];
				var new_link = makePopularLink(existingTagsAttributes, tag, linkLabel);
				new_link.children('a').bind('click', function() { addTagsToForm($(this).parent()); return false; });
				popularsDiv.append(new_link);
			}
		}

		// a maker function for tag containers
		function makeTagContainer(containerLabel, tagArray, linkAttributes, linkLabelType)
		{
			// Add a list of tags that may be removed
			var tagContainer = $('
');
			tagContainer.addClass('a-inline-taggable-widget-tag-container');
			var header = $('
');
			header.text(containerLabel);
			tagContainer.append(header);

			var attributes = {};
			for (x in tagArray)
			{
				var linkLabel = '';
				if (linkLabelType == 'add')
				{
					tagContainer.addClass('a-popular-tags');

					linkLabel = tagArray[x];
					var new_link = makePopularLink(linkAttributes, x, linkLabel);
					new_link.children('a').bind('click', function() { addTagsToForm($(this).parent());  return false; });
				}
				else if (linkLabelType == 'remove')
				{
					tagContainer.addClass('a-existing-tags');

					linkLabel = 'x ' + x;
					var new_link = makeRemoveLink(linkAttributes, x, linkLabel);
					new_link.children('a').bind('click', function() { removeTagsFromForm($(this).parent());  return false; });
				}
				tagContainer.append(new_link);
			}
			return tagContainer;
		}

		// Add the new tags to the existing form input.
		// If the user doesn't click Save changes or add... tough?
		function commitTagsToForm()
		{
			var value = tagInput.val() + ',' + typeAheadBox.val();
			value = trimExcessCommas(value);
			tagInput.val(value);
			typeAheadBox.val('');

			existingTags = {};
			var lp = value.split(',');
			for (x in lp)
			{
				existingTags[lp[x]] = lp[x];
			}

			existingDiv.html(makeTagContainer(tagsLabel, existingTags, existingTagsAttributes, 'remove').html());
			existingDiv.find('a').each(function() {
				$(this).bind('click', function() {
					removeTagsFromForm($(this).parent());
					return false;
				});
			});

			return false;
		}
		addButton.bind('click', function() { commitTagsToForm(); return false; });
		if (commitSelector != 'undefined')
		{
			$(commitSelector).bind('click', function() {
				commitTagsToForm();
				return true;
			});
		}

		existingDiv = makeTagContainer(tagsLabel, existingTags, existingTagsAttributes, 'remove');
		existingDiv.children('a').bind('click', function() { removeTagsFromForm($(this));  return false; });
		tagInput.parent().prepend(existingDiv);

		popularsDiv = makeTagContainer('Popular Tags', unusedPopulars, popularsAttributes, 'add');
		popularsDiv.children('a').bind('click', function() { addTagsToForm($(this));  return false; });
		tagInput.parent().append(popularsDiv);

		// Catch that enter key, baby!
		$(document).keyup(function(e)
			{
				if (e.keyCode == 13)
				{
					e.preventDefault();
				}
			});

		$(document).keypress(function(e)
			{
				if (e.keyCode == 13)
				{
					e.preventDefault();
				}
			});

		typeAheadBox.keyup(function(e)
			{
		 		if (e.keyCode == 13)
		 		{
		 			commitTagsToForm();
		 		}
		 	});
	});

}
<|endoftext|>


<commit_before>
function convertToDygraph(data) {
	// this sort makes sure that the graph colors are always the same.
	data.sort(function(a,b) {
		if(a['t'] < b['t'])
			return 1;
		else if (a['t'] > b['t'])
			return -1;
		else
			return 0;
	});

	var dygraph = {};
	dygraph['datapoints'] = [];
	if (data.length == 0) {
		return [];
	}

	var lengths = $.map(data, function(val, i) {
		return val['d'].length
	});

	var minDatapoints = Math.min.apply(Math, lengths);

	for (var i = 0; i < data[0]['d'].length; i++) {
		var datapoint = [new Date(data[0]['d'][i]['d'] * 1000)];
		for (var j = 0; j < data.length; j++) {
			if (data[j]['d'][i] != undefined) {
				datapoint.push(data[j]['d'][i]['v']);
			}
			else if (i > 0) {
				datapoint.push(dygraph['datapoints'][i - 1][j]);
			}
			else {
				datapoint.push(0);
			}
		}
		dygraph['datapoints'].push(datapoint);
	}

	dygraph['labels'] = ['Date'];
	for (var i = 0; i < data.length; i++) {
		dygraph['labels'].push(data[i]['t']);
	}

	return dygraph;
}

function plotLineGraph(element, data, graphOptions) {
	if (!graphOptions) { graphOptions = {}; }
	return plotGraph(element, data, graphOptions);
}

function plotStackedGraph(element, data, graphOptions) {
	if (!graphOptions) { graphOptions = {}; }
	graphOptions['stackedGraph'] = true;
	return plotGraph(element, data, graphOptions);
}

function plotGraph(element, data, graphOptions) {
	var dygraph = convertToDygraph(data);
	if (dygraph.length == 0) {
		return false;
	}

	graphOptions['labels'] = dygraph['labels'];
	graphOptions['labelsSeparateLines'] = true;
	graphOptions['labelsDiv'] = document.getElementById('legend');

	$('#' + element.split(' ')[0]).html('');
	return new Dygraph(
		document.getElementById(element.split(' ')[0]),
		dygraph['datapoints'],
		graphOptions
	);
}

$(document).mousemove(function(event) {
	var legend = $('#legend');
	var xOffset = 20;
	if (event.pageX + legend.width() + 40 > $(window).width()){
		xOffset = -40 - legend.width();
	}
	var padding = legend.is(':empty') ? 0 : 10;
	legend.css({
		left: event.pageX + xOffset,
		top: event.pageY - legend.height() / 2,
		padding: padding
	});
});
<commit_msg>fix data alignment issue when graphite data had different numbers of data points<commit_after>
function convertToDygraph(data) {
	// this sort makes sure that the graph colors are always the same.
	data.sort(function(a,b) {
		if(a['t'] < b['t'])
			return 1;
		else if (a['t'] > b['t'])
			return -1;
		else
			return 0;
	});

	var dygraph = {};
	dygraph['datapoints'] = [];
	dygraph['labels'] = ['Date'];
	if (data.length == 0) {
		return [];
	}

	var dataByTime = {};
	var dataLength = data.length;
	for (var dataIndex = 0; dataIndex < dataLength; dataIndex++) {
		var target = data[dataIndex];
		var targetLength = target['d'].length;

		dygraph['labels'].push(target['t']);

		for (var targetIndex = 0; targetIndex < targetLength; targetIndex++) {
			var point = target['d'][targetIndex];
			var date = point['d'];
			var value = point['v'];

			if (!dataByTime[date]) {
				dataByTime[date] = {};
			}

			if (value != null && value != undefined) {
				dataByTime[date][dataIndex] = value;
			}
		}
	}

	var times = Object.keys(dataByTime);
	var timeLength = times.length;
	times.sort();

	for (var timeIndex = 0; timeIndex < timeLength; timeIndex++) {
		var time = times[timeIndex];
		var timeData = dataByTime[time];

		var datapoint = [];
		datapoint[0] = new Date(time * 1000);
		for (var dataIndex = 0; dataIndex < dataLength; dataIndex++) {
			// [dataIndex+1] because [0] is the date
			datapoint[dataIndex + 1] = timeData[dataIndex];
		}

		dygraph['datapoints'].push(datapoint);
	}

	return dygraph;
}

function plotLineGraph(element, data, graphOptions) {
	if (!graphOptions) { graphOptions = {}; }
	return plotGraph(element, data, graphOptions);
}

function plotStackedGraph(element, data, graphOptions) {
	if (!graphOptions) { graphOptions = {}; }
	graphOptions['stackedGraph'] = true;
	return plotGraph(element, data, graphOptions);
}

function plotGraph(element, data, graphOptions) {
	var dygraph = convertToDygraph(data);
	if (dygraph.length == 0) {
		return false;
	}

	graphOptions['labels'] = dygraph['labels'];
	graphOptions['labelsSeparateLines'] = true;
	graphOptions['labelsDiv'] = document.getElementById('legend');

	$('#' + element.split(' ')[0]).html('');
	return new Dygraph(
		document.getElementById(element.split(' ')[0]),
		dygraph['datapoints'],
		graphOptions
	);
}

$(document).mousemove(function(event) {
	var legend = $('#legend');
	var xOffset = 20;
	if (event.pageX + legend.width() + 40 > $(window).width()){
		xOffset = -40 - legend.width();
	}
	var padding = legend.is(':empty') ? 0 : 10;
	legend.css({
		left: event.pageX + xOffset,
		top: event.pageY - legend.height() / 2,
		padding: padding
	});
});
<|endoftext|>


<commit_before>import Delta from 'rich-text/lib/delta';
import Parchment from 'parchment';
import Quill from '../core/quill';
import logger from '../core/logger';
import Module from '../core/module';
import { BlockEmbed } from '../blots/block';

import { AlignStyle } from '../formats/align';
import { BackgroundStyle } from '../formats/background';
import { ColorStyle } from '../formats/color';
import { DirectionStyle } from '../formats/direction';
import { FontStyle } from '../formats/font';
import { SizeStyle } from '../formats/size';

let debug = logger('quill:clipboard');

const STYLE_ATTRIBUTORS = [
  AlignStyle,
  BackgroundStyle,
  ColorStyle,
  DirectionStyle,
  FontStyle,
  SizeStyle
].reduce(function(memo, attr) {
  memo[attr.keyName] = attr;
  return memo;
}, {});

class Clipboard extends Module {
  constructor(quill, options) {
    if (options.matchers !== Clipboard.DEFAULTS.matchers) {
      options.matchers = Clipboard.DEFAULTS.matchers.concat(options.matchers);
    }
    super(quill, options);
    this.quill.root.addEventListener('paste', this.onPaste.bind(this));
    this.container = this.quill.addContainer('ql-clipboard');
    this.container.setAttribute('contenteditable', true);
    this.container.setAttribute('tabindex', -1);
    this.matchers = [];
    this.options.matchers.forEach((pair) => {
      this.addMatcher(...pair);
    });
  }

  addMatcher(selector, matcher) {
    this.matchers.push([selector, matcher]);
  }

  clean() {
    let treeWalker = document.createTreeWalker(
      this.container,
      NodeFilter.SHOW_COMMENT,
      { acceptNode: function(node) { return NodeFilter.FILTER_ACCEPT; } },
      false
    );
    let comments = [];
    while(treeWalker.nextNode()) {
      comments.push(treeWalker.currentNode);
    }
    comments.forEach(function(node) {
      if (node != null && node.parentNode != null) {
        node.parentNode.removeChild(node);
      }
    });
    this.container.normalize();
  }

  convert(html) {
    const DOM_KEY = '__ql-matcher';
    if (typeof html === 'string') {
      this.container.innerHTML = html;
    }
    this.clean();
    this.matchers.forEach((pair) => {
      let [selector, matcher] = pair;
      if (typeof selector === 'string') {
        [].forEach.call(this.container.querySelectorAll(selector), (node) => {
          // TODO use weakmap
          node[DOM_KEY] = node[DOM_KEY] || [];
          node[DOM_KEY].push(matcher);
        });
      }
    });
    let traverse = (node) => {  // Post-order
      return [].reduce.call(node.childNodes || [], (delta, childNode) => {
        if (childNode.nodeType !== Node.ELEMENT_NODE && childNode.nodeType !== Node.TEXT_NODE) {
          return delta;
        }
        let childrenDelta = traverse(childNode);
        childrenDelta = this.matchers.reduce(function(childrenDelta, pair) {
          let [type, matcher] = pair;
          if (type === true || childNode.nodeType === type) {
            childrenDelta = matcher(childNode, childrenDelta);
          }
          return childrenDelta;
        }, childrenDelta);
        childrenDelta = (childNode[DOM_KEY] || []).reduce(function(childrenDelta, matcher) {
          return matcher(childNode, childrenDelta);
        }, childrenDelta);
        return delta.concat(childrenDelta);
      }, new Delta());
    };
    let delta = traverse(this.container);
    // Remove trailing newline
    if (deltaEndsWith(delta, '\n') && delta.ops[delta.ops.length - 1].attributes == null) {
      delta = delta.compose(new Delta().retain(delta.length() - 1).delete(1));
    }
    debug.log('convert', this.container.innerHTML, delta);
    this.container.innerHTML = '';
    return delta;
  }

  onPaste(e) {
    if (e.defaultPrevented) return;
    let range = this.quill.getSelection();
    let clipboard = e.clipboardData || window.clipboardData;
    let delta = new Delta().retain(range.index).delete(range.length);
    this.container.focus();
    setTimeout(() => {
      let html = this.container.innerHTML;
      delta = delta.concat(this.convert());
      this.quill.updateContents(delta, Quill.sources.USER);
      // range.length contributes to delta.length()
      this.quill.setSelection(delta.length() - range.length, Quill.sources.SILENT);
      this.quill.selection.scrollIntoView();
    }, 1);
  }
}
Clipboard.DEFAULTS = {
  matchers: [
    [Node.TEXT_NODE, matchText],
    ['br', matchBreak],
    [Node.ELEMENT_NODE, matchNewline],
    [Node.ELEMENT_NODE, matchBlot],
    [Node.ELEMENT_NODE, matchSpacing],
    [Node.ELEMENT_NODE, matchAttributor],
    [Node.ELEMENT_NODE, matchStyles],
    ['b', matchAlias.bind(matchAlias, 'bold')],
    ['i', matchAlias.bind(matchAlias, 'italic')]
  ]
};

function computeStyle(node) {
  if (node.nodeType !== Node.ELEMENT_NODE) return {};
  const DOM_KEY = '__ql-computed-style';
  return node[DOM_KEY] || (node[DOM_KEY] = window.getComputedStyle(node));
}

function deltaEndsWith(delta, text) {
  let endText = "";
  for (let i = delta.ops.length - 1; i >= 0 && endText.length < text.length; --i) {
    let op  = delta.ops[i];
    if (typeof op.insert !== 'string') break;
    endText = op.insert + endText;
  }
  return endText.slice(-1*text.length) === text;
}

function isLine(node) {
  if (node.childNodes.length === 0) return false;   // Exclude embed blocks
  let style = computeStyle(node);
  return ['block', 'list-item'].indexOf(style.display) > -1;
}

function matchAlias(format, node, delta) {
  return delta.compose(new Delta().retain(delta.length(), { [format]: true }));
}

function matchAttributor(node, delta) {
  let attributes = Parchment.Attributor.Attribute.keys(node);
  let classes = Parchment.Attributor.Class.keys(node);
  let styles = Parchment.Attributor.Style.keys(node);
  let formats = {};
  attributes.concat(classes).concat(styles).forEach((name) => {
    let attr = Parchment.query(name, Parchment.Scope.ATTRIBUTE) || STYLE_ATTRIBUTORS[name];
    if (attr != null) {
      formats[attr.attrName] = attr.value(node);
    }
  });
  if (Object.keys(formats).length > 0) {
    delta = delta.compose(new Delta().retain(delta.length(), formats));
  }
  return delta;
}

function matchBlot(node, delta) {
  let match = Parchment.query(node);
  if (match == null) return delta;
  if (match.prototype instanceof Parchment.Embed) {
    let embed = {};
    let value = match.value(node);
    if (value != null) {
      embed[match.blotName] = value;
      delta.insert(embed, match.formats(node));
    }
  } else if (typeof match.formats === 'function') {
    let formats = { [match.blotName]: match.formats(node) };
    delta = delta.compose(new Delta().retain(delta.length(), formats));
  }
  return delta;
}

function matchBreak(node, delta) {
  if (!deltaEndsWith(delta, '\n')) {
    delta.insert('\n');
  }
  return delta;
}

function matchNewline(node, delta) {
  if (!isLine(node)) return delta;
  if (computeStyle(node).whiteSpace.startsWith('pre') || !deltaEndsWith(delta, '\n')) {
    delta.insert('\n');
  }
  return delta;
}

function matchSpacing(node, delta) {
  if (isLine(node) && node.nextElementSibling != null && !deltaEndsWith(delta, '\n\n')) {
    let nodeHeight = node.offsetHeight + parseFloat(computeStyle(node).marginTop) + parseFloat(computeStyle(node).marginBottom);
    if (node.nextElementSibling.offsetTop > node.offsetTop + nodeHeight*1.5) {
      delta.insert('\n');
    }
  }
  return delta;
}

function matchStyles(node, delta) {
  let formats = {};
  let style = node.style || {};
  if (style.fontWeight && computeStyle(node).fontWeight === 'bold') {
    formats.bold = true;
  }
  if (Object.keys(formats).length > 0) {
    delta = delta.compose(new Delta().retain(delta.length(), formats));
  }
  if (parseFloat(style.textIndent || 0) > 0) {  // Could be 0.5in
    delta = new Delta().insert('\t').concat(delta);
  }
  return delta;
}

function matchText(node, delta) {
  let text = node.data;
  // Word represents empty line with <o:p> </o:p>
  if (node.parentNode.tagName === 'O:P') {
    return delta.insert(text.trim());
  }
  if (!computeStyle(node.parentNode).whiteSpace.startsWith('pre')) {
    function replacer(collapse, match) {
      match = match.replace(/[^\u00a0]/g, '');    // \u00a0 is nbsp;
      return match.length < 1 && collapse ? ' ' : match;
    }
    text = text.replace(/\r\n/g, ' ').replace(/\n/g, ' ');
    text = text.replace(/\s\s+/g, replacer.bind(replacer, true));  // collapse whitespace
    if ((node.previousSibling == null && isLine(node.parentNode)) ||
        (node.previousSibling != null && isLine(node.previousSibling))) {
      text = text.replace(/^\s+/, replacer.bind(replacer, false));
    }
    if ((node.nextSibling == null && isLine(node.parentNode)) ||
        (node.nextSibling != null && isLine(node.nextSibling))) {
      text = text.replace(/\s+$/, replacer.bind(replacer, false));
    }
  }
  return delta.insert(text);
}

export { Clipboard as default, matchAttributor, matchBlot, matchNewline, matchSpacing, matchText };
<commit_msg>fix #696<commit_after>import Delta from 'rich-text/lib/delta';
import Parchment from 'parchment';
import Quill from '../core/quill';
import logger from '../core/logger';
import Module from '../core/module';
import { BlockEmbed } from '../blots/block';

import { AlignStyle } from '../formats/align';
import { BackgroundStyle } from '../formats/background';
import { ColorStyle } from '../formats/color';
import { DirectionStyle } from '../formats/direction';
import { FontStyle } from '../formats/font';
import { SizeStyle } from '../formats/size';

let debug = logger('quill:clipboard');

const STYLE_ATTRIBUTORS = [
  AlignStyle,
  BackgroundStyle,
  ColorStyle,
  DirectionStyle,
  FontStyle,
  SizeStyle
].reduce(function(memo, attr) {
  memo[attr.keyName] = attr;
  return memo;
}, {});

class Clipboard extends Module {
  constructor(quill, options) {
    if (options.matchers !== Clipboard.DEFAULTS.matchers) {
      options.matchers = Clipboard.DEFAULTS.matchers.concat(options.matchers);
    }
    super(quill, options);
    this.quill.root.addEventListener('paste', this.onPaste.bind(this));
    this.container = this.quill.addContainer('ql-clipboard');
    this.container.setAttribute('contenteditable', true);
    this.container.setAttribute('tabindex', -1);
    this.matchers = [];
    this.options.matchers.forEach((pair) => {
      this.addMatcher(...pair);
    });
  }

  addMatcher(selector, matcher) {
    this.matchers.push([selector, matcher]);
  }

  clean() {
    let treeWalker = document.createTreeWalker(
      this.container,
      NodeFilter.SHOW_COMMENT,
      { acceptNode: function(node) { return NodeFilter.FILTER_ACCEPT; } },
      false
    );
    let comments = [];
    while(treeWalker.nextNode()) {
      comments.push(treeWalker.currentNode);
    }
    comments.forEach(function(node) {
      if (node != null && node.parentNode != null) {
        node.parentNode.removeChild(node);
      }
    });
    this.container.normalize();
  }

  convert(html) {
    const DOM_KEY = '__ql-matcher';
    if (typeof html === 'string') {
      this.container.innerHTML = html;
    }
    this.clean();
    this.matchers.forEach((pair) => {
      let [selector, matcher] = pair;
      if (typeof selector === 'string') {
        [].forEach.call(this.container.querySelectorAll(selector), (node) => {
          // TODO use weakmap
          node[DOM_KEY] = node[DOM_KEY] || [];
          node[DOM_KEY].push(matcher);
        });
      }
    });
    let traverse = (node) => {  // Post-order
      return [].reduce.call(node.childNodes || [], (delta, childNode) => {
        if (childNode.nodeType !== Node.ELEMENT_NODE && childNode.nodeType !== Node.TEXT_NODE) {
          return delta;
        }
        let childrenDelta = traverse(childNode);
        childrenDelta = this.matchers.reduce(function(childrenDelta, pair) {
          let [type, matcher] = pair;
          if (type === true || childNode.nodeType === type) {
            childrenDelta = matcher(childNode, childrenDelta);
          }
          return childrenDelta;
        }, childrenDelta);
        childrenDelta = (childNode[DOM_KEY] || []).reduce(function(childrenDelta, matcher) {
          return matcher(childNode, childrenDelta);
        }, childrenDelta);
        return delta.concat(childrenDelta);
      }, new Delta());
    };
    let delta = traverse(this.container);
    // Remove trailing newline
    if (deltaEndsWith(delta, '\n') && delta.ops[delta.ops.length - 1].attributes == null) {
      delta = delta.compose(new Delta().retain(delta.length() - 1).delete(1));
    }
    debug.log('convert', this.container.innerHTML, delta);
    this.container.innerHTML = '';
    return delta;
  }

  onPaste(e) {
    if (e.defaultPrevented) return;
    let range = this.quill.getSelection();
    let clipboard = e.clipboardData || window.clipboardData;
    let delta = new Delta().retain(range.index).delete(range.length);
    this.container.focus();
    setTimeout(() => {
      let html = this.container.innerHTML;
      delta = delta.concat(this.convert());
      this.quill.updateContents(delta, Quill.sources.USER);
      // range.length contributes to delta.length()
      this.quill.setSelection(delta.length() - range.length, Quill.sources.SILENT);
      this.quill.selection.scrollIntoView();
    }, 1);
  }
}
Clipboard.DEFAULTS = {
  matchers: [
    [Node.TEXT_NODE, matchText],
    ['br', matchBreak],
    [Node.ELEMENT_NODE, matchNewline],
    [Node.ELEMENT_NODE, matchBlot],
    [Node.ELEMENT_NODE, matchSpacing],
    [Node.ELEMENT_NODE, matchAttributor],
    [Node.ELEMENT_NODE, matchStyles],
    ['b', matchAlias.bind(matchAlias, 'bold')],
    ['i', matchAlias.bind(matchAlias, 'italic')]
  ]
};

function computeStyle(node) {
  if (node.nodeType !== Node.ELEMENT_NODE) return {};
  const DOM_KEY = '__ql-computed-style';
  return node[DOM_KEY] || (node[DOM_KEY] = window.getComputedStyle(node));
}

function deltaEndsWith(delta, text) {
  let endText = "";
  for (let i = delta.ops.length - 1; i >= 0 && endText.length < text.length; --i) {
    let op  = delta.ops[i];
    if (typeof op.insert !== 'string') break;
    endText = op.insert + endText;
  }
  return endText.slice(-1*text.length) === text;
}

function isLine(node) {
  if (node.childNodes.length === 0) return false;   // Exclude embed blocks
  let style = computeStyle(node);
  return ['block', 'list-item'].indexOf(style.display) > -1;
}

function matchAlias(format, node, delta) {
  return delta.compose(new Delta().retain(delta.length(), { [format]: true }));
}

function matchAttributor(node, delta) {
  let attributes = Parchment.Attributor.Attribute.keys(node);
  let classes = Parchment.Attributor.Class.keys(node);
  let styles = Parchment.Attributor.Style.keys(node);
  let formats = {};
  attributes.concat(classes).concat(styles).forEach((name) => {
    let attr = Parchment.query(name, Parchment.Scope.ATTRIBUTE) || STYLE_ATTRIBUTORS[name];
    if (attr != null) {
      formats[attr.attrName] = attr.value(node);
    }
  });
  if (Object.keys(formats).length > 0) {
    delta = delta.compose(new Delta().retain(delta.length(), formats));
  }
  return delta;
}

function matchBlot(node, delta) {
  let match = Parchment.query(node);
  if (match == null) return delta;
  if (match.prototype instanceof Parchment.Embed) {
    let embed = {};
    let value = match.value(node);
    if (value != null) {
      embed[match.blotName] = value;
      delta.insert(embed, match.formats(node));
    }
  } else if (typeof match.formats === 'function') {
    let formats = { [match.blotName]: match.formats(node) };
    delta = delta.compose(new Delta().retain(delta.length(), formats));
  }
  return delta;
}

function matchBreak(node, delta) {
  if (!deltaEndsWith(delta, '\n')) {
    delta.insert('\n');
  }
  return delta;
}

function matchNewline(node, delta) {
  if (isLine(node) && !deltaEndsWith(delta, '\n')) {
    delta.insert('\n');
  }
  return delta;
}

function matchSpacing(node, delta) {
  if (isLine(node) && node.nextElementSibling != null && !deltaEndsWith(delta, '\n\n')) {
    let nodeHeight = node.offsetHeight + parseFloat(computeStyle(node).marginTop) + parseFloat(computeStyle(node).marginBottom);
    if (node.nextElementSibling.offsetTop > node.offsetTop + nodeHeight*1.5) {
      delta.insert('\n');
    }
  }
  return delta;
}

function matchStyles(node, delta) {
  let formats = {};
  let style = node.style || {};
  if (style.fontWeight && computeStyle(node).fontWeight === 'bold') {
    formats.bold = true;
  }
  if (Object.keys(formats).length > 0) {
    delta = delta.compose(new Delta().retain(delta.length(), formats));
  }
  if (parseFloat(style.textIndent || 0) > 0) {  // Could be 0.5in
    delta = new Delta().insert('\t').concat(delta);
  }
  return delta;
}

function matchText(node, delta) {
  let text = node.data;
  // Word represents empty line with <o:p> </o:p>
  if (node.parentNode.tagName === 'O:P') {
    return delta.insert(text.trim());
  }
  if (!computeStyle(node.parentNode).whiteSpace.startsWith('pre')) {
    function replacer(collapse, match) {
      match = match.replace(/[^\u00a0]/g, '');    // \u00a0 is nbsp;
      return match.length < 1 && collapse ? ' ' : match;
    }
    text = text.replace(/\r\n/g, ' ').replace(/\n/g, ' ');
    text = text.replace(/\s\s+/g, replacer.bind(replacer, true));  // collapse whitespace
    if ((node.previousSibling == null && isLine(node.parentNode)) ||
        (node.previousSibling != null && isLine(node.previousSibling))) {
      text = text.replace(/^\s+/, replacer.bind(replacer, false));
    }
    if ((node.nextSibling == null && isLine(node.parentNode)) ||
        (node.nextSibling != null && isLine(node.nextSibling))) {
      text = text.replace(/\s+$/, replacer.bind(replacer, false));
    }
  }
  return delta.insert(text);
}

export { Clipboard as default, matchAttributor, matchBlot, matchNewline, matchSpacing, matchText };
<|endoftext|>


<commit_before>window.UniverseListView = function(options){
    var draggable = 'draggable' in options ? options['draggable'] : false;
    var collection = options['collection'];
    var el = $('#content');
    if(options['el'])
        el = options['el'];

    this.collection = collection;
    this.el = el;

    var default_pagination = 'endless';
    var pagination = 'pagination' in options ? options['pagination'] : default_pagination;

    var default_search_type = 'server';
    var search_type = 'search_type' in options ? options['search_type'] : default_search_type;

    var config = 0;
    var default_facets = appCollection.at(config).get('Search').Lists.facets;
    var facets = 'facets' in options ? options['facets'] : default_facets;

    el.html(template.universe({draggable: draggable}));

    var UniItemViewModel = kb.ViewModel.extend({
        constructor: function(model) {
            var self = this;
            kb.ViewModel.prototype.constructor.apply(this, arguments);
            options = options || {};
            options['keys'] = ['collection', 'name'];
            this.medias =  kb.collectionObservable(model.get('pieces'));
            this.total_time = ko.computed(function(){
//XXX: keep this, it tells KO to update total_time when something happens to the collection
                var x = self.medias();
                return model.pretty_duration();
            }, model);
            this.id = ko.observable(model.id);
        },
    });

    var UniverseListViewModel = kb.ViewModel.extend({
        constructor: function(model) {
            kb.ViewModel.prototype.constructor.apply(this, arguments);
            var self = this;
            this.filter = ko.observable('');
            this.playlists =  kb.collectionObservable(collection, {
               view_model: UniItemViewModel,
               filters: function(model) {
                   var filter;
                   filter = self.filter();
                   if (!filter) return false;
                   var re = new RegExp(filter,"i");
                   return model.get('name').search(re) < 0;
               },

            });
        }
    });

    this.search_view = new SearchView({
        el: $('#media-search',el),
        collection: collection,
        type: search_type,
        pagination: pagination,
        facets: facets
    });

    this.view_model = new UniverseListViewModel(this.collection);

    this.destroy = function () {
        this.search_view.destroy();
        kb.release(this.view_model);
        ko.cleanNode(this.el);
        this.el.html('');
    }

    ko.applyBindings(this.view_model, el[0]);

    this.collection.fetch();
}
<commit_msg>universe: fix display of playlist time<commit_after>window.UniverseListView = function(options){
    var draggable = 'draggable' in options ? options['draggable'] : false;
    var collection = options['collection'];
    var el = $('#content');
    if(options['el'])
        el = options['el'];

    this.collection = collection;
    this.el = el;

    var default_pagination = 'endless';
    var pagination = 'pagination' in options ? options['pagination'] : default_pagination;

    var default_search_type = 'server';
    var search_type = 'search_type' in options ? options['search_type'] : default_search_type;

    var config = 0;
    var default_facets = appCollection.at(config).get('Search').Lists.facets;
    var facets = 'facets' in options ? options['facets'] : default_facets;

    el.html(template.universe({draggable: draggable}));

    var UniItemViewModel = kb.ViewModel.extend({
        constructor: function(model) {
            var self = this;
            kb.ViewModel.prototype.constructor.apply(this, arguments);
            options = options || {};
            options['keys'] = ['collection', 'name'];
            this.medias =  kb.collectionObservable(model.get('pieces'));
            this.duration = kb.observable(model, 'duration');

            this.total_time = ko.computed(function(){
//XXX: keep this, it tells KO to update total_time when something happens to the model.
// For Media.Playlist we look for changes in the duration field, as the pieces may arrive with incomplete
// information when we do a fetchRelated(). We take care of that inside the model and update duration when
// everything is ready.
                var x = self.duration();
                return model.pretty_duration();
            }, model);
            this.id = ko.observable(model.id);
        },
    });

    var UniverseListViewModel = kb.ViewModel.extend({
        constructor: function(model) {
            kb.ViewModel.prototype.constructor.apply(this, arguments);
            var self = this;
            this.filter = ko.observable('');
            this.playlists =  kb.collectionObservable(collection, {
               view_model: UniItemViewModel,
               filters: function(model) {
                   var filter;
                   filter = self.filter();
                   if (!filter) return false;
                   var re = new RegExp(filter,"i");
                   return model.get('name').search(re) < 0;
               },

            });
        }
    });

    this.search_view = new SearchView({
        el: $('#media-search',el),
        collection: collection,
        type: search_type,
        pagination: pagination,
        facets: facets
    });

    this.view_model = new UniverseListViewModel(this.collection);

    this.destroy = function () {
        this.search_view.destroy();
        kb.release(this.view_model);
        ko.cleanNode(this.el);
        this.el.html('');
    }

    ko.applyBindings(this.view_model, el[0]);

    this.collection.fetch();
}
<|endoftext|>


<commit_before>/**
 * Copyright 2017 Google Inc. All Rights Reserved.
 *
 * 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
 *
 *      http://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.
 */
var scene = null;
var summaryPopulated = false;

var showScore = function() {
  console.log(`Scene: ${scene}`);
  firebase.database().ref('/total_scores/' + scene).on('value', function(snapshot) {
    console.log('got score: ', snapshot.val())
    if (snapshot.val()) {
      $('#total-score').text(snapshot.val());
    }
  });
};

var showLangs = function() {
  firebase.database().ref('/total_langs/' + scene + '/numLanguages').on('value', function(snapshot) {
    if (snapshot.val()) {
      $('#total-langs').text(snapshot.val());
    }
  });
};

var addStreamItem = function(noun, correctness) {
  var $streamItem = $([
    '',
    noun,
    ''
  ].join('\n'));

  // Prepend results and only keep 30 of the newest
  $('#activity-stream').prepend($streamItem);
  if ($("#activity-stream").children().length > 30) {
    $("#activity-stream").children().slice(30).remove();
  }
}

var showActivityStream = function() {
  firebase.database().ref('/all_guesses/' +scene).on('child_added', function(snapshot) {
    addStreamItem(snapshot.val().original, snapshot.val().correctness);
  });
};

var initSummaryDialog = function() {
  var dialog = document.querySelector('dialog');
  var showDialogButton = $('#show-dialog');
  if (! dialog.showModal) {
    dialogPolyfill.registerDialog(dialog);
  }
  showDialogButton.click(function() {
    populateSummary();
    dialog.showModal();
    firebase.database().ref('/admin/active').set(false);
  });
  $('dialog .close').click(function() {
    $('table#summary-table tbody').empty();
    summaryPopulated = false;
    dialog.close();
    firebase.database().ref('/admin/active').set(true);
  });
};

var populateSummary = function() {
  if (summaryPopulated) { return; }
  summaryPopulated = true;

  firebase.database().ref('/summary/' + scene).once('value').then(function(snapshot) {
    var results = snapshot.val();
    for (noun in results) {
      if (results.hasOwnProperty(noun)) {
        var nounSummary = results[noun];
        var langCount = nounSummary.num_langs || 0;
        var nounScore = nounSummary.score || 0;
        $row = $('
' + noun + '	' +
          langCount +'	' +
          nounScore + '
');
        $('table#summary-table tbody').append($row);
      }
    }
  });
}

var showVideo = function() {
  var $video = $([
    '<video id="video-player" width="96%" controls>',
    '<source src="/videos/' + scene + '.mp4" type="video/mp4">',
    '</video>'
  ].join('\n'));
  $('#activity-stream').empty();
  $('#video-container').empty();
  $('#video-container').append($video);
};

var initSceneSelector = function(initialScene) {
  firebase.database().ref('/admin/scenes').once('value').then(function(snapshot) {
    $('#scene-selector').empty();
    var scenes = snapshot.val();
    for (key in scenes) {
      if (scenes.hasOwnProperty(key)) {
        var $choice = $('<option value="' + key + '" ' + (key === initialScene ? 'selected' : '') + '>' + key + '</option>');
        $('#scene-selector').append($choice);
      }
    }
    $('#scene-select-submit').click(function() {
      firebase.database().ref('/admin/current_scene').set($('#scene-selector').val());
    });
  })
};

$(document).ready(function() {
  firebase.database().ref('/admin/current_scene').on('value', function(snapshot) {
    scene = snapshot.val();
    showVideo();
    showScore();
    showLangs();
    showActivityStream();
    initSceneSelector(scene);
    initSummaryDialog();
    firebase.database().ref('/admin/active').set(true);
  });
});
<commit_msg>Swapping language count and guesses count for summary view (#1)<commit_after>/**
 * Copyright 2017 Google Inc. All Rights Reserved.
 *
 * 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
 *
 *      http://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.
 */
var scene = null;
var summaryPopulated = false;

var showScore = function() {
  console.log(`Scene: ${scene}`);
  firebase.database().ref('/total_scores/' + scene).on('value', function(snapshot) {
    console.log('got score: ', snapshot.val())
    if (snapshot.val()) {
      $('#total-score').text(snapshot.val());
    }
  });
};

var showLangs = function() {
  firebase.database().ref('/total_langs/' + scene + '/numLanguages').on('value', function(snapshot) {
    if (snapshot.val()) {
      $('#total-langs').text(snapshot.val());
    }
  });
};

var addStreamItem = function(noun, correctness) {
  var $streamItem = $([
    '',
    noun,
    ''
  ].join('\n'));

  // Prepend results and only keep 30 of the newest
  $('#activity-stream').prepend($streamItem);
  if ($("#activity-stream").children().length > 30) {
    $("#activity-stream").children().slice(30).remove();
  }
}

var showActivityStream = function() {
  firebase.database().ref('/all_guesses/' +scene).on('child_added', function(snapshot) {
    addStreamItem(snapshot.val().original, snapshot.val().correctness);
  });
};

var initSummaryDialog = function() {
  var dialog = document.querySelector('dialog');
  var showDialogButton = $('#show-dialog');
  if (! dialog.showModal) {
    dialogPolyfill.registerDialog(dialog);
  }
  showDialogButton.click(function() {
    populateSummary();
    dialog.showModal();
    firebase.database().ref('/admin/active').set(false);
  });
  $('dialog .close').click(function() {
    $('table#summary-table tbody').empty();
    summaryPopulated = false;
    dialog.close();
    firebase.database().ref('/admin/active').set(true);
  });
};

var populateSummary = function() {
  if (summaryPopulated) { return; }
  summaryPopulated = true;

  firebase.database().ref('/summary/' + scene).once('value').then(function(snapshot) {
    var results = snapshot.val();
    for (noun in results) {
      if (results.hasOwnProperty(noun)) {
        var nounSummary = results[noun];
        var langCount = nounSummary.num_langs || 0;
        var nounScore = nounSummary.score || 0;
        $row = $('
' + noun + '	' +
          nounScore +'	' +
          langCount + '
');
        $('table#summary-table tbody').append($row);
      }
    }
  });
}

var showVideo = function() {
  var $video = $([
    '<video id="video-player" width="96%" controls>',
    '<source src="/videos/' + scene + '.mp4" type="video/mp4">',
    '</video>'
  ].join('\n'));
  $('#activity-stream').empty();
  $('#video-container').empty();
  $('#video-container').append($video);
};

var initSceneSelector = function(initialScene) {
  firebase.database().ref('/admin/scenes').once('value').then(function(snapshot) {
    $('#scene-selector').empty();
    var scenes = snapshot.val();
    for (key in scenes) {
      if (scenes.hasOwnProperty(key)) {
        var $choice = $('<option value="' + key + '" ' + (key === initialScene ? 'selected' : '') + '>' + key + '</option>');
        $('#scene-selector').append($choice);
      }
    }
    $('#scene-select-submit').click(function() {
      firebase.database().ref('/admin/current_scene').set($('#scene-selector').val());
    });
  })
};

$(document).ready(function() {
  firebase.database().ref('/admin/current_scene').on('value', function(snapshot) {
    scene = snapshot.val();
    showVideo();
    showScore();
    showLangs();
    showActivityStream();
    initSceneSelector(scene);
    initSummaryDialog();
    firebase.database().ref('/admin/active').set(true);
  });
});
<|endoftext|>


<commit_before>(function (root, factory) {
	if ( typeof exports === 'object' ) {
		module.exports = factory();
	} else {
		root.domready = factory();
	}
}(this, function () {
	'use strict';

	var ready = false,
		stack = [];

	function completed() {
		document.removeEventListener('DOMContentLoaded', completed, false);
		window.removeEventListener('load', completed, false);

		var f;
		while ( f = stack.shift() ) {
			setTimeout(f, 0);
		}

		ready = true;
	}

	function domready(f) {
		if ( typeof f != 'function' ) {
			return;
		}

		if ( ready || (document.readyState === 'complete' ) ) {
			setTimeout(f, 0);
			return;
		}

		if ( stack.length <= 0 ) {
			document.addEventListener('DOMContentLoaded', completed, false);
			window.addEventListener('load', completed, false);
		}

		stack.push(f);
	}

	return domready;
}));
<commit_msg>add Event Listener for old IE<commit_after>(function (root, factory) {
	if ( typeof exports === 'object' ) {
		module.exports = factory();
	} else {
		root.domready = factory();
	}
}(this, function () {
	'use strict';

	var ready = false,
		stack = [];

	function completed() {
		if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) {
			if ( document.addEventListener ) {
				document.removeEventListener('DOMContentLoaded', completed, false);
				window.removeEventListener('load', completed, false);
			} else {
				document.detachEvent("onreadystatechange", completed);
				window.detachEvent("onload", completed);
			}

			var f;
			while ( f = stack.shift() ) {
				setTimeout(f, 0);
			}

			ready = true;
		}
	}

	function domready(f) {
		if ( typeof f != 'function' ) {
			return;
		}

		if ( ready || (document.readyState === 'complete' ) ) {
			setTimeout(f, 0);
			return;
		}

		if ( stack.length <= 0 ) {
			if ( document.addEventListener ) {
				document.addEventListener('DOMContentLoaded', completed, false);
				window.addEventListener('load', completed, false);
			}
			else {
				document.attachEvent("onreadystatechange", completed);
				window.attachEvent("onload", completed);
			}
		}

		stack.push(f);
	}

	return domready;
}));
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import HotelsIndv from './HotelsIndv';
import FilterOptions from './FilterOptions';

//'https://hotel-project-ajboyle.c9users.io/api/locations/' + city + '/hotels/' + hotel.id + '?checkin=' + checkin + '&checkout=' + checkout

class HotelsList extends Component {

    render(){
        var city = this.props.info.city;
        var checkin = this.props.info.checkin;
        var checkout = this.props.info.checkout;

        let hotelNodes = this.props.data.map((hotel) => {
            return (
                <HotelsIndv
                    key={hotel.id}
                    link={'#api/locations/' + city + '/hotels/' + hotel.id + '?checkin=' + checkin + '&checkout=' + checkout}
                    photo={hotel.photos[0]['thumbnail']}
                    name={hotel.name} rate={hotel.available ? '$' + (Number(hotel['nightly_rate']).toFixed(0)) : "Unavailable"}
                />
            );
        });
        return (

{hotelNodes}

        );
    }
}

export default HotelsList;
/*

*/<commit_msg>added api call for single hotel<commit_after>import React, {Component} from 'react';
import HotelsIndv from './HotelsIndv';
import FilterOptions from './FilterOptions';
import 'whatwg-fetch';

const API_URL = 'https://hotel-project-ajboyle.c9users.io/api/locations/';
const API_HEADERS = {
    'Content-Type': 'application/json'
};
//'https://hotel-project-ajboyle.c9users.io/api/locations/' + city + '/hotels/' + hotel.id + '?checkin=' + checkin + '&checkout=' + checkout

class HotelsList extends Component {
    constructor(){
        super();
        this.state = {
            hotel: []
        };
    }
    getHotel(){
        fetch(API_URL + this.props.info.city + '/hotels/' + this.hotel.id + '?checkin=' + this.props.info.checkin + '&checkout=' + this.props.info.checkout, {headers: API_HEADERS})
        .then((response) => response.json())
        .then((responseData) => {
            this.setState({ hotel: responseData });
        })
        .catch((error) => {
            console.log('Error fetching and parsing data', error);
        });
    }
    render(){
        var city = this.props.info.city;
        var checkin = this.props.info.checkin;
        var checkout = this.props.info.checkout;

        let hotelNodes = this.props.data.map((hotel) => {
            return (
                <HotelsIndv
                    key={hotel.id}
                    link={'#api/locations/' + city + '/hotels/' + hotel.id + '?checkin=' + checkin + '&checkout=' + checkout}
                    photo={hotel.photos[0]['thumbnail']}
                    name={hotel.name} rate={hotel.available ? '$' + (Number(hotel['nightly_rate']).toFixed(0)) : "Unavailable"}
                    hotelCallback={this.getHotel.bind(this)}
                />
            );
        });
        return (

{hotelNodes}

            <Hotel hotel={this.state.hotel} />

        );
    }
}

export default HotelsList;<|endoftext|>