<commit_before>import React, {Component, PropTypes} from 'react';
import Keys from '../../utils/keys';
import './DateInput.scss';

const setTwoDigits = number => {
  const num = number / 1;
  const result = number < 10 ? `0${num}` : num;
  return result;
};

class DateInput extends Component {
  constructor(props) {
    super(props);
    this.handleOnChange = this.handleOnChange.bind(this);
    this.handleOnFocus = this.handleOnFocus.bind(this);
    this.handleOnBlur = this.handleOnBlur.bind(this);
  }

  handleOnBlur() {

  }

  handleOnFocus(e) {

  }

  isSeparator(value, i) {
    const isSeparator = (value.length === i) && (value[i - 1] !== '/');
    return isSeparator;
  }

  handleOnChange(e) {
    e.stopPropagation();
    const {changeHandler} = this.props;
    const datePattern = /^\d{2}[/-]\d{2}[/-]\d{4}/;
    const targetValue = e.target.value;
    const value = datePattern.exec(targetValue);
    const selStart = e.target.selectionStart;
    if (value) {
      const isoDate = (new Date(value)).toISOString();
      changeHandler(isoDate);
    } else if (this.isSeparator(targetValue, 2) || this.isSeparator(targetValue, 5)) {
      e.target.value += '/';
    } else if (targetValue.length === 3 && selStart === 2) {
      e.target.setSelectionRange(3, 3);
    }
  }

  componentWillReceiveProps(nextProps) {
    // const {value} = this.props;
    // if (nextProps.value !== value) {
    //   // this.setState({value: nextProps.value});
    // }
  }

  formatDate(isoDate) {
    const date = new Date(isoDate);
    const day = setTwoDigits(date.getDate());
    const month = setTwoDigits(date.getMonth() + 1);
    const year = date.getFullYear();
    return `${day}/${month}/${year}`;
  }

  render() {
    const {name, id, disabled, value} = this.props;
    const formattedValue = value && this.formatDate(value);
    return (

        <input
          id={id}
          name={name}
          ref={c => { if (c) { this.input = c; } }}
          className="date-input__input"
          placeholder="dd/mm/yyyy"
          defaultValue={formattedValue}
          disabled={disabled}
          onFocus={this.handleOnFocus}
          onBlur={this.handleOnBlur}
          onChange={this.handleOnChange} />
        <button className="date-input__calendar-button" disabled={disabled}>

        </button>

    );
  }
}

DateInput.propTypes = {
  name: PropTypes.string.isRequired,
  value: PropTypes.string,
  disabled: PropTypes.bool,
  id: PropTypes.string,
  changeHandler: PropTypes.func.isRequired
};

export default DateInput;
<commit_msg>DateInput: Setting caret position to after slash upon month update.<commit_after>import React, {Component, PropTypes} from 'react';
import Keys from '../../utils/keys';
import './DateInput.scss';

const setTwoDigits = number => {
  const num = number / 1;
  const result = number < 10 ? `0${num}` : num;
  return result;
};

class DateInput extends Component {
  constructor(props) {
    super(props);
    this.handleOnChange = this.handleOnChange.bind(this);
    this.handleOnFocus = this.handleOnFocus.bind(this);
    this.handleOnBlur = this.handleOnBlur.bind(this);
  }

  handleOnBlur() {

  }

  handleOnFocus(e) {

  }

  isSeparator(value, i) {
    const isSeparator = (value.length === i) && (value[i - 1] !== '/');
    return isSeparator;
  }

  handleOnChange(e) {
    e.stopPropagation();
    const {changeHandler} = this.props;
    const datePattern = /^\d{2}[/-]\d{2}[/-]\d{4}/;
    const targetValue = e.target.value;
    const value = datePattern.exec(targetValue);
    const selStart = e.target.selectionStart;
    if (value) {
      const isoDate = (new Date(value)).toISOString();
      changeHandler(isoDate);
    } else if (this.isSeparator(targetValue, 2) || this.isSeparator(targetValue, 5)) {
      e.target.value += '/';
    } else if (targetValue.length === 3 && selStart === 2) {
      e.target.setSelectionRange(3, 3);
    } else if (targetValue.length === 6 && selStart === 5) {
      e.target.setSelectionRange(6, 6);
    }
  }

  componentWillReceiveProps(nextProps) {
    // const {value} = this.props;
    // if (nextProps.value !== value) {
    //   // this.setState({value: nextProps.value});
    // }
  }

  formatDate(isoDate) {
    const date = new Date(isoDate);
    const day = setTwoDigits(date.getDate());
    const month = setTwoDigits(date.getMonth() + 1);
    const year = date.getFullYear();
    return `${day}/${month}/${year}`;
  }

  render() {
    const {name, id, disabled, value} = this.props;
    const formattedValue = value && this.formatDate(value);
    return (

        <input
          id={id}
          name={name}
          ref={c => { if (c) { this.input = c; } }}
          className="date-input__input"
          placeholder="dd/mm/yyyy"
          defaultValue={formattedValue}
          disabled={disabled}
          onFocus={this.handleOnFocus}
          onBlur={this.handleOnBlur}
          onChange={this.handleOnChange} />
        <button className="date-input__calendar-button" disabled={disabled}>

        </button>

    );
  }
}

DateInput.propTypes = {
  name: PropTypes.string.isRequired,
  value: PropTypes.string,
  disabled: PropTypes.bool,
  id: PropTypes.string,
  changeHandler: PropTypes.func.isRequired
};

export default DateInput;
<|endoftext|>


<commit_before>/*
Submits the search filter form. If we are looking at the task list,
does that via ajax. Otherwise does a normal html post
*/
function submitSearchFilterForm() {
    jQuery("#search_filter_form").trigger('submit');
}

/*
Removes the search filter the link belongs to and submits
the containing form.
*/
function removeSearchFilter(link) {
  link = jQuery(link);
  link.parent().parent("li").remove();
  submitSearchFilterForm();
}

function reverseSearchFilter(link){
  input = jQuery(link).siblings("input.reversed");
  if (input.val() == "false") {
    input.val("true");
  } else {
    input.val("false");
  }
  submitSearchFilterForm();
}

/* This function add inputs to search filter form, it works in both cases via normal http post and via ajax
*/
function addSearchFilter(event, ui) {
    selected = ui.item;
    var idName = selected.id;
    var idValue = selected.idval;
    /*NOTE: if user select qulifier, than idName -> name of param qualifier_id
      idValue -> value of this param, etc.
      else if user select keyword, then idName -> name of keyword_id or unread_only  param, idValue-> value of  param,
      but type&column name/value not exist
    */
    var typeName = selected.type;
    var typeValue = selected.typeval;
    var columnName = selected.col;
    var columnValue = selected.colval;
    var reversedName = selected.reversed;
    var reversedVal = selected.reversedval;
    if (idName && idName.length > 0) {
        var filterKeys = jQuery("#search_filter_form ul#search_filter_keys");
        filterKeys.append('<input type="hidden" name="'+idName+'" value="'+idValue+'"/>');
        if (typeName && typeName.length>0){
            filterKeys.append('<input type="hidden" name="'+typeName+'" value="'+typeValue+'"/>');
        }
        if (columnName && columnName.length>0) {
            filterKeys.append('<input type="hidden" name="'+columnName+'" value="'+columnValue+'"/>');
        }
        if (reversedName && reversedName.length > 0) {
            filterKeys.append('<input type="hidden" name="'+reversedName+'" value="'+reversedVal+'"/>');
        }
        submitSearchFilterForm();
    } else {
        // probably selected a heading, just ignore
    }
    jQuery(this).val("");
    return false;
}

function removeTaskFilter(sender) {
  var li = jQuery(sender).parent();
  var form = li.parents("form");
  li.remove();
  form.submit();
}

function addTaskFilter(sender, id, field_name) {
  var li = jQuery(sender);
  var html = "<input type='hidden' name='" + field_name + "' value='" + id + "' />";
  li.append(html);
  jQuery("#filter_form").submit();
}

/*Attach behavior to filters panel,
  change filter via ajax only on task/list page.
  On all other pages, when user click on filter link change filter
*/
function initFiltersPanel() {
  jQuery('div.task_filters>ul>li>a').click(loadFilterPanel);
}

function initTagsPanel() {
  jQuery('#tags a').click(loadFilterPanel);
}

function loadFilterPanel() {
    return loadFilter('', this.href);
}

function loadFilter(data, url){
  jQuery('#search_filter_keys').effect("highlight", {color: '#FF9900'}, 3000);
  jQuery.ajax({
            beforeSend: function(){ showProgress(); },
            complete: function(request){ tasksViewReload(); hideProgress(); } ,
            data: data,
            success:  function(request){jQuery('#search_filter_keys').html(request);},
            type:'post',
            url: url
        });
        return false;
}

jQuery(document).ready(function() {
  //only if we on tasks list or calendar or gantt page
    if( /tasks\/(list|calendar)$/.test(document.location.href) || /schedule\/gantt/.test(document.location.href) ){
      jQuery("#search_filter_form").submit(function(event){
        return loadFilter(jQuery.param(jQuery(this).serializeArray()), "/task_filters/update_current_filter");
      });
      initFiltersPanel();
      initTagsPanel();
  }

  // make search box contents selected when the user clicks in it
  jQuery("#search_filter").focus( function() {
    if (jQuery(this).val() == "Task search...") {
      jQuery(this).val('').removeClass('grey');
    } else {
      jQuery(this).select();
    }
  });

  jQuery("#search_filter").blur( function() {
      if (jQuery(this).val() == '') {
          jQuery(this).val("Task search...").addClass('grey');
      }
  });

  // the user/client search box
  jQuery(".search_filter").focus( function() {
    jQuery(this).select();
  });

  // Go to a task immediately if a number is entered and then the user hits enter
  jQuery("#search_filter").keypress(function(key) {
    if (key.keyCode == 13) { // if key was enter
      var id = jQuery(this).val();
      if (id.match(/^\d+$/)) {
        loadTask(id);
      }
    }
  });

  jQuery(".collapsable-sidepanel-button").click( function() {
    var panel = jQuery(this).parent().attr("id");
    if (jQuery(this).hasClass("panel-collapsed")) {
      jQuery.post("/users/set_side_panel_preference/open?panel=" + panel);
      jQuery(this).attr("class", "collapsable-sidepanel-button panel-open")
    }
    else {
      jQuery.post("/users/set_side_panel_preference/collapsed?panel=" + panel);
      jQuery(this).attr("class", "collapsable-sidepanel-button panel-collapsed")
    }
    jQuery(this).siblings(".panel_content").toggle();
  });

    jQuery('#recent_filters_button').click(function() {
      if(jQuery('#recent_filters ul').is(':visible')){ jQuery('#recent_filters ul').slideToggle(); return false;}
      jQuery('#recent_filters').load("/task_filters/recent", function(){
        jQuery('#recent_filters').children('ul').slideToggle();
        jQuery('#recent_filters ul li').hover(function() {
          jQuery(this).toggleClass('ui-state-hover');
        });
        jQuery('#recent_filters ul li a').click( function(){
          loadFilterPanel();
          jQuery('#recent_filters').slideToggle();
        });
      });
      return false;
    });
});<commit_msg>fix 'edit' link bug in Tags panel : 'AbstractController::ActionNotFound (The action 'create' could not be found for TagsController)'<commit_after>/*
Submits the search filter form. If we are looking at the task list,
does that via ajax. Otherwise does a normal html post
*/
function submitSearchFilterForm() {
    jQuery("#search_filter_form").trigger('submit');
}

/*
Removes the search filter the link belongs to and submits
the containing form.
*/
function removeSearchFilter(link) {
  link = jQuery(link);
  link.parent().parent("li").remove();
  submitSearchFilterForm();
}

function reverseSearchFilter(link){
  input = jQuery(link).siblings("input.reversed");
  if (input.val() == "false") {
    input.val("true");
  } else {
    input.val("false");
  }
  submitSearchFilterForm();
}

/* This function add inputs to search filter form, it works in both cases via normal http post and via ajax
*/
function addSearchFilter(event, ui) {
    selected = ui.item;
    var idName = selected.id;
    var idValue = selected.idval;
    /*NOTE: if user select qulifier, than idName -> name of param qualifier_id
      idValue -> value of this param, etc.
      else if user select keyword, then idName -> name of keyword_id or unread_only  param, idValue-> value of  param,
      but type&column name/value not exist
    */
    var typeName = selected.type;
    var typeValue = selected.typeval;
    var columnName = selected.col;
    var columnValue = selected.colval;
    var reversedName = selected.reversed;
    var reversedVal = selected.reversedval;
    if (idName && idName.length > 0) {
        var filterKeys = jQuery("#search_filter_form ul#search_filter_keys");
        filterKeys.append('<input type="hidden" name="'+idName+'" value="'+idValue+'"/>');
        if (typeName && typeName.length>0){
            filterKeys.append('<input type="hidden" name="'+typeName+'" value="'+typeValue+'"/>');
        }
        if (columnName && columnName.length>0) {
            filterKeys.append('<input type="hidden" name="'+columnName+'" value="'+columnValue+'"/>');
        }
        if (reversedName && reversedName.length > 0) {
            filterKeys.append('<input type="hidden" name="'+reversedName+'" value="'+reversedVal+'"/>');
        }
        submitSearchFilterForm();
    } else {
        // probably selected a heading, just ignore
    }
    jQuery(this).val("");
    return false;
}

function removeTaskFilter(sender) {
  var li = jQuery(sender).parent();
  var form = li.parents("form");
  li.remove();
  form.submit();
}

function addTaskFilter(sender, id, field_name) {
  var li = jQuery(sender);
  var html = "<input type='hidden' name='" + field_name + "' value='" + id + "' />";
  li.append(html);
  jQuery("#filter_form").submit();
}

/*Attach behavior to filters panel,
  change filter via ajax only on task/list page.
  On all other pages, when user click on filter link change filter
*/
function initFiltersPanel() {
  jQuery('div.task_filters>ul>li>a').click(loadFilterPanel);
}

function initTagsPanel() {
  jQuery('#tags .panel_content a').click(loadFilterPanel);
}

function loadFilterPanel() {
    return loadFilter('', this.href);
}

function loadFilter(data, url){
  jQuery('#search_filter_keys').effect("highlight", {color: '#FF9900'}, 3000);
  jQuery.ajax({
            beforeSend: function(){ showProgress(); },
            complete: function(request){ tasksViewReload(); hideProgress(); } ,
            data: data,
            success:  function(request){jQuery('#search_filter_keys').html(request);},
            type:'post',
            url: url
        });
        return false;
}

jQuery(document).ready(function() {
  //only if we on tasks list or calendar or gantt page
    if( /tasks\/(list|calendar)$/.test(document.location.href) || /schedule\/gantt/.test(document.location.href) ){
      jQuery("#search_filter_form").submit(function(event){
        return loadFilter(jQuery.param(jQuery(this).serializeArray()), "/task_filters/update_current_filter");
      });
      initFiltersPanel();
      initTagsPanel();
  }

  // make search box contents selected when the user clicks in it
  jQuery("#search_filter").focus( function() {
    if (jQuery(this).val() == "Task search...") {
      jQuery(this).val('').removeClass('grey');
    } else {
      jQuery(this).select();
    }
  });

  jQuery("#search_filter").blur( function() {
      if (jQuery(this).val() == '') {
          jQuery(this).val("Task search...").addClass('grey');
      }
  });

  // the user/client search box
  jQuery(".search_filter").focus( function() {
    jQuery(this).select();
  });

  // Go to a task immediately if a number is entered and then the user hits enter
  jQuery("#search_filter").keypress(function(key) {
    if (key.keyCode == 13) { // if key was enter
      var id = jQuery(this).val();
      if (id.match(/^\d+$/)) {
        loadTask(id);
      }
    }
  });

  jQuery(".collapsable-sidepanel-button").click( function() {
    var panel = jQuery(this).parent().attr("id");
    if (jQuery(this).hasClass("panel-collapsed")) {
      jQuery.post("/users/set_side_panel_preference/open?panel=" + panel);
      jQuery(this).attr("class", "collapsable-sidepanel-button panel-open")
    }
    else {
      jQuery.post("/users/set_side_panel_preference/collapsed?panel=" + panel);
      jQuery(this).attr("class", "collapsable-sidepanel-button panel-collapsed")
    }
    jQuery(this).siblings(".panel_content").toggle();
  });

    jQuery('#recent_filters_button').click(function() {
      if(jQuery('#recent_filters ul').is(':visible')){ jQuery('#recent_filters ul').slideToggle(); return false;}
      jQuery('#recent_filters').load("/task_filters/recent", function(){
        jQuery('#recent_filters').children('ul').slideToggle();
        jQuery('#recent_filters ul li').hover(function() {
          jQuery(this).toggleClass('ui-state-hover');
        });
        jQuery('#recent_filters ul li a').click( function(){
          loadFilterPanel();
          jQuery('#recent_filters').slideToggle();
        });
      });
      return false;
    });
});<|endoftext|>


<commit_before>const webdriver = require('selenium-webdriver');
const bindAll = require('lodash.bindall');
require('chromedriver');
const chromedriverVersion = require('chromedriver').version;

const headless = process.env.SMOKE_HEADLESS || false;
const remote = process.env.SMOKE_REMOTE || false;
const ci = process.env.CI || false;
const buildID = process.env.TRAVIS_BUILD_NUMBER;
const {SAUCE_USERNAME, SAUCE_ACCESS_KEY} = process.env;
const {By, Key, until} = webdriver;

const DEFAULT_TIMEOUT_MILLISECONDS = 20 * 1000;

class SeleniumHelper {
    constructor () {
        bindAll(this, [
            'buildDriver',
            'clickButton',
            'clickCss',
            'clickText',
            'clickXpath',
            'dragFromXpathToXpath',
            'findByCss',
            'findByXpath',
            'findText',
            'getKey',
            'getDriver',
            'getLogs',
            'getSauceDriver',
            'urlMatches',
            'waitUntilGone'
        ]);
    }
    buildDriver (name) {
        if (remote === 'true'){
            let nameToUse;
            if (ci === 'true'){
                nameToUse = 'travis ' + buildID + ' : ' + name;
            } else {
                nameToUse = name;
            }
            this.driver = this.getSauceDriver(SAUCE_USERNAME, SAUCE_ACCESS_KEY, nameToUse);
        } else {
            this.driver = this.getDriver();
        }
        return this.driver;
    }

    getDriver () {
        const chromeCapabilities = webdriver.Capabilities.chrome();
        let args = [];
        if (headless) {
            args.push('--headless');
            args.push('window-size=1024,1680');
            args.push('--no-sandbox');
        }
        chromeCapabilities.set('chromeOptions', {args});
        let driver = new webdriver.Builder()
            .forBrowser('chrome')
            .withCapabilities(chromeCapabilities)
            .build();
        return driver;
    }

    getChromeVersionNumber () {
        const versionFinder = /\d+\.\d+/;
        const versionArray = versionFinder.exec(chromedriverVersion);
        if (versionArray === null) {
            throw new Error('couldn\'t find version of chromedriver');
        }
        return versionArray[0];
    }

    getSauceDriver (username, accessKey, name) {
        const chromeVersion = this.getChromeVersionNumber();
        // Driver configs can be generated with the Sauce Platform Configurator
        // https://wiki.saucelabs.com/display/DOCS/Platform+Configurator
        let driverConfig = {
            browserName: 'chrome',
            platform: 'macOS 10.14',
            version: chromeVersion
        };
        var driver = new webdriver.Builder()
            .withCapabilities({
                browserName: driverConfig.browserName,
                platform: driverConfig.platform,
                version: driverConfig.version,
                username: username,
                accessKey: accessKey,
                name: name
            })
            .usingServer(`http://${username}:${accessKey
            }@ondemand.saucelabs.com:80/wd/hub`)
            .build();
        return driver;
    }

    getKey (keyName) {
        return Key[keyName];
    }

    findByXpath (xpath, timeoutMessage = `findByXpath timed out for path: ${xpath}`) {
        return this.driver.wait(until.elementLocated(By.xpath(xpath)), DEFAULT_TIMEOUT_MILLISECONDS, timeoutMessage)
            .then(el => (
                this.driver.wait(el.isDisplayed(), DEFAULT_TIMEOUT_MILLISECONDS, `${xpath} is not visible`)
                    .then(() => el)
            ));
    }

    waitUntilGone (element) {
        return this.driver.wait(until.stalenessOf(element));
    }

    clickXpath (xpath) {
        return this.findByXpath(xpath).then(el => el.click());
    }

    clickText (text) {
        return this.clickXpath(`//*[contains(text(), '${text}')]`);
    }

    findText (text) {
        return this.driver.wait(until.elementLocated(By.xpath(`//*[contains(text(), '${text}')]`), 5 * 1000));
    }

    clickButton (text) {
        return this.clickXpath(`//button[contains(text(), '${text}')]`);
    }

    findByCss (css) {
        return this.driver.wait(until.elementLocated(By.css(css), 1000 * 5));
    }

    clickCss (css) {
        return this.findByCss(css).then(el => el.click());
    }

    dragFromXpathToXpath (startXpath, endXpath) {
        return this.findByXpath(startXpath).then(startEl => {
            return this.findByXpath(endXpath).then(endEl => {
                return this.driver.actions()
                    .dragAndDrop(startEl, endEl)
                    .perform();
            });
        });
    }

    urlMatches (regex) {
        return this.driver.wait(until.urlMatches(regex), 1000 * 5);
    }

    getLogs (whitelist) {
        return this.driver.manage()
            .logs()
            .get('browser')
            .then((entries) => {
                return entries.filter((entry) => {
                    const message = entry.message;
                    for (let i = 0; i < whitelist.length; i++) {
                        if (message.indexOf(whitelist[i]) !== -1) {
                            // eslint-disable-next-line no-console
                            // console.warn('Ignoring whitelisted error: ' + whitelist[i]);
                            return false;
                        } else if (entry.level !== 'SEVERE') {
                            // eslint-disable-next-line no-console
                            // console.warn('Ignoring non-SEVERE entry: ' + message);
                            return false;
                        }
                        return true;
                    }
                    return true;
                });
            });
    }

}

module.exports = SeleniumHelper;
<commit_msg>Update selenium-helpers.js<commit_after>const webdriver = require('selenium-webdriver');
const bindAll = require('lodash.bindall');
require('chromedriver');
const chromedriverVersion = require('chromedriver').version;

const headless = process.env.SMOKE_HEADLESS || false;
const remote = process.env.SMOKE_REMOTE || false;
const ci = process.env.CI || false;
const buildID = process.env.TRAVIS_BUILD_NUMBER;
const {SAUCE_USERNAME, SAUCE_ACCESS_KEY} = process.env;
const {By, Key, until} = webdriver;

const DEFAULT_TIMEOUT_MILLISECONDS = 20 * 1000;

class SeleniumHelper {
    constructor () {
        bindAll(this, [
            'buildDriver',
            'clickButton',
            'clickCss',
            'clickText',
            'clickXpath',
            'dragFromXpathToXpath',
            'findByCss',
            'findByXpath',
            'findText',
            'getKey',
            'getDriver',
            'getLogs',
            'getSauceDriver',
            'urlMatches',
            'waitUntilGone'
        ]);
    }
    buildDriver (name) {
        if (remote === 'true'){
            let nameToUse;
            if (ci === 'true'){
                nameToUse = 'travis ' + buildID + ' : ' + name;
            } else {
                nameToUse = name;
            }
            this.driver = this.getSauceDriver(SAUCE_USERNAME, SAUCE_ACCESS_KEY, nameToUse);
        } else {
            this.driver = this.getDriver();
        }
        return this.driver;
    }

    getDriver () {
        const chromeCapabilities = webdriver.Capabilities.chrome();
        let args = [];
        if (headless) {
            args.push('--headless');
            args.push('window-size=1024,1680');
            args.push('--no-sandbox');
        }
        chromeCapabilities.set('chromeOptions', {args});
        let driver = new webdriver.Builder()
            .forBrowser('chrome')
            .withCapabilities(chromeCapabilities)
            .build();
        return driver;
    }

    getChromeVersionNumber () {
        const versionFinder = /\d+\.\d+/;
        const versionArray = versionFinder.exec(chromedriverVersion);
        if (versionArray === null) {
            throw new Error('couldn\'t find version of chromedriver');
        }
        return versionArray[0];
    }

    getSauceDriver (username, accessKey, name) {
        const chromeVersion = this.getChromeVersionNumber();
        // Driver configs can be generated with the Sauce Platform Configurator
        // https://wiki.saucelabs.com/display/DOCS/Platform+Configurator
        let driverConfig = {
            browserName: 'chrome',
            platform: 'macOS 10.14',
            version: chromeVersion
        };
        var driver = new webdriver.Builder()
            .withCapabilities({
                browserName: driverConfig.browserName,
                platform: driverConfig.platform,
                version: driverConfig.version,
                username: username,
                accessKey: accessKey,
                name: name
            })
            .usingServer(`http://${username}:${accessKey
            }@ondemand.saucelabs.com:80/wd/hub`)
            .build();
        return driver;
    }

    getKey (keyName) {
        return Key[keyName];
    }

    findByXpath (xpath, timeoutMessage = `findByXpath timed out for path: ${xpath}`) {
        return this.driver.wait(until.elementLocated(By.xpath(xpath)), DEFAULT_TIMEOUT_MILLISECONDS, timeoutMessage)
            .then(el => (
                this.driver.wait(el.isDisplayed(), DEFAULT_TIMEOUT_MILLISECONDS, `${xpath} is not visible`)
                    .then(() => el)
            ));
    }

    waitUntilGone (element) {
        return this.driver.wait(until.stalenessOf(element));
    }

    clickXpath (xpath) {
        return this.findByXpath(xpath).then(el => el.click());
    }

    clickText (text) {
        return this.clickXpath(`//*[contains(text(), '${text}')]`);
    }

    findText (text) {
        return this.driver.wait(until.elementLocated(By.xpath(`//*[contains(text(), '${text}')]`), 5 * 1000));
    }

    clickButton (text) {
        return this.clickXpath(`//button[contains(text(), '${text}')]`);
    }

    findByCss (css) {
        return this.driver.wait(until.elementLocated(By.css(css), 1000 * 5));
    }

    clickCss (css) {
        return this.findByCss(css).then(el => el.click());
    }

    dragFromXpathToXpath (startXpath, endXpath) {
        return this.findByXpath(startXpath).then(startEl => {
            return this.findByXpath(endXpath).then(endEl => {
                return this.driver.actions()
                    .dragAndDrop(startEl, endEl)
                    .perform();
            });
        });
    }

    urlMatches (regex) {
        return this.driver.wait(until.urlMatches(regex), 1000 * 5);
    }

    getLogs (whitelist) {
        return this.driver.manage()
            .logs()
            .get('browser')
            .then((entries) => {
                return entries.filter((entry) => {
                    const message = entry.message;
                    for (let i = 0; i < whitelist.length; i++) {
                        if (message.indexOf(whitelist[i]) !== -1) {
                            // eslint-disable-next-line no-console
                            // console.warn('Ignoring whitelisted error: ' + whitelist[i]);
                            return false;
                        } else if (entry.level !== 'SEVERE') {
                            // eslint-disable-next-line no-console
                            // console.warn('Ignoring non-SEVERE entry: ' + message);
                            return false;
                        }
                        return true;
                    }
                    return true;
                });
            });
    }

}

module.exports = SeleniumHelper;
<|endoftext|>


<commit_before>$(document).on('rex:ready', function() {
       quicknavi_filter_init();
       var ctype = getUrlVars()["ctype"];
       if (ctype)
       {
	   $(".quicknavi a").attr('href', function(i, h) {
	     return h + (h.indexOf('?') != -1 ? "&ctype="+ctype : "?ctype="+ctype);
	   		});
       }
});

function quicknavi_filter_init() {
	$('#qsearch').keyup(function(){
		var current_query = $('#qsearch').val();
		if (current_query !== "") {
			$(".quicknavi.list-group li.quickitem").hide();
			$(".quicknavi.list-group li.quickitem").each(function(){
				var current_keyword = $(this).text();
				 var upercase = current_query.substr(0,1).toUpperCase() + current_query.substr(1);
			    if ((current_keyword.indexOf(current_query) >=0) ||  (current_keyword.indexOf(upercase) >=0)) {
				$(this).show();
				}

			});
		} else {
			$(".quicknavi.list-group li.quickitem").show();
		}
	});
}

function getUrlVars() {
var vars = {};
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) {
vars[key] = value;
});
return vars;
}
<commit_msg>$(document).on('shown.bs.dropdown'<commit_after>$(document).on('rex:ready', function() {
       quicknavi_filter_init();

       var ctype = getUrlVars()["ctype"];
       if (ctype)
       {
	   $(".quicknavi a").attr('href', function(i, h) {
	     return h + (h.indexOf('?') != -1 ? "&ctype="+ctype : "?ctype="+ctype);
	   		});
       }

});

function quicknavi_filter_init() {
	$('#qsearch').keyup(function(){
		var current_query = $('#qsearch').val();
		if (current_query !== "") {
			$(".quicknavi.list-group li.quickitem").hide();
			$(".quicknavi.list-group li.quickitem").each(function(){
				var current_keyword = $(this).text();
				 var upercase = current_query.substr(0,1).toUpperCase() + current_query.substr(1);
			    if ((current_keyword.indexOf(current_query) >=0) ||  (current_keyword.indexOf(upercase) >=0)) {
				$(this).show();
				}

			});
		} else {
			$(".quicknavi.list-group li.quickitem").show();
		}
	});
}

function getUrlVars() {
var vars = {};
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) {
vars[key] = value;
});
return vars;
}

$(document).on('shown.bs.dropdown', function(event) {
    var dropdown = $(event.target);

    dropdown.find('.dropdown-menu').attr('aria-expanded', true);

    setTimeout(function() {
        dropdown.find('.dropdown-menu li:first-child #qsearch').focus();
    }, 10);
});

$(document).on('hidden.bs.dropdown', function(event) {
    var dropdown = $(event.target);

    dropdown.find('.dropdown-menu').attr('aria-expanded', false);

    dropdown.find('.dropdown-toggle').focus();
});
<|endoftext|>


<commit_before>/*
 openBD Chrome Extension
 Copyright (c) 2017 CALIL Inc.
 This software is released under the MIT License.
 http://opensource.org/licenses/mit-license.php
 */

// 対応サイトの定義
const SITES = [
  {
    url: 'https?://ilisod001.apsel.jp/', // 対象にするURL 正規表現 /(スラッシュ)は\/(バックスラッシュ)付きに置換される
    isbnSelector: document.querySelectorAll('.normal-td'), // ISBNが含まれるエレメント 複数でも可
    targetElement: document.querySelectorAll('.contents')[2], // 書誌情報を挿入するエレメント
    insert: 'after' // after or beofre ターゲットの前後どちらに挿入するか
  },
  {
    url: 'https?://.*?/opac/wopc/pc/pages/SearchResultList.jsp',
    isbnSelector: document.querySelectorAll('.normal-td'),
    targetElement: document.querySelectorAll('.contents')[2],
    insert: 'after'
  },
  {
    url: 'https?://.*?/WebOpac/webopac/searchdetail.do',
    isbnSelector: document.querySelectorAll('#highlight_keyword td'),
    targetElement: document.querySelectorAll('#highlight_keyword')[0],
    insert: 'after'
  },
  {
    url: 'https?://www.amazon.co.jp/?.*?/dp/',
    isbnSelector: document.querySelectorAll('.content li'),
    targetElement: document.querySelectorAll('#productDescription_feature_div')[0],
    insert: 'after'
  },
  {
    url: 'https://calil.jp/book/',
    isbnSelector: document.querySelectorAll('.bookarea')[2],
    targetElement: document.querySelectorAll('.detailheader')[1],
    insert: 'before'
  },
  {
    url: 'https://librize.com/items/',
    isbnSelector: document.querySelectorAll('.book-info li'),
    targetElement: document.querySelectorAll('.book-info ul')[0],
    insert: 'after'
  },
];

// ISBNの抽出
const parseISBN = (isbnSelector)=> {
  let isbn = null;
  let list = isbnSelector.length ? isbnSelector : [isbnSelector];
  for(let item of list) {
    let match = item.innerHTML.replace(/-/g, '').match(/[0-9\-]{12}[0-9X]/);
    // console.log(match)
    if (match) {
      isbn = match[0];
      break;
    }
  };
  return isbn;
};

let isbn = null;
let targetElement = null;
let insert = null;

SITES.forEach((site)=> {
  let url = site.url.replace(/\//g, '\\/');
  // console.log(url);
  if (location.href.match(url)) {
    isbn = parseISBN(site.isbnSelector);
    targetElement = site.targetElement;
    insert = site.insert;
  }
});

// 本のデータをパース
const parseBook = (book)=>{
  // console.log(book.onix);
  // 書影
  let thumbnail = null;
  // タイトル
  let title = book.onix.DescriptiveDetail.TitleDetail.TitleElement.TitleText.content;
  let titleYomi = book.onix.DescriptiveDetail.TitleDetail.TitleElement.TitleText.collationkey;
  // 紹介・目次
  let description = null;
  let descriptionLong = null;
  let tableOfContents = null;
  let maegakinado = null;
  // console.log(book.onix.CollateralDetail.SupportingResource)
  if(book.summary.cover){
    thumbnail = book.summary.cover;
  }
  // console.log(book.onix.CollateralDetail.TextContent)
  if(book.onix.CollateralDetail.TextContent){
    book.onix.CollateralDetail.TextContent.forEach((text)=>{
      // console.log(text)
      if(text.TextType==='02'){
        description = text.Text.replace(/\n/g, '
');
      }
      if(text.TextType==='03'){
        descriptionLong = text.Text.replace(/\n/g, '
');
      }
      if(text.TextType==='04'){
        tableOfContents = text.Text.replace(/\n/g, '
');
      }
    });
  }
  if(book.hanmoto.maegakinado){
    maegakinado = book.hanmoto.maegakinado.replace(/\n/g, '
');
  }
  // 著者
  let parseAuthor = (author)=>{
    if(typeof author.PersonName === 'string'){
      authors.push({
        name: author.PersonName,
        yomi: null,
        profile: author.BiographicalNote
      });
    }else{
      authors.push({
        name: author.PersonName.content,
        yomi: author.PersonName.collationkey,
        profile: author.BiographicalNote
      });
    }
  };
  let authors = [];
  if(book.onix.DescriptiveDetail.Contributor instanceof Array){
    book.onix.DescriptiveDetail.Contributor.forEach((author)=>{
      parseAuthor(author);
    });
  }else{
    parseAuthor(book.onix.DescriptiveDetail.Contributor);
  }

  // console.log(title, titleYomi);
  // console.log(authors);
  // console.log(description, descriptionLong, tableOfContents)
  return {
    thumbnail: thumbnail,
    title: title,
    titleYomi: titleYomi,
    authors: authors,
    description: description,
    descriptionLong: descriptionLong,
    tableOfContents: tableOfContents,
    maegakinado: maegakinado,
    isbn: book.summary.isbn,
    publisher: book.summary.publisher,
    pubdate: book.summary.pubdate.replace(/(\d{4})(\d{2})(\d{2})/g , "$1/$2/$3"),
    author: book.summary.author
  };
};

// 本の追加
const renderBook = (book)=>{
  let content = document.createElement('div');
  content.className = 'openbd_content';

  let authorContent = '';
  // 著者情報の追加
  if(book.authors.length>0){
    authorContent = '
著者
';
    book.authors.forEach((author)=> {
      let authorYomi = author.yomi ? '(' + author.yomi + ')' : '';
      authorContent += `<h3 class="openbd_author_header">${author.name}${authorYomi}</h3>`;
      if(author.profile){
        authorContent += `<div class="openbd_author_profile">${author.profile}</div>`;
      }
    });
  }

  if(book.thumbnail){
    content.innerHTML += `<img src="${book.thumbnail}" class="openbd_thumbnail" />`;
  }
  let titleYomi = (book.titleYomi) ? '(' + book.titleYomi + ')' : '';
  content.innerHTML += `

タイトル

${book.title}${titleYomi}

  `;
  if(book.description || book.descriptionLong){
    content.innerHTML += '
紹介
';
  }
  if(book.description){
    content.innerHTML += `<div class="openbd_description">${book.description}</div>`;
  }
  if(book.descriptionLong){
    content.innerHTML += `<div class="openbd_description">${book.descriptionLong}</div>`;
  }
  if(book.tableOfContents){
    content.innerHTML += `

目次

${book.tableOfContents}

    `;
  }
  if(book.maegakinado){
    content.innerHTML += `

前書きなど

${book.maegakinado}

    `;
  }
  content.innerHTML += authorContent;
  let pubdate = book.pubdate!=='' ?` <span class="openbd_pubdate">(${book.pubdate})</span>` : '';
  content.innerHTML += `

出版社

${book.publisher}${pubdate}

ISBN

${book.isbn}

  `;
  content.innerHTML += `

by openBD

  `;
  if(insert==='after'){
    targetElement.parentNode.insertBefore(content, targetElement.nextSibling);
  }else if(insert==='before'){
    targetElement.parentNode.insertBefore(content, targetElement);
  }
};

// スタイルの追加
const renderStyle = ()=>{
  let style = document.createElement('style');
  style.innerHTML = `
  .openbd_content {
    font-size: 1em;
    margin: 3em auto;
    padding: 10px;
    background-color: #ffffff;
    color: #636363;
    border-radius: 0.25em;
  }
  .openbd_thumbnail {
    box-shadow: 0 1px 2px rgba(0,0,0,0.5);
  }
  .openbd_header {
    font-size: 1.25em;
    font-weight: bold !important;
    color: #000000;
    margin: 1em 0 0.5em;
  }
  .openbd_header:after {
    content:"";
    display:block;
    height:1px;
    width:100%;
    background-color: #dddddd;
    background-image: -moz-linear-gradient(90deg, #efa8b0, #a89cc8, #8cc9f0);
    background-image: -webkit-linear-gradient(90deg, #efa8b0, #a89cc8, #8cc9f0);
    background-image: -ms-linear-gradient(90deg, #efa8b0, #a89cc8, #8cc9f0);
    background-image: linear-gradient(90deg, #efa8b0, #a89cc8, #8cc9f0);
  }
  .openbd_author_header {
    font-size: 1em;
    color: #000000;
    margin: 0.5em 0 0.5em;
  }
  .openbd_author_profile {
    font-size: 1em
  }
  .openbd_powered, .openbd_powered a {
    color: #999999;
    font-size: 0.8em;
    text-align: right;
  }
  `;
  if(insert==='after'){
    targetElement.parentNode.insertBefore(style, targetElement.nextSibling);
  }else if(insert==='before'){
    targetElement.parentNode.insertBefore(style, targetElement);
  }
};

// APIから書誌を取得
if (isbn) {
  // console.log('ISBN:' + isbn);
  new api([isbn], (data)=> {
    // document.body.append(JSON.stringify(data));
    renderStyle();
    data.forEach(function (book) {
      // 書誌があれば
      if (book !== null){
        let b = parseBook(book);
        renderBook(b);
      };
    })
  });
}

// // sample api data
// console.log(chrome.extension.getURL('/api.json'))
// superagent
// .get(chrome.extension.getURL('/api.json'))
// .end(function(err, res){
//   if(!err){
//     let data = JSON.parse(res.text);
//     // console.log(data);
//     renderStyle();
//     data.forEach((book)=>{
//       if(book!==null){
//         let b = parseBook(book);
//         b.isbn = isbn;
//         renderBook(b);
//       }
//     });
//   }
// });

// var injectScript;
//
// injectScript = function(file, node) {
//   var s, th;
//   th = document.getElementsByTagName(node)[0];
//   s = document.createElement('script');
//   s.setAttribute('type', 'text/javascript');
//   s.setAttribute('src', file);
//   return th.appendChild(s);
// };

// injectScript(chrome.extension.getURL('/api.js'), 'body');
// injectScript(chrome.extension.getURL('/embeded-script.js'), 'body');
// injectScript('http://lab.calil.jp/demo/openbd/superagent.js', 'body');
// injectScript('http://lab.calil.jp/demo/openbd/api.js?isbn='+isbn.join(','), 'body');
<commit_msg>カーリルのISBNを取得できない問題を修正<commit_after>/*
 openBD Chrome Extension
 Copyright (c) 2017 CALIL Inc.
 This software is released under the MIT License.
 http://opensource.org/licenses/mit-license.php
 */

// 対応サイトの定義
const SITES = [
  {
    url: 'https?://ilisod001.apsel.jp/', // 対象にするURL 正規表現 /(スラッシュ)は\/(バックスラッシュ)付きに置換される
    isbnSelector: document.querySelectorAll('.normal-td'), // ISBNが含まれるエレメント 複数でも可
    targetElement: document.querySelectorAll('.contents')[2], // 書誌情報を挿入するエレメント
    insert: 'after' // after or beofre ターゲットの前後どちらに挿入するか
  },
  {
    url: 'https?://.*?/opac/wopc/pc/pages/SearchResultList.jsp',
    isbnSelector: document.querySelectorAll('.normal-td'),
    targetElement: document.querySelectorAll('.contents')[2],
    insert: 'after'
  },
  {
    url: 'https?://.*?/WebOpac/webopac/searchdetail.do',
    isbnSelector: document.querySelectorAll('#highlight_keyword td'),
    targetElement: document.querySelectorAll('#highlight_keyword')[0],
    insert: 'after'
  },
  {
    url: 'https?://www.amazon.co.jp/?.*?/dp/',
    isbnSelector: document.querySelectorAll('.content li'),
    targetElement: document.querySelectorAll('#productDescription_feature_div')[0],
    insert: 'after'
  },
  {
    url: 'https://calil.jp/book/',
    isbnSelector: document.querySelectorAll('.bookarea'),
    targetElement: document.querySelectorAll('.detailheader')[1],
    insert: 'before'
  },
  {
    url: 'https://librize.com/items/',
    isbnSelector: document.querySelectorAll('.book-info li'),
    targetElement: document.querySelectorAll('.book-info ul')[0],
    insert: 'after'
  },
];

// ISBNの抽出
const parseISBN = (isbnSelector)=> {
  let isbn = null;
  let list = isbnSelector.length ? isbnSelector : [isbnSelector];
  for(let item of list) {
    let match = item.innerHTML.replace(/-/g, '').match(/[0-9\-]{12}[0-9X]/);
    // console.log(match)
    if (match) {
      isbn = match[0];
      break;
    }
  };
  return isbn;
};

let isbn = null;
let targetElement = null;
let insert = null;

SITES.forEach((site)=> {
  let url = site.url.replace(/\//g, '\\/');
  // console.log(url);
  if (location.href.match(url)) {
    isbn = parseISBN(site.isbnSelector);
    targetElement = site.targetElement;
    insert = site.insert;
  }
});

// 本のデータをパース
const parseBook = (book)=>{
  // console.log(book.onix);
  // 書影
  let thumbnail = null;
  // タイトル
  let title = book.onix.DescriptiveDetail.TitleDetail.TitleElement.TitleText.content;
  let titleYomi = book.onix.DescriptiveDetail.TitleDetail.TitleElement.TitleText.collationkey;
  // 紹介・目次
  let description = null;
  let descriptionLong = null;
  let tableOfContents = null;
  let maegakinado = null;
  // console.log(book.onix.CollateralDetail.SupportingResource)
  if(book.summary.cover){
    thumbnail = book.summary.cover;
  }
  // console.log(book.onix.CollateralDetail.TextContent)
  if(book.onix.CollateralDetail.TextContent){
    book.onix.CollateralDetail.TextContent.forEach((text)=>{
      // console.log(text)
      if(text.TextType==='02'){
        description = text.Text.replace(/\n/g, '
');
      }
      if(text.TextType==='03'){
        descriptionLong = text.Text.replace(/\n/g, '
');
      }
      if(text.TextType==='04'){
        tableOfContents = text.Text.replace(/\n/g, '
');
      }
    });
  }
  if(book.hanmoto.maegakinado){
    maegakinado = book.hanmoto.maegakinado.replace(/\n/g, '
');
  }
  // 著者
  let parseAuthor = (author)=>{
    if(typeof author.PersonName === 'string'){
      authors.push({
        name: author.PersonName,
        yomi: null,
        profile: author.BiographicalNote
      });
    }else{
      authors.push({
        name: author.PersonName.content,
        yomi: author.PersonName.collationkey,
        profile: author.BiographicalNote
      });
    }
  };
  let authors = [];
  if(book.onix.DescriptiveDetail.Contributor instanceof Array){
    book.onix.DescriptiveDetail.Contributor.forEach((author)=>{
      parseAuthor(author);
    });
  }else{
    parseAuthor(book.onix.DescriptiveDetail.Contributor);
  }

  // console.log(title, titleYomi);
  // console.log(authors);
  // console.log(description, descriptionLong, tableOfContents)
  return {
    thumbnail: thumbnail,
    title: title,
    titleYomi: titleYomi,
    authors: authors,
    description: description,
    descriptionLong: descriptionLong,
    tableOfContents: tableOfContents,
    maegakinado: maegakinado,
    isbn: book.summary.isbn,
    publisher: book.summary.publisher,
    pubdate: book.summary.pubdate.replace(/(\d{4})(\d{2})(\d{2})/g , "$1/$2/$3"),
    author: book.summary.author
  };
};

// 本の追加
const renderBook = (book)=>{
  let content = document.createElement('div');
  content.className = 'openbd_content';

  let authorContent = '';
  // 著者情報の追加
  if(book.authors.length>0){
    authorContent = '
著者
';
    book.authors.forEach((author)=> {
      let authorYomi = author.yomi ? '(' + author.yomi + ')' : '';
      authorContent += `<h3 class="openbd_author_header">${author.name}${authorYomi}</h3>`;
      if(author.profile){
        authorContent += `<div class="openbd_author_profile">${author.profile}</div>`;
      }
    });
  }

  if(book.thumbnail){
    content.innerHTML += `<img src="${book.thumbnail}" class="openbd_thumbnail" />`;
  }
  let titleYomi = (book.titleYomi) ? '(' + book.titleYomi + ')' : '';
  content.innerHTML += `

タイトル

${book.title}${titleYomi}

  `;
  if(book.description || book.descriptionLong){
    content.innerHTML += '
紹介
';
  }
  if(book.description){
    content.innerHTML += `<div class="openbd_description">${book.description}</div>`;
  }
  if(book.descriptionLong){
    content.innerHTML += `<div class="openbd_description">${book.descriptionLong}</div>`;
  }
  if(book.tableOfContents){
    content.innerHTML += `

目次

${book.tableOfContents}

    `;
  }
  if(book.maegakinado){
    content.innerHTML += `

前書きなど

${book.maegakinado}

    `;
  }
  content.innerHTML += authorContent;
  let pubdate = book.pubdate!=='' ?` <span class="openbd_pubdate">(${book.pubdate})</span>` : '';
  content.innerHTML += `

出版社

${book.publisher}${pubdate}

ISBN

${book.isbn}

  `;
  content.innerHTML += `

by openBD

  `;
  if(insert==='after'){
    targetElement.parentNode.insertBefore(content, targetElement.nextSibling);
  }else if(insert==='before'){
    targetElement.parentNode.insertBefore(content, targetElement);
  }
};

// スタイルの追加
const renderStyle = ()=>{
  let style = document.createElement('style');
  style.innerHTML = `
  .openbd_content {
    font-size: 1em;
    margin: 3em auto;
    padding: 10px;
    background-color: #ffffff;
    color: #636363;
    border-radius: 0.25em;
  }
  .openbd_thumbnail {
    box-shadow: 0 1px 2px rgba(0,0,0,0.5);
  }
  .openbd_header {
    font-size: 1.25em;
    font-weight: bold !important;
    color: #000000;
    margin: 1em 0 0.5em;
  }
  .openbd_header:after {
    content:"";
    display:block;
    height:1px;
    width:100%;
    background-color: #dddddd;
    background-image: -moz-linear-gradient(90deg, #efa8b0, #a89cc8, #8cc9f0);
    background-image: -webkit-linear-gradient(90deg, #efa8b0, #a89cc8, #8cc9f0);
    background-image: -ms-linear-gradient(90deg, #efa8b0, #a89cc8, #8cc9f0);
    background-image: linear-gradient(90deg, #efa8b0, #a89cc8, #8cc9f0);
  }
  .openbd_author_header {
    font-size: 1em;
    color: #000000;
    margin: 0.5em 0 0.5em;
  }
  .openbd_author_profile {
    font-size: 1em
  }
  .openbd_powered, .openbd_powered a {
    color: #999999;
    font-size: 0.8em;
    text-align: right;
  }
  `;
  if(insert==='after'){
    targetElement.parentNode.insertBefore(style, targetElement.nextSibling);
  }else if(insert==='before'){
    targetElement.parentNode.insertBefore(style, targetElement);
  }
};

// APIから書誌を取得
if (isbn) {
  // console.log('ISBN:' + isbn);
  new api([isbn], (data)=> {
    // document.body.append(JSON.stringify(data));
    renderStyle();
    data.forEach(function (book) {
      // 書誌があれば
      if (book !== null){
        let b = parseBook(book);
        renderBook(b);
      };
    })
  });
}

// // sample api data
// console.log(chrome.extension.getURL('/api.json'))
// superagent
// .get(chrome.extension.getURL('/api.json'))
// .end(function(err, res){
//   if(!err){
//     let data = JSON.parse(res.text);
//     // console.log(data);
//     renderStyle();
//     data.forEach((book)=>{
//       if(book!==null){
//         let b = parseBook(book);
//         b.isbn = isbn;
//         renderBook(b);
//       }
//     });
//   }
// });

// var injectScript;
//
// injectScript = function(file, node) {
//   var s, th;
//   th = document.getElementsByTagName(node)[0];
//   s = document.createElement('script');
//   s.setAttribute('type', 'text/javascript');
//   s.setAttribute('src', file);
//   return th.appendChild(s);
// };

// injectScript(chrome.extension.getURL('/api.js'), 'body');
// injectScript(chrome.extension.getURL('/embeded-script.js'), 'body');
// injectScript('http://lab.calil.jp/demo/openbd/superagent.js', 'body');
// injectScript('http://lab.calil.jp/demo/openbd/api.js?isbn='+isbn.join(','), 'body');
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import classNames from 'classnames'
import styles from './styles.less'
import formStyles from '../styles.less'

export default class Textarea extends Component {
   static propTypes = {
      className: React.PropTypes.string,
	  disabled: React.PropTypes.bool,
      error: React.PropTypes.string,
      id: React.PropTypes.string,
      label: React.PropTypes.string,
      name: React.PropTypes.string,
      onBlur: React.PropTypes.func,
      onChange: React.PropTypes.func,
      placeholder: React.PropTypes.string,
      rows: React.PropTypes.string,
      touched: React.PropTypes.bool,
      value: React.PropTypes.string
   }

   static defaultProps = {
 	  disabled: false,
      rows: '5',
      value: ''
   }

   constructor(props, context) {
      super(props, context)
      this.handleChange = this.handleChange.bind(this)
      this.state = {
         value: props.value
      }
   }

   componentWillReceiveProps(newProps) {
      if (newProps.value !== this.state.value) {
         this.setState({ value: newProps.value })
      }
   }

   handleChange(event) {
      this.setState({ value: event.target.value })
      if (this.props.onChange) {
         this.props.onChange(event, event.target.value)
      }
   }

   render() {
      const classes = [styles.textarea, this.props.className]

      return (

            {this.props.label && {this.props.label}}
            <textarea
               className={classNames(classes)}
               disabled={this.props.disabled}
               id={this.props.id}
               onBlur={this.props.onBlur}
               onChange={this.handleChange}
               name={this.props.name}
               placeholder={this.props.placeholder}
               value={this.state.value}
               rows={this.props.rows}
            />
            {this.props.touched && this.props.error &&

                  {this.props.error}

            }

      )
   }
}
<commit_msg>code spacing fix<commit_after>import React, { Component } from 'react'
import classNames from 'classnames'
import styles from './styles.less'
import formStyles from '../styles.less'

export default class Textarea extends Component {
   static propTypes = {
      className: React.PropTypes.string,
	   disabled: React.PropTypes.bool,
      error: React.PropTypes.string,
      id: React.PropTypes.string,
      label: React.PropTypes.string,
      name: React.PropTypes.string,
      onBlur: React.PropTypes.func,
      onChange: React.PropTypes.func,
      placeholder: React.PropTypes.string,
      rows: React.PropTypes.string,
      touched: React.PropTypes.bool,
      value: React.PropTypes.string
   }

   static defaultProps = {
 	   disabled: false,
      rows: '5',
      value: ''
   }

   constructor(props, context) {
      super(props, context)
      this.handleChange = this.handleChange.bind(this)
      this.state = {
         value: props.value
      }
   }

   componentWillReceiveProps(newProps) {
      if (newProps.value !== this.state.value) {
         this.setState({ value: newProps.value })
      }
   }

   handleChange(event) {
      this.setState({ value: event.target.value })
      if (this.props.onChange) {
         this.props.onChange(event, event.target.value)
      }
   }

   render() {
      const classes = [styles.textarea, this.props.className]

      return (

            {this.props.label && {this.props.label}}
            <textarea
               className={classNames(classes)}
               disabled={this.props.disabled}
               id={this.props.id}
               onBlur={this.props.onBlur}
               onChange={this.handleChange}
               name={this.props.name}
               placeholder={this.props.placeholder}
               value={this.state.value}
               rows={this.props.rows}
            />
            {this.props.touched && this.props.error &&

                  {this.props.error}

            }

      )
   }
}
<|endoftext|>


<commit_before>var cocktail = require('cocktail');

//Add Logger annotation.
var Logger = require('../annotations/Logger');
cocktail.use(Logger);

//Using Behavior class.
var Behavior = require('./Behavior');

/*
 *  Adding this trait lets the constructor accept an object as parameter.
 *  Automaticaly, it will map the properties of the object with
 *  the properties defined as instance variables.
 */
//var Configurable = require('cocktail-trait-configurable');

/*
 **********************************************************************
 *  This Memory defines Equals and Clone method.                      *
 *  This Memory allows to have the same element twice.                *
 **********************************************************************
 */
cocktail.mix({
  //Define this file as a single class module exportable.
  '@exports' : module,
  '@as' : 'class',

  //'@traits' : [Configurable],

  //Set the logger and signature of this class.
 '@logger' : [console, "Memory:"],

  /*
   *  Instance variables of the class.
   *  Since we want them to be private, we'll define them in the constructor.
   */
  // '@properties': {
  //   array: undefined,
  //   size: 0,
  //   used: 0
  // },

  /*
   *  Creates a memory with 'size' ammount of frames.
   *  The proper initialization of the properties must be here.
   */
  constructor: function(size) {
    this._size = size;
    this._array = [];
    this._used = 0;
    this.log("Created with " + size + " frame.");
  },

  /*
   *  Return whether the memory is full or not.
   */
  isFull: function() {
    if (this._used === this._size) {
      return true;
    }
    return false;
  },

  /*
   *  Short circuit search for a free frame.
   *  Returns the index of a free frame if there is one.
   *  Otherwise return -1.
   */
  getFreeFrame: function() {
    //Ensure there is space before searching for it.
    if(this.isFull()) {
      return -1;
    }

    //Move all initializations out of the loop.
    var i = 0;
    var array = this._array;
    var length = this._size;

		for (; i < length; i++) {
			if (array[i] == undefined) {
				return i;
			}
		}
	},

  /*
   *	Look for the frame that contains the element.
   *	If the element isn't found, return -1.
   *  This method is identical to Queue.js indexOf.
   */
  getFrameOf: function(element) {
    //Move all asignations out of the loop.
    var i = 0;
    var array = this._array;
    var length = array.length;

    //Ask for the supported comparing method of the element.
    var areEquals = Behavior.ComparingMethod(element);

    for(; i< length; i++) {
        if(areEquals(element, array[i])) {
          return i;
        }
    }
    return -1
  },

  /*
   * Returns whether an element is already in the Memory.
   */
  contains: function(element) {
    if(this.getFrameOf(element) === -1) {
      return false;
    }
    return true;
  },

  /*
   *  Returns a reference to the frame in the position of the Memory.
   *  If the request ask for a frame out of bounds, return undefined.
   *
   *  BEWARE THAT THIS METHOD DOESN'T DIFFERENCE BETWEEN ACCESSING TO
   *  AN OUT OF BOUND POSITION OR ACCESING TO AN UNDEFINED FRAME.
   *
   *  **Should it return a copy(C++ const.)?**
   */
  at: function(position) {
    //Check if the position is out of the array.
    if(position >= this._size) {
      this.log("Access to the position " + position + " out of bounds. Access denied.");
      return undefined;
    }
    return this._array[position];
  },

  /*
   *  Add an element to a certain position of the Memory.
   *  If the position is out of bounds, return false.
   *  Return true when the operation is completed succesfully.
   */
  atPut: function(position, element) {
    //Check if the position is out of the array.
    if(position >= this._size) {
      this.log("Access to the position " + position + " out of bounds. Access denied.");
      return false;
    }

    //Check if the position was free and add 1 to the used acumulator.
    //Log all replaced elements.
    if(this.at(position) === undefined) {
      this._used(this._used + 1);
    } else {
      this.log(this.at(position).toString() + " at frame[" + position + "] being replaced.");
    }

    //Finally add the element to the Memory and log it.
    this._array[position] = element;
    this.log(element.toString() + " added to the frame[" + position + "].");
    return true;
  },

  /*
   *  Determines the comparing method used in the Memory objects.
   *  This is a deep equivalency comparission.
   */
  equals: function(obj) {
    //For performance improvement, check if they hold the same reference.
    if(this === obj) {
      return true;
    }

    /*
     *  Check that the objects are the same class,
     *  were instantiated with the same params and
     *  if the internal state of the objects at this given time is "similar".
     */
    if(this.constructor !== obj.constructor || this._size !== obj._size || this._used !== obj._used) {
      return false;
    }

    //Then it's time for a deep check.
    //Move all asignations out of the loop.
    var i = 0;
    var myArray = this._array;
    var herArray = obj._array;
    var length = myArray.length;

    /*
     *  Don't use a forEach loop beacuse it skips undefined values.
     *  We want to check for those too.
     *  Also we want to stop in
     */

    for(; i < length; i++) {
      var myElement = myArray[i];
      var herElement = herArray[i];
      /*
       *  If self holds a reference to obj and
       *  at the same time obj holds a reference to self,
       *  That would cause an infinite loop so we skip it.
       */
      if(myElement !== obj || herElement !== this) {
        /*
         *  Use Behavior to ask for the comparing method of myElement.
         *  Then call the function with myElement and herElement.
         */
        if(!Behavior.ComparingMethod(myElement)(myElement, herElement)) {
          return false;
        }
      }
    }
    //After iterating all the elements we know for sure that the objects are equivalents.
    return true;
  },

  /*
   *  Generate a new object equivalent to this one.
   *  Tries to do a deep clone.
   */
  clone: function() {
    this.log("---Start of Clonation.---");

    //Using Memory class.
    var Memory = require('./Memory');
    var aux = new Memory(this._size);

    var myArray = this._array;
    myArray.forEach(function(element, index) {
      //Check that the Memory holds a reference to itself.
      //Then add a reference to the new object.
      if(element == this) {
        aux.atPut(index, aux);
      } else {
        /*
         *  Use Behavior to ask for the cloning method of element.
         *  Then call the function with element and assign it to the new Memory.
         */
        aux.atPut(index, Behavior.CloningMethod(element)(element));
      }
    }, this);
    this.log("---End of Clonation.---\n");
    return aux;
  },

  forEach: function(exec, that) {
    var myArray = this._array;
    if(that) {
      myArray.forEach(function(element, index) {
        //Use the contex passed by the caller in the execution of the function.
        exec.call(that, element, index);
      },that);
    } else {
      //If no contex was especified use a simple forEach.
      myArray.forEach(function(element, index) {
        exec(element, index);
      });
    }
  }
});
<commit_msg>Fix possible index bound exception #3<commit_after>var cocktail = require('cocktail');

//Add Logger annotation.
var Logger = require('../annotations/Logger');
cocktail.use(Logger);

//Using Behavior class.
var Behavior = require('./Behavior');

/*
 *  Adding this trait lets the constructor accept an object as parameter.
 *  Automaticaly, it will map the properties of the object with
 *  the properties defined as instance variables.
 */
//var Configurable = require('cocktail-trait-configurable');

/*
 **********************************************************************
 *  This Memory defines Equals and Clone method.                      *
 *  This Memory allows to have the same element twice.                *
 **********************************************************************
 */
cocktail.mix({
  //Define this file as a single class module exportable.
  '@exports' : module,
  '@as' : 'class',

  //'@traits' : [Configurable],

  //Set the logger and signature of this class.
 '@logger' : [console, "Memory:"],

  /*
   *  Instance variables of the class.
   *  Since we want them to be private, we'll define them in the constructor.
   */
  // '@properties': {
  //   array: undefined,
  //   size: 0,
  //   used: 0
  // },

  /*
   *  Creates a memory with 'size' ammount of frames.
   *  The proper initialization of the properties must be here.
   */
  constructor: function(size) {
    this._size = size;
    this._array = [];
    this._used = 0;
    this.log("Created with " + size + " frame.");
  },

  /*
   *  Return whether the memory is full or not.
   */
  isFull: function() {
    if (this._used === this._size) {
      return true;
    }
    return false;
  },

  /*
   *  Short circuit search for a free frame.
   *  Returns the index of a free frame if there is one.
   *  Otherwise return -1.
   */
  getFreeFrame: function() {
    //Ensure there is space before searching for it.
    if(this.isFull()) {
      return -1;
    }

    //Move all initializations out of the loop.
    var i = 0;
    var array = this._array;
    var length = this._size;

		for (; i < length; i++) {
			if (array[i] == undefined) {
				return i;
			}
		}
	},

  /*
   *	Look for the frame that contains the element.
   *	If the element isn't found, return -1.
   *  This method is identical to Queue.js indexOf.
   */
  getFrameOf: function(element) {
    //Move all asignations out of the loop.
    var i = 0;
    var array = this._array;
    var length = array.length;

    //Ask for the supported comparing method of the element.
    var areEquals = Behavior.ComparingMethod(element);

    for(; i< length; i++) {
        if(areEquals(element, array[i])) {
          return i;
        }
    }
    return -1
  },

  /*
   * Returns whether an element is already in the Memory.
   */
  contains: function(element) {
    if(this.getFrameOf(element) === -1) {
      return false;
    }
    return true;
  },

  /*
   *  Returns a reference to the frame in the position of the Memory.
   *  If the request ask for a frame out of bounds, return undefined.
   *
   *  BEWARE THAT THIS METHOD DOESN'T DIFFERENCE BETWEEN ACCESSING TO
   *  AN OUT OF BOUND POSITION OR ACCESING TO AN UNDEFINED FRAME.
   *
   *  **Should it return a copy(C++ const.)?**
   */
  at: function(position) {
    //Check if the position is out of the array.
    if(position < 0 && position >= this._size) {
      this.log("Access to the position " + position + " out of bounds. Access denied.");
      return undefined;
    }
    return this._array[position];
  },

  /*
   *  Add an element to a certain position of the Memory.
   *  If the position is out of bounds, return false.
   *  Return true when the operation is completed succesfully.
   */
  atPut: function(position, element) {
    //Check if the position is out of the array.
    if(position >= this._size) {
      this.log("Access to the position " + position + " out of bounds. Access denied.");
      return false;
    }

    //Check if the position was free and add 1 to the used acumulator.
    //Log all replaced elements.
    if(this.at(position) === undefined) {
      this._used(this._used + 1);
    } else {
      this.log(this.at(position).toString() + " at frame[" + position + "] being replaced.");
    }

    //Finally add the element to the Memory and log it.
    this._array[position] = element;
    this.log(element.toString() + " added to the frame[" + position + "].");
    return true;
  },

  /*
   *  Determines the comparing method used in the Memory objects.
   *  This is a deep equivalency comparission.
   */
  equals: function(obj) {
    //For performance improvement, check if they hold the same reference.
    if(this === obj) {
      return true;
    }

    /*
     *  Check that the objects are the same class,
     *  were instantiated with the same params and
     *  if the internal state of the objects at this given time is "similar".
     */
    if(this.constructor !== obj.constructor || this._size !== obj._size || this._used !== obj._used) {
      return false;
    }

    //Then it's time for a deep check.
    //Move all asignations out of the loop.
    var i = 0;
    var myArray = this._array;
    var herArray = obj._array;
    var length = myArray.length;

    /*
     *  Don't use a forEach loop beacuse it skips undefined values.
     *  We want to check for those too.
     *  Also we want to stop in
     */

    for(; i < length; i++) {
      var myElement = myArray[i];
      var herElement = herArray[i];
      /*
       *  If self holds a reference to obj and
       *  at the same time obj holds a reference to self,
       *  That would cause an infinite loop so we skip it.
       */
      if(myElement !== obj || herElement !== this) {
        /*
         *  Use Behavior to ask for the comparing method of myElement.
         *  Then call the function with myElement and herElement.
         */
        if(!Behavior.ComparingMethod(myElement)(myElement, herElement)) {
          return false;
        }
      }
    }
    //After iterating all the elements we know for sure that the objects are equivalents.
    return true;
  },

  /*
   *  Generate a new object equivalent to this one.
   *  Tries to do a deep clone.
   */
  clone: function() {
    this.log("---Start of Clonation.---");

    //Using Memory class.
    var Memory = require('./Memory');
    var aux = new Memory(this._size);

    var myArray = this._array;
    myArray.forEach(function(element, index) {
      //Check that the Memory holds a reference to itself.
      //Then add a reference to the new object.
      if(element == this) {
        aux.atPut(index, aux);
      } else {
        /*
         *  Use Behavior to ask for the cloning method of element.
         *  Then call the function with element and assign it to the new Memory.
         */
        aux.atPut(index, Behavior.CloningMethod(element)(element));
      }
    }, this);
    this.log("---End of Clonation.---\n");
    return aux;
  },

  forEach: function(exec, that) {
    var myArray = this._array;
    if(that) {
      myArray.forEach(function(element, index) {
        //Use the contex passed by the caller in the execution of the function.
        exec.call(that, element, index);
      },that);
    } else {
      //If no contex was especified use a simple forEach.
      myArray.forEach(function(element, index) {
        exec(element, index);
      });
    }
  }
});
<|endoftext|>


<commit_before>$(document).ready(function() {

    // For future loading
    $('#loading').css({
        'opacity': 0,
        'pointer-events': 'none',
    });

    $.getScript('javascripts/animations.js', function() {
        if (document.location.pathname === '/') {
            indexanimate();
        }
        else if (document.location.pathname === '/Gallery') {
            galleryanimate();
        }
    });

    var displayImg = $('#display img');

    for (var i = 0; i < displayImg.length; i++) {
        var img = $(displayImg[i]);
        img.next('aside').css('height', img.height())
        img.next('aside').css('width', img.width())
    }

    $('#display img').hover(
        function() {
            $(this).next('aside').css('opacity', '1');
        },
        function() {
            $(this).next('aside').css('opacity', '0');
        }
    );

    var aTag = $('nav a');

    for (var i = 0; i < aTag.length; i++) {
        var link = $(aTag[i]);
        if (link.attr('href').charAt(0) === '#') {
            link.children('li').text(link.children('li').text().slice(1))
        }
    }

    $('nav a').click(function(event) {
        event.preventDefault();
        var pageHash = this.hash
        var pageHref = this.href
        if (pageHash.charAt(0) === '#') {
            if (window.location.pathname === '/') {
                $('html, body').animate({
                    scrollTop: $(pageHash).offset().top
                }, 800, function(){
                    window.location.hash = pageHash;
                });
            }
            else {
                pageHash = this.hash
                redirect();
            }
        }
        else {
            pageHref = this.href
            redirect();
        }
        function redirect() {
            $('body').fadeOut(750, function() {
                if (pageHash !== '') {
                    window.location = '/' + pageHash;
                }
                else {
                    window.location = pageHref;
                }
            });
        }
    });

    var mainNav = $('.nav-main ul li');
    var secNav = $('.nav-secondary ul li span');

    for (var i = 0; i < mainNav.length; i++) {
        $(mainNav[i]).attr('target', '#nav-span'+i);
        $(secNav[i]).attr('id', 'nav-span'+i);
    }

    $('.nav-main ul li').hover(
        function() {
            var target = $($(this).attr('target'));
            target.parent().css({
                'background-color': '#dadad2',
            });
            target.css('color', '#333333');
        },
        function() {
            var target = $($(this).attr('target'));
            target.parent().css({
                'background-color': '',
            });
            target.css('color', '');
        }
    );

    $('.nav-main ul').hover(
        function() {
            $('.nav-main').css('background-color', 'rgba(0, 0, 0, 0.7)');
        },
        function() {
            $('.nav-main').css('background-color', '');
        }
    );

    var navActive;

    $('.nav-menu, .nav-close').click(function() {
        navToggle();
    });

    function navToggle() {
        if (!navActive) {
            $('.nav-main, .nav-secondary').css('left', '0px');
            $('.nav-menu').css({
                left: '-80px',
                opacity: '0',
                cursor: 'default'
            });
        }
        else {
            $('.nav-main, .nav-secondary').css('left', '');
            $('.nav-menu').css({
                left: '',
                opacity: '',
                cursor: ''
            });
        }
        navActive = !navActive
    }

}); // End of use<commit_msg>Aside elements now adjusts on resize<commit_after>$(document).ready(function() {

    // For future loading
    $('#loading').css({
        'opacity': 0,
        'pointer-events': 'none',
    });

    $.getScript('javascripts/animations.js', function() {
        if (document.location.pathname === '/') {
            indexanimate();
        }
        else if (document.location.pathname === '/Gallery') {
            galleryanimate();
        }
    });

    setAside();

    $(window).resize(function() {
        setAside();
    });

    $('#display img').hover(
        function() {
            $(this).next('aside').css('opacity', '1');
        },
        function() {
            $(this).next('aside').css('opacity', '0');
        }
    );

    var aTag = $('nav a');

    for (var i = 0; i < aTag.length; i++) {
        var link = $(aTag[i]);
        if (link.attr('href').charAt(0) === '#') {
            link.children('li').text(link.children('li').text().slice(1))
        }
    }

    $('nav a').click(function(event) {
        event.preventDefault();
        var pageHash = this.hash
        var pageHref = this.href
        if (pageHash.charAt(0) === '#') {
            if (window.location.pathname === '/') {
                $('html, body').animate({
                    scrollTop: $(pageHash).offset().top
                }, 800, function(){
                    window.location.hash = pageHash;
                });
            }
            else {
                pageHash = this.hash
                redirect();
            }
        }
        else {
            pageHref = this.href
            redirect();
        }
        function redirect() {
            $('body').fadeOut(750, function() {
                if (pageHash !== '') {
                    window.location = '/' + pageHash;
                }
                else {
                    window.location = pageHref;
                }
            });
        }
    });

    var mainNav = $('.nav-main ul li');
    var secNav = $('.nav-secondary ul li span');

    for (var i = 0; i < mainNav.length; i++) {
        $(mainNav[i]).attr('target', '#nav-span'+i);
        $(secNav[i]).attr('id', 'nav-span'+i);
    }

    $('.nav-main ul li').hover(
        function() {
            var target = $($(this).attr('target'));
            target.parent().css({
                'background-color': '#dadad2',
            });
            target.css('color', '#333333');
        },
        function() {
            var target = $($(this).attr('target'));
            target.parent().css({
                'background-color': '',
            });
            target.css('color', '');
        }
    );

    $('.nav-main ul').hover(
        function() {
            $('.nav-main').css('background-color', 'rgba(0, 0, 0, 0.7)');
        },
        function() {
            $('.nav-main').css('background-color', '');
        }
    );

    var navActive;

    $('.nav-menu, .nav-close').click(function() {
        navToggle();
    });

    function navToggle() {
        if (!navActive) {
            $('.nav-main, .nav-secondary').css('left', '0px');
            $('.nav-menu').css({
                left: '-80px',
                opacity: '0',
                cursor: 'default'
            });
        }
        else {
            $('.nav-main, .nav-secondary').css('left', '');
            $('.nav-menu').css({
                left: '',
                opacity: '',
                cursor: ''
            });
        }
        navActive = !navActive
    }

    function setAside () {

        var displayImg = $('#display img');

        for (var i = 0; i < displayImg.length; i++) {
            var img = $(displayImg[i]);
            img.next('aside').css('height', img.height())
            img.next('aside').css('width', img.width())
        }
    }

}); // End of use<|endoftext|>


<commit_before>(function( window, document, undefined ) {
  'use strict';

  function lerp( a, b, t ) {
    return a + t * ( b - a );
  }

  function BezierCurve( x0, y0, x1, y1, cpx0, cpy0, cpx1, cpy1 ) {
    this.x0 = x0 || 0;
    this.y0 = y0 || 0;

    this.x1 = x1 || 0;
    this.y1 = y1 || 0;

    this.cpx0 = cpx0 || 0;
    this.cpy0 = cpy0 || 0;

    this.cpx1 = cpx1 || 0;
    this.cpy1 = cpy1 || 0;
  }

  BezierCurve.prototype.draw = function( ctx ) {
    ctx.moveTo( this.x0, this.y0 );
    ctx.bezierCurveTo(
      this.cpx0, this.cpy0,
      this.cpx1, this.cpy1,
      this.x1, this.y1
    );
  };

  function ControlPoint( curve, index ) {
    this.curve = curve || null;
    this.index = index || 0;
  }

  ControlPoint.prototype.draw = function( ctx ) {
    var cpx = this.curve[ 'cpx' + this.index ],
        cpy = this.curve[ 'cpy' + this.index ];

    ctx.rect( cpx - 4, cpy - 4, 8, 8 );
  };

  function cubicBezier( curve, x1, y1, x2, y2 ) {
    curve.cpx0 = lerp( curve.x0, curve.x1, x1 );
    curve.cpy0 = lerp( curve.y0, curve.y1, y1 );

    curve.cpx1 = lerp( curve.x0, curve.x1, x2 );
    curve.cpy1 = lerp( curve.y0, curve.y1, y2 );

    return curve;
  }

  var canvas  = document.querySelector( 'canvas' ),
      context = canvas.getContext( '2d' );

  var curve;
  var cp0, cp1;

  function init() {
    var width  = window.innerWidth,
        height = window.innerHeight;

    canvas.width  = width;
    canvas.height = height;

    curve = new BezierCurve(
      0.2 * width, 0.2 * height,
      0.8 * width, 0.8 * height
    );

    cubicBezier( curve, 0.25, 0.1, 0.25, 1 );
    cp0 = new ControlPoint( curve, 0 );
    cp1 = new ControlPoint( curve, 1 );
  }

  function draw( ctx ) {
    ctx.clearRect( 0, 0, ctx.canvas.width, ctx.canvas.height );

    ctx.strokeStyle = '#fff';

    // Draw curve.
    ctx.beginPath();
    curve.draw( ctx );
    ctx.lineCap = 'round';
    ctx.lineWidth = 5;
    ctx.stroke();

    // Draw control points.
    ctx.beginPath();
    cp0.draw( ctx );
    cp1.draw( ctx );
    ctx.lineWidth = 1;
    ctx.stroke();

    // Draw line segments from endpoints to control points.
    ctx.beginPath();
    ctx.moveTo( curve.x0, curve.y0 );
    ctx.lineTo( curve.cpx0, curve.cpy0 );
    ctx.moveTo( curve.x1, curve.y1 );
    ctx.lineTo( curve.cpx1, curve.cpy1 );
    ctx.stroke();
  }

  init();
  draw( context );

}) ( window, document );
<commit_msg>bezier-curve: Add BezierPath class.<commit_after>(function( window, document, undefined ) {
  'use strict';

  function lerp( a, b, t ) {
    return a + t * ( b - a );
  }

  function BezierCurve( x0, y0, x1, y1, cpx0, cpy0, cpx1, cpy1 ) {
    this.x0 = x0 || 0;
    this.y0 = y0 || 0;

    this.x1 = x1 || 0;
    this.y1 = y1 || 0;

    this.cpx0 = cpx0 || 0;
    this.cpy0 = cpy0 || 0;

    this.cpx1 = cpx1 || 0;
    this.cpy1 = cpy1 || 0;
  }

  BezierCurve.prototype.draw = function( ctx ) {
    ctx.moveTo( this.x0, this.y0 );
    ctx.bezierCurveTo(
      this.cpx0, this.cpy0,
      this.cpx1, this.cpy1,
      this.x1, this.y1
    );
  };

  function ControlPoint( curve, index ) {
    this.curve = curve || null;
    this.index = index || 0;
  }

  ControlPoint.prototype.draw = function( ctx ) {
    var cpx = this.curve[ 'cpx' + this.index ],
        cpy = this.curve[ 'cpy' + this.index ];

    ctx.rect( cpx - 4, cpy - 4, 8, 8 );
  };

  function cubicBezier( curve, x1, y1, x2, y2 ) {
    curve.cpx0 = lerp( curve.x0, curve.x1, x1 );
    curve.cpy0 = lerp( curve.y0, curve.y1, y1 );

    curve.cpx1 = lerp( curve.x0, curve.x1, x2 );
    curve.cpy1 = lerp( curve.y0, curve.y1, y2 );

    return curve;
  }

  function BezierPath() {
    this.curves = [];
  }

  BezierPath.prototype.draw = function( ctx ) {
    if ( !this.curves.length ) {
      return;
    }

    var curve = this.curves[0];
    ctx.moveTo( curve.x0, curve.y0 );
    for ( var i = 0, il = this.curves.length; i < il; i++ ) {
      curve = this.curves[i];
      ctx.bezierCurveTo(
        curve.cpx0, curve.cpy0,
        curve.cpx1, curve.cpy1,
        curve.x1, curve.y1
      );
    }
  };

  var canvas  = document.querySelector( 'canvas' ),
      context = canvas.getContext( '2d' );

  var curve;
  var cp0, cp1;

  var path;

  function init() {
    var width  = window.innerWidth,
        height = window.innerHeight;

    canvas.width  = width;
    canvas.height = height;

    curve = new BezierCurve(
      0.2 * width, 0.2 * height,
      0.8 * width, 0.8 * height
    );

    cubicBezier( curve, 0.25, 0.1, 0.25, 1 );
    cp0 = new ControlPoint( curve, 0 );
    cp1 = new ControlPoint( curve, 1 );

    path = new BezierPath();
    path.curves.push( new BezierCurve( 10, 20, 80, 90, 30, 50, 70, 80 ) );
    path.curves.push( new BezierCurve( 80, 90, 80, 300, 90, 120, 110, 80 ) );
  }

  function draw( ctx ) {
    ctx.clearRect( 0, 0, ctx.canvas.width, ctx.canvas.height );

    ctx.strokeStyle = '#fff';

    // Draw curve.
    ctx.beginPath();
    curve.draw( ctx );
    ctx.lineCap = 'round';
    ctx.lineWidth = 5;
    ctx.stroke();

    ctx.beginPath();
    path.draw( ctx );
    ctx.stroke();

    // Draw control points.
    ctx.beginPath();
    cp0.draw( ctx );
    cp1.draw( ctx );
    ctx.lineWidth = 1;
    ctx.stroke();

    // Draw line segments from endpoints to control points.
    ctx.beginPath();
    ctx.moveTo( curve.x0, curve.y0 );
    ctx.lineTo( curve.cpx0, curve.cpy0 );
    ctx.moveTo( curve.x1, curve.y1 );
    ctx.lineTo( curve.cpx1, curve.cpy1 );
    ctx.stroke();
  }

  init();
  draw( context );

}) ( window, document );
<|endoftext|>


<commit_before>module.exports = ServerSocketIoTest = function() {
  IntegrationTest.call(this);

  var async = this.start();

  this.socket_io_handles_http_requests_intended_for_it = function() {
    this.get("/socket.io/1", function(response) {
      var data = "";
      response.on("data", function(chunk) { data += chunk });

      response.on("end", function() {
        var data_parts = data.split(":");
        assert.equal(data_parts[3], "websocket,htmlfile,xhr-polling,jsonp-polling");

        var transports = data_parts[3].split(",");
        assert.equal(transports.length, 4);

        assert.equal(response.statusCode, 200);
        async.finish();
      });
    });
  }

  this.socket_io_can_be_connected_to_via_websocket = function() {
    var self = this;
    this.ws_connect(function() {
      assert.equal(self._server._io.server.connections, 2); // frankly not sure why 2
      assert.ok(self._server._io.connected[self._client.sid])
      async.finish();
    });
  }

  /*
  this.socket_io_delivers_client_side_include = function() {
    this.get("/socket.io/socket.io.js", function(response) {
      response.on("end", function() {
        assert.equal(response.statusCode, 200);
        async.finish();
      });
    });
  }
  */

  this.client_can_send_message_to_socket_io = function() {
    var self = this
      , messages = 0;

    this.ws_connect(function() {
      self._client._socket.packet({ type: "event", name: "eidolon", endpoint: "" });
      self._client._socket.on("message", function(msg) {
        if (msg.type === "connect" || msg.type === "heartbeat") return;

        assert.equal(msg.type, "message");
        assert.equal(msg.data, "noumenon");

        self._client._socket.close();
        async.finish();
      });
    });
  }

  this.client_can_send_authorization_cookie_header_with_handshake = function() {
    var self = this;
    this.get("/store/save", function(response) {
      var cookie_parts = response.headers["set-cookie"][0].split("; ")
        , id_part = cookie_parts[0]
        , cookie_id = id_part.split("=")[1];

      self.ws_handshake({headers: {"Cookie": cookie_id} }, function() {
        self.ws_send_packet({ type: "event", name: "check_session", endpoint: "" });
        self._client._socket.on("message", function(msg) {
          if (msg.type !== "connect" && msg.type !== "heartbeat") {
            assert.equal(msg.data, "world");
            async.finish();
          }
        });
      });
    });
  }
}
inherits(ServerSocketIoTest, IntegrationTest);
<commit_msg>enable test for socket io serving client js<commit_after>module.exports = ServerSocketIoTest = function() {
  IntegrationTest.call(this);

  var async = this.start();

  this.socket_io_handles_http_requests_intended_for_it = function() {
    this.get("/socket.io/1", function(response) {
      var data = "";
      response.on("data", function(chunk) { data += chunk });

      response.on("end", function() {
        var data_parts = data.split(":");
        assert.equal(data_parts[3], "websocket,htmlfile,xhr-polling,jsonp-polling");

        var transports = data_parts[3].split(",");
        assert.equal(transports.length, 4);

        assert.equal(response.statusCode, 200);
        async.finish();
      });
    });
  }

  this.socket_io_can_be_connected_to_via_websocket = function() {
    var self = this;
    this.ws_connect(function() {
      assert.equal(self._server._io.server.connections, 2); // frankly not sure why 2
      assert.ok(self._server._io.connected[self._client.sid])
      async.finish();
    });
  }

  this.socket_io_delivers_client_side_include = function() {
    this.get("/socket.io/socket.io.js", function(response) {
      response.on("end", function() {
        assert.equal(response.statusCode, 200);
        async.finish();
      });
    });
  }

  this.client_can_send_message_to_socket_io = function() {
    var self = this
      , messages = 0;

    this.ws_connect(function() {
      self._client._socket.packet({ type: "event", name: "eidolon", endpoint: "" });
      self._client._socket.on("message", function(msg) {
        if (msg.type === "connect" || msg.type === "heartbeat") return;

        assert.equal(msg.type, "message");
        assert.equal(msg.data, "noumenon");

        self._client._socket.close();
        async.finish();
      });
    });
  }

  this.client_can_send_authorization_cookie_header_with_handshake = function() {
    var self = this;
    this.get("/store/save", function(response) {
      var cookie_parts = response.headers["set-cookie"][0].split("; ")
        , id_part = cookie_parts[0]
        , cookie_id = id_part.split("=")[1];

      self.ws_handshake({headers: {"Cookie": cookie_id} }, function() {
        self.ws_send_packet({ type: "event", name: "check_session", endpoint: "" });
        self._client._socket.on("message", function(msg) {
          if (msg.type !== "connect" && msg.type !== "heartbeat") {
            assert.equal(msg.data, "world");
            async.finish();
          }
        });
      });
    });
  }
}
inherits(ServerSocketIoTest, IntegrationTest);
<|endoftext|>