<commit_before>require('jquery');

/*

AutoComplete module
- Create an autoComplete with the following markup
- Create a JSON object as a global variable, the structure is demonstrated below
- Optionally provide a target input to update with suggestions value

Auto complete html markup:

    <input type="text"
           name="country-code-auto-complete"
           id="country-code-auto-complete"
           class="form-control form-control--block js-choose-country-auto-complete"
           autocomplete="off"
           spellcheck="false"
           required
           data-rule-suggestion="true"
           aria-autocomplete="list"
           aria-haspopup="country-code-suggestions"
           aria-activedescendant />

Data format:

*/

var suggestions;
var $suggestionsContainer;
var $autoCompleteInputElem;
var $targetInput;
var suggestionDisplayFormat;
var $suggestionsStatusMessage;
var $clearInputButton;

/**
 * Display suggestions
 * @param $suggestionsContainer
 * @param matches
 * @param match
 */
var displaySuggestions = function ($suggestionsContainer, matches, match) {
  var ulHtmlFragment = document.createElement('ul');
  var liHtmlFragment = document.createElement('li');
  var containerFragment = document.createElement('div');

  var createSuggestion = function (index, suggestion) {
    var positionalClassNames = [];
    var li = liHtmlFragment.cloneNode();
    var pattern = new RegExp('^(' + match + ')', 'i');
    var html;

    suggestion.title.replace(pattern, '$1');
    html = (typeof suggestionDisplayFormat === 'function') ? suggestionDisplayFormat(suggestion.title, suggestion.value) : suggestion.title;

    if (index === 0) {
      positionalClassNames.push('suggestion--first');
    }
    if (index === matches.length - 1) {
      positionalClassNames.push('suggestion--last');
    }

    li.innerHTML = html;
    li.className = 'suggestion ' + positionalClassNames.join(' ');
    li.setAttribute('data-suggestion-value', suggestion.value);
    li.setAttribute('data-suggestion-title', suggestion.title);
    li.setAttribute('role', 'option');
    ulHtmlFragment.className = 'suggestions-list';
    ulHtmlFragment.setAttribute('role', 'listbox');
    ulHtmlFragment.appendChild(li);
  };

  $(matches).each(function (index, suggestion) {
    createSuggestion(index, suggestion);
  });

  $suggestionsStatusMessage.text(matches.length + ' suggestion' + (matches.length > 1 ? 's' : '') + ' available, please navigate by using up and down');
  $autoCompleteInputElem.addClass('has-suggestions');

  containerFragment.appendChild(ulHtmlFragment);
  $suggestionsContainer.html(containerFragment.innerHTML);
};

/**
 * close suggestions display
 */
var closeSuggestions = function () {
  $suggestionsContainer.html('');
  $autoCompleteInputElem.removeClass('has-suggestions');
};

/**
 * close suggestions display and focus the auto complete element and place cursor at end of text in input
 */
var closeSuggestionsAndFocus = function () {
  closeSuggestions();
  $autoCompleteInputElem.focus().val($autoCompleteInputElem.val()); //place cursor at end of input text
};

/**
 * empty input, focus input, close suggestions display and empty target input
 */
var clearInput = function () {
  $autoCompleteInputElem.val('').focus();
  closeSuggestions();
  updateTargetInput(0);
};

/**
 * place cursor at end of input and updateTarget element
 * @param title
 * @param value
 */
var updateInput = function (title, value) {
  $autoCompleteInputElem.val(title).val($autoCompleteInputElem.val()); //place cursor at end of input text
  updateTargetInput(value);
};

/**
 * Allow auto complete to control and pass over the .value of your suggestion to another optional input
 * specified on creation of the autoComplete
 * @param value
 */
var updateTargetInput = function (value) {
  if ($targetInput) {
    $targetInput.val(value);
  }
};

/**
 * Controls for the following keys to navigate the suggestions list
 * 40 (down) - move down the suggestions list
 * 38 (up) - move up teh suggestions list
 * 27 (esc) - close from the suggestion list
 * 13 (enter) - choose item if list is open
 * @param keyCode
 * @param $suggestionsContainer
 */
var suggestionsKeyControl = function (keyCode, $suggestionsContainer) {

  //40 (down), 38 (up)
  if ((keyCode === 38 || keyCode === 40) && $suggestionsContainer.html()) {

    var $next;
    var $selected = $suggestionsContainer.find('.suggestion--selected').first();
    var updateAutoCompleteInput = function ($elem) {
      updateInput($elem.data('suggestion-title'), $elem.data('suggestion-value'));
    };

    if (!$selected.length) {
      if (keyCode === 40) {
        $next = $suggestionsContainer.find('li').first().addClass('suggestion--selected').attr('aria-selected', true);
        updateAutoCompleteInput($next);
      }
    } else {
      $next = (keyCode === 40) ? $selected.next() : $selected.prev();
      if ($next.length) {
        $selected.removeClass('suggestion--selected').removeAttr('aria-selected');
        $next.addClass('suggestion--selected').attr('aria-selected', true);
        updateAutoCompleteInput($next);
      } else {
        if ($selected.prev().length === 0 && keyCode === 38) { //hide when reaching top of suggestions
          $selected.removeClass('suggestion--selected').removeAttr('aria-selected');
          closeSuggestionsAndFocus();
        }
      }
    }
  } else if (keyCode === 27 || keyCode === 13) { //27 (esc), 13 (enter)
    closeSuggestionsAndFocus();
  }
};

var addEventListeners = function () {
  inputKeyDownEvent();
  inputKeyupEvent();
  inputBlurEvent();
  clearSuggestionsEvent();
  suggestionsEvent();
};

/**
 * keydown event for auto complete input
 * keydown used to capture enter key (13) before form is submitted if we have suggestions
 * allow suggestion list key controls through
 * 40 (down)
 * 38 (up)
 * 27 (esc)
 * 13 (enter)
 */
var inputKeyDownEvent = function () {
  $autoCompleteInputElem.on('keydown', function (event) {
    var keyCode = event.which;

    if (keyCode === 13 && $suggestionsContainer.html()) {
      event.preventDefault();
    }

    if (keyCode === 38 || keyCode === 40 || keyCode === 13 || keyCode === 27) {
      suggestionsKeyControl(keyCode, $suggestionsContainer);
    }
  });
};

/**
 * keyup event dependant on auto complete input, if key is not a suggestion list key control and we have a value in
 * the auto complete input the search through suggestions.
 *
 * if we have suggestions display them
 * If we have no input value them clear suggestions
 */
var inputKeyupEvent = function () {
  $autoCompleteInputElem.on('keyup', function (event) {
    var inputVal = $(event.target).val();
    var matches = [];
    var keyCode = event.which;

    if ((keyCode !== 36 && keyCode !== 38 && keyCode !== 40 && keyCode !== 13 && keyCode !== 27) && inputVal) {

      $clearInputButton.removeClass('hidden');

      $(suggestions).each(function (index, suggestion) {
        var regEx;

        try {
          regEx = new RegExp('^(' + inputVal + ')', 'i');
          if (regEx.test(suggestion.title)) {
            matches.push(suggestion);
          }
        } catch (e) {
          //TODO add reporting?
        }

      });

      if (matches.length) {
        displaySuggestions($suggestionsContainer, matches, inputVal);
      }
    } else if (!inputVal) {
      $clearInputButton.addClass('hidden');
      closeSuggestionsAndFocus();
      $suggestionsStatusMessage.text('');
    }
  });
};

/**
 * on blur check to see if what has been entered matches a suggestion, in case the customer hasn't interacted with
 * the suggestions list
 */
var inputBlurEvent = function () {
  $autoCompleteInputElem.on('blur', function (event) {
    isMatchingSuggestion(event.target.value);
    closeSuggestions();
  });
};

/**
 * check to see if the value specified in teh auto complete input matches a unique title found in suggestions
 * if it does then update it and the target input
 * @param value
 */
var isMatchingSuggestion = function (value) {
  var matchFound = false;

  if (value) {
    $(suggestions).each(function (index, suggestion) {
      if (value.toLowerCase() === suggestion.title.toLowerCase()) {
        updateInput(suggestion.title, suggestion.value);
        matchFound = true;
        return false;
      }
    });

    if (!matchFound) { //clear out the input
      updateTargetInput(0);
    }
  }
};

/**
 * (x) button on input to clear out the input value, close suggestions and clear out input target
 */
var clearSuggestionsEvent = function () {
  $clearInputButton.on('mousedown', function (event) {
    event.preventDefault();
    clearInput();
    $clearInputButton.addClass('hidden');
  });
};

/**
 * update input to suggested values when mouse click actioned on suggestion list
 * mousedown event used so this fires before blur event
 */
var suggestionsEvent = function () {
  $suggestionsContainer.on('mousedown', '.suggestion', function (event) {
    var $suggestion = $(event.target);
    event.preventDefault();

    updateInput($suggestion.data('suggestion-title'), $suggestion.data('suggestion-value'));
    closeSuggestions();
  });
};

/**
 * setup variables and create the autoComplete
 *
 * @param $autoCompleteInputElem
 *
 * @param suggestionsData
 * The suggestion data used in the autoComplete. Typically this will be a global variable.
 *
 * @param $targetInputElem - [optional]
 * Input element to apply the suggestion.value too when a suggestion is selected. If this is not supplied then the autoComplete input will contain the
 * suggestion.title which will be sent to the backend as the value of the $autoCompleteInputElem
 *
 * @param suggestionFormat - [optional]
 * Format for the suggestion to be displayed in the suggestion list, this will default to suggestion.title if a format is not supplied
 *
 */
var setup = function ($elem, suggestionsData, $targetInputElem, suggestionFormat) {
  if ($elem.length) {
    $autoCompleteInputElem = $elem;
    $targetInput = $targetInputElem;
    suggestionDisplayFormat = suggestionFormat;
    suggestions = suggestionsData;
    $clearInputButton = $('.js-suggestions-clear');
    $suggestionsContainer = $('.js-suggestions').first();
    $suggestionsStatusMessage = $('.js-suggestions-status-message').first();

    addEventListeners();
  }
};

module.exports = setup;
<commit_msg>update automcomplete example<commit_after>require('jquery');

/*

AutoComplete module
- Create an autoComplete with the following markup
- Create a JSON object as a global variable, the structure is demonstrated below
- Optionally provide a target input to update with suggestions value

Auto complete html markup:

    <input type="text"
           name="country-code-auto-complete"
           id="country-code-auto-complete"
           class="form-control form-control--block js-choose-country-auto-complete"
           autocomplete="off"
           spellcheck="false"
           required
           data-suggestions="countries"
           data-rule-suggestion="true"
           aria-autocomplete="list"
           aria-haspopup="country-code-suggestions"
           aria-activedescendant />

Data format:

*/

var suggestions;
var $suggestionsContainer;
var $autoCompleteInputElem;
var $targetInput;
var suggestionDisplayFormat;
var $suggestionsStatusMessage;
var $clearInputButton;

/**
 * Display suggestions
 * @param $suggestionsContainer
 * @param matches
 * @param match
 */
var displaySuggestions = function ($suggestionsContainer, matches, match) {
  var ulHtmlFragment = document.createElement('ul');
  var liHtmlFragment = document.createElement('li');
  var containerFragment = document.createElement('div');

  var createSuggestion = function (index, suggestion) {
    var positionalClassNames = [];
    var li = liHtmlFragment.cloneNode();
    var pattern = new RegExp('^(' + match + ')', 'i');
    var html;

    suggestion.title.replace(pattern, '$1');
    html = (typeof suggestionDisplayFormat === 'function') ? suggestionDisplayFormat(suggestion.title, suggestion.value) : suggestion.title;

    if (index === 0) {
      positionalClassNames.push('suggestion--first');
    }
    if (index === matches.length - 1) {
      positionalClassNames.push('suggestion--last');
    }

    li.innerHTML = html;
    li.className = 'suggestion ' + positionalClassNames.join(' ');
    li.setAttribute('data-suggestion-value', suggestion.value);
    li.setAttribute('data-suggestion-title', suggestion.title);
    li.setAttribute('role', 'option');
    ulHtmlFragment.className = 'suggestions-list';
    ulHtmlFragment.setAttribute('role', 'listbox');
    ulHtmlFragment.appendChild(li);
  };

  $(matches).each(function (index, suggestion) {
    createSuggestion(index, suggestion);
  });

  $suggestionsStatusMessage.text(matches.length + ' suggestion' + (matches.length > 1 ? 's' : '') + ' available, please navigate by using up and down');
  $autoCompleteInputElem.addClass('has-suggestions');

  containerFragment.appendChild(ulHtmlFragment);
  $suggestionsContainer.html(containerFragment.innerHTML);
};

/**
 * close suggestions display
 */
var closeSuggestions = function () {
  $suggestionsContainer.html('');
  $autoCompleteInputElem.removeClass('has-suggestions');
};

/**
 * close suggestions display and focus the auto complete element and place cursor at end of text in input
 */
var closeSuggestionsAndFocus = function () {
  closeSuggestions();
  $autoCompleteInputElem.focus().val($autoCompleteInputElem.val()); //place cursor at end of input text
};

/**
 * empty input, focus input, close suggestions display and empty target input
 */
var clearInput = function () {
  $autoCompleteInputElem.val('').focus();
  closeSuggestions();
  updateTargetInput(0);
};

/**
 * place cursor at end of input and updateTarget element
 * @param title
 * @param value
 */
var updateInput = function (title, value) {
  $autoCompleteInputElem.val(title).val($autoCompleteInputElem.val()); //place cursor at end of input text
  updateTargetInput(value);
};

/**
 * Allow auto complete to control and pass over the .value of your suggestion to another optional input
 * specified on creation of the autoComplete
 * @param value
 */
var updateTargetInput = function (value) {
  if ($targetInput) {
    $targetInput.val(value);
  }
};

/**
 * Controls for the following keys to navigate the suggestions list
 * 40 (down) - move down the suggestions list
 * 38 (up) - move up teh suggestions list
 * 27 (esc) - close from the suggestion list
 * 13 (enter) - choose item if list is open
 * @param keyCode
 * @param $suggestionsContainer
 */
var suggestionsKeyControl = function (keyCode, $suggestionsContainer) {

  //40 (down), 38 (up)
  if ((keyCode === 38 || keyCode === 40) && $suggestionsContainer.html()) {

    var $next;
    var $selected = $suggestionsContainer.find('.suggestion--selected').first();
    var updateAutoCompleteInput = function ($elem) {
      updateInput($elem.data('suggestion-title'), $elem.data('suggestion-value'));
    };

    if (!$selected.length) {
      if (keyCode === 40) {
        $next = $suggestionsContainer.find('li').first().addClass('suggestion--selected').attr('aria-selected', true);
        updateAutoCompleteInput($next);
      }
    } else {
      $next = (keyCode === 40) ? $selected.next() : $selected.prev();
      if ($next.length) {
        $selected.removeClass('suggestion--selected').removeAttr('aria-selected');
        $next.addClass('suggestion--selected').attr('aria-selected', true);
        updateAutoCompleteInput($next);
      } else {
        if ($selected.prev().length === 0 && keyCode === 38) { //hide when reaching top of suggestions
          $selected.removeClass('suggestion--selected').removeAttr('aria-selected');
          closeSuggestionsAndFocus();
        }
      }
    }
  } else if (keyCode === 27 || keyCode === 13) { //27 (esc), 13 (enter)
    closeSuggestionsAndFocus();
  }
};

var addEventListeners = function () {
  inputKeyDownEvent();
  inputKeyupEvent();
  inputBlurEvent();
  clearSuggestionsEvent();
  suggestionsEvent();
};

/**
 * keydown event for auto complete input
 * keydown used to capture enter key (13) before form is submitted if we have suggestions
 * allow suggestion list key controls through
 * 40 (down)
 * 38 (up)
 * 27 (esc)
 * 13 (enter)
 */
var inputKeyDownEvent = function () {
  $autoCompleteInputElem.on('keydown', function (event) {
    var keyCode = event.which;

    if (keyCode === 13 && $suggestionsContainer.html()) {
      event.preventDefault();
    }

    if (keyCode === 38 || keyCode === 40 || keyCode === 13 || keyCode === 27) {
      suggestionsKeyControl(keyCode, $suggestionsContainer);
    }
  });
};

/**
 * keyup event dependant on auto complete input, if key is not a suggestion list key control and we have a value in
 * the auto complete input the search through suggestions.
 *
 * if we have suggestions display them
 * If we have no input value them clear suggestions
 */
var inputKeyupEvent = function () {
  $autoCompleteInputElem.on('keyup', function (event) {
    var inputVal = $(event.target).val();
    var matches = [];
    var keyCode = event.which;

    if ((keyCode !== 36 && keyCode !== 38 && keyCode !== 40 && keyCode !== 13 && keyCode !== 27) && inputVal) {

      $clearInputButton.removeClass('hidden');

      $(suggestions).each(function (index, suggestion) {
        var regEx;

        try {
          regEx = new RegExp('^(' + inputVal + ')', 'i');
          if (regEx.test(suggestion.title)) {
            matches.push(suggestion);
          }
        } catch (e) {
          //TODO add reporting?
        }

      });

      if (matches.length) {
        displaySuggestions($suggestionsContainer, matches, inputVal);
      }
    } else if (!inputVal) {
      $clearInputButton.addClass('hidden');
      closeSuggestionsAndFocus();
      $suggestionsStatusMessage.text('');
    }
  });
};

/**
 * on blur check to see if what has been entered matches a suggestion, in case the customer hasn't interacted with
 * the suggestions list
 */
var inputBlurEvent = function () {
  $autoCompleteInputElem.on('blur', function (event) {
    isMatchingSuggestion(event.target.value);
    closeSuggestions();
  });
};

/**
 * check to see if the value specified in teh auto complete input matches a unique title found in suggestions
 * if it does then update it and the target input
 * @param value
 */
var isMatchingSuggestion = function (value) {
  var matchFound = false;

  if (value) {
    $(suggestions).each(function (index, suggestion) {
      if (value.toLowerCase() === suggestion.title.toLowerCase()) {
        updateInput(suggestion.title, suggestion.value);
        matchFound = true;
        return false;
      }
    });

    if (!matchFound) { //clear out the input
      updateTargetInput(0);
    }
  }
};

/**
 * (x) button on input to clear out the input value, close suggestions and clear out input target
 */
var clearSuggestionsEvent = function () {
  $clearInputButton.on('mousedown', function (event) {
    event.preventDefault();
    clearInput();
    $clearInputButton.addClass('hidden');
  });
};

/**
 * update input to suggested values when mouse click actioned on suggestion list
 * mousedown event used so this fires before blur event
 */
var suggestionsEvent = function () {
  $suggestionsContainer.on('mousedown', '.suggestion', function (event) {
    var $suggestion = $(event.target);
    event.preventDefault();

    updateInput($suggestion.data('suggestion-title'), $suggestion.data('suggestion-value'));
    closeSuggestions();
  });
};

/**
 * setup variables and create the autoComplete
 *
 * @param $autoCompleteInputElem
 *
 * @param suggestionsData
 * The suggestion data used in the autoComplete. Typically this will be a global variable.
 *
 * @param $targetInputElem - [optional]
 * Input element to apply the suggestion.value too when a suggestion is selected. If this is not supplied then the autoComplete input will contain the
 * suggestion.title which will be sent to the backend as the value of the $autoCompleteInputElem
 *
 * @param suggestionFormat - [optional]
 * Format for the suggestion to be displayed in the suggestion list, this will default to suggestion.title if a format is not supplied
 *
 */
var setup = function ($elem, suggestionsData, $targetInputElem, suggestionFormat) {
  if ($elem.length) {
    $autoCompleteInputElem = $elem;
    $targetInput = $targetInputElem;
    suggestionDisplayFormat = suggestionFormat;
    suggestions = suggestionsData;
    $clearInputButton = $('.js-suggestions-clear');
    $suggestionsContainer = $('.js-suggestions').first();
    $suggestionsStatusMessage = $('.js-suggestions-status-message').first();

    addEventListeners();
  }
};

module.exports = setup;
<|endoftext|>


<commit_before>import {
  SendEndpoint,
  ReceiveEndpoint,
  SendEndpointDefault
} from 'kronos-endpoint';
import { Service } from 'kronos-service';
import { makeLogEvent } from 'loglevel-mixin';
import { mergeAttributes, createAttributes } from 'model-attributes';

export class Step extends Service {
  static get configurationAttributes() {
    return mergeAttributes(
      createAttributes({
        // TODO move into service
        description: {
          type: 'string'
        }
      }),
      Service.configurationAttributes
    );
  }

  static get name() {
    return 'kronos-step';
  }

  /**
   * This method could be overwritten by a derived object to setup default endpoints.
   * This method may be overwritten by derived classes.
   * This will be called by the initialize method
   * @param {Object} stepConfiguration The default step configuration
   * @api protected
   */
  createPredefinedEndpoints(stepConfiguration) {
    if (this.endpoints.log) return;

    const logEndpoint = new SendEndpointDefault('log', this);
    if (this.manager.services && this.manager.services.logger) {
      logEndpoint.connected = this.manager.services.logger.endpoints.log;
    }
    this.addEndpoint(logEndpoint);
  }

  /**
   * Creates the endpoint objects defined as a combination from
   * implementation and definition
   * @param {Object} def The step configuration
   * @api protected
   */
  createEndpoints(def) {
    if (def && def.endpoints) {
      Object.keys(def.endpoints).forEach(name =>
        this.createEndpoint(name, def.endpoints[name])
      );
    }
  }

  /**
   * Deliver the endpoint options for a given endpoint definition.
   * @return {Object} suiable to pass as options to the endpoint factory
   */
  endpointOptions(name, def) {
    let options = {};

    if (def.opposite) {
      options.createOpposite = true;
    }

    return options;
  }

  createEndpoint(name, def) {
    let ep;

    if (def.in)
      ep = new ReceiveEndpoint(name, this, this.endpointOptions(name, def));
    if (def.out)
      ep = new SendEndpoint(name, this, this.endpointOptions(name, def));

    this.addEndpoint(ep);

    if (def.interceptors) {
      ep.interceptors = def.interceptors.map(icDef =>
        this.manager.createInterceptorInstanceFromConfig(icDef, ep)
      );
    }
  }

  /**
   * @return {String} separator between step name and endpoint name
   **/
  get endpointParentSeparator() {
    return '/';
  }

  log(level, arg) {
    const logevent = makeLogEvent(level, arg, {
      'step-type': this.type,
      'step-name': this.name
    });

    if (this.endpoints.log && this.endpoints.log.isConnected) {
      this.endpoints.log.receive(logevent);
    } else {
      console.log(logevent);
    }
  }
}
<commit_msg>refactor: use more functionalty from Service class<commit_after>import {
  SendEndpoint,
  ReceiveEndpoint,
  SendEndpointDefault
} from 'kronos-endpoint';
import { Service } from 'kronos-service';
import { makeLogEvent } from 'loglevel-mixin';
import { mergeAttributes, createAttributes } from 'model-attributes';

export class Step extends Service {
  static get configurationAttributes() {
    return mergeAttributes(
      createAttributes({
        // TODO move into service
        description: {
          type: 'string'
        }
      }),
      Service.configurationAttributes
    );
  }

  static get name() {
    return 'kronos-step';
  }

  /**
   * Creates the endpoint objects defined as a combination from
   * implementation and definition
   * @param {Object} def The step configuration
   * @api protected
   */
  createEndpoints(def) {
    if (def && def.endpoints) {
      Object.keys(def.endpoints).forEach(name =>
        this.createEndpoint(name, def.endpoints[name])
      );
    }
  }

  /**
   * Deliver the endpoint options for a given endpoint definition.
   * @return {Object} suiable to pass as options to the endpoint factory
   */
  endpointOptions(name, def) {
    let options = {};

    if (def.opposite) {
      options.createOpposite = true;
    }

    return options;
  }

  createEndpoint(name, def) {
    let ep;

    if (def.in)
      ep = new ReceiveEndpoint(name, this, this.endpointOptions(name, def));
    if (def.out)
      ep = new SendEndpoint(name, this, this.endpointOptions(name, def));

    this.addEndpoint(ep);

    if (def.interceptors) {
      ep.interceptors = def.interceptors.map(icDef =>
        this.manager.createInterceptorInstanceFromConfig(icDef, ep)
      );
    }
  }

  /**
   * @return {String} separator between step name and endpoint name
   **/
  get endpointParentSeparator() {
    return '/';
  }

  log(level, arg) {
    this.endpoints.log.receive(
      makeLogEvent(level, arg, {
        'step-type': this.type,
        'step-name': this.name
      })
    );
  }
}
<|endoftext|>


<commit_before>// $Id$

Drupal.textareaAttach = function() {
  $('textarea.resizable:not(.processed)').each(function() {
    var textarea = $(this).addClass('processed'), staticOffset = null;

    $(this).wrap('

')
      .parent().append($('

').mousedown(startDrag));

    // Inherit visibility
    if ($(this).is(':hidden')) {
      $(this).parent().hide();
      $(this).show();
    }

    var grippie = $('div.grippie', $(this).parent())[0];
    grippie.style.marginRight = (grippie.offsetWidth - $(this)[0].offsetWidth) +'px';

    function startDrag(e) {
      staticOffset = textarea.height() - Drupal.mousePosition(e).y;
      textarea.css('opacity', 0.25);
      $(document).mousemove(performDrag).mouseup(endDrag);
      return false;
    }

    function performDrag(e) {
      textarea.height(Math.max(32, staticOffset + Drupal.mousePosition(e).y) + 'px');
      return false;
    }

    function endDrag(e) {
      $(document).unbind("mousemove");
      $(document).unbind("mouseup");
      textarea.css('opacity', 1);
    }
  });
}

if (Drupal.jsEnabled) {
  $(document).ready(Drupal.textareaAttach);
}
<commit_msg>- Patch #101305 by bjaspan: work around IE textarea bug.<commit_after>// $Id$

Drupal.textareaAttach = function() {
  $('textarea.resizable:not(.processed)').each(function() {
    var textarea = $(this).addClass('processed'), staticOffset = null;

    // When wrapping the text area, work around an IE margin bug.  See:
    // http://jaspan.com/ie-inherited-margin-bug-form-elements-and-haslayout
    $(this).wrap('

')
      .parent().append($('

').mousedown(startDrag));

    // Inherit visibility
    if ($(this).is(':hidden')) {
      $(this).parent().hide();
      $(this).show();
    }

    var grippie = $('div.grippie', $(this).parent())[0];
    grippie.style.marginRight = (grippie.offsetWidth - $(this)[0].offsetWidth) +'px';

    function startDrag(e) {
      staticOffset = textarea.height() - Drupal.mousePosition(e).y;
      textarea.css('opacity', 0.25);
      $(document).mousemove(performDrag).mouseup(endDrag);
      return false;
    }

    function performDrag(e) {
      textarea.height(Math.max(32, staticOffset + Drupal.mousePosition(e).y) + 'px');
      return false;
    }

    function endDrag(e) {
      $(document).unbind("mousemove");
      $(document).unbind("mouseup");
      textarea.css('opacity', 1);
    }
  });
}

if (Drupal.jsEnabled) {
  $(document).ready(Drupal.textareaAttach);
}
<|endoftext|>


<commit_before>(function($) {

    var editingUser = false;

    this.startSharingBag = function(id) {
      if (editingUser) {
        stopSharingBag();
      }
      editingUser = id;
      hideEl('addLink-' + id);
      showEl('shareEdit-' + id);
    };

    this.stopSharingBag = function() {
      if (editingUser) {
        hideEl('shareEdit-' + editingUser);
        showEl('addLink-' + editingUser);
      }
      editingUser = false;
    };

    this.addUser = function(id, bagName) {
/*      if (failureNotification != null) {
        //failureNotification.$el.hide('slow', function() {failureNotification.remove()});
        failureNotification.$el.html('');
      }*/
      var user = jQuery('#userValue-' + id).val();
      var callBack = function(returnStr) {
        if (returnStr == 'ok') {
          refreshSharingUsers(bagName, id);
          jQuery('#userValue-' + id).val('');
        } else {
            new FailureNotification({message: returnStr}).render();
        }
      };
      if (user != '') {
        AjaxServices.addUserToShareBag(user, bagName, callBack);
      }
    };

    this.refreshSharingUsers = function(bagName, id) {
      var callBack = function(users) {
        displayUsers(id, users, bagName);
      };
      AjaxServices.getUsersSharingBag(bagName, callBack);
    };

    this.deleteUser = function(user, bagName, id) {
      var callBack = function(returnStr) {
        if (returnStr == 'ok') {
          refreshSharingUsers(bagName, id);
        } else {
          new FailureNotification({message: returnStr}).render();
        }
      };

      AjaxServices.deleteUserToShareBag(user, bagName, callBack);
    };

    this.displayUsers = function(id, users, bagName) {
      var usersl = users.length,
      parent = $('#sharingUsers-' + id).empty();
      for (i = 0; i < usersl; i++) {
        user = users[i];
        addUserSpan(id, user, bagName);
      }
    };

    this.addUserSpan = function(id, user, bagName) {
      var parent = $('#sharingUsers-' + id);
      var span = $('');
      var a = $('[x]');
      a.click(function() {
          deleteUser(user, bagName, id);
      });
      span.append(user).append(a);
      parent.append(span);
    };

}).call(window, jQuery);
<commit_msg>Added invitation logic<commit_after>(function($) {

    var editingUser = false;

    this.startSharingBag = function(id) {
      if (editingUser) {
        stopSharingBag();
      }
      editingUser = id;
      hideEl('addLink-' + id);
      showEl('shareEdit-' + id);
    };

    this.stopSharingBag = function() {
      if (editingUser) {
        hideEl('shareEdit-' + editingUser);
        showEl('addLink-' + editingUser);
      }
      editingUser = false;
    };

    this.addUser = function(id, bagName) {
/*      if (failureNotification != null) {
        //failureNotification.$el.hide('slow', function() {failureNotification.remove()});
        failureNotification.$el.html('');
      }*/
      var user = jQuery('#userValue-' + id).val();
      var callBack = function(returnStr) {
        var sendInvite, notification, failureMessage = returnStr;
        if (returnStr == 'ok') {
          refreshSharingUsers(bagName, id);
          jQuery('#userValue-' + id).val('');
        } else {
            if (returnStr.match(/not found/)) {
                failureMessage = jQuery("");
                failureMessage.append(returnStr);
                failureMessage.append(" Would you like to send an invitation to " + user + "? ");
                jQuery("<button>OK</button>").appendTo(failureMessage).click(function(evt) {
                    notification.close();
                    if (window.$SERVICE) {
                      $SERVICE.fetchList(bagName, function(l) {
                          l.inviteUserToShare(user).fail(function(err) {new FailureNotification({message: err}).render()});
                      });
                    } else {
                        new FailureNotification({message: "You are not logged in"}).render();
                    }
                });
            }
            notification = new FailureNotification({message: failureMessage});
            notification.render();
        }
      };
      if (user != '') {
        AjaxServices.addUserToShareBag(user, bagName, callBack);
      }
    };

    this.refreshSharingUsers = function(bagName, id) {
      var callBack = function(users) {
        displayUsers(id, users, bagName);
      };
      AjaxServices.getUsersSharingBag(bagName, callBack);
    };

    this.deleteUser = function(user, bagName, id) {
      var callBack = function(returnStr) {
        if (returnStr == 'ok') {
          refreshSharingUsers(bagName, id);
        } else {
          new FailureNotification({message: returnStr}).render();
        }
      };

      AjaxServices.deleteUserToShareBag(user, bagName, callBack);
    };

    this.displayUsers = function(id, users, bagName) {
      var usersl = users.length,
      parent = $('#sharingUsers-' + id).empty();
      for (i = 0; i < usersl; i++) {
        user = users[i];
        addUserSpan(id, user, bagName);
      }
    };

    this.addUserSpan = function(id, user, bagName) {
      var parent = $('#sharingUsers-' + id);
      var span = $('');
      var a = $('[x]');
      a.click(function() {
          deleteUser(user, bagName, id);
      });
      span.append(user).append(a);
      parent.append(span);
    };

}).call(window, jQuery);
<|endoftext|>


<commit_before>function logTestModule(module){
  console.log('\n==========\n' + module + '\n==========\n')
}
function logTestProto(proto){
  console.log('==> ' + proto)
}
var MAGNET = "magnet:?xt=urn:btih:6a9759bffd5c0af65319979fb7832189f4f3c35d&dn=sintel.mp4&tr=wss%3A%2F%2Ftracker.btorrent.xyz&tr=wss%3A%2F%2Ftracker.fastcast.nz&tr=wss%3A%2F%2Ftracker.openwebtorrent.com&tr=wss%3A%2F%2Ftracker.webtorrent.io&ws=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2Fsintel-1024-surround.mp4"

/*
* TEST LOG
*/
logTestModule('Log')
var Log = require('./log.js')

logTestProto('print')
Log.print('Print ok')

logTestProto('echo')
Log.echo('Echo ok')

delete Log
/*
* TEST CLIENT
*/
logTestModule('Client')
var Client = require('./client.js')
var c = new Client()

logTestProto('on start')
c.on('start', function(hash){
  console.log('Client start trigger')
  console.log('Hash: ' + hash)

  logTestProto('getTorrent')
  var torrent = c.getTorrent()
  console.log('Torrent: ')
  console.log(torrent)
})

logTestProto('on download')
c.on('download', function(torrent){
  console.log('Client download trigger')
  console.log('Torrent: ')
  console.log(torrent)

  logTestProto('stop')
  c.stop()
})

logTestProto('on done')
c.on('done', function(hash, name){
  console.log('Client done trigger')
  console.log('Hash: ' + hash + '\nName: ' + name)

  delete Client
  delete c
})

c.download(MAGNET)
<commit_msg>add process exit when each module have finished<commit_after>function logTestModule(module){
  console.log('\n==========\n' + module + '\n==========\n')
}
function logTestProto(proto){
  console.log('==> ' + proto)
}

var MAGNET = "magnet:?xt=urn:btih:6a9759bffd5c0af65319979fb7832189f4f3c35d&dn=sintel.mp4&tr=wss%3A%2F%2Ftracker.btorrent.xyz&tr=wss%3A%2F%2Ftracker.fastcast.nz&tr=wss%3A%2F%2Ftracker.openwebtorrent.com&tr=wss%3A%2F%2Ftracker.webtorrent.io&ws=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2Fsintel-1024-surround.mp4"
var OK = {
  'log': false,
  'client': false
}

var Sleep = require('sleep')

/*
* TEST LOG
*/
logTestModule('Log')
var Log = require('./log.js')

logTestProto('print')
Log.print('Print ok')

logTestProto('echo')
Log.echo('Echo ok')

Sleep.sleep(1)

OK.log = true
delete Log
/*
* TEST CLIENT
*/
logTestModule('Client')
var Client = require('./client.js')
var c = new Client()

logTestProto('on start')
c.on('start', function(hash){
  console.log('Client start trigger')
  console.log('Hash: ' + hash)

  logTestProto('getTorrent')
  var torrent = c.getTorrent()
  console.log('Torrent: ')
  console.log(torrent)
})

logTestProto('on download')
c.on('download', function(torrent){
  console.log('Client download trigger')
  console.log('Torrent: ')
  console.log(torrent)

  logTestProto('stop')
  c.stop()
})

logTestProto('on done')
c.on('done', function(hash, name){
  console.log('Client done trigger')
  console.log('Hash: ' + hash + '\nName: ' + name)

  OK.client = true
  delete Client
  delete c
})

c.download(MAGNET)

var end = true
do {
  end = true
  for (var key in OK){
    end = end && OK[key]
  }
} while (!end)

process.exit(0)
<|endoftext|>


<commit_before>var page = require('webpage').create(),
    system = require('system'),
    t, url;

function checkReadyState(done) {
  setTimeout(function () {
    var readyState = page.evaluate(function () {
      return document.readyState;
    });

    if ("complete" === readyState) {
      done();
    } else {
      checkReadyState(done);
    }
  }, 15000);
}

function pageLoadDone() {
  var htmlContent = page.evaluate(function () {
    function isSupportedAdSize(width, height) {
      return width === 728 && height === 90 ||
        width === 300 && height === 250 ||
        width === 160 && height === 600 ||
        width === 320 && height === 50;
    }

    var iframes = document.querySelectorAll('iframe');
    var iframeSrcs = [];
    for (var i = 0; i < iframes.length; i++) {
      var iframe = iframes[i];
      if (isSupportedAdSize(iframe.clientWidth, iframe.clientHeight)) {
        iframeSrcs.push(iframe.src);
      }
    }
    return iframeSrcs;
  });

  console.log(htmlContent.length);
  phantom.exit();
}

if (system.args.length === 1) {
    console.log('Usage: test.js <some URL>');
    phantom.exit(1);
} else {
  t = Date.now();
  url = system.args[1];
  page.open(url, function (status) {
    checkReadyState(pageLoadDone);
  });
}
<commit_msg>Add stack trace and more iframe detail<commit_after>var page = require('webpage').create(),
    system = require('system');

function checkReadyState(done) {
  setTimeout(function () {
    var readyState = page.evaluate(function () {
      return document.readyState;
    });

    if ("complete" === readyState) {
      done();
    } else {
      checkReadyState(done);
    }
  }, 15000);
}

function pageLoadDone() {
  var iframesData = page.evaluate(function () {
    function isSupportedAdSize(width, height) {
      return width === 728 && height === 90 ||
        width === 300 && height === 250 ||
        width === 160 && height === 600 ||
        width === 320 && height === 50;
    }

    var iframes = document.querySelectorAll('iframe');
    var iframesData = [];
    for (var i = 0; i < iframes.length; i++) {
      var iframe = iframes[i];
      var rect = iframe.getBoundingClientRect();
      if (isSupportedAdSize(rect.width, rect.height)) {
        iframesData.push({
          src: iframe.src,
          width: rect.width,
          height: rect.height,
          top: rect.top,
          left: rect.left
        });
      }
    }
    return iframesData;
  });

  for (var i = 0; i < iframesData.length; i++) {
    var iframeData = iframesData[i];
    console.log(JSON.stringify(iframeData));
  }
  phantom.exit();
}

if (system.args.length === 1) {
  console.log('Usage: test.js <some URL>');
  phantom.exit(1);
}

function main() {
  var url = system.args[1];
  page.onConsoleMessage = function(msg, lineNum, sourceId) {
    console.log('CONSOLE: ' + msg + ' (from line #' + lineNum + ' in "' + sourceId + '")');
  };
  page.onError = function(msg, trace) {
    var msgStack = ['ERROR: ' + msg];
    if (trace && trace.length) {
      msgStack.push('TRACE:');
      trace.forEach(function(t) {
        msgStack.push(' -> ' + t.file + ': ' + t.line + (t.function ? ' (in function "' + t.function +'")' : ''));
      });
    }
    console.error(msgStack.join('\n'));
  };

  page.open(url, function (status) {
    checkReadyState(pageLoadDone);
  });
}

main();
<|endoftext|>


<commit_before>/*
 * thar - Automatic content anchors link plugin for jQuery
 * https://github.com/gocom/thar
 *
 * Copyright (C) 2013 Jukka Svahn
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/**
 * @name jQuery
 * @class
 */

/**
 * @name fn
 * @class
 * @memberOf jQuery
 */

;(function (factory)
{
    if (typeof define === 'function' && define.amd)
    {
        define(['jquery'], factory);
    }
    else
    {
        factory(jQuery);
    }
}(function ($)
{
    var hash = window.location.hash.substr(1), methods = {};

    /**
     * Sets an anchor ID from the element text contents.
     *
     * @return   {Object}         this
     * @method   setAnchorID
     * @memberof jQuery.fn.thar
     */

    methods.setAnchorID = function ()
    {
        return this.each(function ()
        {
            var $this = $(this), id = $this.attr('id'), occurrences = 1, uniqueID;

            if (!id)
            {
                uniqueID = id = encodeURIComponent($this.text().replace(/\s/g, '-'))
                    .replace(/[^A-Z0-9\-]/gi, '-')
                    .substr(0, 255)
                    .replace(/^[\d\-]|-$/g, '')
                    .replace(/-{2,}/g, '-')
                    .toLowerCase() || 'thar';

                while (1)
                {
                    if (!document.getElementById(uniqueID))
                    {
                        $this.attr('id', uniqueID);
                        return;
                    }

                    uniqueID = id + '-' + (occurrences++);
                }
            }
        });
    };

    /**
     * Scrolls to the selected element.
     *
     * @return   {Object}         this
     * @method   scrollTo
     * @memberof jQuery.fn.thar
     */

    methods.scrollTo = function ()
    {
        var $this = $(this).eq(0), id = $this.attr('id');

        $('html, body').animate({
            scrollTop : $this.offset().top
        }, 1000, 'swing', function ()
        {
            if (id)
            {
                window.location.hash = '#' + id;
            }
        });

        return this;
    };

    /**
     * Gets a contents listing.
     *
     * This method can be used to generate menus and
     * content tables based on headings.
     *
     * The content listing is added to the target element
     * specified with the target option. If
     * the target is left to its default NULL,
     * the list is added before the first heading.
     *
     * @param    {Object}       [options={}]          Options
     * @param    {Object|null}  [options.target=null] The target where the list is added to, takes an jQuery object
     * @return   {Object}       this
     * @method   getContentList
     * @memberof jQuery.fn.thar
     * @example
     * $('h2, h3, h4, h5, h6').thar('getContentList', {
     *  'target' : $('nav#contents')
     * });
     */

    methods.getContentList = function (options)
    {
        options = $.extend({
            target : null
        }, options);

        var previousLevel = 0, contents = '', baseLevel = this.get(0).nodeName.substr(1, 1), target = this.filter('h1, h2, h3, h4, h5, h6');

        target.each(function ()
        {
            var $this = $(this), level = this.nodeName.substr(1, 1);

            if (level > baseLevel || !$this.attr('id'))
            {
                return;
            }

            if (!previousLevel)
            {
                contents += '
';
            }
            else if (level == previousLevel)
            {
                contents += '

';
            }
            else if (level > previousLevel)
            {
                contents += new Array(level - previousLevel + 1).join('

');
            }
            else if (level < previousLevel)
            {
                contents += new Array(level - previousLevel + 1).join('

') + '

';
            }

            previousLevel = level;
            contents += $('
').html($('').text($this.clone().find('.jquery-thar-anchor').remove().end().text()).attr('href', '#' + $this.attr('id'))).html();
        });

        if (contents)
        {
            contents = $('
' + contents + new Array(previousLevel - baseLevel + 1).join('

') + '

');
            contents.find('a').on('click', function (e)
            {
                var id = $(this).attr('href').substr(1);
                e.preventDefault();
                methods.scrollTo.apply(
                    target.filter(function ()
                    {
                        return $(this).attr('id') === id;
                    })
                );
            });

            if (!options.target)
            {
                this.eq(0).before(contents);
            }
            else
            {
                options.target.html(contents);
            }
        }

        return this;
    };

    /**
     * Renders content anchor links.
     *
     * @param    {Object}         [options={}]              Options
     * @param    {String}         [options.prefix='']       A prefix added to the generated links
     * @param    {String|Boolean} [options.anchor=>#167] The anchor link text
     * @return   {Object}         this
     * @method   init
     * @memberof jQuery.fn.thar
     */

    methods.init = function (options)
    {
        options = $.extend({
            'anchor' : '§'
        }, options);

        return this.thar('setAnchorID').each(function ()
        {
            var _this = this, $this = $(this), id = $this.attr('id'), anchor, content = $this.text();

            if ($this.hasClass('jquery-thar'))
            {
                return;
            }

            $this.addClass('jquery-thar').trigger('anchorcreate.thar');

            if (id === hash)
            {
                methods.scrollTo.apply(this);

                $this.trigger('anchorload.thar', {
                    'id' : id
                });

                hash = false;
            }

            if (options.anchor !== false)
            {
                anchor = $('')
                    .attr('href', '#' + id)
                    .on('click.thar', function (e)
                    {
                        e.preventDefault();
                        methods.scrollTo.apply(_this);
                    });

                if (options.anchor === true)
                {
                    $this.wrapInner(anchor);
                }
                else
                {
                    $this.prepend(anchor.html(options.anchor)).prepend(' ');
                }
            }
        });
    };

    /**
     * Renders content anchor links.
     *
     * @param    {String}  [method=init] The called method
     * @param    {Object}  [options={}]  Options passed to the method
     * @class    thar
     * @memberof jQuery.fn
     */

    $.fn.thar = function (method, options)
    {
        if ($.type(method) !== 'string' || $.type(methods[method]) !== 'function')
        {
            options = method;
            method = 'init';
        }

        return methods[method].call(this, options);
    };
}));<commit_msg>Being public method, it wants jQuery objects.<commit_after>/*
 * thar - Automatic content anchors link plugin for jQuery
 * https://github.com/gocom/thar
 *
 * Copyright (C) 2013 Jukka Svahn
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/**
 * @name jQuery
 * @class
 */

/**
 * @name fn
 * @class
 * @memberOf jQuery
 */

;(function (factory)
{
    if (typeof define === 'function' && define.amd)
    {
        define(['jquery'], factory);
    }
    else
    {
        factory(jQuery);
    }
}(function ($)
{
    var hash = window.location.hash.substr(1), methods = {};

    /**
     * Sets an anchor ID from the element text contents.
     *
     * @return   {Object}         this
     * @method   setAnchorID
     * @memberof jQuery.fn.thar
     */

    methods.setAnchorID = function ()
    {
        return this.each(function ()
        {
            var $this = $(this), id = $this.attr('id'), occurrences = 1, uniqueID;

            if (!id)
            {
                uniqueID = id = encodeURIComponent($this.text().replace(/\s/g, '-'))
                    .replace(/[^A-Z0-9\-]/gi, '-')
                    .substr(0, 255)
                    .replace(/^[\d\-]|-$/g, '')
                    .replace(/-{2,}/g, '-')
                    .toLowerCase() || 'thar';

                while (1)
                {
                    if (!document.getElementById(uniqueID))
                    {
                        $this.attr('id', uniqueID);
                        return;
                    }

                    uniqueID = id + '-' + (occurrences++);
                }
            }
        });
    };

    /**
     * Scrolls to the selected element.
     *
     * @return   {Object}         this
     * @method   scrollTo
     * @memberof jQuery.fn.thar
     */

    methods.scrollTo = function ()
    {
        var $this = this.eq(0), id = $this.attr('id');

        $('html, body').animate({
            scrollTop : $this.offset().top
        }, 1000, 'swing', function ()
        {
            if (id)
            {
                window.location.hash = '#' + id;
            }
        });

        return this;
    };

    /**
     * Gets a contents listing.
     *
     * This method can be used to generate menus and
     * content tables based on headings.
     *
     * The content listing is added to the target element
     * specified with the target option. If
     * the target is left to its default NULL,
     * the list is added before the first heading.
     *
     * @param    {Object}       [options={}]          Options
     * @param    {Object|null}  [options.target=null] The target where the list is added to, takes an jQuery object
     * @return   {Object}       this
     * @method   getContentList
     * @memberof jQuery.fn.thar
     * @example
     * $('h2, h3, h4, h5, h6').thar('getContentList', {
     *  'target' : $('nav#contents')
     * });
     */

    methods.getContentList = function (options)
    {
        options = $.extend({
            target : null
        }, options);

        var previousLevel = 0, contents = '', baseLevel = this.get(0).nodeName.substr(1, 1), target = this.filter('h1, h2, h3, h4, h5, h6');

        target.each(function ()
        {
            var $this = $(this), level = this.nodeName.substr(1, 1);

            if (level > baseLevel || !$this.attr('id'))
            {
                return;
            }

            if (!previousLevel)
            {
                contents += '
';
            }
            else if (level == previousLevel)
            {
                contents += '

';
            }
            else if (level > previousLevel)
            {
                contents += new Array(level - previousLevel + 1).join('

');
            }
            else if (level < previousLevel)
            {
                contents += new Array(level - previousLevel + 1).join('

') + '

';
            }

            previousLevel = level;
            contents += $('
').html($('').text($this.clone().find('.jquery-thar-anchor').remove().end().text()).attr('href', '#' + $this.attr('id'))).html();
        });

        if (contents)
        {
            contents = $('
' + contents + new Array(previousLevel - baseLevel + 1).join('

') + '

');
            contents.find('a').on('click', function (e)
            {
                var id = $(this).attr('href').substr(1);
                e.preventDefault();
                methods.scrollTo.apply(
                    target.filter(function ()
                    {
                        return $(this).attr('id') === id;
                    })
                );
            });

            if (!options.target)
            {
                this.eq(0).before(contents);
            }
            else
            {
                options.target.html(contents);
            }
        }

        return this;
    };

    /**
     * Renders content anchor links.
     *
     * @param    {Object}         [options={}]              Options
     * @param    {String}         [options.prefix='']       A prefix added to the generated links
     * @param    {String|Boolean} [options.anchor=>#167] The anchor link text
     * @return   {Object}         this
     * @method   init
     * @memberof jQuery.fn.thar
     */

    methods.init = function (options)
    {
        options = $.extend({
            'anchor' : '§'
        }, options);

        return this.thar('setAnchorID').each(function ()
        {
            var $this = $(this), id = $this.attr('id'), anchor, content = $this.text();

            if ($this.hasClass('jquery-thar'))
            {
                return;
            }

            $this.addClass('jquery-thar').trigger('anchorcreate.thar');

            if (id === hash)
            {
                methods.scrollTo.apply($this);

                $this.trigger('anchorload.thar', {
                    'id' : id
                });

                hash = false;
            }

            if (options.anchor !== false)
            {
                anchor = $('')
                    .attr('href', '#' + id)
                    .on('click.thar', function (e)
                    {
                        e.preventDefault();
                        methods.scrollTo.apply($this);
                    });

                if (options.anchor === true)
                {
                    $this.wrapInner(anchor);
                }
                else
                {
                    $this.prepend(anchor.html(options.anchor)).prepend(' ');
                }
            }
        });
    };

    /**
     * Renders content anchor links.
     *
     * @param    {String}  [method=init] The called method
     * @param    {Object}  [options={}]  Options passed to the method
     * @class    thar
     * @memberof jQuery.fn
     */

    $.fn.thar = function (method, options)
    {
        if ($.type(method) !== 'string' || $.type(methods[method]) !== 'function')
        {
            options = method;
            method = 'init';
        }

        return methods[method].call(this, options);
    };
}));<|endoftext|>


<commit_before>var _ = require('squishy-pants/lib/squishy-pants');

//
//   # Time
//
//   The time type encodes the presence of a value of time.
//
//   * `ap(s)` - Applicative ap(ply)
//   * `chain(f)` - Monadic flatMap/bind
//   * `concat(s, plus)` - Semigroup concat
//   * `equal(a)` -  `true` if `a` is equal to `this`
//   * `extract()` -  extract the value from time
//   * `fold(h, m, s, ms)` - Applies functions to value of time.
//   * `map(f)` - Functor map
//
var Time = _.taggedSum('Time', {
    Hours: ['hours'],
    Minutes: ['minutes'],
    Seconds: ['seconds'],
    Milliseconds: ['milliseconds']
});

//
//  ### chain(f)
//
//  Bind through the value of the time
//  Monadic flatMap/bind
//
Time.prototype.chain = function(f) {
    return this.fold(f, f, f, f);
};

//
//  ### concat(s)
//
//  Concatenate two times associatively together.
//  Semigroup concat
//
Time.prototype.concat = function(s) {
    var env = this,
        value = env.extract();
    return _.map(
            s,
            function(y) {
                return _.concat(value, y);
            }
        );
};

//
//  ### extract()
//
//  Extract the value from the time.
//
Time.prototype.extract = function() {
    return this.match({
        Hours: function(a) {
            return a * 1000 * 60 * 60;
        },
        Minutes: function(a) {
            return a * 1000 * 60;
        },
        Seconds: function(a) {
            return a * 1000;
        },
        Milliseconds: _.identity
    });
};

//
//  ### fold(h, m, s, ms)
//
//  Catamorphism. Run the first given function if time, otherwise,
//  the run subsequent functions
//
Time.prototype.fold = function(h, m, s, ms) {
    return this.match({
        Hours: h,
        Minutes: m,
        Seconds: s,
        Milliseconds: ms,
    });
};

//
//  ### map(f)
//
//  Map on the value of this time.
//  Functor map
//
Time.prototype.map = function(f) {
    var env = this,
        value = env.extract();

    return env.match({
        Hours: function(a) {
            return Time.Hours(f(value) / 1000 / 60 / 60);
        },
        Minutes: function(a) {
            return Time.Minutes(f(value) / 1000 / 60);
        },
        Seconds: function(a) {
            return Time.Seconds(f(value) / 1000);
        },
        Milliseconds: function(a) {
            return Time.Milliseconds(f(a));
        }
    });
};

var isTime = _.isInstanceOf(Time);

//
//  ## hoursOf()
//
//  Sentinel value for when an hours of a particular type is needed:
//
//       hoursOf()
//
function hoursOf() {
    var self = _.getInstance(this, hoursOf);
    self.type = Number;
    return self;
}

//
//  ## isHoursOf(a)
//
//  Returns `true` if `a` is an instance of `hoursOf`.
//
var isHoursOf = _.isInstanceOf(hoursOf);

//
//  ## minutesOf()
//
//  Sentinel value for when an minutes of a particular type is needed:
//
//       minutesOf()
//
function minutesOf() {
    var self = _.getInstance(this, minutesOf);
    self.type = Number;
    return self;
}

//
//  ## isMinutesOf(a)
//
//  Returns `true` if `a` is an instance of `minutesOf`.
//
var isMinutesOf = _.isInstanceOf(minutesOf);

//
//  ## secondsOf()
//
//  Sentinel value for when an seconds of a particular type is needed:
//
//       secondsOf()
//
function secondsOf() {
    var self = _.getInstance(this, secondsOf);
    self.type = Number;
    return self;
}

//
//  ## isSecondsOf(a)
//
//  Returns `true` if `a` is an instance of `secondsOf`.
//
var isSecondsOf = _.isInstanceOf(secondsOf);

//
//  ## millisecondsOf()
//
//  Sentinel value for when an milliseconds of a particular type is needed:
//
//       millisecondsOf()
//
function millisecondsOf() {
    var self = _.getInstance(this, millisecondsOf);
    self.type = Number;
    return self;
}

//
//  ## isMillisecondsOf(a)
//
//  Returns `true` if `a` is an instance of `millisecondsOf`.
//
var isMillisecondsOf = _.isInstanceOf(millisecondsOf);

var inc = _.curry(function(t, x, v) {
    return t + (x * v);
});

var dec = _.curry(function(t, x, v) {
    return t - (x * v);
});

//
//  ## incHours(a, v)
//
//  Increments the number by v
//
var incHours = inc(360000);

//
//  ## incHour(a)
//
//  Increments the number by 1
//
var incHour = incHours(1);

//
//  ## incMinutes(a, v)
//
//  Increments the number by v
//
var incMinutes = inc(60000);

//
//  ## incMinute(a)
//
//  Increments the number by 1
//
var incMinute = incMinutes(1);

//
//  ## incSeconds(a, v)
//
//  Increments the number by v
//
var incSeconds = inc(1000);

//
//  ## incSecond(a)
//
//  Increments the number by 1
//
var incSecond = incSeconds(1);

//
//  ## incMilliseconds(a, v)
//
//  Increments the number by v
//
var incMilliseconds = inc(1);

//
//  ## incMillisecond(a)
//
//  Increments the number by 1
//
var incMillisecond = incMilliseconds(1);

//
//  ## decHours(a, v)
//
//  Decrements the number by v
//
var decHours = dec(360000);

//
//  ## decHour(a)
//
//  Decrements the number by 1
//
var decHour = decHours(1);

//
//  ## decMinutes(a, v)
//
//  Decrements the number by v
//
var decMinutes = dec(60000);

//
//  ## decMinute(a)
//
//  Decrements the number by 1
//
var decMinute = decMinutes(1);

//
//  ## decSeconds(a, v)
//
//  Decrements the number by v
//
var decSeconds = dec(1000);

//
//  ## decSecond(a)
//
//  Decrements the number by 1
//
var decSecond = decSeconds(1);

//
//  ## decMilliseconds(a, v)
//
//  Decrements the number by v
//
var decMilliseconds = dec(1);

//
//  ## decMillisecond(a)
//
//  Decrements the number by 1
//
var decMillisecond = decMilliseconds(1);

//
//  ### Fantasy Overload
//
_.fo.unsafeSetValueOf(Time.prototype);

//
//  append methods to the environment.
//
_ = _
  .property('Time', Time)
  .property('Hours', Time.Hours)
  .property('Minutes', Time.Minutes)
  .property('Seconds', Time.Seconds)
  .property('Milliseconds', Time.Milliseconds)
  .property('hoursOf', hoursOf)
  .property('minutesOf', minutesOf)
  .property('secondsOf', secondsOf)
  .property('millisecondsOf', millisecondsOf)
  .property('isHoursOf', isHoursOf)
  .property('isMinutesOf', isMinutesOf)
  .property('isSecondsOf', isSecondsOf)
  .property('isMillisecondsOf', isMillisecondsOf)
  .property('incHours', incHours)
  .property('incHour', incHour)
  .property('incMinutes', incMinutes)
  .property('incMinute', incMinute)
  .property('incSeconds', incSeconds)
  .property('incSecond', incSecond)
  .property('incMilliseconds', incMilliseconds)
  .property('incMillisecond', incMillisecond)
  .property('decHours', decHours)
  .property('decHour', decHour)
  .property('decMinutes', decMinutes)
  .property('decMinute', decMinute)
  .property('decSeconds', decSeconds)
  .property('decSecond', decSecond)
  .property('decMilliseconds', decMilliseconds)
  .property('decMillisecond', decMillisecond)
  .method('chain', isTime, function(a, b) {
      return a.chain(b);
  })
  .method('extract', isTime, function(a) {
      return a.extract();
  })
  .method('fold', isTime, function(a, b, c, d, e) {
      return a.fold(b, c, d, e);
  })
  .method('map', isTime, function(a, b) {
      return a.map(b);
  });

exports = module.exports = _;<commit_msg>Adding concat to exporting to squishy.<commit_after>var _ = require('squishy-pants/lib/squishy-pants');

//
//   # Time
//
//   The time type encodes the presence of a value of time.
//
//   * `ap(s)` - Applicative ap(ply)
//   * `chain(f)` - Monadic flatMap/bind
//   * `concat(s, plus)` - Semigroup concat
//   * `equal(a)` -  `true` if `a` is equal to `this`
//   * `extract()` -  extract the value from time
//   * `fold(h, m, s, ms)` - Applies functions to value of time.
//   * `map(f)` - Functor map
//
var Time = _.taggedSum('Time', {
    Hours: ['hours'],
    Minutes: ['minutes'],
    Seconds: ['seconds'],
    Milliseconds: ['milliseconds']
});

//
//  ### chain(f)
//
//  Bind through the value of the time
//  Monadic flatMap/bind
//
Time.prototype.chain = function(f) {
    return this.fold(f, f, f, f);
};

//
//  ### concat(s)
//
//  Concatenate two times associatively together.
//  Semigroup concat
//
Time.prototype.concat = function(s) {
    var env = this,
        value = env.extract();
    return _.map(
            s,
            function(y) {
                return _.concat(value, y);
            }
        );
};

//
//  ### extract()
//
//  Extract the value from the time.
//
Time.prototype.extract = function() {
    return this.match({
        Hours: function(a) {
            return a * 1000 * 60 * 60;
        },
        Minutes: function(a) {
            return a * 1000 * 60;
        },
        Seconds: function(a) {
            return a * 1000;
        },
        Milliseconds: _.identity
    });
};

//
//  ### fold(h, m, s, ms)
//
//  Catamorphism. Run the first given function if time, otherwise,
//  the run subsequent functions
//
Time.prototype.fold = function(h, m, s, ms) {
    return this.match({
        Hours: h,
        Minutes: m,
        Seconds: s,
        Milliseconds: ms,
    });
};

//
//  ### map(f)
//
//  Map on the value of this time.
//  Functor map
//
Time.prototype.map = function(f) {
    var env = this,
        value = env.extract();

    return env.match({
        Hours: function(a) {
            return Time.Hours(f(value) / 1000 / 60 / 60);
        },
        Minutes: function(a) {
            return Time.Minutes(f(value) / 1000 / 60);
        },
        Seconds: function(a) {
            return Time.Seconds(f(value) / 1000);
        },
        Milliseconds: function(a) {
            return Time.Milliseconds(f(a));
        }
    });
};

var isTime = _.isInstanceOf(Time);

//
//  ## hoursOf()
//
//  Sentinel value for when an hours of a particular type is needed:
//
//       hoursOf()
//
function hoursOf() {
    var self = _.getInstance(this, hoursOf);
    self.type = Number;
    return self;
}

//
//  ## isHoursOf(a)
//
//  Returns `true` if `a` is an instance of `hoursOf`.
//
var isHoursOf = _.isInstanceOf(hoursOf);

//
//  ## minutesOf()
//
//  Sentinel value for when an minutes of a particular type is needed:
//
//       minutesOf()
//
function minutesOf() {
    var self = _.getInstance(this, minutesOf);
    self.type = Number;
    return self;
}

//
//  ## isMinutesOf(a)
//
//  Returns `true` if `a` is an instance of `minutesOf`.
//
var isMinutesOf = _.isInstanceOf(minutesOf);

//
//  ## secondsOf()
//
//  Sentinel value for when an seconds of a particular type is needed:
//
//       secondsOf()
//
function secondsOf() {
    var self = _.getInstance(this, secondsOf);
    self.type = Number;
    return self;
}

//
//  ## isSecondsOf(a)
//
//  Returns `true` if `a` is an instance of `secondsOf`.
//
var isSecondsOf = _.isInstanceOf(secondsOf);

//
//  ## millisecondsOf()
//
//  Sentinel value for when an milliseconds of a particular type is needed:
//
//       millisecondsOf()
//
function millisecondsOf() {
    var self = _.getInstance(this, millisecondsOf);
    self.type = Number;
    return self;
}

//
//  ## isMillisecondsOf(a)
//
//  Returns `true` if `a` is an instance of `millisecondsOf`.
//
var isMillisecondsOf = _.isInstanceOf(millisecondsOf);

var inc = _.curry(function(t, x, v) {
    return t + (x * v);
});

var dec = _.curry(function(t, x, v) {
    return t - (x * v);
});

//
//  ## incHours(a, v)
//
//  Increments the number by v
//
var incHours = inc(360000);

//
//  ## incHour(a)
//
//  Increments the number by 1
//
var incHour = incHours(1);

//
//  ## incMinutes(a, v)
//
//  Increments the number by v
//
var incMinutes = inc(60000);

//
//  ## incMinute(a)
//
//  Increments the number by 1
//
var incMinute = incMinutes(1);

//
//  ## incSeconds(a, v)
//
//  Increments the number by v
//
var incSeconds = inc(1000);

//
//  ## incSecond(a)
//
//  Increments the number by 1
//
var incSecond = incSeconds(1);

//
//  ## incMilliseconds(a, v)
//
//  Increments the number by v
//
var incMilliseconds = inc(1);

//
//  ## incMillisecond(a)
//
//  Increments the number by 1
//
var incMillisecond = incMilliseconds(1);

//
//  ## decHours(a, v)
//
//  Decrements the number by v
//
var decHours = dec(360000);

//
//  ## decHour(a)
//
//  Decrements the number by 1
//
var decHour = decHours(1);

//
//  ## decMinutes(a, v)
//
//  Decrements the number by v
//
var decMinutes = dec(60000);

//
//  ## decMinute(a)
//
//  Decrements the number by 1
//
var decMinute = decMinutes(1);

//
//  ## decSeconds(a, v)
//
//  Decrements the number by v
//
var decSeconds = dec(1000);

//
//  ## decSecond(a)
//
//  Decrements the number by 1
//
var decSecond = decSeconds(1);

//
//  ## decMilliseconds(a, v)
//
//  Decrements the number by v
//
var decMilliseconds = dec(1);

//
//  ## decMillisecond(a)
//
//  Decrements the number by 1
//
var decMillisecond = decMilliseconds(1);

//
//  ### Fantasy Overload
//
_.fo.unsafeSetValueOf(Time.prototype);

//
//  append methods to the environment.
//
_ = _
  .property('Time', Time)
  .property('Hours', Time.Hours)
  .property('Minutes', Time.Minutes)
  .property('Seconds', Time.Seconds)
  .property('Milliseconds', Time.Milliseconds)
  .property('hoursOf', hoursOf)
  .property('minutesOf', minutesOf)
  .property('secondsOf', secondsOf)
  .property('millisecondsOf', millisecondsOf)
  .property('isHoursOf', isHoursOf)
  .property('isMinutesOf', isMinutesOf)
  .property('isSecondsOf', isSecondsOf)
  .property('isMillisecondsOf', isMillisecondsOf)
  .property('incHours', incHours)
  .property('incHour', incHour)
  .property('incMinutes', incMinutes)
  .property('incMinute', incMinute)
  .property('incSeconds', incSeconds)
  .property('incSecond', incSecond)
  .property('incMilliseconds', incMilliseconds)
  .property('incMillisecond', incMillisecond)
  .property('decHours', decHours)
  .property('decHour', decHour)
  .property('decMinutes', decMinutes)
  .property('decMinute', decMinute)
  .property('decSeconds', decSeconds)
  .property('decSecond', decSecond)
  .property('decMilliseconds', decMilliseconds)
  .property('decMillisecond', decMillisecond)
  .method('chain', isTime, function(a, b) {
      return a.chain(b);
  })
  .method('concat', isTime, function(a, b) {
      return a.concat(b);
  })
  .method('extract', isTime, function(a) {
      return a.extract();
  })
  .method('fold', isTime, function(a, b, c, d, e) {
      return a.fold(b, c, d, e);
  })
  .method('map', isTime, function(a, b) {
      return a.map(b);
  });

exports = module.exports = _;<|endoftext|>


<commit_before>var React = require('react'),
	DOM = React.DOM;

var languagesComponent = require(__dirname + '/Languages');
var plainTextComponent = require(__dirname + '/PlainText');
var skillsComponent = require(__dirname + '/Skills');
var workComponent = require(__dirname + '/Work');
var educationItemComponent = require(__dirname + '/EducationItem');
var footerComponent = require(__dirname + '/Footer');

module.exports = React.createClass({

	propTypes: {
		jsonResume: React.PropTypes.object
	},

	render: function () {

		var components = [];

		components.push(DOM.div({ className: 'page-header' },
			DOM.h1(null, [
				DOM.div(null, this.props.jsonResume.basics.name),
				DOM.small(null, ' ' + this.props.jsonResume.basics.label)
			])
		));

		components.push(
			plainTextComponent({ title: 'About', body: this.props.jsonResume.basics.summary })
		);

		components.push(skillsComponent({ title: 'Skills', items: this.props.jsonResume.skills }));

		components.push(workComponent({ title: 'Work', work: this.props.jsonResume.work }));

		// Education
		// TODO Create an Education component and move this in there. Or be generic?
		components.push(
			DOM.div({ className: 'educationContainer' }, [
				DOM.div({ className: 'row' },
					DOM.div({ className: 'col-xs-12' },
						DOM.h2(null, 'Education')
					)
				),
				DOM.div({ className: 'row' },
					DOM.div({ className: 'col-xs-12' },
						this.props.jsonResume.education.map(function (education) {
							return educationItemComponent(education);
						})
					)
				)
			])
		);

		// Optional, human languages component.
		if (this.props.jsonResume.languages && this.props.jsonResume.languages.length > 0) {
			components.push(languagesComponent({
				languages: this.props.jsonResume.languages
			}));
		}

		// Footer
		components.push(
			footerComponent({
				author: this.props.jsonResume.basics.name,
				email: this.props.jsonResume.basics.email,
				githubUrl: 'https://github.com/ScottMaclure/scott-cv/'
			})
		);

		return DOM.div(
			{ className: 'container-fluid' },
			components
		);

	}

});<commit_msg>[#2] Adjusted DOM layout for nicer img fit.<commit_after>var React = require('react'),
	DOM = React.DOM;

var languagesComponent = require(__dirname + '/Languages');
var plainTextComponent = require(__dirname + '/PlainText');
var skillsComponent = require(__dirname + '/Skills');
var workComponent = require(__dirname + '/Work');
var educationItemComponent = require(__dirname + '/EducationItem');
var footerComponent = require(__dirname + '/Footer');

module.exports = React.createClass({

	propTypes: {
		jsonResume: React.PropTypes.object
	},

	render: function () {

		var components = [];

		components.push(
			DOM.div({ className: 'page-header' }, [
				DOM.div({ className: 'row' }, [
					DOM.div({ className: 'col-xs-12 col-sm-10' },
						DOM.h1(null, [
							DOM.div(null, this.props.jsonResume.basics.name),
							DOM.small(null, ' ' + this.props.jsonResume.basics.label)
						])
					),
					DOM.div({ className: 'col-xs-12 col-sm-2 hidden-xs text-right' },
						DOM.img({ className: 'gravatar', src: this.props.jsonResume.basics.picture })
					)
				])
			])
		);

		components.push(
			plainTextComponent({ title: 'About', body: this.props.jsonResume.basics.summary })
		);

		components.push(skillsComponent({ title: 'Skills', items: this.props.jsonResume.skills }));

		components.push(workComponent({ title: 'Work', work: this.props.jsonResume.work }));

		// Education
		// TODO Create an Education component and move this in there. Or be generic?
		components.push(
			DOM.div({ className: 'educationContainer' }, [
				DOM.div({ className: 'row' },
					DOM.div({ className: 'col-xs-12' },
						DOM.h2(null, 'Education')
					)
				),
				DOM.div({ className: 'row' },
					DOM.div({ className: 'col-xs-12' },
						this.props.jsonResume.education.map(function (education) {
							return educationItemComponent(education);
						})
					)
				)
			])
		);

		// Optional, human languages component.
		if (this.props.jsonResume.languages && this.props.jsonResume.languages.length > 0) {
			components.push(languagesComponent({
				languages: this.props.jsonResume.languages
			}));
		}

		// Footer
		components.push(
			footerComponent({
				author: this.props.jsonResume.basics.name,
				email: this.props.jsonResume.basics.email,
				githubUrl: 'https://github.com/ScottMaclure/scott-cv/'
			})
		);

		return DOM.div(
			{ className: 'container-fluid' },
			components
		);

	}

});<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { Linking, Alert, TouchableOpacity, Text, View, Image, TextInput, ListView, TouchableHighlight } from 'react-native';
import helpers from '../helpers/helpers.js';
import styles from '../styles.ios.js';
import AddRecipe from './AddRecipe.ios.js';
import ShareFacebook from './ShareFacebook.ios.js';
import { connect } from "react-redux";

class Recipe extends Component {
  constructor(props) {
    super(props);
    let steps = props.recipe.analyzedInstructions[0] ? props.recipe.analyzedInstructions[0].steps : [];
    const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    this.state = {
      steps: ds.cloneWithRows(steps),
      ingredients: []
    }
  }

  componentWillMount() {
    this.renderIngredients();
    this.renderOptions();
  }

  goBack() {
    this.props.navigator.pop();
  }

  renderIngredients() {
    let ingredients = [];
    for (let ingredient of this.props.recipe.extendedIngredients) {
      ingredients.push(ingredient.originalString);
    }
    this.setState({
      ingredients: ingredients
    })
  }

  renderOptions() {
    const recipe = this.props.recipe;
    recipe.vegan = recipe.vegan ? '∙ Vegan' : false;
    recipe.glutenFree = recipe.glutenFree ? '∙ Gluten Free' : false;
    recipe.dairyFree = recipe.dairyFree ? '∙ Dairy Free' : false;
    if (!recipe.vegan && !recipe.glutenFree && !recipe.dairyFree) {
      recipe.dairyFree = 'No Dietary Restrictions';
    }
    recipe.cheap = recipe.cheap ? '$' : '$$$';
    recipe.sustainable = recipe.sustainable ? '∙ Recipe Sustainable' : false;
    recipe.servings = recipe.servings ? recipe.servings : 'Not available';
    recipe.healthscore = recipe.healthscore ? recipe.healthscore : 'Not available';
    // recipe.sourceUrl
  }

  //DRY click handler for social media deep linking
  shareToSocialMedia(deepLink, appName, storeURL) {
    //deep link into the provided app if installed, else direct user to the proper app store link
    Linking.canOpenURL(appName.toLowerCase() + '://').then(supported => {
      if (!supported) {
        Alert.alert(`You must install the ${appName} app in order to use this feature.`,
                    '',
                    [{text: `Install ${appName}`, onPress: () => Linking.openURL(storeURL)},
                     {text: 'Not Now'}]
        );
      } else {
        return Linking.openURL(deepLink);
      }
    }).catch(err => /*Alert.alert('Error; ', err), */console.error('Error: ', err));
  }

  render() {
    const ingredients = this.state.ingredients.map((ingredient, i) => {
      return (
        <Text style = {styles.ingredientListText2} key={i}>∙ {ingredient} </Text>
      )
    });
    const recipe = this.props.recipe;

    return (
      <View style={styles.recipe}>
        <View style={styles.resultsTitle}>
          <TouchableHighlight style={styles.backButton} onPress={this.goBack.bind(this)}>
            <Image style={styles.backButtonImage} source={{uri: 'https://cdn0.iconfinder.com/data/icons/vector-basic-tab-bar-icons/48/back_button-128.png'}} />
          </TouchableHighlight>
          <Text style={styles.resultsTitleText}>Instructions for {this.props.food}:</Text>
        </View>
        <View style={styles.ingredientContainer}>
          <View style={styles.ingredientList}>
            <Text> {recipe.vegan} {recipe.glutenFree} {recipe.dairyFree}</Text>
            <Text> {recipe.cheap} {recipe.sustainable} Servings: {recipe.servings} Healthscore: {recipe.healthscore}</Text>
            <Text style={styles.recipeTitle2}>Ingredients:</Text>
            <Text style={styles.ingredientListText2}>{ingredients}</Text>
          </View>
          <View style={styles.recipeImageAndIcons}>
            <Image source={{uri: this.props.image}} style={styles.recipeImage} />
            <View style={styles.iconsContainer}>
              {
                this.props.state.isAuthenticated
                  ? (<ShareFacebook url={this.props.recipe.sourceUrl} />)
                  : (<Text></Text>)
              }
              <TouchableOpacity onPress={
                () => this.shareToSocialMedia('instagram://camera', 'Instagram', 'https://itunes.apple.com/us/app/instagram/id389801252?mt=8')
              }>
                <Image source={require('../public/instagram_icon.png')} style={styles.shareIcons} />
              </TouchableOpacity>
              <TouchableOpacity onPress={
                () => this.shareToSocialMedia(
                  `twitter://post?message=I%20just%20made%20${this.props.food.split(' ').join('%20')}%20with%20a%20little%20help%20from%20the%20app%20Snacktime!
                  ${this.props.recipe.sourceUrl}`,
                  'Twitter',
                  'https://itunes.apple.com/us/app/twitter/id333903271?mt=8'
                )
              }>
                <Image source={require('../public/twitter_icon.png')} style={styles.shareIcons} />
              </TouchableOpacity>
            </View>
            <AddRecipe info={this.props.recipe} ingredients={this.state.ingredients} userid={this.props.state.id} navigator={this.props.navigator} store={this.props.store}/>
          </View>
        </View>
        <View>
          <Text style={styles.recipeTitle2}>Directions:</Text>
        </View>
        <ListView
          style={styles.recipe}
          dataSource={this.state.steps}
          renderRow={(step, i) => {
            let image = step.ingredients[0] ? step.ingredients[0].image : 'https://s3-us-west-1.amazonaws.com/filmedin/food+(1).png';
            return (
            <View
              key={i}
              style={styles.recipeStep}
              underlayColor="grey"
            >
              <Image source={{uri: image}} style={styles.recipeImage} />
              <Text style={styles.recipeStepText}>step {step.number}: {step.step}</Text>
            </View>
            )}
          }
        />
      </View>
    )
  }
}

export default connect(state => ({
    state: state.facebook
  }), null
)(Recipe);
<commit_msg>(code cleanup and refactor) Replaced unnecessarily duplicated render code with a single ternary function for the facebook share icon and made instagram/twitter icon click-handlers DRYer<commit_after>import React, { Component } from 'react';
import { Linking, Alert, TouchableOpacity, Text, View, Image, TextInput, ListView, TouchableHighlight } from 'react-native';
import helpers from '../helpers/helpers.js';
import styles from '../styles.ios.js';
import AddRecipe from './AddRecipe.ios.js';
import ShareFacebook from './ShareFacebook.ios.js';
import { connect } from "react-redux";

class Recipe extends Component {
  constructor(props) {
    super(props);
    let steps = props.recipe.analyzedInstructions[0] ? props.recipe.analyzedInstructions[0].steps : [];
    const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    this.state = {
      steps: ds.cloneWithRows(steps),
      ingredients: []
    }
  }

  componentWillMount() {
    this.renderIngredients();
    this.renderOptions();
  }

  goBack() {
    this.props.navigator.pop();
  }

  renderIngredients() {
    let ingredients = [];
    for (let ingredient of this.props.recipe.extendedIngredients) {
      ingredients.push(ingredient.originalString);
    }
    this.setState({
      ingredients: ingredients
    })
  }

  renderOptions() {
    const recipe = this.props.recipe;
    recipe.vegan = recipe.vegan ? '∙ Vegan' : false;
    recipe.glutenFree = recipe.glutenFree ? '∙ Gluten Free' : false;
    recipe.dairyFree = recipe.dairyFree ? '∙ Dairy Free' : false;
    if (!recipe.vegan && !recipe.glutenFree && !recipe.dairyFree) {
      recipe.dairyFree = 'No Dietary Restrictions';
    }
    recipe.cheap = recipe.cheap ? '$' : '$$$';
    recipe.sustainable = recipe.sustainable ? '∙ Recipe Sustainable' : false;
    recipe.servings = recipe.servings ? recipe.servings : 'Not available';
    recipe.healthscore = recipe.healthscore ? recipe.healthscore : 'Not available';
    // recipe.sourceUrl
  }

  //DRY click handler for social media deep linking
  shareToSocialMedia(deepLink, appName, storeURL) {
    //deep link into the provided app if installed, else direct user to the proper app store link
    Linking.canOpenURL(appName.toLowerCase() + '://').then(supported => {
      if (!supported) {
        Alert.alert(`You must install the ${appName} app in order to use this feature.`,
                    '',
                    [{text: `Install ${appName}`, onPress: () => Linking.openURL(storeURL)},
                     {text: 'Not Now'}]
        );
      } else {
        return Linking.openURL(deepLink);
      }
    }).catch(err => /*Alert.alert('Error; ', err), */console.error('Error: ', err));
  }

  render() {
    const ingredients = this.state.ingredients.map((ingredient, i) => {
      return (
        <Text style = {styles.ingredientListText2} key={i}>∙ {ingredient} </Text>
      )
    });
    const recipe = this.props.recipe;

    return (
      <View style={styles.recipe}>
        <View style={styles.resultsTitle}>
          <TouchableHighlight style={styles.backButton} onPress={this.goBack.bind(this)}>
            <Image style={styles.backButtonImage} source={{uri: 'https://cdn0.iconfinder.com/data/icons/vector-basic-tab-bar-icons/48/back_button-128.png'}} />
          </TouchableHighlight>
          <Text style={styles.resultsTitleText}>Instructions for {this.props.food}:</Text>
        </View>
        <View style={styles.ingredientContainer}>
          <View style={styles.ingredientList}>
            <Text> {recipe.vegan} {recipe.glutenFree} {recipe.dairyFree}</Text>
            <Text> {recipe.cheap} {recipe.sustainable} Servings: {recipe.servings} Healthscore: {recipe.healthscore}</Text>
            <Text style={styles.recipeTitle2}>Ingredients:</Text>
            <Text style={styles.ingredientListText2}>{ingredients}</Text>
          </View>
          <View style={styles.recipeImageAndIcons}>
            <Image source={{uri: this.props.image}} style={styles.recipeImage} />
            <View style={styles.iconsContainer}>
              {
                this.props.state.isAuthenticated
                  ? (<ShareFacebook url={this.props.recipe.sourceUrl} />)
                  : (<Text></Text>)
              }
              <TouchableOpacity onPress={
                () => this.shareToSocialMedia('instagram://camera', 'Instagram', 'https://itunes.apple.com/us/app/instagram/id389801252?mt=8')
              }>
                <Image source={require('../public/instagram_icon.png')} style={styles.shareIcons} />
              </TouchableOpacity>
              <TouchableOpacity onPress={
                () => this.shareToSocialMedia(
                  `twitter://post?message=I%20just%20made%20${this.props.food.split(' ').join('%20')}%20with%20a%20little%20help%20from%20the%20app%20Snacktime!
                  ${this.props.recipe.sourceUrl}`,
                  'Twitter',
                  'https://itunes.apple.com/us/app/twitter/id333903271?mt=8'
                )
              }>
                <Image source={require('../public/twitter_icon.png')} style={styles.shareIcons} />
              </TouchableOpacity>
            </View>
            <AddRecipe info={this.props.recipe} ingredients={this.state.ingredients} userid={this.props.state.id} navigator={this.props.navigator} store={this.props.store}/>
          </View>
        </View>
        <View>
          <Text style={styles.recipeTitle2}>Directions:</Text>
        </View>
        <ListView
          style={styles.recipe}
          dataSource={this.state.steps}
          renderRow={(step, i) => {
            let image = step.ingredients[0] ? step.ingredients[0].image : 'https://s3-us-west-1.amazonaws.com/filmedin/food+(1).png';
            return (
            <View
              key={i}
              style={styles.recipeStep}
              underlayColor="grey"
              >
                <Image source={{uri: image}} style={styles.recipeImage} />
                <Text style={styles.recipeStepText}>step {step.number}: {step.step}</Text>
            </View>
            )}}
        />
      </View>
    )
  }
}

export default connect(state => ({
    state: state.facebook
  }), null
)(Recipe);
<|endoftext|>