<commit_before>/**
 * # Match
 *
 * Retrieve selector for a node.
 */

import { escapeValue } from './utilities'

const defaultIgnore = {
  attribute (attributeName) {
    return [
      'style',
      'data-reactid',
      'data-react-checksum'
    ].indexOf(attributeName) > -1
  }
}

/**
 * Get the path of the element
 *
 * @param  {HTMLElement} node    - [description]
 * @param  {Object}      options - [description]
 * @return {string}              - [description]
 */
export default function match (node, options) {

  const {
    root = document,
    skip = null,
    priority = ['id', 'class', 'href', 'src'],
    ignore = {}
  } = options

  const path = []
  var element = node
  var length = path.length
  var ignoreClass = false

  const skipCompare = skip && (Array.isArray(skip) ? skip : [skip]).map((entry) => {
    if (typeof entry !== 'function') {
      return (element) => element === entry
    }
    return entry
  })

  const skipChecks = (element) => {
    return skip && skipCompare.some((compare) => compare(element))
  }

  Object.keys(ignore).forEach((type) => {
    if (type === 'class') {
      ignoreClass = true
    }
    var predicate = ignore[type]
    if (typeof predicate === 'function') return
    if (typeof predicate === 'number') {
      predicate = predicate.toString()
    }
    if (typeof predicate === 'string') {
      predicate = new RegExp(escapeValue(predicate).replace(/\\/g, '\\\\'))
    }
    // check class-/attributename for regex
    ignore[type] = (name, value) => predicate.test(value)
  })

  if (ignoreClass) {
    const ignoreAttribute = ignore.attribute
    ignore.attribute = (name, value, defaultPredicate) => {
      return ignore.class(value) || ignoreAttribute && ignoreAttribute(name, value, defaultPredicate)
    }
  }

  while (element !== root) {
    if (skipChecks(element) !== true) {
      // ~ global
      if (checkAttributes(priority, element, ignore, path, root)) break
      if (checkTag(element, ignore, path, root)) break

      // ~ local
      checkAttributes(priority, element, ignore, path)
      if (path.length === length) {
        checkTag(element, ignore, path)
      }

      // define only one part each iteration
      if (path.length === length) {
        checkChilds(priority, element, ignore, path)
      }
    }

    element = element.parentNode
    length = path.length
  }

  if (element === root) {
    const pattern = findPattern(priority, element, ignore)
    path.unshift(pattern)
  }

  return path.join(' ')
}

/**
 * Extend path with attribute identifier
 *
 * @param  {Array.<string>} priority - [description]
 * @param  {HTMLElement}    element  - [description]
 * @param  {Object}         ignore   - [description]
 * @param  {Array.<string>} path     - [description]
 * @param  {HTMLElement}    parent   - [description]
 * @return {boolean}                 - [description]
 */
function checkAttributes (priority, element, ignore, path, parent = element.parentNode) {
  const pattern = findAttributesPattern(priority, element, ignore)
  if (pattern) {
    const matches = parent.querySelectorAll(pattern)
    if (matches.length === 1) {
      path.unshift(pattern)
      return true
    }
  }
  return false
}

/**
 * Lookup attribute identifier
 *
 * @param  {Array.<string>} priority - [description]
 * @param  {HTMLElement}    element  - [description]
 * @param  {Object}         ignore   - [description]
 * @return {string?}                 - [description]
 */
function findAttributesPattern (priority, element, ignore) {
  const attributes = element.attributes
  const sortedKeys = Object.keys(attributes).sort((curr, next) => {
    return priority.indexOf(attributes[curr].name) - priority.indexOf(attributes[next].name)
  })

  for (var i = 0, l = sortedKeys.length; i < l; i++) {
    const key = sortedKeys[i]
    const attribute = attributes[key]
    const attributeName = attribute.name
    const attributeValue = escapeValue(attribute.value)

    const currentIgnore = ignore[attributeName] || ignore.attribute
    const currentDefaultIgnore = defaultIgnore[attributeName] || defaultIgnore.attribute
    if (checkIgnore(currentIgnore, attributeName, attributeValue, currentDefaultIgnore)) {
      continue
    }

    var pattern = `[${attributeName}="${attributeValue}"]`

    if ((/\b\d/).test(attributeValue) === false) {
      if (attributeName === 'id') {
        pattern = `#${attributeValue}`
      }

      if (attributeName === 'class') {
        const className = attributeValue.trim().replace(/\s+/g, '.')
        pattern = `.${className}`
      }
    }

    return pattern
  }
  return null
}

/**
 * Extend path with tag identifier
 *
 * @param  {HTMLElement}    element - [description]
 * @param  {Object}         ignore  - [description]
 * @param  {Array.<string>} path    - [description]
 * @param  {HTMLElement}    parent  - [description]
 * @return {boolean}                - [description]
 */
function checkTag (element, ignore, path, parent = element.parentNode) {
  const pattern = findTagPattern(element, ignore)
  if (pattern) {
    const matches = parent.getElementsByTagName(pattern)
    if (matches.length === 1) {
      path.unshift(pattern)
      return true
    }
  }
  return false
}

/**
 * Lookup tag identifier
 *
 * @param  {HTMLElement} element - [description]
 * @param  {Object}      ignore  - [description]
 * @return {boolean}             - [description]
 */
function findTagPattern (element, ignore) {
  const tagName = element.tagName.toLowerCase()
  if (checkIgnore(ignore.tag, tagName)) {
    return null
  }
  return tagName
}

/**
 * Extend path with specific child identifier
 *
 * NOTE: 'childTags' is a custom property to use as a view filter for tags using 'adapter.js'
 *
 * @param  {Array.<string>} priority - [description]
 * @param  {HTMLElement}    element  - [description]
 * @param  {Object}         ignore   - [description]
 * @param  {Array.<string>} path     - [description]
 * @return {boolean}                 - [description]
 */
function checkChilds (priority, element, ignore, path) {
  const parent = element.parentNode
  const children = parent.childTags || parent.children
  for (var i = 0, l = children.length; i < l; i++) {
    const child = children[i]
    if (child === element) {
      const childPattern = findPattern(priority, child, ignore)
      if (!childPattern) {
        return console.warn(`
          Element couldn\'t be matched through strict ignore pattern!
        `, child, ignore, childPattern)
      }
      const pattern = `> ${childPattern}:nth-child(${i+1})`
      path.unshift(pattern)
      return true
    }
  }
  return false
}

/**
 * Lookup identifier
 *
 * @param  {Array.<string>} priority - [description]
 * @param  {HTMLElement}    element  - [description]
 * @param  {Object}         ignore   - [description]
 * @return {string}                  - [description]
 */
function findPattern (priority, element, ignore) {
  var pattern = findAttributesPattern(priority, element, ignore)
  if (!pattern) {
    pattern = findTagPattern(element, ignore)
  }
  return pattern
}

/**
 * Validate with custom and default functions
 *
 * @param  {Function} predicate        - [description]
 * @param  {string}   name             - [description]
 * @param  {string}   value            - [description]
 * @param  {Function} defaultPredicate - [description]
 * @return {boolean}                   - [description]
 */
function checkIgnore (predicate, name, value, defaultPredicate) {
  const check = predicate || defaultPredicate
  if (!check) {
    return false
  }
  return check(name, value || name, defaultPredicate)
}
<commit_msg>handle boolean values for predicates<commit_after>/**
 * # Match
 *
 * Retrieve selector for a node.
 */

import { escapeValue } from './utilities'

const defaultIgnore = {
  attribute (attributeName) {
    return [
      'style',
      'data-reactid',
      'data-react-checksum'
    ].indexOf(attributeName) > -1
  }
}

/**
 * Get the path of the element
 *
 * @param  {HTMLElement} node    - [description]
 * @param  {Object}      options - [description]
 * @return {string}              - [description]
 */
export default function match (node, options) {

  const {
    root = document,
    skip = null,
    priority = ['id', 'class', 'href', 'src'],
    ignore = {}
  } = options

  const path = []
  var element = node
  var length = path.length
  var ignoreClass = false

  const skipCompare = skip && (Array.isArray(skip) ? skip : [skip]).map((entry) => {
    if (typeof entry !== 'function') {
      return (element) => element === entry
    }
    return entry
  })

  const skipChecks = (element) => {
    return skip && skipCompare.some((compare) => compare(element))
  }

  Object.keys(ignore).forEach((type) => {
    if (type === 'class') {
      ignoreClass = true
    }
    var predicate = ignore[type]
    if (typeof predicate === 'function') return
    if (typeof predicate === 'number') {
      predicate = predicate.toString()
    }
    if (typeof predicate === 'string') {
      predicate = new RegExp(escapeValue(predicate).replace(/\\/g, '\\\\'))
    }
    if (typeof predicate === 'boolean') {
      predicate = predicate ? /(?:)/ : /.^/
    }
    // check class-/attributename for regex
    ignore[type] = (name, value) => predicate.test(value)
  })

  if (ignoreClass) {
    const ignoreAttribute = ignore.attribute
    ignore.attribute = (name, value, defaultPredicate) => {
      return ignore.class(value) || ignoreAttribute && ignoreAttribute(name, value, defaultPredicate)
    }
  }

  while (element !== root) {
    if (skipChecks(element) !== true) {
      // ~ global
      if (checkAttributes(priority, element, ignore, path, root)) break
      if (checkTag(element, ignore, path, root)) break

      // ~ local
      checkAttributes(priority, element, ignore, path)
      if (path.length === length) {
        checkTag(element, ignore, path)
      }

      // define only one part each iteration
      if (path.length === length) {
        checkChilds(priority, element, ignore, path)
      }
    }

    element = element.parentNode
    length = path.length
  }

  if (element === root) {
    const pattern = findPattern(priority, element, ignore)
    path.unshift(pattern)
  }

  return path.join(' ')
}

/**
 * Extend path with attribute identifier
 *
 * @param  {Array.<string>} priority - [description]
 * @param  {HTMLElement}    element  - [description]
 * @param  {Object}         ignore   - [description]
 * @param  {Array.<string>} path     - [description]
 * @param  {HTMLElement}    parent   - [description]
 * @return {boolean}                 - [description]
 */
function checkAttributes (priority, element, ignore, path, parent = element.parentNode) {
  const pattern = findAttributesPattern(priority, element, ignore)
  if (pattern) {
    const matches = parent.querySelectorAll(pattern)
    if (matches.length === 1) {
      path.unshift(pattern)
      return true
    }
  }
  return false
}

/**
 * Lookup attribute identifier
 *
 * @param  {Array.<string>} priority - [description]
 * @param  {HTMLElement}    element  - [description]
 * @param  {Object}         ignore   - [description]
 * @return {string?}                 - [description]
 */
function findAttributesPattern (priority, element, ignore) {
  const attributes = element.attributes
  const sortedKeys = Object.keys(attributes).sort((curr, next) => {
    return priority.indexOf(attributes[curr].name) - priority.indexOf(attributes[next].name)
  })

  for (var i = 0, l = sortedKeys.length; i < l; i++) {
    const key = sortedKeys[i]
    const attribute = attributes[key]
    const attributeName = attribute.name
    const attributeValue = escapeValue(attribute.value)

    const currentIgnore = ignore[attributeName] || ignore.attribute
    const currentDefaultIgnore = defaultIgnore[attributeName] || defaultIgnore.attribute
    if (checkIgnore(currentIgnore, attributeName, attributeValue, currentDefaultIgnore)) {
      continue
    }

    var pattern = `[${attributeName}="${attributeValue}"]`

    if ((/\b\d/).test(attributeValue) === false) {
      if (attributeName === 'id') {
        pattern = `#${attributeValue}`
      }

      if (attributeName === 'class') {
        const className = attributeValue.trim().replace(/\s+/g, '.')
        pattern = `.${className}`
      }
    }

    return pattern
  }
  return null
}

/**
 * Extend path with tag identifier
 *
 * @param  {HTMLElement}    element - [description]
 * @param  {Object}         ignore  - [description]
 * @param  {Array.<string>} path    - [description]
 * @param  {HTMLElement}    parent  - [description]
 * @return {boolean}                - [description]
 */
function checkTag (element, ignore, path, parent = element.parentNode) {
  const pattern = findTagPattern(element, ignore)
  if (pattern) {
    const matches = parent.getElementsByTagName(pattern)
    if (matches.length === 1) {
      path.unshift(pattern)
      return true
    }
  }
  return false
}

/**
 * Lookup tag identifier
 *
 * @param  {HTMLElement} element - [description]
 * @param  {Object}      ignore  - [description]
 * @return {boolean}             - [description]
 */
function findTagPattern (element, ignore) {
  const tagName = element.tagName.toLowerCase()
  if (checkIgnore(ignore.tag, tagName)) {
    return null
  }
  return tagName
}

/**
 * Extend path with specific child identifier
 *
 * NOTE: 'childTags' is a custom property to use as a view filter for tags using 'adapter.js'
 *
 * @param  {Array.<string>} priority - [description]
 * @param  {HTMLElement}    element  - [description]
 * @param  {Object}         ignore   - [description]
 * @param  {Array.<string>} path     - [description]
 * @return {boolean}                 - [description]
 */
function checkChilds (priority, element, ignore, path) {
  const parent = element.parentNode
  const children = parent.childTags || parent.children
  for (var i = 0, l = children.length; i < l; i++) {
    const child = children[i]
    if (child === element) {
      const childPattern = findPattern(priority, child, ignore)
      if (!childPattern) {
        return console.warn(`
          Element couldn\'t be matched through strict ignore pattern!
        `, child, ignore, childPattern)
      }
      const pattern = `> ${childPattern}:nth-child(${i+1})`
      path.unshift(pattern)
      return true
    }
  }
  return false
}

/**
 * Lookup identifier
 *
 * @param  {Array.<string>} priority - [description]
 * @param  {HTMLElement}    element  - [description]
 * @param  {Object}         ignore   - [description]
 * @return {string}                  - [description]
 */
function findPattern (priority, element, ignore) {
  var pattern = findAttributesPattern(priority, element, ignore)
  if (!pattern) {
    pattern = findTagPattern(element, ignore)
  }
  return pattern
}

/**
 * Validate with custom and default functions
 *
 * @param  {Function} predicate        - [description]
 * @param  {string}   name             - [description]
 * @param  {string}   value            - [description]
 * @param  {Function} defaultPredicate - [description]
 * @return {boolean}                   - [description]
 */
function checkIgnore (predicate, name, value, defaultPredicate) {
  const check = predicate || defaultPredicate
  if (!check) {
    return false
  }
  return check(name, value || name, defaultPredicate)
}
<|endoftext|>


<commit_before>// const copyObj = (dist, source) => {
//     Object.getOwnPropertyNames(source).forEach(name => {
//         Object.defineProperty(dist, name, Object.getOwnPropertyDescriptor(source, name))
//     })
//     return dist;
// }

// clear fields
const clearFields = (fields) => {
    let newFields = {}, fieldNames = [];
    if (fields.constructor === Array) {
        fields.map(field => {
            newFields[field] = {type: undefined, default: undefined}
        })
        fieldNames = fields.slice(0)
    } else if(fields.constructor === Object) {
        Object.keys(fields).map(name => {
            if (fields.hasOwnProperty(name)) {
                if (fieldNames.indexOf(name) === -1) {
                    fieldNames.push(name)
                }

                let item = fields[name]
                newFields[name] = {
                    type: item.type,
                    default: item.default
                }
                if (item.hasOwnProperty('type') &&  [undefined, null].indexOf(item.type) === -1) {
                    if ([undefined, null].indexOf(item.default) !== -1 || item.default.constructor !== item.type) {
                        newFields[name].default = item.type.call(null);
                    }
                }
            }
        })
    }
    return {
        fields: newFields,
        fieldNames
    }
}

// filter url
const filterUrl = (url) => {
    if (typeof url !== 'string') url = '';

    if (url.charAt(0) !== '/') {
        if (url.indexOf('http') !== 0 && url.indexOf('https') !== 0) {
            url = `/${url}`
        }
    }

    let _l = url.length - 1;
    if(_l > 0 && url.charAt(_l) === '/') {
        url = url.slice(0, _l)
    }

    return url
}

// init config
const initConfig = (config) => {
    let _config = {}
    if (config) {
        let {pkey} = config
        if (typeof pkey === 'string' && pkey !== '') {
            _config.pkey = pkey
        }
    }
    return _config
}

const serialize = (params) => {
    if (params.constructor === Object) {
        return Object.keys(params).reduce((prev, current, index) => {
            if (index === 0) {
                return `${prev}${current}=${params[current]}`
            }
            return `${prev}&${current}=${params[current]}`
        }, '?')
    }
    return ''
}

const fetchApi = (url, options = {}) => {
    options = options || {};

    if (!options.hasOwnProperty('method')) {
        options.method = 'GET';
    }
    return new Promise((resolve, rejected) => {
        fetch(url, options).then(response => {
            if (response.status >= 200 && response.status < 300) {
                resolve(response.json())
            } else {
                rejected(response)
            }
        }).catch(rejected)
    })
}

const EM2 = (m, config = {}) => {
    if (!m || typeof m !== 'object') {
        console.error('model is invalid, did you forget to pass model object to new EM2?')
        return {}
    }
    if (!m.hasOwnProperty('name')) {
        console.warn('model needs a name, could not register to models')
    }

    // init config, Model methods
    m = Object.assign(m, EM2.prototype, initConfig(config))

    // format url, fields
    m.url = filterUrl(m.url)
    let {fields, fieldNames} = clearFields(m.fields)
    m.fields = fields
    m.fieldNames = fieldNames

    // register Model and ModelNames
    if (m.hasOwnProperty('name')) {
        EM2.models[m.name] = m
        EM2.modelNames.push(m.name)
    }
    return m
}

// init EM2
EM2.models = {}
EM2.modelNames = []

EM2.fillFields = (modelName, params) => {
    let model = EM2.models[modelName]
    if (!model) {
        return params
    }
    let {fields} = model;
    Object.keys(fields).forEach(name => {
        if (params.hasOwnProperty(name)) {
            let item = fields[name]
            if (params[name].constructor !== item.type) {
                params[name] = item.type()
            }
            console.log(params[name], name)
        }
    })
}

// remove register
EM2.drop = (name) => {
    delete EM2.models[name]
    let {modelNames} = EM2
    return modelNames.splice(modelNames.indexOf(name), 1)
}

EM2.prototype = {
    pkey: '_id',
    findOne(id, params) {
        return fetchApi(`${this.url}/${id}${serialize(params)}`)
    },

    find(params) {
        return fetchApi(`${this.url}${serialize(params)}`)
    },

    update(id, params) {
        let options = EM2.fillFields(this.name, params)
        // let options = Object.assign({method: 'PUT'}, {body: JSON.stringify(params)})
        return fetchApi(`${this.url}/${id}`, options)
    },

    create(params) {
        let options = Object.assign({method: 'POST'}, {body: JSON.stringify(params)})
        return fetchApi(`${this.url}`, options)
    },

    save(params) {
        let method = 'POST'
        if (params && params[this.pkey]) {
            method = 'PUT'
            delete params[this.pkey]
        }
        let options = Object.assign({method}, {body: JSON.stringify(params)})
        return fetchApi(`${this.url}`, options)
    },

    destroy(id, params) {
        return fetchApi(`${this.url}/${id}${serialize(params)}`, {method: 'DELETE'})
    }
}

export default EM2<commit_msg>fill fields<commit_after>// const copyObj = (dist, source) => {
//     Object.getOwnPropertyNames(source).forEach(name => {
//         Object.defineProperty(dist, name, Object.getOwnPropertyDescriptor(source, name))
//     })
//     return dist;
// }

const trimFieldSlot = (item) => {
    let newItem = {
        type: item.type,
        default: item.default
    }
    if (item.hasOwnProperty('type') &&  [undefined, null].indexOf(item.type) === -1) {
        if ([undefined, null].indexOf(item.default) !== -1 || item.default.constructor !== item.type) {
            newItem.default = item.type.call(null);
        }
    }

    return newItem
}

const pushFieldNames = (fieldNames, name) => {
    let names = fieldNames.slice(0)
    if (names.indexOf(name) === -1) {
        names.push(name)
    }
    return names
}

// clear fields
const clearFields = (fields) => {
    let newFields = {}, fieldNames = [];
    if (fields.constructor === Array) {
        fields.map(field => {
            if (field.constructor === String) {
                newFields[field] = trimFieldSlot(field)
                fieldNames = pushFieldNames(fieldNames, field)
            } else if (field.constructor === Object && field.hasOwnProperty('name')){
                let name = field.name
                newFields[name] = trimFieldSlot(field)
                fieldNames = pushFieldNames(fieldNames, name)
            }
        })
    } else if(fields.constructor === Object) {
        Object.keys(fields).map(name => {
            if (fields.hasOwnProperty(name)) {
                newFields[name] = trimFieldSlot(fields[name])
                fieldNames = pushFieldNames(fieldNames, name)
            }
        })
    }
    return {
        fields: newFields,
        fieldNames
    }
}

// filter url
const filterUrl = (url) => {
    if (typeof url !== 'string') url = '';

    if (url.charAt(0) !== '/') {
        if (url.indexOf('http') !== 0 && url.indexOf('https') !== 0) {
            url = `/${url}`
        }
    }

    let _l = url.length - 1;
    if(_l > 0 && url.charAt(_l) === '/') {
        url = url.slice(0, _l)
    }

    return url
}

// init config
const initConfig = (config) => {
    let _config = {}
    if (config) {
        let {pkey} = config
        if (typeof pkey === 'string' && pkey !== '') {
            _config.pkey = pkey
        }
    }
    return _config
}

const serialize = (params) => {
    if (params.constructor === Object) {
        return Object.keys(params).reduce((prev, current, index) => {
            if (index === 0) {
                return `${prev}${current}=${params[current]}`
            }
            return `${prev}&${current}=${params[current]}`
        }, '?')
    }
    return ''
}

const fetchApi = (url, options = {}) => {
    options = options || {};

    if (!options.hasOwnProperty('method')) {
        options.method = 'GET';
    }
    return new Promise((resolve, rejected) => {
        fetch(url, options).then(response => {
            response.status >= 200 && response.status < 300 ?  resolve(response.json()) : rejected(response)
        }).catch(rejected)
    })
}

const EM2 = (m, config = {}) => {
    if (!m || typeof m !== 'object') {
        console.error('model is invalid, did you forget to pass model object to new EM2?')
        return {}
    }
    if (!m.hasOwnProperty('name')) {
        console.warn('model needs a name, could not register to models')
    }

    // init config, Model methods
    m = Object.assign(m, EM2.prototype, initConfig(config))

    // format url, fields
    m.url = filterUrl(m.url)
    let {fields, fieldNames} = clearFields(m.fields)
    m.fields = fields
    m.fieldNames = fieldNames

    // register Model and ModelNames
    if (m.hasOwnProperty('name')) {
        EM2.models[m.name] = m
        EM2.modelNames.push(m.name)
    }
    return m
}

// init EM2
EM2.models = {}
EM2.modelNames = []

EM2.fillFields = (modelName, params) => {
    let model = EM2.models[modelName]
    if (!model) {
        return params
    }
    let {fields} = model;
    Object.keys(fields).forEach(name => {
        if (params.hasOwnProperty(name)) {
            let item = fields[name]
            if (params[name].constructor !== item.type) {
                params[name] = item.type()
            }
            console.log(params[name], name)
        }
    })
}

// remove register
EM2.drop = (name) => {
    delete EM2.models[name]
    let {modelNames} = EM2
    return modelNames.splice(modelNames.indexOf(name), 1)
}

EM2.prototype = {
    pkey: '_id',
    findOne(id, params) {
        return fetchApi(`${this.url}/${id}${serialize(params)}`)
    },

    find(params) {
        return fetchApi(`${this.url}${serialize(params)}`)
    },

    update(id, params) {
        let options = EM2.fillFields(this.name, params)
        // let options = Object.assign({method: 'PUT'}, {body: JSON.stringify(params)})
        return fetchApi(`${this.url}/${id}`, options)
    },

    create(params) {
        let options = Object.assign({method: 'POST'}, {body: JSON.stringify(params)})
        return fetchApi(`${this.url}`, options)
    },

    save(params) {
        let method = 'POST'
        if (params && params[this.pkey]) {
            method = 'PUT'
            delete params[this.pkey]
        }
        let options = Object.assign({method}, {body: JSON.stringify(params)})
        return fetchApi(`${this.url}`, options)
    },

    destroy(id, params) {
        return fetchApi(`${this.url}/${id}${serialize(params)}`, {method: 'DELETE'})
    }
}

export default EM2<|endoftext|>


<commit_before>// ----------------------------------------------------------------------------------------------------------------- //
// no
// ----------------------------------------------------------------------------------------------------------------- //

var no = {};

// ----------------------------------------------------------------------------------------------------------------- //

no.inherits = function(child, parent) {
    var F = function() {};
    F.prototype = parent.prototype;
    child.prototype = new F();
    child.prototype.constructor = child;
};

// ----------------------------------------------------------------------------------------------------------------- //

/**
    @param {!Object} dest
    @param {...!Object} srcs
    @return {!Object}
*/
no.extend = function(dest) {
    var srcs = [].slice.call(arguments, 1);

    for (var i = 0, l = srcs.length; i < l; i++) {
        var src = srcs[i];
        for (var key in src) {
            dest[key] = src[key];
        }
    }

    return dest;
};

// ----------------------------------------------------------------------------------------------------------------- //

/**
    Do not clone functions, only data.
    @param {!Object} dest
    @param {...!Object} srcs
    @return {!Object}
*/
no.extendRecursive = function(dest) {
    var srcs = [].slice.call(arguments, 1);

    for (var i = 0, l = srcs.length; i < l; i++) {
        var src = srcs[i];
        for (var key in src) {
            var value = src[key];
            if (no.isArray(value)) {
                var ar = [];
                for (var j = 0; j < value.length; j++) {
                    var item = value[j];
                    if (typeof item === "object") {
                        ar[j] = no.extendRecursive({}, item);
                    } else {
                        ar[j] = item;
                    }
                }
                dest[key] = ar;
            }
            else if (typeof value === "object") {
                dest[key] = {};
                no.extendRecursive(dest[key], value);
            } else {
                dest[key] = value;
            }
        }
    }

    return dest;
};

// ----------------------------------------------------------------------------------------------------------------- //

no.isArray = Array.isArray; // TODO old browsers version

/**
    Пустая функция. No operation.
*/
no.pe = function() {};

// ----------------------------------------------------------------------------------------------------------------- //

/**
    @param {string} className
    @param {Element} context
    @return {Array.<Element>}
*/
no.byClass = function(className, context) {
    context = context || document;
    return context.getElementsByClassName(className); // FIXME: Поддержка старых браузеров.
};

// ----------------------------------------------------------------------------------------------------------------- //

/**
    @param {Element} oldNode
    @param {Element} newNode
*/
no.replaceNode = function(oldNode, newNode) {
    oldNode.parentNode.replaceChild(newNode, oldNode);
};

// ----------------------------------------------------------------------------------------------------------------- //

no.todo = function() {
    throw new Error('Unimplemented');
};

<commit_msg>Luke, use single quotes, spaces and instanceof Array<commit_after>// ----------------------------------------------------------------------------------------------------------------- //
// no
// ----------------------------------------------------------------------------------------------------------------- //

var no = {};

// ----------------------------------------------------------------------------------------------------------------- //

no.inherits = function(child, parent) {
    var F = function() {};
    F.prototype = parent.prototype;
    child.prototype = new F();
    child.prototype.constructor = child;
};

// ----------------------------------------------------------------------------------------------------------------- //

/**
    @param {!Object} dest
    @param {...!Object} srcs
    @return {!Object}
*/
no.extend = function(dest) {
    var srcs = [].slice.call(arguments, 1);

    for (var i = 0, l = srcs.length; i < l; i++) {
        var src = srcs[i];
        for (var key in src) {
            dest[key] = src[key];
        }
    }

    return dest;
};

// ----------------------------------------------------------------------------------------------------------------- //

/**
    Do not clone functions, only data.
    @param {!Object} dest
    @param {!Object} src
    @return {!Object}
*/
no.extendRecursive = function(dest, src) {
    for (var key in src) {
        var value = src[key];

        if (value instanceof Array) {
            var ar = dest[key] = [];
            for (var j = 0, m = value.length; j < m; j++) {
                var item = value[j];
                ar[j] = (typeof item === 'object') ? no.extendRecursive( {}, item ) : item;
            }

        } else if (typeof value === 'object') {
            dest[key] = no.extendRecursive( {}, value );

        } else {
            dest[key] = value;

        }
    }

    return dest;
};

// ----------------------------------------------------------------------------------------------------------------- //

/**
    Пустая функция. No operation.
*/
no.pe = function() {};

// ----------------------------------------------------------------------------------------------------------------- //

/**
    @param {string} className
    @param {Element} context
    @return {Array.<Element>}
*/
no.byClass = function(className, context) {
    context = context || document;
    return context.getElementsByClassName(className); // FIXME: Поддержка старых браузеров.
};

// ----------------------------------------------------------------------------------------------------------------- //

/**
    @param {Element} oldNode
    @param {Element} newNode
*/
no.replaceNode = function(oldNode, newNode) {
    oldNode.parentNode.replaceChild(newNode, oldNode);
};

// ----------------------------------------------------------------------------------------------------------------- //

no.todo = function() {
    throw new Error('Unimplemented');
};

<|endoftext|>


<commit_before>const flickrSizesAll = [
  { suffix: 's', longest: 75, square: true, label: 'Square' },
  { suffix: 'q', longest: 150, square: true, label: 'Large Square' },
  { suffix: 't', longest: 100, label: 'Thumbnail' },
  { suffix: 'm', longest: 240, label: 'Small' },
  { suffix: 'n', longest: 320, label: 'Small 320' },
  { suffix: '', longest: 500, label: 'Medium' },
  { suffix: 'z', longest: 640, label: 'Medium 640' },
  { suffix: 'c', longest: 800, since: '20120301', label: 'Medium 800' },
  { suffix: 'b', longest: 1024, since: '20100525', label: 'Large' },
  { suffix: 'h', longest: 1600, since: '20120301', label: 'Large 1600' },
  { suffix: 'k', longest: 2048, since: '20120301', label: 'Large 2048' },
  { suffix: 'o', customExtention: true, label: 'Original' },
];
const flickrSizes = flickrSizesAll.filter(s => !s.square).filter(s => s.longest);

/*
Example:
images_flickr: { photo: "15605014039", farm: 6, server: 5604, secret: "3aa65f125e", secret_h: "2c5f7cc664", secret_k: "31513b8bf6" }

Parameters:
  photo: string required
  farm: number/string required
  server: number/string required
  secret: string required
  secret_h: string optional
  secret_k: string optional
  before: enum('20100525' | '20120301') optional
    If '20100525', c, b, h, and k are not available.
    If '20120301', c, h, and k are not available.
Parmeters not supported:
  secret_o: string optional
  format_o: enum('jpg' | 'gif' | 'png') optional
*/
class FlickrSrcsetProvider {
  static createUrl(photoId, farmId, serverId, secret, suffix) {
    let suffix2 = '';
    if (suffix) suffix2 = `_${suffix}`;
    return `https://farm${farmId}.staticflickr.com/${serverId}/${photoId}_${secret}${suffix2}.jpg`;
  }
  static getImages(photo) {
    const maxO = Math.max(photo.data.size.width_o, photo.data.size.height_o);
    const imgsF = photo.data.images_flickr;
    const result = [];
    const availableSizes = flickrSizes.filter(s => {
      if (imgsF.before && s.since && imgsF.before <= s.since) return false;
      if (s.suffix === 'h' && !imgsF.secret_h) return false;
      if (s.suffix === 'k' && !imgsF.secret_k) return false;
      if (s.suffix === 'o') throw new Error('Not supported');
      return true;
    });
    for (const size of availableSizes) {
      let secret;
      switch (size.suffix) {
        case 'h':
          secret = imgsF.secret_h;
          break;
        case 'k':
          secret = imgsF.secret_k;
          break;
        default:
          secret = imgsF.secret;
          break;
      }
      const url = FlickrSrcsetProvider.createUrl(imgsF.photo, imgsF.farm, imgsF.server, secret, size.suffix);
      const max = Math.min(maxO, size.longest);
      result.push({ url, max });
    }
    return result;
  }
}

const picasaSizes = flickrSizes.map(s => s.longest);
/*
Example:
images_picasa: { lh: 4, dirs: "-_rqggmnuF6k/UWF0fBPaTnI/AAAAAAAAO2w/5KIyFyTmkIA", file: "5D3D8863%2520%25281280x1920%2529.jpg" }

Parameters:
  lh: number required
  dirs: string required
  file: string required
*/
class PicasaSrcsetProvider {
  static createUrl(lh, dirs, file, size) {
    return `https://lh${lh}.googleusercontent.com/${dirs}/s${size}/${file}`;
  }
  static getImages(photo) {
    const maxO = Math.max(photo.data.size.width_o, photo.data.size.height_o);
    const imgsP = photo.data.images_picasa;
    const result = [];
    for (const size of picasaSizes) {
      if (size >= maxO) {
        const url = PicasaSrcsetProvider.createUrl(imgsP.lh, imgsP.dirs, imgsP.file, maxO);
        result.push({ url, max: maxO });
        break;
      }
      const url = PicasaSrcsetProvider.createUrl(imgsP.lh, imgsP.dirs, imgsP.file, size);
      result.push({ url, max: size });
    }
    return result;
  }
}

export default class Photo {
  constructor(data) {
    this.data = data;
  }

  static loadPhotos(file, callback) {
    /* eslint-disable global-require */
    const actualFilename = require(`file?name=[name].json!./../data/${file}.yaml`);
    /* eslint-enable */

    window.fetch(actualFilename).then(res => {
      if (res.ok) {
        return res.json();
      }
      const error = new Error(res.statusText);
      error.response = res;
      throw error;
    })
    .then(arr => {
      const photos = arr.map(obj => new Photo(obj));
      callback(true, photos);
    })
    .catch(ex => {
      callback(false, ex);
    });
  }

  getSrcsetModel() {
    let images;
    if (this.data.images) {
      images = this.data.images.map(i => this.prepareSize(i));
    } else if (this.data.images_flickr) {
      images = FlickrSrcsetProvider.getImages(this).map(i => this.prepareSize(i));
    } else if (this.data.images_picasa) {
      images = PicasaSrcsetProvider.getImages(this).map(i => this.prepareSize(i));
    } else {
      throw new Error('images not specified');
    }
    return images;
  }
  getLargestImageAtMost(upperWidth, upperHeight) {
    const images = this.getSrcsetModel().slice(0);
    images.sort((a, b) => a.max - b.max);
    for (let j = images.length - 1; j >= 0; j--) {
      const img = images[j];
      if (img.width <= upperWidth && img.height <= upperHeight) return img;
    }
    // Nothing matches. Return smallest one.
    return images[0];
  }
  prepareSize(image) {
    const ret = { url: image.url };
    ret.width = this.calcWidthOrHeight(image, true);
    ret.height = this.calcWidthOrHeight(image, false);
    ret.max = Math.max(ret.width, ret.height);
    return ret;
  }
  calcWidthOrHeight(image, isWidth) {
    const wanted = isWidth ? 'width' : 'height';
    if (image[wanted]) return image[wanted];
    const other = isWidth ? 'height' : 'width';
    const ratio = isWidth ? this.getAspectRatio() : 1 / this.getAspectRatio();
    if (image[other]) return Math.round(image[other] * ratio);
    if (image.max) {
      if (ratio >= 1) return image.max;
      return Math.round(image.max * ratio);
    }
    throw new Error('Cannot calc image width/height');
  }
  getSrcSet() {
    return this.getSrcsetModel().map(i => `${i.url} ${i.width}w`).join(', ');
  }
  getAspectRatio() {
    return this.data.size.width_o / this.data.size.height_o;
  }
  match(re) {
    if (this.data.title.match(re)) return true;
    if (this.data.source.author.match(re)) return true;
    for (const note of this.data.notes) {
      if (note.match(re)) return true;
    }
    return false;
  }
}
<commit_msg>Refactor<commit_after>const flickrSizes = [
  { suffix: 's', longest: 75, square: true, label: 'Square' },
  { suffix: 'q', longest: 150, square: true, label: 'Large Square' },
  { suffix: 't', longest: 100, label: 'Thumbnail' },
  { suffix: 'm', longest: 240, label: 'Small' },
  { suffix: 'n', longest: 320, label: 'Small 320' },
  { suffix: '', longest: 500, label: 'Medium' },
  { suffix: 'z', longest: 640, label: 'Medium 640' },
  { suffix: 'c', longest: 800, since: '20120301', label: 'Medium 800' },
  { suffix: 'b', longest: 1024, since: '20100525', label: 'Large' },
  { suffix: 'h', longest: 1600, since: '20120301', label: 'Large 1600' },
  { suffix: 'k', longest: 2048, since: '20120301', label: 'Large 2048' },
  { suffix: 'o', customExtention: true, label: 'Original' },
];

/*
Example:
images_flickr: { photo: "15605014039", farm: 6, server: 5604, secret: "3aa65f125e", secret_h: "2c5f7cc664", secret_k: "31513b8bf6" }

Parameters:
  photo: string required
  farm: number/string required
  server: number/string required
  secret: string required
  secret_h: string optional
  secret_k: string optional
  before: enum('20100525' | '20120301') optional
    If '20100525', c, b, h, and k are not available.
    If '20120301', c, h, and k are not available.
Parmeters not supported:
  secret_o: string optional
  format_o: enum('jpg' | 'gif' | 'png') optional
*/
class FlickrSrcsetProvider {
  static createUrl(photoId, farmId, serverId, secret, suffix) {
    let suffix2 = '';
    if (suffix) suffix2 = `_${suffix}`;
    return `https://farm${farmId}.staticflickr.com/${serverId}/${photoId}_${secret}${suffix2}.jpg`;
  }
  static getImages(photo) {
    const maxO = Math.max(photo.data.size.width_o, photo.data.size.height_o);
    const imgsF = photo.data.images_flickr;
    const result = [];
    const availableSizes = flickrSizes.filter(s => {
      if (imgsF.before && s.since && imgsF.before <= s.since) return false;
      if (s.suffix === 'h' && !imgsF.secret_h) return false;
      if (s.suffix === 'k' && !imgsF.secret_k) return false;
      if (s.square) return false; // Skip square size because it trims images.
      if (s.suffix === 'o') return false; // Skip original size because it may be too huge.
      return true;
    });
    for (const size of availableSizes) {
      let secret;
      switch (size.suffix) {
        case 'h':
          secret = imgsF.secret_h;
          break;
        case 'k':
          secret = imgsF.secret_k;
          break;
        default:
          secret = imgsF.secret;
          break;
      }
      const url = FlickrSrcsetProvider.createUrl(imgsF.photo, imgsF.farm, imgsF.server, secret, size.suffix);
      const max = Math.min(maxO, size.longest);
      result.push({ url, max });
    }
    return result;
  }
}

const picasaSizes = flickrSizes.filter(s => !s.square).filter(s => s.longest).map(s => s.longest);
/*
Example:
images_picasa: { lh: 4, dirs: "-_rqggmnuF6k/UWF0fBPaTnI/AAAAAAAAO2w/5KIyFyTmkIA", file: "5D3D8863%2520%25281280x1920%2529.jpg" }

Parameters:
  lh: number required
  dirs: string required
  file: string required
*/
class PicasaSrcsetProvider {
  static createUrl(lh, dirs, file, size) {
    return `https://lh${lh}.googleusercontent.com/${dirs}/s${size}/${file}`;
  }
  static getImages(photo) {
    const maxO = Math.max(photo.data.size.width_o, photo.data.size.height_o);
    const imgsP = photo.data.images_picasa;
    const result = [];
    for (const size of picasaSizes) {
      if (size >= maxO) {
        const url = PicasaSrcsetProvider.createUrl(imgsP.lh, imgsP.dirs, imgsP.file, maxO);
        result.push({ url, max: maxO });
        break;
      }
      const url = PicasaSrcsetProvider.createUrl(imgsP.lh, imgsP.dirs, imgsP.file, size);
      result.push({ url, max: size });
    }
    return result;
  }
}

export default class Photo {
  constructor(data) {
    this.data = data;
  }

  static loadPhotos(file, callback) {
    /* eslint-disable global-require */
    const actualFilename = require(`file?name=[name].json!./../data/${file}.yaml`);
    /* eslint-enable */

    window.fetch(actualFilename).then(res => {
      if (res.ok) {
        return res.json();
      }
      const error = new Error(res.statusText);
      error.response = res;
      throw error;
    })
    .then(arr => {
      const photos = arr.map(obj => new Photo(obj));
      callback(true, photos);
    })
    .catch(ex => {
      callback(false, ex);
    });
  }

  getSrcsetModel() {
    let images;
    if (this.data.images) {
      images = this.data.images.map(i => this.prepareSize(i));
    } else if (this.data.images_flickr) {
      images = FlickrSrcsetProvider.getImages(this).map(i => this.prepareSize(i));
    } else if (this.data.images_picasa) {
      images = PicasaSrcsetProvider.getImages(this).map(i => this.prepareSize(i));
    } else {
      throw new Error('images not specified');
    }
    return images;
  }
  getLargestImageAtMost(upperWidth, upperHeight) {
    const images = this.getSrcsetModel().slice(0);
    images.sort((a, b) => a.max - b.max);
    for (let j = images.length - 1; j >= 0; j--) {
      const img = images[j];
      if (img.width <= upperWidth && img.height <= upperHeight) return img;
    }
    // Nothing matches. Return smallest one.
    return images[0];
  }
  prepareSize(image) {
    const ret = { url: image.url };
    ret.width = this.calcWidthOrHeight(image, true);
    ret.height = this.calcWidthOrHeight(image, false);
    ret.max = Math.max(ret.width, ret.height);
    return ret;
  }
  calcWidthOrHeight(image, isWidth) {
    const wanted = isWidth ? 'width' : 'height';
    if (image[wanted]) return image[wanted];
    const other = isWidth ? 'height' : 'width';
    const ratio = isWidth ? this.getAspectRatio() : 1 / this.getAspectRatio();
    if (image[other]) return Math.round(image[other] * ratio);
    if (image.max) {
      if (ratio >= 1) return image.max;
      return Math.round(image.max * ratio);
    }
    throw new Error('Cannot calc image width/height');
  }
  getSrcSet() {
    return this.getSrcsetModel().map(i => `${i.url} ${i.width}w`).join(', ');
  }
  getAspectRatio() {
    return this.data.size.width_o / this.data.size.height_o;
  }
  match(re) {
    if (this.data.title.match(re)) return true;
    if (this.data.source.author.match(re)) return true;
    for (const note of this.data.notes) {
      if (note.match(re)) return true;
    }
    return false;
  }
}
<|endoftext|>


<commit_before>import {slice} from "./array";

var noabort = {};

function Queue(size) {
  if (!((size = +size) >= 1)) throw new Error("invalid size");
  this._size = size;
  this._call =
  this._error = null;
  this._tasks = [];
  this._data = [];
  this._waiting =
  this._active =
  this._ended =
  this._start = 0; // inside a synchronous task callback?
}

Queue.prototype = queue.prototype = {
  constructor: Queue,
  defer: function(callback) {
    if (typeof callback !== "function") throw new Error("invalid callback");
    if (this._call) throw new Error("defer after await");
    if (this._error != null) return this;
    var t = slice.call(arguments, 1);
    t.push(callback);
    ++this._waiting, this._tasks.push(t);
    poke(this);
    return this;
  },
  abort: function() {
    if (this._error == null) abort(this, new Error("abort"));
    return this;
  },
  await: function(callback) {
    if (typeof callback !== "function") throw new Error("invalid callback");
    if (this._call) throw new Error("multiple await");
    this._call = function(error, results) { callback.apply(null, [error].concat(results)); };
    maybeNotify(this);
    return this;
  },
  awaitAll: function(callback) {
    if (typeof callback !== "function") throw new Error("invalid callback");
    if (this._call) throw new Error("multiple await");
    this._call = callback;
    maybeNotify(this);
    return this;
  }
};

function poke(q) {
  if (!q._start) {
    try { start(q); } // let the current task complete
    catch (e) {
      if (q._tasks[q._ended + q._active - 1]) abort(q, e); // task errored synchronously
      else if (!q._data) throw e; // await callback errored synchronously
    }
  }
}

function start(q) {
  while (q._start = q._waiting && q._active < q._size) {
    var i = q._ended + q._active,
        t = q._tasks[i],
        j = t.length - 1,
        c = t[j];
    t[j] = end(q, i);
    --q._waiting, ++q._active;
    t = c.apply(null, t);
    if (!q._tasks[i]) continue; // task finished synchronously
    q._tasks[i] = t || noabort;
  }
}

function end(q, i) {
  return function(e, r) {
    if (!q._tasks[i]) return; // ignore multiple callbacks
    --q._active, ++q._ended;
    q._tasks[i] = null;
    if (q._error != null) return; // ignore secondary errors
    if (e != null) {
      abort(q, e);
    } else {
      q._data[i] = r;
      if (q._waiting) poke(q);
      else maybeNotify(q);
    }
  };
}

function abort(q, e) {
  var i = q._tasks.length, t;
  q._error = e; // ignore active callbacks
  q._data = undefined; // allow gc
  q._waiting = NaN; // prevent starting

  while (--i >= 0) {
    if (t = q._tasks[i]) {
      q._tasks[i] = null;
      if (t.abort) {
        try { t.abort(); }
        catch (e) { /* ignore */ }
      }
    }
  }

  q._active = NaN; // allow notification
  maybeNotify(q);
}

function maybeNotify(q) {
  if (!q._active && q._call) {
    var d = q._data;
    q._data = undefined; // allow gc
    q._call(q._error, d);
  }
}

export default function queue(concurrency) {
  return new Queue(arguments.length ? +concurrency : Infinity);
}
<commit_msg>Consolidate logic.<commit_after>import {slice} from "./array";

var noabort = {};

function Queue(size) {
  this._size = size;
  this._call =
  this._error = null;
  this._tasks = [];
  this._data = [];
  this._waiting =
  this._active =
  this._ended =
  this._start = 0; // inside a synchronous task callback?
}

Queue.prototype = queue.prototype = {
  constructor: Queue,
  defer: function(callback) {
    if (typeof callback !== "function") throw new Error("invalid callback");
    if (this._call) throw new Error("defer after await");
    if (this._error != null) return this;
    var t = slice.call(arguments, 1);
    t.push(callback);
    ++this._waiting, this._tasks.push(t);
    poke(this);
    return this;
  },
  abort: function() {
    if (this._error == null) abort(this, new Error("abort"));
    return this;
  },
  await: function(callback) {
    if (typeof callback !== "function") throw new Error("invalid callback");
    if (this._call) throw new Error("multiple await");
    this._call = function(error, results) { callback.apply(null, [error].concat(results)); };
    maybeNotify(this);
    return this;
  },
  awaitAll: function(callback) {
    if (typeof callback !== "function") throw new Error("invalid callback");
    if (this._call) throw new Error("multiple await");
    this._call = callback;
    maybeNotify(this);
    return this;
  }
};

function poke(q) {
  if (!q._start) {
    try { start(q); } // let the current task complete
    catch (e) {
      if (q._tasks[q._ended + q._active - 1]) abort(q, e); // task errored synchronously
      else if (!q._data) throw e; // await callback errored synchronously
    }
  }
}

function start(q) {
  while (q._start = q._waiting && q._active < q._size) {
    var i = q._ended + q._active,
        t = q._tasks[i],
        j = t.length - 1,
        c = t[j];
    t[j] = end(q, i);
    --q._waiting, ++q._active;
    t = c.apply(null, t);
    if (!q._tasks[i]) continue; // task finished synchronously
    q._tasks[i] = t || noabort;
  }
}

function end(q, i) {
  return function(e, r) {
    if (!q._tasks[i]) return; // ignore multiple callbacks
    --q._active, ++q._ended;
    q._tasks[i] = null;
    if (q._error != null) return; // ignore secondary errors
    if (e != null) {
      abort(q, e);
    } else {
      q._data[i] = r;
      if (q._waiting) poke(q);
      else maybeNotify(q);
    }
  };
}

function abort(q, e) {
  var i = q._tasks.length, t;
  q._error = e; // ignore active callbacks
  q._data = undefined; // allow gc
  q._waiting = NaN; // prevent starting

  while (--i >= 0) {
    if (t = q._tasks[i]) {
      q._tasks[i] = null;
      if (t.abort) {
        try { t.abort(); }
        catch (e) { /* ignore */ }
      }
    }
  }

  q._active = NaN; // allow notification
  maybeNotify(q);
}

function maybeNotify(q) {
  if (!q._active && q._call) {
    var d = q._data;
    q._data = undefined; // allow gc
    q._call(q._error, d);
  }
}

export default function queue(concurrency) {
  if (concurrency == null) concurrency = Infinity;
  else if (!((concurrency = +concurrency) >= 1)) throw new Error("invalid concurrency");
  return new Queue(concurrency);
}
<|endoftext|>


<commit_before>import { takeEvery, delay, END } from 'redux-saga';
import { select, put, take } from 'redux-saga/effects';

import * as types from './constants/actionTypes';
import { moveShowCities } from './actions/mapActions';
import { discardFromHand, drawCardsInit, drawCardsHandle, epidemicIncrease,
  epidemicInfect, epidemicIntensify, discardBottomInfectionCard, discardTopInfectionCard,
  chooseCardsToDiscard, cardOverLimitComplete, shareCardsShowCandidates } from './actions/cardActions';
import { eradicateDisease, infectCity, infectNeighbor, initOutbreak, completeOutbreak,
  queueOutbreak, infectCities, useDiseaseCubes } from './actions/diseaseActions';
import { victory, defeat, passTurn } from './actions/globalActions';
import * as sel from './selectors';

function* showAvailableCities() {
  const cities = yield select(sel.getAvailableCities);
  yield put(moveShowCities(cities));
}

function* processMoveToCity(action) {
  const currentPlayer = yield select(sel.getCurrentPlayer);
  switch (action.source) {
    case 'direct':
      yield put(discardFromHand('city', currentPlayer.id, action.destinationId));
      break;
    case 'charter':
      yield put(discardFromHand('city', currentPlayer.id, action.originId));
      break;
  }
}

function* showShareCandidates() {
  const players = yield select(sel.getShareCandidates);
  yield put(shareCardsShowCandidates(players));

  const action = yield take([types.PLAYER_SHARE_CARD, types.PLAYER_SHARE_CANCEL]);
  if (action.type === types.PLAYER_SHARE_CARD) {
    yield waitToDiscardIfOverLimit(action.receiverId);
  }
}

function* checkForEradication({ color }) {
  const treatedAll = yield select(sel.treatedAllOfColor, color);
  const status = yield select(sel.getDiseaseStatus, color);
  if (treatedAll && status === 'cured') {
    yield put(eradicateDisease(color));
  }
}

function* checkForVictory() {
  const allCured = yield select(sel.areAllDiseasesCured);

  if (allCured) {
    yield yieldVictory();
  }
}

function* useCubes(cityId, color, count) {
  const cubesInCity = yield select(sel.getCubesInCity, cityId, color);
  const actualCubesToUse = Math.min(3 - cubesInCity, count);
  if (yield select(sel.isOutOfCubes, actualCubesToUse, color)) {
    yield yieldDefeat();
  } else {
    yield put(useDiseaseCubes(color, actualCubesToUse));
  }
}

function* yieldDefeat() {
  yield put(defeat());
  yield put(END);
}

function* yieldVictory() {
  yield put(victory());
  yield put(END);
}

function* yieldOutbreak(cityId, color) {
  yield put(initOutbreak(cityId, color));
  const neighbors = yield select(sel.getNeighborCities, cityId);

  for (let i = 0; i < neighbors.length; i++) {
    const id = neighbors[i].id;
    const cubesInNeighbor = yield select(sel.getCubesInCity, id, color);
    yield useCubes(id, color, 1);
    yield put(infectNeighbor(id, cityId, color));
    if (cubesInNeighbor === 3) {
      yield put(queueOutbreak(id, color));
    }
  }
  yield put(completeOutbreak(cityId));
  const nextOutbreakCityId = yield select(sel.getNextOutbreakCityId);
  if (nextOutbreakCityId) {
    yield yieldOutbreak(nextOutbreakCityId, color);
  }
}

function* infectOrOutbreak(cityId, color, count) {
  const cubesInCity = yield select(sel.getCubesInCity, cityId, color);
  yield useCubes(cityId, color, count);
  yield put(infectCity(cityId, color, count));
  if (cubesInCity + count > 3) {
    yield yieldOutbreak(cityId, color);
  }
}

function* yieldEpidemic() {
  yield put(epidemicIncrease());
  const infectionCityId = yield select(sel.getInfectionDeckBottom);
  const color = yield select(sel.getCityColor, infectionCityId);
  const status = yield select(sel.getDiseaseStatus, color);
  if (status !== 'eradicated') {
    yield put(epidemicInfect(infectionCityId));
    yield infectOrOutbreak(infectionCityId, color, 3);
    yield put(discardBottomInfectionCard());
  }
  yield put(epidemicIntensify());
}

function* drawPlayerCards() {
  const cards = yield select(sel.getPlayerCardsToDraw);
  if (cards.length < 2) {
    yield yieldDefeat();
  } else {
    const currentPlayer = yield select(sel.getCurrentPlayer);
    yield put(drawCardsInit(cards));
    yield delay(1000);
    yield put(drawCardsHandle(cards[0], currentPlayer.id));
    if (cards[0].cardType === 'epidemic') {
      yield yieldEpidemic();
    }
    yield delay(1000);
    yield put(drawCardsHandle(cards[1], currentPlayer.id));
    if (cards[1].cardType === 'epidemic') {
      yield yieldEpidemic();
    }
  }
}

function* infections() {
  yield put(infectCities());
  const infectionRate = yield select(sel.getInfectionRate);
  for (let i = 0; i < infectionRate; i++) {
    const city = yield select(sel.peekAtInfectionDeck);
    const color = yield select(sel.getCityColor, city);
    const status = yield select(sel.getDiseaseStatus, color);
    if (status !== 'eradicated') {
      yield infectOrOutbreak(city, color, 1);
    }
    yield put(discardTopInfectionCard());
  }
}

function* drawIfNoActionsLeft() {
  function* continueTurn() {
    yield infections();
    const nextPlayer = yield select(sel.getNextPlayer);
    yield put(passTurn(nextPlayer));
  }

  if (!(yield select(sel.isPlaying))) {
    // won
    return;
  }

  const actionsLeft = yield select(sel.getActionsLeft);
  if (actionsLeft === 0) {
    const currentPlayer = yield select(sel.getCurrentPlayer);
    yield drawPlayerCards();
    if (!(yield select(sel.isPlaying))) {
      // lost due to not enough player cards or won
      return;
    }
    yield waitToDiscardIfOverLimit(currentPlayer.id);
    yield continueTurn();
  }
}

function* waitToDiscardIfOverLimit(playerId) {
  if (yield select(sel.isOverHandLimit, playerId)) {
    yield put(chooseCardsToDiscard(playerId));
    yield take(types.CARD_OVER_LIMIT_DISCARD_COMPLETE);
  }
}

function* checkForInfectionRateDefeat() {
  if (yield select(sel.isInfectionRateOutOfBounds)) {
    yield yieldDefeat();
  }
}

function* checkForOutbreaksDefeat() {
  if (yield select(sel.isOutbreaksCountOutOfBounds)) {
    yield yieldDefeat();
  }
}

function* checkIfHandWentUnderLimit() {
  const player = yield select(sel.getPlayerOverHandLimit);
  if (player && !(yield select(sel.isOverHandLimit, player))) {
    yield put(cardOverLimitComplete());
  }
}

function* watchMoveInit() {
  yield* takeEvery(types.PLAYER_MOVE_INIT, showAvailableCities);
}

function* watchMoveToCity() {
  yield* takeEvery(types.PLAYER_MOVE_TO_CITY, processMoveToCity);
}

function* watchShareInit() {
  yield* takeEvery(types.PLAYER_SHARE_INIT, showShareCandidates);
}

function* watchForTreatEradication() {
  yield* takeEvery([types.PLAYER_TREAT_DISEASE, types.PLAYER_TREAT_ALL_DISEASE], checkForEradication);
}

function* watchForCureEradication() {
  yield* takeEvery(types.PLAYER_CURE_DISEASE, checkForEradication);
}

function* watchForVictory() {
  yield* takeEvery(types.PLAYER_CURE_DISEASE, checkForVictory);
}

function* watchActionsLeft() {
  yield* takeEvery(types.ACTIONS, drawIfNoActionsLeft);
}

function* watchForInfectionRateDefeat() {
  yield* takeEvery(types.EPIDEMIC_INCREASE, checkForInfectionRateDefeat);
}

function* watchForOutbreaksDefeat() {
  yield* takeEvery(types.OUTBREAK_INIT, checkForOutbreaksDefeat);
}

function* watchOverLimitDiscardComplete() {
  yield* takeEvery(types.CARD_DISCARD_FROM_HAND, checkIfHandWentUnderLimit);
}

export default function* rootSaga() {
  yield [
    watchMoveInit(),
    watchMoveToCity(),
    watchShareInit(),
    watchForTreatEradication(),
    watchForCureEradication(),
    watchForVictory(),
    watchActionsLeft(),
    watchForInfectionRateDefeat(),
    watchForOutbreaksDefeat(),
    watchOverLimitDiscardComplete()
  ];
}
<commit_msg>Refactor move city<commit_after>import { takeEvery, delay, END } from 'redux-saga';
import { select, put, take } from 'redux-saga/effects';

import * as types from './constants/actionTypes';
import { moveShowCities } from './actions/mapActions';
import { discardFromHand, drawCardsInit, drawCardsHandle, epidemicIncrease,
  epidemicInfect, epidemicIntensify, discardBottomInfectionCard, discardTopInfectionCard,
  chooseCardsToDiscard, cardOverLimitComplete, shareCardsShowCandidates } from './actions/cardActions';
import { eradicateDisease, infectCity, infectNeighbor, initOutbreak, completeOutbreak,
  queueOutbreak, infectCities, useDiseaseCubes } from './actions/diseaseActions';
import { victory, defeat, passTurn } from './actions/globalActions';
import * as sel from './selectors';

function* showAvailableCities() {
  const cities = yield select(sel.getAvailableCities);
  yield put(moveShowCities(cities));
  const action = yield take([types.PLAYER_MOVE_TO_CITY, types.PLAYER_MOVE_CANCEL]);
  if (action.type === types.PLAYER_MOVE_TO_CITY) {
    const currentPlayer = yield select(sel.getCurrentPlayer);
    switch (action.source) {
      case 'direct':
        yield put(discardFromHand('city', currentPlayer.id, action.destinationId));
        break;
      case 'charter':
        yield put(discardFromHand('city', currentPlayer.id, action.originId));
        break;
    }
  }
}

function* showShareCandidates() {
  const players = yield select(sel.getShareCandidates);
  yield put(shareCardsShowCandidates(players));

  const action = yield take([types.PLAYER_SHARE_CARD, types.PLAYER_SHARE_CANCEL]);
  if (action.type === types.PLAYER_SHARE_CARD) {
    yield waitToDiscardIfOverLimit(action.receiverId);
  }
}

function* checkForEradication({ color }) {
  const treatedAll = yield select(sel.treatedAllOfColor, color);
  const status = yield select(sel.getDiseaseStatus, color);
  if (treatedAll && status === 'cured') {
    yield put(eradicateDisease(color));
  }
}

function* checkForVictory() {
  const allCured = yield select(sel.areAllDiseasesCured);

  if (allCured) {
    yield yieldVictory();
  }
}

function* useCubes(cityId, color, count) {
  const cubesInCity = yield select(sel.getCubesInCity, cityId, color);
  const actualCubesToUse = Math.min(3 - cubesInCity, count);
  if (yield select(sel.isOutOfCubes, actualCubesToUse, color)) {
    yield yieldDefeat();
  } else {
    yield put(useDiseaseCubes(color, actualCubesToUse));
  }
}

function* yieldDefeat() {
  yield put(defeat());
  yield put(END);
}

function* yieldVictory() {
  yield put(victory());
  yield put(END);
}

function* yieldOutbreak(cityId, color) {
  yield put(initOutbreak(cityId, color));
  const neighbors = yield select(sel.getNeighborCities, cityId);

  for (let i = 0; i < neighbors.length; i++) {
    const id = neighbors[i].id;
    const cubesInNeighbor = yield select(sel.getCubesInCity, id, color);
    yield useCubes(id, color, 1);
    yield put(infectNeighbor(id, cityId, color));
    if (cubesInNeighbor === 3) {
      yield put(queueOutbreak(id, color));
    }
  }
  yield put(completeOutbreak(cityId));
  const nextOutbreakCityId = yield select(sel.getNextOutbreakCityId);
  if (nextOutbreakCityId) {
    yield yieldOutbreak(nextOutbreakCityId, color);
  }
}

function* infectOrOutbreak(cityId, color, count) {
  const cubesInCity = yield select(sel.getCubesInCity, cityId, color);
  yield useCubes(cityId, color, count);
  yield put(infectCity(cityId, color, count));
  if (cubesInCity + count > 3) {
    yield yieldOutbreak(cityId, color);
  }
}

function* yieldEpidemic() {
  yield put(epidemicIncrease());
  const infectionCityId = yield select(sel.getInfectionDeckBottom);
  const color = yield select(sel.getCityColor, infectionCityId);
  const status = yield select(sel.getDiseaseStatus, color);
  if (status !== 'eradicated') {
    yield put(epidemicInfect(infectionCityId));
    yield infectOrOutbreak(infectionCityId, color, 3);
    yield put(discardBottomInfectionCard());
  }
  yield put(epidemicIntensify());
}

function* drawPlayerCards() {
  const cards = yield select(sel.getPlayerCardsToDraw);
  if (cards.length < 2) {
    yield yieldDefeat();
  } else {
    const currentPlayer = yield select(sel.getCurrentPlayer);
    yield put(drawCardsInit(cards));
    yield delay(1000);
    yield put(drawCardsHandle(cards[0], currentPlayer.id));
    if (cards[0].cardType === 'epidemic') {
      yield yieldEpidemic();
    }
    yield delay(1000);
    yield put(drawCardsHandle(cards[1], currentPlayer.id));
    if (cards[1].cardType === 'epidemic') {
      yield yieldEpidemic();
    }
  }
}

function* infections() {
  yield put(infectCities());
  const infectionRate = yield select(sel.getInfectionRate);
  for (let i = 0; i < infectionRate; i++) {
    const city = yield select(sel.peekAtInfectionDeck);
    const color = yield select(sel.getCityColor, city);
    const status = yield select(sel.getDiseaseStatus, color);
    if (status !== 'eradicated') {
      yield infectOrOutbreak(city, color, 1);
    }
    yield put(discardTopInfectionCard());
  }
}

function* drawIfNoActionsLeft() {
  function* continueTurn() {
    yield infections();
    const nextPlayer = yield select(sel.getNextPlayer);
    yield put(passTurn(nextPlayer));
  }

  if (!(yield select(sel.isPlaying))) {
    // won
    return;
  }

  const actionsLeft = yield select(sel.getActionsLeft);
  if (actionsLeft === 0) {
    const currentPlayer = yield select(sel.getCurrentPlayer);
    yield drawPlayerCards();
    if (!(yield select(sel.isPlaying))) {
      // lost due to not enough player cards or won
      return;
    }
    yield waitToDiscardIfOverLimit(currentPlayer.id);
    yield continueTurn();
  }
}

function* waitToDiscardIfOverLimit(playerId) {
  if (yield select(sel.isOverHandLimit, playerId)) {
    yield put(chooseCardsToDiscard(playerId));
    yield take(types.CARD_OVER_LIMIT_DISCARD_COMPLETE);
  }
}

function* checkForInfectionRateDefeat() {
  if (yield select(sel.isInfectionRateOutOfBounds)) {
    yield yieldDefeat();
  }
}

function* checkForOutbreaksDefeat() {
  if (yield select(sel.isOutbreaksCountOutOfBounds)) {
    yield yieldDefeat();
  }
}

function* checkIfHandWentUnderLimit() {
  const player = yield select(sel.getPlayerOverHandLimit);
  if (player && !(yield select(sel.isOverHandLimit, player))) {
    yield put(cardOverLimitComplete());
  }
}

function* watchMoveInit() {
  yield* takeEvery(types.PLAYER_MOVE_INIT, showAvailableCities);
}

function* watchShareInit() {
  yield* takeEvery(types.PLAYER_SHARE_INIT, showShareCandidates);
}

function* watchForTreatEradication() {
  yield* takeEvery([types.PLAYER_TREAT_DISEASE, types.PLAYER_TREAT_ALL_DISEASE], checkForEradication);
}

function* watchForCureEradication() {
  yield* takeEvery(types.PLAYER_CURE_DISEASE, checkForEradication);
}

function* watchForVictory() {
  yield* takeEvery(types.PLAYER_CURE_DISEASE, checkForVictory);
}

function* watchActionsLeft() {
  yield* takeEvery(types.ACTIONS, drawIfNoActionsLeft);
}

function* watchForInfectionRateDefeat() {
  yield* takeEvery(types.EPIDEMIC_INCREASE, checkForInfectionRateDefeat);
}

function* watchForOutbreaksDefeat() {
  yield* takeEvery(types.OUTBREAK_INIT, checkForOutbreaksDefeat);
}

function* watchOverLimitDiscardComplete() {
  yield* takeEvery(types.CARD_DISCARD_FROM_HAND, checkIfHandWentUnderLimit);
}

export default function* rootSaga() {
  yield [
    watchMoveInit(),
    watchShareInit(),
    watchForTreatEradication(),
    watchForCureEradication(),
    watchForVictory(),
    watchActionsLeft(),
    watchForInfectionRateDefeat(),
    watchForOutbreaksDefeat(),
    watchOverLimitDiscardComplete()
  ];
}
<|endoftext|>


<commit_before>'use strict';

// load .env variables if set
require('dotenv').config();

const config = require('config');
const rp = require('request-promise-native');

module.exports.notify = function(restaurants) {

  let content = createContentFrom(restaurants),
      completeWebhook = process.env.SLACK.concat(process.env.WEBHOOK),
      options = {
        method: 'POST',
        uri: completeWebhook,
        form: {
          payload: JSON.stringify(content)
        }
      };

  return rp(options)
    .then(function(body) {
      return { message: 'Slack message sent' };
    })
    .catch(function(error) {
      console.log(error.message);
      throw error;
    });
};

function createContentFrom(restaurants) {
  return {
    'text'       : { toString: () => '' }, // only way to have no text without attachment fallback
    'channel'    : config.has('slack.channel') ? config.get('slack.channel') : undefined,
    'username'   : config.get('slack.username'),
    'icon_emoji' : config.get('slack.icon_emoji'),
    'attachments': restaurants.map(restaurant => (
      {
        'title'     : restaurant.name,
        'title_link': restaurant.url,
        'fallback'  : config.get('slack.fallback'),
        'color'     : restaurant.color,
        'mrkdwn_in' : ['text', 'pretext', 'fields'],
        'fields'    : restaurant.offers.map(offer => (
          {
            'title' : offer.title,
            'value' : offer.description,
            'short' : true,
          }
        )),
        'footer': restaurant.parser.name,
        'ts': Math.round((new Date).getTime() / 1000)
      }
    ))
  };
}

<commit_msg>Remove text field from payload<commit_after>'use strict';

// load .env variables if set
require('dotenv').config();

const config = require('config');
const rp = require('request-promise-native');

module.exports.notify = function(restaurants) {

  let content = createContentFrom(restaurants),
      completeWebhook = process.env.SLACK.concat(process.env.WEBHOOK),
      options = {
        method: 'POST',
        uri: completeWebhook,
        form: {
          payload: JSON.stringify(content)
        }
      };

  return rp(options)
    .then(function(body) {
      return { message: 'Slack message sent' };
    })
    .catch(function(error) {
      console.log(error.message);
      throw error;
    });
};

function createContentFrom(restaurants) {
  return {
    'channel'    : config.has('slack.channel') ? config.get('slack.channel') : undefined,
    'username'   : config.get('slack.username'),
    'icon_emoji' : config.get('slack.icon_emoji'),
    'attachments': restaurants.map(restaurant => (
      {
        'title'     : restaurant.name,
        'title_link': restaurant.url,
        'fallback'  : config.get('slack.fallback'),
        'color'     : restaurant.color,
        'mrkdwn_in' : ['text', 'pretext', 'fields'],
        'fields'    : restaurant.offers.map(offer => (
          {
            'title' : offer.title,
            'value' : offer.description,
            'short' : true,
          }
        )),
        'footer': restaurant.parser.name,
        'ts': Math.round((new Date).getTime() / 1000)
      }
    ))
  };
}

<|endoftext|>


<commit_before>class Space {
  constructor(iterable) {
    if (!iterable) {
      throw new Error();
    }
    this.table = new Map(iterable);
  }

  toString() {
    let object = Object.create(null);
    for (let [key, value] of this.table) {
      object[key] = value;
    }
    return JSON.stringify(object);
  }

  isValid() {
    return false;
  }

  clone() {
    throw new Error("clone() is not implemented");
  }

  hex() {
    throw new Error("hex() is not implemented");
  }

  rgb() {
    throw new Error("rgb() is not implemented");
  }

  hsl() {
    throw new Error("hsl() is not implemented");
  }

  hsv() {
    throw new Error("hsv() is not implemented");
  }

  cmyk() {
    throw new Error("cmyk() is not implemented");
  }

  get(key) {
    return this.table.get(key);
  }

  set(key, value) {
    return this.table.set(key, value);
  }

  getOrSet(key, value = null) {
    if (value == null) {
      return this.get(key);
    }
    this.set(key, value);
    return this;
  }

  values() {
    return this.table.values();
  }
}

export class Rgb extends Space {
  static KEY_R = "r";
  static KEY_G = "g";
  static KEY_B = "b";

  constructor(r, g, b) {
    super([[Rgb.KEY_R, r], [Rgb.KEY_G, g], [Rgb.KEY_B, b]]);
  }

  toString() {
    return `rgb(${this.r()}, ${this.g()}, ${this.b()})`;
  }

  isValid() {
    let values = this.values();
    for (let value of values) {
      if (value < 0x00 || value > 0xFF) {
        return false;
      }
    }
    return true;
  }

  clone() {
    return new Rgb(this.r(), this.g(), this.b());
  }

  r(value = null) {
    return this.getOrSet(Rgb.KEY_R, value);
  }

  g(value = null) {
    return this.getOrSet(Rgb.KEY_G, value);
  }

  b(value = null) {
    return this.getOrSet(Rgb.KEY_B, value);
  }

  hex() {
    let parts = [];
    for (let value of this.values()) {
      let text = value.toString(16);
      if (text.length === 1) {
        text = `0${text}`;
      }
      parts.push(text.toUppderCase());
    }
    return `#${parts.join('')}`;
  }

  rgb() {
    return this;
  }

  hsl() {
    let r = this.r() / 0xFF;
    let g = this.g() / 0xFF;
    let b = this.b() / 0xFF;

    let max   = Math.max(r, g, b);
    let min   = Math.min(r, g, b);
    let delta = max - min;
    if (delta === 0) {
      return new Hsl(0, 0, max);
    }

    let h = 0;
    if (max === r) {
      h = 60 * ((g - b) / delta % 360);
    } else if (max === g) {
      h = 60 * ((b - r) / delta + 2);
    } else if (max === b) {
      h = 60 * ((r - g) / delta + 4);
    }

    let l = (max + min) / 2;

    let s = delta / (1 - Math.abs(2 * l - 1))

    return new Hsl(h, s, l);
  }

  hsv() {
    let r = this.r() / 0xFF;
    let g = this.g() / 0xFF;
    let b = this.b() / 0xFF;

    let max   = Math.max(r, g, b);
    let min   = Math.min(r, g, b);
    let delta = max - min;
    if (delta === 0) {
      return new Hsv(0, 0, max);
    }

    let h = 0;
    if (max === r) {
      h = 60 * (((g - b) / delta) % 6);
    } else if (max === g) {
      h = 60 * (((b - r) / delta) + 2);
    } else if (max === b) {
      h = 60 * (((r - g) / delta) + 4);
    }

    let s = 0;
    if (max !== 0) {
      s = delta / max;
    }

    let v = max;
    return new Hsv(h, s, v);
  }

  cmyk() {
    let r = this.r() / 0xFF;
    let g = this.g() / 0xFF;
    let b = this.b() / 0xFF;

    let k = 1 - Math.max(r, g, b);
    if (k === 1) {
      return new Cmyk(0, 0, 0, 1);
    }

    let c = (1 - r - k) / (1 - k);
    let m = (1 - g - k) / (1 - k);
    let y = (1 - b - k) / (1 - k);
    return new Cmyk(c, m, y, k);
  }
}

export class Hsl extends Space {
  static KEY_H = "h";
  static KEY_S = "s";
  static KEY_L = "l";

  constructor(h, s, l) {
    super([[Hsl.KEY_H, h], [Hsl.KEY_S, s], [Hsl.KEY_L, l]]);
  }

  toString() {
    return `hsl(${this.h()}, ${this.s()}%, ${this.l()}%)`;
  }

  clone() {
    return new Hsl(this.h(), this.s(), this.l());
  }

  isValid() {
    let h = this.h();
    if (!Number.isFinite(h) || h < 0 || h > 360) {
      return false;
    }

    let s = this.s();
    if (!Number.isFinite(s) || s < 0 || s > 100) {
      return false;
    }

    let l = this.l();
    if (!Number.isFinite(l) || l < 0 || l > 100) {
      return false;
    }
    return true;
  }

  h(value = null) {
    return this.getOrSet(Hsl.KEY_H, value);
  }

  s(value = null) {
    return this.getOrSet(Hsl.KEY_S, value);
  }

  l(value = null) {
    return this.getOrSet(Hsl.KEY_L, value);
  }

  hex() {
    return this.rgb().hex();
  }

  rgb() {
    let h = this.h();
    let s = this.s();
    let l = this.l();
    if (s === 0) {
      let value = Math.round(l * 0xFF);
      return new Rgb(value, value, value);
    }

    let m2 = 0;
    if (l < 0.5) {
      m2 = l * (s + 1);
    } else {
      m2 = l + s - l * s;
    }

    let m1 = 2 * l - m2;
    let r = Math.round(255 * Hsl.hueToRgb(m1, m2, h / 360 + 1 / 3));
    let g = Math.round(255 * Hsl.hueToRgb(m1, m2, h / 360));
    let b = Math.round(255 * Hsl.hueToRgb(m1, m2, h / 360 - 1 / 3));
    return new Rgb(r, g, b)
  }

  hsl() {
    return this;
  }

  hsv() {
    return this.rgb().hsv();
  }

  cmyk() {
    return this.rgb().cmyk();
  }

  static hueToRgb(m1, m2, h) {
    if (h < 0) {
      h += 1;
    } else if (h > 1) {
      h -= 1;
    }

    if (h * 6 < 1) {
      return m1 + (m2 - m1) * h * 6;
    } else if (h * 2 < 1) {
      return m2;
    } else if (h * 3 < 2) {
      return m1 + (m2 - m1) * (2 / 3 - h) * 6;
    }
    return m1;
  }
}

export class Hsv extends Space {
  static KEY_H = 'h';
  static KEY_S = 's';
  static KEY_V = 'v';

  constructor(h, s, v) {
    super([[Hsv.KEY_H, h], [Hsv.KEY_S, s], [Hsv.KEY_V, v]]);
  }

  h(value = null) {
    return this.getOrSet(Hsv.KEY_H, value);
  }

  s(value = null) {
    return this.getOrSet(Hsv.KEY_S, value);
  }

  v(value = null) {
    return this.getOrSet(Hsv.KEY_V, value);
  }

  rgb() {
    let h = this.h();
    let s = this.s();
    let v = this.v();

    let c = v * s;
    let x = c * (1 - Math.abs(((h / 60) % 2) - 1));
    let m = v - c;

    // TODO: Need to rafactor the following switch statement
    let [r, g, b] = [0, 0, 0];
    switch (!!(h / 60)) {
      case 0:
        [r, g, b] = [c, x, 0];
        break;
      case 1:
        [r, g, b] = [x, c, 0];
        break;
      case 2:
        [r, g, b] = [0, c, x];
        break;
      case 3:
        [r, g, b] = [0, x, c];
        break;
      case 4:
        [r, g, b] = [x, 0, c];
        break;
      case 5:
        [r, g, b] = [c, 0, x];
        break;
      default:
        break;
    }

    return new Rgb((r + m) * 0xFF, (g + m) * 0xFF, (b + m) * 0xFF);
  }
}

export class Cmyk extends Space {
  static KEY_C = 'c';
  static KEY_M = 'm';
  static KEY_Y = 'y';
  static KEY_K = 'k';

  constructor(c, m, y, k) {
    super([[Cmyk.KEY_C, c], [Cmyk.KEY_M, m], [Cmyk.KEY_Y, y], [Cmyk.KEY_K]]);
  }

  isValid() {
    for (let value of this.values()) {
      if (!Number.isFinite(value)) {
        return false;
      }
      if (value < 0 || value > 1) {
        return false;
      }
    }
    return true;
  }

  clone() {
    return new Cmyk(this.c(), this.m(), this.y(), this.k());
  }

  c(value = null) {
    return this.getOrSet(Cmyk.KEY_C, value);
  }

  m(value = null) {
    return this.getOrSet(Cmyk.KEY_M, value);
  }

  y(value = null) {
    return this.getOrSet(Cmyk.KEY_Y, value);
  }

  k(value = null) {
    return this.getOrSet(Cmyk.KEY_K, value);
  }

  hex() {
    return this.rgb().hex();
  }

  rgb() {
    let k = this.k();
    if (k === 1) {
      return new Rgb(0, 0, 0);
    }

    let r = 0xFF * (1 - this.c()) * (1 - k);
    let g = 0xFF * (1 - this.m()) * (1 - k);
    let b = 0xFF * (1 - this.y()) * (1 - k);
    return new Rgb(r, g, b);
  }

  hsl() {
    return this.rgb().hsl();
  }

  hsv() {
    return this.rgb().hsv();
  }

  cmyk() {
    return this;
  }
}
<commit_msg>Fixed a but that value of k is missing in a constructor<commit_after>class Space {
  constructor(iterable) {
    if (!iterable) {
      throw new Error();
    }
    this.table = new Map(iterable);
  }

  toString() {
    let object = Object.create(null);
    for (let [key, value] of this.table) {
      object[key] = value;
    }
    return JSON.stringify(object);
  }

  isValid() {
    return false;
  }

  clone() {
    throw new Error("clone() is not implemented");
  }

  hex() {
    throw new Error("hex() is not implemented");
  }

  rgb() {
    throw new Error("rgb() is not implemented");
  }

  hsl() {
    throw new Error("hsl() is not implemented");
  }

  hsv() {
    throw new Error("hsv() is not implemented");
  }

  cmyk() {
    throw new Error("cmyk() is not implemented");
  }

  get(key) {
    return this.table.get(key);
  }

  set(key, value) {
    return this.table.set(key, value);
  }

  getOrSet(key, value = null) {
    if (value == null) {
      return this.get(key);
    }
    this.set(key, value);
    return this;
  }

  values() {
    return this.table.values();
  }
}

export class Rgb extends Space {
  static KEY_R = "r";
  static KEY_G = "g";
  static KEY_B = "b";

  constructor(r, g, b) {
    super([[Rgb.KEY_R, r], [Rgb.KEY_G, g], [Rgb.KEY_B, b]]);
  }

  toString() {
    return `rgb(${this.r()}, ${this.g()}, ${this.b()})`;
  }

  isValid() {
    let values = this.values();
    for (let value of values) {
      if (value < 0x00 || value > 0xFF) {
        return false;
      }
    }
    return true;
  }

  clone() {
    return new Rgb(this.r(), this.g(), this.b());
  }

  r(value = null) {
    return this.getOrSet(Rgb.KEY_R, value);
  }

  g(value = null) {
    return this.getOrSet(Rgb.KEY_G, value);
  }

  b(value = null) {
    return this.getOrSet(Rgb.KEY_B, value);
  }

  hex() {
    let parts = [];
    for (let value of this.values()) {
      let text = value.toString(16);
      if (text.length === 1) {
        text = `0${text}`;
      }
      parts.push(text.toUppderCase());
    }
    return `#${parts.join('')}`;
  }

  rgb() {
    return this;
  }

  hsl() {
    let r = this.r() / 0xFF;
    let g = this.g() / 0xFF;
    let b = this.b() / 0xFF;

    let max   = Math.max(r, g, b);
    let min   = Math.min(r, g, b);
    let delta = max - min;
    if (delta === 0) {
      return new Hsl(0, 0, max);
    }

    let h = 0;
    if (max === r) {
      h = 60 * ((g - b) / delta % 360);
    } else if (max === g) {
      h = 60 * ((b - r) / delta + 2);
    } else if (max === b) {
      h = 60 * ((r - g) / delta + 4);
    }

    let l = (max + min) / 2;

    let s = delta / (1 - Math.abs(2 * l - 1))

    return new Hsl(h, s, l);
  }

  hsv() {
    let r = this.r() / 0xFF;
    let g = this.g() / 0xFF;
    let b = this.b() / 0xFF;

    let max   = Math.max(r, g, b);
    let min   = Math.min(r, g, b);
    let delta = max - min;
    if (delta === 0) {
      return new Hsv(0, 0, max);
    }

    let h = 0;
    if (max === r) {
      h = 60 * (((g - b) / delta) % 6);
    } else if (max === g) {
      h = 60 * (((b - r) / delta) + 2);
    } else if (max === b) {
      h = 60 * (((r - g) / delta) + 4);
    }

    let s = 0;
    if (max !== 0) {
      s = delta / max;
    }

    let v = max;
    return new Hsv(h, s, v);
  }

  cmyk() {
    let r = this.r() / 0xFF;
    let g = this.g() / 0xFF;
    let b = this.b() / 0xFF;

    let k = 1 - Math.max(r, g, b);
    if (k === 1) {
      return new Cmyk(0, 0, 0, 1);
    }

    let c = (1 - r - k) / (1 - k);
    let m = (1 - g - k) / (1 - k);
    let y = (1 - b - k) / (1 - k);
    return new Cmyk(c, m, y, k);
  }
}

export class Hsl extends Space {
  static KEY_H = "h";
  static KEY_S = "s";
  static KEY_L = "l";

  constructor(h, s, l) {
    super([[Hsl.KEY_H, h], [Hsl.KEY_S, s], [Hsl.KEY_L, l]]);
  }

  toString() {
    return `hsl(${this.h()}, ${this.s()}%, ${this.l()}%)`;
  }

  clone() {
    return new Hsl(this.h(), this.s(), this.l());
  }

  isValid() {
    let h = this.h();
    if (!Number.isFinite(h) || h < 0 || h > 360) {
      return false;
    }

    let s = this.s();
    if (!Number.isFinite(s) || s < 0 || s > 100) {
      return false;
    }

    let l = this.l();
    if (!Number.isFinite(l) || l < 0 || l > 100) {
      return false;
    }
    return true;
  }

  h(value = null) {
    return this.getOrSet(Hsl.KEY_H, value);
  }

  s(value = null) {
    return this.getOrSet(Hsl.KEY_S, value);
  }

  l(value = null) {
    return this.getOrSet(Hsl.KEY_L, value);
  }

  hex() {
    return this.rgb().hex();
  }

  rgb() {
    let h = this.h();
    let s = this.s();
    let l = this.l();
    if (s === 0) {
      let value = Math.round(l * 0xFF);
      return new Rgb(value, value, value);
    }

    let m2 = 0;
    if (l < 0.5) {
      m2 = l * (s + 1);
    } else {
      m2 = l + s - l * s;
    }

    let m1 = 2 * l - m2;
    let r = Math.round(255 * Hsl.hueToRgb(m1, m2, h / 360 + 1 / 3));
    let g = Math.round(255 * Hsl.hueToRgb(m1, m2, h / 360));
    let b = Math.round(255 * Hsl.hueToRgb(m1, m2, h / 360 - 1 / 3));
    return new Rgb(r, g, b)
  }

  hsl() {
    return this;
  }

  hsv() {
    return this.rgb().hsv();
  }

  cmyk() {
    return this.rgb().cmyk();
  }

  static hueToRgb(m1, m2, h) {
    if (h < 0) {
      h += 1;
    } else if (h > 1) {
      h -= 1;
    }

    if (h * 6 < 1) {
      return m1 + (m2 - m1) * h * 6;
    } else if (h * 2 < 1) {
      return m2;
    } else if (h * 3 < 2) {
      return m1 + (m2 - m1) * (2 / 3 - h) * 6;
    }
    return m1;
  }
}

export class Hsv extends Space {
  static KEY_H = 'h';
  static KEY_S = 's';
  static KEY_V = 'v';

  constructor(h, s, v) {
    super([[Hsv.KEY_H, h], [Hsv.KEY_S, s], [Hsv.KEY_V, v]]);
  }

  h(value = null) {
    return this.getOrSet(Hsv.KEY_H, value);
  }

  s(value = null) {
    return this.getOrSet(Hsv.KEY_S, value);
  }

  v(value = null) {
    return this.getOrSet(Hsv.KEY_V, value);
  }

  rgb() {
    let h = this.h();
    let s = this.s();
    let v = this.v();

    let c = v * s;
    let x = c * (1 - Math.abs(((h / 60) % 2) - 1));
    let m = v - c;

    // TODO: Need to rafactor the following switch statement
    let [r, g, b] = [0, 0, 0];
    switch (!!(h / 60)) {
      case 0:
        [r, g, b] = [c, x, 0];
        break;
      case 1:
        [r, g, b] = [x, c, 0];
        break;
      case 2:
        [r, g, b] = [0, c, x];
        break;
      case 3:
        [r, g, b] = [0, x, c];
        break;
      case 4:
        [r, g, b] = [x, 0, c];
        break;
      case 5:
        [r, g, b] = [c, 0, x];
        break;
      default:
        break;
    }

    return new Rgb((r + m) * 0xFF, (g + m) * 0xFF, (b + m) * 0xFF);
  }
}

export class Cmyk extends Space {
  static KEY_C = 'c';
  static KEY_M = 'm';
  static KEY_Y = 'y';
  static KEY_K = 'k';

  constructor(c, m, y, k) {
    super([[Cmyk.KEY_C, c], [Cmyk.KEY_M, m], [Cmyk.KEY_Y, y], [Cmyk.KEY_K, k]]);
  }

  isValid() {
    for (let value of this.values()) {
      if (!Number.isFinite(value)) {
        return false;
      }
      if (value < 0 || value > 1) {
        return false;
      }
    }
    return true;
  }

  clone() {
    return new Cmyk(this.c(), this.m(), this.y(), this.k());
  }

  c(value = null) {
    return this.getOrSet(Cmyk.KEY_C, value);
  }

  m(value = null) {
    return this.getOrSet(Cmyk.KEY_M, value);
  }

  y(value = null) {
    return this.getOrSet(Cmyk.KEY_Y, value);
  }

  k(value = null) {
    return this.getOrSet(Cmyk.KEY_K, value);
  }

  hex() {
    return this.rgb().hex();
  }

  rgb() {
    let k = this.k();
    if (k === 1) {
      return new Rgb(0, 0, 0);
    }

    let r = 0xFF * (1 - this.c()) * (1 - k);
    let g = 0xFF * (1 - this.m()) * (1 - k);
    let b = 0xFF * (1 - this.y()) * (1 - k);
    return new Rgb(r, g, b);
  }

  hsl() {
    return this.rgb().hsl();
  }

  hsv() {
    return this.rgb().hsv();
  }

  cmyk() {
    return this;
  }
}
<|endoftext|>


<commit_before>import { update } from "./router"

const tuple = {
	data: null,
	key: null,
	parentKey: null
}

class Store
{
	constructor() {
		this.data = {}
		this.watchers = {}
		this.proxies = {}
	}

	set(key, value, force)
	{
		this.dispatch({
			action: "SET",
			key,
			value,
			force
		})
	}

	add(key, value)
	{
		this.dispatch({
			action: "ADD",
			key,
			value
		})
	}

	remove(key, value)
	{
		this.dispatch({
			action: "REMOVE",
			key
		})
	}

	dispatch(data)
	{
		const globalProxy = this.proxies[""]

		const index = data.key.indexOf(".")
		const proxy = (index === -1) ?
						this.proxies[data.key] :
						this.proxies[data.key.slice(0, index)]
		if(proxy)
		{
			if(globalProxy) {
				globalProxy(data)
			}
			proxy(data)
		}
		else
		{
			if(globalProxy) {
				globalProxy(data)
			}
			else {
				this.handle(data)
			}
		}
	}

	performSet(payload)
	{
		if(!this.getData(payload.key, payload.force)) { return }

		if(!tuple.key)
		{
			this.data = payload.value

			for(let key in this.watchers)
			{
				const value = this.get(key)

				const emitPayload = {
					action: "SET",
					key: key,
					value: value
				}
				this.emit(emitPayload)
			}
		}
		else
		{
			tuple.data[tuple.key] = payload.value
			this.emit(payload)

			if(tuple.parentKey)
			{
				const emitPayload = {
					action: payload.action,
					key: tuple.parentKey,
					value: tuple.data
				}
				this.emit(emitPayload)
			}
		}
	}

	performAdd(payload)
	{
		if(!this.getData(payload.key)) { return }

		let array = tuple.data[tuple.key]
		if(!array) {
			array = [ payload.value ]
			tuple.data[tuple.key] = array
		}
		else if(!Array.isArray(array)) {
			console.warn("(store) Data at key '${payload.key}' is not an Array")
			return
		}
		else {
			array.push(payload.value)
		}

		const emitPayload = {
			action: payload.action,
			key: tuple.key,
			value: array
		}
		this.emit(emitPayload)
	}

	performRemove(payload)
	{
		if(!this.getData(payload.key)) { return }

		const data = tuple.data
		if(Array.isArray(data)) {
			data.splice(parseInt(tuple.key), 1)
		}
		else {
			delete data[tuple.key]
		}

		const emitPayload = {
			action: payload.action,
			key: tuple.parentKey,
			value: data
		}
		this.emit(emitPayload)
	}

	handle(data)
	{
		switch(data.action)
		{
			case "SET":
				this.performSet(data)
				break;

			case "ADD":
				this.performAdd(data)
				break;

			case "REMOVE":
				this.performRemove(data)
				break;
		}
	}

	watch(key, element)
	{
		const buffer = this.watchers[key]
		if(!buffer) {
			this.watchers[key] = [ element ]
		}
		else {
			buffer.push(element)
		}
	}

	unwatch(key, element)
	{
		const buffer = this.watchers[key]
		if(!buffer) { return }

		for(let n = 0; n < buffer.length; n++)
		{
			if(buffer[n] === element) {
				buffer[n] = buffer[buffer.length - 1]
				buffer.pop()
				break
			}
		}

		if(buffer.length === 0) {
			delete this.watchers[key]
		}
	}

	emit(payload)
	{
		const buffer = this.watchers[payload.key]
		if(!buffer) { return }

		for(let n = 0; n < buffer.length; n++) {
			buffer[n](payload)
		}
	}

	get(key)
	{
		if(!key)
		{
			if(key === undefined) {
				return ""
			}

			return this.data
		}

		const buffer = key.split(".")
		let data = this.data

		for(let n = 0; n < buffer.length; n++)
		{
			const id = buffer[n]
			if(id === "@") {
				return buffer[n - 1]
			}
			else {
				data = data[id]
			}

			if(data === undefined) {
				return null
			}
		}

		return data
	}

	getData(key, force)
	{
		if(!key) {
			tuple.data = this.data
			tuple.key = null
			tuple.parentKey = null
			return tuple
		}

		const buffer = key.split(".")
		let data = this.data

		const num = buffer.length - 1;
		if(num === 0)
		{
			tuple.data = data
			tuple.key = buffer[0]
			tuple.parentKey = null
		}
		else
		{
			for(let n = 0; n < num; n++)
			{
				const newData = data[buffer[n]]
				if(!newData)
				{
					if(!force) {
						console.warn(`(store.getData) No data available with key: [${buffer[n]}] in address: [${key}]`)
						return null
					}
					else
					{
						for(; n < num; n++) {
							const newDict = {}
							data[buffer[n]] = newDict
							data = newDict
						}
						break
					}

				}

				data = newData
			}

			tuple.data = data
			tuple.key = buffer[num]
			tuple.parentKey = key.slice(0, key.lastIndexOf("."))
		}

		return tuple
	}

	proxy(key, func) {
		this.proxies[key] = func
	}

	toJSON() {
		return this.data
	}
}

const store = new Store()

const lastSegment = function(str)
{
	const index = str.lastIndexOf(".")
	if(index === -1) { return null }

	return str.slice(index + 1)
}

export {
	store, lastSegment
}
<commit_msg>Add store create action<commit_after>import { update } from "./router"

const tuple = {
	data: null,
	key: null,
	parentKey: null
}

class Store
{
	constructor() {
		this.data = {}
		this.watchers = {}
		this.proxies = {}
	}

	set(key, value, force)
	{
		this.dispatch({
			action: "SET",
			key,
			value,
			force
		})
	}

	add(key, value)
	{
		this.dispatch({
			action: "ADD",
			key,
			value
		})
	}

	remove(key, value)
	{
		this.dispatch({
			action: "REMOVE",
			key
		})
	}

	create(key, value)
	{
		this.dispatch({
			action: "CREATE",
			key,
			value
		})
	}

	dispatch(data)
	{
		const globalProxy = this.proxies[""]

		const index = data.key.indexOf(".")
		const proxy = (index === -1) ?
						this.proxies[data.key] :
						this.proxies[data.key.slice(0, index)]
		if(proxy)
		{
			if(globalProxy) {
				globalProxy(data)
			}
			proxy(data)
		}
		else
		{
			if(globalProxy) {
				globalProxy(data)
			}
			else {
				this.handle(data)
			}
		}
	}

	performSet(payload)
	{
		if(!this.getData(payload.key, payload.force)) { return }

		if(!tuple.key)
		{
			this.data = payload.value

			for(let key in this.watchers)
			{
				const value = this.get(key)

				const emitPayload = {
					action: "SET",
					key: key,
					value: value
				}
				this.emit(emitPayload)
			}
		}
		else
		{
			tuple.data[tuple.key] = payload.value
			this.emit(payload)

			if(tuple.parentKey)
			{
				const emitPayload = {
					action: payload.action,
					key: tuple.parentKey,
					value: tuple.data
				}
				this.emit(emitPayload)
			}
		}
	}

	performAdd(payload)
	{
		if(!this.getData(payload.key)) { return }

		let array = tuple.data[tuple.key]
		if(!array) {
			array = [ payload.value ]
			tuple.data[tuple.key] = array
		}
		else if(!Array.isArray(array)) {
			console.warn("(store) Data at key '${payload.key}' is not an Array")
			return
		}
		else {
			array.push(payload.value)
		}

		const emitPayload = {
			action: payload.action,
			key: tuple.key,
			value: array
		}
		this.emit(emitPayload)
	}

	performRemove(payload)
	{
		if(!this.getData(payload.key)) { return }

		const data = tuple.data
		if(Array.isArray(data)) {
			data.splice(parseInt(tuple.key), 1)
		}
		else {
			delete data[tuple.key]
		}

		const emitPayload = {
			action: payload.action,
			key: tuple.parentKey,
			value: data
		}
		this.emit(emitPayload)
	}

	performCreate(payload)
	{
		if(!this.getData(payload.key, true)) { return }

		if(!tuple.data[tuple.key]) {
			tuple.data[tuple.key] = payload.value
		}

		// if(tuple.parentKey)
		// {
		// 	const emitPayload = {
		// 		action: payload.action,
		// 		key: tuple.parentKey,
		// 		value: tuple.data
		// 	}
		// 	this.emit(emitPayload)
		// }
	}

	handle(data)
	{
		switch(data.action)
		{
			case "SET":
				this.performSet(data)
				break

			case "ADD":
				this.performAdd(data)
				break

			case "REMOVE":
				this.performRemove(data)
				break

			case "CREATE":
				this.performCreate(data)
				break
		}
	}

	watch(key, element)
	{
		const buffer = this.watchers[key]
		if(!buffer) {
			this.watchers[key] = [ element ]
		}
		else {
			buffer.push(element)
		}
	}

	unwatch(key, element)
	{
		const buffer = this.watchers[key]
		if(!buffer) { return }

		for(let n = 0; n < buffer.length; n++)
		{
			if(buffer[n] === element) {
				buffer[n] = buffer[buffer.length - 1]
				buffer.pop()
				break
			}
		}

		if(buffer.length === 0) {
			delete this.watchers[key]
		}
	}

	emit(payload)
	{
		const buffer = this.watchers[payload.key]
		if(!buffer) { return }

		for(let n = 0; n < buffer.length; n++) {
			buffer[n](payload)
		}
	}

	get(key)
	{
		if(!key)
		{
			if(key === undefined) {
				return ""
			}

			return this.data
		}

		const buffer = key.split(".")
		let data = this.data

		for(let n = 0; n < buffer.length; n++)
		{
			const id = buffer[n]
			if(id === "@") {
				return buffer[n - 1]
			}
			else {
				data = data[id]
			}

			if(data === undefined) {
				return null
			}
		}

		return data
	}

	getData(key, force)
	{
		if(!key) {
			tuple.data = this.data
			tuple.key = null
			tuple.parentKey = null
			return tuple
		}

		const buffer = key.split(".")
		let data = this.data

		const num = buffer.length - 1;
		if(num === 0)
		{
			tuple.data = data
			tuple.key = buffer[0]
			tuple.parentKey = null
		}
		else
		{
			for(let n = 0; n < num; n++)
			{
				const newData = data[buffer[n]]
				if(!newData)
				{
					if(!force) {
						console.warn(`(store.getData) No data available with key: [${buffer[n]}] in address: [${key}]`)
						return null
					}
					else
					{
						for(; n < num; n++) {
							const newDict = {}
							data[buffer[n]] = newDict
							data = newDict
						}
						break
					}

				}

				data = newData
			}

			tuple.data = data
			tuple.key = buffer[num]
			tuple.parentKey = key.slice(0, key.lastIndexOf("."))
		}

		return tuple
	}

	proxy(key, func) {
		this.proxies[key] = func
	}

	toJSON() {
		return this.data
	}
}

const store = new Store()

const lastSegment = function(str)
{
	const index = str.lastIndexOf(".")
	if(index === -1) { return null }

	return str.slice(index + 1)
}

export {
	store, lastSegment
}
<|endoftext|>